object-introspection/oi/OILibraryImpl.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

260 lines
8.3 KiB
C++
Raw Permalink Normal View History

2022-12-19 14:37:51 +00:00
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
2023-08-16 20:40:36 +01:00
#include "OILibraryImpl.h"
2022-12-19 14:37:51 +00:00
#include <glog/logging.h>
#include <sys/mman.h>
2023-08-16 20:40:36 +01:00
#include <boost/core/demangle.hpp>
2022-12-19 14:37:51 +00:00
#include <boost/format.hpp>
2023-08-16 20:40:36 +01:00
#include <cerrno>
#include <cstring>
2022-12-19 14:37:51 +00:00
#include <fstream>
2023-08-16 20:40:36 +01:00
#include <stdexcept>
2022-12-19 14:37:51 +00:00
#include "oi/Config.h"
2023-08-16 20:40:36 +01:00
#include "oi/DrgnUtils.h"
#include "oi/Headers.h"
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
namespace oi::detail {
namespace {
// Map between the high level feature requests in the OIL API and the underlying
// codegen features.
std::map<Feature, bool> convertFeatures(std::unordered_set<oi::Feature> fs);
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
// Extract the root type from an atomic function pointer
drgn_qualified_type getTypeFromAtomicHole(drgn_program* prog, void* hole);
} // namespace
OILibraryImpl::LocalTextSegment::LocalTextSegment(size_t size) {
void* base = mmap(NULL,
size,
PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0);
2023-08-16 20:40:36 +01:00
if (base == MAP_FAILED)
throw std::runtime_error(std::string("segment map failed: ") +
std::strerror(errno));
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
data_ = {static_cast<uint8_t*>(base), size};
2022-12-19 14:37:51 +00:00
}
2023-08-16 20:40:36 +01:00
OILibraryImpl::LocalTextSegment::~LocalTextSegment() {
if (data_.empty())
return;
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
PLOG_IF(ERROR, munmap(data_.data(), data_.size()) != 0)
<< "segment unmap failed";
2022-12-19 14:37:51 +00:00
}
2023-08-16 20:40:36 +01:00
OILibraryImpl::MemoryFile::MemoryFile(const char* name) {
fd_ = memfd_create(name, 0);
if (fd_ == -1)
throw std::runtime_error(std::string("memfd creation failed: ") +
std::strerror(errno));
2022-12-19 14:37:51 +00:00
}
2023-08-16 20:40:36 +01:00
OILibraryImpl::MemoryFile::~MemoryFile() {
if (fd_ == -1)
return;
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
PLOG_IF(ERROR, close(fd_) == -1) << "memfd close failed";
2022-12-19 14:37:51 +00:00
}
2023-08-16 20:40:36 +01:00
std::filesystem::path OILibraryImpl::MemoryFile::path() {
return {(boost::format("/dev/fd/%1%") % fd_).str()};
}
OILibraryImpl::OILibraryImpl(void* atomicHole,
std::unordered_set<oi::Feature> fs,
GeneratorOptions opts)
: atomicHole_(atomicHole),
requestedFeatures_(convertFeatures(std::move(fs))),
opts_(std::move(opts)) {
2022-12-19 14:37:51 +00:00
}
2023-08-16 20:40:36 +01:00
std::pair<void*, const exporters::inst::Inst&> OILibraryImpl::init() {
processConfigFile();
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
constexpr size_t TextSegSize = 1u << 22;
textSeg = {TextSegSize};
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
return compileCode();
2022-12-19 14:37:51 +00:00
}
2023-08-16 20:40:36 +01:00
void OILibraryImpl::processConfigFile() {
auto features = config::processConfigFiles(opts_.configFilePaths,
requestedFeatures_,
compilerConfig_,
generatorConfig_);
2023-08-16 20:40:36 +01:00
if (!features)
throw std::runtime_error("failed to process configuration");
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
generatorConfig_.features = *features;
compilerConfig_.features = *features;
}
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
std::pair<void*, const exporters::inst::Inst&> OILibraryImpl::compileCode() {
2023-08-24 13:41:39 +01:00
google::SetVLOGLevel("*", opts_.debugLevel);
2023-08-16 20:40:36 +01:00
auto symbols = std::make_shared<SymbolService>(getpid());
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
auto* prog = symbols->getDrgnProgram();
CHECK(prog != nullptr) << "does this check need to exist?";
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
auto rootType = getTypeFromAtomicHole(symbols->getDrgnProgram(), atomicHole_);
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
CodeGen codegen{generatorConfig_, *symbols};
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
std::string code;
if (!codegen.codegenFromDrgn(rootType.type, code))
throw std::runtime_error("oil jit codegen failed!");
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
std::string sourcePath = opts_.sourceFileDumpPath;
if (sourcePath.empty()) {
sourcePath = "oil_jit.cpp"; // fake path for JIT debug info
2022-12-19 14:37:51 +00:00
} else {
2023-03-16 13:09:18 +00:00
std::ofstream outputFile(sourcePath);
outputFile << code;
}
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
auto object = MemoryFile("oil_object_code");
OICompiler compiler{symbols, compilerConfig_};
if (!compiler.compile(code, sourcePath, object.path()))
throw std::runtime_error("oil jit compilation failed!");
2022-12-19 14:37:51 +00:00
auto relocRes = compiler.applyRelocs(
2023-08-16 20:40:36 +01:00
reinterpret_cast<uint64_t>(textSeg.data().data()), {object.path()}, {});
if (!relocRes)
throw std::runtime_error("oil jit relocation failed!");
const auto& [_, segments, jitSymbols] = *relocRes;
std::string nameHash =
(boost::format("%1$016x") %
std::hash<std::string>{}(SymbolService::getTypeName(rootType.type)))
.str();
std::string functionSymbolPrefix = "_Z27introspect_" + nameHash;
std::string typeSymbolName = "treeBuilderInstructions" + nameHash;
void* fp = nullptr;
const exporters::inst::Inst* ty = nullptr;
for (const auto& [symName, symAddr] : jitSymbols) {
if (fp == nullptr && symName.starts_with(functionSymbolPrefix)) {
fp = reinterpret_cast<void*>(symAddr);
if (ty != nullptr)
break;
} else if (ty == nullptr && symName == typeSymbolName) {
ty = reinterpret_cast<const exporters::inst::Inst*>(symAddr);
if (fp != nullptr)
break;
}
2022-12-19 14:37:51 +00:00
}
2023-08-16 20:40:36 +01:00
CHECK(fp != nullptr && ty != nullptr)
<< "failed to find always present symbols!";
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
for (const auto& [baseAddr, relocAddr, size] : segments)
std::memcpy(reinterpret_cast<void*>(relocAddr),
reinterpret_cast<void*>(baseAddr),
size);
2023-08-16 20:40:36 +01:00
textSeg.release(); // don't munmap() the region containing the code
return {fp, *ty};
}
namespace {
std::map<Feature, bool> convertFeatures(std::unordered_set<oi::Feature> fs) {
std::map<Feature, bool> out{
{Feature::TypeGraph, true},
{Feature::TreeBuilderV2, true},
{Feature::Library, true},
{Feature::PackStructs, true},
{Feature::PruneTypeGraph, true},
};
for (const auto f : fs) {
switch (f) {
case oi::Feature::ChaseRawPointers:
out[Feature::ChaseRawPointers] = true;
break;
case oi::Feature::CaptureThriftIsset:
out[Feature::CaptureThriftIsset] = true;
break;
case oi::Feature::GenJitDebug:
out[Feature::GenJitDebug] = true;
break;
2022-12-19 14:37:51 +00:00
}
}
2023-08-16 20:40:36 +01:00
return out;
}
2022-12-19 14:37:51 +00:00
2023-08-16 20:40:36 +01:00
drgn_qualified_type getTypeFromAtomicHole(drgn_program* prog, void* hole) {
// get the getter type:
// std::atomic<std::vector<uint8_t> (*)(const T&)>& getIntrospectionFunc();
auto atomicGetterType =
SymbolService::findTypeOfAddr(prog, reinterpret_cast<uintptr_t>(hole));
if (!atomicGetterType)
throw std::runtime_error("failed to lookup function");
// get the return type:
// std::atomic<std::vector<uint8_t> (*)(const T&)>&
CHECK(drgn_type_has_type(atomicGetterType->type))
<< "functions have a return type";
auto retType = drgn_type_type(atomicGetterType->type);
// get the atomic type:
// std::atomic<std::vector<uint8_t> (*)(const T&)>
CHECK(drgn_type_has_type(retType.type)) << "pointers have a value type";
auto atomicType = drgn_type_type(retType.type);
// get the function pointer type:
// std::vector<uint8_t> (*)(const T&)
CHECK(drgn_type_has_template_parameters(atomicType.type))
<< "atomic should have template parameters";
CHECK(drgn_type_num_template_parameters(atomicType.type) == 1)
<< "atomic should have 1 template parameter";
auto* templateParam = drgn_type_template_parameters(atomicType.type);
struct drgn_qualified_type funcPointerType;
if (auto err = drgn_template_parameter_type(templateParam, &funcPointerType))
throw drgnplusplus::error(err);
// get the function type:
// std::vector<uint8_t>(const T&)
CHECK(drgn_type_has_type(funcPointerType.type))
<< "function pointers have a value type";
auto funcType = drgn_type_type(funcPointerType.type);
// get the argument type:
// const T&
CHECK(drgn_type_has_parameters(funcType.type)) << "functions have parameters";
CHECK(drgn_type_num_parameters(funcType.type) == 2)
<< "function should have 2 parameters";
drgn_qualified_type argType;
if (auto err =
drgn_parameter_type(drgn_type_parameters(funcType.type), &argType))
throw drgnplusplus::error(err);
// get the type
CHECK(drgn_type_has_type(argType.type))
<< "reference types have a value type";
return drgn_type_type(argType.type);
2022-12-19 14:37:51 +00:00
}
2023-08-16 20:40:36 +01:00
} // namespace
} // namespace oi::detail