aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Sala <salapenades1@llnl.gov>2025-06-10 12:03:23 -0700
committerKevin Sala <salapenades1@llnl.gov>2025-07-06 22:01:14 -0700
commitee7e77f8a9a858a88697e292cf569d29bf4c8619 (patch)
treee92e30c221c718b5c49f5fa2ed46b1bd27c06318
parent5acd5abfd92bf1be03960ec885e9b18ab30ee3f3 (diff)
downloadllvm-users/kevinsala/instrumentor-base-pr.zip
llvm-users/kevinsala/instrumentor-base-pr.tar.gz
llvm-users/kevinsala/instrumentor-base-pr.tar.bz2
Fix most review commentsusers/kevinsala/instrumentor-base-pr
-rw-r--r--llvm/include/llvm/Transforms/IPO/Instrumentor.h519
-rw-r--r--llvm/include/llvm/Transforms/IPO/InstrumentorConfigFile.h8
-rw-r--r--llvm/include/llvm/Transforms/IPO/InstrumentorUtils.h172
-rw-r--r--llvm/lib/Transforms/IPO/Instrumentor.cpp287
-rw-r--r--llvm/lib/Transforms/IPO/InstrumentorConfigFile.cpp87
5 files changed, 680 insertions, 393 deletions
diff --git a/llvm/include/llvm/Transforms/IPO/Instrumentor.h b/llvm/include/llvm/Transforms/IPO/Instrumentor.h
index 6fb5a06..26445d2 100644
--- a/llvm/include/llvm/Transforms/IPO/Instrumentor.h
+++ b/llvm/include/llvm/Transforms/IPO/Instrumentor.h
@@ -1,4 +1,4 @@
-//===- Transforms/IPO/Instrumentor.h --------------------------===//
+//===- Transforms/IPO/Instrumentor.h --------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -29,9 +29,9 @@
#include "llvm/Support/Allocator.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/StringSaver.h"
+#include "llvm/Transforms/IPO/InstrumentorUtils.h"
#include "llvm/Transforms/Utils/Instrumentation.h"
-#include <bitset>
#include <cstdint>
#include <functional>
#include <string>
@@ -43,100 +43,17 @@ namespace instrumentor {
struct InstrumentationConfig;
struct InstrumentationOpportunity;
-struct InstrumentorIRBuilderTy {
- InstrumentorIRBuilderTy(Module &M, FunctionAnalysisManager &FAM)
- : M(M), Ctx(M.getContext()), FAM(FAM),
- IRB(Ctx, ConstantFolder(),
- IRBuilderCallbackInserter(
- [&](Instruction *I) { NewInsts[I] = Epoche; })) {}
-
- ~InstrumentorIRBuilderTy() {
- for (auto *I : ToBeErased) {
- if (!I->getType()->isVoidTy())
- I->replaceAllUsesWith(PoisonValue::get(I->getType()));
- I->eraseFromParent();
- }
- }
-
- /// Get a temporary alloca to communicate (large) values with the runtime.
- AllocaInst *getAlloca(Function *Fn, Type *Ty, bool MatchType = false) {
- const DataLayout &DL = Fn->getDataLayout();
- auto *&AllocaList = AllocaMap[{Fn, DL.getTypeAllocSize(Ty)}];
- if (!AllocaList)
- AllocaList = new AllocaListTy;
- AllocaInst *AI = nullptr;
- for (auto *&ListAI : *AllocaList) {
- if (MatchType && ListAI->getAllocatedType() != Ty)
- continue;
- AI = ListAI;
- ListAI = *AllocaList->rbegin();
- break;
- }
- if (AI)
- AllocaList->pop_back();
- else
- AI = new AllocaInst(Ty, DL.getAllocaAddrSpace(), "",
- Fn->getEntryBlock().begin());
- UsedAllocas[AI] = AllocaList;
- return AI;
- }
-
- /// Return the temporary allocas.
- void returnAllocas() {
- for (auto [AI, List] : UsedAllocas)
- List->push_back(AI);
- UsedAllocas.clear();
- }
-
- /// Commonly used values for IR inspection and creation.
- ///{
-
- Module &M;
-
- /// The underying LLVM context.
- LLVMContext &Ctx;
-
- const DataLayout &DL = M.getDataLayout();
-
- Type *VoidTy = Type::getVoidTy(Ctx);
- Type *IntptrTy = M.getDataLayout().getIntPtrType(Ctx);
- PointerType *PtrTy = PointerType::getUnqual(Ctx);
- IntegerType *Int8Ty = Type::getInt8Ty(Ctx);
- IntegerType *Int32Ty = Type::getInt32Ty(Ctx);
- IntegerType *Int64Ty = Type::getInt64Ty(Ctx);
- Constant *NullPtrVal = Constant::getNullValue(PtrTy);
- ///}
-
- /// Mapping to remember temporary allocas for reuse.
- using AllocaListTy = SmallVector<AllocaInst *>;
- DenseMap<std::pair<Function *, unsigned>, AllocaListTy *> AllocaMap;
- DenseMap<AllocaInst *, SmallVector<AllocaInst *> *> UsedAllocas;
-
- void eraseLater(Instruction *I) { ToBeErased.insert(I); }
- SmallPtrSet<Instruction *, 32> ToBeErased;
-
- FunctionAnalysisManager &FAM;
-
- IRBuilder<ConstantFolder, IRBuilderCallbackInserter> IRB;
-
- /// Each instrumentation, i.a., of an instruction, is happening in a dedicated
- /// epoche. The epoche allows to determine if instrumentation instructions
- /// were already around, due to prior instrumentations, or have been
- /// introduced to support the current instrumentation, i.a., compute
- /// information about the current instruction.
- unsigned Epoche = 0;
-
- /// A mapping from instrumentation instructions to the epoche they have been
- /// created.
- DenseMap<Instruction *, unsigned> NewInsts;
-};
-
+/// Callback type for getting/setting a value for a instrumented opportunity.
+///{
using GetterCallbackTy = std::function<Value *(
Value &, Type &, InstrumentationConfig &, InstrumentorIRBuilderTy &)>;
using SetterCallbackTy = std::function<Value *(
Value &, Value &, InstrumentationConfig &, InstrumentorIRBuilderTy &)>;
+///}
+/// Helper to represent an argument to a instrumentation runtime function.
struct IRTArg {
+ /// Flags describing the possible properties of an argument.
enum IRArgFlagTy {
NONE = 0,
STRING = 1 << 0,
@@ -144,10 +61,10 @@ struct IRTArg {
REPLACABLE_CUSTOM = 1 << 2,
POTENTIALLY_INDIRECT = 1 << 3,
INDIRECT_HAS_SIZE = 1 << 4,
-
LAST,
};
+ /// Construct an argument.
IRTArg(Type *Ty, StringRef Name, StringRef Description, unsigned Flags,
GetterCallbackTy GetterCB, SetterCallbackTy SetterCB = nullptr,
bool Enabled = true, bool NoCache = false)
@@ -155,49 +72,85 @@ struct IRTArg {
Flags(Flags), GetterCB(std::move(GetterCB)),
SetterCB(std::move(SetterCB)), NoCache(NoCache) {}
+ /// Whether the argument is enabled and should be passed to the function call.
bool Enabled;
+
+ /// The type of the argument.
Type *Ty;
+
+ /// A string with the name of the argument.
StringRef Name;
+
+ /// A string with the description of the argument.
StringRef Description;
+
+ /// The flags that describe the properties of the argument. Multiple flags may
+ /// be specified.
unsigned Flags;
+
+ /// The callback for getting the value of the argument.
GetterCallbackTy GetterCB;
+
+ /// The callback for consuming the output value of the argument.
SetterCallbackTy SetterCB;
- bool NoCache;
-};
-struct InstrumentationCaches {
- DenseMap<std::tuple<unsigned, StringRef, StringRef>, Value *> DirectArgCache;
- DenseMap<std::tuple<unsigned, StringRef, StringRef>, Value *>
- IndirectArgCache;
+ /// Whether the argument value can be cached between the PRE and POST calls.
+ bool NoCache;
};
+/// Helper to represent an instrumentation runtime function that is related to
+/// an instrumentation opportunity.
struct IRTCallDescription {
- IRTCallDescription(InstrumentationOpportunity &IConf, Type *RetTy = nullptr);
+ /// Construct an instrumentation function description linked to the \p IO
+ /// instrumentation opportunity and \p RetTy return type.
+ IRTCallDescription(InstrumentationOpportunity &IO, Type *RetTy = nullptr);
+ /// Create the type of the instrumentation function.
FunctionType *createLLVMSignature(InstrumentationConfig &IConf,
LLVMContext &Ctx, const DataLayout &DL,
bool ForceIndirection);
+
+ /// Create a call instruction that calls to the instrumentation function and
+ /// passes the corresponding arguments.
CallInst *createLLVMCall(Value *&V, InstrumentationConfig &IConf,
InstrumentorIRBuilderTy &IIRB, const DataLayout &DL,
InstrumentationCaches &ICaches);
+ /// Return whether the \p IRTA argument can be replaced.
bool isReplacable(IRTArg &IRTA) const {
return (IRTA.Flags & (IRTArg::REPLACABLE | IRTArg::REPLACABLE_CUSTOM));
}
+ /// Return whether the function may have any indirect argument.
bool isPotentiallyIndirect(IRTArg &IRTA) const {
return ((IRTA.Flags & IRTArg::POTENTIALLY_INDIRECT) ||
((IRTA.Flags & IRTArg::REPLACABLE) && NumReplaceableArgs > 1));
}
+ /// Whether the function requires indirection in some argument.
bool RequiresIndirection = false;
+
+ /// Whether any argument may require indirection.
bool MightRequireIndirection = false;
+
+ /// The number of arguments that can be replaced.
unsigned NumReplaceableArgs = 0;
+
+ /// The instrumentation opportunity which it is linked to.
InstrumentationOpportunity &IO;
+
+ /// The return type of the instrumentation function.
Type *RetTy = nullptr;
};
+/// Helper to represent an instrumentation location, which is composed of an
+/// instrumentation opportunity type and a position.
struct InstrumentationLocation {
+ /// The supported location kinds, which are composed of a opportunity type and
+ /// position. The PRE position indicates the instrumentation function call is
+ /// inserted before the instrumented event occurs. The POST position indicates
+ /// the instrumentation call is inserted after the event occurs. Some
+ /// opportunity types may only support one position.
enum KindTy {
MODULE_PRE,
MODULE_POST,
@@ -209,20 +162,26 @@ struct InstrumentationLocation {
BASIC_BLOCK_POST,
INSTRUCTION_PRE,
INSTRUCTION_POST,
- SPECIAL_VALUE,
- Last = SPECIAL_VALUE,
+ Last = INSTRUCTION_POST,
};
+ /// Construct an instrumentation location that is not instrumenting an
+ /// instruction.
InstrumentationLocation(KindTy Kind) : Kind(Kind) {
assert(Kind != INSTRUCTION_PRE && Kind != INSTRUCTION_POST &&
"Opcode required!");
}
+ /// Construct an instrumentation location belonging to the instrumentation of
+ /// an instruction.
InstrumentationLocation(unsigned Opcode, bool IsPRE)
: Kind(IsPRE ? INSTRUCTION_PRE : INSTRUCTION_POST), Opcode(Opcode) {}
+ /// Return the type and position.
KindTy getKind() const { return Kind; }
+ /// Return the string representation given a location kind. This is the string
+ /// used in the configuration file.
static StringRef getKindStr(KindTy Kind) {
switch (Kind) {
case MODULE_PRE:
@@ -245,11 +204,11 @@ struct InstrumentationLocation {
return "instruction_pre";
case INSTRUCTION_POST:
return "instruction_post";
- case SPECIAL_VALUE:
- return "special_value";
}
llvm_unreachable("Invalid kind!");
}
+
+ /// Return the location kind described by a string.
static KindTy getKindFromStr(StringRef S) {
return StringSwitch<KindTy>(S)
.Case("module_pre", MODULE_PRE)
@@ -262,10 +221,10 @@ struct InstrumentationLocation {
.Case("basic_block_post", BASIC_BLOCK_POST)
.Case("instruction_pre", INSTRUCTION_PRE)
.Case("instruction_post", INSTRUCTION_POST)
- .Case("special_value", SPECIAL_VALUE)
.Default(Last);
}
+ /// Return whether a location kind is positioned before the event occurs.
static bool isPRE(KindTy Kind) {
switch (Kind) {
case MODULE_PRE:
@@ -279,13 +238,16 @@ struct InstrumentationLocation {
case FUNCTION_POST:
case BASIC_BLOCK_POST:
case INSTRUCTION_POST:
- case SPECIAL_VALUE:
return false;
}
llvm_unreachable("Invalid kind!");
}
+
+ /// Return whether the instrumentation location is before the event occurs.
bool isPRE() const { return isPRE(Kind); }
+ /// Get the opcode of the instruction instrumentation location. This function
+ /// may not be called by a non-instruction instrumentation location.
unsigned getOpcode() const {
assert((Kind == INSTRUCTION_PRE || Kind == INSTRUCTION_POST) &&
"Expected instruction!");
@@ -293,95 +255,118 @@ struct InstrumentationLocation {
}
private:
+ /// The kind (type and position) of the instrumentation location.
const KindTy Kind;
+
+ /// The opcode for instruction instrumentation locations.
const unsigned Opcode = -1;
};
-struct BaseConfigurationOpportunity {
+/// An option for the base configuration.
+struct BaseConfigurationOption {
+ /// The possible types of options.
enum KindTy {
STRING,
BOOLEAN,
};
- static BaseConfigurationOpportunity *getBoolOption(InstrumentationConfig &IC,
- StringRef Name,
- StringRef Description,
- bool B);
- static BaseConfigurationOpportunity *
- getStringOption(InstrumentationConfig &IC, StringRef Name,
- StringRef Description, StringRef Value);
+ /// Create a boolean option with \p Name name, \p Description description and
+ /// \p DefaultValue as boolean default value.
+ static BaseConfigurationOption *getBoolOption(InstrumentationConfig &IC,
+ StringRef Name,
+ StringRef Description,
+ bool DefaultValue);
+
+ /// Create a string option with \p Name name, \p Description description and
+ /// \p DefaultValue as string default value.
+ static BaseConfigurationOption *getStringOption(InstrumentationConfig &IC,
+ StringRef Name,
+ StringRef Description,
+ StringRef DefaultValue);
+
+ /// Helper union that holds any possible option type.
union ValueTy {
- bool B;
- int64_t I;
- StringRef S;
+ bool Bool;
+ StringRef String;
};
+ /// Set and get of the boolean value. Only valid if it is a boolean option.
+ ///{
void setBool(bool B) {
assert(Kind == BOOLEAN && "Not a boolean!");
- V.B = B;
+ Value.Bool = B;
}
bool getBool() const {
assert(Kind == BOOLEAN && "Not a boolean!");
- return V.B;
+ return Value.Bool;
}
+ ///}
+
+ /// Set and get the string value. Only valid if it is a boolean option.
+ ///{
void setString(StringRef S) {
assert(Kind == STRING && "Not a string!");
- V.S = S;
+ Value.String = S;
}
StringRef getString() const {
assert(Kind == STRING && "Not a string!");
- return V.S;
+ return Value.String;
}
+ ///}
+ /// The information of the option.
+ ///{
StringRef Name;
StringRef Description;
KindTy Kind;
- ValueTy V = {0};
+ ValueTy Value = {0};
+ ///}
};
-struct InstrumentorIRBuilderTy;
+/// The class that contains the configuration for the instrumentor. It holds the
+/// information for each instrumented opportunity, including the base
+/// configuration options. Another class may inherit from this one to modify the
+/// default behavior.
struct InstrumentationConfig {
virtual ~InstrumentationConfig() {}
+ /// Construct an instrumentation configuration with the base options.
InstrumentationConfig() : SS(StringAllocator) {
- RuntimePrefix = BaseConfigurationOpportunity::getStringOption(
+ RuntimePrefix = BaseConfigurationOption::getStringOption(
*this, "runtime_prefix", "The runtime API prefix.", "__instrumentor_");
- TargetRegex = BaseConfigurationOpportunity::getStringOption(
+ TargetRegex = BaseConfigurationOption::getStringOption(
*this, "target_regex",
"Regular expression to be matched against the module target. "
"Only targets that match this regex will be instrumented",
"");
- HostEnabled = BaseConfigurationOpportunity::getBoolOption(
+ HostEnabled = BaseConfigurationOption::getBoolOption(
*this, "host_enabled", "Instrument non-GPU targets", true);
- GPUEnabled = BaseConfigurationOpportunity::getBoolOption(
+ GPUEnabled = BaseConfigurationOption::getBoolOption(
*this, "gpu_enabled", "Instrument GPU targets", true);
}
- bool ReadConfig = true;
-
+ /// Populate the instrumentation opportunities.
virtual void populate(InstrumentorIRBuilderTy &IIRB);
+
+ /// Get the runtime prefix for the instrumentation runtime functions.
StringRef getRTName() const { return RuntimePrefix->getString(); }
+ /// Get the instrumentation function name.
std::string getRTName(StringRef Prefix, StringRef Name,
StringRef Suffix1 = "", StringRef Suffix2 = "") const {
return (getRTName() + Prefix + Name + Suffix1 + Suffix2).str();
}
- void addBaseChoice(BaseConfigurationOpportunity *BCO) {
- BaseConfigurationOpportunities.push_back(BCO);
+ /// Add the base configuration option \p BCO into the list of base options.
+ void addBaseChoice(BaseConfigurationOption *BCO) {
+ BaseConfigurationOptions.push_back(BCO);
}
- SmallVector<BaseConfigurationOpportunity *> BaseConfigurationOpportunities;
-
- BaseConfigurationOpportunity *RuntimePrefix;
- BaseConfigurationOpportunity *TargetRegex;
- BaseConfigurationOpportunity *HostEnabled;
- BaseConfigurationOpportunity *GPUEnabled;
- EnumeratedArray<StringMap<InstrumentationOpportunity *>,
- InstrumentationLocation::KindTy>
- IChoices;
- void addChoice(InstrumentationOpportunity &IO);
+ /// Register instrumentation opportunity \p IO.
+ void addChoice(InstrumentationOpportunity &IO, LLVMContext &Ctx);
+ /// Allocate an object of type \p Ty using a bump allocator and construct it
+ /// with the \p Args arguments. The object may not be freed manually.
template <typename Ty, typename... ArgsTy>
static Ty *allocate(ArgsTy &&...Args) {
static SpecificBumpPtrAllocator<Ty> Allocator;
@@ -390,31 +375,47 @@ struct InstrumentationConfig {
return Obj;
}
- BumpPtrAllocator StringAllocator;
- StringSaver SS;
-};
+ /// The list of enabled base configuration options.
+ SmallVector<BaseConfigurationOption *> BaseConfigurationOptions;
-template <typename EnumTy> struct BaseConfigTy {
- std::bitset<static_cast<int>(EnumTy::NumConfig)> Options;
+ /// The base configuration options.
+ BaseConfigurationOption *RuntimePrefix;
+ BaseConfigurationOption *TargetRegex;
+ BaseConfigurationOption *HostEnabled;
+ BaseConfigurationOption *GPUEnabled;
- BaseConfigTy(bool Enable = true) {
- if (Enable)
- Options.set();
- }
+ /// The map registered instrumentation opportunities. The map is indexed by
+ /// the instrumentation location kind and then by the opportunity name. Notice
+ /// that an instrumentation location may have more than one instrumentation
+ /// opportunity registered.
+ EnumeratedArray<StringMap<InstrumentationOpportunity *>,
+ InstrumentationLocation::KindTy>
+ IChoices;
- bool has(EnumTy Opt) const { return Options.test(static_cast<int>(Opt)); }
- void set(EnumTy Opt, bool Value = true) {
- Options.set(static_cast<int>(Opt), Value);
- }
+ /// Utilities for allocating and building strings.
+ ///{
+ BumpPtrAllocator StringAllocator;
+ StringSaver SS;
+ ///}
};
+/// Base class for instrumentation opportunities. All opportunities should
+/// inherit from this class and implement the virtual class members.
struct InstrumentationOpportunity {
- InstrumentationOpportunity(const InstrumentationLocation IP) : IP(IP) {}
virtual ~InstrumentationOpportunity() {}
+ /// Construct an opportunity with location \p IP.
+ InstrumentationOpportunity(const InstrumentationLocation IP) : IP(IP) {}
+
+ /// The instrumentation location of the opportunity.
InstrumentationLocation IP;
+ /// The list of possible arguments for the instrumentation runtime function.
+ /// The order within the array determines the order of arguments. Arguments
+ /// may be disabled and will not be passed to the function call.
SmallVector<IRTArg> IRTArgs;
+
+ /// Whether the opportunity is enabled.
bool Enabled = true;
/// Helpers to cast values, pass them to the runtime, and replace them. To be
@@ -425,12 +426,13 @@ struct InstrumentationOpportunity {
InstrumentorIRBuilderTy &IIRB) {
return forceCast(V, Ty, IIRB);
}
-
static Value *replaceValue(Value &V, Value &NewV,
InstrumentationConfig &IConf,
InstrumentorIRBuilderTy &IIRB);
///}
+ /// Instrument the value \p V using the configuration \p IConf, and
+ /// potentially, the caches \p ICaches.
virtual Value *instrument(Value *&V, InstrumentationConfig &IConf,
InstrumentorIRBuilderTy &IIRB,
InstrumentationCaches &ICaches) {
@@ -443,62 +445,89 @@ struct InstrumentationOpportunity {
return CI;
}
+ /// Get the return type for the instrumentation runtime function.
virtual Type *getRetTy(LLVMContext &Ctx) const { return nullptr; }
+
+ /// Get the name of the instrumentation opportunity.
virtual StringRef getName() const = 0;
+ /// Get the opcode of the instruction instrumentation opportunity. Only valid
+ /// if it is instruction instrumentation.
unsigned getOpcode() const { return IP.getOpcode(); }
+
+ /// Get the location kind of the instrumentation opportunity.
InstrumentationLocation::KindTy getLocationKind() const {
return IP.getKind();
}
/// An optional callback that takes the value that is about to be
/// instrumented and can return false if it should be skipped.
+ ///{
using CallbackTy = std::function<bool(Value &)>;
-
CallbackTy CB = nullptr;
+ ///}
- static Value *getIdPre(Value &V, Type &Ty, InstrumentationConfig &IConf,
- InstrumentorIRBuilderTy &IIRB);
- static Value *getIdPost(Value &V, Type &Ty, InstrumentationConfig &IConf,
- InstrumentorIRBuilderTy &IIRB);
-
+ /// Add arguments available in all instrumentation opportunities.
void addCommonArgs(InstrumentationConfig &IConf, LLVMContext &Ctx,
bool PassId) {
const auto CB = IP.isPRE() ? getIdPre : getIdPost;
- if (PassId)
+ if (PassId) {
IRTArgs.push_back(
IRTArg(IntegerType::getInt32Ty(Ctx), "id",
"A unique ID associated with the given instrumentor call",
IRTArg::NONE, CB, nullptr, true, true));
+ }
}
- static int32_t getIdFromEpoche(uint32_t Epoche) {
- static DenseMap<uint32_t, int32_t> EpocheIdMap;
+ /// Get the opportunity identifier for the pre and post positions.
+ ///{
+ static Value *getIdPre(Value &V, Type &Ty, InstrumentationConfig &IConf,
+ InstrumentorIRBuilderTy &IIRB);
+ static Value *getIdPost(Value &V, Type &Ty, InstrumentationConfig &IConf,
+ InstrumentorIRBuilderTy &IIRB);
+ ///}
+
+ /// Compute the opportunity identifier for the current instrumentation epoch
+ /// \p CurrentEpoch. The identifiers are assigned consecutively as the epoch
+ /// advances. Epochs may have no identifier assigned (e.g., because no id was
+ /// requested). This function always returns the same identifier when called
+ /// multiple times with the same epoch.
+ static int32_t getIdFromEpoch(uint32_t CurrentEpoch) {
+ static DenseMap<uint32_t, int32_t> EpochIdMap;
static int32_t GlobalId = 0;
- int32_t &EpochId = EpocheIdMap[Epoche];
+ int32_t &EpochId = EpochIdMap[CurrentEpoch];
if (EpochId == 0)
EpochId = ++GlobalId;
return EpochId;
}
};
+/// The base instrumentation opportunity class for instruction opportunities.
+/// Each instruction opportunity should inherit from this class and implement
+/// the virtual class members.
template <unsigned Opcode>
struct InstructionIO : public InstrumentationOpportunity {
- InstructionIO(bool IsPRE)
- : InstrumentationOpportunity(InstrumentationLocation(Opcode, IsPRE)) {}
virtual ~InstructionIO() {}
- unsigned getOpcode() const { return Opcode; }
+ /// Construct an instruction opportunity.
+ InstructionIO(bool IsPRE)
+ : InstrumentationOpportunity(InstrumentationLocation(Opcode, IsPRE)) {}
+ /// Get the name of the instruction.
StringRef getName() const override {
return Instruction::getOpcodeName(Opcode);
}
};
+/// The instrumentation opportunity for store instructions.
struct StoreIO : public InstructionIO<Instruction::Store> {
- StoreIO(bool IsPRE) : InstructionIO(IsPRE) {}
virtual ~StoreIO() {};
+ /// Construct a store instruction opportunity.
+ StoreIO(bool IsPRE) : InstructionIO(IsPRE) {}
+
+ /// The selector of arguments for store opportunities.
+ ///{
enum ConfigKind {
PassPointer = 0,
ReplacePointer,
@@ -514,65 +543,23 @@ struct StoreIO : public InstructionIO<Instruction::Store> {
NumConfig,
};
+ using ConfigTy = BaseConfigTy<ConfigKind>;
+ ConfigTy Config;
+ ///}
+
+ /// Get the type of the stored value.
virtual Type *getValueType(LLVMContext &Ctx) const {
return IntegerType::getInt64Ty(Ctx);
}
- using ConfigTy = BaseConfigTy<ConfigKind>;
- ConfigTy Config;
-
+ /// Initialize the store opportunity using the instrumentation config \p IConf
+ /// and the user config \p UserConfig.
void init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
- ConfigTy *UserConfig = nullptr) {
- if (UserConfig)
- Config = *UserConfig;
-
- bool IsPRE = getLocationKind() == InstrumentationLocation::INSTRUCTION_PRE;
- if (Config.has(PassPointer))
- IRTArgs.push_back(
- IRTArg(IIRB.PtrTy, "pointer", "The accessed pointer.",
- ((IsPRE && Config.has(ReplacePointer)) ? IRTArg::REPLACABLE
- : IRTArg::NONE),
- getPointer, setPointer));
- if (Config.has(PassPointerAS))
- IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "pointer_as",
- "The address space of the accessed pointer.",
- IRTArg::NONE, getPointerAS));
- if (Config.has(PassStoredValue))
- IRTArgs.push_back(
- IRTArg(getValueType(IIRB.Ctx), "value", "The stored value.",
- IRTArg::POTENTIALLY_INDIRECT | (Config.has(PassStoredValueSize)
- ? IRTArg::INDIRECT_HAS_SIZE
- : IRTArg::NONE),
- getValue));
- if (Config.has(PassStoredValueSize))
- IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "value_size",
- "The size of the stored value.", IRTArg::NONE,
- getValueSize));
- if (Config.has(PassAlignment))
- IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "alignment",
- "The known access alignment.", IRTArg::NONE,
- getAlignment));
- if (Config.has(PassValueTypeId))
- IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "value_type_id",
- "The type id of the stored value.", IRTArg::NONE,
- getValueTypeId));
- if (Config.has(PassAtomicityOrdering))
- IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "atomicity_ordering",
- "The atomicity ordering of the store.",
- IRTArg::NONE, getAtomicityOrdering));
- if (Config.has(PassSyncScopeId))
- IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "sync_scope_id",
- "The sync scope id of the store.", IRTArg::NONE,
- getSyncScopeId));
- if (Config.has(PassIsVolatile))
- IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "is_volatile",
- "Flag indicating a volatile store.",
- IRTArg::NONE, isVolatile));
-
- addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
- IConf.addChoice(*this);
- }
+ ConfigTy *UserConfig = nullptr);
+ /// Getters and setters for the arguments of the instrumentation function for
+ /// the store opportunity.
+ ///{
static Value *getPointer(Value &V, Type &Ty, InstrumentationConfig &IConf,
InstrumentorIRBuilderTy &IIRB);
static Value *setPointer(Value &V, Value &NewV, InstrumentationConfig &IConf,
@@ -594,7 +581,11 @@ struct StoreIO : public InstructionIO<Instruction::Store> {
InstrumentorIRBuilderTy &IIRB);
static Value *isVolatile(Value &V, Type &Ty, InstrumentationConfig &IConf,
InstrumentorIRBuilderTy &IIRB);
+ ///}
+ /// Create the store opportunities for pre and post positions. The
+ /// opportunities are also initialized with the arguments for their
+ /// instrumentation calls.
static void populate(InstrumentationConfig &IConf,
InstrumentorIRBuilderTy &IIRB) {
for (auto IsPRE : {true, false}) {
@@ -604,10 +595,15 @@ struct StoreIO : public InstructionIO<Instruction::Store> {
}
};
+/// The instrumentation opportunity for load instructions.
struct LoadIO : public InstructionIO<Instruction::Load> {
- LoadIO(bool IsPRE) : InstructionIO(IsPRE) {}
virtual ~LoadIO() {};
+ /// Construct a load opportunity.
+ LoadIO(bool IsPRE) : InstructionIO(IsPRE) {}
+
+ /// The selector of arguments for load opportunities.
+ ///{
enum ConfigKind {
PassPointer = 0,
ReplacePointer,
@@ -624,65 +620,23 @@ struct LoadIO : public InstructionIO<Instruction::Load> {
NumConfig,
};
+ using ConfigTy = BaseConfigTy<ConfigKind>;
+ ConfigTy Config;
+ ///}
+
+ /// Get the type of the loaded value.
virtual Type *getValueType(LLVMContext &Ctx) const {
return IntegerType::getInt64Ty(Ctx);
}
- using ConfigTy = BaseConfigTy<ConfigKind>;
- ConfigTy Config;
-
+ /// Initialize the load opportunity using the instrumentation config \p IConf
+ /// and the user config \p UserConfig.
void init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
- ConfigTy *UserConfig = nullptr) {
- bool IsPRE = getLocationKind() == InstrumentationLocation::INSTRUCTION_PRE;
- if (UserConfig)
- Config = *UserConfig;
- if (Config.has(PassPointer))
- IRTArgs.push_back(
- IRTArg(IIRB.PtrTy, "pointer", "The accessed pointer.",
- ((IsPRE && Config.has(ReplacePointer)) ? IRTArg::REPLACABLE
- : IRTArg::NONE),
- getPointer, setPointer));
- if (Config.has(PassPointerAS))
- IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "pointer_as",
- "The address space of the accessed pointer.",
- IRTArg::NONE, getPointerAS));
- if (!IsPRE && Config.has(PassValue))
- IRTArgs.push_back(IRTArg(
- getValueType(IIRB.Ctx), "value", "The loaded value.",
- Config.has(ReplaceValue)
- ? IRTArg::REPLACABLE | IRTArg::POTENTIALLY_INDIRECT |
- (Config.has(PassValueSize) ? IRTArg::INDIRECT_HAS_SIZE
- : IRTArg::NONE)
- : IRTArg::NONE,
- getValue, Config.has(ReplaceValue) ? replaceValue : nullptr));
- if (Config.has(PassValueSize))
- IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "value_size",
- "The size of the loaded value.", IRTArg::NONE,
- getValueSize));
- if (Config.has(PassAlignment))
- IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "alignment",
- "The known access alignment.", IRTArg::NONE,
- getAlignment));
- if (Config.has(PassValueTypeId))
- IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "value_type_id",
- "The type id of the loaded value.", IRTArg::NONE,
- getValueTypeId));
- if (Config.has(PassAtomicityOrdering))
- IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "atomicity_ordering",
- "The atomicity ordering of the load.",
- IRTArg::NONE, getAtomicityOrdering));
- if (Config.has(PassSyncScopeId))
- IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "sync_scope_id",
- "The sync scope id of the load.", IRTArg::NONE,
- getSyncScopeId));
- if (Config.has(PassIsVolatile))
- IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "is_volatile",
- "Flag indicating a volatile load.", IRTArg::NONE,
- isVolatile));
- addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
- IConf.addChoice(*this);
- }
+ ConfigTy *UserConfig = nullptr);
+ /// Getters and setters for the arguments of the instrumentation function for
+ /// the load opportunity.
+ ///{
static Value *getPointer(Value &V, Type &Ty, InstrumentationConfig &IConf,
InstrumentorIRBuilderTy &IIRB);
static Value *setPointer(Value &V, Value &NewV, InstrumentationConfig &IConf,
@@ -704,7 +658,9 @@ struct LoadIO : public InstructionIO<Instruction::Load> {
InstrumentorIRBuilderTy &IIRB);
static Value *isVolatile(Value &V, Type &Ty, InstrumentationConfig &IConf,
InstrumentorIRBuilderTy &IIRB);
+ ///}
+ /// Create the store opportunities for PRE and POST positions.
static void populate(InstrumentationConfig &IConf,
InstrumentorIRBuilderTy &IIRB) {
for (auto IsPRE : {true, false}) {
@@ -716,17 +672,24 @@ struct LoadIO : public InstructionIO<Instruction::Load> {
} // namespace instrumentor
+/// The Instrumentor pass.
class InstrumentorPass : public PassInfoMixin<InstrumentorPass> {
using InstrumentationConfig = instrumentor::InstrumentationConfig;
using InstrumentorIRBuilderTy = instrumentor::InstrumentorIRBuilderTy;
+
+ /// The configuration and IR builder provided by the user.
InstrumentationConfig *UserIConf;
InstrumentorIRBuilderTy *UserIIRB;
- PreservedAnalyses run(Module &M, FunctionAnalysisManager &FAM,
- InstrumentationConfig &IConf,
- InstrumentorIRBuilderTy &IIRB);
+ PreservedAnalyses run(Module &M, InstrumentationConfig &IConf,
+ InstrumentorIRBuilderTy &IIRB, bool ReadConfig);
public:
+ /// Construct an instrumentor pass that will use the instrumentation
+ /// configuration \p IC and the IR builder \p IIRB. If an IR builder is not
+ /// provided, a default builder is used. When the configuration is not
+ /// provided, it is read from the config file if available and otherwise a
+ /// default configuration is used.
InstrumentorPass(InstrumentationConfig *IC = nullptr,
InstrumentorIRBuilderTy *IIRB = nullptr)
: UserIConf(IC), UserIIRB(IIRB) {}
diff --git a/llvm/include/llvm/Transforms/IPO/InstrumentorConfigFile.h b/llvm/include/llvm/Transforms/IPO/InstrumentorConfigFile.h
index 370e3b7..155df19 100644
--- a/llvm/include/llvm/Transforms/IPO/InstrumentorConfigFile.h
+++ b/llvm/include/llvm/Transforms/IPO/InstrumentorConfigFile.h
@@ -19,9 +19,13 @@
namespace llvm {
namespace instrumentor {
-void writeConfigToJSON(InstrumentationConfig &IConf, StringRef OutputFile);
+/// Write the configuration in /p IConf to the file with path \p OutputFile.
+void writeConfigToJSON(InstrumentationConfig &IConf, StringRef OutputFile,
+ LLVMContext &Ctx);
-bool readConfigFromJSON(InstrumentationConfig &IConf, StringRef InputFile);
+/// Read the configuration from the file with path \p InputFile into /p IConf.
+bool readConfigFromJSON(InstrumentationConfig &IConf, StringRef InputFile,
+ LLVMContext &Ctx);
} // end namespace instrumentor
} // end namespace llvm
diff --git a/llvm/include/llvm/Transforms/IPO/InstrumentorUtils.h b/llvm/include/llvm/Transforms/IPO/InstrumentorUtils.h
new file mode 100644
index 0000000..c38462d
--- /dev/null
+++ b/llvm/include/llvm/Transforms/IPO/InstrumentorUtils.h
@@ -0,0 +1,172 @@
+//===- Transforms/IPO/InstrumentorUtils.h ---------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// General utilities for the Instrumentor pass.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_IPO_INSTRUMENTOR_UTILS_H
+#define LLVM_TRANSFORMS_IPO_INSTRUMENTOR_UTILS_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+
+#include <bitset>
+#include <tuple>
+
+namespace llvm {
+namespace instrumentor {
+
+/// An IR builder augmented with extra information for the instrumentor pass.
+/// The underlying IR builder features an insertion callback to keep track of
+/// the new instructions.
+struct InstrumentorIRBuilderTy {
+ /// Construct an IR builder for the module \p M.
+ InstrumentorIRBuilderTy(Module &M)
+ : M(M), Ctx(M.getContext()),
+ IRB(Ctx, ConstantFolder(),
+ // Save the inserted instructions in a structure.
+ IRBuilderCallbackInserter(
+ [&](Instruction *I) { NewInsts[I] = Epoch; })) {}
+
+ /// Destroy the IR builder and remove all erasable instructions cached during
+ /// the process of instrumenting.
+ ~InstrumentorIRBuilderTy() {
+ for (auto *I : ErasableInstructions) {
+ if (!I->getType()->isVoidTy())
+ I->replaceAllUsesWith(PoisonValue::get(I->getType()));
+ I->eraseFromParent();
+ }
+ }
+
+ /// Get a temporary alloca to communicate (large) values with the runtime.
+ AllocaInst *getAlloca(Function *Fn, Type *Ty, bool MatchType = false) {
+ const DataLayout &DL = Fn->getDataLayout();
+ auto *&AllocaList = AllocaMap[{Fn, DL.getTypeAllocSize(Ty)}];
+ if (!AllocaList)
+ AllocaList = new AllocaListTy;
+ AllocaInst *AI = nullptr;
+ for (auto *&ListAI : *AllocaList) {
+ if (MatchType && ListAI->getAllocatedType() != Ty)
+ continue;
+ AI = ListAI;
+ ListAI = *AllocaList->rbegin();
+ break;
+ }
+ if (AI)
+ AllocaList->pop_back();
+ else
+ AI = new AllocaInst(Ty, DL.getAllocaAddrSpace(), "",
+ Fn->getEntryBlock().begin());
+ UsedAllocas[AI] = AllocaList;
+ return AI;
+ }
+
+ /// Return the temporary allocas.
+ void returnAllocas() {
+ for (auto [AI, List] : UsedAllocas)
+ List->push_back(AI);
+ UsedAllocas.clear();
+ }
+
+ /// Save instruction \p I to be erased later. The instructions are erased when
+ /// the IR builder is destroyed.
+ void eraseLater(Instruction *I) { ErasableInstructions.insert(I); }
+
+ /// Commonly used values for IR inspection and creation.
+ ///{
+ Module &M;
+
+ LLVMContext &Ctx;
+
+ const DataLayout &DL = M.getDataLayout();
+
+ Type *VoidTy = Type::getVoidTy(Ctx);
+ Type *IntptrTy = M.getDataLayout().getIntPtrType(Ctx);
+ PointerType *PtrTy = PointerType::getUnqual(Ctx);
+ IntegerType *Int8Ty = Type::getInt8Ty(Ctx);
+ IntegerType *Int32Ty = Type::getInt32Ty(Ctx);
+ IntegerType *Int64Ty = Type::getInt64Ty(Ctx);
+ Constant *NullPtrVal = Constant::getNullValue(PtrTy);
+ ///}
+
+ using AllocaListTy = SmallVector<AllocaInst *>;
+
+ /// Map that holds a list of currently available allocas for a function and
+ /// alloca size.
+ DenseMap<std::pair<Function *, unsigned>, AllocaListTy *> AllocaMap;
+
+ /// Map that holds the currently used allocas and the list where they belong.
+ /// Once an alloca has to be returned, it is returned directly to its list.
+ DenseMap<AllocaInst *, AllocaListTy *> UsedAllocas;
+
+ /// Instructions that should be erased later.
+ SmallPtrSet<Instruction *, 32> ErasableInstructions;
+
+ /// The underlying IR builder with insertion callback.
+ IRBuilder<ConstantFolder, IRBuilderCallbackInserter> IRB;
+
+ /// The current epoch number. Each instrumentation, e.g., of an instruction,
+ /// is happening in a dedicated epoch. The epoch allows to determine if
+ /// instrumentation instructions were already around, due to prior
+ /// instrumentations, or have been introduced to support the current
+ /// instrumentation, e.g., compute information about the current instruction.
+ unsigned Epoch = 0;
+
+ /// A mapping from instrumentation instructions to the epoch they have been
+ /// created.
+ DenseMap<Instruction *, unsigned> NewInsts;
+};
+
+/// Helper that represent the caches for instrumentation call arguments. The
+/// value of an argument may not need to be recomputed between the pre and post
+/// instrumentation calls.
+struct InstrumentationCaches {
+ /// A cache for direct and indirect arguments. The cache is indexed by the
+ /// epoch, the instrumentation opportunity name and the argument name. The
+ /// result is a value.
+ DenseMap<std::tuple<unsigned, StringRef, StringRef>, Value *> DirectArgCache;
+ DenseMap<std::tuple<unsigned, StringRef, StringRef>, Value *>
+ IndirectArgCache;
+};
+
+/// Boolean option bitset with a compile-time number of bits to store as many
+/// options as the enumeration type \p EnumTy defines. The enumeration type is
+/// expected to have an ascending and consecutive values, starting at zero, and
+/// the last value being artificial and named as NumConfig (i.e., the number of
+/// values in the enumeration).
+template <typename EnumTy> struct BaseConfigTy {
+ /// The bistset with as many bits as the enumeration's values.
+ std::bitset<static_cast<int>(EnumTy::NumConfig)> Options;
+
+ /// Construct the option bitset with all bits set to \p Enable. If not
+ /// provided, all options are enabled.
+ BaseConfigTy(bool Enable = true) {
+ if (Enable)
+ Options.set();
+ }
+
+ /// Check if the option \p Opt is enabled.
+ bool has(EnumTy Opt) const { return Options.test(static_cast<int>(Opt)); }
+
+ /// Set the boolean value of option \p Opt to \p Value.
+ void set(EnumTy Opt, bool Value = true) {
+ Options.set(static_cast<int>(Opt), Value);
+ }
+};
+
+} // namespace instrumentor
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_IPO_INSTRUMENTOR_UTILS_H
diff --git a/llvm/lib/Transforms/IPO/Instrumentor.cpp b/llvm/lib/Transforms/IPO/Instrumentor.cpp
index 17657cf..a28a40b 100644
--- a/llvm/lib/Transforms/IPO/Instrumentor.cpp
+++ b/llvm/lib/Transforms/IPO/Instrumentor.cpp
@@ -21,6 +21,7 @@
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstrTypes.h"
@@ -42,6 +43,7 @@
#include <cstdint>
#include <functional>
#include <iterator>
+#include <memory>
#include <string>
#include <system_error>
#include <type_traits>
@@ -51,19 +53,24 @@ using namespace llvm::instrumentor;
#define DEBUG_TYPE "instrumentor"
-static cl::opt<std::string> WriteJSONConfig(
+namespace {
+
+/// The user option to specify an output JSON file to write the configuration.
+static cl::opt<std::string> WriteConfigFile(
"instrumentor-write-config-file",
cl::desc(
"Write the instrumentor configuration into the specified JSON file"),
cl::init(""));
-static cl::opt<std::string> ReadJSONConfig(
+
+/// The user option to specify an input JSON file to read the configuration.
+static cl::opt<std::string> ReadConfigFile(
"instrumentor-read-config-file",
cl::desc(
"Read the instrumentor configuration from the specified JSON file"),
cl::init(""));
-namespace {
-
+/// Set the debug location, if not set, after changing the insertion point of
+/// the IR builder \p IRB.
template <typename IRBuilderTy> void ensureDbgLoc(IRBuilderTy &IRB) {
if (IRB.getCurrentDebugLocation())
return;
@@ -72,22 +79,23 @@ template <typename IRBuilderTy> void ensureDbgLoc(IRBuilderTy &IRB) {
IRB.SetCurrentDebugLocation(DILocation::get(BB->getContext(), 0, 0, SP));
}
+/// Attempt to cast \p V to type \p Ty.
template <typename IRBTy>
Value *tryToCast(IRBTy &IRB, Value *V, Type *Ty, const DataLayout &DL,
bool AllowTruncate = false) {
if (!V)
return Constant::getAllOnesValue(Ty);
- auto *VTy = V->getType();
+ Type *VTy = V->getType();
if (VTy == Ty)
return V;
if (VTy->isAggregateType())
return V;
- auto RequestedSize = DL.getTypeSizeInBits(Ty);
- auto ValueSize = DL.getTypeSizeInBits(VTy);
- bool IsTruncate = RequestedSize < ValueSize;
- if (IsTruncate && !AllowTruncate)
+ TypeSize RequestedSize = DL.getTypeSizeInBits(Ty);
+ TypeSize ValueSize = DL.getTypeSizeInBits(VTy);
+ bool ShouldTruncate = RequestedSize < ValueSize;
+ if (ShouldTruncate && !AllowTruncate)
return V;
- if (IsTruncate && AllowTruncate)
+ if (ShouldTruncate && AllowTruncate)
return tryToCast(IRB,
IRB.CreateIntCast(V, IRB.getIntNTy(RequestedSize),
/*IsSigned=*/false),
@@ -97,35 +105,25 @@ Value *tryToCast(IRBTy &IRB, Value *V, Type *Ty, const DataLayout &DL,
if (VTy->isIntegerTy() && Ty->isIntegerTy())
return IRB.CreateIntCast(V, Ty, /*IsSigned=*/false);
if (VTy->isFloatingPointTy() && Ty->isIntOrPtrTy()) {
- switch (ValueSize) {
- case 64:
- return tryToCast(IRB, IRB.CreateBitCast(V, IRB.getInt64Ty()), Ty, DL,
- AllowTruncate);
- case 32:
- return tryToCast(IRB, IRB.CreateBitCast(V, IRB.getInt32Ty()), Ty, DL,
- AllowTruncate);
- case 16:
- return tryToCast(IRB, IRB.CreateBitCast(V, IRB.getInt16Ty()), Ty, DL,
- AllowTruncate);
- case 8:
- return tryToCast(IRB, IRB.CreateBitCast(V, IRB.getInt8Ty()), Ty, DL,
- AllowTruncate);
- default:
- llvm_unreachable("unsupported floating point size");
- }
+ return tryToCast(IRB, IRB.CreateBitCast(V, IRB.getIntNTy(ValueSize)), Ty,
+ DL, AllowTruncate);
}
return IRB.CreateBitOrPointerCast(V, Ty);
}
+/// Get a constant integer/boolean of type \p IT and value \p Val.
template <typename Ty> Constant *getCI(Type *IT, Ty Val) {
return ConstantInt::get(IT, Val);
}
+/// The core of the instrumentor pass, which instruments the module as the
+/// instrumentation configuration mandates.
class InstrumentorImpl final {
public:
+ /// Construct an instrumentor implementation using the configuration \p IConf.
InstrumentorImpl(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
- Module &M, FunctionAnalysisManager &FAM)
- : IConf(IConf), M(M), FAM(FAM), IIRB(IIRB) {
+ Module &M)
+ : IConf(IConf), M(M), IIRB(IIRB) {
IConf.populate(IIRB);
}
@@ -133,10 +131,17 @@ public:
bool instrument();
private:
+ /// Indicate if the module should be instrumented based on the target.
bool shouldInstrumentTarget();
+
+ /// Indicate if the function \p Fn should be instrumented.
bool shouldInstrumentFunction(Function &Fn);
+ /// Instrument instruction \p I if needed, and use the argument caches in \p
+ /// ICaches.
bool instrumentInstruction(Instruction &I, InstrumentationCaches &ICaches);
+
+ /// Instrument function \p Fn.
bool instrumentFunction(Function &Fn);
/// The instrumentation opportunities for instructions indexed by
@@ -150,8 +155,6 @@ private:
/// The underlying module.
Module &M;
- FunctionAnalysisManager &FAM;
-
protected:
/// A special IR builder that keeps track of the inserted instructions.
InstrumentorIRBuilderTy &IIRB;
@@ -169,12 +172,14 @@ bool InstrumentorImpl::shouldInstrumentTarget() {
llvm::Regex TargetRegex(TargetRegexStr);
std::string ErrMsg;
if (!TargetRegex.isValid(ErrMsg)) {
- errs() << "WARNING: failed to parse target regex: " << ErrMsg << "\n";
+ IIRB.Ctx.diagnose(DiagnosticInfoInstrumentation(
+ Twine("failed to parse target regex: ") + ErrMsg, DS_Warning));
return false;
}
RegexMatches = TargetRegex.match(T.str());
}
+ // Only instrument the module if the target has to be instrumented.
return ((IsGPU && IConf.GPUEnabled->getBool()) ||
(!IsGPU && IConf.HostEnabled->getBool())) &&
RegexMatches;
@@ -196,7 +201,7 @@ bool InstrumentorImpl::instrumentInstruction(Instruction &I,
return Changed;
// Count epochs eagerly.
- ++IIRB.Epoche;
+ ++IIRB.Epoch;
Value *IPtr = &I;
if (auto *IO = InstChoicesPRE.lookup(I.getOpcode())) {
@@ -247,14 +252,14 @@ bool InstrumentorImpl::instrument() {
return Changed;
}
-PreservedAnalyses InstrumentorPass::run(Module &M, FunctionAnalysisManager &FAM,
- InstrumentationConfig &IConf,
- InstrumentorIRBuilderTy &IIRB) {
- InstrumentorImpl Impl(IConf, IIRB, M, FAM);
- if (IConf.ReadConfig && !readConfigFromJSON(IConf, ReadJSONConfig))
+PreservedAnalyses InstrumentorPass::run(Module &M, InstrumentationConfig &IConf,
+ InstrumentorIRBuilderTy &IIRB,
+ bool ReadConfig) {
+ InstrumentorImpl Impl(IConf, IIRB, M);
+ if (ReadConfig && !readConfigFromJSON(IConf, ReadConfigFile, IIRB.Ctx))
return PreservedAnalyses::all();
- writeConfigToJSON(IConf, WriteJSONConfig);
+ writeConfigToJSON(IConf, WriteConfigFile, IIRB.Ctx);
bool Changed = Impl.instrument();
if (!Changed)
@@ -263,45 +268,43 @@ PreservedAnalyses InstrumentorPass::run(Module &M, FunctionAnalysisManager &FAM,
}
PreservedAnalyses InstrumentorPass::run(Module &M, ModuleAnalysisManager &MAM) {
- auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
- InstrumentationConfig *IConf =
- UserIConf ? UserIConf : new InstrumentationConfig();
- InstrumentorIRBuilderTy *IIRB =
- UserIIRB ? UserIIRB : new InstrumentorIRBuilderTy(M, FAM);
+ // Only create them if the user did not provide them.
+ std::unique_ptr<InstrumentationConfig> IConfInt(
+ !UserIConf ? new InstrumentationConfig() : nullptr);
+ std::unique_ptr<InstrumentorIRBuilderTy> IIRBInt(
+ !UserIIRB ? new InstrumentorIRBuilderTy(M) : nullptr);
- auto PA = run(M, FAM, *IConf, *IIRB);
+ auto *IConf = IConfInt ? IConfInt.get() : UserIConf;
+ auto *IIRB = IIRBInt ? IIRBInt.get() : UserIIRB;
- if (!UserIIRB)
- delete IIRB;
- if (!UserIConf)
- delete IConf;
+ auto PA = run(M, *IConf, *IIRB, !UserIConf);
assert(!verifyModule(M, &errs()));
-
return PA;
}
-BaseConfigurationOpportunity *
-BaseConfigurationOpportunity::getBoolOption(InstrumentationConfig &IConf,
- StringRef Name,
- StringRef Description, bool Value) {
- auto *BCO = new BaseConfigurationOpportunity();
+BaseConfigurationOption *
+BaseConfigurationOption::getBoolOption(InstrumentationConfig &IConf,
+ StringRef Name, StringRef Description,
+ bool DefaultValue) {
+ auto *BCO = new BaseConfigurationOption();
BCO->Name = Name;
BCO->Description = Description;
BCO->Kind = BOOLEAN;
- BCO->V.B = Value;
+ BCO->Value.Bool = DefaultValue;
IConf.addBaseChoice(BCO);
return BCO;
}
-BaseConfigurationOpportunity *BaseConfigurationOpportunity::getStringOption(
- InstrumentationConfig &IConf, StringRef Name, StringRef Description,
- StringRef Value) {
- auto *BCO = new BaseConfigurationOpportunity();
+BaseConfigurationOption *
+BaseConfigurationOption::getStringOption(InstrumentationConfig &IConf,
+ StringRef Name, StringRef Description,
+ StringRef DefaultValue) {
+ auto *BCO = new BaseConfigurationOption();
BCO->Name = Name;
BCO->Description = Description;
BCO->Kind = STRING;
- BCO->V.S = Value;
+ BCO->Value.String = DefaultValue;
IConf.addBaseChoice(BCO);
return BCO;
}
@@ -312,12 +315,15 @@ void InstrumentationConfig::populate(InstrumentorIRBuilderTy &IIRB) {
StoreIO::populate(*this, IIRB);
}
-void InstrumentationConfig::addChoice(InstrumentationOpportunity &IO) {
+void InstrumentationConfig::addChoice(InstrumentationOpportunity &IO,
+ LLVMContext &Ctx) {
auto *&ICPtr = IChoices[IO.getLocationKind()][IO.getName()];
- if (ICPtr && IO.getLocationKind() != InstrumentationLocation::SPECIAL_VALUE) {
- errs() << "WARNING: registered two instrumentation opportunities for the "
- "same location ("
- << ICPtr->getName() << " vs " << IO.getName() << ")!\n";
+ if (ICPtr) {
+ Ctx.diagnose(DiagnosticInfoInstrumentation(
+ Twine("registered two instrumentation opportunities for the same "
+ "location (") +
+ ICPtr->getName() + Twine(" vs ") + IO.getName() + Twine(")"),
+ DS_Warning));
}
ICPtr = &IO;
}
@@ -325,13 +331,13 @@ void InstrumentationConfig::addChoice(InstrumentationOpportunity &IO) {
Value *InstrumentationOpportunity::getIdPre(Value &V, Type &Ty,
InstrumentationConfig &IConf,
InstrumentorIRBuilderTy &IIRB) {
- return getCI(&Ty, getIdFromEpoche(IIRB.Epoche));
+ return getCI(&Ty, getIdFromEpoch(IIRB.Epoch));
}
Value *InstrumentationOpportunity::getIdPost(Value &V, Type &Ty,
InstrumentationConfig &IConf,
InstrumentorIRBuilderTy &IIRB) {
- return getCI(&Ty, -getIdFromEpoche(IIRB.Epoche));
+ return getCI(&Ty, -getIdFromEpoch(IIRB.Epoch));
}
Value *InstrumentationOpportunity::forceCast(Value &V, Type &Ty,
@@ -357,7 +363,7 @@ Value *InstrumentationOpportunity::replaceValue(Value &V, Value &NewV,
/*AllowTruncate=*/true);
}
V.replaceUsesWithIf(NewVCasted, [&](Use &U) {
- if (IIRB.NewInsts.lookup(cast<Instruction>(U.getUser())) == IIRB.Epoche)
+ if (IIRB.NewInsts.lookup(cast<Instruction>(U.getUser())) == IIRB.Epoch)
return false;
if (isa<LifetimeIntrinsic>(U.getUser()) || U.getUser()->isDroppable())
return false;
@@ -424,12 +430,10 @@ CallInst *IRTCallDescription::createLLVMCall(Value *&V,
for (auto &It : IO.IRTArgs) {
if (!It.Enabled)
continue;
- auto *&Param = ICaches.DirectArgCache[{IIRB.Epoche, IO.getName(), It.Name}];
+ auto *&Param = ICaches.DirectArgCache[{IIRB.Epoch, IO.getName(), It.Name}];
if (!Param || It.NoCache)
// Avoid passing the caches to the getter.
Param = It.GetterCB(*V, *It.Ty, IConf, IIRB);
- if (!Param)
- errs() << IO.getName() << " : " << It.Name << "\n";
assert(Param);
if (Param->getType()->isVoidTy()) {
@@ -438,10 +442,11 @@ CallInst *IRTCallDescription::createLLVMCall(Value *&V,
DL.getTypeSizeInBits(Param->getType()) >
DL.getTypeSizeInBits(It.Ty)) {
if (!isPotentiallyIndirect(It)) {
- errs() << "WARNING: Indirection needed for " << It.Name << " of " << *V
- << " in " << IO.getName() << ", but not indicated\n. Got "
- << *Param << " expected " << *It.Ty
- << "; instrumentation is skipped";
+ IIRB.Ctx.diagnose(DiagnosticInfoInstrumentation(
+ Twine("indirection needed for ") + It.Name + Twine(" in ") +
+ IO.getName() +
+ Twine(", but not indicated. Instrumentation is skipped"),
+ DS_Warning));
return nullptr;
}
ForceIndirection = true;
@@ -471,7 +476,7 @@ CallInst *IRTCallDescription::createLLVMCall(Value *&V,
}
auto *&CachedParam =
- ICaches.IndirectArgCache[{IIRB.Epoche, IO.getName(), It.Name}];
+ ICaches.IndirectArgCache[{IIRB.Epoch, IO.getName(), It.Name}];
if (CachedParam) {
CallParam = CachedParam;
continue;
@@ -504,14 +509,15 @@ CallInst *IRTCallDescription::createLLVMCall(Value *&V,
continue;
bool IsCustomReplaceable = IO.IRTArgs[I].Flags & IRTArg::REPLACABLE_CUSTOM;
Value *NewValue = FnTy->isVoidTy() || IsCustomReplaceable
- ? ICaches.DirectArgCache[{IIRB.Epoche, IO.getName(),
+ ? ICaches.DirectArgCache[{IIRB.Epoch, IO.getName(),
IO.IRTArgs[I].Name}]
: CI;
assert(NewValue);
if (ForceIndirection && !IsCustomReplaceable &&
isPotentiallyIndirect(IO.IRTArgs[I])) {
- auto *Q = ICaches.IndirectArgCache[{IIRB.Epoche, IO.getName(),
- IO.IRTArgs[I].Name}];
+ auto *Q =
+ ICaches
+ .IndirectArgCache[{IIRB.Epoch, IO.getName(), IO.IRTArgs[I].Name}];
NewValue = IIRB.IRB.CreateLoad(V->getType(), Q);
}
V = IO.IRTArgs[I].SetterCB(*V, *NewValue, IConf, IIRB);
@@ -519,6 +525,67 @@ CallInst *IRTCallDescription::createLLVMCall(Value *&V,
return CI;
}
+void StoreIO::init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
+ ConfigTy *UserConfig) {
+ if (UserConfig)
+ Config = *UserConfig;
+
+ bool IsPRE = getLocationKind() == InstrumentationLocation::INSTRUCTION_PRE;
+ if (Config.has(PassPointer)) {
+ IRTArgs.push_back(
+ IRTArg(IIRB.PtrTy, "pointer", "The accessed pointer.",
+ ((IsPRE && Config.has(ReplacePointer)) ? IRTArg::REPLACABLE
+ : IRTArg::NONE),
+ getPointer, setPointer));
+ }
+ if (Config.has(PassPointerAS)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "pointer_as",
+ "The address space of the accessed pointer.",
+ IRTArg::NONE, getPointerAS));
+ }
+ if (Config.has(PassStoredValue)) {
+ IRTArgs.push_back(
+ IRTArg(getValueType(IIRB.Ctx), "value", "The stored value.",
+ IRTArg::POTENTIALLY_INDIRECT |
+ (Config.has(PassStoredValueSize) ? IRTArg::INDIRECT_HAS_SIZE
+ : IRTArg::NONE),
+ getValue));
+ }
+ if (Config.has(PassStoredValueSize)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "value_size",
+ "The size of the stored value.", IRTArg::NONE,
+ getValueSize));
+ }
+ if (Config.has(PassAlignment)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "alignment",
+ "The known access alignment.", IRTArg::NONE,
+ getAlignment));
+ }
+ if (Config.has(PassValueTypeId)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "value_type_id",
+ "The type id of the stored value.", IRTArg::NONE,
+ getValueTypeId));
+ }
+ if (Config.has(PassAtomicityOrdering)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "atomicity_ordering",
+ "The atomicity ordering of the store.",
+ IRTArg::NONE, getAtomicityOrdering));
+ }
+ if (Config.has(PassSyncScopeId)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "sync_scope_id",
+ "The sync scope id of the store.", IRTArg::NONE,
+ getSyncScopeId));
+ }
+ if (Config.has(PassIsVolatile)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "is_volatile",
+ "Flag indicating a volatile store.", IRTArg::NONE,
+ isVolatile));
+ }
+
+ addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
+ IConf.addChoice(*this, IIRB.Ctx);
+}
+
Value *StoreIO::getPointer(Value &V, Type &Ty, InstrumentationConfig &IConf,
InstrumentorIRBuilderTy &IIRB) {
auto &SI = cast<StoreInst>(V);
@@ -582,6 +649,68 @@ Value *StoreIO::isVolatile(Value &V, Type &Ty, InstrumentationConfig &IConf,
return getCI(&Ty, SI.isVolatile());
}
+void LoadIO::init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
+ ConfigTy *UserConfig) {
+ bool IsPRE = getLocationKind() == InstrumentationLocation::INSTRUCTION_PRE;
+ if (UserConfig)
+ Config = *UserConfig;
+ if (Config.has(PassPointer)) {
+ IRTArgs.push_back(
+ IRTArg(IIRB.PtrTy, "pointer", "The accessed pointer.",
+ ((IsPRE && Config.has(ReplacePointer)) ? IRTArg::REPLACABLE
+ : IRTArg::NONE),
+ getPointer, setPointer));
+ }
+ if (Config.has(PassPointerAS)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "pointer_as",
+ "The address space of the accessed pointer.",
+ IRTArg::NONE, getPointerAS));
+ }
+ if (!IsPRE && Config.has(PassValue)) {
+ IRTArgs.push_back(
+ IRTArg(getValueType(IIRB.Ctx), "value", "The loaded value.",
+ Config.has(ReplaceValue)
+ ? IRTArg::REPLACABLE | IRTArg::POTENTIALLY_INDIRECT |
+ (Config.has(PassValueSize) ? IRTArg::INDIRECT_HAS_SIZE
+ : IRTArg::NONE)
+ : IRTArg::NONE,
+ getValue, Config.has(ReplaceValue) ? replaceValue : nullptr));
+ }
+ if (Config.has(PassValueSize)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "value_size",
+ "The size of the loaded value.", IRTArg::NONE,
+ getValueSize));
+ }
+ if (Config.has(PassAlignment)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "alignment",
+ "The known access alignment.", IRTArg::NONE,
+ getAlignment));
+ }
+ if (Config.has(PassValueTypeId)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "value_type_id",
+ "The type id of the loaded value.", IRTArg::NONE,
+ getValueTypeId));
+ }
+ if (Config.has(PassAtomicityOrdering)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "atomicity_ordering",
+ "The atomicity ordering of the load.",
+ IRTArg::NONE, getAtomicityOrdering));
+ }
+ if (Config.has(PassSyncScopeId)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "sync_scope_id",
+ "The sync scope id of the load.", IRTArg::NONE,
+ getSyncScopeId));
+ }
+ if (Config.has(PassIsVolatile)) {
+ IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "is_volatile",
+ "Flag indicating a volatile load.", IRTArg::NONE,
+ isVolatile));
+ }
+
+ addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
+ IConf.addChoice(*this, IIRB.Ctx);
+}
+
Value *LoadIO::getPointer(Value &V, Type &Ty, InstrumentationConfig &IConf,
InstrumentorIRBuilderTy &IIRB) {
auto &LI = cast<LoadInst>(V);
diff --git a/llvm/lib/Transforms/IPO/InstrumentorConfigFile.cpp b/llvm/lib/Transforms/IPO/InstrumentorConfigFile.cpp
index 2f31c71..6e12399 100644
--- a/llvm/lib/Transforms/IPO/InstrumentorConfigFile.cpp
+++ b/llvm/lib/Transforms/IPO/InstrumentorConfigFile.cpp
@@ -12,6 +12,7 @@
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/MemoryBuffer.h"
@@ -22,16 +23,18 @@
namespace llvm {
namespace instrumentor {
-void writeConfigToJSON(InstrumentationConfig &IConf, StringRef OutputFile) {
+void writeConfigToJSON(InstrumentationConfig &IConf, StringRef OutputFile,
+ LLVMContext &Ctx) {
if (OutputFile.empty())
return;
std::error_code EC;
raw_fd_stream OS(OutputFile, EC);
if (EC) {
- errs() << "WARNING: Failed to open instrumentor configuration file for "
- "writing: "
- << EC.message() << "\n";
+ Ctx.diagnose(DiagnosticInfoInstrumentation(
+ Twine("failed to open instrumentor configuration file for writing: ") +
+ EC.message(),
+ DS_Warning));
return;
}
@@ -40,12 +43,12 @@ void writeConfigToJSON(InstrumentationConfig &IConf, StringRef OutputFile) {
J.attributeBegin("configuration");
J.objectBegin();
- for (auto *BaseCO : IConf.BaseConfigurationOpportunities) {
+ for (auto *BaseCO : IConf.BaseConfigurationOptions) {
switch (BaseCO->Kind) {
- case BaseConfigurationOpportunity::STRING:
+ case BaseConfigurationOption::STRING:
J.attribute(BaseCO->Name, BaseCO->getString());
break;
- case BaseConfigurationOpportunity::BOOLEAN:
+ case BaseConfigurationOption::BOOLEAN:
J.attribute(BaseCO->Name, BaseCO->getBool());
break;
}
@@ -89,67 +92,81 @@ void writeConfigToJSON(InstrumentationConfig &IConf, StringRef OutputFile) {
J.objectEnd();
}
-bool readConfigFromJSON(InstrumentationConfig &IConf, StringRef InputFile) {
+bool readConfigFromJSON(InstrumentationConfig &IConf, StringRef InputFile,
+ LLVMContext &Ctx) {
if (InputFile.empty())
return true;
std::error_code EC;
auto BufferOrErr = MemoryBuffer::getFileOrSTDIN(InputFile);
if (std::error_code EC = BufferOrErr.getError()) {
- errs() << "WARNING: Failed to open instrumentor configuration file for "
- "reading: "
- << EC.message() << "\n";
+ Ctx.diagnose(DiagnosticInfoInstrumentation(
+ Twine("failed to open instrumentor configuration file for reading: ") +
+ EC.message(),
+ DS_Warning));
return false;
}
auto Buffer = std::move(BufferOrErr.get());
json::Path::Root NullRoot;
auto Parsed = json::parse(Buffer->getBuffer());
if (!Parsed) {
- errs() << "WARNING: Failed to parse the instrumentor configuration file: "
- << Parsed.takeError() << "\n";
+ Ctx.diagnose(DiagnosticInfoInstrumentation(
+ Twine("failed to parse instrumentor configuration file: ") +
+ toString(Parsed.takeError()),
+ DS_Warning));
return false;
}
auto *Config = Parsed->getAsObject();
if (!Config) {
- errs() << "WARNING: Failed to parse the instrumentor configuration file: "
- "Expected "
- "an object '{ ... }'\n";
+ Ctx.diagnose(DiagnosticInfoInstrumentation(
+ "failed to parse instrumentor configuration file, expected an object "
+ "'{ ... }'",
+ DS_Warning));
return false;
}
- StringMap<BaseConfigurationOpportunity *> BCOMap;
- for (auto *BO : IConf.BaseConfigurationOpportunities)
+ StringMap<BaseConfigurationOption *> BCOMap;
+ for (auto *BO : IConf.BaseConfigurationOptions)
BCOMap[BO->Name] = BO;
SmallPtrSet<InstrumentationOpportunity *, 32> SeenIOs;
for (auto &It : *Config) {
auto *Obj = It.second.getAsObject();
if (!Obj) {
- errs() << "WARNING: malformed JSON configuration, expected an object.\n";
+ Ctx.diagnose(DiagnosticInfoInstrumentation(
+ "malformed JSON configuration, expected an object", DS_Warning));
continue;
}
if (It.first == "configuration") {
for (auto &ObjIt : *Obj) {
if (auto *BO = BCOMap.lookup(ObjIt.first)) {
switch (BO->Kind) {
- case BaseConfigurationOpportunity::STRING:
+ case BaseConfigurationOption::STRING:
if (auto V = ObjIt.second.getAsString()) {
BO->setString(IConf.SS.save(*V));
- } else
- errs() << "WARNING: configuration key '" << ObjIt.first
- << "' expects a string, value ignored\n";
+ } else {
+ Ctx.diagnose(DiagnosticInfoInstrumentation(
+ Twine("configuration key '") + ObjIt.first.str() +
+ Twine("' expects a string, value ignored"),
+ DS_Warning));
+ }
break;
- case BaseConfigurationOpportunity::BOOLEAN:
+ case BaseConfigurationOption::BOOLEAN:
if (auto V = ObjIt.second.getAsBoolean())
BO->setBool(*V);
- else
- errs() << "WARNING: configuration key '" << ObjIt.first
- << "' expects a boolean, value ignored\n";
+ else {
+ Ctx.diagnose(DiagnosticInfoInstrumentation(
+ Twine("configuration key '") + ObjIt.first.str() +
+ Twine("' expects a boolean, value ignored"),
+ DS_Warning));
+ }
break;
}
} else if (!StringRef(ObjIt.first).ends_with(".description")) {
- errs() << "WARNING: configuration key not found and ignored: "
- << ObjIt.first << "\n";
+ Ctx.diagnose(DiagnosticInfoInstrumentation(
+ Twine("configuration key '") + ObjIt.first.str() +
+ Twine("' not found and ignored"),
+ DS_Warning));
}
}
continue;
@@ -160,15 +177,17 @@ bool readConfigFromJSON(InstrumentationConfig &IConf, StringRef InputFile) {
for (auto &ObjIt : *Obj) {
auto *InnerObj = ObjIt.second.getAsObject();
if (!InnerObj) {
- errs()
- << "WARNING: malformed JSON configuration, expected an object.\n";
+ Ctx.diagnose(DiagnosticInfoInstrumentation(
+ "malformed JSON configuration, expected an object", DS_Warning));
continue;
}
auto *IO = IChoiceMap.lookup(ObjIt.first);
if (!IO) {
- errs() << "WARNING: malformed JSON configuration, expected an object "
- "matching an instrumentor choice, got "
- << ObjIt.first << ".\n";
+ Ctx.diagnose(DiagnosticInfoInstrumentation(
+ Twine("malformed JSON configuration, expected an object matching "
+ "an instrumentor choice, got ") +
+ ObjIt.first.str(),
+ DS_Warning));
continue;
}
SeenIOs.insert(IO);