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
:
56 D
.Diag(diag::err_drv_unsupported_opt_for_target
)
57 << XRayInstrument
->getSpelling() << Triple
.str();
60 D
.Diag(diag::err_drv_unsupported_opt_for_target
)
61 << XRayInstrument
->getSpelling() << Triple
.str();
64 if (Args
.hasFlag(options::OPT_fxray_shared
, options::OPT_fno_xray_shared
,
68 // Certain targets support DSO instrumentation
69 switch (Triple
.getArch()) {
70 case llvm::Triple::aarch64
:
71 case llvm::Triple::x86_64
:
74 D
.Diag(diag::err_drv_unsupported_opt_for_target
)
75 << "-fxray-shared" << Triple
.str();
78 unsigned PICLvl
= std::get
<1>(tools::ParsePICArgs(TC
, Args
));
80 D
.Diag(diag::err_opt_not_valid_without_opt
) << "-fxray-shared"
85 // Both XRay and -fpatchable-function-entry use
86 // TargetOpcode::PATCHABLE_FUNCTION_ENTER.
87 if (Arg
*A
= Args
.getLastArg(options::OPT_fpatchable_function_entry_EQ
))
88 D
.Diag(diag::err_drv_argument_not_allowed_with
)
89 << XRayInstrument
->getSpelling() << A
->getSpelling();
91 if (!Args
.hasFlag(options::OPT_fxray_link_deps
,
92 options::OPT_fno_xray_link_deps
, true))
96 Args
.getAllArgValues(options::OPT_fxray_instrumentation_bundle
);
98 InstrumentationBundle
.Mask
= XRayInstrKind::All
;
100 for (const auto &B
: Bundles
) {
101 llvm::SmallVector
<StringRef
, 2> BundleParts
;
102 llvm::SplitString(B
, BundleParts
, ",");
103 for (const auto &P
: BundleParts
) {
104 // TODO: Automate the generation of the string case table.
105 auto Valid
= llvm::StringSwitch
<bool>(P
)
106 .Cases("none", "all", "function", "function-entry",
107 "function-exit", "custom", true)
111 D
.Diag(clang::diag::err_drv_invalid_value
)
112 << "-fxray-instrumentation-bundle=" << P
;
116 auto Mask
= parseXRayInstrValue(P
);
117 if (Mask
== XRayInstrKind::None
) {
118 InstrumentationBundle
.clear();
122 InstrumentationBundle
.Mask
|= Mask
;
126 // Validate the always/never attribute files. We also make sure that they
127 // are treated as actual dependencies.
128 for (const auto &Filename
:
129 Args
.getAllArgValues(options::OPT_fxray_always_instrument
)) {
130 if (D
.getVFS().exists(Filename
)) {
131 AlwaysInstrumentFiles
.push_back(Filename
);
132 ExtraDeps
.push_back(Filename
);
134 D
.Diag(clang::diag::err_drv_no_such_file
) << Filename
;
137 for (const auto &Filename
:
138 Args
.getAllArgValues(options::OPT_fxray_never_instrument
)) {
139 if (D
.getVFS().exists(Filename
)) {
140 NeverInstrumentFiles
.push_back(Filename
);
141 ExtraDeps
.push_back(Filename
);
143 D
.Diag(clang::diag::err_drv_no_such_file
) << Filename
;
146 for (const auto &Filename
:
147 Args
.getAllArgValues(options::OPT_fxray_attr_list
)) {
148 if (D
.getVFS().exists(Filename
)) {
149 AttrListFiles
.push_back(Filename
);
150 ExtraDeps
.push_back(Filename
);
152 D
.Diag(clang::diag::err_drv_no_such_file
) << Filename
;
155 // Get the list of modes we want to support.
156 auto SpecifiedModes
= Args
.getAllArgValues(options::OPT_fxray_modes
);
157 if (SpecifiedModes
.empty())
158 llvm::copy(XRaySupportedModes
, std::back_inserter(Modes
));
160 for (const auto &Arg
: SpecifiedModes
) {
161 // Parse CSV values for -fxray-modes=...
162 llvm::SmallVector
<StringRef
, 2> ModeParts
;
163 llvm::SplitString(Arg
, ModeParts
, ",");
164 for (const auto &M
: ModeParts
)
168 llvm::copy(XRaySupportedModes
, std::back_inserter(Modes
));
170 Modes
.push_back(std::string(M
));
173 // Then we want to sort and unique the modes we've collected.
175 Modes
.erase(std::unique(Modes
.begin(), Modes
.end()), Modes
.end());
178 void XRayArgs::addArgs(const ToolChain
&TC
, const ArgList
&Args
,
179 ArgStringList
&CmdArgs
, types::ID InputType
) const {
182 const Driver
&D
= TC
.getDriver();
183 XRayInstrument
->render(Args
, CmdArgs
);
185 // By default, the back-end will not emit the lowering for XRay customevent
186 // calls if the function is not instrumented. In the future we will change
187 // this default to be the reverse, but in the meantime we're going to
188 // introduce the new functionality behind a flag.
189 Args
.addOptInFlag(CmdArgs
, options::OPT_fxray_always_emit_customevents
,
190 options::OPT_fno_xray_always_emit_customevents
);
192 Args
.addOptInFlag(CmdArgs
, options::OPT_fxray_always_emit_typedevents
,
193 options::OPT_fno_xray_always_emit_typedevents
);
194 Args
.addOptInFlag(CmdArgs
, options::OPT_fxray_ignore_loops
,
195 options::OPT_fno_xray_ignore_loops
);
196 Args
.addOptOutFlag(CmdArgs
, options::OPT_fxray_function_index
,
197 options::OPT_fno_xray_function_index
);
200 Args
.addOptInFlag(CmdArgs
, options::OPT_fxray_shared
,
201 options::OPT_fno_xray_shared
);
204 Args
.getLastArg(options::OPT_fxray_instruction_threshold_EQ
)) {
206 StringRef S
= A
->getValue();
207 if (S
.getAsInteger(0, Value
) || Value
< 0)
208 D
.Diag(clang::diag::err_drv_invalid_value
) << A
->getAsString(Args
) << S
;
210 A
->render(Args
, CmdArgs
);
213 int XRayFunctionGroups
= 1;
214 int XRaySelectedFunctionGroup
= 0;
215 if (const Arg
*A
= Args
.getLastArg(options::OPT_fxray_function_groups
)) {
216 StringRef S
= A
->getValue();
217 if (S
.getAsInteger(0, XRayFunctionGroups
) || XRayFunctionGroups
< 1)
218 D
.Diag(clang::diag::err_drv_invalid_value
) << A
->getAsString(Args
) << S
;
219 if (XRayFunctionGroups
> 1)
220 A
->render(Args
, CmdArgs
);
223 Args
.getLastArg(options::OPT_fxray_selected_function_group
)) {
224 StringRef S
= A
->getValue();
225 if (S
.getAsInteger(0, XRaySelectedFunctionGroup
) ||
226 XRaySelectedFunctionGroup
< 0 ||
227 XRaySelectedFunctionGroup
>= XRayFunctionGroups
)
228 D
.Diag(clang::diag::err_drv_invalid_value
) << A
->getAsString(Args
) << S
;
229 if (XRaySelectedFunctionGroup
!= 0)
230 A
->render(Args
, CmdArgs
);
233 for (const auto &Always
: AlwaysInstrumentFiles
) {
234 SmallString
<64> AlwaysInstrumentOpt("-fxray-always-instrument=");
235 AlwaysInstrumentOpt
+= Always
;
236 CmdArgs
.push_back(Args
.MakeArgString(AlwaysInstrumentOpt
));
239 for (const auto &Never
: NeverInstrumentFiles
) {
240 SmallString
<64> NeverInstrumentOpt("-fxray-never-instrument=");
241 NeverInstrumentOpt
+= Never
;
242 CmdArgs
.push_back(Args
.MakeArgString(NeverInstrumentOpt
));
245 for (const auto &AttrFile
: AttrListFiles
) {
246 SmallString
<64> AttrListFileOpt("-fxray-attr-list=");
247 AttrListFileOpt
+= AttrFile
;
248 CmdArgs
.push_back(Args
.MakeArgString(AttrListFileOpt
));
251 for (const auto &Dep
: ExtraDeps
) {
252 SmallString
<64> ExtraDepOpt("-fdepfile-entry=");
254 CmdArgs
.push_back(Args
.MakeArgString(ExtraDepOpt
));
257 for (const auto &Mode
: Modes
) {
258 SmallString
<64> ModeOpt("-fxray-modes=");
260 CmdArgs
.push_back(Args
.MakeArgString(ModeOpt
));
263 SmallString
<64> Bundle("-fxray-instrumentation-bundle=");
264 if (InstrumentationBundle
.full()) {
266 } else if (InstrumentationBundle
.empty()) {
269 if (InstrumentationBundle
.has(XRayInstrKind::FunctionEntry
) &&
270 InstrumentationBundle
.has(XRayInstrKind::FunctionExit
))
271 Bundle
+= "function";
272 else if (InstrumentationBundle
.has(XRayInstrKind::FunctionEntry
))
273 Bundle
+= "function-entry";
274 else if (InstrumentationBundle
.has(XRayInstrKind::FunctionExit
))
275 Bundle
+= "function-exit";
277 if (InstrumentationBundle
.has(XRayInstrKind::Custom
))
279 if (InstrumentationBundle
.has(XRayInstrKind::Typed
))
282 CmdArgs
.push_back(Args
.MakeArgString(Bundle
));