CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
283 lines
9.1 KiB
C++
283 lines
9.1 KiB
C++
//===-- InstrumentorConfigFile.cpp ----------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// The implementation of the utilities for the Instrumentor JSON configuration
|
|
// file.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Transforms/IPO/Instrumentor.h"
|
|
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/IR/DiagnosticInfo.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/JSON.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/StringSaver.h"
|
|
#include "llvm/Support/VirtualFileSystem.h"
|
|
|
|
#include <string>
|
|
|
|
using namespace llvm;
|
|
|
|
static Expected<std::unique_ptr<MemoryBuffer>>
|
|
setupMemoryBuffer(const Twine &Filename, vfs::FileSystem &FS) {
|
|
auto BufferOrErr = Filename.str() == "-" ? MemoryBuffer::getSTDIN()
|
|
: FS.getBufferForFile(Filename);
|
|
if (std::error_code EC = BufferOrErr.getError())
|
|
return errorCodeToError(EC);
|
|
return std::move(BufferOrErr.get());
|
|
}
|
|
|
|
namespace llvm {
|
|
namespace instrumentor {
|
|
|
|
void writeConfigToJSON(InstrumentationConfig &IConf, StringRef OutputFile,
|
|
LLVMContext &Ctx) {
|
|
if (OutputFile.empty())
|
|
return;
|
|
|
|
std::error_code EC;
|
|
raw_fd_stream OS(OutputFile, EC);
|
|
if (EC) {
|
|
Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
Twine("failed to open instrumentor configuration file for writing: ") +
|
|
EC.message(),
|
|
DS_Warning));
|
|
return;
|
|
}
|
|
|
|
json::OStream J(OS, 2);
|
|
J.objectBegin();
|
|
|
|
J.attributeBegin("configuration");
|
|
J.objectBegin();
|
|
for (auto *BaseCO : IConf.BaseConfigurationOptions) {
|
|
switch (BaseCO->Kind) {
|
|
case BaseConfigurationOption::STRING:
|
|
J.attribute(BaseCO->Name, BaseCO->getString());
|
|
break;
|
|
case BaseConfigurationOption::BOOLEAN:
|
|
J.attribute(BaseCO->Name, BaseCO->getBool());
|
|
break;
|
|
}
|
|
if (!BaseCO->Description.empty())
|
|
J.attribute(std::string(BaseCO->Name) + ".description",
|
|
BaseCO->Description);
|
|
}
|
|
J.objectEnd();
|
|
J.attributeEnd();
|
|
|
|
for (unsigned KindVal = 0; KindVal <= InstrumentationLocation::Last;
|
|
++KindVal) {
|
|
auto Kind = InstrumentationLocation::KindTy(KindVal);
|
|
|
|
auto &KindChoices = IConf.IChoices[Kind];
|
|
if (KindChoices.empty())
|
|
continue;
|
|
|
|
J.attributeBegin(InstrumentationLocation::getKindStr(Kind));
|
|
J.objectBegin();
|
|
for (auto &[Name, Choice] : KindChoices) {
|
|
J.attributeBegin(Name);
|
|
J.objectBegin();
|
|
J.attribute("enabled", Choice->Enabled);
|
|
J.attribute("filter", Choice->Filter);
|
|
J.attribute("filter.description",
|
|
"Static property filter to exclude instrumentation.");
|
|
for (auto &ArgIt : Choice->IRTArgs) {
|
|
J.attribute(ArgIt.Name, ArgIt.Enabled);
|
|
if ((ArgIt.Flags & IRTArg::REPLACABLE) ||
|
|
(ArgIt.Flags & IRTArg::REPLACABLE_CUSTOM))
|
|
J.attribute(std::string(ArgIt.Name) + ".replace", true);
|
|
if (!ArgIt.Description.empty())
|
|
J.attribute(std::string(ArgIt.Name) + ".description",
|
|
ArgIt.Description);
|
|
}
|
|
J.objectEnd();
|
|
J.attributeEnd();
|
|
}
|
|
J.objectEnd();
|
|
J.attributeEnd();
|
|
}
|
|
|
|
J.objectEnd();
|
|
}
|
|
|
|
bool readConfigFromJSON(InstrumentationConfig &IConf, StringRef InputFile,
|
|
LLVMContext &Ctx, vfs::FileSystem &FS) {
|
|
if (InputFile.empty())
|
|
return true;
|
|
|
|
auto BufferOrErr = setupMemoryBuffer(InputFile, FS);
|
|
if (Error E = BufferOrErr.takeError()) {
|
|
Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
Twine("failed to open instrumentor configuration file for reading: ") +
|
|
toString(std::move(E)),
|
|
DS_Warning));
|
|
return false;
|
|
}
|
|
auto Buffer = std::move(BufferOrErr.get());
|
|
json::Path::Root NullRoot;
|
|
auto Parsed = json::parse(Buffer->getBuffer());
|
|
if (!Parsed) {
|
|
Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
Twine("failed to parse instrumentor configuration file: ") +
|
|
toString(Parsed.takeError()),
|
|
DS_Warning));
|
|
return false;
|
|
}
|
|
auto *Config = Parsed->getAsObject();
|
|
if (!Config) {
|
|
Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
"failed to parse instrumentor configuration file, expected an object "
|
|
"'{ ... }'",
|
|
DS_Warning));
|
|
return false;
|
|
}
|
|
|
|
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) {
|
|
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 BaseConfigurationOption::STRING:
|
|
if (auto V = ObjIt.second.getAsString()) {
|
|
BO->setString(IConf.SS.save(*V));
|
|
} else {
|
|
Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
Twine("configuration key '") + ObjIt.first.str() +
|
|
Twine("' expects a string, value ignored"),
|
|
DS_Warning));
|
|
}
|
|
break;
|
|
case BaseConfigurationOption::BOOLEAN:
|
|
if (auto V = ObjIt.second.getAsBoolean())
|
|
BO->setBool(*V);
|
|
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")) {
|
|
Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
Twine("configuration key '") + ObjIt.first.str() +
|
|
Twine("' not found and ignored"),
|
|
DS_Warning));
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
auto &IChoiceMap =
|
|
IConf.IChoices[InstrumentationLocation::getKindFromStr(It.first)];
|
|
for (auto &ObjIt : *Obj) {
|
|
auto *InnerObj = ObjIt.second.getAsObject();
|
|
if (!InnerObj) {
|
|
Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
"malformed JSON configuration, expected an object", DS_Warning));
|
|
continue;
|
|
}
|
|
auto *IO = IChoiceMap.lookup(ObjIt.first);
|
|
if (!IO) {
|
|
Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
Twine("malformed JSON configuration, expected an object matching "
|
|
"an instrumentor choice, got ") +
|
|
ObjIt.first.str(),
|
|
DS_Warning));
|
|
continue;
|
|
}
|
|
SeenIOs.insert(IO);
|
|
StringMap<bool> ValueMap, ReplaceMap;
|
|
StringRef FilterStr;
|
|
for (auto &InnerObjIt : *InnerObj) {
|
|
auto Name = StringRef(InnerObjIt.first);
|
|
if (Name == "filter") {
|
|
if (auto V = InnerObjIt.second.getAsString())
|
|
FilterStr = IConf.SS.save(*V);
|
|
} else if (Name.consume_back(".replace")) {
|
|
ReplaceMap[Name] = InnerObjIt.second.getAsBoolean().value_or(false);
|
|
} else {
|
|
ValueMap[Name] = InnerObjIt.second.getAsBoolean().value_or(false);
|
|
}
|
|
}
|
|
IO->Enabled = ValueMap["enabled"];
|
|
IO->Filter = FilterStr;
|
|
for (auto &IRArg : IO->IRTArgs) {
|
|
IRArg.Enabled = ValueMap[IRArg.Name];
|
|
if (!ReplaceMap.lookup(IRArg.Name)) {
|
|
IRArg.Flags &= ~IRTArg::REPLACABLE;
|
|
IRArg.Flags &= ~IRTArg::REPLACABLE_CUSTOM;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto &IChoiceMap : IConf.IChoices)
|
|
for (auto &It : IChoiceMap)
|
|
if (!SeenIOs.count(It.second))
|
|
It.second->Enabled = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool readConfigPathsFile(StringRef InputFile, cl::list<std::string> &Configs,
|
|
LLVMContext &Ctx, vfs::FileSystem &FS) {
|
|
if (InputFile.empty())
|
|
return true;
|
|
|
|
auto BufferOrErr = setupMemoryBuffer(InputFile, FS);
|
|
if (Error E = BufferOrErr.takeError()) {
|
|
Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
Twine("failed to open instrumentor configuration paths file for "
|
|
"reading: ") +
|
|
toString(std::move(E)),
|
|
DS_Warning));
|
|
return false;
|
|
}
|
|
|
|
StringRef InputFilePath(sys::path::parent_path(InputFile));
|
|
|
|
auto Buffer = std::move(BufferOrErr.get());
|
|
StringRef Content = Buffer->getBuffer();
|
|
StringRef EOL = Content.detectEOL();
|
|
do {
|
|
auto [LHS, RHS] = Content.split(EOL);
|
|
std::string ConfigPath = LHS.trim().str();
|
|
if (!sys::path::is_absolute(ConfigPath)) {
|
|
SmallString<128> InputFilePathStringVec(InputFilePath);
|
|
sys::path::append(InputFilePathStringVec, ConfigPath);
|
|
ConfigPath = InputFilePathStringVec.c_str();
|
|
}
|
|
Configs.push_back(ConfigPath);
|
|
Content = RHS.trim();
|
|
} while (!Content.empty());
|
|
|
|
return true;
|
|
}
|
|
|
|
} // end namespace instrumentor
|
|
} // end namespace llvm
|