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/FileSystem.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/ScopedPrinter.h"
19 #include "llvm/Support/SpecialCaseList.h"
20 
21 using namespace clang;
22 using namespace clang::driver;
23 using namespace llvm::opt;
24 
25 namespace {
26 constexpr char XRayInstrumentOption[] = "-fxray-instrument";
27 constexpr char XRayInstructionThresholdOption[] =
28     "-fxray-instruction-threshold=";
29 constexpr const char *const XRaySupportedModes[] = {"xray-fdr", "xray-basic"};
30 } // namespace
31 
32 XRayArgs::XRayArgs(const ToolChain &TC, const ArgList &Args) {
33   const Driver &D = TC.getDriver();
34   const llvm::Triple &Triple = TC.getTriple();
35   if (!Args.hasFlag(options::OPT_fxray_instrument,
36                     options::OPT_fno_xray_instrument, false))
37     return;
38   if (Triple.getOS() == llvm::Triple::Linux) {
39     switch (Triple.getArch()) {
40     case llvm::Triple::x86_64:
41     case llvm::Triple::arm:
42     case llvm::Triple::aarch64:
43     case llvm::Triple::ppc64le:
44     case llvm::Triple::mips:
45     case llvm::Triple::mipsel:
46     case llvm::Triple::mips64:
47     case llvm::Triple::mips64el:
48       break;
49     default:
50       D.Diag(diag::err_drv_clang_unsupported)
51           << (std::string(XRayInstrumentOption) + " on " + Triple.str());
52     }
53   } else if (Triple.isOSFreeBSD() || Triple.isOSOpenBSD() ||
54              Triple.isOSNetBSD() || Triple.isMacOSX()) {
55     if (Triple.getArch() != llvm::Triple::x86_64) {
56       D.Diag(diag::err_drv_clang_unsupported)
57           << (std::string(XRayInstrumentOption) + " on " + Triple.str());
58     }
59   } else if (Triple.getOS() == llvm::Triple::Fuchsia) {
60     switch (Triple.getArch()) {
61     case llvm::Triple::x86_64:
62     case llvm::Triple::aarch64:
63       break;
64     default:
65       D.Diag(diag::err_drv_clang_unsupported)
66           << (std::string(XRayInstrumentOption) + " on " + Triple.str());
67     }
68   } else {
69     D.Diag(diag::err_drv_clang_unsupported)
70         << (std::string(XRayInstrumentOption) + " on " + Triple.str());
71   }
72 
73   // Both XRay and -fpatchable-function-entry use
74   // TargetOpcode::PATCHABLE_FUNCTION_ENTER.
75   if (Arg *A = Args.getLastArg(options::OPT_fpatchable_function_entry_EQ))
76     D.Diag(diag::err_drv_argument_not_allowed_with)
77         << "-fxray-instrument" << A->getSpelling();
78 
79   XRayInstrument = true;
80   if (const Arg *A =
81           Args.getLastArg(options::OPT_fxray_instruction_threshold_,
82                           options::OPT_fxray_instruction_threshold_EQ)) {
83     StringRef S = A->getValue();
84     if (S.getAsInteger(0, InstructionThreshold) || InstructionThreshold < 0)
85       D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
86   }
87 
88   // By default, the back-end will not emit the lowering for XRay customevent
89   // calls if the function is not instrumented. In the future we will change
90   // this default to be the reverse, but in the meantime we're going to
91   // introduce the new functionality behind a flag.
92   if (Args.hasFlag(options::OPT_fxray_always_emit_customevents,
93                    options::OPT_fno_xray_always_emit_customevents, false))
94     XRayAlwaysEmitCustomEvents = true;
95 
96   if (Args.hasFlag(options::OPT_fxray_always_emit_typedevents,
97                    options::OPT_fno_xray_always_emit_typedevents, false))
98     XRayAlwaysEmitTypedEvents = true;
99 
100   if (!Args.hasFlag(options::OPT_fxray_link_deps,
101                     options::OPT_fnoxray_link_deps, true))
102     XRayRT = false;
103 
104   auto Bundles =
105       Args.getAllArgValues(options::OPT_fxray_instrumentation_bundle);
106   if (Bundles.empty())
107     InstrumentationBundle.Mask = XRayInstrKind::All;
108   else
109     for (const auto &B : Bundles) {
110       llvm::SmallVector<StringRef, 2> BundleParts;
111       llvm::SplitString(B, BundleParts, ",");
112       for (const auto &P : BundleParts) {
113         // TODO: Automate the generation of the string case table.
114         auto Valid = llvm::StringSwitch<bool>(P)
115                          .Cases("none", "all", "function", "function-entry",
116                                 "function-exit", "custom", true)
117                          .Default(false);
118 
119         if (!Valid) {
120           D.Diag(clang::diag::err_drv_invalid_value)
121               << "-fxray-instrumentation-bundle=" << P;
122           continue;
123         }
124 
125         auto Mask = parseXRayInstrValue(P);
126         if (Mask == XRayInstrKind::None) {
127           InstrumentationBundle.clear();
128           break;
129         }
130 
131         InstrumentationBundle.Mask |= Mask;
132       }
133     }
134 
135   // Validate the always/never attribute files. We also make sure that they
136   // are treated as actual dependencies.
137   for (const auto &Filename :
138        Args.getAllArgValues(options::OPT_fxray_always_instrument)) {
139     if (D.getVFS().exists(Filename)) {
140       AlwaysInstrumentFiles.push_back(Filename);
141       ExtraDeps.push_back(Filename);
142     } else
143       D.Diag(clang::diag::err_drv_no_such_file) << Filename;
144   }
145 
146   for (const auto &Filename :
147        Args.getAllArgValues(options::OPT_fxray_never_instrument)) {
148     if (D.getVFS().exists(Filename)) {
149       NeverInstrumentFiles.push_back(Filename);
150       ExtraDeps.push_back(Filename);
151     } else
152       D.Diag(clang::diag::err_drv_no_such_file) << Filename;
153   }
154 
155   for (const auto &Filename :
156        Args.getAllArgValues(options::OPT_fxray_attr_list)) {
157     if (D.getVFS().exists(Filename)) {
158       AttrListFiles.push_back(Filename);
159       ExtraDeps.push_back(Filename);
160     } else
161       D.Diag(clang::diag::err_drv_no_such_file) << Filename;
162   }
163 
164   // Get the list of modes we want to support.
165   auto SpecifiedModes = Args.getAllArgValues(options::OPT_fxray_modes);
166   if (SpecifiedModes.empty())
167     llvm::copy(XRaySupportedModes, std::back_inserter(Modes));
168   else
169     for (const auto &Arg : SpecifiedModes) {
170       // Parse CSV values for -fxray-modes=...
171       llvm::SmallVector<StringRef, 2> ModeParts;
172       llvm::SplitString(Arg, ModeParts, ",");
173       for (const auto &M : ModeParts)
174         if (M == "none")
175           Modes.clear();
176         else if (M == "all")
177           llvm::copy(XRaySupportedModes, std::back_inserter(Modes));
178         else
179           Modes.push_back(std::string(M));
180     }
181 
182   // Then we want to sort and unique the modes we've collected.
183   llvm::sort(Modes);
184   Modes.erase(std::unique(Modes.begin(), Modes.end()), Modes.end());
185 }
186 
187 void XRayArgs::addArgs(const ToolChain &TC, const ArgList &Args,
188                        ArgStringList &CmdArgs, types::ID InputType) const {
189   if (!XRayInstrument)
190     return;
191 
192   CmdArgs.push_back(XRayInstrumentOption);
193 
194   if (XRayAlwaysEmitCustomEvents)
195     CmdArgs.push_back("-fxray-always-emit-customevents");
196 
197   if (XRayAlwaysEmitTypedEvents)
198     CmdArgs.push_back("-fxray-always-emit-typedevents");
199 
200   CmdArgs.push_back(Args.MakeArgString(Twine(XRayInstructionThresholdOption) +
201                                        Twine(InstructionThreshold)));
202 
203   for (const auto &Always : AlwaysInstrumentFiles) {
204     SmallString<64> AlwaysInstrumentOpt("-fxray-always-instrument=");
205     AlwaysInstrumentOpt += Always;
206     CmdArgs.push_back(Args.MakeArgString(AlwaysInstrumentOpt));
207   }
208 
209   for (const auto &Never : NeverInstrumentFiles) {
210     SmallString<64> NeverInstrumentOpt("-fxray-never-instrument=");
211     NeverInstrumentOpt += Never;
212     CmdArgs.push_back(Args.MakeArgString(NeverInstrumentOpt));
213   }
214 
215   for (const auto &AttrFile : AttrListFiles) {
216     SmallString<64> AttrListFileOpt("-fxray-attr-list=");
217     AttrListFileOpt += AttrFile;
218     CmdArgs.push_back(Args.MakeArgString(AttrListFileOpt));
219   }
220 
221   for (const auto &Dep : ExtraDeps) {
222     SmallString<64> ExtraDepOpt("-fdepfile-entry=");
223     ExtraDepOpt += Dep;
224     CmdArgs.push_back(Args.MakeArgString(ExtraDepOpt));
225   }
226 
227   for (const auto &Mode : Modes) {
228     SmallString<64> ModeOpt("-fxray-modes=");
229     ModeOpt += Mode;
230     CmdArgs.push_back(Args.MakeArgString(ModeOpt));
231   }
232 
233   SmallString<64> Bundle("-fxray-instrumentation-bundle=");
234   if (InstrumentationBundle.full()) {
235     Bundle += "all";
236   } else if (InstrumentationBundle.empty()) {
237     Bundle += "none";
238   } else {
239     if (InstrumentationBundle.has(XRayInstrKind::FunctionEntry) &&
240         InstrumentationBundle.has(XRayInstrKind::FunctionExit))
241       Bundle += "function";
242     else if (InstrumentationBundle.has(XRayInstrKind::FunctionEntry))
243       Bundle += "function-entry";
244     else if (InstrumentationBundle.has(XRayInstrKind::FunctionExit))
245       Bundle += "function-exit";
246 
247     if (InstrumentationBundle.has(XRayInstrKind::Custom))
248       Bundle += "custom";
249     if (InstrumentationBundle.has(XRayInstrKind::Typed))
250       Bundle += "typed";
251   }
252   CmdArgs.push_back(Args.MakeArgString(Bundle));
253 }
254