//===--- XRayArgs.cpp - Arguments for XRay --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/Driver/XRayArgs.h" #include "ToolChains/CommonArgs.h" #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" #include "clang/Driver/ToolChain.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/SpecialCaseList.h" #include "llvm/Support/VirtualFileSystem.h" using namespace clang; using namespace clang::driver; using namespace llvm::opt; namespace { constexpr char XRayInstrumentOption[] = "-fxray-instrument"; constexpr char XRayInstructionThresholdOption[] = "-fxray-instruction-threshold="; constexpr const char *const XRaySupportedModes[] = {"xray-fdr", "xray-basic"}; } // namespace XRayArgs::XRayArgs(const ToolChain &TC, const ArgList &Args) { const Driver &D = TC.getDriver(); const llvm::Triple &Triple = TC.getTriple(); if (!Args.hasFlag(options::OPT_fxray_instrument, options::OPT_fno_xray_instrument, false)) return; if (Triple.getOS() == llvm::Triple::Linux) { switch (Triple.getArch()) { case llvm::Triple::x86_64: case llvm::Triple::arm: case llvm::Triple::aarch64: case llvm::Triple::hexagon: case llvm::Triple::ppc64le: case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: break; default: D.Diag(diag::err_drv_clang_unsupported) << (std::string(XRayInstrumentOption) + " on " + Triple.str()); } } else if (Triple.isOSFreeBSD() || Triple.isOSOpenBSD() || Triple.isOSNetBSD() || Triple.isMacOSX()) { if (Triple.getArch() != llvm::Triple::x86_64) { D.Diag(diag::err_drv_clang_unsupported) << (std::string(XRayInstrumentOption) + " on " + Triple.str()); } } else if (Triple.getOS() == llvm::Triple::Fuchsia) { switch (Triple.getArch()) { case llvm::Triple::x86_64: case llvm::Triple::aarch64: break; default: D.Diag(diag::err_drv_clang_unsupported) << (std::string(XRayInstrumentOption) + " on " + Triple.str()); } } else { D.Diag(diag::err_drv_clang_unsupported) << (std::string(XRayInstrumentOption) + " on " + Triple.str()); } // Both XRay and -fpatchable-function-entry use // TargetOpcode::PATCHABLE_FUNCTION_ENTER. if (Arg *A = Args.getLastArg(options::OPT_fpatchable_function_entry_EQ)) D.Diag(diag::err_drv_argument_not_allowed_with) << "-fxray-instrument" << A->getSpelling(); XRayInstrument = true; if (const Arg *A = Args.getLastArg(options::OPT_fxray_instruction_threshold_, options::OPT_fxray_instruction_threshold_EQ)) { StringRef S = A->getValue(); if (S.getAsInteger(0, InstructionThreshold) || InstructionThreshold < 0) D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S; } // By default, the back-end will not emit the lowering for XRay customevent // calls if the function is not instrumented. In the future we will change // this default to be the reverse, but in the meantime we're going to // introduce the new functionality behind a flag. if (Args.hasFlag(options::OPT_fxray_always_emit_customevents, options::OPT_fno_xray_always_emit_customevents, false)) XRayAlwaysEmitCustomEvents = true; if (Args.hasFlag(options::OPT_fxray_always_emit_typedevents, options::OPT_fno_xray_always_emit_typedevents, false)) XRayAlwaysEmitTypedEvents = true; if (!Args.hasFlag(options::OPT_fxray_link_deps, options::OPT_fnoxray_link_deps, true)) XRayRT = false; if (Args.hasFlag(options::OPT_fxray_ignore_loops, options::OPT_fno_xray_ignore_loops, false)) XRayIgnoreLoops = true; XRayFunctionIndex = Args.hasFlag(options::OPT_fxray_function_index, options::OPT_fno_xray_function_index, true); auto Bundles = Args.getAllArgValues(options::OPT_fxray_instrumentation_bundle); if (Bundles.empty()) InstrumentationBundle.Mask = XRayInstrKind::All; else for (const auto &B : Bundles) { llvm::SmallVector BundleParts; llvm::SplitString(B, BundleParts, ","); for (const auto &P : BundleParts) { // TODO: Automate the generation of the string case table. auto Valid = llvm::StringSwitch(P) .Cases("none", "all", "function", "function-entry", "function-exit", "custom", true) .Default(false); if (!Valid) { D.Diag(clang::diag::err_drv_invalid_value) << "-fxray-instrumentation-bundle=" << P; continue; } auto Mask = parseXRayInstrValue(P); if (Mask == XRayInstrKind::None) { InstrumentationBundle.clear(); break; } InstrumentationBundle.Mask |= Mask; } } // Validate the always/never attribute files. We also make sure that they // are treated as actual dependencies. for (const auto &Filename : Args.getAllArgValues(options::OPT_fxray_always_instrument)) { if (D.getVFS().exists(Filename)) { AlwaysInstrumentFiles.push_back(Filename); ExtraDeps.push_back(Filename); } else D.Diag(clang::diag::err_drv_no_such_file) << Filename; } for (const auto &Filename : Args.getAllArgValues(options::OPT_fxray_never_instrument)) { if (D.getVFS().exists(Filename)) { NeverInstrumentFiles.push_back(Filename); ExtraDeps.push_back(Filename); } else D.Diag(clang::diag::err_drv_no_such_file) << Filename; } for (const auto &Filename : Args.getAllArgValues(options::OPT_fxray_attr_list)) { if (D.getVFS().exists(Filename)) { AttrListFiles.push_back(Filename); ExtraDeps.push_back(Filename); } else D.Diag(clang::diag::err_drv_no_such_file) << Filename; } // Get the list of modes we want to support. auto SpecifiedModes = Args.getAllArgValues(options::OPT_fxray_modes); if (SpecifiedModes.empty()) llvm::copy(XRaySupportedModes, std::back_inserter(Modes)); else for (const auto &Arg : SpecifiedModes) { // Parse CSV values for -fxray-modes=... llvm::SmallVector ModeParts; llvm::SplitString(Arg, ModeParts, ","); for (const auto &M : ModeParts) if (M == "none") Modes.clear(); else if (M == "all") llvm::copy(XRaySupportedModes, std::back_inserter(Modes)); else Modes.push_back(std::string(M)); } if (const Arg *A = Args.getLastArg(options::OPT_fxray_function_groups)) { StringRef S = A->getValue(); if (S.getAsInteger(0, XRayFunctionGroups) || XRayFunctionGroups < 1) D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S; } if (const Arg *A = Args.getLastArg(options::OPT_fxray_selected_function_group)) { StringRef S = A->getValue(); if (S.getAsInteger(0, XRaySelectedFunctionGroup) || XRaySelectedFunctionGroup < 0 || XRaySelectedFunctionGroup >= XRayFunctionGroups) D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S; } // Then we want to sort and unique the modes we've collected. llvm::sort(Modes); Modes.erase(std::unique(Modes.begin(), Modes.end()), Modes.end()); } void XRayArgs::addArgs(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs, types::ID InputType) const { if (!XRayInstrument) return; CmdArgs.push_back(XRayInstrumentOption); if (XRayAlwaysEmitCustomEvents) CmdArgs.push_back("-fxray-always-emit-customevents"); if (XRayAlwaysEmitTypedEvents) CmdArgs.push_back("-fxray-always-emit-typedevents"); if (XRayIgnoreLoops) CmdArgs.push_back("-fxray-ignore-loops"); if (!XRayFunctionIndex) CmdArgs.push_back("-fno-xray-function-index"); if (XRayFunctionGroups > 1) { CmdArgs.push_back(Args.MakeArgString(Twine("-fxray-function-groups=") + Twine(XRayFunctionGroups))); } if (XRaySelectedFunctionGroup != 0) { CmdArgs.push_back( Args.MakeArgString(Twine("-fxray-selected-function-group=") + Twine(XRaySelectedFunctionGroup))); } CmdArgs.push_back(Args.MakeArgString(Twine(XRayInstructionThresholdOption) + Twine(InstructionThreshold))); for (const auto &Always : AlwaysInstrumentFiles) { SmallString<64> AlwaysInstrumentOpt("-fxray-always-instrument="); AlwaysInstrumentOpt += Always; CmdArgs.push_back(Args.MakeArgString(AlwaysInstrumentOpt)); } for (const auto &Never : NeverInstrumentFiles) { SmallString<64> NeverInstrumentOpt("-fxray-never-instrument="); NeverInstrumentOpt += Never; CmdArgs.push_back(Args.MakeArgString(NeverInstrumentOpt)); } for (const auto &AttrFile : AttrListFiles) { SmallString<64> AttrListFileOpt("-fxray-attr-list="); AttrListFileOpt += AttrFile; CmdArgs.push_back(Args.MakeArgString(AttrListFileOpt)); } for (const auto &Dep : ExtraDeps) { SmallString<64> ExtraDepOpt("-fdepfile-entry="); ExtraDepOpt += Dep; CmdArgs.push_back(Args.MakeArgString(ExtraDepOpt)); } for (const auto &Mode : Modes) { SmallString<64> ModeOpt("-fxray-modes="); ModeOpt += Mode; CmdArgs.push_back(Args.MakeArgString(ModeOpt)); } SmallString<64> Bundle("-fxray-instrumentation-bundle="); if (InstrumentationBundle.full()) { Bundle += "all"; } else if (InstrumentationBundle.empty()) { Bundle += "none"; } else { if (InstrumentationBundle.has(XRayInstrKind::FunctionEntry) && InstrumentationBundle.has(XRayInstrKind::FunctionExit)) Bundle += "function"; else if (InstrumentationBundle.has(XRayInstrKind::FunctionEntry)) Bundle += "function-entry"; else if (InstrumentationBundle.has(XRayInstrKind::FunctionExit)) Bundle += "function-exit"; if (InstrumentationBundle.has(XRayInstrKind::Custom)) Bundle += "custom"; if (InstrumentationBundle.has(XRayInstrKind::Typed)) Bundle += "typed"; } CmdArgs.push_back(Args.MakeArgString(Bundle)); }