zturner updated this revision to Diff 53775. zturner added a comment. Changes from first patch:
1. Based on your other comment that it wasn't actually necessary to create a `TypeSP` for builtin types, I went ahead and removed all the code that was looking at bit and byte sizes. So all that stuff is now gone. 2. Changed classes to resolve as forward declares, but I don't actually complete them yet. If it crashes if someone tries to complete it, I'm fine with that for now, I will probably work on completing them next anyway. 3. Fully completing enums from the pdb file now. 4. Added a bunch of unit tests. There's no real way to write non-unit tests for this code yet, because that would require having a PDB and a matching executable and having the test generate it (like it does with dwarf, for example, by just compiling the executable with a Makefile). I could do this with MSVC, but our test Makefile system only works with clang, which won't know how to emit PDBs for a number of months. In the future once clang knows how to generate pdbs I will update the test suite to run the entire suite with PDB as a 4th kind of debug format in addition to dsym, dwarf, and dwo. http://reviews.llvm.org/D18848 Files: include/lldb/Symbol/ClangASTContext.h source/Plugins/SymbolFile/PDB/CMakeLists.txt source/Plugins/SymbolFile/PDB/PDBASTParser.cpp source/Plugins/SymbolFile/PDB/PDBASTParser.h source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp source/Plugins/SymbolFile/PDB/SymbolFilePDB.h source/Symbol/ClangASTContext.cpp unittests/SymbolFile/PDB/CMakeLists.txt unittests/SymbolFile/PDB/Inputs/test-pdb-types.cpp unittests/SymbolFile/PDB/Inputs/test-pdb-types.pdb unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp
Index: unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp =================================================================== --- unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp +++ unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp @@ -11,6 +11,8 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Config/config.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -20,6 +22,7 @@ #include "lldb/Core/ModuleSpec.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/SymbolVendor.h" @@ -29,9 +32,12 @@ #include "Plugins/SymbolFile/PDB/SymbolFilePDB.h" #if defined(_MSC_VER) +#include "lldb/Host/windows/windows.h" #include <objbase.h> #endif +#include <algorithm> + extern const char *TestMainArgv0; using namespace lldb_private; @@ -42,13 +48,17 @@ void SetUp() override { +// Initialize and TearDown the plugin every time, so we get a brand new +// AST every time so that modifications to the AST from each test don't +// leak into the next test. #if defined(_MSC_VER) ::CoInitializeEx(nullptr, COINIT_MULTITHREADED); #endif - HostInfoBase::Initialize(); + HostInfo::Initialize(); ObjectFilePECOFF::Initialize(); SymbolFileDWARF::Initialize(); + ClangASTContext::Initialize(); SymbolFilePDB::Initialize(); llvm::StringRef exe_folder = llvm::sys::path::parent_path(TestMainArgv0); @@ -57,24 +67,30 @@ m_pdb_test_exe = inputs_folder; m_dwarf_test_exe = inputs_folder; + m_types_test_exe = inputs_folder; llvm::sys::path::append(m_pdb_test_exe, "test-pdb.exe"); llvm::sys::path::append(m_dwarf_test_exe, "test-dwarf.exe"); + llvm::sys::path::append(m_types_test_exe, "test-pdb-types.exe"); } void TearDown() override { -#if defined(_MSC_VER) - ::CoUninitialize(); -#endif SymbolFilePDB::Terminate(); + ClangASTContext::Initialize(); SymbolFileDWARF::Terminate(); ObjectFilePECOFF::Terminate(); + HostInfo::Terminate(); + +#if defined(_MSC_VER) + ::CoUninitialize(); +#endif } protected: llvm::SmallString<128> m_pdb_test_exe; llvm::SmallString<128> m_dwarf_test_exe; + llvm::SmallString<128> m_types_test_exe; bool FileSpecMatchesAsBaseOrFull(const FileSpec &left, const FileSpec &right) const @@ -116,6 +132,35 @@ } return false; } + + int + GetGlobalConstantInteger(const llvm::IPDBSession &session, llvm::StringRef var) const + { + auto global = session.getGlobalScope(); + auto results = global->findChildren(llvm::PDB_SymType::Data, var, llvm::PDB_NameSearchFlags::NS_Default); + uint32_t count = results->getChildCount(); + if (count == 0) + return -1; + + auto item = results->getChildAtIndex(0); + auto symbol = llvm::dyn_cast<llvm::PDBSymbolData>(item.get()); + if (!symbol) + return -1; + llvm::Variant value = symbol->getValue(); + switch (value.Type) + { + case llvm::PDB_VariantType::Int16: + return value.Value.Int16; + case llvm::PDB_VariantType::Int32: + return value.Value.Int32; + case llvm::PDB_VariantType::UInt16: + return value.Value.UInt16; + case llvm::PDB_VariantType::UInt32: + return value.Value.UInt32; + default: + return 0; + } + } }; #if defined(HAVE_DIA_SDK) @@ -342,3 +387,196 @@ VerifyLineEntry(module, sc, source_file, *lt, 9, 0x401045); VerifyLineEntry(module, sc, header1, *lt, 9, 0x401090); } + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestSimpleClassTypes)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + EXPECT_EQ(1, symfile->FindTypes(sc, ConstString("Class"), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1, results.GetSize()); + lldb::TypeSP udt_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString("Class"), udt_type->GetName()); + CompilerType compiler_type = udt_type->GetForwardCompilerType(); + EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); + EXPECT_EQ(GetGlobalConstantInteger(session, "sizeof_Class"), udt_type->GetByteSize()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestNestedClassTypes)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + EXPECT_EQ(1, symfile->FindTypes(sc, ConstString("Class::NestedClass"), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1, results.GetSize()); + lldb::TypeSP udt_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString("Class::NestedClass"), udt_type->GetName()); + CompilerType compiler_type = udt_type->GetForwardCompilerType(); + EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); + EXPECT_EQ(GetGlobalConstantInteger(session, "sizeof_NestedClass"), udt_type->GetByteSize()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestClassInNamespace)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + EXPECT_EQ(1, symfile->FindTypes(sc, ConstString("NS::NSClass"), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1, results.GetSize()); + lldb::TypeSP udt_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString("NS::NSClass"), udt_type->GetName()); + CompilerType compiler_type = udt_type->GetForwardCompilerType(); + EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); + EXPECT_EQ(GetGlobalConstantInteger(session, "sizeof_NSClass"), udt_type->GetByteSize()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestEnumTypes)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + const char *EnumsToCheck[] = {"Enum", "ShortEnum"}; + for (auto Enum : EnumsToCheck) + { + TypeMap results; + EXPECT_EQ(1, symfile->FindTypes(sc, ConstString(Enum), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1, results.GetSize()); + lldb::TypeSP enum_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString(Enum), enum_type->GetName()); + CompilerType compiler_type = enum_type->GetFullCompilerType(); + EXPECT_TRUE(ClangASTContext::IsEnumType(compiler_type.GetOpaqueQualType())); + clang::EnumDecl *enum_decl = ClangASTContext::GetAsEnumDecl(compiler_type); + EXPECT_NE(nullptr, enum_decl); + EXPECT_EQ(2, std::distance(enum_decl->enumerator_begin(), enum_decl->enumerator_end())); + + std::string sizeof_var = "sizeof_"; + sizeof_var.append(Enum); + EXPECT_EQ(GetGlobalConstantInteger(session, sizeof_var.c_str()), enum_type->GetByteSize()); + } +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestArrayTypes)) +{ + // In order to get this test working, we need to support lookup by symbol name. Because array + // types themselves do not have names, only the symbols have names (i.e. the name of the array). +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestFunctionTypes)) +{ + // In order to get this test working, we need to support lookup by symbol name. Because array + // types themselves do not have names, only the symbols have names (i.e. the name of the array). +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestTypedefs)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + + const char *TypedefsToCheck[] = {"ClassTypedef", "NSClassTypedef"}; + for (auto Typedef : TypedefsToCheck) + { + TypeMap results; + EXPECT_EQ(1, symfile->FindTypes(sc, ConstString(Typedef), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1, results.GetSize()); + lldb::TypeSP typedef_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString(Typedef), typedef_type->GetName()); + CompilerType compiler_type = typedef_type->GetFullCompilerType(); + ClangASTContext *clang_type_system = llvm::dyn_cast_or_null<ClangASTContext>(compiler_type.GetTypeSystem()); + EXPECT_TRUE(clang_type_system->IsTypedefType(compiler_type.GetOpaqueQualType())); + + std::string sizeof_var = "sizeof_"; + sizeof_var.append(Typedef); + EXPECT_EQ(GetGlobalConstantInteger(session, sizeof_var.c_str()), typedef_type->GetByteSize()); + } +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestRegexNameMatch)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + int num_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, false, 0, searched_files, results); + EXPECT_GT(num_results, 1); + EXPECT_EQ(num_results, results.GetSize()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestMaxMatches)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + int num_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, false, 0, searched_files, results); + // Try to limit ourselves from 1 to 10 results, otherwise we could be doing this thousands of times. + // The idea is just to make sure that for a variety of values, the number of limited results always + // comes out to the number we are expecting. + int iterations = std::min(num_results, 10); + for (int i = 1; i <= iterations; ++i) + { + int num_limited_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, false, i, searched_files, results); + EXPECT_EQ(i, num_limited_results); + EXPECT_EQ(num_limited_results, results.GetSize()); + } +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestNullName)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + int num_results = symfile->FindTypes(sc, ConstString(), nullptr, false, 0, searched_files, results); + EXPECT_EQ(0, num_results); + EXPECT_EQ(0, results.GetSize()); +} Index: unittests/SymbolFile/PDB/Inputs/test-pdb-types.cpp =================================================================== --- /dev/null +++ unittests/SymbolFile/PDB/Inputs/test-pdb-types.cpp @@ -0,0 +1,86 @@ +// Compile with "cl /c /Zi /GR- /EHsc test-pdb-types.cpp" +// Link with "link test-pdb-types.obj /debug /nodefaultlib /entry:main /out:test-pdb-types.exe" + +using namespace std; + +// Sizes of builtin types +static const int sizeof_char = sizeof(char); +static const int sizeof_uchar = sizeof(unsigned char); +static const int sizeof_short = sizeof(short); +static const int sizeof_ushort = sizeof(unsigned short); +static const int sizeof_int = sizeof(int); +static const int sizeof_uint = sizeof(unsigned int); +static const int sizeof_long = sizeof(long); +static const int sizeof_ulong = sizeof(unsigned long); +static const int sizeof_longlong = sizeof(long long); +static const int sizeof_ulonglong = sizeof(unsigned long long); +static const int sizeof_int64 = sizeof(__int64); +static const int sizeof_uint64 = sizeof(unsigned __int64); +static const int sizeof_float = sizeof(float); +static const int sizeof_double = sizeof(double); +static const int sizeof_bool = sizeof(bool); +static const int sizeof_wchar = sizeof(wchar_t); + +enum Enum +{ + EValue1 = 1, + EValue2 = 2, +}; + +enum ShortEnum : short +{ + ESValue1 = 1, + ESValue2 = 2 +}; + +namespace NS +{ +class NSClass +{ + float f; + double d; +}; +} + +class Class +{ +public: + class NestedClass + { + Enum e; + }; + ShortEnum se; +}; + +int +test_func(int a, int b) +{ + return a + b; +} + +typedef Class ClassTypedef; +typedef NS::NSClass NSClassTypedef; +int GlobalArray[10]; + +static const int sizeof_NSClass = sizeof(NS::NSClass); +static const int sizeof_Class = sizeof(Class); +static const int sizeof_NestedClass = sizeof(Class::NestedClass); +static const int sizeof_Enum = sizeof(Enum); +static const int sizeof_ShortEnum = sizeof(ShortEnum); +static const int sizeof_ClassTypedef = sizeof(ClassTypedef); +static const int sizeof_NSClassTypedef = sizeof(NSClassTypedef); +static const int sizeof_GlobalArray = sizeof(GlobalArray); + +int +main(int argc, char **argv) +{ + ShortEnum e1; + Enum e2; + Class c1; + Class::NestedClass c2; + NS::NSClass c3; + + ClassTypedef t1; + NSClassTypedef t2; + return test_func(1, 2); +} Index: unittests/SymbolFile/PDB/CMakeLists.txt =================================================================== --- unittests/SymbolFile/PDB/CMakeLists.txt +++ unittests/SymbolFile/PDB/CMakeLists.txt @@ -5,6 +5,8 @@ set(test_inputs test-pdb.exe test-pdb.pdb - test-dwarf.exe) + test-dwarf.exe + test-pdb-types.exe + test-pdb-types.pdb) add_unittest_inputs(SymbolFilePDBTests "${test_inputs}") Index: source/Symbol/ClangASTContext.cpp =================================================================== --- source/Symbol/ClangASTContext.cpp +++ source/Symbol/ClangASTContext.cpp @@ -91,6 +91,7 @@ #include "lldb/Target/Target.h" #include "Plugins/SymbolFile/DWARF/DWARFASTParserClang.h" +#include "Plugins/SymbolFile/PDB/PDBASTParser.h" #include <stdio.h> @@ -3443,7 +3444,27 @@ } bool -ClangASTContext::IsPolymorphicClass (lldb::opaque_compiler_type_t type) +ClangASTContext::IsClassType(lldb::opaque_compiler_type_t type) +{ + if (!type) + return false; + clang::QualType qual_type(GetCanonicalQualType(type)); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + return (type_class == clang::Type::Record); +} + +bool +ClangASTContext::IsEnumType(lldb::opaque_compiler_type_t type) +{ + if (!type) + return false; + clang::QualType qual_type(GetCanonicalQualType(type)); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + return (type_class == clang::Type::Enum); +} + +bool +ClangASTContext::IsPolymorphicClass(lldb::opaque_compiler_type_t type) { if (type) { @@ -5205,7 +5226,7 @@ CompilerType ClangASTContext::GetBuiltinTypeByName (const ConstString &name) { - return GetBasicType (GetBasicTypeEnumeration (name)); + return GetBasicType(GetBasicTypeEnumeration(name)); } lldb::BasicType @@ -9465,6 +9486,13 @@ return m_dwarf_ast_parser_ap.get(); } +PDBASTParser * +ClangASTContext::GetPDBParser() +{ + if (!m_pdb_ast_parser_ap) + m_pdb_ast_parser_ap.reset(new PDBASTParser(*this)); + return m_pdb_ast_parser_ap.get(); +} bool ClangASTContext::LayoutRecordType(void *baton, Index: source/Plugins/SymbolFile/PDB/SymbolFilePDB.h =================================================================== --- source/Plugins/SymbolFile/PDB/SymbolFilePDB.h +++ source/Plugins/SymbolFile/PDB/SymbolFilePDB.h @@ -10,11 +10,10 @@ #ifndef lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_ #define lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_ -#include <unordered_map> - #include "lldb/Core/UserID.h" #include "lldb/Symbol/SymbolFile.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/DebugInfo/PDB/IPDBSession.h" #include "llvm/DebugInfo/PDB/PDB.h" @@ -170,6 +169,12 @@ uint32_t GetPluginVersion() override; + llvm::IPDBSession & + GetPDBSession(); + + const llvm::IPDBSession & + GetPDBSession() const; + private: lldb::CompUnitSP ParseCompileUnitForSymIndex(uint32_t id); @@ -179,12 +184,21 @@ void BuildSupportFileIdToSupportFileIndexMap(const llvm::PDBSymbolCompiland &cu, - std::unordered_map<uint32_t, uint32_t> &index_map) const; + llvm::DenseMap<uint32_t, uint32_t> &index_map) const; + + void + FindTypesByRegex(const std::string ®ex, uint32_t max_matches, lldb_private::TypeMap &types); + + void + FindTypesByName(const std::string &name, uint32_t max_matches, lldb_private::TypeMap &types); - std::unordered_map<uint32_t, lldb::CompUnitSP> m_comp_units; + llvm::DenseMap<uint32_t, lldb::CompUnitSP> m_comp_units; + llvm::DenseMap<uint32_t, lldb::TypeSP> m_types; + std::vector<lldb::TypeSP> m_builtin_types; std::unique_ptr<llvm::IPDBSession> m_session_up; uint32_t m_cached_compile_unit_count; + std::unique_ptr<lldb_private::CompilerDeclContext> m_tu_decl_ctx_up; }; #endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_ Index: source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp =================================================================== --- source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp +++ source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp @@ -9,12 +9,16 @@ #include "SymbolFilePDB.h" +#include "clang/Lex/Lexer.h" + #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/TypeMap.h" #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" #include "llvm/DebugInfo/PDB/IPDBLineNumber.h" @@ -26,6 +30,13 @@ #include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" #include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h" #include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" + +#include "Plugins/SymbolFile/PDB/PDBASTParser.h" + +#include <regex> using namespace lldb_private; @@ -116,6 +127,10 @@ { lldb::addr_t obj_load_address = m_obj_file->GetFileOffset(); m_session_up->setLoadAddress(obj_load_address); + + TypeSystem *type_system = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + ClangASTContext *clang_type_system = llvm::dyn_cast_or_null<ClangASTContext>(type_system); + m_tu_decl_ctx_up = llvm::make_unique<CompilerDeclContext>(type_system, clang_type_system->GetTranslationUnitDecl()); } uint32_t @@ -245,7 +260,25 @@ lldb_private::Type * SymbolFilePDB::ResolveTypeUID(lldb::user_id_t type_uid) { - return nullptr; + auto find_result = m_types.find(type_uid); + if (find_result != m_types.end()) + return find_result->second.get(); + + TypeSystem *type_system = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + ClangASTContext *clang_type_system = llvm::dyn_cast_or_null<ClangASTContext>(type_system); + if (!clang_type_system) + return nullptr; + PDBASTParser *pdb = llvm::dyn_cast<PDBASTParser>(clang_type_system->GetPDBParser()); + if (!pdb) + return nullptr; + + auto pdb_type = m_session_up->getSymbolById(type_uid); + if (pdb_type == nullptr) + return nullptr; + + lldb::TypeSP result = pdb->CreateLLDBTypeFromPDBType(*pdb_type); + m_types.insert(std::make_pair(type_uid, result)); + return result.get(); } bool @@ -264,13 +297,15 @@ lldb_private::CompilerDeclContext SymbolFilePDB::GetDeclContextForUID(lldb::user_id_t uid) { - return lldb_private::CompilerDeclContext(); + // PDB always uses the translation unit decl context for everything. We can improve this later + // but it's not easy because PDB doesn't provide a high enough level of type fidelity in this area. + return *m_tu_decl_ctx_up; } lldb_private::CompilerDeclContext SymbolFilePDB::GetDeclContextContainingUID(lldb::user_id_t uid) { - return lldb_private::CompilerDeclContext(); + return *m_tu_decl_ctx_up; } void @@ -376,14 +411,121 @@ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files, lldb_private::TypeMap &types) { - return uint32_t(); + if (!append) + types.Clear(); + if (!name) + return 0; + + searched_symbol_files.clear(); + searched_symbol_files.insert(this); + + std::string name_str = name.AsCString(); + + // If this might be a regex, we have to return EVERY symbol and process them one by one, which is going + // to destroy performance on large PDB files. So try really hard not to use a regex match. + if (name_str.find_first_of("[]?*.-+\\") != std::string::npos) + FindTypesByRegex(name_str, max_matches, types); + else + FindTypesByName(name_str, max_matches, types); + return types.GetSize(); +} + +void +SymbolFilePDB::FindTypesByRegex(const std::string ®ex, uint32_t max_matches, lldb_private::TypeMap &types) +{ + // When searching by regex, we need to go out of our way to limit the search space as much as possible, since + // the way this is implemented is by searching EVERYTHING in the PDB and manually doing a regex compare. PDB + // library isn't optimized for regex searches or searches across multiple symbol types at the same time, so the + // best we can do is to search enums, then typedefs, then classes one by one, and do a regex compare against all + // of them. + llvm::PDB_SymType tags_to_search[] = {llvm::PDB_SymType::Enum, llvm::PDB_SymType::Typedef, llvm::PDB_SymType::UDT}; + auto global = m_session_up->getGlobalScope(); + std::unique_ptr<llvm::IPDBEnumSymbols> results; + + std::regex re(regex); + + uint32_t matches = 0; + + for (auto tag : tags_to_search) + { + results = global->findAllChildren(tag); + while (auto result = results->getNext()) + { + if (max_matches > 0 && matches >= max_matches) + break; + + std::string type_name; + if (auto enum_type = llvm::dyn_cast<llvm::PDBSymbolTypeEnum>(result.get())) + type_name = enum_type->getName(); + else if (auto typedef_type = llvm::dyn_cast<llvm::PDBSymbolTypeTypedef>(result.get())) + type_name = typedef_type->getName(); + else if (auto class_type = llvm::dyn_cast<llvm::PDBSymbolTypeUDT>(result.get())) + type_name = class_type->getName(); + else + { + // We're only looking for types that have names. Skip symbols, as well as + // unnamed types such as arrays, pointers, etc. + continue; + } + + if (!std::regex_match(type_name, re)) + continue; + + // This should cause the type to get cached and stored in the `m_types` lookup. + if (!ResolveTypeUID(result->getSymIndexId())) + continue; + + auto iter = m_types.find(result->getSymIndexId()); + if (iter == m_types.end()) + continue; + types.Insert(iter->second); + ++matches; + } + } +} + +void +SymbolFilePDB::FindTypesByName(const std::string &name, uint32_t max_matches, lldb_private::TypeMap &types) +{ + auto global = m_session_up->getGlobalScope(); + std::unique_ptr<llvm::IPDBEnumSymbols> results; + results = global->findChildren(llvm::PDB_SymType::None, name.c_str(), llvm::PDB_NameSearchFlags::NS_Default); + + uint32_t matches = 0; + + while (auto result = results->getNext()) + { + if (max_matches > 0 && matches >= max_matches) + break; + switch (result->getSymTag()) + { + case llvm::PDB_SymType::Enum: + case llvm::PDB_SymType::UDT: + case llvm::PDB_SymType::Typedef: + break; + default: + // We're only looking for types that have names. Skip symbols, as well as + // unnamed types such as arrays, pointers, etc. + continue; + } + + // This should cause the type to get cached and stored in the `m_types` lookup. + if (!ResolveTypeUID(result->getSymIndexId())) + continue; + + auto iter = m_types.find(result->getSymIndexId()); + if (iter == m_types.end()) + continue; + types.Insert(iter->second); + ++matches; + } } size_t -SymbolFilePDB::FindTypes(const std::vector<lldb_private::CompilerContext> &context, bool append, +SymbolFilePDB::FindTypes(const std::vector<lldb_private::CompilerContext> &contexts, bool append, lldb_private::TypeMap &types) { - return size_t(); + return 0; } lldb_private::TypeList * @@ -428,6 +570,18 @@ return 1; } +llvm::IPDBSession & +SymbolFilePDB::GetPDBSession() +{ + return *m_session_up; +} + +const llvm::IPDBSession & +SymbolFilePDB::GetPDBSession() const +{ + return *m_session_up; +} + lldb::CompUnitSP SymbolFilePDB::ParseCompileUnitForSymIndex(uint32_t id) { @@ -470,7 +624,7 @@ // ParseCompileUnitSupportFiles. But the underlying SDK gives us a globally unique // idenfitifier in the namespace of the PDB. So, we have to do a mapping so that we // can hand out indices. - std::unordered_map<uint32_t, uint32_t> index_map; + llvm::DenseMap<uint32_t, uint32_t> index_map; BuildSupportFileIdToSupportFileIndexMap(*cu, index_map); auto line_table = llvm::make_unique<LineTable>(sc.comp_unit); @@ -555,7 +709,7 @@ void SymbolFilePDB::BuildSupportFileIdToSupportFileIndexMap(const llvm::PDBSymbolCompiland &cu, - std::unordered_map<uint32_t, uint32_t> &index_map) const + llvm::DenseMap<uint32_t, uint32_t> &index_map) const { // This is a hack, but we need to convert the source id into an index into the support // files array. We don't want to do path comparisons to avoid basename / full path Index: source/Plugins/SymbolFile/PDB/PDBASTParser.h =================================================================== --- /dev/null +++ source/Plugins/SymbolFile/PDB/PDBASTParser.h @@ -0,0 +1,58 @@ +//===-- PDBASTParser.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H +#define LLDB_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H + +#include "lldb/lldb-forward.h" + +#include "lldb/Symbol/ClangASTImporter.h" + +namespace clang +{ +class CharUnits; +class CXXRecordDecl; +class FieldDecl; +class RecordDecl; +} + +namespace lldb_private +{ +class ClangASTContext; +class CompilerType; +} + +namespace llvm +{ +class PDBSymbol; +class PDBSymbolData; +class PDBSymbolTypeBuiltin; +} + +class PDBASTParser +{ +public: + PDBASTParser(lldb_private::ClangASTContext &ast); + ~PDBASTParser(); + + lldb::TypeSP + CreateLLDBTypeFromPDBType(const llvm::PDBSymbol &type); + +private: + llvm::StringRef + GetBuiltinTypeName(const llvm::PDBSymbolTypeBuiltin &type) const; + + bool + AddEnumValue(lldb_private::CompilerType enum_type, const llvm::PDBSymbolData &data) const; + + lldb_private::ClangASTContext &m_ast; + lldb_private::ClangASTImporter m_ast_importer; +}; + +#endif // SymbolFileDWARF_DWARFASTParserClang_h_ Index: source/Plugins/SymbolFile/PDB/PDBASTParser.cpp =================================================================== --- /dev/null +++ source/Plugins/SymbolFile/PDB/PDBASTParser.cpp @@ -0,0 +1,312 @@ +//===-- PDBASTParser.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PDBASTParser.h" + +#include "clang/AST/CharUnits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangUtil.h" +#include "lldb/Symbol/Declaration.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/TypeSystem.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +namespace +{ +int +TranslateUdtKind(PDB_UdtType pdb_kind) +{ + switch (pdb_kind) + { + case PDB_UdtType::Class: + return clang::TTK_Class; + case PDB_UdtType::Struct: + return clang::TTK_Struct; + case PDB_UdtType::Union: + return clang::TTK_Union; + case PDB_UdtType::Interface: + return clang::TTK_Interface; + } + return clang::TTK_Class; +} + +lldb::Encoding +TranslateBuiltinEncoding(PDB_BuiltinType type) +{ + switch (type) + { + case PDB_BuiltinType::Float: + return lldb::eEncodingIEEE754; + case PDB_BuiltinType::Int: + case PDB_BuiltinType::Long: + case PDB_BuiltinType::Char: + return lldb::eEncodingSint; + case PDB_BuiltinType::Bool: + case PDB_BuiltinType::UInt: + case PDB_BuiltinType::ULong: + case PDB_BuiltinType::HResult: + return lldb::eEncodingUint; + default: + return lldb::eEncodingInvalid; + } +} +} + +PDBASTParser::PDBASTParser(lldb_private::ClangASTContext &ast) : m_ast(ast) +{ +} + +PDBASTParser::~PDBASTParser() +{ +} + +// DebugInfoASTParser interface + +lldb::TypeSP +PDBASTParser::CreateLLDBTypeFromPDBType(const llvm::PDBSymbol &type) +{ + // PDB doesn't maintain enough information to robustly rebuild the entire + // tree, and this is most problematic when it comes to figure out the + // right DeclContext to put a type in. So for now, everything goes in + // the translation unit decl as a fully qualified type. + clang::DeclContext *tu_decl_ctx = m_ast.GetTranslationUnitDecl(); + Declaration decl; + + if (auto udt = llvm::dyn_cast<PDBSymbolTypeUDT>(&type)) + { + AccessType access = lldb::eAccessPublic; + PDB_UdtType udt_kind = udt->getUdtKind(); + + if (udt_kind == PDB_UdtType::Class) + access = lldb::eAccessPrivate; + + CompilerType clang_type = + m_ast.CreateRecordType(tu_decl_ctx, access, udt->getName().c_str(), TranslateUdtKind(udt_kind), + lldb::eLanguageTypeC_plus_plus, nullptr); + + m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true); + + return std::make_shared<Type>(type.getSymIndexId(), m_ast.GetSymbolFile(), ConstString(udt->getName()), + udt->getLength(), nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, decl, + clang_type, Type::eResolveStateForward); + } + else if (auto base = llvm::dyn_cast<PDBSymbolTypeBuiltin>(&type)) + { + uint64_t bytes = base->getLength(); + lldb::Encoding encoding = TranslateBuiltinEncoding(base->getBuiltinType()); + + CompilerType builtin_type = m_ast.GetBuiltinTypeForEncodingAndBitSize(encoding, bytes * 8); + if (!builtin_type.IsValid()) + return nullptr; + + llvm::StringRef name = GetBuiltinTypeName(*base); + return std::make_shared<Type>(type.getSymIndexId(), m_ast.GetSymbolFile(), ConstString(name), bytes, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, builtin_type, + Type::eResolveStateFull); + } + else if (auto enum_type = llvm::dyn_cast<PDBSymbolTypeEnum>(&type)) + { + std::string name = enum_type->getName(); + lldb::Encoding encoding = TranslateBuiltinEncoding(enum_type->getBuiltinType()); + uint64_t bytes = enum_type->getLength(); + CompilerType builtin_type = m_ast.GetBuiltinTypeForEncodingAndBitSize(encoding, bytes * 8); + + CompilerType ast_enum = m_ast.CreateEnumerationType(name.c_str(), tu_decl_ctx, decl, builtin_type); + auto enum_values = enum_type->findAllChildren<PDBSymbolData>(); + while (auto enum_value = enum_values->getNext()) + { + if (enum_value->getDataKind() != PDB_DataKind::Constant) + continue; + AddEnumValue(ast_enum, *enum_value); + } + + return std::make_shared<Type>(type.getSymIndexId(), m_ast.GetSymbolFile(), ConstString(name), bytes, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ast_enum, Type::eResolveStateFull); + } + else if (auto type_def = llvm::dyn_cast<PDBSymbolTypeTypedef>(&type)) + { + Type *target_type = m_ast.GetSymbolFile()->ResolveTypeUID(type_def->getTypeId()); + std::string name = type_def->getName(); + uint64_t bytes = type_def->getLength(); + if (!target_type) + return nullptr; + CompilerType target_ast_type = target_type->GetFullCompilerType(); + CompilerDeclContext target_decl_ctx = m_ast.GetSymbolFile()->GetDeclContextForUID(target_type->GetID()); + CompilerType ast_typedef = m_ast.CreateTypedefType(target_ast_type, name.c_str(), target_decl_ctx); + return std::make_shared<Type>(type_def->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(name), bytes, + nullptr, target_type->GetID(), Type::eEncodingIsTypedefUID, decl, ast_typedef, + Type::eResolveStateFull); + } + else if (auto func_sig = llvm::dyn_cast<PDBSymbolTypeFunctionSig>(&type)) + { + auto arg_enum = func_sig->getArguments(); + uint32_t num_args = arg_enum->getChildCount(); + std::vector<CompilerType> arg_list(num_args); + while (auto arg = arg_enum->getNext()) + { + Type *arg_type = m_ast.GetSymbolFile()->ResolveTypeUID(arg->getSymIndexId()); + // If there's some error looking up one of the dependent types of this function signature, bail. + if (!arg_type) + return nullptr; + CompilerType arg_ast_type = arg_type->GetFullCompilerType(); + arg_list.push_back(arg_ast_type); + } + auto pdb_return_type = func_sig->getReturnType(); + Type *return_type = m_ast.GetSymbolFile()->ResolveTypeUID(pdb_return_type->getSymIndexId()); + // If there's some error looking up one of the dependent types of this function signature, bail. + if (!return_type) + return nullptr; + CompilerType return_ast_type = return_type->GetFullCompilerType(); + uint32_t type_quals = 0; + if (func_sig->isConstType()) + type_quals |= clang::Qualifiers::Const; + if (func_sig->isVolatileType()) + type_quals |= clang::Qualifiers::Volatile; + CompilerType func_sig_ast_type = + m_ast.CreateFunctionType(return_ast_type, &arg_list[0], num_args, false, type_quals); + + return std::make_shared<Type>(func_sig->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(), 0, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, func_sig_ast_type, + Type::eResolveStateFull); + } + else if (auto array_type = llvm::dyn_cast<PDBSymbolTypeArray>(&type)) + { + uint32_t num_elements = array_type->getCount(); + uint32_t element_uid = array_type->getElementType()->getSymIndexId(); + uint32_t bytes = array_type->getLength(); + + Type *element_type = m_ast.GetSymbolFile()->ResolveTypeUID(element_uid); + CompilerType element_ast_type = element_type->GetFullCompilerType(); + CompilerType array_ast_type = m_ast.CreateArrayType(element_ast_type, num_elements, false); + return std::make_shared<Type>(array_type->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(), bytes, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, array_ast_type, + Type::eResolveStateFull); + } + return nullptr; +} + +llvm::StringRef +PDBASTParser::GetBuiltinTypeName(const PDBSymbolTypeBuiltin &type) const +{ + uint64_t bit_length = type.getLength() * 8; + clang::ASTContext *ast = m_ast.getASTContext(); + + switch (type.getBuiltinType()) + { + case PDB_BuiltinType::Float: + if (bit_length == ast->getTypeSize(ast->FloatTy)) + return "float"; + if (bit_length == ast->getTypeSize(ast->DoubleTy)) + return "double"; + if (bit_length == ast->getTypeSize(ast->LongDoubleTy)) + return "long double"; + break; + case PDB_BuiltinType::Int: + if (bit_length == ast->getTypeSize(ast->CharTy)) + return "char"; + if (bit_length == ast->getTypeSize(ast->ShortTy)) + return "short"; + if (bit_length == ast->getTypeSize(ast->IntTy)) + return "int"; + if (bit_length == ast->getTypeSize(ast->LongLongTy)) + return "__int64"; + break; + case PDB_BuiltinType::Long: + if (bit_length == ast->getTypeSize(ast->LongTy)) + return "long"; + if (bit_length == ast->getTypeSize(ast->LongLongTy)) + return "long long"; + break; + case PDB_BuiltinType::Char: + return "char"; + case PDB_BuiltinType::WCharT: + return "wchar_t"; + case PDB_BuiltinType::Bool: + return "bool"; + case PDB_BuiltinType::UInt: + if (bit_length == ast->getTypeSize(ast->UnsignedCharTy)) + return "unsigned char"; + if (bit_length == ast->getTypeSize(ast->UnsignedShortTy)) + return "unsigned short"; + if (bit_length == ast->getTypeSize(ast->UnsignedIntTy)) + return "unsigned int"; + if (bit_length == ast->getTypeSize(ast->UnsignedLongLongTy)) + return "unsigned __int64"; + break; + case PDB_BuiltinType::ULong: + if (bit_length == ast->getTypeSize(ast->UnsignedLongTy)) + return "unsigned long"; + if (bit_length == ast->getTypeSize(ast->UnsignedLongLongTy)) + return "unsigned long long"; + break; + case PDB_BuiltinType::HResult: + return "HRESULT"; + default: + return llvm::StringRef(); + } + return llvm::StringRef(); +} + +bool +PDBASTParser::AddEnumValue(CompilerType enum_type, const llvm::PDBSymbolData &enum_value) const +{ + Declaration decl; + Variant v = enum_value.getValue(); + std::string name = enum_value.getName(); + int64_t raw_value; + switch (v.Type) + { + case PDB_VariantType::Int8: + raw_value = v.Value.Int8; + break; + case PDB_VariantType::Int16: + raw_value = v.Value.Int16; + break; + case PDB_VariantType::Int32: + raw_value = v.Value.Int32; + break; + case PDB_VariantType::Int64: + raw_value = v.Value.Int64; + break; + case PDB_VariantType::UInt8: + raw_value = v.Value.UInt8; + break; + case PDB_VariantType::UInt16: + raw_value = v.Value.UInt16; + break; + case PDB_VariantType::UInt32: + raw_value = v.Value.UInt32; + break; + case PDB_VariantType::UInt64: + raw_value = v.Value.UInt64; + break; + default: + return false; + } + CompilerType underlying_type = m_ast.GetEnumerationIntegerType(enum_type.GetOpaqueQualType()); + uint32_t byte_size = m_ast.getASTContext()->getTypeSize(ClangUtil::GetQualType(underlying_type)); + return m_ast.AddEnumerationValueToEnumerationType(enum_type.GetOpaqueQualType(), underlying_type, decl, + name.c_str(), raw_value, byte_size * 8); +} Index: source/Plugins/SymbolFile/PDB/CMakeLists.txt =================================================================== --- source/Plugins/SymbolFile/PDB/CMakeLists.txt +++ source/Plugins/SymbolFile/PDB/CMakeLists.txt @@ -2,5 +2,6 @@ DebugInfoPDB) add_lldb_library(lldbPluginSymbolFilePDB + PDBASTParser.cpp SymbolFilePDB.cpp ) Index: include/lldb/Symbol/ClangASTContext.h =================================================================== --- include/lldb/Symbol/ClangASTContext.h +++ include/lldb/Symbol/ClangASTContext.h @@ -37,6 +37,7 @@ #include "lldb/lldb-enumerations.h" class DWARFASTParserClang; +class PDBASTParser; namespace lldb_private { @@ -524,6 +525,8 @@ //------------------------------------------------------------------ DWARFASTParser * GetDWARFParser() override; + PDBASTParser * + GetPDBParser(); //------------------------------------------------------------------ // ClangASTContext callbacks for external source lookups. @@ -696,7 +699,13 @@ bool IsPolymorphicClass (lldb::opaque_compiler_type_t type) override; - + + static bool + IsClassType(lldb::opaque_compiler_type_t type); + + static bool + IsEnumType(lldb::opaque_compiler_type_t type); + bool IsPossibleDynamicType(lldb::opaque_compiler_type_t type, CompilerType *target_type, // Can pass nullptr @@ -1189,6 +1198,7 @@ std::unique_ptr<clang::SelectorTable> m_selector_table_ap; std::unique_ptr<clang::Builtin::Context> m_builtins_ap; std::unique_ptr<DWARFASTParserClang> m_dwarf_ast_parser_ap; + std::unique_ptr<PDBASTParser> m_pdb_ast_parser_ap; std::unique_ptr<ClangASTSource> m_scratch_ast_source_ap; std::unique_ptr<clang::MangleContext> m_mangle_ctx_ap; CompleteTagDeclCallback m_callback_tag_decl;
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits