Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ jobs:
libmsgpack-dev \
libzstd-dev \
llvm-15-dev \
liblldb-15-dev \
ninja-build \
pkg-config \
python3-setuptools
Expand All @@ -129,7 +130,7 @@ jobs:
- run:
name: Build
command: |
cmake -G Ninja -B build/ -DWITH_FLAKY_TESTS=Off -DCODE_COVERAGE=On -DWARNINGS_AS_ERRORS=<< parameters.warnings_as_errors >>
cmake -G Ninja -B build/ -DWITH_FLAKY_TESTS=Off -DCODE_COVERAGE=On -DWARNINGS_AS_ERRORS=<< parameters.warnings_as_errors >> -DLLDB_ROOT=/usr/lib/llvm-15
cmake --build build/
# Testing rubbish:
cp test/ci.oid.toml build/testing.oid.toml
Expand Down Expand Up @@ -166,6 +167,7 @@ jobs:
libboost-all-dev \
libgflags-dev \
llvm-15-dev \
liblldb-15-dev \
libfmt-dev \
libjemalloc-dev
environment:
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ find_package(Clang REQUIRED CONFIG)
message(STATUS "Found Clang ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using ClangConfig.cmake in: ${Clang_DIR}")

find_package(LLDB REQUIRED)

### msgpack
# msgpack v3.0.0 doesn't define the msgpackc-cxx target, but since the library is header only,
# we can locate the header dir and add it to our include directories.
Expand Down
29 changes: 29 additions & 0 deletions cmake/FindLLDB.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# - Find LLDB
# Find the LLDB debugger library and includes
#
# LLDB_INCLUDE_DIRS - where to find LLDB.h, etc.
# LLDB_LIBRARIES - List of libraries when using LLDB.
# LLDB_FOUND - True if LLDB found.

find_path(LLDB_INCLUDE_DIRS LLDB.h
HINTS ${LLDB_ROOT_DIR}/include
PATH_SUFFIXES lldb/API/)
# Remove the path suffixes
cmake_path(APPEND LLDB_INCLUDE_DIRS .. ..)

find_library(LLDB_LIBRARIES lldb lldb-15 HINTS ${LLDB_ROOT_DIR}/lib)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LLDB DEFAULT_MSG LLDB_LIBRARIES LLDB_INCLUDE_DIRS)

mark_as_advanced(
LLDB_LIBRARIES
LLDB_INCLUDE_DIRS)

