From 366d0c64ef36e54f3673bd342c73b9e9f6a6d07a Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Sat, 9 Dec 2023 16:34:01 -0800 Subject: [PATCH 01/20] Add skeleton for typegraph::LLDBParser --- oi/type_graph/CMakeLists.txt | 1 + oi/type_graph/LLDBParser.cpp | 32 ++ oi/type_graph/LLDBParser.h | 47 +++ test/CMakeLists.txt | 1 + test/test_drgn_parser.cpp | 4 +- test/test_lldb_parser.cpp | 697 +++++++++++++++++++++++++++++++++++ test/test_lldb_parser.h | 48 +++ 7 files changed, 828 insertions(+), 2 deletions(-) create mode 100644 oi/type_graph/LLDBParser.cpp create mode 100644 oi/type_graph/LLDBParser.h create mode 100644 test/test_lldb_parser.cpp create mode 100644 test/test_lldb_parser.h diff --git a/oi/type_graph/CMakeLists.txt b/oi/type_graph/CMakeLists.txt index 190bdfcd..8eccddb0 100644 --- a/oi/type_graph/CMakeLists.txt +++ b/oi/type_graph/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(type_graph Flattener.cpp IdentifyContainers.cpp KeyCapture.cpp + LLDBParser.cpp NameGen.cpp PassManager.cpp Printer.cpp diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp new file mode 100644 index 00000000..4a9f5573 --- /dev/null +++ b/oi/type_graph/LLDBParser.cpp @@ -0,0 +1,32 @@ +/* + * 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. + */ +#include "LLDBParser.h" + +#include + +namespace oi::detail::type_graph { + +Type& LLDBParser::parse(lldb::SBType* root) { + // The following two lines silence a warning about unused variables. + // Remove them once we have enough implementation to resolve the warning. + typeGraph_.size(); + options_ = {}; + + depth_ = 0; + throw std::runtime_error("Not implemented yet!"); +} + +} // namespace oi::detail::type_graph diff --git a/oi/type_graph/LLDBParser.h b/oi/type_graph/LLDBParser.h new file mode 100644 index 00000000..4c4b6427 --- /dev/null +++ b/oi/type_graph/LLDBParser.h @@ -0,0 +1,47 @@ +/* + * 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. + */ +#pragma once + +#include "TypeGraph.h" +#include "Types.h" + +// TODO: use LLDB's includes +namespace lldb { +class SBType; +} + +namespace oi::detail::type_graph { + +struct LLDBParserOptions {}; + +class LLDBParser { + public: + LLDBParser(TypeGraph& typeGraph, LLDBParserOptions options) + : typeGraph_(typeGraph), options_(options) {} + Type& parse(lldb::SBType* root); + + private: + TypeGraph& typeGraph_; + int depth_; + LLDBParserOptions options_; +}; + +class LLDBParserError : public std::runtime_error { + public: + LLDBParserError(const std::string& msg) : std::runtime_error{msg} {} +}; + +} // namespace oi::detail::type_graph diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 287ebcda..d24b3c58 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -45,6 +45,7 @@ add_executable(test_type_graph test_flattener.cpp test_identify_containers.cpp test_key_capture.cpp + test_lldb_parser.cpp test_name_gen.cpp test_node_tracker.cpp test_prune.cpp diff --git a/test/test_drgn_parser.cpp b/test/test_drgn_parser.cpp index e8d03e3d..799c288b 100644 --- a/test/test_drgn_parser.cpp +++ b/test/test_drgn_parser.cpp @@ -73,7 +73,7 @@ void DrgnParserTest::test(std::string_view function, test(function, expected, options); } -std::pair globMatch(std::string_view pattern, +static std::pair globMatch(std::string_view pattern, std::string_view str) { size_t i = 0; size_t j = 0; @@ -114,7 +114,7 @@ std::pair globMatch(std::string_view pattern, return {j, i}; } -std::string prefixLines(std::string_view str, +static std::string prefixLines(std::string_view str, std::string_view prefix, size_t maxLen) { std::string res; diff --git a/test/test_lldb_parser.cpp b/test/test_lldb_parser.cpp new file mode 100644 index 00000000..5b04a1b9 --- /dev/null +++ b/test/test_lldb_parser.cpp @@ -0,0 +1,697 @@ +#include "test_lldb_parser.h" + +#include + +#include + +#include "oi/OIParser.h" +#include "oi/SymbolService.h" +#include "oi/type_graph/NodeTracker.h" +#include "oi/type_graph/Printer.h" +#include "oi/type_graph/TypeGraph.h" +#include "oi/type_graph/Types.h" + +using namespace type_graph; + +// TODO setup google logging for tests so it doesn't appear on terminal by +// default + +SymbolService* LLDBParserTest::symbols_ = nullptr; + +void LLDBParserTest::SetUpTestSuite() { + symbols_ = new SymbolService{TARGET_EXE_PATH}; +} + +void LLDBParserTest::TearDownTestSuite() { + delete symbols_; +} + +LLDBParser LLDBParserTest::getLLDBParser(TypeGraph& typeGraph, + LLDBParserOptions options) { + LLDBParser lldbParser{typeGraph, options}; + return lldbParser; +} + +lldb::SBType* LLDBParserTest::getLLDBRoot(std::string_view function) { + irequest req{"entry", std::string{function}, "arg0"}; + auto* lldbRoot = symbols_->getRootType(req)->type.type; + return nullptr; +} + +std::string LLDBParserTest::run(std::string_view function, + LLDBParserOptions options) { + TypeGraph typeGraph; + auto lldbParser = getLLDBParser(typeGraph, options); + auto* lldbRoot = getLLDBRoot(function); + + Type& type = lldbParser.parse(lldbRoot); + + std::stringstream out; + NodeTracker tracker; + Printer printer{out, tracker, typeGraph.size()}; + printer.print(type); + + return out.str(); +} + +void LLDBParserTest::test(std::string_view function, + std::string_view expected, + LLDBParserOptions options) { + auto actual = run(function, options); + + expected.remove_prefix(1); // Remove initial '\n' + EXPECT_EQ(expected, actual); +} + +void LLDBParserTest::test(std::string_view function, + std::string_view expected) { + // Enable options in unit tests so we get more coverage + LLDBParserOptions options = {}; + test(function, expected, options); +} + +static std::pair globMatch(std::string_view pattern, + std::string_view str) { + size_t i = 0; + size_t j = 0; + size_t prevWildcardIdx = -1; + size_t backtrackIdx = -1; + size_t patternLineStart = 0; + size_t strLineStart = 0; + + while (i < str.size()) { + if (i + 1 < str.size() && str[i] == '\n') + strLineStart = i + 1; + if (j + 1 < pattern.size() && pattern[j] == '\n') + patternLineStart = j + 1; + + if (j < pattern.size() && str[i] == pattern[j]) { + // Exact character match + i++; + j++; + } else if (j < pattern.size() && pattern[j] == '*') { + // Wildcard + backtrackIdx = i + 1; + prevWildcardIdx = j++; + } else if (prevWildcardIdx != static_cast(-1)) { + // No match, backtrack to previous wildcard + i = ++backtrackIdx; + j = prevWildcardIdx + 1; + } else { + // No match + return {patternLineStart, strLineStart}; + } + } + + while (j < pattern.size() && pattern[j] == '*') { + j++; + } + + // If the pattern has been fully consumed then it's a match + return {j, i}; +} + +static std::string prefixLines(std::string_view str, + std::string_view prefix, + size_t maxLen) { + std::string res; + res += prefix; + for (size_t i = 0; i < maxLen && i < str.size(); i++) { + char c = str[i]; + res += c; + if (c == '\n') { + res += prefix; + } + } + if (str.size() > maxLen) + res += "..."; + return res; +} + +void LLDBParserTest::testGlob(std::string_view function, + std::string_view expected, + LLDBParserOptions options) { + auto actual = run(function, options); + + expected.remove_prefix(1); // Remove initial '\n' + auto [expectedIdx, actualIdx] = globMatch(expected, actual); + if (expected.size() != expectedIdx) { + ADD_FAILURE() << prefixLines(expected.substr(expectedIdx), "-", 10000) + << "\n" + << prefixLines(actual.substr(actualIdx), "+", 10000); + } +} + +void LLDBParserTest::testMultiCompiler( + std::string_view function, + [[maybe_unused]] std::string_view expectedClang, + [[maybe_unused]] std::string_view expectedGcc, + LLDBParserOptions options) { +#if defined(__clang__) + test(function, expectedClang, options); +#else + test(function, expectedGcc, options); +#endif +} + +void LLDBParserTest::testMultiCompilerGlob( + std::string_view function, + [[maybe_unused]] std::string_view expectedClang, + [[maybe_unused]] std::string_view expectedGcc, + LLDBParserOptions options) { +#if defined(__clang__) + testGlob(function, expectedClang, options); +#else + testGlob(function, expectedGcc, options); +#endif +} + +TEST_F(LLDBParserTest, SimpleStruct) { + test("oid_test_case_simple_struct", R"( +[1] Pointer +[0] Struct: SimpleStruct (size: 16) + Member: a (offset: 0) + Primitive: int32_t + Member: b (offset: 4) + Primitive: int8_t + Member: c (offset: 8) + Primitive: int64_t +)"); +} + +TEST_F(LLDBParserTest, SimpleClass) { + test("oid_test_case_simple_class", R"( +[1] Pointer +[0] Class: SimpleClass (size: 16) + Member: a (offset: 0) + Primitive: int32_t + Member: b (offset: 4) + Primitive: int8_t + Member: c (offset: 8) + Primitive: int64_t +)"); +} + +TEST_F(LLDBParserTest, SimpleUnion) { + test("oid_test_case_simple_union", R"( +[1] Pointer +[0] Union: SimpleUnion (size: 8) + Member: a (offset: 0) + Primitive: int32_t + Member: b (offset: 0) + Primitive: int8_t + Member: c (offset: 0) + Primitive: int64_t +)"); +} + +TEST_F(LLDBParserTest, Inheritance) { + test("oid_test_case_inheritance_access_public", R"( +[4] Pointer +[0] Class: Public (size: 8) + Parent (offset: 0) +[1] Class: Base (size: 4) + Member: base_int (offset: 0) +[3] Typedef: int32_t +[2] Typedef: __int32_t + Primitive: int32_t + Member: public_int (offset: 4) + [3] +)"); +} + +TEST_F(LLDBParserTest, InheritanceMultiple) { + test("oid_test_case_inheritance_multiple_a", R"( +[6] Pointer +[0] Struct: Derived_2 (size: 24) + Parent (offset: 0) +[1] Struct: Base_1 (size: 4) + Member: a (offset: 0) + Primitive: int32_t + Parent (offset: 4) +[2] Struct: Derived_1 (size: 12) + Parent (offset: 0) +[3] Struct: Base_2 (size: 4) + Member: b (offset: 0) + Primitive: int32_t + Parent (offset: 4) +[4] Struct: Base_3 (size: 4) + Member: c (offset: 0) + Primitive: int32_t + Member: d (offset: 8) + Primitive: int32_t + Parent (offset: 16) +[5] Struct: Base_4 (size: 4) + Member: e (offset: 0) + Primitive: int32_t + Member: f (offset: 20) + Primitive: int32_t +)"); +} + +TEST_F(LLDBParserTest, Container) { + testMultiCompilerGlob("oid_test_case_std_vector_int_empty", + R"( +[13] Pointer +[0] Class: vector > (size: 24) + Param + Primitive: int32_t + Param +[1] Class: allocator (size: 1) + Param + Primitive: int32_t + Parent (offset: 0) +[3] Typedef: __allocator_base +[2] Class: new_allocator (size: 1) + Param + Primitive: int32_t + Function: new_allocator + Function: new_allocator + Function: allocate + Function: deallocate + Function: _M_max_size + Function: allocator + Function: allocator + Function: operator= + Function: ~allocator + Function: allocate + Function: deallocate + Parent (offset: 0) +* +)", + R"( +[9] Pointer +[0] Class: vector > (size: 24) + Param + Primitive: int32_t + Param +[1] Class: allocator (size: 1) + Parent (offset: 0) +[2] Class: new_allocator (size: 1) + Param + Primitive: int32_t + Function: new_allocator + Function: new_allocator + Function: allocate + Function: deallocate + Function: _M_max_size + Function: allocator + Function: allocator + Function: operator= + Function: ~allocator + Function: allocate + Function: deallocate + Parent (offset: 0) +* +)"); +} +// TODO test vector with custom allocator + +TEST_F(LLDBParserTest, Enum) { + test("oid_test_case_enums_scoped", R"( + Enum: ScopedEnum (size: 4) + Enumerator: 0:CaseA + Enumerator: 1:CaseB + Enumerator: 2:CaseC +)"); +} + +TEST_F(LLDBParserTest, EnumUint8) { + test("oid_test_case_enums_scoped_uint8", R"( + Enum: ScopedEnumUint8 (size: 1) + Enumerator: 2:CaseA + Enumerator: 3:CaseB + Enumerator: 4:CaseC +)"); +} + +TEST_F(LLDBParserTest, UnscopedEnum) { + test("oid_test_case_enums_unscoped", R"( + Enum: UNSCOPED_ENUM (size: 4) + Enumerator: -2:CASE_B + Enumerator: 5:CASE_A + Enumerator: 20:CASE_C +)"); +} + +TEST_F(LLDBParserTest, EnumNoValues) { + LLDBParserOptions options{}; + test("oid_test_case_enums_scoped", + R"( + Enum: ScopedEnum (size: 4) +)", + options); +} + +TEST_F(LLDBParserTest, Typedef) { + test("oid_test_case_typedefs_c_style", R"( +[2] Typedef: TdUInt64 +[1] Typedef: uint64_t +[0] Typedef: __uint64_t + Primitive: uint64_t +)"); +} + +TEST_F(LLDBParserTest, Using) { + test("oid_test_case_typedefs_using", R"( +[2] Typedef: UsingUInt64 +[1] Typedef: uint64_t +[0] Typedef: __uint64_t + Primitive: uint64_t +)"); +} + +TEST_F(LLDBParserTest, ArrayMember) { + test("oid_test_case_arrays_member_int10", R"( +[2] Pointer +[0] Struct: Foo10 (size: 40) + Member: arr (offset: 0) +[1] Array: (length: 10) + Primitive: int32_t +)"); +} + +TEST_F(LLDBParserTest, ArrayRef) { + test("oid_test_case_arrays_ref_int10", R"( +[1] Pointer +[0] Array: (length: 10) + Primitive: int32_t +)"); +} + +TEST_F(LLDBParserTest, ArrayDirect) { + test("oid_test_case_arrays_direct_int10", R"( +[0] Pointer + Primitive: int32_t +)"); +} + +TEST_F(LLDBParserTest, Pointer) { + test("oid_test_case_pointers_struct_primitive_ptrs", R"( +[3] Pointer +[0] Struct: PrimitivePtrs (size: 24) + Member: a (offset: 0) + Primitive: int32_t + Member: b (offset: 8) +[1] Pointer + Primitive: int32_t + Member: c (offset: 16) +[2] Pointer + Primitive: void +)"); +} + +TEST_F(LLDBParserTest, PointerNoFollow) { + LLDBParserOptions options{}; + test("oid_test_case_pointers_struct_primitive_ptrs", + R"( +[1] Pointer +[0] Struct: PrimitivePtrs (size: 24) + Member: a (offset: 0) + Primitive: int32_t + Member: b (offset: 8) + Primitive: StubbedPointer + Member: c (offset: 16) + Primitive: StubbedPointer +)", + options); +} + +TEST_F(LLDBParserTest, PointerIncomplete) { + test("oid_test_case_pointers_incomplete_raw", R"( +[0] Pointer + Incomplete: [IncompleteType] +)"); +} + +TEST_F(LLDBParserTest, Cycle) { + test("oid_test_case_cycles_raw_ptr", R"( +[2] Pointer +[0] Struct: RawNode (size: 16) + Member: value (offset: 0) + Primitive: int32_t + Member: next (offset: 8) +[1] Pointer + [0] +)"); +} + +TEST_F(LLDBParserTest, ClassTemplateInt) { + test("oid_test_case_templates_int", R"( +[1] Pointer +[0] Class: TemplatedClass1 (size: 4) + Param + Primitive: int32_t + Member: val (offset: 0) + Primitive: int32_t +)"); +} + +TEST_F(LLDBParserTest, ClassTemplateTwo) { + test("oid_test_case_templates_two", R"( +[3] Pointer +[0] Class: TemplatedClass2 (size: 12) + Param +[1] Struct: Foo (size: 8) + Member: a (offset: 0) + Primitive: int32_t + Member: b (offset: 4) + Primitive: int32_t + Param + Primitive: int32_t + Member: tc1 (offset: 0) +[2] Class: TemplatedClass1 (size: 8) + Param + [1] + Member: val (offset: 0) + [1] + Member: val2 (offset: 8) + Primitive: int32_t +)"); +} + +TEST_F(LLDBParserTest, ClassTemplateValue) { + test("oid_test_case_templates_value", R"( +[2] Pointer +[0] Struct: TemplatedClassVal<3> (size: 12) + Param + Value: 3 + Primitive: int32_t + Member: arr (offset: 0) +[1] Array: (length: 3) + Primitive: int32_t +)"); +} + +TEST_F(LLDBParserTest, TemplateEnumValue) { + testMultiCompilerGlob("oid_test_case_enums_params_scoped_enum_val", + R"( +[1] Pointer +[0] Class: MyClass (size: 4) + Param + Value: ns_enums_params::MyNS::ScopedEnum::One + Enum: ScopedEnum (size: 4) +* +)", + R"( +[1] Pointer +[0] Class: MyClass<(ns_enums_params::MyNS::ScopedEnum)1> (size: 4) + Param + Value: ns_enums_params::MyNS::ScopedEnum::One + Enum: ScopedEnum (size: 4) +* +)"); +} + +TEST_F(LLDBParserTest, TemplateEnumValueGaps) { + testMultiCompilerGlob("oid_test_case_enums_params_scoped_enum_val_gaps", + R"( +[1] Pointer +[0] Class: ClassGaps (size: 4) + Param + Value: ns_enums_params::MyNS::EnumWithGaps::Twenty + Enum: EnumWithGaps (size: 4) +* +)", + R"( +[1] Pointer +[0] Class: ClassGaps<(ns_enums_params::MyNS::EnumWithGaps)20> (size: 4) + Param + Value: ns_enums_params::MyNS::EnumWithGaps::Twenty + Enum: EnumWithGaps (size: 4) +* +)"); +} + +TEST_F(LLDBParserTest, TemplateEnumValueNegative) { + testMultiCompilerGlob("oid_test_case_enums_params_scoped_enum_val_negative", + R"( +[1] Pointer +[0] Class: ClassGaps (size: 4) + Param + Value: ns_enums_params::MyNS::EnumWithGaps::MinusTwo + Enum: EnumWithGaps (size: 4) +* +)", + R"( +[1] Pointer +[0] Class: ClassGaps<(ns_enums_params::MyNS::EnumWithGaps)-2> (size: 4) + Param + Value: ns_enums_params::MyNS::EnumWithGaps::MinusTwo + Enum: EnumWithGaps (size: 4) +* +)"); +} + +// TODO +// TEST_F(LLDBParserTest, ClassFunctions) { +// test("TestClassFunctions", R"( +//[0] Pointer +//[1] Class: ClassFunctions (size: 4) +// Member: memberA (offset: 0) +// Primitive: int32_t +// Function: foo (virtuality: 0) +// Function: bar (virtuality: 0) +//)"); +//} + +TEST_F(LLDBParserTest, StructAlignment) { + test("oid_test_case_alignment_struct", R"( +[1] Pointer +[0] Struct: Align16 (size: 16, align: 16) + Member: c (offset: 0) + Primitive: int8_t +)"); +} + +TEST_F(LLDBParserTest, MemberAlignment) { + test("oid_test_case_alignment_member_alignment", R"( +[1] Pointer +[0] Struct: MemberAlignment (size: 64) + Member: c (offset: 0) + Primitive: int8_t + Member: c32 (offset: 32, align: 32) + Primitive: int8_t +)"); +} + +TEST_F(LLDBParserTest, VirtualFunctions) { + testMultiCompiler("oid_test_case_inheritance_polymorphic_a_as_a", + R"( +[1] Pointer +[0] Class: A (size: 16) + Member: _vptr$A (offset: 0) + Primitive: StubbedPointer + Member: int_a (offset: 8) + Primitive: int32_t + Function: ~A (virtual) + Function: myfunc (virtual) + Function: A + Function: A +)", + R"( +[1] Pointer +[0] Class: A (size: 16) + Member: _vptr.A (offset: 0) + Primitive: StubbedPointer + Member: int_a (offset: 8) + Primitive: int32_t + Function: operator= + Function: A + Function: A + Function: ~A (virtual) + Function: myfunc (virtual) +)"); +} + +TEST_F(LLDBParserTest, BitfieldsSingle) { + test("oid_test_case_bitfields_single", R"( +[1] Pointer +[0] Struct: Single (size: 4) + Member: bitfield (offset: 0, bitsize: 3) + Primitive: int32_t +)"); +} + +TEST_F(LLDBParserTest, BitfieldsWithinBytes) { + test("oid_test_case_bitfields_within_bytes", R"( +[1] Pointer +[0] Struct: WithinBytes (size: 2) + Member: a (offset: 0, bitsize: 3) + Primitive: int8_t + Member: b (offset: 0.375, bitsize: 5) + Primitive: int8_t + Member: c (offset: 1, bitsize: 7) + Primitive: int8_t +)"); +} + +TEST_F(LLDBParserTest, BitfieldsStraddleBytes) { + test("oid_test_case_bitfields_straddle_bytes", R"( +[1] Pointer +[0] Struct: StraddleBytes (size: 3) + Member: a (offset: 0, bitsize: 7) + Primitive: int8_t + Member: b (offset: 1, bitsize: 7) + Primitive: int8_t + Member: c (offset: 2, bitsize: 2) + Primitive: int8_t +)"); +} + +TEST_F(LLDBParserTest, BitfieldsMixed) { + test("oid_test_case_bitfields_mixed", R"( +[1] Pointer +[0] Struct: Mixed (size: 12) + Member: a (offset: 0) + Primitive: int32_t + Member: b (offset: 4, bitsize: 4) + Primitive: int8_t + Member: c (offset: 4.5, bitsize: 12) + Primitive: int16_t + Member: d (offset: 6) + Primitive: int8_t + Member: e (offset: 8, bitsize: 22) + Primitive: int32_t +)"); +} + +TEST_F(LLDBParserTest, BitfieldsMoreBitsThanType) { + GTEST_SKIP() << "LLDB errors out"; + test("oid_test_case_bitfields_more_bits_than_type", R"( +[1] Pointer +[0] Struct: MoreBitsThanType (size: 4) + Member: a (offset: 0, bitsize: 8) + Primitive: int8_t +)"); +} + +TEST_F(LLDBParserTest, BitfieldsZeroBits) { + test("oid_test_case_bitfields_zero_bits", R"( +[1] Pointer +[0] Struct: ZeroBits (size: 2) + Member: b1 (offset: 0, bitsize: 3) + Primitive: int8_t + Member: b2 (offset: 1, bitsize: 2) + Primitive: int8_t +)"); +} + +TEST_F(LLDBParserTest, BitfieldsEnum) { + test("oid_test_case_bitfields_enum", R"( +[1] Pointer +[0] Struct: Enum (size: 4) + Member: e (offset: 0, bitsize: 2) + Enum: MyEnum (size: 4) + Enumerator: 0:One + Enumerator: 1:Two + Enumerator: 2:Three + Member: f (offset: 0.25, bitsize: 4) + Enum: MyEnum (size: 4) + Enumerator: 0:One + Enumerator: 1:Two + Enumerator: 2:Three +)"); +} + +// TODO test virtual classes diff --git a/test/test_lldb_parser.h b/test/test_lldb_parser.h new file mode 100644 index 00000000..9fec06e8 --- /dev/null +++ b/test/test_lldb_parser.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include "oi/type_graph/LLDBParser.h" + +namespace oi::detail { +class SymbolService; +} +namespace oi::detail::type_graph { +class TypeGraph; +} +// TODO: use LLDB's includes +namespace lldb { +class SBType; +} + +using namespace oi::detail; + +class LLDBParserTest : public ::testing::Test { + protected: + static void SetUpTestSuite(); + static void TearDownTestSuite(); + + static type_graph::LLDBParser getLLDBParser( + type_graph::TypeGraph& typeGraph, type_graph::LLDBParserOptions options); + lldb::SBType* getLLDBRoot(std::string_view function); + + virtual std::string run(std::string_view function, + type_graph::LLDBParserOptions options); + void test(std::string_view function, + std::string_view expected, + type_graph::LLDBParserOptions options); + void test(std::string_view function, std::string_view expected); + void testGlob(std::string_view function, + std::string_view expected, + type_graph::LLDBParserOptions options = {}); + void testMultiCompiler(std::string_view function, + std::string_view expectedClang, + std::string_view expectedGcc, + type_graph::LLDBParserOptions options = {}); + void testMultiCompilerGlob(std::string_view function, + std::string_view expectedClang, + std::string_view expectedGcc, + type_graph::LLDBParserOptions options = {}); + + static SymbolService* symbols_; +}; From 5bd59672724c391348d145e55cfd3fef837c0029 Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Sun, 10 Dec 2023 15:23:55 -0800 Subject: [PATCH 02/20] Locate function argument's type --- CMakeLists.txt | 2 ++ oi/type_graph/CMakeLists.txt | 1 + oi/type_graph/LLDBParser.cpp | 2 ++ test/test_lldb_parser.cpp | 28 +++++++++++++++++++--------- test/test_lldb_parser.h | 11 +++++------ 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7f9ee7b..bd68e473 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) + ### 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. diff --git a/oi/type_graph/CMakeLists.txt b/oi/type_graph/CMakeLists.txt index 8eccddb0..d6cbfb76 100644 --- a/oi/type_graph/CMakeLists.txt +++ b/oi/type_graph/CMakeLists.txt @@ -25,6 +25,7 @@ target_link_libraries(type_graph container_info symbol_service + LLDB "-L${DRGN_PATH}/.libs" drgn ) diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index 4a9f5573..c66f1567 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -16,6 +16,7 @@ #include "LLDBParser.h" #include +#include namespace oi::detail::type_graph { @@ -26,6 +27,7 @@ Type& LLDBParser::parse(lldb::SBType* root) { options_ = {}; depth_ = 0; + LOG(WARNING) << "Parsing lldb type: " << root->GetName(); throw std::runtime_error("Not implemented yet!"); } diff --git a/test/test_lldb_parser.cpp b/test/test_lldb_parser.cpp index 5b04a1b9..25bad33e 100644 --- a/test/test_lldb_parser.cpp +++ b/test/test_lldb_parser.cpp @@ -1,6 +1,8 @@ #include "test_lldb_parser.h" #include +#include +#include #include @@ -16,14 +18,19 @@ using namespace type_graph; // TODO setup google logging for tests so it doesn't appear on terminal by // default -SymbolService* LLDBParserTest::symbols_ = nullptr; +lldb::SBDebugger LLDBParserTest::debugger_; +lldb::SBTarget LLDBParserTest::target_; void LLDBParserTest::SetUpTestSuite() { - symbols_ = new SymbolService{TARGET_EXE_PATH}; + lldb::SBDebugger::Initialize(); + debugger_ = lldb::SBDebugger::Create(false); + target_ = debugger_.CreateTarget(TARGET_EXE_PATH); } void LLDBParserTest::TearDownTestSuite() { - delete symbols_; + debugger_.DeleteTarget(target_); + lldb::SBDebugger::Destroy(debugger_); + lldb::SBDebugger::Terminate(); } LLDBParser LLDBParserTest::getLLDBParser(TypeGraph& typeGraph, @@ -32,19 +39,22 @@ LLDBParser LLDBParserTest::getLLDBParser(TypeGraph& typeGraph, return lldbParser; } -lldb::SBType* LLDBParserTest::getLLDBRoot(std::string_view function) { - irequest req{"entry", std::string{function}, "arg0"}; - auto* lldbRoot = symbols_->getRootType(req)->type.type; - return nullptr; +lldb::SBType LLDBParserTest::getLLDBRoot(std::string_view function) { + auto fns = target_.FindFunctions(function.data()); + assert(fns.GetSize() == 1); + auto fn = fns.GetContextAtIndex(0).GetFunction(); + auto args = fn.GetBlock().GetVariables(target_, true, false, false); + assert(args.GetSize() >= 1); + return args.GetValueAtIndex(0).GetType(); } std::string LLDBParserTest::run(std::string_view function, LLDBParserOptions options) { TypeGraph typeGraph; auto lldbParser = getLLDBParser(typeGraph, options); - auto* lldbRoot = getLLDBRoot(function); + auto lldbRoot = getLLDBRoot(function); - Type& type = lldbParser.parse(lldbRoot); + Type& type = lldbParser.parse(&lldbRoot); std::stringstream out; NodeTracker tracker; diff --git a/test/test_lldb_parser.h b/test/test_lldb_parser.h index 9fec06e8..066278a2 100644 --- a/test/test_lldb_parser.h +++ b/test/test_lldb_parser.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include "oi/type_graph/LLDBParser.h" @@ -10,10 +12,6 @@ class SymbolService; namespace oi::detail::type_graph { class TypeGraph; } -// TODO: use LLDB's includes -namespace lldb { -class SBType; -} using namespace oi::detail; @@ -24,7 +22,7 @@ class LLDBParserTest : public ::testing::Test { static type_graph::LLDBParser getLLDBParser( type_graph::TypeGraph& typeGraph, type_graph::LLDBParserOptions options); - lldb::SBType* getLLDBRoot(std::string_view function); + lldb::SBType getLLDBRoot(std::string_view function); virtual std::string run(std::string_view function, type_graph::LLDBParserOptions options); @@ -44,5 +42,6 @@ class LLDBParserTest : public ::testing::Test { std::string_view expectedGcc, type_graph::LLDBParserOptions options = {}); - static SymbolService* symbols_; + static lldb::SBDebugger debugger_; + static lldb::SBTarget target_; }; From 94e957ae74f8528034239c95b06f39aae39224fb Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Mon, 11 Dec 2023 05:04:42 -0800 Subject: [PATCH 03/20] Move globMatch and prefixLines in type_graph_utils --- CMakeLists.txt | 2 +- oi/type_graph/LLDBParser.h | 6 ++-- test/test_drgn_parser.cpp | 59 +------------------------------------- test/test_lldb_parser.cpp | 59 +------------------------------------- test/test_lldb_parser.h | 2 +- test/type_graph_utils.cpp | 58 +++++++++++++++++++++++++++++++++++++ test/type_graph_utils.h | 6 ++++ 7 files changed, 72 insertions(+), 120 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd68e473..c7c712fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,7 +185,7 @@ find_package(Clang REQUIRED CONFIG) message(STATUS "Found Clang ${LLVM_PACKAGE_VERSION}") message(STATUS "Using ClangConfig.cmake in: ${Clang_DIR}") -find_package(LLDB) +find_package(LLDB REQUIRED) ### msgpack # msgpack v3.0.0 doesn't define the msgpackc-cxx target, but since the library is header only, diff --git a/oi/type_graph/LLDBParser.h b/oi/type_graph/LLDBParser.h index 4c4b6427..a72a8d15 100644 --- a/oi/type_graph/LLDBParser.h +++ b/oi/type_graph/LLDBParser.h @@ -30,7 +30,8 @@ struct LLDBParserOptions {}; class LLDBParser { public: LLDBParser(TypeGraph& typeGraph, LLDBParserOptions options) - : typeGraph_(typeGraph), options_(options) {} + : typeGraph_(typeGraph), options_(options) { + } Type& parse(lldb::SBType* root); private: @@ -41,7 +42,8 @@ class LLDBParser { class LLDBParserError : public std::runtime_error { public: - LLDBParserError(const std::string& msg) : std::runtime_error{msg} {} + LLDBParserError(const std::string& msg) : std::runtime_error{msg} { + } }; } // namespace oi::detail::type_graph diff --git a/test/test_drgn_parser.cpp b/test/test_drgn_parser.cpp index 799c288b..c4e51e1d 100644 --- a/test/test_drgn_parser.cpp +++ b/test/test_drgn_parser.cpp @@ -10,6 +10,7 @@ #include "oi/type_graph/Printer.h" #include "oi/type_graph/TypeGraph.h" #include "oi/type_graph/Types.h" +#include "type_graph_utils.h" using namespace type_graph; @@ -73,64 +74,6 @@ void DrgnParserTest::test(std::string_view function, test(function, expected, options); } -static std::pair globMatch(std::string_view pattern, - std::string_view str) { - size_t i = 0; - size_t j = 0; - size_t prevWildcardIdx = -1; - size_t backtrackIdx = -1; - size_t patternLineStart = 0; - size_t strLineStart = 0; - - while (i < str.size()) { - if (i + 1 < str.size() && str[i] == '\n') - strLineStart = i + 1; - if (j + 1 < pattern.size() && pattern[j] == '\n') - patternLineStart = j + 1; - - if (j < pattern.size() && str[i] == pattern[j]) { - // Exact character match - i++; - j++; - } else if (j < pattern.size() && pattern[j] == '*') { - // Wildcard - backtrackIdx = i + 1; - prevWildcardIdx = j++; - } else if (prevWildcardIdx != static_cast(-1)) { - // No match, backtrack to previous wildcard - i = ++backtrackIdx; - j = prevWildcardIdx + 1; - } else { - // No match - return {patternLineStart, strLineStart}; - } - } - - while (j < pattern.size() && pattern[j] == '*') { - j++; - } - - // If the pattern has been fully consumed then it's a match - return {j, i}; -} - -static std::string prefixLines(std::string_view str, - std::string_view prefix, - size_t maxLen) { - std::string res; - res += prefix; - for (size_t i = 0; i < maxLen && i < str.size(); i++) { - char c = str[i]; - res += c; - if (c == '\n') { - res += prefix; - } - } - if (str.size() > maxLen) - res += "..."; - return res; -} - void DrgnParserTest::testGlob(std::string_view function, std::string_view expected, DrgnParserOptions options) { diff --git a/test/test_lldb_parser.cpp b/test/test_lldb_parser.cpp index 25bad33e..60d2bacb 100644 --- a/test/test_lldb_parser.cpp +++ b/test/test_lldb_parser.cpp @@ -12,6 +12,7 @@ #include "oi/type_graph/Printer.h" #include "oi/type_graph/TypeGraph.h" #include "oi/type_graph/Types.h" +#include "type_graph_utils.h" using namespace type_graph; @@ -80,64 +81,6 @@ void LLDBParserTest::test(std::string_view function, test(function, expected, options); } -static std::pair globMatch(std::string_view pattern, - std::string_view str) { - size_t i = 0; - size_t j = 0; - size_t prevWildcardIdx = -1; - size_t backtrackIdx = -1; - size_t patternLineStart = 0; - size_t strLineStart = 0; - - while (i < str.size()) { - if (i + 1 < str.size() && str[i] == '\n') - strLineStart = i + 1; - if (j + 1 < pattern.size() && pattern[j] == '\n') - patternLineStart = j + 1; - - if (j < pattern.size() && str[i] == pattern[j]) { - // Exact character match - i++; - j++; - } else if (j < pattern.size() && pattern[j] == '*') { - // Wildcard - backtrackIdx = i + 1; - prevWildcardIdx = j++; - } else if (prevWildcardIdx != static_cast(-1)) { - // No match, backtrack to previous wildcard - i = ++backtrackIdx; - j = prevWildcardIdx + 1; - } else { - // No match - return {patternLineStart, strLineStart}; - } - } - - while (j < pattern.size() && pattern[j] == '*') { - j++; - } - - // If the pattern has been fully consumed then it's a match - return {j, i}; -} - -static std::string prefixLines(std::string_view str, - std::string_view prefix, - size_t maxLen) { - std::string res; - res += prefix; - for (size_t i = 0; i < maxLen && i < str.size(); i++) { - char c = str[i]; - res += c; - if (c == '\n') { - res += prefix; - } - } - if (str.size() > maxLen) - res += "..."; - return res; -} - void LLDBParserTest::testGlob(std::string_view function, std::string_view expected, LLDBParserOptions options) { diff --git a/test/test_lldb_parser.h b/test/test_lldb_parser.h index 066278a2..fd25d27e 100644 --- a/test/test_lldb_parser.h +++ b/test/test_lldb_parser.h @@ -1,8 +1,8 @@ #pragma once #include -#include #include +#include #include "oi/type_graph/LLDBParser.h" diff --git a/test/type_graph_utils.cpp b/test/type_graph_utils.cpp index 2d5709f3..3c81e96a 100644 --- a/test/type_graph_utils.cpp +++ b/test/type_graph_utils.cpp @@ -106,3 +106,61 @@ Container getPair(NodeId id) { return Container{ id, info, 8, nullptr}; // Nonsense size, shouldn't matter for tests } + +std::pair globMatch(std::string_view pattern, + std::string_view str) { + size_t i = 0; + size_t j = 0; + size_t prevWildcardIdx = -1; + size_t backtrackIdx = -1; + size_t patternLineStart = 0; + size_t strLineStart = 0; + + while (i < str.size()) { + if (i + 1 < str.size() && str[i] == '\n') + strLineStart = i + 1; + if (j + 1 < pattern.size() && pattern[j] == '\n') + patternLineStart = j + 1; + + if (j < pattern.size() && str[i] == pattern[j]) { + // Exact character match + i++; + j++; + } else if (j < pattern.size() && pattern[j] == '*') { + // Wildcard + backtrackIdx = i + 1; + prevWildcardIdx = j++; + } else if (prevWildcardIdx != static_cast(-1)) { + // No match, backtrack to previous wildcard + i = ++backtrackIdx; + j = prevWildcardIdx + 1; + } else { + // No match + return {patternLineStart, strLineStart}; + } + } + + while (j < pattern.size() && pattern[j] == '*') { + j++; + } + + // If the pattern has been fully consumed then it's a match + return {j, i}; +} + +std::string prefixLines(std::string_view str, + std::string_view prefix, + size_t maxLen) { + std::string res; + res += prefix; + for (size_t i = 0; i < maxLen && i < str.size(); i++) { + char c = str[i]; + res += c; + if (c == '\n') { + res += prefix; + } + } + if (str.size() > maxLen) + res += "..."; + return res; +} diff --git a/test/type_graph_utils.h b/test/type_graph_utils.h index 216b6b32..bba9fd55 100644 --- a/test/type_graph_utils.h +++ b/test/type_graph_utils.h @@ -28,3 +28,9 @@ type_graph::Container getVector(type_graph::NodeId id = 0); type_graph::Container getMap(type_graph::NodeId id = 0); type_graph::Container getList(type_graph::NodeId id = 0); type_graph::Container getPair(type_graph::NodeId id = 0); + +std::pair globMatch(std::string_view pattern, + std::string_view str); +std::string prefixLines(std::string_view str, + std::string_view prefix, + size_t maxLen); From 5fd221ef9d6c8888e383b1d453e59c11a37affec Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Mon, 11 Dec 2023 05:47:52 -0800 Subject: [PATCH 04/20] Add LLDB as a CircleCI dependency --- .circleci/config.yml | 4 +++- cmake/FindLLDB.cmake | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 cmake/FindLLDB.cmake diff --git a/.circleci/config.yml b/.circleci/config.yml index 0a75d995..c98ecdbd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -119,6 +119,7 @@ jobs: libmsgpack-dev \ libzstd-dev \ llvm-15-dev \ + liblldb-15-dev \ ninja-build \ pkg-config \ python3-setuptools @@ -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 @@ -166,6 +167,7 @@ jobs: libboost-all-dev \ libgflags-dev \ llvm-15-dev \ + liblldb-15-dev \ libfmt-dev \ libjemalloc-dev environment: diff --git a/cmake/FindLLDB.cmake b/cmake/FindLLDB.cmake new file mode 100644 index 00000000..871eb434 --- /dev/null +++ b/cmake/FindLLDB.cmake @@ -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() From ade185aad16777c8dcced97d3c6821db767e3fc6 Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Wed, 13 Dec 2023 08:00:15 -0800 Subject: [PATCH 05/20] Implement LLDBParser::enumerateEnum --- oi/type_graph/LLDBParser.cpp | 70 ++++++++++++++++++++++++++++++++---- oi/type_graph/LLDBParser.h | 14 +++++++- test/test_lldb_parser.cpp | 15 ++++---- 3 files changed, 84 insertions(+), 15 deletions(-) diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index c66f1567..cff617c2 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -21,14 +21,70 @@ namespace oi::detail::type_graph { Type& LLDBParser::parse(lldb::SBType* root) { - // The following two lines silence a warning about unused variables. - // Remove them once we have enough implementation to resolve the warning. - typeGraph_.size(); - options_ = {}; - depth_ = 0; - LOG(WARNING) << "Parsing lldb type: " << root->GetName(); - throw std::runtime_error("Not implemented yet!"); + return enumerateType(*root); +} + +struct DepthGuard { + explicit DepthGuard(int& depth) : depth_(depth) { + ++depth_; + } + ~DepthGuard() { + --depth_; + } + private: + int& depth_; +}; + +Type& LLDBParser::enumerateType(lldb::SBType& type) { + DepthGuard guard(depth_); + + Type *t = nullptr; + switch (auto kind = type.GetTypeClass()) { + case lldb::eTypeClassEnumeration: + t = &enumerateEnum(type); + break; + case lldb::eTypeClassInvalid: + case lldb::eTypeClassArray: + case lldb::eTypeClassBlockPointer: + case lldb::eTypeClassBuiltin: + case lldb::eTypeClassClass: + case lldb::eTypeClassComplexFloat: + case lldb::eTypeClassComplexInteger: + case lldb::eTypeClassFunction: + case lldb::eTypeClassMemberPointer: + case lldb::eTypeClassObjCObject: + case lldb::eTypeClassObjCInterface: + case lldb::eTypeClassObjCObjectPointer: + case lldb::eTypeClassPointer: + case lldb::eTypeClassReference: + case lldb::eTypeClassStruct: + case lldb::eTypeClassTypedef: + case lldb::eTypeClassUnion: + case lldb::eTypeClassVector: + case lldb::eTypeClassOther: + case lldb::eTypeClassAny: + throw std::runtime_error("Unhandled type class: " + std::to_string(kind)); + } + + return *t; +} + +Enum& LLDBParser::enumerateEnum(lldb::SBType& type) { + const char *typeName = type.GetName(); + std::string name = typeName ? typeName : ""; + uint64_t size = type.GetByteSize(); + + std::map enumeratorMap; + if (options_.readEnumValues) { + auto members = type.GetEnumMembers(); + for (uint32_t i = 0; i < members.GetSize(); i++) { + auto member = members.GetTypeEnumMemberAtIndex(i); + enumeratorMap[member.GetValueAsSigned()] = member.GetName(); + } + } + + return makeType(&type, name, size, std::move(enumeratorMap)); } } // namespace oi::detail::type_graph diff --git a/oi/type_graph/LLDBParser.h b/oi/type_graph/LLDBParser.h index a72a8d15..e84d6a40 100644 --- a/oi/type_graph/LLDBParser.h +++ b/oi/type_graph/LLDBParser.h @@ -25,7 +25,9 @@ class SBType; namespace oi::detail::type_graph { -struct LLDBParserOptions {}; +struct LLDBParserOptions { + bool readEnumValues = false; +}; class LLDBParser { public: @@ -35,6 +37,16 @@ class LLDBParser { Type& parse(lldb::SBType* root); private: + Type& enumerateType(lldb::SBType& type); + Enum& enumerateEnum(lldb::SBType& type); + + template + T& makeType(lldb::SBType *lldbType, Args&&... args) { + auto& newType = typeGraph_.makeType(std::forward(args)...); + //drgn_types_.insert({drgnType, newType}); + return newType; + } + TypeGraph& typeGraph_; int depth_; LLDBParserOptions options_; diff --git a/test/test_lldb_parser.cpp b/test/test_lldb_parser.cpp index 60d2bacb..994d7476 100644 --- a/test/test_lldb_parser.cpp +++ b/test/test_lldb_parser.cpp @@ -36,8 +36,7 @@ void LLDBParserTest::TearDownTestSuite() { LLDBParser LLDBParserTest::getLLDBParser(TypeGraph& typeGraph, LLDBParserOptions options) { - LLDBParser lldbParser{typeGraph, options}; - return lldbParser; + return LLDBParser{typeGraph, options}; } lldb::SBType LLDBParserTest::getLLDBRoot(std::string_view function) { @@ -77,7 +76,9 @@ void LLDBParserTest::test(std::string_view function, void LLDBParserTest::test(std::string_view function, std::string_view expected) { // Enable options in unit tests so we get more coverage - LLDBParserOptions options = {}; + LLDBParserOptions options = { + .readEnumValues = true, + }; test(function, expected, options); } @@ -262,7 +263,7 @@ TEST_F(LLDBParserTest, Container) { TEST_F(LLDBParserTest, Enum) { test("oid_test_case_enums_scoped", R"( - Enum: ScopedEnum (size: 4) + Enum: ns_enums::ScopedEnum (size: 4) Enumerator: 0:CaseA Enumerator: 1:CaseB Enumerator: 2:CaseC @@ -271,7 +272,7 @@ TEST_F(LLDBParserTest, Enum) { TEST_F(LLDBParserTest, EnumUint8) { test("oid_test_case_enums_scoped_uint8", R"( - Enum: ScopedEnumUint8 (size: 1) + Enum: ns_enums::ScopedEnumUint8 (size: 1) Enumerator: 2:CaseA Enumerator: 3:CaseB Enumerator: 4:CaseC @@ -280,7 +281,7 @@ TEST_F(LLDBParserTest, EnumUint8) { TEST_F(LLDBParserTest, UnscopedEnum) { test("oid_test_case_enums_unscoped", R"( - Enum: UNSCOPED_ENUM (size: 4) + Enum: ns_enums::UNSCOPED_ENUM (size: 4) Enumerator: -2:CASE_B Enumerator: 5:CASE_A Enumerator: 20:CASE_C @@ -291,7 +292,7 @@ TEST_F(LLDBParserTest, EnumNoValues) { LLDBParserOptions options{}; test("oid_test_case_enums_scoped", R"( - Enum: ScopedEnum (size: 4) + Enum: ns_enums::ScopedEnum (size: 4) )", options); } From ec516091f2cc824f0d30848f5442f1e355b42fba Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Wed, 13 Dec 2023 10:46:17 -0800 Subject: [PATCH 06/20] Save and re-use enumerated type in LLDBParser --- oi/type_graph/LLDBParser.cpp | 10 +++++++--- oi/type_graph/LLDBParser.h | 34 +++++++++++++++++++++++++++------- test/test_lldb_parser.cpp | 14 ++++++++++---- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index cff617c2..cc63954c 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -20,9 +20,9 @@ namespace oi::detail::type_graph { -Type& LLDBParser::parse(lldb::SBType* root) { +Type& LLDBParser::parse(lldb::SBType& root) { depth_ = 0; - return enumerateType(*root); + return enumerateType(root); } struct DepthGuard { @@ -37,6 +37,10 @@ struct DepthGuard { }; Type& LLDBParser::enumerateType(lldb::SBType& type) { + // Avoid re-enumerating an already-processsed type + if (auto it = lldb_types_.find(type); it != lldb_types_.end()) + return it->second; + DepthGuard guard(depth_); Type *t = nullptr; @@ -84,7 +88,7 @@ Enum& LLDBParser::enumerateEnum(lldb::SBType& type) { } } - return makeType(&type, name, size, std::move(enumeratorMap)); + return makeType(type, name, size, std::move(enumeratorMap)); } } // namespace oi::detail::type_graph diff --git a/oi/type_graph/LLDBParser.h b/oi/type_graph/LLDBParser.h index e84d6a40..85a4abaa 100644 --- a/oi/type_graph/LLDBParser.h +++ b/oi/type_graph/LLDBParser.h @@ -18,10 +18,28 @@ #include "TypeGraph.h" #include "Types.h" -// TODO: use LLDB's includes -namespace lldb { -class SBType; -} +#include + +namespace std { +template <> +struct hash { + size_t operator()(const lldb::SBType& key) const { + auto &keyAsSP = reinterpret_cast(key); + auto SPHasher = std::hash(); + return SPHasher(keyAsSP); + } +}; + +template <> +struct equal_to { + bool operator()(const lldb::SBType& lhs, const lldb::SBType& rhs) const { + auto &lhsAsSP = reinterpret_cast(lhs); + auto &rhsAsSP = reinterpret_cast(rhs); + auto SPEqualTo = std::equal_to(); + return SPEqualTo(lhsAsSP, rhsAsSP); + } +}; +} // namespace std namespace oi::detail::type_graph { @@ -34,19 +52,21 @@ class LLDBParser { LLDBParser(TypeGraph& typeGraph, LLDBParserOptions options) : typeGraph_(typeGraph), options_(options) { } - Type& parse(lldb::SBType* root); + Type& parse(lldb::SBType& root); private: Type& enumerateType(lldb::SBType& type); Enum& enumerateEnum(lldb::SBType& type); template - T& makeType(lldb::SBType *lldbType, Args&&... args) { + T& makeType(lldb::SBType& lldbType, Args&&... args) { auto& newType = typeGraph_.makeType(std::forward(args)...); - //drgn_types_.insert({drgnType, newType}); + lldb_types_.emplace(lldbType, newType); return newType; } + std::unordered_map> lldb_types_; + TypeGraph& typeGraph_; int depth_; LLDBParserOptions options_; diff --git a/test/test_lldb_parser.cpp b/test/test_lldb_parser.cpp index 994d7476..53ad825f 100644 --- a/test/test_lldb_parser.cpp +++ b/test/test_lldb_parser.cpp @@ -40,12 +40,18 @@ LLDBParser LLDBParserTest::getLLDBParser(TypeGraph& typeGraph, } lldb::SBType LLDBParserTest::getLLDBRoot(std::string_view function) { + /* This method makes a lot of assumptions about the structure of the + * integration_test_target. Each test case has a single function with a + * single argument. + */ auto fns = target_.FindFunctions(function.data()); - assert(fns.GetSize() == 1); + EXPECT_EQ(fns.GetSize(), 1); auto fn = fns.GetContextAtIndex(0).GetFunction(); auto args = fn.GetBlock().GetVariables(target_, true, false, false); - assert(args.GetSize() >= 1); - return args.GetValueAtIndex(0).GetType(); + EXPECT_EQ(args.GetSize(), 1); + auto root = args.GetValueAtIndex(0).GetType(); + EXPECT_TRUE(root.IsValid()); + return root; } std::string LLDBParserTest::run(std::string_view function, @@ -54,7 +60,7 @@ std::string LLDBParserTest::run(std::string_view function, auto lldbParser = getLLDBParser(typeGraph, options); auto lldbRoot = getLLDBRoot(function); - Type& type = lldbParser.parse(&lldbRoot); + Type& type = lldbParser.parse(lldbRoot); std::stringstream out; NodeTracker tracker; From 643d6462edd5d987670746a60aa6f68e9728c3fd Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Wed, 13 Dec 2023 10:47:29 -0800 Subject: [PATCH 07/20] Implement LLDBParser::enumeratePointer --- oi/type_graph/LLDBParser.cpp | 39 ++++++++++++++++++++++++++++++++++-- oi/type_graph/LLDBParser.h | 4 ++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index cc63954c..5a533f6b 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -48,6 +48,10 @@ Type& LLDBParser::enumerateType(lldb::SBType& type) { case lldb::eTypeClassEnumeration: t = &enumerateEnum(type); break; + case lldb::eTypeClassPointer: + case lldb::eTypeClassReference: + t = &enumeratePointer(type); + break; case lldb::eTypeClassInvalid: case lldb::eTypeClassArray: case lldb::eTypeClassBlockPointer: @@ -60,8 +64,6 @@ Type& LLDBParser::enumerateType(lldb::SBType& type) { case lldb::eTypeClassObjCObject: case lldb::eTypeClassObjCInterface: case lldb::eTypeClassObjCObjectPointer: - case lldb::eTypeClassPointer: - case lldb::eTypeClassReference: case lldb::eTypeClassStruct: case lldb::eTypeClassTypedef: case lldb::eTypeClassUnion: @@ -91,4 +93,37 @@ Enum& LLDBParser::enumerateEnum(lldb::SBType& type) { return makeType(type, name, size, std::move(enumeratorMap)); } +Type& LLDBParser::enumeratePointer(lldb::SBType& type) { + if (!chasePointer()) { + return makeType(type, Primitive::Kind::StubbedPointer); + } + + lldb::SBType pointeeType; + switch (auto kind = type.GetTypeClass()) { + case lldb::eTypeClassPointer: + pointeeType = type.GetPointeeType(); + break; + case lldb::eTypeClassReference: + pointeeType = type.GetDereferencedType(); + break; + default: + throw LLDBParserError{"Invalid type class for pointer type: " + std::to_string(kind)}; + } + + if (pointeeType.IsFunctionType()) { + return makeType(type, Primitive::Kind::StubbedPointer); + } + + Type& t = enumerateType(pointeeType); + return makeType(type, t); +} + +bool LLDBParser::chasePointer() const { + // Always chase top-level pointers. + if (depth_ == 1) + return true; + return options_.chaseRawPointers; +} + + } // namespace oi::detail::type_graph diff --git a/oi/type_graph/LLDBParser.h b/oi/type_graph/LLDBParser.h index 85a4abaa..0a9399d2 100644 --- a/oi/type_graph/LLDBParser.h +++ b/oi/type_graph/LLDBParser.h @@ -44,6 +44,7 @@ struct equal_to { namespace oi::detail::type_graph { struct LLDBParserOptions { + bool chaseRawPointers = false; bool readEnumValues = false; }; @@ -57,6 +58,9 @@ class LLDBParser { private: Type& enumerateType(lldb::SBType& type); Enum& enumerateEnum(lldb::SBType& type); + Type& enumeratePointer(lldb::SBType& type); + + bool chasePointer() const; template T& makeType(lldb::SBType& lldbType, Args&&... args) { From 0f0d7be2fc24a569e188f827bfbeab7620a8116a Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Wed, 13 Dec 2023 11:15:53 -0800 Subject: [PATCH 08/20] Implement LLDBParser::enumeratePrimitive and LLDBParser::enumerateTypedef --- oi/type_graph/LLDBParser.cpp | 100 +++++++++++++++++++++++++++++++++-- oi/type_graph/LLDBParser.h | 5 ++ test/test_lldb_parser.cpp | 4 +- 3 files changed, 104 insertions(+), 5 deletions(-) diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index 5a533f6b..d8a67afd 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -48,14 +48,19 @@ Type& LLDBParser::enumerateType(lldb::SBType& type) { case lldb::eTypeClassEnumeration: t = &enumerateEnum(type); break; + case lldb::eTypeClassTypedef: + t = &enumerateTypedef(type); + break; case lldb::eTypeClassPointer: case lldb::eTypeClassReference: t = &enumeratePointer(type); break; + case lldb::eTypeClassBuiltin: + t = &enumeratePrimitive(type); + break; case lldb::eTypeClassInvalid: case lldb::eTypeClassArray: case lldb::eTypeClassBlockPointer: - case lldb::eTypeClassBuiltin: case lldb::eTypeClassClass: case lldb::eTypeClassComplexFloat: case lldb::eTypeClassComplexInteger: @@ -65,12 +70,11 @@ Type& LLDBParser::enumerateType(lldb::SBType& type) { case lldb::eTypeClassObjCInterface: case lldb::eTypeClassObjCObjectPointer: case lldb::eTypeClassStruct: - case lldb::eTypeClassTypedef: case lldb::eTypeClassUnion: case lldb::eTypeClassVector: case lldb::eTypeClassOther: case lldb::eTypeClassAny: - throw std::runtime_error("Unhandled type class: " + std::to_string(kind)); + throw LLDBParserError{"Unhandled type class: " + std::to_string(kind)}; } return *t; @@ -93,6 +97,14 @@ Enum& LLDBParser::enumerateEnum(lldb::SBType& type) { return makeType(type, name, size, std::move(enumeratorMap)); } +Typedef& LLDBParser::enumerateTypedef(lldb::SBType& type) { + std::string name = type.GetName(); + + lldb::SBType underlyingType = type.GetTypedefedType(); + auto& t = enumerateType(underlyingType); + return makeType(type, name, t); +} + Type& LLDBParser::enumeratePointer(lldb::SBType& type) { if (!chasePointer()) { return makeType(type, Primitive::Kind::StubbedPointer); @@ -125,5 +137,87 @@ bool LLDBParser::chasePointer() const { return options_.chaseRawPointers; } +Primitive::Kind LLDBParser::primitiveIntKind(lldb::SBType& type, bool is_signed) { + switch (auto size = type.GetByteSize()) { + case 1: + return is_signed ? Primitive::Kind::Int8 : Primitive::Kind::UInt8; + case 2: + return is_signed ? Primitive::Kind::Int16 : Primitive::Kind::UInt16; + case 4: + return is_signed ? Primitive::Kind::Int32 : Primitive::Kind::UInt32; + case 8: + return is_signed ? Primitive::Kind::Int64 : Primitive::Kind::UInt64; + default: + throw LLDBParserError{"Invalid integer size: " + std::to_string(size)}; + } +} +Primitive::Kind LLDBParser::primitiveFloatKind(lldb::SBType& type) { + switch (auto size = type.GetByteSize()) { + case 4: + return Primitive::Kind::Float32; + case 8: + return Primitive::Kind::Float64; + case 16: + return Primitive::Kind::Float128; + default: + throw LLDBParserError{"Invalid float size: " + std::to_string(size)}; + } +} + +Primitive& LLDBParser::enumeratePrimitive(lldb::SBType& type) { + Primitive::Kind primitiveKind = Primitive::Kind::Void; + + switch (auto kind = type.GetBasicType()) { + /* TODO: Do we need to handle char differently from int? */ + case lldb::eBasicTypeChar: + case lldb::eBasicTypeSignedChar: + case lldb::eBasicTypeWChar: + case lldb::eBasicTypeSignedWChar: + case lldb::eBasicTypeChar16: + case lldb::eBasicTypeChar32: + case lldb::eBasicTypeChar8: + case lldb::eBasicTypeShort: + case lldb::eBasicTypeInt: + case lldb::eBasicTypeLong: + case lldb::eBasicTypeLongLong: + case lldb::eBasicTypeInt128: + primitiveKind = primitiveIntKind(type, true); + break; + case lldb::eBasicTypeUnsignedChar: + case lldb::eBasicTypeUnsignedWChar: + case lldb::eBasicTypeUnsignedShort: + case lldb::eBasicTypeUnsignedInt: + case lldb::eBasicTypeUnsignedLong: + case lldb::eBasicTypeUnsignedLongLong: + case lldb::eBasicTypeUnsignedInt128: + primitiveKind = primitiveIntKind(type, false); + break; + case lldb::eBasicTypeHalf: + case lldb::eBasicTypeFloat: + case lldb::eBasicTypeDouble: + case lldb::eBasicTypeLongDouble: + primitiveKind = primitiveFloatKind(type); + break; + case lldb::eBasicTypeBool: + primitiveKind = Primitive::Kind::Bool; + break; + case lldb::eBasicTypeVoid: + case lldb::eBasicTypeNullPtr: + primitiveKind = Primitive::Kind::Void; + break; + case lldb::eBasicTypeInvalid: + case lldb::eBasicTypeFloatComplex: + case lldb::eBasicTypeDoubleComplex: + case lldb::eBasicTypeLongDoubleComplex: + case lldb::eBasicTypeObjCID: + case lldb::eBasicTypeObjCClass: + case lldb::eBasicTypeObjCSel: + case lldb::eBasicTypeOther: + default: + throw LLDBParserError{"Unhandled primitive type: " + std::to_string(kind)}; + } + + return makeType(type, primitiveKind); +} } // namespace oi::detail::type_graph diff --git a/oi/type_graph/LLDBParser.h b/oi/type_graph/LLDBParser.h index 0a9399d2..30926b72 100644 --- a/oi/type_graph/LLDBParser.h +++ b/oi/type_graph/LLDBParser.h @@ -58,7 +58,12 @@ class LLDBParser { private: Type& enumerateType(lldb::SBType& type); Enum& enumerateEnum(lldb::SBType& type); + Typedef& enumerateTypedef(lldb::SBType& type); Type& enumeratePointer(lldb::SBType& type); + Primitive& enumeratePrimitive(lldb::SBType& type); + + Primitive::Kind primitiveIntKind(lldb::SBType& type, bool is_signed); + Primitive::Kind primitiveFloatKind(lldb::SBType& type); bool chasePointer() const; diff --git a/test/test_lldb_parser.cpp b/test/test_lldb_parser.cpp index 53ad825f..38c2e995 100644 --- a/test/test_lldb_parser.cpp +++ b/test/test_lldb_parser.cpp @@ -305,7 +305,7 @@ TEST_F(LLDBParserTest, EnumNoValues) { TEST_F(LLDBParserTest, Typedef) { test("oid_test_case_typedefs_c_style", R"( -[2] Typedef: TdUInt64 +[2] Typedef: ns_typedefs::TdUInt64 [1] Typedef: uint64_t [0] Typedef: __uint64_t Primitive: uint64_t @@ -314,7 +314,7 @@ TEST_F(LLDBParserTest, Typedef) { TEST_F(LLDBParserTest, Using) { test("oid_test_case_typedefs_using", R"( -[2] Typedef: UsingUInt64 +[2] Typedef: ns_typedefs::UsingUInt64 [1] Typedef: uint64_t [0] Typedef: __uint64_t Primitive: uint64_t From 26e0ed76d57f0dc89879faecf9d923c8fbbbbddd Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Wed, 13 Dec 2023 12:51:02 -0800 Subject: [PATCH 09/20] Implement LLDBParser::enumerateArray --- oi/type_graph/LLDBParser.cpp | 12 +++++++++++- oi/type_graph/LLDBParser.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index d8a67afd..9f817ae4 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -58,8 +58,10 @@ Type& LLDBParser::enumerateType(lldb::SBType& type) { case lldb::eTypeClassBuiltin: t = &enumeratePrimitive(type); break; - case lldb::eTypeClassInvalid: case lldb::eTypeClassArray: + t = &enumerateArray(type); + break; + case lldb::eTypeClassInvalid: case lldb::eTypeClassBlockPointer: case lldb::eTypeClassClass: case lldb::eTypeClassComplexFloat: @@ -164,6 +166,14 @@ Primitive::Kind LLDBParser::primitiveFloatKind(lldb::SBType& type) { } } +Array& LLDBParser::enumerateArray(lldb::SBType& type) { + auto elementType = type.GetArrayElementType(); + /* TODO: Is there a better way to get the array size? */ + auto len = type.GetByteSize() / elementType.GetByteSize(); + auto& t = enumerateType(elementType); + return makeType(type, t, len); +} + Primitive& LLDBParser::enumeratePrimitive(lldb::SBType& type) { Primitive::Kind primitiveKind = Primitive::Kind::Void; diff --git a/oi/type_graph/LLDBParser.h b/oi/type_graph/LLDBParser.h index 30926b72..2dea038e 100644 --- a/oi/type_graph/LLDBParser.h +++ b/oi/type_graph/LLDBParser.h @@ -60,6 +60,7 @@ class LLDBParser { Enum& enumerateEnum(lldb::SBType& type); Typedef& enumerateTypedef(lldb::SBType& type); Type& enumeratePointer(lldb::SBType& type); + Array& enumerateArray(lldb::SBType& type); Primitive& enumeratePrimitive(lldb::SBType& type); Primitive::Kind primitiveIntKind(lldb::SBType& type, bool is_signed); From ee020513aa4f87765608d0c5f02d6cea94a97210 Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Wed, 13 Dec 2023 12:51:38 -0800 Subject: [PATCH 10/20] First draft for LLDBParser::enumerateClass --- oi/type_graph/LLDBParser.cpp | 33 ++++++++++++++++++++++++++++++--- oi/type_graph/LLDBParser.h | 1 + 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index 9f817ae4..e0374286 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -45,6 +45,11 @@ Type& LLDBParser::enumerateType(lldb::SBType& type) { Type *t = nullptr; switch (auto kind = type.GetTypeClass()) { + case lldb::eTypeClassClass: + case lldb::eTypeClassStruct: + case lldb::eTypeClassUnion: + t = &enumerateClass(type); + break; case lldb::eTypeClassEnumeration: t = &enumerateEnum(type); break; @@ -63,7 +68,6 @@ Type& LLDBParser::enumerateType(lldb::SBType& type) { break; case lldb::eTypeClassInvalid: case lldb::eTypeClassBlockPointer: - case lldb::eTypeClassClass: case lldb::eTypeClassComplexFloat: case lldb::eTypeClassComplexInteger: case lldb::eTypeClassFunction: @@ -71,8 +75,6 @@ Type& LLDBParser::enumerateType(lldb::SBType& type) { case lldb::eTypeClassObjCObject: case lldb::eTypeClassObjCInterface: case lldb::eTypeClassObjCObjectPointer: - case lldb::eTypeClassStruct: - case lldb::eTypeClassUnion: case lldb::eTypeClassVector: case lldb::eTypeClassOther: case lldb::eTypeClassAny: @@ -82,6 +84,31 @@ Type& LLDBParser::enumerateType(lldb::SBType& type) { return *t; } +Class& LLDBParser::enumerateClass(lldb::SBType& type) { + auto name = type.GetName(); + auto displayName = type.GetDisplayTypeName(); + auto size = type.GetByteSize(); + auto virtuality = type.IsPolymorphicClass(); + + Class::Kind kind; + switch (auto typeClass = type.GetTypeClass()) { + case lldb::eTypeClassClass: + kind = Class::Kind::Class; + break; + case lldb::eTypeClassStruct: + kind = Class::Kind::Struct; + break; + case lldb::eTypeClassUnion: + kind = Class::Kind::Union; + break; + default: + throw LLDBParserError{"Invalid type class for compound type: " + std::to_string(typeClass)}; + } + + auto &c = makeType(type, kind, displayName, name, size, virtuality); + return c; +} + Enum& LLDBParser::enumerateEnum(lldb::SBType& type) { const char *typeName = type.GetName(); std::string name = typeName ? typeName : ""; diff --git a/oi/type_graph/LLDBParser.h b/oi/type_graph/LLDBParser.h index 2dea038e..877535a9 100644 --- a/oi/type_graph/LLDBParser.h +++ b/oi/type_graph/LLDBParser.h @@ -57,6 +57,7 @@ class LLDBParser { private: Type& enumerateType(lldb::SBType& type); + Class& enumerateClass(lldb::SBType& type); Enum& enumerateEnum(lldb::SBType& type); Typedef& enumerateTypedef(lldb::SBType& type); Type& enumeratePointer(lldb::SBType& type); From fb9568653926ba674c56d5c37b312f881e8c1ad7 Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Wed, 13 Dec 2023 13:14:26 -0800 Subject: [PATCH 11/20] First draft at handling Invalid type --- oi/type_graph/LLDBParser.cpp | 85 ++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index e0374286..15195ee9 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -43,45 +43,56 @@ Type& LLDBParser::enumerateType(lldb::SBType& type) { DepthGuard guard(depth_); - Type *t = nullptr; - switch (auto kind = type.GetTypeClass()) { - case lldb::eTypeClassClass: - case lldb::eTypeClassStruct: - case lldb::eTypeClassUnion: - t = &enumerateClass(type); - break; - case lldb::eTypeClassEnumeration: - t = &enumerateEnum(type); - break; - case lldb::eTypeClassTypedef: - t = &enumerateTypedef(type); - break; - case lldb::eTypeClassPointer: - case lldb::eTypeClassReference: - t = &enumeratePointer(type); - break; - case lldb::eTypeClassBuiltin: - t = &enumeratePrimitive(type); - break; - case lldb::eTypeClassArray: - t = &enumerateArray(type); - break; - case lldb::eTypeClassInvalid: - case lldb::eTypeClassBlockPointer: - case lldb::eTypeClassComplexFloat: - case lldb::eTypeClassComplexInteger: - case lldb::eTypeClassFunction: - case lldb::eTypeClassMemberPointer: - case lldb::eTypeClassObjCObject: - case lldb::eTypeClassObjCInterface: - case lldb::eTypeClassObjCObjectPointer: - case lldb::eTypeClassVector: - case lldb::eTypeClassOther: - case lldb::eTypeClassAny: - throw LLDBParserError{"Unhandled type class: " + std::to_string(kind)}; + bool isTypeIncomplete = !type.IsTypeComplete(); + + std::optional> t; + try { + switch (auto kind = type.GetTypeClass()) { + case lldb::eTypeClassClass: + case lldb::eTypeClassStruct: + case lldb::eTypeClassUnion: + t = enumerateClass(type); + break; + case lldb::eTypeClassEnumeration: + t = enumerateEnum(type); + break; + case lldb::eTypeClassTypedef: + t = enumerateTypedef(type); + break; + case lldb::eTypeClassPointer: + case lldb::eTypeClassReference: + t = enumeratePointer(type); + break; + case lldb::eTypeClassBuiltin: + t = enumeratePrimitive(type); + break; + case lldb::eTypeClassArray: + t = enumerateArray(type); + break; + case lldb::eTypeClassInvalid: + case lldb::eTypeClassBlockPointer: + case lldb::eTypeClassComplexFloat: + case lldb::eTypeClassComplexInteger: + case lldb::eTypeClassFunction: + case lldb::eTypeClassMemberPointer: + case lldb::eTypeClassObjCObject: + case lldb::eTypeClassObjCInterface: + case lldb::eTypeClassObjCObjectPointer: + case lldb::eTypeClassVector: + case lldb::eTypeClassOther: + case lldb::eTypeClassAny: + throw LLDBParserError{"Unhandled type class: " + std::to_string(kind)}; + } + } catch (const LLDBParserError& e) { + if (!isTypeIncomplete) + throw; + } + + if (isTypeIncomplete) { + return makeType(type, *t); } - return *t; + return t.value(); } Class& LLDBParser::enumerateClass(lldb::SBType& type) { From 2149db17c59b22f93cb0c28078b68d1afd15ac1a Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Thu, 14 Dec 2023 04:48:06 -0800 Subject: [PATCH 12/20] Implement LLDBParser::enumerateClassMembers --- oi/type_graph/LLDBParser.cpp | 28 +++++++++++++++++++++++- oi/type_graph/LLDBParser.h | 4 +++- test/integration/alignment.toml | 15 +++++++++++++ test/test_lldb_parser.cpp | 38 +++++++++++++++++---------------- 4 files changed, 65 insertions(+), 20 deletions(-) diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index 15195ee9..f1f0f6b8 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -17,6 +17,7 @@ #include #include +#include namespace oi::detail::type_graph { @@ -97,7 +98,7 @@ Type& LLDBParser::enumerateType(lldb::SBType& type) { Class& LLDBParser::enumerateClass(lldb::SBType& type) { auto name = type.GetName(); - auto displayName = type.GetDisplayTypeName(); + auto displayName = type.GetUnqualifiedType().GetDisplayTypeName(); auto size = type.GetByteSize(); auto virtuality = type.IsPolymorphicClass(); @@ -117,9 +118,34 @@ Class& LLDBParser::enumerateClass(lldb::SBType& type) { } auto &c = makeType(type, kind, displayName, name, size, virtuality); + + enumerateClassMembers(type, c.members); + return c; } +void LLDBParser::enumerateClassMembers(lldb::SBType& type, std::vector& members) { + assert(members.empty()); + members.reserve(type.GetNumberOfFields()); + + for (uint32_t i = 0; i < type.GetNumberOfFields(); i++) { + auto field = type.GetFieldAtIndex(i); + if (field.GetName() == nullptr) + continue; // Skip anonymous fields (TODO: Why?) + auto fieldName = field.GetName(); + auto fieldType = field.GetType(); + auto bitFieldSize = field.GetBitfieldSizeInBits(); + auto bitOffset = field.GetOffsetInBits(); + + auto& enumeratedField = enumerateType(fieldType); + members.emplace_back(enumeratedField, fieldName, bitOffset, bitFieldSize); + } + + std::sort(members.begin(), members.end(), [](const auto& a, const auto& b) { + return a.bitOffset < b.bitOffset; + }); +} + Enum& LLDBParser::enumerateEnum(lldb::SBType& type) { const char *typeName = type.GetName(); std::string name = typeName ? typeName : ""; diff --git a/oi/type_graph/LLDBParser.h b/oi/type_graph/LLDBParser.h index 877535a9..0c3efbaa 100644 --- a/oi/type_graph/LLDBParser.h +++ b/oi/type_graph/LLDBParser.h @@ -67,9 +67,11 @@ class LLDBParser { Primitive::Kind primitiveIntKind(lldb::SBType& type, bool is_signed); Primitive::Kind primitiveFloatKind(lldb::SBType& type); + void enumerateClassMembers(lldb::SBType& type, std::vector& members); + bool chasePointer() const; - template + template T& makeType(lldb::SBType& lldbType, Args&&... args) { auto& newType = typeGraph_.makeType(std::forward(args)...); lldb_types_.emplace(lldbType, newType); diff --git a/test/integration/alignment.toml b/test/integration/alignment.toml index b87e1620..7baeea16 100644 --- a/test/integration/alignment.toml +++ b/test/integration/alignment.toml @@ -41,6 +41,21 @@ definitions = ''' }; ''' [cases] + [cases.struct] + param_types = ["const Align16&"] + setup = "return {};" + expect_json = '''[ + {"staticSize": 16, "exclusiveSize": 15, "members": [ + {"typeName": "int8_t", "staticSize": 1, "exclusiveSize": 1} + ]}]''' + [cases.member_alignment] + param_types = ["const MemberAlignment&"] + setup = "return {};" + expect_json = '''[ + {"staticSize": 64, "exclusiveSize": 32, "members": [ + {"typeName": "int8_t", "staticSize": 1, "exclusiveSize": 1}, + {"typeName": "int8_t", "staticSize": 1, "exclusiveSize": 1} + ]}]''' [cases.wrapper_struct] param_types = ["const Wrapper&"] setup = "return {};" diff --git a/test/test_lldb_parser.cpp b/test/test_lldb_parser.cpp index 38c2e995..d45ee0a3 100644 --- a/test/test_lldb_parser.cpp +++ b/test/test_lldb_parser.cpp @@ -83,6 +83,7 @@ void LLDBParserTest::test(std::string_view function, std::string_view expected) { // Enable options in unit tests so we get more coverage LLDBParserOptions options = { + .chaseRawPointers = true, .readEnumValues = true, }; test(function, expected, options); @@ -129,7 +130,7 @@ void LLDBParserTest::testMultiCompilerGlob( TEST_F(LLDBParserTest, SimpleStruct) { test("oid_test_case_simple_struct", R"( [1] Pointer -[0] Struct: SimpleStruct (size: 16) +[0] Struct: ns_simple::SimpleStruct (size: 16) Member: a (offset: 0) Primitive: int32_t Member: b (offset: 4) @@ -142,7 +143,7 @@ TEST_F(LLDBParserTest, SimpleStruct) { TEST_F(LLDBParserTest, SimpleClass) { test("oid_test_case_simple_class", R"( [1] Pointer -[0] Class: SimpleClass (size: 16) +[0] Class: ns_simple::SimpleClass (size: 16) Member: a (offset: 0) Primitive: int32_t Member: b (offset: 4) @@ -155,7 +156,7 @@ TEST_F(LLDBParserTest, SimpleClass) { TEST_F(LLDBParserTest, SimpleUnion) { test("oid_test_case_simple_union", R"( [1] Pointer -[0] Union: SimpleUnion (size: 8) +[0] Union: ns_simple::SimpleUnion (size: 8) Member: a (offset: 0) Primitive: int32_t Member: b (offset: 0) @@ -324,7 +325,7 @@ TEST_F(LLDBParserTest, Using) { TEST_F(LLDBParserTest, ArrayMember) { test("oid_test_case_arrays_member_int10", R"( [2] Pointer -[0] Struct: Foo10 (size: 40) +[0] Struct: ns_arrays::Foo10 (size: 40) Member: arr (offset: 0) [1] Array: (length: 10) Primitive: int32_t @@ -349,7 +350,7 @@ TEST_F(LLDBParserTest, ArrayDirect) { TEST_F(LLDBParserTest, Pointer) { test("oid_test_case_pointers_struct_primitive_ptrs", R"( [3] Pointer -[0] Struct: PrimitivePtrs (size: 24) +[0] Struct: ns_pointers::PrimitivePtrs (size: 24) Member: a (offset: 0) Primitive: int32_t Member: b (offset: 8) @@ -366,7 +367,7 @@ TEST_F(LLDBParserTest, PointerNoFollow) { test("oid_test_case_pointers_struct_primitive_ptrs", R"( [1] Pointer -[0] Struct: PrimitivePtrs (size: 24) +[0] Struct: ns_pointers::PrimitivePtrs (size: 24) Member: a (offset: 0) Primitive: int32_t Member: b (offset: 8) @@ -516,18 +517,20 @@ TEST_F(LLDBParserTest, TemplateEnumValueNegative) { //} TEST_F(LLDBParserTest, StructAlignment) { + GTEST_SKIP() << "Alignment not reported by LLDB yet"; test("oid_test_case_alignment_struct", R"( [1] Pointer -[0] Struct: Align16 (size: 16, align: 16) +[0] Struct: ns_alignment::Align16 (size: 16, align: 16) Member: c (offset: 0) Primitive: int8_t )"); } TEST_F(LLDBParserTest, MemberAlignment) { + GTEST_SKIP() << "Alignment not reported by LLDB yet"; test("oid_test_case_alignment_member_alignment", R"( [1] Pointer -[0] Struct: MemberAlignment (size: 64) +[0] Struct: ns_alignment::MemberAlignment (size: 64) Member: c (offset: 0) Primitive: int8_t Member: c32 (offset: 32, align: 32) @@ -567,7 +570,7 @@ TEST_F(LLDBParserTest, VirtualFunctions) { TEST_F(LLDBParserTest, BitfieldsSingle) { test("oid_test_case_bitfields_single", R"( [1] Pointer -[0] Struct: Single (size: 4) +[0] Struct: ns_bitfields::Single (size: 4) Member: bitfield (offset: 0, bitsize: 3) Primitive: int32_t )"); @@ -576,7 +579,7 @@ TEST_F(LLDBParserTest, BitfieldsSingle) { TEST_F(LLDBParserTest, BitfieldsWithinBytes) { test("oid_test_case_bitfields_within_bytes", R"( [1] Pointer -[0] Struct: WithinBytes (size: 2) +[0] Struct: ns_bitfields::WithinBytes (size: 2) Member: a (offset: 0, bitsize: 3) Primitive: int8_t Member: b (offset: 0.375, bitsize: 5) @@ -589,7 +592,7 @@ TEST_F(LLDBParserTest, BitfieldsWithinBytes) { TEST_F(LLDBParserTest, BitfieldsStraddleBytes) { test("oid_test_case_bitfields_straddle_bytes", R"( [1] Pointer -[0] Struct: StraddleBytes (size: 3) +[0] Struct: ns_bitfields::StraddleBytes (size: 3) Member: a (offset: 0, bitsize: 7) Primitive: int8_t Member: b (offset: 1, bitsize: 7) @@ -602,7 +605,7 @@ TEST_F(LLDBParserTest, BitfieldsStraddleBytes) { TEST_F(LLDBParserTest, BitfieldsMixed) { test("oid_test_case_bitfields_mixed", R"( [1] Pointer -[0] Struct: Mixed (size: 12) +[0] Struct: ns_bitfields::Mixed (size: 12) Member: a (offset: 0) Primitive: int32_t Member: b (offset: 4, bitsize: 4) @@ -617,10 +620,9 @@ TEST_F(LLDBParserTest, BitfieldsMixed) { } TEST_F(LLDBParserTest, BitfieldsMoreBitsThanType) { - GTEST_SKIP() << "LLDB errors out"; test("oid_test_case_bitfields_more_bits_than_type", R"( [1] Pointer -[0] Struct: MoreBitsThanType (size: 4) +[0] Struct: ns_bitfields::MoreBitsThanType (size: 4) Member: a (offset: 0, bitsize: 8) Primitive: int8_t )"); @@ -629,7 +631,7 @@ TEST_F(LLDBParserTest, BitfieldsMoreBitsThanType) { TEST_F(LLDBParserTest, BitfieldsZeroBits) { test("oid_test_case_bitfields_zero_bits", R"( [1] Pointer -[0] Struct: ZeroBits (size: 2) +[0] Struct: ns_bitfields::ZeroBits (size: 2) Member: b1 (offset: 0, bitsize: 3) Primitive: int8_t Member: b2 (offset: 1, bitsize: 2) @@ -640,14 +642,14 @@ TEST_F(LLDBParserTest, BitfieldsZeroBits) { TEST_F(LLDBParserTest, BitfieldsEnum) { test("oid_test_case_bitfields_enum", R"( [1] Pointer -[0] Struct: Enum (size: 4) +[0] Struct: ns_bitfields::Enum (size: 4) Member: e (offset: 0, bitsize: 2) - Enum: MyEnum (size: 4) + Enum: ns_bitfields::MyEnum (size: 4) Enumerator: 0:One Enumerator: 1:Two Enumerator: 2:Three Member: f (offset: 0.25, bitsize: 4) - Enum: MyEnum (size: 4) + Enum: ns_bitfields::MyEnum (size: 4) Enumerator: 0:One Enumerator: 1:Two Enumerator: 2:Three From 01c90ed68f97297f2167465a671273855dee9bd3 Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Thu, 14 Dec 2023 05:14:02 -0800 Subject: [PATCH 13/20] First draft for LLDBParser::enumerateClassFunctions --- oi/type_graph/LLDBParser.cpp | 15 +++++++++++++++ oi/type_graph/LLDBParser.h | 1 + test/test_lldb_parser.cpp | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index f1f0f6b8..42b5e034 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -120,6 +120,7 @@ Class& LLDBParser::enumerateClass(lldb::SBType& type) { auto &c = makeType(type, kind, displayName, name, size, virtuality); enumerateClassMembers(type, c.members); + enumerateClassFunctions(type, c.functions); return c; } @@ -128,6 +129,7 @@ void LLDBParser::enumerateClassMembers(lldb::SBType& type, std::vector& assert(members.empty()); members.reserve(type.GetNumberOfFields()); + /* TODO: We are missing the _vptr */ for (uint32_t i = 0; i < type.GetNumberOfFields(); i++) { auto field = type.GetFieldAtIndex(i); if (field.GetName() == nullptr) @@ -146,6 +148,19 @@ void LLDBParser::enumerateClassMembers(lldb::SBType& type, std::vector& }); } +void LLDBParser::enumerateClassFunctions(lldb::SBType &type, std::vector& functions) { + assert(functions.empty()); + functions.reserve(type.GetNumberOfMemberFunctions()); + + /* TODO: We are missing the default constructors */ + for (uint32_t i = 0; i < type.GetNumberOfMemberFunctions(); i++) { + auto function = type.GetMemberFunctionAtIndex(i); + + /* TODO: We don't know if the function is virtual */ + functions.emplace_back(function.GetName(), false); + } +} + Enum& LLDBParser::enumerateEnum(lldb::SBType& type) { const char *typeName = type.GetName(); std::string name = typeName ? typeName : ""; diff --git a/oi/type_graph/LLDBParser.h b/oi/type_graph/LLDBParser.h index 0c3efbaa..291a162d 100644 --- a/oi/type_graph/LLDBParser.h +++ b/oi/type_graph/LLDBParser.h @@ -68,6 +68,7 @@ class LLDBParser { Primitive::Kind primitiveFloatKind(lldb::SBType& type); void enumerateClassMembers(lldb::SBType& type, std::vector& members); + void enumerateClassFunctions(lldb::SBType &type, std::vector& functions); bool chasePointer() const; diff --git a/test/test_lldb_parser.cpp b/test/test_lldb_parser.cpp index d45ee0a3..bddefdd6 100644 --- a/test/test_lldb_parser.cpp +++ b/test/test_lldb_parser.cpp @@ -542,7 +542,7 @@ TEST_F(LLDBParserTest, VirtualFunctions) { testMultiCompiler("oid_test_case_inheritance_polymorphic_a_as_a", R"( [1] Pointer -[0] Class: A (size: 16) +[0] Class: ns_inheritance_polymorphic::A (size: 16) Member: _vptr$A (offset: 0) Primitive: StubbedPointer Member: int_a (offset: 8) From 3b6484adb7b73ee66596564ae8a75743dfd055b7 Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Thu, 14 Dec 2023 06:41:29 -0800 Subject: [PATCH 14/20] Implement LLDBParser::enumerateClassParents --- oi/type_graph/LLDBParser.cpp | 51 ++++++++++++++++++++++++++++++++++++ oi/type_graph/LLDBParser.h | 17 +++++------- test/test_lldb_parser.cpp | 16 +++++------ 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index 42b5e034..275026a9 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -19,6 +19,44 @@ #include #include +namespace std { +/* We use the address of the name string for hashing and equality. + * This relies on string de-duplication, two objects with the same name + * might have different addresses, but their de-duplicated name will + * have the same address. + * + * TODO: Is this correct? How does -fsimple-template-names impact this? + * Is there a better way to do this? + */ +size_t hash::operator()(const lldb::SBType& key) const { + auto *name = const_cast(key).GetName(); + auto Hasher = std::hash(); + { /* Debugging + auto &keyAsSP = reinterpret_cast(key); + fprintf(stderr, "LLDBType::hash(key@0x%08zx = (0x%08zx, %s)) = 0x%08zx\n", + (uintptr_t)keyAsSP.get(), (uintptr_t)name, name, Hasher(name)); + // Debugging */ + } + return Hasher(name); +} + +bool equal_to::operator()(const lldb::SBType& lhs, const lldb::SBType& rhs) const { + auto* lhsName = const_cast(lhs).GetName(); + auto* rhsName = const_cast(rhs).GetName(); + auto EqualTo = std::equal_to(); + { /* Debugging + auto &lhsAsSP = reinterpret_cast(lhs); + auto &rhsAsSP = reinterpret_cast(rhs); + fprintf(stderr, "LLDBType::equal_to(lhs@0x%08zx = (0x%08zx, %s), rhs@0x%08zx = (0x%08zx, %s)) = %d\n", + (uintptr_t)lhsAsSP.get(), (uintptr_t)lhsName, lhsName, + (uintptr_t)rhsAsSP.get(), (uintptr_t)rhsName, rhsName, + EqualTo(lhsName, rhsName)); + // Debugging */ + } + return EqualTo(lhsName, rhsName); +} +} // namespace std + namespace oi::detail::type_graph { Type& LLDBParser::parse(lldb::SBType& root) { @@ -119,12 +157,25 @@ Class& LLDBParser::enumerateClass(lldb::SBType& type) { auto &c = makeType(type, kind, displayName, name, size, virtuality); + enumerateClassParents(type, c.parents); enumerateClassMembers(type, c.members); enumerateClassFunctions(type, c.functions); return c; } +void LLDBParser::enumerateClassParents(lldb::SBType& type, std::vector& parents) { + assert(parents.empty()); + parents.reserve(type.GetNumberOfDirectBaseClasses()); + + for (uint32_t i = 0; i < type.GetNumberOfDirectBaseClasses(); i++) { + auto base = type.GetDirectBaseClassAtIndex(i); + auto baseType = base.GetType(); + + parents.emplace_back(enumerateType(baseType), base.GetOffsetInBits()); + } +} + void LLDBParser::enumerateClassMembers(lldb::SBType& type, std::vector& members) { assert(members.empty()); members.reserve(type.GetNumberOfFields()); diff --git a/oi/type_graph/LLDBParser.h b/oi/type_graph/LLDBParser.h index 291a162d..0bf9a5fd 100644 --- a/oi/type_graph/LLDBParser.h +++ b/oi/type_graph/LLDBParser.h @@ -21,23 +21,17 @@ #include namespace std { +/* lldb::SBType doesn't provide a hash and equality operators. + * So we define our own specialization of them here. + */ template <> struct hash { - size_t operator()(const lldb::SBType& key) const { - auto &keyAsSP = reinterpret_cast(key); - auto SPHasher = std::hash(); - return SPHasher(keyAsSP); - } + size_t operator()(const lldb::SBType& type) const; }; template <> struct equal_to { - bool operator()(const lldb::SBType& lhs, const lldb::SBType& rhs) const { - auto &lhsAsSP = reinterpret_cast(lhs); - auto &rhsAsSP = reinterpret_cast(rhs); - auto SPEqualTo = std::equal_to(); - return SPEqualTo(lhsAsSP, rhsAsSP); - } + bool operator()(const lldb::SBType& lhs, const lldb::SBType& rhs) const; }; } // namespace std @@ -67,6 +61,7 @@ class LLDBParser { Primitive::Kind primitiveIntKind(lldb::SBType& type, bool is_signed); Primitive::Kind primitiveFloatKind(lldb::SBType& type); + void enumerateClassParents(lldb::SBType& type, std::vector& parents); void enumerateClassMembers(lldb::SBType& type, std::vector& members); void enumerateClassFunctions(lldb::SBType &type, std::vector& functions); diff --git a/test/test_lldb_parser.cpp b/test/test_lldb_parser.cpp index bddefdd6..09e62332 100644 --- a/test/test_lldb_parser.cpp +++ b/test/test_lldb_parser.cpp @@ -169,9 +169,9 @@ TEST_F(LLDBParserTest, SimpleUnion) { TEST_F(LLDBParserTest, Inheritance) { test("oid_test_case_inheritance_access_public", R"( [4] Pointer -[0] Class: Public (size: 8) +[0] Class: ns_inheritance_access::Public (size: 8) Parent (offset: 0) -[1] Class: Base (size: 4) +[1] Class: ns_inheritance_access::Base (size: 4) Member: base_int (offset: 0) [3] Typedef: int32_t [2] Typedef: __int32_t @@ -184,25 +184,25 @@ TEST_F(LLDBParserTest, Inheritance) { TEST_F(LLDBParserTest, InheritanceMultiple) { test("oid_test_case_inheritance_multiple_a", R"( [6] Pointer -[0] Struct: Derived_2 (size: 24) +[0] Struct: ns_inheritance_multiple::Derived_2 (size: 24) Parent (offset: 0) -[1] Struct: Base_1 (size: 4) +[1] Struct: ns_inheritance_multiple::Base_1 (size: 4) Member: a (offset: 0) Primitive: int32_t Parent (offset: 4) -[2] Struct: Derived_1 (size: 12) +[2] Struct: ns_inheritance_multiple::Derived_1 (size: 12) Parent (offset: 0) -[3] Struct: Base_2 (size: 4) +[3] Struct: ns_inheritance_multiple::Base_2 (size: 4) Member: b (offset: 0) Primitive: int32_t Parent (offset: 4) -[4] Struct: Base_3 (size: 4) +[4] Struct: ns_inheritance_multiple::Base_3 (size: 4) Member: c (offset: 0) Primitive: int32_t Member: d (offset: 8) Primitive: int32_t Parent (offset: 16) -[5] Struct: Base_4 (size: 4) +[5] Struct: ns_inheritance_multiple::Base_4 (size: 4) Member: e (offset: 0) Primitive: int32_t Member: f (offset: 20) From eb24f28e07bf745a47fbd67aaebe20bcec042914 Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Thu, 14 Dec 2023 08:22:14 -0800 Subject: [PATCH 15/20] Implement LLDBParser::enumerateClassTemplateParams --- oi/type_graph/LLDBParser.cpp | 22 ++++++++++++++ oi/type_graph/LLDBParser.h | 2 ++ test/test_lldb_parser.cpp | 56 +++++++++++++++++++----------------- 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index 275026a9..215afff1 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -157,6 +157,7 @@ Class& LLDBParser::enumerateClass(lldb::SBType& type) { auto &c = makeType(type, kind, displayName, name, size, virtuality); + enumerateClassTemplateParams(type, c.templateParams); enumerateClassParents(type, c.parents); enumerateClassMembers(type, c.members); enumerateClassFunctions(type, c.functions); @@ -164,6 +165,27 @@ Class& LLDBParser::enumerateClass(lldb::SBType& type) { return c; } +void LLDBParser::enumerateClassTemplateParams(lldb::SBType &type, std::vector& params) { + assert(params.empty()); + params.reserve(type.GetNumberOfTemplateArguments()); + + for (uint32_t i = 0; i < type.GetNumberOfTemplateArguments(); i++) { + auto param = type.GetTemplateArgumentType(i); + enumerateTemplateParam(type, param, i, params); + } +} + +void LLDBParser::enumerateTemplateParam(lldb::SBType& /*type*/, + lldb::SBType& param, + uint32_t /*i*/, + std::vector& params) { + QualifierSet qualifiers; + qualifiers[Qualifier::Const] = param.GetName() != param.GetUnqualifiedType().GetName(); // TODO: Wonky as hell :/ + auto& paramType = enumerateType(param); + + params.emplace_back(paramType, qualifiers); +} + void LLDBParser::enumerateClassParents(lldb::SBType& type, std::vector& parents) { assert(parents.empty()); parents.reserve(type.GetNumberOfDirectBaseClasses()); diff --git a/oi/type_graph/LLDBParser.h b/oi/type_graph/LLDBParser.h index 0bf9a5fd..43e55281 100644 --- a/oi/type_graph/LLDBParser.h +++ b/oi/type_graph/LLDBParser.h @@ -61,6 +61,8 @@ class LLDBParser { Primitive::Kind primitiveIntKind(lldb::SBType& type, bool is_signed); Primitive::Kind primitiveFloatKind(lldb::SBType& type); + void enumerateClassTemplateParams(lldb::SBType &type, std::vector& params); + void enumerateTemplateParam(lldb::SBType& type, lldb::SBType& param, uint32_t i, std::vector& params); void enumerateClassParents(lldb::SBType& type, std::vector& parents); void enumerateClassMembers(lldb::SBType& type, std::vector& members); void enumerateClassFunctions(lldb::SBType &type, std::vector& functions); diff --git a/test/test_lldb_parser.cpp b/test/test_lldb_parser.cpp index 09e62332..11659ec1 100644 --- a/test/test_lldb_parser.cpp +++ b/test/test_lldb_parser.cpp @@ -214,16 +214,16 @@ TEST_F(LLDBParserTest, Container) { testMultiCompilerGlob("oid_test_case_std_vector_int_empty", R"( [13] Pointer -[0] Class: vector > (size: 24) +[0] Class: std::vector > (size: 24) Param Primitive: int32_t Param -[1] Class: allocator (size: 1) +[1] Class: std::allocator (size: 1) Param Primitive: int32_t Parent (offset: 0) -[3] Typedef: __allocator_base -[2] Class: new_allocator (size: 1) +[3] Typedef: std::__allocator_base +[2] Class: __gnu_cxx::new_allocator (size: 1) Param Primitive: int32_t Function: new_allocator @@ -242,13 +242,13 @@ TEST_F(LLDBParserTest, Container) { )", R"( [9] Pointer -[0] Class: vector > (size: 24) +[0] Class: std::vector > (size: 24) Param Primitive: int32_t Param -[1] Class: allocator (size: 1) +[1] Class: std::allocator (size: 1) Parent (offset: 0) -[2] Class: new_allocator (size: 1) +[2] Class: __gnu_cxx::new_allocator (size: 1) Param Primitive: int32_t Function: new_allocator @@ -380,15 +380,16 @@ TEST_F(LLDBParserTest, PointerNoFollow) { TEST_F(LLDBParserTest, PointerIncomplete) { test("oid_test_case_pointers_incomplete_raw", R"( -[0] Pointer - Incomplete: [IncompleteType] +[1] Pointer + Incomplete +[0] Struct: ns_pointers_incomplete::IncompleteType (size: 0) )"); } TEST_F(LLDBParserTest, Cycle) { test("oid_test_case_cycles_raw_ptr", R"( [2] Pointer -[0] Struct: RawNode (size: 16) +[0] Struct: ns_cycles::RawNode (size: 16) Member: value (offset: 0) Primitive: int32_t Member: next (offset: 8) @@ -400,7 +401,7 @@ TEST_F(LLDBParserTest, Cycle) { TEST_F(LLDBParserTest, ClassTemplateInt) { test("oid_test_case_templates_int", R"( [1] Pointer -[0] Class: TemplatedClass1 (size: 4) +[0] Class: ns_templates::TemplatedClass1 (size: 4) Param Primitive: int32_t Member: val (offset: 0) @@ -411,9 +412,9 @@ TEST_F(LLDBParserTest, ClassTemplateInt) { TEST_F(LLDBParserTest, ClassTemplateTwo) { test("oid_test_case_templates_two", R"( [3] Pointer -[0] Class: TemplatedClass2 (size: 12) +[0] Class: ns_templates::TemplatedClass2 (size: 12) Param -[1] Struct: Foo (size: 8) +[1] Struct: ns_templates::Foo (size: 8) Member: a (offset: 0) Primitive: int32_t Member: b (offset: 4) @@ -421,7 +422,7 @@ TEST_F(LLDBParserTest, ClassTemplateTwo) { Param Primitive: int32_t Member: tc1 (offset: 0) -[2] Class: TemplatedClass1 (size: 8) +[2] Class: ns_templates::TemplatedClass1 (size: 8) Param [1] Member: val (offset: 0) @@ -434,7 +435,7 @@ TEST_F(LLDBParserTest, ClassTemplateTwo) { TEST_F(LLDBParserTest, ClassTemplateValue) { test("oid_test_case_templates_value", R"( [2] Pointer -[0] Struct: TemplatedClassVal<3> (size: 12) +[0] Struct: ns_templates::TemplatedClassVal<3> (size: 12) Param Value: 3 Primitive: int32_t @@ -448,18 +449,18 @@ TEST_F(LLDBParserTest, TemplateEnumValue) { testMultiCompilerGlob("oid_test_case_enums_params_scoped_enum_val", R"( [1] Pointer -[0] Class: MyClass (size: 4) +[0] Class: ns_enums_params::MyClass (size: 4) Param Value: ns_enums_params::MyNS::ScopedEnum::One - Enum: ScopedEnum (size: 4) + Enum: ns_enums_params::MyNS::ScopedEnum (size: 4) * )", R"( [1] Pointer -[0] Class: MyClass<(ns_enums_params::MyNS::ScopedEnum)1> (size: 4) +[0] Class: ns_enums_params::MyClass<(ns_enums_params::MyNS::ScopedEnum)1> (size: 4) Param Value: ns_enums_params::MyNS::ScopedEnum::One - Enum: ScopedEnum (size: 4) + Enum: ns_enums_params::MyNS::ScopedEnum (size: 4) * )"); } @@ -468,10 +469,10 @@ TEST_F(LLDBParserTest, TemplateEnumValueGaps) { testMultiCompilerGlob("oid_test_case_enums_params_scoped_enum_val_gaps", R"( [1] Pointer -[0] Class: ClassGaps (size: 4) +[0] Class: ns_enums_params::ClassGaps (size: 4) Param Value: ns_enums_params::MyNS::EnumWithGaps::Twenty - Enum: EnumWithGaps (size: 4) + Enum: ns_enums_params::MyNS::EnumWithGaps (size: 4) * )", R"( @@ -479,7 +480,7 @@ TEST_F(LLDBParserTest, TemplateEnumValueGaps) { [0] Class: ClassGaps<(ns_enums_params::MyNS::EnumWithGaps)20> (size: 4) Param Value: ns_enums_params::MyNS::EnumWithGaps::Twenty - Enum: EnumWithGaps (size: 4) + Enum: ns_enums_params::MyNS::EnumWithGaps (size: 4) * )"); } @@ -488,18 +489,18 @@ TEST_F(LLDBParserTest, TemplateEnumValueNegative) { testMultiCompilerGlob("oid_test_case_enums_params_scoped_enum_val_negative", R"( [1] Pointer -[0] Class: ClassGaps (size: 4) +[0] Class: ns_enums_params::ClassGaps (size: 4) Param Value: ns_enums_params::MyNS::EnumWithGaps::MinusTwo - Enum: EnumWithGaps (size: 4) + Enum: ns_enums_params::MyNS::EnumWithGaps (size: 4) * )", R"( [1] Pointer -[0] Class: ClassGaps<(ns_enums_params::MyNS::EnumWithGaps)-2> (size: 4) +[0] Class: ns_enums_params::ClassGaps<(ns_enums_params::MyNS::EnumWithGaps)-2> (size: 4) Param Value: ns_enums_params::MyNS::EnumWithGaps::MinusTwo - Enum: EnumWithGaps (size: 4) + Enum: ns_enums_params::MyNS::EnumWithGaps (size: 4) * )"); } @@ -620,10 +621,11 @@ TEST_F(LLDBParserTest, BitfieldsMixed) { } TEST_F(LLDBParserTest, BitfieldsMoreBitsThanType) { + // TODO: Validate with integration tests that 29 bitsize doesn't break CodeGen test("oid_test_case_bitfields_more_bits_than_type", R"( [1] Pointer [0] Struct: ns_bitfields::MoreBitsThanType (size: 4) - Member: a (offset: 0, bitsize: 8) + Member: a (offset: 0, bitsize: 29) Primitive: int8_t )"); } From e1f70763301a4fbe05926be2354b42c08174ef9d Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Thu, 14 Dec 2023 09:24:16 -0800 Subject: [PATCH 16/20] Formatting --- oi/type_graph/LLDBParser.cpp | 58 ++++++++++++++++++++++-------------- oi/type_graph/LLDBParser.h | 15 ++++++---- test/test_lldb_parser.cpp | 4 +-- 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index 215afff1..6d6f4eef 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -17,6 +17,7 @@ #include #include + #include namespace std { @@ -29,10 +30,10 @@ namespace std { * Is there a better way to do this? */ size_t hash::operator()(const lldb::SBType& key) const { - auto *name = const_cast(key).GetName(); + auto* name = const_cast(key).GetName(); auto Hasher = std::hash(); { /* Debugging - auto &keyAsSP = reinterpret_cast(key); + auto& keyAsSP = reinterpret_cast(key); fprintf(stderr, "LLDBType::hash(key@0x%08zx = (0x%08zx, %s)) = 0x%08zx\n", (uintptr_t)keyAsSP.get(), (uintptr_t)name, name, Hasher(name)); // Debugging */ @@ -40,14 +41,17 @@ size_t hash::operator()(const lldb::SBType& key) const { return Hasher(name); } -bool equal_to::operator()(const lldb::SBType& lhs, const lldb::SBType& rhs) const { +bool equal_to::operator()(const lldb::SBType& lhs, + const lldb::SBType& rhs) const { auto* lhsName = const_cast(lhs).GetName(); auto* rhsName = const_cast(rhs).GetName(); auto EqualTo = std::equal_to(); { /* Debugging - auto &lhsAsSP = reinterpret_cast(lhs); - auto &rhsAsSP = reinterpret_cast(rhs); - fprintf(stderr, "LLDBType::equal_to(lhs@0x%08zx = (0x%08zx, %s), rhs@0x%08zx = (0x%08zx, %s)) = %d\n", + auto& lhsAsSP = reinterpret_cast(lhs); + auto& rhsAsSP = reinterpret_cast(rhs); + fprintf(stderr, "LLDBType::equal_to(" + "lhs@0x%08zx = (0x%08zx, %s), " + "rhs@0x%08zx = (0x%08zx, %s)) = %d\n", (uintptr_t)lhsAsSP.get(), (uintptr_t)lhsName, lhsName, (uintptr_t)rhsAsSP.get(), (uintptr_t)rhsName, rhsName, EqualTo(lhsName, rhsName)); @@ -71,6 +75,7 @@ struct DepthGuard { ~DepthGuard() { --depth_; } + private: int& depth_; }; @@ -152,10 +157,11 @@ Class& LLDBParser::enumerateClass(lldb::SBType& type) { kind = Class::Kind::Union; break; default: - throw LLDBParserError{"Invalid type class for compound type: " + std::to_string(typeClass)}; + throw LLDBParserError{"Invalid type class for compound type: " + + std::to_string(typeClass)}; } - auto &c = makeType(type, kind, displayName, name, size, virtuality); + auto& c = makeType(type, kind, displayName, name, size, virtuality); enumerateClassTemplateParams(type, c.templateParams); enumerateClassParents(type, c.parents); @@ -165,7 +171,8 @@ Class& LLDBParser::enumerateClass(lldb::SBType& type) { return c; } -void LLDBParser::enumerateClassTemplateParams(lldb::SBType &type, std::vector& params) { +void LLDBParser::enumerateClassTemplateParams( + lldb::SBType& type, std::vector& params) { assert(params.empty()); params.reserve(type.GetNumberOfTemplateArguments()); @@ -180,13 +187,15 @@ void LLDBParser::enumerateTemplateParam(lldb::SBType& /*type*/, uint32_t /*i*/, std::vector& params) { QualifierSet qualifiers; - qualifiers[Qualifier::Const] = param.GetName() != param.GetUnqualifiedType().GetName(); // TODO: Wonky as hell :/ + qualifiers[Qualifier::Const] = // TODO: Wonky as hell :/ + param.GetName() != param.GetUnqualifiedType().GetName(); auto& paramType = enumerateType(param); params.emplace_back(paramType, qualifiers); } -void LLDBParser::enumerateClassParents(lldb::SBType& type, std::vector& parents) { +void LLDBParser::enumerateClassParents(lldb::SBType& type, + std::vector& parents) { assert(parents.empty()); parents.reserve(type.GetNumberOfDirectBaseClasses()); @@ -198,7 +207,8 @@ void LLDBParser::enumerateClassParents(lldb::SBType& type, std::vector& } } -void LLDBParser::enumerateClassMembers(lldb::SBType& type, std::vector& members) { +void LLDBParser::enumerateClassMembers(lldb::SBType& type, + std::vector& members) { assert(members.empty()); members.reserve(type.GetNumberOfFields()); @@ -206,7 +216,7 @@ void LLDBParser::enumerateClassMembers(lldb::SBType& type, std::vector& for (uint32_t i = 0; i < type.GetNumberOfFields(); i++) { auto field = type.GetFieldAtIndex(i); if (field.GetName() == nullptr) - continue; // Skip anonymous fields (TODO: Why?) + continue; // Skip anonymous fields (TODO: Why?) auto fieldName = field.GetName(); auto fieldType = field.GetType(); auto bitFieldSize = field.GetBitfieldSizeInBits(); @@ -221,7 +231,8 @@ void LLDBParser::enumerateClassMembers(lldb::SBType& type, std::vector& }); } -void LLDBParser::enumerateClassFunctions(lldb::SBType &type, std::vector& functions) { +void LLDBParser::enumerateClassFunctions(lldb::SBType& type, + std::vector& functions) { assert(functions.empty()); functions.reserve(type.GetNumberOfMemberFunctions()); @@ -235,7 +246,7 @@ void LLDBParser::enumerateClassFunctions(lldb::SBType &type, std::vector(type, Primitive::Kind::StubbedPointer); - } + if (!chasePointer()) { + return makeType(type, Primitive::Kind::StubbedPointer); + } lldb::SBType pointeeType; switch (auto kind = type.GetTypeClass()) { @@ -273,11 +284,12 @@ Type& LLDBParser::enumeratePointer(lldb::SBType& type) { pointeeType = type.GetDereferencedType(); break; default: - throw LLDBParserError{"Invalid type class for pointer type: " + std::to_string(kind)}; + throw LLDBParserError{"Invalid type class for pointer type: " + + std::to_string(kind)}; } if (pointeeType.IsFunctionType()) { - return makeType(type, Primitive::Kind::StubbedPointer); + return makeType(type, Primitive::Kind::StubbedPointer); } Type& t = enumerateType(pointeeType); @@ -291,7 +303,8 @@ bool LLDBParser::chasePointer() const { return options_.chaseRawPointers; } -Primitive::Kind LLDBParser::primitiveIntKind(lldb::SBType& type, bool is_signed) { +Primitive::Kind LLDBParser::primitiveIntKind(lldb::SBType& type, + bool is_signed) { switch (auto size = type.GetByteSize()) { case 1: return is_signed ? Primitive::Kind::Int8 : Primitive::Kind::UInt8; @@ -376,7 +389,8 @@ Primitive& LLDBParser::enumeratePrimitive(lldb::SBType& type) { case lldb::eBasicTypeObjCSel: case lldb::eBasicTypeOther: default: - throw LLDBParserError{"Unhandled primitive type: " + std::to_string(kind)}; + throw LLDBParserError{"Unhandled primitive type: " + + std::to_string(kind)}; } return makeType(type, primitiveKind); diff --git a/oi/type_graph/LLDBParser.h b/oi/type_graph/LLDBParser.h index 43e55281..be9e158b 100644 --- a/oi/type_graph/LLDBParser.h +++ b/oi/type_graph/LLDBParser.h @@ -15,11 +15,11 @@ */ #pragma once +#include + #include "TypeGraph.h" #include "Types.h" -#include - namespace std { /* lldb::SBType doesn't provide a hash and equality operators. * So we define our own specialization of them here. @@ -61,11 +61,16 @@ class LLDBParser { Primitive::Kind primitiveIntKind(lldb::SBType& type, bool is_signed); Primitive::Kind primitiveFloatKind(lldb::SBType& type); - void enumerateClassTemplateParams(lldb::SBType &type, std::vector& params); - void enumerateTemplateParam(lldb::SBType& type, lldb::SBType& param, uint32_t i, std::vector& params); + void enumerateClassTemplateParams(lldb::SBType& type, + std::vector& params); + void enumerateTemplateParam(lldb::SBType& type, + lldb::SBType& param, + uint32_t i, + std::vector& params); void enumerateClassParents(lldb::SBType& type, std::vector& parents); void enumerateClassMembers(lldb::SBType& type, std::vector& members); - void enumerateClassFunctions(lldb::SBType &type, std::vector& functions); + void enumerateClassFunctions(lldb::SBType& type, + std::vector& functions); bool chasePointer() const; diff --git a/test/test_lldb_parser.cpp b/test/test_lldb_parser.cpp index 11659ec1..1089d6ce 100644 --- a/test/test_lldb_parser.cpp +++ b/test/test_lldb_parser.cpp @@ -83,8 +83,8 @@ void LLDBParserTest::test(std::string_view function, std::string_view expected) { // Enable options in unit tests so we get more coverage LLDBParserOptions options = { - .chaseRawPointers = true, - .readEnumValues = true, + .chaseRawPointers = true, + .readEnumValues = true, }; test(function, expected, options); } From 233ce3c5b1080c29402a605cf5587864d3ad6327 Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Tue, 19 Dec 2023 07:02:28 -0800 Subject: [PATCH 17/20] Simplify depth handling --- oi/type_graph/CMakeLists.txt | 1 + oi/type_graph/LLDBParser.cpp | 22 +++++----------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/oi/type_graph/CMakeLists.txt b/oi/type_graph/CMakeLists.txt index d6cbfb76..2ddf726d 100644 --- a/oi/type_graph/CMakeLists.txt +++ b/oi/type_graph/CMakeLists.txt @@ -24,6 +24,7 @@ add_dependencies(type_graph libdrgn) target_link_libraries(type_graph container_info symbol_service + Boost::headers LLDB "-L${DRGN_PATH}/.libs" diff --git a/oi/type_graph/LLDBParser.cpp b/oi/type_graph/LLDBParser.cpp index 6d6f4eef..4d40f768 100644 --- a/oi/type_graph/LLDBParser.cpp +++ b/oi/type_graph/LLDBParser.cpp @@ -15,6 +15,7 @@ */ #include "LLDBParser.h" +#include #include #include @@ -68,26 +69,13 @@ Type& LLDBParser::parse(lldb::SBType& root) { return enumerateType(root); } -struct DepthGuard { - explicit DepthGuard(int& depth) : depth_(depth) { - ++depth_; - } - ~DepthGuard() { - --depth_; - } - - private: - int& depth_; -}; - Type& LLDBParser::enumerateType(lldb::SBType& type) { // Avoid re-enumerating an already-processsed type if (auto it = lldb_types_.find(type); it != lldb_types_.end()) return it->second; - DepthGuard guard(depth_); - - bool isTypeIncomplete = !type.IsTypeComplete(); + ++depth_; + BOOST_SCOPE_EXIT_ALL(&) { --depth_; }; std::optional> t; try { @@ -128,11 +116,11 @@ Type& LLDBParser::enumerateType(lldb::SBType& type) { throw LLDBParserError{"Unhandled type class: " + std::to_string(kind)}; } } catch (const LLDBParserError& e) { - if (!isTypeIncomplete) + if (type.IsTypeComplete()) throw; } - if (isTypeIncomplete) { + if (!type.IsTypeComplete()) { return makeType(type, *t); } From 1d752ca55606f127c299bdde591970ad4af3c8e7 Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Tue, 19 Dec 2023 09:18:50 -0800 Subject: [PATCH 18/20] Isolate drgn_type to CodeGenV1 --- oi/Features.cpp | 5 +++++ oi/Features.h | 1 + oi/OIDebugger.cpp | 24 ++++++++++++++++++------ oi/SymbolService.cpp | 2 +- oi/SymbolService.h | 2 +- test/test_drgn_parser.cpp | 2 +- 6 files changed, 27 insertions(+), 9 deletions(-) diff --git a/oi/Features.cpp b/oi/Features.cpp index 9d82b17c..9e04ac50 100644 --- a/oi/Features.cpp +++ b/oi/Features.cpp @@ -36,6 +36,8 @@ std::optional 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: @@ -60,6 +62,9 @@ std::optional featureHelp(Feature f) { std::span 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; diff --git a/oi/Features.h b/oi/Features.h index 959a5761..01300cc5 100644 --- a/oi/Features.h +++ b/oi/Features.h @@ -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") \ diff --git a/oi/OIDebugger.cpp b/oi/OIDebugger.cpp index ee9f040c..2d4fa1ee 100644 --- a/oi/OIDebugger.cpp +++ b/oi/OIDebugger.cpp @@ -2962,17 +2962,24 @@ bool OIDebugger::processTargetData() { } std::optional 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 @@ -2981,10 +2988,15 @@ std::optional 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{}}; } else { + auto root = symbols->getDrgnRootType(req); + if (!root.has_value()) { + return std::nullopt; + } + // OICodeGen (v1) auto codegen = OICodeGen::buildFromConfig(generatorConfig, *symbols); if (!codegen) { diff --git a/oi/SymbolService.cpp b/oi/SymbolService.cpp index 15d19d2e..59853879 100644 --- a/oi/SymbolService.cpp +++ b/oi/SymbolService.cpp @@ -804,7 +804,7 @@ std::string SymbolService::getTypeName(struct drgn_type* type) { return drgn_utils::typeToName(type); } -std::optional SymbolService::getRootType(const irequest& req) { +std::optional SymbolService::getDrgnRootType(const irequest& req) { if (req.type == "global") { /* * This is super simple as all we have to do is locate assign the diff --git a/oi/SymbolService.h b/oi/SymbolService.h index c95f125a..e12c93a0 100644 --- a/oi/SymbolService.h +++ b/oi/SymbolService.h @@ -54,7 +54,7 @@ class SymbolService { std::shared_ptr findFuncDesc(const irequest&); std::shared_ptr findGlobalDesc(const std::string&); static std::string getTypeName(struct drgn_type*); - std::optional getRootType(const irequest&); + std::optional getDrgnRootType(const irequest&); static std::optional findTypeOfSymbol( drgn_program*, const std::string& symbolName); diff --git a/test/test_drgn_parser.cpp b/test/test_drgn_parser.cpp index c4e51e1d..87796ac6 100644 --- a/test/test_drgn_parser.cpp +++ b/test/test_drgn_parser.cpp @@ -35,7 +35,7 @@ DrgnParser DrgnParserTest::getDrgnParser(TypeGraph& typeGraph, drgn_type* DrgnParserTest::getDrgnRoot(std::string_view function) { irequest req{"entry", std::string{function}, "arg0"}; - auto* drgnRoot = symbols_->getRootType(req)->type.type; + auto* drgnRoot = symbols_->getDrgnRootType(req)->type.type; return drgnRoot; } From 0c3dde852084ff49936b210d38f90e36bbe831d4 Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Tue, 19 Dec 2023 10:54:40 -0800 Subject: [PATCH 19/20] Implement SymbolService::getLLDBTarget() --- oi/CMakeLists.txt | 1 + oi/SymbolService.cpp | 71 +++++++++++++++++++++++++++++++++++++++++--- oi/SymbolService.h | 12 ++++++-- 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/oi/CMakeLists.txt b/oi/CMakeLists.txt index 3754f413..c6112001 100644 --- a/oi/CMakeLists.txt +++ b/oi/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(symbol_service SymbolService.cpp ) target_link_libraries(symbol_service + LLDB drgn_utils Boost::headers diff --git a/oi/SymbolService.cpp b/oi/SymbolService.cpp index 59853879..21eff2d9 100644 --- a/oi/SymbolService.cpp +++ b/oi/SymbolService.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "oi/DrgnUtils.h" #include "oi/OIParser.h" @@ -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()) { @@ -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()); @@ -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 { @@ -432,7 +443,12 @@ std::optional 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; } @@ -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(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(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. diff --git a/oi/SymbolService.h b/oi/SymbolService.h index e12c93a0..ee0a593f 100644 --- a/oi/SymbolService.h +++ b/oi/SymbolService.h @@ -16,6 +16,9 @@ #pragma once #include +#include +#include +#include #include #include #include @@ -38,14 +41,16 @@ struct SymbolInfo { }; class SymbolService { + enum class Backend { DRGN, LLDB }; public: - SymbolService(pid_t); - SymbolService(std::filesystem::path); + SymbolService(pid_t, Backend = Backend::DRGN); + SymbolService(std::filesystem::path, Backend = Backend::DRGN); SymbolService(const SymbolService&) = delete; SymbolService& operator=(const SymbolService&) = delete; ~SymbolService(); struct drgn_program* getDrgnProgram(); + lldb::SBTarget getLLDBTarget(); std::optional locateBuildID(); std::optional locateSymbol(const std::string&, @@ -70,8 +75,11 @@ class SymbolService { private: std::variant target; + Backend backend; struct Dwfl* dwfl{nullptr}; struct drgn_program* prog{nullptr}; + lldb::SBDebugger lldbDebugger{}; + lldb::SBTarget lldbTarget{}; bool loadModules(); bool loadModulesFromPid(pid_t); From 7be58b15f8006dcb1d552b71392d0a49717cb79d Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Tue, 19 Dec 2023 10:56:49 -0800 Subject: [PATCH 20/20] Implement SymbolService::getLLDBRootType() --- oi/SymbolService.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++++ oi/SymbolService.h | 1 + 2 files changed, 78 insertions(+) diff --git a/oi/SymbolService.cpp b/oi/SymbolService.cpp index 21eff2d9..f637c907 100644 --- a/oi/SymbolService.cpp +++ b/oi/SymbolService.cpp @@ -975,4 +975,81 @@ std::optional SymbolService::getDrgnRootType(const irequest& req) { return RootInfo{paramName, paramType}; } +std::optional 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 diff --git a/oi/SymbolService.h b/oi/SymbolService.h index ee0a593f..c4f7f654 100644 --- a/oi/SymbolService.h +++ b/oi/SymbolService.h @@ -60,6 +60,7 @@ class SymbolService { std::shared_ptr findGlobalDesc(const std::string&); static std::string getTypeName(struct drgn_type*); std::optional getDrgnRootType(const irequest&); + std::optional getLLDBRootType(const irequest&); static std::optional findTypeOfSymbol( drgn_program*, const std::string& symbolName);