1 //===--- XRayArgs.cpp - Arguments for XRay --------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
8 #include "clang/Driver/XRayArgs.h"
9 #include "ToolChains/CommonArgs.h"
10 #include "clang/Driver/Driver.h"
11 #include "clang/Driver/Options.h"
12 #include "clang/Driver/ToolChain.h"
13 #include "llvm/ADT/StringExtras.h"
14 #include "llvm/ADT/StringSwitch.h"
15 #include "llvm/Support/SpecialCaseList.h"
16 #include "llvm/Support/VirtualFileSystem.h"
18 using namespace clang
;
19 using namespace clang::driver
;
20 using namespace llvm::opt
;
22 constexpr const char *XRaySupportedModes
[] = {"xray-fdr", "xray-basic"};
24 XRayArgs::XRayArgs(const ToolChain
&TC
, const ArgList
&Args
) {
25 const Driver
&D
= TC
.getDriver();
26 const llvm::Triple
&Triple
= TC
.getTriple();
27 if (!Args
.hasFlag(options::OPT_fxray_instrument
,
28 options::OPT_fno_xray_instrument
, false))
30 XRayInstrument
= Args
.getLastArg(options::OPT_fxray_instrument
);
31 if (Triple
.isMacOSX()) {
32 switch (Triple
.getArch()) {
33 case llvm::Triple::aarch64
:
34 case llvm::Triple::x86_64
:
37 D
.Diag(diag::err_drv_unsupported_opt_for_target
)
38 << XRayInstrument
->getSpelling() << Triple
.str();
41 } else if (Triple
.isOSBinFormatELF()) {
42 switch (Triple
.getArch()) {
43 case llvm::Triple::x86_64
:
44 case llvm::Triple::arm
:
45 case llvm::Triple::aarch64
:
46 case llvm::Triple::hexagon
:
47 case llvm::Triple::ppc64le
:
48 case llvm::Triple::loongarch64
:
49 case llvm::Triple::mips
:
50 case llvm::Triple::mipsel
:
51 case llvm::Triple::mips64
:
52 case llvm::Triple::mips64el
:
53 case llvm::Triple::systemz
:
54 case llvm::Triple::riscv32
:
55 case llvm::Triple::riscv64
:
58 D
.Diag(diag::err_drv_unsupported_opt_for_target
)
59 << XRayInstrument
->getSpelling() << Triple
.str();
62 D
.Diag(diag::err_drv_unsupported_opt_for_target
)
63 << XRayInstrument
->getSpelling() << Triple
.str();
66 if (Args
.hasFlag(options::OPT_fxray_shared
, options::OPT_fno_xray_shared
,
70 // Certain targets support DSO instrumentation
71 switch (Triple
.getArch()) {
72 case llvm::Triple::aarch64
:
73 case llvm::Triple::x86_64
:
76 D
.Diag(diag::err_drv_unsupported_opt_for_target
)
77 << "-fxray-shared" << Triple
.str();
80 unsigned PICLvl
= std::get
<1>(tools::ParsePICArgs(TC
, Args
));
82 D
.Diag(diag::err_opt_not_valid_without_opt
) << "-fxray-shared"
87 // Both XRay and -fpatchable-function-entry use
88 // TargetOpcode::PATCHABLE_FUNCTION_ENTER.
89 if (Arg
*A
= Args
.getLastArg(options::OPT_fpatchable_function_entry_EQ
))
90 D
.Diag(diag::err_drv_argument_not_allowed_with
)
91 << XRayInstrument
->getSpelling() << A
->getSpelling();
93 if (!Args
.hasFlag(options::OPT_fxray_link_deps
,
94 options::OPT_fno_xray_link_deps
, true))
98 Args
.getAllArgValues(options::OPT_fxray_instrumentation_bundle
);
100 InstrumentationBundle
.Mask
= XRayInstrKind::All
;
102 for (const auto &B
: Bundles
) {
103 llvm::SmallVector
<StringRef
, 2> BundleParts
;
104 llvm::SplitString(B
, BundleParts
, ",");
105 for (const auto &P
: BundleParts
) {
106 // TODO: Automate the generation of the string case table.
107 auto Valid
= llvm::StringSwitch
<bool>(P
)
108 .Cases("none", "all", "function", "function-entry",
109 "function-exit", "custom", true)
113 D
.Diag(clang::diag::err_drv_invalid_value
)
114 << "-fxray-instrumentation-bundle=" << P
;
118 auto Mask
= parseXRayInstrValue(P
);
119 if (Mask
== XRayInstrKind::None
) {
120 InstrumentationBundle
.clear();
124 InstrumentationBundle
.Mask
|= Mask
;
128 // Validate the always/never attribute files. We also make sure that they
129 // are treated as actual dependencies.
130 for (const auto &Filename
:
131 Args
.getAllArgValues(options::OPT_fxray_always_instrument
)) {
132 if (D
.getVFS().exists(Filename
)) {
133 AlwaysInstrumentFiles
.push_back(Filename
);
134 ExtraDeps
.push_back(Filename
);
136 D
.Diag(clang::diag::err_drv_no_such_file
) << Filename
;
139 for (const auto &Filename
:
140 Args
.getAllArgValues(options::OPT_fxray_never_instrument
)) {
141 if (D
.getVFS().exists(Filename
)) {
142 NeverInstrumentFiles
.push_back(Filename
);
143 ExtraDeps
.push_back(Filename
);
145 D
.Diag(clang::diag::err_drv_no_such_file
) << Filename
;
148 for (const auto &Filename
:
149 Args
.getAllArgValues(options::OPT_fxray_attr_list
)) {
150 if (D
.getVFS().exists(Filename
)) {
151 AttrListFiles
.push_back(Filename
);
152 ExtraDeps
.push_back(Filename
);
154 D
.Diag(clang::diag::err_drv_no_such_file
) << Filename
;
157 // Get the list of modes we want to support.
158 auto SpecifiedModes
= Args
.getAllArgValues(options::OPT_fxray_modes
);
159 if (SpecifiedModes
.empty())
160 llvm::copy(XRaySupportedModes
, std::back_inserter(Modes
));
162 for (const auto &Arg
: SpecifiedModes
) {
163 // Parse CSV values for -fxray-modes=...
164 llvm::SmallVector
<StringRef
, 2> ModeParts
;
165 llvm::SplitString(Arg
, ModeParts
, ",");
166 for (const auto &M
: ModeParts
)
170 llvm::copy(XRaySupportedModes
, std::back_inserter(Modes
));
172 Modes
.push_back(std::string(M
));
175 // Then we want to sort and unique the modes we've collected.
177 Modes
.erase(std::unique(Modes
.begin(), Modes
.end()), Modes
.end());
180 void XRayArgs::addArgs(const ToolChain
&TC
, const ArgList
&Args
,
181 ArgStringList
&CmdArgs
, types::ID InputType
) const {
184 const Driver
&D
= TC
.getDriver();
185 XRayInstrument
->render(Args
, CmdArgs
);
187 // By default, the back-end will not emit the lowering for XRay customevent
188 // calls if the function is not instrumented. In the future we will change
189 // this default to be the reverse, but in the meantime we're going to
190 // introduce the new functionality behind a flag.
191 Args
.addOptInFlag(CmdArgs
, options::OPT_fxray_always_emit_customevents
,
192 options::OPT_fno_xray_always_emit_customevents
);
194 Args
.addOptInFlag(CmdArgs
, options::OPT_fxray_always_emit_typedevents
,
195 options::OPT_fno_xray_always_emit_typedevents
);
196 Args
.addOptInFlag(CmdArgs
, options::OPT_fxray_ignore_loops
,
197 options::OPT_fno_xray_ignore_loops
);
198 Args
.addOptOutFlag(CmdArgs
, options::OPT_fxray_function_index
,
199 options::OPT_fno_xray_function_index
);
202 Args
.addOptInFlag(CmdArgs
, options::OPT_fxray_shared
,
203 options::OPT_fno_xray_shared
);
206 Args
.getLastArg(options::OPT_fxray_instruction_threshold_EQ
)) {
208 StringRef S
= A
->getValue();
209 if (S
.getAsInteger(0, Value
) || Value
< 0)
210 D
.Diag(clang::diag::err_drv_invalid_value
) << A
->getAsString(Args
) << S
;
212 A
->render(Args
, CmdArgs
);
215 int XRayFunctionGroups
= 1;
216 int XRaySelectedFunctionGroup
= 0;
217 if (const Arg
*A
= Args
.getLastArg(options::OPT_fxray_function_groups
)) {
218 StringRef S
= A
->getValue();
219 if (S
.getAsInteger(0, XRayFunctionGroups
) || XRayFunctionGroups
< 1)
220 D
.Diag(clang::diag::err_drv_invalid_value
) << A
->getAsString(Args
) << S
;
221 if (XRayFunctionGroups
> 1)
222 A
->render(Args
, CmdArgs
);
225 Args
.getLastArg(options::OPT_fxray_selected_function_group
)) {
226 StringRef S
= A
->getValue();
227 if (S
.getAsInteger(0, XRaySelectedFunctionGroup
) ||
228 XRaySelectedFunctionGroup
< 0 ||
229 XRaySelectedFunctionGroup
>= XRayFunctionGroups
)
230 D
.Diag(clang::diag::err_drv_invalid_value
) << A
->getAsString(Args
) << S
;
231 if (XRaySelectedFunctionGroup
!= 0)
232 A
->render(Args
, CmdArgs
);
235 for (const auto &Always
: AlwaysInstrumentFiles
) {
236 SmallString
<64> AlwaysInstrumentOpt("-fxray-always-instrument=");
237 AlwaysInstrumentOpt
+= Always
;
238 CmdArgs
.push_back(Args
.MakeArgString(AlwaysInstrumentOpt
));
241 for (const auto &Never
: NeverInstrumentFiles
) {
242 SmallString
<64> NeverInstrumentOpt("-fxray-never-instrument=");
243 NeverInstrumentOpt
+= Never
;
244 CmdArgs
.push_back(Args
.MakeArgString(NeverInstrumentOpt
));
247 for (const auto &AttrFile
: AttrListFiles
) {
248 SmallString
<64> AttrListFileOpt("-fxray-attr-list=");
249 AttrListFileOpt
+= AttrFile
;
250 CmdArgs
.push_back(Args
.MakeArgString(AttrListFileOpt
));
253 for (const auto &Dep
: ExtraDeps
) {
254 SmallString
<64> ExtraDepOpt("-fdepfile-entry=");
256 CmdArgs
.push_back(Args
.MakeArgString(ExtraDepOpt
));
259 for (const auto &Mode
: Modes
) {
260 SmallString
<64> ModeOpt("-fxray-modes=");
262 CmdArgs
.push_back(Args
.MakeArgString(ModeOpt
));
265 SmallString
<64> Bundle("-fxray-instrumentation-bundle=");
266 if (InstrumentationBundle
.full()) {
268 } else if (InstrumentationBundle
.empty()) {
271 if (InstrumentationBundle
.has(XRayInstrKind::FunctionEntry
) &&
272 InstrumentationBundle
.has(XRayInstrKind::FunctionExit
))
273 Bundle
+= "function";
274 else if (InstrumentationBundle
.has(XRayInstrKind::FunctionEntry
))
275 Bundle
+= "function-entry";
276 else if (InstrumentationBundle
.has(XRayInstrKind::FunctionExit
))
277 Bundle
+= "function-exit";
279 if (InstrumentationBundle
.has(XRayInstrKind::Custom
))
281 if (InstrumentationBundle
.has(XRayInstrKind::Typed
))
284 CmdArgs
.push_back(Args
.MakeArgString(Bundle
));