1//===--- XRayArgs.cpp - Arguments for XRay --------------------------------===//
2//
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
6//
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"
20
21using namespace clang;
22using namespace clang::driver;
23using namespace llvm::opt;
24
25constexpr const char *XRaySupportedModes[] = {"xray-fdr", "xray-basic"};
26
27XRayArgs::XRayArgs(const ToolChain &TC, const ArgList &Args) {
28 const Driver &D = TC.getDriver();
29 const llvm::Triple &Triple = TC.getTriple();
30 if (!Args.hasFlag(Pos: options::OPT_fxray_instrument,
31 Neg: options::OPT_fno_xray_instrument, Default: false))
32 return;
33 XRayInstrument = Args.getLastArg(Ids: options::OPT_fxray_instrument);
34 if (Triple.isMacOSX()) {
35 switch (Triple.getArch()) {
36 case llvm::Triple::aarch64:
37 case llvm::Triple::x86_64:
38 break;
39 default:
40 D.Diag(DiagID: diag::err_drv_unsupported_opt_for_target)
41 << XRayInstrument->getSpelling() << Triple.str();
42 break;
43 }
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:
56 break;
57 default:
58 D.Diag(DiagID: diag::err_drv_unsupported_opt_for_target)
59 << XRayInstrument->getSpelling() << Triple.str();
60 }
61 } else {
62 D.Diag(DiagID: diag::err_drv_unsupported_opt_for_target)
63 << XRayInstrument->getSpelling() << Triple.str();
64 }
65
66 // Both XRay and -fpatchable-function-entry use
67 // TargetOpcode::PATCHABLE_FUNCTION_ENTER.
68 if (Arg *A = Args.getLastArg(Ids: options::OPT_fpatchable_function_entry_EQ))
69 D.Diag(DiagID: diag::err_drv_argument_not_allowed_with)
70 << XRayInstrument->getSpelling() << A->getSpelling();
71
72 if (!Args.hasFlag(Pos: options::OPT_fxray_link_deps,
73 Neg: options::OPT_fno_xray_link_deps, Default: true))
74 XRayRT = false;
75
76 auto Bundles =
77 Args.getAllArgValues(Id: options::OPT_fxray_instrumentation_bundle);
78 if (Bundles.empty())
79 InstrumentationBundle.Mask = XRayInstrKind::All;
80 else
81 for (const auto &B : Bundles) {
82 llvm::SmallVector<StringRef, 2> BundleParts;
83 llvm::SplitString(Source: B, OutFragments&: BundleParts, Delimiters: ",");
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(S0: "none", S1: "all", S2: "function", S3: "function-entry",
88 S4: "function-exit", S5: "custom", Value: true)
89 .Default(Value: false);
90
91 if (!Valid) {
92 D.Diag(DiagID: clang::diag::err_drv_invalid_value)
93 << "-fxray-instrumentation-bundle=" << P;
94 continue;
95 }
96
97 auto Mask = parseXRayInstrValue(Value: P);
98 if (Mask == XRayInstrKind::None) {
99 InstrumentationBundle.clear();
100 break;
101 }
102
103 InstrumentationBundle.Mask |= Mask;
104 }
105 }
106
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(Id: options::OPT_fxray_always_instrument)) {
111 if (D.getVFS().exists(Path: Filename)) {
112 AlwaysInstrumentFiles.push_back(x: Filename);
113 ExtraDeps.push_back(x: Filename);
114 } else
115 D.Diag(DiagID: clang::diag::err_drv_no_such_file) << Filename;
116 }
117
118 for (const auto &Filename :
119 Args.getAllArgValues(Id: options::OPT_fxray_never_instrument)) {
120 if (D.getVFS().exists(Path: Filename)) {
121 NeverInstrumentFiles.push_back(x: Filename);
122 ExtraDeps.push_back(x: Filename);
123 } else
124 D.Diag(DiagID: clang::diag::err_drv_no_such_file) << Filename;
125 }
126
127 for (const auto &Filename :
128 Args.getAllArgValues(Id: options::OPT_fxray_attr_list)) {
129 if (D.getVFS().exists(Path: Filename)) {
130 AttrListFiles.push_back(x: Filename);
131 ExtraDeps.push_back(x: Filename);
132 } else
133 D.Diag(DiagID: clang::diag::err_drv_no_such_file) << Filename;
134 }
135
136 // Get the list of modes we want to support.
137 auto SpecifiedModes = Args.getAllArgValues(Id: options::OPT_fxray_modes);
138 if (SpecifiedModes.empty())
139 llvm::copy(Range: XRaySupportedModes, Out: std::back_inserter(x&: Modes));
140 else
141 for (const auto &Arg : SpecifiedModes) {
142 // Parse CSV values for -fxray-modes=...
143 llvm::SmallVector<StringRef, 2> ModeParts;
144 llvm::SplitString(Source: Arg, OutFragments&: ModeParts, Delimiters: ",");
145 for (const auto &M : ModeParts)
146 if (M == "none")
147 Modes.clear();
148 else if (M == "all")
149 llvm::copy(Range: XRaySupportedModes, Out: std::back_inserter(x&: Modes));
150 else
151 Modes.push_back(x: std::string(M));
152 }
153
154 // Then we want to sort and unique the modes we've collected.
155 llvm::sort(C&: Modes);
156 Modes.erase(first: std::unique(first: Modes.begin(), last: Modes.end()), last: Modes.end());
157}
158
159void XRayArgs::addArgs(const ToolChain &TC, const ArgList &Args,
160 ArgStringList &CmdArgs, types::ID InputType) const {
161 if (!XRayInstrument)
162 return;
163 const Driver &D = TC.getDriver();
164 XRayInstrument->render(Args, Output&: CmdArgs);
165
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(Output&: CmdArgs, Pos: options::OPT_fxray_always_emit_customevents,
171 Neg: options::OPT_fno_xray_always_emit_customevents);
172
173 Args.addOptInFlag(Output&: CmdArgs, Pos: options::OPT_fxray_always_emit_typedevents,
174 Neg: options::OPT_fno_xray_always_emit_typedevents);
175 Args.addOptInFlag(Output&: CmdArgs, Pos: options::OPT_fxray_ignore_loops,
176 Neg: options::OPT_fno_xray_ignore_loops);
177 Args.addOptOutFlag(Output&: CmdArgs, Pos: options::OPT_fxray_function_index,
178 Neg: options::OPT_fno_xray_function_index);
179
180 if (const Arg *A =
181 Args.getLastArg(Ids: options::OPT_fxray_instruction_threshold_EQ)) {
182 int Value;
183 StringRef S = A->getValue();
184 if (S.getAsInteger(Radix: 0, Result&: Value) || Value < 0)
185 D.Diag(DiagID: clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
186 else
187 A->render(Args, Output&: CmdArgs);
188 }
189
190 int XRayFunctionGroups = 1;
191 int XRaySelectedFunctionGroup = 0;
192 if (const Arg *A = Args.getLastArg(Ids: options::OPT_fxray_function_groups)) {
193 StringRef S = A->getValue();
194 if (S.getAsInteger(Radix: 0, Result&: XRayFunctionGroups) || XRayFunctionGroups < 1)
195 D.Diag(DiagID: clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
196 if (XRayFunctionGroups > 1)
197 A->render(Args, Output&: CmdArgs);
198 }
199 if (const Arg *A =
200 Args.getLastArg(Ids: options::OPT_fxray_selected_function_group)) {
201 StringRef S = A->getValue();
202 if (S.getAsInteger(Radix: 0, Result&: XRaySelectedFunctionGroup) ||
203 XRaySelectedFunctionGroup < 0 ||
204 XRaySelectedFunctionGroup >= XRayFunctionGroups)
205 D.Diag(DiagID: clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
206 if (XRaySelectedFunctionGroup != 0)
207 A->render(Args, Output&: CmdArgs);
208 }
209
210 for (const auto &Always : AlwaysInstrumentFiles) {
211 SmallString<64> AlwaysInstrumentOpt("-fxray-always-instrument=");
212 AlwaysInstrumentOpt += Always;
213 CmdArgs.push_back(Elt: Args.MakeArgString(Str: AlwaysInstrumentOpt));
214 }
215
216 for (const auto &Never : NeverInstrumentFiles) {
217 SmallString<64> NeverInstrumentOpt("-fxray-never-instrument=");
218 NeverInstrumentOpt += Never;
219 CmdArgs.push_back(Elt: Args.MakeArgString(Str: NeverInstrumentOpt));
220 }
221
222 for (const auto &AttrFile : AttrListFiles) {
223 SmallString<64> AttrListFileOpt("-fxray-attr-list=");
224 AttrListFileOpt += AttrFile;
225 CmdArgs.push_back(Elt: Args.MakeArgString(Str: AttrListFileOpt));
226 }
227
228 for (const auto &Dep : ExtraDeps) {
229 SmallString<64> ExtraDepOpt("-fdepfile-entry=");
230 ExtraDepOpt += Dep;
231 CmdArgs.push_back(Elt: Args.MakeArgString(Str: ExtraDepOpt));
232 }
233
234 for (const auto &Mode : Modes) {
235 SmallString<64> ModeOpt("-fxray-modes=");
236 ModeOpt += Mode;
237 CmdArgs.push_back(Elt: Args.MakeArgString(Str: ModeOpt));
238 }
239
240 SmallString<64> Bundle("-fxray-instrumentation-bundle=");
241 if (InstrumentationBundle.full()) {
242 Bundle += "all";
243 } else if (InstrumentationBundle.empty()) {
244 Bundle += "none";
245 } else {
246 if (InstrumentationBundle.has(K: XRayInstrKind::FunctionEntry) &&
247 InstrumentationBundle.has(K: XRayInstrKind::FunctionExit))
248 Bundle += "function";
249 else if (InstrumentationBundle.has(K: XRayInstrKind::FunctionEntry))
250 Bundle += "function-entry";
251 else if (InstrumentationBundle.has(K: XRayInstrKind::FunctionExit))
252 Bundle += "function-exit";
253
254 if (InstrumentationBundle.has(K: XRayInstrKind::Custom))
255 Bundle += "custom";
256 if (InstrumentationBundle.has(K: XRayInstrKind::Typed))
257 Bundle += "typed";
258 }
259 CmdArgs.push_back(Elt: Args.MakeArgString(Str: Bundle));
260}
261