* This file is part of the MindStudio project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* MindStudio is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* ------------------------------------------------------------------------- */
#include "BinaryInstrumentation.h"
#include <iterator>
#include <vector>
#include <dlfcn.h>
#include "core/FunctionLoader.h"
#include "core/PlatformConfig.h"
#include "utils/FileSystem.h"
#include "utils/PipeCall.h"
#include "utils/InjectLogger.h"
#include "utils/Future.h"
#include "utils/Ustring.h"
#include "utils/UmaskGuard.h"
using namespace std;
using namespace func_injection;
namespace {
constexpr mode_t DEFAULT_UMASK_FOR_LOG_FILE = 0027;
bool ParseFirstSymbol(std::string const &text, std::string const &property, std::string &symbol)
{
std::vector<std::string> lines;
SplitString(text, '\n', lines);
constexpr std::size_t propertyIndex = 1;
constexpr std::size_t typeIndex = 2;
constexpr std::size_t sectionIndex = 3;
constexpr std::size_t symbolIndex = 5;
for (auto const &line: lines) {
std::istringstream iss(line);
auto items = std::vector<std::string>(std::istream_iterator<std::string>{iss},
std::istream_iterator<std::string>{});
if (items.size() > symbolIndex && items[propertyIndex] == property &&
items[typeIndex] == "F" && items[sectionIndex] == ".text") {
symbol = items[symbolIndex];
return true;
}
}
return false;
}
}
BinaryInstrumentation::BinaryInstrumentation(BIType biType): config_{}, biType_(biType) {}
bool BBCountDBI::Convert(const std::string& newKernelFile, const std::string& oldKernelFile,
const std::string& tilingKey)
{
UmaskGuard guard{DEFAULT_UMASK_FOR_LOG_FILE};
std::vector<std::string> args;
if (config_.archName.find("dav-c310") != std::string::npos) {
args = {
"bisheng-tune",
"--action=instru-probe",
"--tune-bbbend-offset",
"--instru-bbprobe",
"--tune-argsize=" + std::to_string(config_.argSize),
oldKernelFile,
"-o",
newKernelFile
};
} else {
args = {
"bisheng-tune",
"--action=block-count-instr",
"--tune-bbbend-offset",
"--tune-argsize=" + std::to_string(config_.argSize),
oldKernelFile,
"-o",
newKernelFile
};
}
if (!tilingKey.empty()) {
args.push_back("--tiling-key=" + tilingKey);
}
std::string output;
if (!PipeCall(args, output)) {
WARN_LOG("Convert bbcount failed.");
DEBUG_LOG("Command execute message: %.10240s", output.c_str());
return false;
}
return Chmod(newKernelFile, SAVE_DATA_FILE_AUTHORITY);
}
bool PGODBI::Convert(const std::string& newKernelFile, const std::string& oldKernelFile, const std::string& tilingKey)
{
UmaskGuard guard{DEFAULT_UMASK_FOR_LOG_FILE};
std::vector<std::string> args = {
"bisheng-tune",
"--action=block-count-instr",
"--tune-argsize=" + std::to_string(config_.argSize),
oldKernelFile,
"-o=" + newKernelFile
};
if (!tilingKey.empty()) {
args.push_back("--tiling-key=" + tilingKey);
}
std::string output;
if (!PipeCall(args, output)) {
return false;
}
return Chmod(newKernelFile, SAVE_DATA_FILE_AUTHORITY);
}
bool PGODBI::ExpandArgs(const std::string& expendArgs)
{
return true;
}
bool CustomDBI::GenerateOrderingFile(std::string const &kernelFile,
std::string const &probeFile,
std::string const &orderingFile) const
{
std::vector<std::string> args = { "llvm-objdump", "--syms", kernelFile };
std::string output;
if (!PipeCall(args, output)) {
WARN_LOG("dump kernel symbols failed.");
DEBUG_LOG("Command execute message: %.10240s", output.c_str());
return false;
}
std::string kernelSymbol;
if (!ParseFirstSymbol(output, "g", kernelSymbol)) {
WARN_LOG("parse first kernel symbol failed");
return false;
}
args = { "llvm-objdump", "--syms", probeFile };
if (!PipeCall(args, output)) {
WARN_LOG("dump probe symbols failed.");
DEBUG_LOG("Command execute message: %.10240s", output.c_str());
return false;
}
std::string probeSymbol;
if (!ParseFirstSymbol(output, "w", probeSymbol)) {
WARN_LOG("parse first probe symbol failed");
return false;
}
std::string ordering = kernelSymbol + "\n" + probeSymbol;
if (!WriteStringToFile(orderingFile, ordering)) {
WARN_LOG("write ordering file failed");
return false;
}
return true;
}
bool CustomDBI::GenerateTempProbe(const string &probeFile) const
{
vector<string> args = {"llvm-objcopy", "-j", "." + config_.archName,
"-O", "binary", config_.pluginPath, probeFile,
};
string output;
if (!PipeCall(args, output)) {
WARN_LOG("Extract probe.o from plugin file failed.");
return false;
}
return Chmod(probeFile, SAVE_DATA_FILE_AUTHORITY);
}
bool CustomDBI::GenerateKernelWithProbe(const string &kernelFile, const string &probeFile,
const string &orderingFile, const string &kernelWithProbeFile) const
{
vector<string> args = {"ld.lld", "-m", "aicorelinux", "-Ttext=0", "-execute-probe",
"--symbol-ordering-file", orderingFile,
"-z", "separate-loadable-segments", probeFile, kernelFile,
"-static", "-q", "-o", kernelWithProbeFile,
};
string output;
if (!PipeCall(args, output)) {
WARN_LOG("Generate kernel_with_probe file failed");
DEBUG_LOG("Command execute message: %s", output.c_str());
return false;
}
return Chmod(kernelWithProbeFile, SAVE_DATA_FILE_AUTHORITY);
}
bool CustomDBI::Convert(const std::string& newKernelFile, const std::string& oldKernelFile,
const std::string& tilingKey)
{
const string &archName = config_.archName;
if (!initFunc_ || archName.empty()) {
return false;
}
string tempProbePath = JoinPath({config_.tmpDir, "probe.o"});
string tempCtrlPath = JoinPath({config_.tmpDir, "ctrl.bin"});
string tempOrderingPath = JoinPath({config_.tmpDir, "symbol_ordering.txt"});
string tempKernelWithProbePath = JoinPath({config_.tmpDir, "old_kernel_with_probe.o"});
if (!GenerateTempProbe(tempProbePath)) {
WARN_LOG("generate probe.o failed.");
return false;
}
if (!GenerateOrderingFile(oldKernelFile, tempProbePath, tempOrderingPath)) {
WARN_LOG("generate link ordering file failed");
return false;
}
if (!GenerateKernelWithProbe(oldKernelFile, tempProbePath, tempOrderingPath, tempKernelWithProbePath)) {
WARN_LOG("generate kernel with probe file failed");
return false;
}
initFunc_(tempCtrlPath.c_str(), tempCtrlPath.length(), archName.c_str());
if (!Chmod(tempCtrlPath, SAVE_DATA_FILE_AUTHORITY)) {
return false;
}
string output;
vector<string> args = {
"bisheng-tune", "--action=instru-probe", "--tune-argsize=" + to_string(config_.argSize), "--instru-memprobe",
tempKernelWithProbePath, "--dbi-config=" + tempCtrlPath, "-o=" + newKernelFile
};
for (size_t i = 0; i < config_.extraArgs.size(); ++i) {
args.push_back(config_.extraArgs[i]);
}
if (!tilingKey.empty()) {
args.push_back("--tiling-key=" + tilingKey);
}
auto writeTuneLog = std::shared_ptr<void>(nullptr, [&](void*) {
if (!config_.tuneLogPath.empty()) {
WriteStringToFile(config_.tuneLogPath, output);
}
});
if (!PipeCall(args, output)) {
WARN_LOG("Generate kernel from tune failed.");
DEBUG_LOG("Command execute message: %s", output.c_str());
return false;
}
return Chmod(newKernelFile, SAVE_DATA_FILE_AUTHORITY);
}
bool CustomDBI::SetConfig(const Config& config)
{
config_ = config;
const string &pluginPath = config.pluginPath;
if (pluginPath.empty() || config.archName.empty()) {
DEBUG_LOG("Invalid dbi config, empty plugin path (%s) or empty arch name (%s).",
pluginPath.c_str(),
config.archName.c_str());
return false;
}
handle_ = dlopen(pluginPath.c_str(), RTLD_LAZY);
if (handle_ == nullptr) {
DEBUG_LOG("Invalid dbi config, dlopen %s failed", pluginPath.c_str());
return false;
}
initFunc_ = reinterpret_cast<PluginInitFunc>(dlsym(handle_, "MSBitStart"));
if (!initFunc_) {
DEBUG_LOG("Invalid dbi config, no msbitStart function");
return false;
}
return true;
}
CustomDBI::~CustomDBI()
{
if (handle_) {
dlclose(handle_);
}
}
DBIFactory &DBIFactory::Instance()
{
static DBIFactory inst;
return inst;
}
BinaryInstrumentationSP DBIFactory::Create(BIType type) const
{
switch (type) {
case BIType::PGO:
return MakeShared<PGODBI>();
case BIType::BB_COUNT:
return MakeShared<BBCountDBI>();
case BIType::CUSTOMIZE:
return MakeShared<CustomDBI>();
default:
return nullptr;
}
}