xref: /llvm-project/clang/lib/Driver/XRayArgs.cpp (revision ea76b2d8d83d6885bf5707832cbc4b7655e21b08)
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/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"
17 
18 using namespace clang;
19 using namespace clang::driver;
20 using namespace llvm::opt;
21 
22 constexpr const char *XRaySupportedModes[] = {"xray-fdr", "xray-basic"};
23 
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))
29     return;
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:
35       break;
36     default:
37       D.Diag(diag::err_drv_unsupported_opt_for_target)
38           << XRayInstrument->getSpelling() << Triple.str();
39       break;
40     }
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:
56       break;
57     default:
58       D.Diag(diag::err_drv_unsupported_opt_for_target)
59           << XRayInstrument->getSpelling() << Triple.str();
60     }
61   } else {
62     D.Diag(diag::err_drv_unsupported_opt_for_target)
63         << XRayInstrument->getSpelling() << Triple.str();
64   }
65 
66   if (Args.hasFlag(options::OPT_fxray_shared, options::OPT_fno_xray_shared,
67                    false)) {
68     XRayShared = true;
69 
70     // Certain targets support DSO instrumentation
71     switch (Triple.getArch()) {
72     case llvm::Triple::aarch64:
73     case llvm::Triple::x86_64:
74       break;
75     default:
76       D.Diag(diag::err_drv_unsupported_opt_for_target)
77           << "-fxray-shared" << Triple.str();
78     }
79 
80     unsigned PICLvl = std::get<1>(tools::ParsePICArgs(TC, Args));
81     if (!PICLvl) {
82       D.Diag(diag::err_opt_not_valid_without_opt) << "-fxray-shared"
83                                                   << "-fPIC";
84     }
85   }
86 
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();
92 
93   if (!Args.hasFlag(options::OPT_fxray_link_deps,
94                     options::OPT_fno_xray_link_deps, true))
95     XRayRT = false;
96 
97   auto Bundles =
98       Args.getAllArgValues(options::OPT_fxray_instrumentation_bundle);
99   if (Bundles.empty())
100     InstrumentationBundle.Mask = XRayInstrKind::All;
101   else
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)
110                          .Default(false);
111 
112         if (!Valid) {
113           D.Diag(clang::diag::err_drv_invalid_value)
114               << "-fxray-instrumentation-bundle=" << P;
115           continue;
116         }
117 
118         auto Mask = parseXRayInstrValue(P);
119         if (Mask == XRayInstrKind::None) {
120           InstrumentationBundle.clear();
121           break;
122         }
123 
124         InstrumentationBundle.Mask |= Mask;
125       }
126     }
127 
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);
135     } else
136       D.Diag(clang::diag::err_drv_no_such_file) << Filename;
137   }
138 
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);
144     } else
145       D.Diag(clang::diag::err_drv_no_such_file) << Filename;
146   }
147 
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);
153     } else
154       D.Diag(clang::diag::err_drv_no_such_file) << Filename;
155   }
156 
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));
161   else
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)
167         if (M == "none")
168           Modes.clear();
169         else if (M == "all")
170           llvm::copy(XRaySupportedModes, std::back_inserter(Modes));
171         else
172           Modes.push_back(std::string(M));
173     }
174 
175   // Then we want to sort and unique the modes we've collected.
176   llvm::sort(Modes);
177   Modes.erase(std::unique(Modes.begin(), Modes.end()), Modes.end());
178 }
179 
180 void XRayArgs::addArgs(const ToolChain &TC, const ArgList &Args,
181                        ArgStringList &CmdArgs, types::ID InputType) const {
182   if (!XRayInstrument)
183     return;
184   const Driver &D = TC.getDriver();
185   XRayInstrument->render(Args, CmdArgs);
186 
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);
193 
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);
200 
201   if (XRayShared)
202     Args.addOptInFlag(CmdArgs, options::OPT_fxray_shared,
203                       options::OPT_fno_xray_shared);
204 
205   if (const Arg *A =
206           Args.getLastArg(options::OPT_fxray_instruction_threshold_EQ)) {
207     int Value;
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;
211     else
212       A->render(Args, CmdArgs);
213   }
214 
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);
223   }
224   if (const Arg *A =
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);
233   }
234 
235   for (const auto &Always : AlwaysInstrumentFiles) {
236     SmallString<64> AlwaysInstrumentOpt("-fxray-always-instrument=");
237     AlwaysInstrumentOpt += Always;
238     CmdArgs.push_back(Args.MakeArgString(AlwaysInstrumentOpt));
239   }
240 
241   for (const auto &Never : NeverInstrumentFiles) {
242     SmallString<64> NeverInstrumentOpt("-fxray-never-instrument=");
243     NeverInstrumentOpt += Never;
244     CmdArgs.push_back(Args.MakeArgString(NeverInstrumentOpt));
245   }
246 
247   for (const auto &AttrFile : AttrListFiles) {
248     SmallString<64> AttrListFileOpt("-fxray-attr-list=");
249     AttrListFileOpt += AttrFile;
250     CmdArgs.push_back(Args.MakeArgString(AttrListFileOpt));
251   }
252 
253   for (const auto &Dep : ExtraDeps) {
254     SmallString<64> ExtraDepOpt("-fdepfile-entry=");
255     ExtraDepOpt += Dep;
256     CmdArgs.push_back(Args.MakeArgString(ExtraDepOpt));
257   }
258 
259   for (const auto &Mode : Modes) {
260     SmallString<64> ModeOpt("-fxray-modes=");
261     ModeOpt += Mode;
262     CmdArgs.push_back(Args.MakeArgString(ModeOpt));
263   }
264 
265   SmallString<64> Bundle("-fxray-instrumentation-bundle=");
266   if (InstrumentationBundle.full()) {
267     Bundle += "all";
268   } else if (InstrumentationBundle.empty()) {
269     Bundle += "none";
270   } else {
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";
278 
279     if (InstrumentationBundle.has(XRayInstrKind::Custom))
280       Bundle += "custom";
281     if (InstrumentationBundle.has(XRayInstrKind::Typed))
282       Bundle += "typed";
283   }
284   CmdArgs.push_back(Args.MakeArgString(Bundle));
285 }
286