1 //===--- XRayArgs.cpp - Arguments for XRay --------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 #include "clang/Driver/XRayArgs.h"
10 #include "ToolChains/CommonArgs.h"
11 #include "clang/Driver/Driver.h"
12 #include "clang/Driver/DriverDiagnostic.h"
13 #include "clang/Driver/Options.h"
14 #include "clang/Driver/ToolChain.h"
15 #include "llvm/ADT/StringExtras.h"
16 #include "llvm/ADT/StringSwitch.h"
17 #include "llvm/Support/FileSystem.h"
18 #include "llvm/Support/Path.h"
19 #include "llvm/Support/ScopedPrinter.h"
20 #include "llvm/Support/SpecialCaseList.h"
21 
22 using namespace clang;
23 using namespace clang::driver;
24 using namespace llvm::opt;
25 
26 namespace {
27 constexpr char XRayInstrumentOption[] = "-fxray-instrument";
28 constexpr char XRayInstructionThresholdOption[] =
29     "-fxray-instruction-threshold=";
30 constexpr const char *const XRaySupportedModes[] = {"xray-fdr", "xray-basic"};
31 } // namespace
32 
33 XRayArgs::XRayArgs(const ToolChain &TC, const ArgList &Args) {
34   const Driver &D = TC.getDriver();
35   const llvm::Triple &Triple = TC.getTriple();
36   if (Args.hasFlag(options::OPT_fxray_instrument,
37                    options::OPT_fnoxray_instrument, false)) {
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.getOS() == llvm::Triple::FreeBSD ||
54                Triple.getOS() == llvm::Triple::OpenBSD) {
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 {
60       D.Diag(diag::err_drv_clang_unsupported)
61           << (std::string(XRayInstrumentOption) + " on " + Triple.str());
62     }
63     XRayInstrument = true;
64     if (const Arg *A =
65             Args.getLastArg(options::OPT_fxray_instruction_threshold_,
66                             options::OPT_fxray_instruction_threshold_EQ)) {
67       StringRef S = A->getValue();
68       if (S.getAsInteger(0, InstructionThreshold) || InstructionThreshold < 0)
69         D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
70     }
71 
72     // By default, the back-end will not emit the lowering for XRay customevent
73     // calls if the function is not instrumented. In the future we will change
74     // this default to be the reverse, but in the meantime we're going to
75     // introduce the new functionality behind a flag.
76     if (Args.hasFlag(options::OPT_fxray_always_emit_customevents,
77                      options::OPT_fnoxray_always_emit_customevents, false))
78       XRayAlwaysEmitCustomEvents = true;
79 
80     if (Args.hasFlag(options::OPT_fxray_always_emit_typedevents,
81                      options::OPT_fnoxray_always_emit_typedevents, false))
82       XRayAlwaysEmitTypedEvents = true;
83 
84     if (!Args.hasFlag(options::OPT_fxray_link_deps,
85                       options::OPT_fnoxray_link_deps, true))
86       XRayRT = false;
87 
88     auto Bundles =
89         Args.getAllArgValues(options::OPT_fxray_instrumentation_bundle);
90     if (Bundles.empty())
91       InstrumentationBundle.Mask = XRayInstrKind::All;
92     else
93       for (const auto &B : Bundles) {
94         llvm::SmallVector<StringRef, 2> BundleParts;
95         llvm::SplitString(B, BundleParts, ",");
96         for (const auto &P : BundleParts) {
97           // TODO: Automate the generation of the string case table.
98           auto Valid = llvm::StringSwitch<bool>(P)
99                            .Cases("none", "all", "function", "custom", true)
100                            .Default(false);
101 
102           if (!Valid) {
103             D.Diag(clang::diag::err_drv_invalid_value)
104                 << "-fxray-instrumentation-bundle=" << P;
105             continue;
106           }
107 
108           auto Mask = parseXRayInstrValue(P);
109           if (Mask == XRayInstrKind::None) {
110             InstrumentationBundle.clear();
111             break;
112           }
113 
114           InstrumentationBundle.Mask |= Mask;
115         }
116       }
117 
118     // Validate the always/never attribute files. We also make sure that they
119     // are treated as actual dependencies.
120     for (const auto &Filename :
121          Args.getAllArgValues(options::OPT_fxray_always_instrument)) {
122       if (llvm::sys::fs::exists(Filename)) {
123         AlwaysInstrumentFiles.push_back(Filename);
124         ExtraDeps.push_back(Filename);
125       } else
126         D.Diag(clang::diag::err_drv_no_such_file) << Filename;
127     }
128 
129     for (const auto &Filename :
130          Args.getAllArgValues(options::OPT_fxray_never_instrument)) {
131       if (llvm::sys::fs::exists(Filename)) {
132         NeverInstrumentFiles.push_back(Filename);
133         ExtraDeps.push_back(Filename);
134       } else
135         D.Diag(clang::diag::err_drv_no_such_file) << Filename;
136     }
137 
138     for (const auto &Filename :
139          Args.getAllArgValues(options::OPT_fxray_attr_list)) {
140       if (llvm::sys::fs::exists(Filename)) {
141         AttrListFiles.push_back(Filename);
142         ExtraDeps.push_back(Filename);
143       } else
144         D.Diag(clang::diag::err_drv_no_such_file) << Filename;
145     }
146 
147     // Get the list of modes we want to support.
148     auto SpecifiedModes = Args.getAllArgValues(options::OPT_fxray_modes);
149     if (SpecifiedModes.empty())
150       llvm::copy(XRaySupportedModes, std::back_inserter(Modes));
151     else
152       for (const auto &Arg : SpecifiedModes) {
153         // Parse CSV values for -fxray-modes=...
154         llvm::SmallVector<StringRef, 2> ModeParts;
155         llvm::SplitString(Arg, ModeParts, ",");
156         for (const auto &M : ModeParts)
157           if (M == "none")
158             Modes.clear();
159           else if (M == "all")
160             llvm::copy(XRaySupportedModes, std::back_inserter(Modes));
161           else
162             Modes.push_back(M);
163       }
164 
165     // Then we want to sort and unique the modes we've collected.
166     llvm::sort(Modes.begin(), Modes.end());
167     Modes.erase(std::unique(Modes.begin(), Modes.end()), Modes.end());
168   }
169 }
170 
171 void XRayArgs::addArgs(const ToolChain &TC, const ArgList &Args,
172                        ArgStringList &CmdArgs, types::ID InputType) const {
173   if (!XRayInstrument)
174     return;
175 
176   CmdArgs.push_back(XRayInstrumentOption);
177 
178   if (XRayAlwaysEmitCustomEvents)
179     CmdArgs.push_back("-fxray-always-emit-customevents");
180 
181   if (XRayAlwaysEmitTypedEvents)
182     CmdArgs.push_back("-fxray-always-emit-typedevents");
183 
184   CmdArgs.push_back(Args.MakeArgString(Twine(XRayInstructionThresholdOption) +
185                                        Twine(InstructionThreshold)));
186 
187   for (const auto &Always : AlwaysInstrumentFiles) {
188     SmallString<64> AlwaysInstrumentOpt("-fxray-always-instrument=");
189     AlwaysInstrumentOpt += Always;
190     CmdArgs.push_back(Args.MakeArgString(AlwaysInstrumentOpt));
191   }
192 
193   for (const auto &Never : NeverInstrumentFiles) {
194     SmallString<64> NeverInstrumentOpt("-fxray-never-instrument=");
195     NeverInstrumentOpt += Never;
196     CmdArgs.push_back(Args.MakeArgString(NeverInstrumentOpt));
197   }
198 
199   for (const auto &AttrFile : AttrListFiles) {
200     SmallString<64> AttrListFileOpt("-fxray-attr-list=");
201     AttrListFileOpt += AttrFile;
202     CmdArgs.push_back(Args.MakeArgString(AttrListFileOpt));
203   }
204 
205   for (const auto &Dep : ExtraDeps) {
206     SmallString<64> ExtraDepOpt("-fdepfile-entry=");
207     ExtraDepOpt += Dep;
208     CmdArgs.push_back(Args.MakeArgString(ExtraDepOpt));
209   }
210 
211   for (const auto &Mode : Modes) {
212     SmallString<64> ModeOpt("-fxray-modes=");
213     ModeOpt += Mode;
214     CmdArgs.push_back(Args.MakeArgString(ModeOpt));
215   }
216 }
217