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/DriverDiagnostic.h"
12 #include "clang/Driver/Options.h"
13 #include "clang/Driver/ToolChain.h"
14 #include "llvm/ADT/StringExtras.h"
15 #include "llvm/ADT/StringSwitch.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Support/ScopedPrinter.h"
18 #include "llvm/Support/SpecialCaseList.h"
19 #include "llvm/Support/VirtualFileSystem.h"
21 using namespace clang
;
22 using namespace clang::driver
;
23 using namespace llvm::opt
;
25 constexpr const char *XRaySupportedModes
[] = {"xray-fdr", "xray-basic"};
27 XRayArgs::XRayArgs(const ToolChain
&TC
, const ArgList
&Args
) {
28 const Driver
&D
= TC
.getDriver();
29 const llvm::Triple
&Triple
= TC
.getTriple();
30 if (!Args
.hasFlag(options::OPT_fxray_instrument
,
31 options::OPT_fno_xray_instrument
, false))
33 XRayInstrument
= Args
.getLastArg(options::OPT_fxray_instrument
);
34 if (Triple
.isMacOSX()) {
35 switch (Triple
.getArch()) {
36 case llvm::Triple::aarch64
:
37 case llvm::Triple::x86_64
:
40 D
.Diag(diag::err_drv_unsupported_opt_for_target
)
41 << XRayInstrument
->getSpelling() << Triple
.str();
44 } else if (Triple
.isOSBinFormatELF()) {
45 switch (Triple
.getArch()) {
46 case llvm::Triple::x86_64
:
47 case llvm::Triple::arm
:
48 case llvm::Triple::aarch64
:
49 case llvm::Triple::hexagon
:
50 case llvm::Triple::ppc64le
:
51 case llvm::Triple::loongarch64
:
52 case llvm::Triple::mips
:
53 case llvm::Triple::mipsel
:
54 case llvm::Triple::mips64
:
55 case llvm::Triple::mips64el
:
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 // Both XRay and -fpatchable-function-entry use
67 // TargetOpcode::PATCHABLE_FUNCTION_ENTER.
68 if (Arg
*A
= Args
.getLastArg(options::OPT_fpatchable_function_entry_EQ
))
69 D
.Diag(diag::err_drv_argument_not_allowed_with
)
70 << XRayInstrument
->getSpelling() << A
->getSpelling();
72 if (!Args
.hasFlag(options::OPT_fxray_link_deps
,
73 options::OPT_fno_xray_link_deps
, true))
77 Args
.getAllArgValues(options::OPT_fxray_instrumentation_bundle
);
79 InstrumentationBundle
.Mask
= XRayInstrKind::All
;
81 for (const auto &B
: Bundles
) {
82 llvm::SmallVector
<StringRef
, 2> BundleParts
;
83 llvm::SplitString(B
, BundleParts
, ",");
84 for (const auto &P
: BundleParts
) {
85 // TODO: Automate the generation of the string case table.
86 auto Valid
= llvm::StringSwitch
<bool>(P
)
87 .Cases("none", "all", "function", "function-entry",
88 "function-exit", "custom", true)
92 D
.Diag(clang::diag::err_drv_invalid_value
)
93 << "-fxray-instrumentation-bundle=" << P
;
97 auto Mask
= parseXRayInstrValue(P
);
98 if (Mask
== XRayInstrKind::None
) {
99 InstrumentationBundle
.clear();
103 InstrumentationBundle
.Mask
|= Mask
;
107 // Validate the always/never attribute files. We also make sure that they
108 // are treated as actual dependencies.
109 for (const auto &Filename
:
110 Args
.getAllArgValues(options::OPT_fxray_always_instrument
)) {
111 if (D
.getVFS().exists(Filename
)) {
112 AlwaysInstrumentFiles
.push_back(Filename
);
113 ExtraDeps
.push_back(Filename
);
115 D
.Diag(clang::diag::err_drv_no_such_file
) << Filename
;
118 for (const auto &Filename
:
119 Args
.getAllArgValues(options::OPT_fxray_never_instrument
)) {
120 if (D
.getVFS().exists(Filename
)) {
121 NeverInstrumentFiles
.push_back(Filename
);
122 ExtraDeps
.push_back(Filename
);
124 D
.Diag(clang::diag::err_drv_no_such_file
) << Filename
;
127 for (const auto &Filename
:
128 Args
.getAllArgValues(options::OPT_fxray_attr_list
)) {
129 if (D
.getVFS().exists(Filename
)) {
130 AttrListFiles
.push_back(Filename
);
131 ExtraDeps
.push_back(Filename
);
133 D
.Diag(clang::diag::err_drv_no_such_file
) << Filename
;
136 // Get the list of modes we want to support.
137 auto SpecifiedModes
= Args
.getAllArgValues(options::OPT_fxray_modes
);
138 if (SpecifiedModes
.empty())
139 llvm::copy(XRaySupportedModes
, std::back_inserter(Modes
));
141 for (const auto &Arg
: SpecifiedModes
) {
142 // Parse CSV values for -fxray-modes=...
143 llvm::SmallVector
<StringRef
, 2> ModeParts
;
144 llvm::SplitString(Arg
, ModeParts
, ",");
145 for (const auto &M
: ModeParts
)
149 llvm::copy(XRaySupportedModes
, std::back_inserter(Modes
));
151 Modes
.push_back(std::string(M
));
154 // Then we want to sort and unique the modes we've collected.
156 Modes
.erase(std::unique(Modes
.begin(), Modes
.end()), Modes
.end());
159 void XRayArgs::addArgs(const ToolChain
&TC
, const ArgList
&Args
,
160 ArgStringList
&CmdArgs
, types::ID InputType
) const {
163 const Driver
&D
= TC
.getDriver();
164 XRayInstrument
->render(Args
, CmdArgs
);
166 // By default, the back-end will not emit the lowering for XRay customevent
167 // calls if the function is not instrumented. In the future we will change
168 // this default to be the reverse, but in the meantime we're going to
169 // introduce the new functionality behind a flag.
170 Args
.addOptInFlag(CmdArgs
, options::OPT_fxray_always_emit_customevents
,
171 options::OPT_fno_xray_always_emit_customevents
);
173 Args
.addOptInFlag(CmdArgs
, options::OPT_fxray_always_emit_typedevents
,
174 options::OPT_fno_xray_always_emit_typedevents
);
175 Args
.addOptInFlag(CmdArgs
, options::OPT_fxray_ignore_loops
,
176 options::OPT_fno_xray_ignore_loops
);
177 Args
.addOptOutFlag(CmdArgs
, options::OPT_fxray_function_index
,
178 options::OPT_fno_xray_function_index
);
181 Args
.getLastArg(options::OPT_fxray_instruction_threshold_EQ
)) {
183 StringRef S
= A
->getValue();
184 if (S
.getAsInteger(0, Value
) || Value
< 0)
185 D
.Diag(clang::diag::err_drv_invalid_value
) << A
->getAsString(Args
) << S
;
187 A
->render(Args
, CmdArgs
);
190 int XRayFunctionGroups
= 1;
191 int XRaySelectedFunctionGroup
= 0;
192 if (const Arg
*A
= Args
.getLastArg(options::OPT_fxray_function_groups
)) {
193 StringRef S
= A
->getValue();
194 if (S
.getAsInteger(0, XRayFunctionGroups
) || XRayFunctionGroups
< 1)
195 D
.Diag(clang::diag::err_drv_invalid_value
) << A
->getAsString(Args
) << S
;
196 if (XRayFunctionGroups
> 1)
197 A
->render(Args
, CmdArgs
);
200 Args
.getLastArg(options::OPT_fxray_selected_function_group
)) {
201 StringRef S
= A
->getValue();
202 if (S
.getAsInteger(0, XRaySelectedFunctionGroup
) ||
203 XRaySelectedFunctionGroup
< 0 ||
204 XRaySelectedFunctionGroup
>= XRayFunctionGroups
)
205 D
.Diag(clang::diag::err_drv_invalid_value
) << A
->getAsString(Args
) << S
;
206 if (XRaySelectedFunctionGroup
!= 0)
207 A
->render(Args
, CmdArgs
);
210 for (const auto &Always
: AlwaysInstrumentFiles
) {
211 SmallString
<64> AlwaysInstrumentOpt("-fxray-always-instrument=");
212 AlwaysInstrumentOpt
+= Always
;
213 CmdArgs
.push_back(Args
.MakeArgString(AlwaysInstrumentOpt
));
216 for (const auto &Never
: NeverInstrumentFiles
) {
217 SmallString
<64> NeverInstrumentOpt("-fxray-never-instrument=");
218 NeverInstrumentOpt
+= Never
;
219 CmdArgs
.push_back(Args
.MakeArgString(NeverInstrumentOpt
));
222 for (const auto &AttrFile
: AttrListFiles
) {
223 SmallString
<64> AttrListFileOpt("-fxray-attr-list=");
224 AttrListFileOpt
+= AttrFile
;
225 CmdArgs
.push_back(Args
.MakeArgString(AttrListFileOpt
));
228 for (const auto &Dep
: ExtraDeps
) {
229 SmallString
<64> ExtraDepOpt("-fdepfile-entry=");
231 CmdArgs
.push_back(Args
.MakeArgString(ExtraDepOpt
));
234 for (const auto &Mode
: Modes
) {
235 SmallString
<64> ModeOpt("-fxray-modes=");
237 CmdArgs
.push_back(Args
.MakeArgString(ModeOpt
));
240 SmallString
<64> Bundle("-fxray-instrumentation-bundle=");
241 if (InstrumentationBundle
.full()) {
243 } else if (InstrumentationBundle
.empty()) {
246 if (InstrumentationBundle
.has(XRayInstrKind::FunctionEntry
) &&
247 InstrumentationBundle
.has(XRayInstrKind::FunctionExit
))
248 Bundle
+= "function";
249 else if (InstrumentationBundle
.has(XRayInstrKind::FunctionEntry
))
250 Bundle
+= "function-entry";
251 else if (InstrumentationBundle
.has(XRayInstrKind::FunctionExit
))
252 Bundle
+= "function-exit";
254 if (InstrumentationBundle
.has(XRayInstrKind::Custom
))
256 if (InstrumentationBundle
.has(XRayInstrKind::Typed
))
259 CmdArgs
.push_back(Args
.MakeArgString(Bundle
));