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_fnoxray_instrument, false)) {
37     if (Triple.getOS() == llvm::Triple::Linux) {
38       switch (Triple.getArch()) {
39       case llvm::Triple::x86_64:
40       case llvm::Triple::arm:
41       case llvm::Triple::aarch64:
42       case llvm::Triple::ppc64le:
43       case llvm::Triple::mips:
44       case llvm::Triple::mipsel:
45       case llvm::Triple::mips64:
46       case llvm::Triple::mips64el:
47         break;
48       default:
49         D.Diag(diag::err_drv_clang_unsupported)
50             << (std::string(XRayInstrumentOption) + " on " + Triple.str());
51       }
52     } else if (Triple.isOSFreeBSD() ||
53                Triple.isOSOpenBSD() ||
54                Triple.isOSNetBSD() ||
55                Triple.isMacOSX()) {
56       if (Triple.getArch() != llvm::Triple::x86_64) {
57         D.Diag(diag::err_drv_clang_unsupported)
58             << (std::string(XRayInstrumentOption) + " on " + Triple.str());
59       }
60     } else if (Triple.getOS() == llvm::Triple::Fuchsia) {
61       switch (Triple.getArch()) {
62       case llvm::Triple::x86_64:
63       case llvm::Triple::aarch64:
64         break;
65       default:
66         D.Diag(diag::err_drv_clang_unsupported)
67             << (std::string(XRayInstrumentOption) + " on " + Triple.str());
68       }
69     } else {
70       D.Diag(diag::err_drv_clang_unsupported)
71           << (std::string(XRayInstrumentOption) + " on " + Triple.str());
72     }
73 
74     // Both XRay and -fpatchable-function-entry use
75     // TargetOpcode::PATCHABLE_FUNCTION_ENTER.
76     if (Arg *A = Args.getLastArg(options::OPT_fpatchable_function_entry_EQ))
77       D.Diag(diag::err_drv_argument_not_allowed_with)
78           << "-fxray-instrument" << A->getSpelling();
79 
80     XRayInstrument = true;
81     if (const Arg *A =
82             Args.getLastArg(options::OPT_fxray_instruction_threshold_,
83                             options::OPT_fxray_instruction_threshold_EQ)) {
84       StringRef S = A->getValue();
85       if (S.getAsInteger(0, InstructionThreshold) || InstructionThreshold < 0)
86         D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
87     }
88 
89     // By default, the back-end will not emit the lowering for XRay customevent
90     // calls if the function is not instrumented. In the future we will change
91     // this default to be the reverse, but in the meantime we're going to
92     // introduce the new functionality behind a flag.
93     if (Args.hasFlag(options::OPT_fxray_always_emit_customevents,
94                      options::OPT_fnoxray_always_emit_customevents, false))
95       XRayAlwaysEmitCustomEvents = true;
96 
97     if (Args.hasFlag(options::OPT_fxray_always_emit_typedevents,
98                      options::OPT_fnoxray_always_emit_typedevents, false))
99       XRayAlwaysEmitTypedEvents = true;
100 
101     if (!Args.hasFlag(options::OPT_fxray_link_deps,
102                       options::OPT_fnoxray_link_deps, true))
103       XRayRT = false;
104 
105     auto Bundles =
106         Args.getAllArgValues(options::OPT_fxray_instrumentation_bundle);
107     if (Bundles.empty())
108       InstrumentationBundle.Mask = XRayInstrKind::All;
109     else
110       for (const auto &B : Bundles) {
111         llvm::SmallVector<StringRef, 2> BundleParts;
112         llvm::SplitString(B, BundleParts, ",");
113         for (const auto &P : BundleParts) {
114           // TODO: Automate the generation of the string case table.
115           auto Valid = llvm::StringSwitch<bool>(P)
116                            .Cases("none", "all", "function", "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(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 
188 void XRayArgs::addArgs(const ToolChain &TC, const ArgList &Args,
189                        ArgStringList &CmdArgs, types::ID InputType) const {
190   if (!XRayInstrument)
191     return;
192 
193   CmdArgs.push_back(XRayInstrumentOption);
194 
195   if (XRayAlwaysEmitCustomEvents)
196     CmdArgs.push_back("-fxray-always-emit-customevents");
197 
198   if (XRayAlwaysEmitTypedEvents)
199     CmdArgs.push_back("-fxray-always-emit-typedevents");
200 
201   CmdArgs.push_back(Args.MakeArgString(Twine(XRayInstructionThresholdOption) +
202                                        Twine(InstructionThreshold)));
203 
204   for (const auto &Always : AlwaysInstrumentFiles) {
205     SmallString<64> AlwaysInstrumentOpt("-fxray-always-instrument=");
206     AlwaysInstrumentOpt += Always;
207     CmdArgs.push_back(Args.MakeArgString(AlwaysInstrumentOpt));
208   }
209 
210   for (const auto &Never : NeverInstrumentFiles) {
211     SmallString<64> NeverInstrumentOpt("-fxray-never-instrument=");
212     NeverInstrumentOpt += Never;
213     CmdArgs.push_back(Args.MakeArgString(NeverInstrumentOpt));
214   }
215 
216   for (const auto &AttrFile : AttrListFiles) {
217     SmallString<64> AttrListFileOpt("-fxray-attr-list=");
218     AttrListFileOpt += AttrFile;
219     CmdArgs.push_back(Args.MakeArgString(AttrListFileOpt));
220   }
221 
222   for (const auto &Dep : ExtraDeps) {
223     SmallString<64> ExtraDepOpt("-fdepfile-entry=");
224     ExtraDepOpt += Dep;
225     CmdArgs.push_back(Args.MakeArgString(ExtraDepOpt));
226   }
227 
228   for (const auto &Mode : Modes) {
229     SmallString<64> ModeOpt("-fxray-modes=");
230     ModeOpt += Mode;
231     CmdArgs.push_back(Args.MakeArgString(ModeOpt));
232   }
233 
234   SmallString<64> Bundle("-fxray-instrumentation-bundle=");
235   if (InstrumentationBundle.full()) {
236     Bundle += "all";
237   } else if (InstrumentationBundle.empty()) {
238     Bundle += "none";
239   } else {
240     if (InstrumentationBundle.has(XRayInstrKind::Function))
241       Bundle += "function";
242     if (InstrumentationBundle.has(XRayInstrKind::Custom))
243       Bundle += "custom";
244     if (InstrumentationBundle.has(XRayInstrKind::Typed))
245       Bundle += "typed";
246   }
247   CmdArgs.push_back(Args.MakeArgString(Bundle));
248 }
249