if(LLDB_FOUND AND NOT (TARGET LLDB))
add_library(LLDB UNKNOWN IMPORTED)
set_target_properties(LLDB
PROPERTIES
IMPORTED_LOCATION ${LLDB_LIBRARIES}
INTERFACE_INCLUDE_DIRECTORIES ${LLDB_INCLUDE_DIRS})
endif()
1 change: 1 addition & 0 deletions oi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ add_library(symbol_service
SymbolService.cpp
)
target_link_libraries(symbol_service
LLDB
drgn_utils

Boost::headers
Expand Down
5 changes: 5 additions & 0 deletions oi/Features.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ std::optional<std::string_view> featureHelp(Feature f) {
return "Generate statistics on padding of structures.";
case Feature::CaptureThriftIsset:
return "Capture isset data for Thrift object.";
case Feature::LLDB:
return "Use LLDB (instead of drgn) to parse the debug info.";
case Feature::TypeGraph:
return "Use Type Graph for code generation (CodeGen v2).";
case Feature::PruneTypeGraph:
Expand All @@ -60,6 +62,9 @@ std::optional<std::string_view> featureHelp(Feature f) {

std::span<const Feature> requirements(Feature f) {
switch (f) {
case Feature::LLDB:
static constexpr std::array lldb = {Feature::TypeGraph};
return lldb;
case Feature::TreeBuilderV2:
static constexpr std::array tb2 = {Feature::TypeGraph};
return tb2;
Expand Down
1 change: 1 addition & 0 deletions oi/Features.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
X(PackStructs, "pack-structs") \
X(GenPaddingStats, "gen-padding-stats") \
X(CaptureThriftIsset, "capture-thrift-isset") \
X(LLDB, "lldb") \
X(TypeGraph, "type-graph") \
X(PruneTypeGraph, "prune-type-graph") \
X(Library, "library") \
Expand Down
24 changes: 18 additions & 6 deletions oi/OIDebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2962,17 +2962,24 @@ bool OIDebugger::processTargetData() {
}

std::optional<std::string> OIDebugger::generateCode(const irequest& req) {
auto root = symbols->getRootType(req);
if (!root.has_value()) {
return std::nullopt;
}

std::string code(headers::oi_OITraceCode_cpp);

if (generatorConfig.features[Feature::TypeGraph]) {
// CodeGen v2
std::string rootVariableName;
CodeGen codegen2{generatorConfig, *symbols};
codegen2.codegenFromDrgn(root->type.type, code);
if (generatorConfig.features[Feature::LLDB]) {
throw std::runtime_error{"LLDB is not implemented yet"};
} else {
auto root = symbols->getDrgnRootType(req);
if (!root.has_value()) {
return std::nullopt;
}

rootVariableName = std::move(root->varName);
codegen2.codegenFromDrgn(root->type.type, code);
}

TypeHierarchy th;
// Make this static as a big hack to extend the fake drgn_types' lifetimes
Expand All @@ -2981,10 +2988,15 @@ std::optional<std::string> OIDebugger::generateCode(const irequest& req) {
drgn_type* rootType;
codegen2.exportDrgnTypes(th, drgnTypes, &rootType);

typeInfos[req] = {RootInfo{root->varName, {rootType, drgn_qualifiers{}}},
typeInfos[req] = {RootInfo{rootVariableName, {rootType, drgn_qualifiers{}}},
th,
std::map<std::string, PaddingInfo>{}};
} else {
auto root = symbols->getDrgnRootType(req);
if (!root.has_value()) {
return std::nullopt;
}

// OICodeGen (v1)
auto codegen = OICodeGen::buildFromConfig(generatorConfig, *symbols);
if (!codegen) {
Expand Down
150 changes: 145 additions & 5 deletions oi/SymbolService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <cassert>
#include <cstring>
#include <fstream>
#include <lldb/API/LLDB.h>

#include "oi/DrgnUtils.h"
#include "oi/OIParser.h"
Expand Down Expand Up @@ -106,7 +107,8 @@ static bool isExecutableAddr(
return it != end(exeAddrs) && addr >= it->first;
}

SymbolService::SymbolService(pid_t pid) : target{pid} {
SymbolService::SymbolService(pid_t pid, Backend back)
: target{pid}, backend{back} {
// Update target processes memory map
LoadExecutableAddressRange(pid, executableAddrs);
if (!loadModules()) {
Expand All @@ -115,8 +117,8 @@ SymbolService::SymbolService(pid_t pid) : target{pid} {
}
}

SymbolService::SymbolService(fs::path executablePath)
: target{std::move(executablePath)} {
SymbolService::SymbolService(fs::path executablePath, Backend back)
: target{std::move(executablePath)}, backend{back} {
if (!loadModules()) {
throw std::runtime_error("Failed to load modules for executable " +
executablePath.string());
Expand All @@ -131,6 +133,15 @@ SymbolService::~SymbolService() {
if (prog != nullptr) {
drgn_program_destroy(prog);
}

if (lldbTarget) {
lldbDebugger.DeleteTarget(lldbTarget);
}

if (lldbDebugger) {
lldb::SBDebugger::Destroy(lldbDebugger);
lldb::SBDebugger::Terminate();
}
}

struct ModParams {
Expand Down Expand Up @@ -432,7 +443,12 @@ std::optional<std::string> SymbolService::locateBuildID() {

struct drgn_program* SymbolService::getDrgnProgram() {
if (hardDisableDrgn) {
LOG(ERROR) << "drgn is disabled, refusing to initialize";
LOG(ERROR) << "drgn/LLDB is disabled, refusing to initialize";
return nullptr;
}

if (backend != Backend::DRGN) {
LOG(ERROR) << "drgn is not the selected backend, refusing to initialize";
return nullptr;
}

Expand Down Expand Up @@ -484,6 +500,53 @@ struct drgn_program* SymbolService::getDrgnProgram() {
return prog;
}

lldb::SBTarget SymbolService::getLLDBTarget() {
if (hardDisableDrgn) {
LOG(ERROR) << "drgn/LLDB is disabled, refusing to initialize";
return lldb::SBTarget();
}

if (backend != Backend::LLDB) {
LOG(ERROR) << "LLDB is not the selected backend, refusing to initialize";
return lldb::SBTarget();
}

bool success = false;

lldb::SBDebugger::Initialize();
lldbDebugger = lldb::SBDebugger::Create(false);
BOOST_SCOPE_EXIT_ALL(&) {
if (!success) {
lldb::SBDebugger::Destroy(lldbDebugger);
lldb::SBDebugger::Terminate();
}
};

switch (target.index()) {
case 0: {
auto pid = std::get<pid_t>(target);
lldbTarget = lldbDebugger.FindTargetWithProcessID(pid);
if (!lldbTarget) {
LOG(ERROR) << "Failed to find target with PID " << pid;
return lldb::SBTarget();
}
break;
}
case 1: {
auto path = std::get<fs::path>(target);
lldbTarget = lldbDebugger.CreateTarget(path.c_str());
if (!lldbTarget) {
LOG(ERROR) << "Failed to create target from " << path;
return lldb::SBTarget();
}
break;
}
}

success = true;
return lldbTarget;
}

/*
* Although 'parseFormalParam' has an all-encompassing sounding name, its sole
* task is to extract the location information for this parameter if any exist.
Expand Down Expand Up @@ -804,7 +867,7 @@ std::string SymbolService::getTypeName(struct drgn_type* type) {
return drgn_utils::typeToName(type);
}

std::optional<RootInfo> SymbolService::getRootType(const irequest& req) {
std::optional<RootInfo> SymbolService::getDrgnRootType(const irequest& req) {
if (req.type == "global") {
/*
* This is super simple as all we have to do is locate assign the
Expand Down Expand Up @@ -912,4 +975,81 @@ std::optional<RootInfo> SymbolService::getRootType(const irequest& req) {
return RootInfo{paramName, paramType};
}

std::optional<lldb::SBType> SymbolService::getLLDBRootType(const irequest& req) {
auto lldbTarget = getLLDBTarget();

if (req.type == "global") {
/*
* This is super simple as all we have to do is locate assign the
* type of the provided global variable.
*/
VLOG(1) << "Processing global: " << req.func;

auto globalDesc = findGlobalDesc(req.func);
if (!globalDesc) {
return std::nullopt;
}

auto globalVariable = lldbTarget.FindFirstGlobalVariable(req.func.c_str());
if (!globalVariable.IsValid()) {
LOG(ERROR) << "Failed to lookup global variable '" << req.func << "'";
return std::nullopt;
}

return globalVariable.GetType();
}

VLOG(1) << "Passing : " << req.func;
auto fd = findFuncDesc(req);
if (!fd) {
VLOG(1) << "Failed to lookup function " << req.func;
return std::nullopt;
}

auto functions = lldbTarget.FindFunctions(req.func.c_str());
if (functions.GetSize() != 1) {
LOG(ERROR) << "Failed to lookup function '" << req.func << "'";
return std::nullopt;
}

auto function = functions.GetContextAtIndex(0).GetFunction();
if (!function.IsValid()) {
LOG(ERROR) << "Failed to lookup function '" << req.func << "'";
return std::nullopt;
}

if (req.isReturnRetVal()) {
VLOG(1) << "Processing return retval";
return function.GetType().GetFunctionReturnType();
}

if (req.arg == "this") {
VLOG(1) << "Processing this pointer";
auto vars = function.GetBlock().GetVariables(lldbTarget, true, true, true);
for (uint32_t i = 0; i < vars.GetSize(); ++i) {
auto var = vars.GetValueAtIndex(i);
if (strcmp(var.GetName(), "this"))
return var.GetType();
}

LOG(ERROR) << "This pointer not found in function '" << req.func << "'";
return std::nullopt;
}

auto argIdx = fd->getArgumentIndex(req.arg);
if (!argIdx.has_value()) {
LOG(ERROR) << "Failed to lookup argument " << req.arg << " in function '"
<< req.func << "'";
return std::nullopt;
}

auto args = function.GetBlock().GetVariables(lldbTarget, true, false, false);
if (!args.IsValid() || args.GetSize() <= argIdx.value()) {
LOG(ERROR) << "Failed to lookup arguments in function '" << req.func << "'";
return std::nullopt;
}

return args.GetValueAtIndex(*argIdx).GetType();
}

} // namespace oi::detail
Loading