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 21 using namespace clang; 22 using namespace clang::driver; 23 using namespace llvm::opt; 24 25 constexpr const char *XRaySupportedModes[] = {"xray-fdr", "xray-basic"}; 26 27 XRayArgs::XRayArgs(const ToolChain &TC, const ArgList &Args) { 28 const Driver &D = TC.getDriver(); 29 const llvm::Triple &Triple = TC.getTriple(); 30 if (!Args.hasFlag(options::OPT_fxray_instrument, 31 options::OPT_fno_xray_instrument, false)) 32 return; 33 XRayInstrument = Args.getLastArg(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(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(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 // Both XRay and -fpatchable-function-entry use 67 // TargetOpcode::PATCHABLE_FUNCTION_ENTER. 68 if (Arg *A = Args.getLastArg(options::OPT_fpatchable_function_entry_EQ)) 69 D.Diag(diag::err_drv_argument_not_allowed_with) 70 << XRayInstrument->getSpelling() << A->getSpelling(); 71 72 if (!Args.hasFlag(options::OPT_fxray_link_deps, 73 options::OPT_fno_xray_link_deps, true)) 74 XRayRT = false; 75 76 auto Bundles = 77 Args.getAllArgValues(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(B, BundleParts, ","); 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("none", "all", "function", "function-entry", 88 "function-exit", "custom", true) 89 .Default(false); 90 91 if (!Valid) { 92 D.Diag(clang::diag::err_drv_invalid_value) 93 << "-fxray-instrumentation-bundle=" << P; 94 continue; 95 } 96 97 auto Mask = parseXRayInstrValue(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(options::OPT_fxray_always_instrument)) { 111 if (D.getVFS().exists(Filename)) { 112 AlwaysInstrumentFiles.push_back(Filename); 113 ExtraDeps.push_back(Filename); 114 } else 115 D.Diag(clang::diag::err_drv_no_such_file) << Filename; 116 } 117 118 for (const auto &Filename : 119 Args.getAllArgValues(options::OPT_fxray_never_instrument)) { 120 if (D.getVFS().exists(Filename)) { 121 NeverInstrumentFiles.push_back(Filename); 122 ExtraDeps.push_back(Filename); 123 } else 124 D.Diag(clang::diag::err_drv_no_such_file) << Filename; 125 } 126 127 for (const auto &Filename : 128 Args.getAllArgValues(options::OPT_fxray_attr_list)) { 129 if (D.getVFS().exists(Filename)) { 130 AttrListFiles.push_back(Filename); 131 ExtraDeps.push_back(Filename); 132 } else 133 D.Diag(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(options::OPT_fxray_modes); 138 if (SpecifiedModes.empty()) 139 llvm::copy(XRaySupportedModes, std::back_inserter(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(Arg, ModeParts, ","); 145 for (const auto &M : ModeParts) 146 if (M == "none") 147 Modes.clear(); 148 else if (M == "all") 149 llvm::copy(XRaySupportedModes, std::back_inserter(Modes)); 150 else 151 Modes.push_back(std::string(M)); 152 } 153 154 // Then we want to sort and unique the modes we've collected. 155 llvm::sort(Modes); 156 Modes.erase(std::unique(Modes.begin(), Modes.end()), Modes.end()); 157 } 158 159 void 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, 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(CmdArgs, options::OPT_fxray_always_emit_customevents, 171 options::OPT_fno_xray_always_emit_customevents); 172 173 Args.addOptInFlag(CmdArgs, options::OPT_fxray_always_emit_typedevents, 174 options::OPT_fno_xray_always_emit_typedevents); 175 Args.addOptInFlag(CmdArgs, options::OPT_fxray_ignore_loops, 176 options::OPT_fno_xray_ignore_loops); 177 Args.addOptOutFlag(CmdArgs, options::OPT_fxray_function_index, 178 options::OPT_fno_xray_function_index); 179 180 if (const Arg *A = 181 Args.getLastArg(options::OPT_fxray_instruction_threshold_EQ)) { 182 int Value; 183 StringRef S = A->getValue(); 184 if (S.getAsInteger(0, Value) || Value < 0) 185 D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S; 186 else 187 A->render(Args, CmdArgs); 188 } 189 190 int XRayFunctionGroups = 1; 191 int XRaySelectedFunctionGroup = 0; 192 if (const Arg *A = Args.getLastArg(options::OPT_fxray_function_groups)) { 193 StringRef S = A->getValue(); 194 if (S.getAsInteger(0, XRayFunctionGroups) || XRayFunctionGroups < 1) 195 D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S; 196 if (XRayFunctionGroups > 1) 197 A->render(Args, CmdArgs); 198 } 199 if (const Arg *A = 200 Args.getLastArg(options::OPT_fxray_selected_function_group)) { 201 StringRef S = A->getValue(); 202 if (S.getAsInteger(0, XRaySelectedFunctionGroup) || 203 XRaySelectedFunctionGroup < 0 || 204 XRaySelectedFunctionGroup >= XRayFunctionGroups) 205 D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S; 206 if (XRaySelectedFunctionGroup != 0) 207 A->render(Args, CmdArgs); 208 } 209 210 for (const auto &Always : AlwaysInstrumentFiles) { 211 SmallString<64> AlwaysInstrumentOpt("-fxray-always-instrument="); 212 AlwaysInstrumentOpt += Always; 213 CmdArgs.push_back(Args.MakeArgString(AlwaysInstrumentOpt)); 214 } 215 216 for (const auto &Never : NeverInstrumentFiles) { 217 SmallString<64> NeverInstrumentOpt("-fxray-never-instrument="); 218 NeverInstrumentOpt += Never; 219 CmdArgs.push_back(Args.MakeArgString(NeverInstrumentOpt)); 220 } 221 222 for (const auto &AttrFile : AttrListFiles) { 223 SmallString<64> AttrListFileOpt("-fxray-attr-list="); 224 AttrListFileOpt += AttrFile; 225 CmdArgs.push_back(Args.MakeArgString(AttrListFileOpt)); 226 } 227 228 for (const auto &Dep : ExtraDeps) { 229 SmallString<64> ExtraDepOpt("-fdepfile-entry="); 230 ExtraDepOpt += Dep; 231 CmdArgs.push_back(Args.MakeArgString(ExtraDepOpt)); 232 } 233 234 for (const auto &Mode : Modes) { 235 SmallString<64> ModeOpt("-fxray-modes="); 236 ModeOpt += Mode; 237 CmdArgs.push_back(Args.MakeArgString(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(XRayInstrKind::FunctionEntry) && 247 InstrumentationBundle.has(XRayInstrKind::FunctionExit)) 248 Bundle += "function"; 249 else if (InstrumentationBundle.has(XRayInstrKind::FunctionEntry)) 250 Bundle += "function-entry"; 251 else if (InstrumentationBundle.has(XRayInstrKind::FunctionExit)) 252 Bundle += "function-exit"; 253 254 if (InstrumentationBundle.has(XRayInstrKind::Custom)) 255 Bundle += "custom"; 256 if (InstrumentationBundle.has(XRayInstrKind::Typed)) 257 Bundle += "typed"; 258 } 259 CmdArgs.push_back(Args.MakeArgString(Bundle)); 260 } 261