Index: lldb/trunk/lit/SymbolFile/PDB/Inputs/VariablesTest.cpp =================================================================== --- lldb/trunk/lit/SymbolFile/PDB/Inputs/VariablesTest.cpp +++ lldb/trunk/lit/SymbolFile/PDB/Inputs/VariablesTest.cpp @@ -0,0 +1,50 @@ +typedef int IntTypedef; +IntTypedef g_IntVar; // Testing globals. + +typedef enum Enum { // Testing constants. + RED, + GREEN, + BLUE +} EnumTypedef; +EnumTypedef g_EnumVar; // Testing members. + +// FIXME: `sg_IntVar` appears both in global scope's children and compiland's +// children but with different symbol's id. +static int sg_IntVar = -1; // Testing file statics. + +// FIXME: `g_Const` appears both in global scope's children and compiland's +// children but with different symbol's id. +const int g_Const = 0x88; // Testing constant data. +const int *g_pConst = &g_Const; // Avoid optimizing the const away + +thread_local int g_tls = 0; // Testing thread-local storage. + +class Class { + static int m_StaticClassMember; +public: + explicit Class(int a) {} + void Func() {} +}; +int Class::m_StaticClassMember = 10; // Testing static class members. +Class ClassVar(1); + +int f(int var_arg1, int var_arg2) { // Testing parameters. + long same_name_var = -1; + return 1; +} + +int same_name_var = 100; +int main() { + int same_name_var = 0; // Testing locals. + const char local_const = 0x1; + + // FIXME: 'local_CString` is not found through compiland's children. + const char local_CString[] = "abc"; // Testing constant string. + const char *local_pCString = local_CString; // Avoid optimizing the const away + + int a = 10; + a++; + + ClassVar.Func(); + return 0; +} Index: lldb/trunk/lit/SymbolFile/PDB/variables.test =================================================================== --- lldb/trunk/lit/SymbolFile/PDB/variables.test +++ lldb/trunk/lit/SymbolFile/PDB/variables.test @@ -0,0 +1,58 @@ +REQUIRES: windows +RUN: clang-cl /Z7 /c %S/Inputs/VariablesTest.cpp /o %T/VariablesTest.cpp.obj +RUN: link %T/VariablesTest.cpp.obj /DEBUG /nodefaultlib /ENTRY:main /OUT:%T/VariablesTest.cpp.exe +RUN: lldb-test symbols %T/VariablesTest.cpp.exe | FileCheck %s + +CHECK: Module [[MOD:.*]] +CHECK: SymbolVendor ([[MOD]]) +CHECK: CompileUnit{{.*}}, language = "c++", file = '{{.*}}\VariablesTest.cpp' +CHECK-DAG: Variable{{.*}}, name = "g_IntVar" +CHECK-SAME: scope = global, external +CHECK-DAG: Variable{{.*}}, name = "m_StaticClassMember" +CHECK-SAME: scope = global, external +CHECK-DAG: Variable{{.*}}, name = "g_pConst" +CHECK-SAME: scope = global, external +CHECK-DAG: Variable{{.*}}, name = "same_name_var" +CHECK-SAME: scope = global, external +CHECK-DAG: Variable{{.*}}, name = "g_EnumVar" +CHECK-SAME: scope = global, external +CHECK-DAG: Variable{{.*}}, name = "g_tls" +CHECK-SAME: scope = thread local, external +CHECK-DAG: Variable{{.*}}, name = "ClassVar" +CHECK-SAME: scope = global, external +CHECK-DAG: Variable{{.*}}, name = "g_Const" +CHECK-SAME: scope = ??? (2) + +CHECK-DAG: Function{[[FID1:.*]]}, mangled = ?f@@YAHHH@Z +CHECK-NEXT: Block{[[FID1]]} +CHECK-DAG: Variable{{.*}}, name = "var_arg1" +CHECK-SAME: scope = parameter +CHECK-DAG: Variable{{.*}}, name = "var_arg2" +CHECK-SAME: scope = parameter +CHECK-DAG: Variable{{.*}}, name = "same_name_var" +CHECK-SAME: scope = local + +CHECK-DAG: Function{[[FID2:.*]]}, mangled = main +CHECK-NEXT: Block{[[FID2]]} +CHECK-DAG: Variable{{.*}}, name = "same_name_var" +CHECK-SAME: scope = local +CHECK-DAG: Variable{{.*}}, name = "local_const" +CHECK-SAME: scope = local +CHECK-DAG: Variable{{.*}}, name = "local_pCString" +CHECK-SAME: scope = local +CHECK-DAG: Variable{{.*}}, name = "a" +CHECK-SAME: scope = local + +CHECK-DAG: Function{[[FID3:.*]]}, mangled = ??0Class@@QEAA@H@Z +CHECK-NEXT: Block{[[FID3]]} +CHECK-DAG: Variable{{.*}}, name = "this" +CHECK-SAME: scope = parameter +CHECK-SAME: artificial +CHECK-DAG: Variable{{.*}}, name = "a" +CHECK-SAME: scope = parameter + +CHECK-DAG: Function{[[FID4:.*]]}, mangled = ?Func@Class@@QEAAXXZ +CHECK-NEXT: Block{[[FID4]]} +CHECK-DAG: Variable{{.*}}, name = "this" +CHECK-SAME: scope = parameter +CHECK-SAME: artificial \ No newline at end of file Index: lldb/trunk/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h =================================================================== --- lldb/trunk/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h +++ lldb/trunk/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h @@ -12,6 +12,7 @@ #include "lldb/Core/UniqueCStringMap.h" #include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/VariableList.h" #include "lldb/Utility/UserID.h" #include "llvm/ADT/DenseMap.h" @@ -181,6 +182,16 @@ void FindTypesByName(const std::string &name, uint32_t max_matches, lldb_private::TypeMap &types); + std::string GetMangledForPDBData(const llvm::pdb::PDBSymbolData &pdb_data); + + lldb::VariableSP + ParseVariableForPDBData(const lldb_private::SymbolContext &sc, + const llvm::pdb::PDBSymbolData &pdb_data); + + size_t ParseVariables(const lldb_private::SymbolContext &sc, + const llvm::pdb::PDBSymbol &pdb_data, + lldb_private::VariableList *variable_list = nullptr); + lldb::CompUnitSP GetCompileUnitContainsAddress(const lldb_private::Address &so_addr); @@ -217,6 +228,7 @@ llvm::DenseMap m_comp_units; llvm::DenseMap m_types; + llvm::DenseMap m_variables; std::vector m_builtin_types; std::unique_ptr m_session_up; Index: lldb/trunk/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp =================================================================== --- lldb/trunk/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp +++ lldb/trunk/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp @@ -21,6 +21,7 @@ #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/TypeMap.h" +#include "lldb/Symbol/Variable.h" #include "lldb/Utility/RegularExpression.h" #include "llvm/DebugInfo/PDB/GenericError.h" @@ -483,8 +484,51 @@ size_t SymbolFilePDB::ParseVariablesForContext(const lldb_private::SymbolContext &sc) { - // TODO: Implement this - return size_t(); + if (!sc.comp_unit) + return 0; + + size_t num_added = 0; + if (sc.function) { + auto pdb_func = m_session_up->getConcreteSymbolById( + sc.function->GetID()); + if (!pdb_func) + return 0; + + num_added += ParseVariables(sc, *pdb_func); + sc.function->GetBlock(false).SetDidParseVariables(true, true); + } else if (sc.comp_unit) { + auto compiland = GetPDBCompilandByUID(sc.comp_unit->GetID()); + if (!compiland) + return 0; + + if (sc.comp_unit->GetVariableList(false)) + return 0; + + auto results = m_global_scope_up->findAllChildren(); + if (results && results->getChildCount()) { + while (auto result = results->getNext()) { + auto cu_id = result->getCompilandId(); + // FIXME: We are not able to determine variable's compile unit. + if (cu_id == 0) + continue; + + if (cu_id == sc.comp_unit->GetID()) + num_added += ParseVariables(sc, *result); + } + } + + // FIXME: A `file static` or `global constant` variable appears both in + // compiland's children and global scope's children with unexpectedly + // different symbol's Id making it ambiguous. + + // FIXME: 'local constant', for example, const char var[] = "abc", declared + // in a function scope, can't be found in PDB. + + // Parse variables in this compiland. + num_added += ParseVariables(sc, *compiland); + } + + return num_added; } lldb_private::Type *SymbolFilePDB::ResolveTypeUID(lldb::user_id_t type_uid) { @@ -714,18 +758,270 @@ return sc_list.GetSize() - old_size; } +std::string SymbolFilePDB::GetMangledForPDBData(const PDBSymbolData &pdb_data) { + std::string decorated_name; + auto vm_addr = pdb_data.getVirtualAddress(); + if (vm_addr != LLDB_INVALID_ADDRESS && vm_addr) { + auto result_up = + m_global_scope_up->findAllChildren(PDB_SymType::PublicSymbol); + if (result_up) { + while (auto symbol_up = result_up->getNext()) { + if (symbol_up->getRawSymbol().getVirtualAddress() == vm_addr) { + decorated_name = symbol_up->getRawSymbol().getName(); + break; + } + } + } + } + if (!decorated_name.empty()) + return decorated_name; + + return std::string(); +} + +VariableSP SymbolFilePDB::ParseVariableForPDBData( + const lldb_private::SymbolContext &sc, + const llvm::pdb::PDBSymbolData &pdb_data) { + VariableSP var_sp; + uint32_t var_uid = pdb_data.getSymIndexId(); + auto result = m_variables.find(var_uid); + if (result != m_variables.end()) + return result->second; + + ValueType scope = eValueTypeInvalid; + bool is_static_member = false; + bool is_external = false; + bool is_artificial = false; + + switch (pdb_data.getDataKind()) { + case PDB_DataKind::Global: + scope = eValueTypeVariableGlobal; + is_external = true; + break; + case PDB_DataKind::Local: + scope = eValueTypeVariableLocal; + break; + case PDB_DataKind::FileStatic: + scope = eValueTypeVariableStatic; + break; + case PDB_DataKind::StaticMember: + is_static_member = true; + scope = eValueTypeVariableStatic; + break; + case PDB_DataKind::Member: + scope = eValueTypeVariableStatic; + break; + case PDB_DataKind::Param: + scope = eValueTypeVariableArgument; + break; + case PDB_DataKind::Constant: + scope = eValueTypeConstResult; + break; + default: + break; + } + + switch (pdb_data.getLocationType()) { + case PDB_LocType::TLS: + scope = eValueTypeVariableThreadLocal; + break; + case PDB_LocType::RegRel: { + // It is a `this` pointer. + if (pdb_data.getDataKind() == PDB_DataKind::ObjectPtr) { + scope = eValueTypeVariableArgument; + is_artificial = true; + } + } break; + default: + break; + } + + Declaration decl; + if (!is_artificial && !pdb_data.isCompilerGenerated()) { + if (auto lines = pdb_data.getLineNumbers()) { + if (auto first_line = lines->getNext()) { + uint32_t src_file_id = first_line->getSourceFileId(); + auto src_file = m_session_up->getSourceFileById(src_file_id); + if (src_file) { + FileSpec spec(src_file->getFileName(), /*resolve_path*/ false); + decl.SetFile(spec); + decl.SetColumn(first_line->getColumnNumber()); + decl.SetLine(first_line->getLineNumber()); + } + } + } + } + + Variable::RangeList ranges; + SymbolContextScope *context_scope = sc.comp_unit; + if (scope == eValueTypeVariableLocal) { + if (sc.function) { + context_scope = sc.function->GetBlock(true).FindBlockByID( + pdb_data.getClassParentId()); + if (context_scope == nullptr) + context_scope = sc.function; + } + } + + SymbolFileTypeSP type_sp = + std::make_shared(*this, pdb_data.getTypeId()); + + auto var_name = pdb_data.getName(); + auto mangled = GetMangledForPDBData(pdb_data); + auto mangled_cstr = mangled.empty() ? nullptr : mangled.c_str(); + + DWARFExpression location(nullptr); + + var_sp = std::make_shared( + var_uid, var_name.c_str(), mangled_cstr, type_sp, scope, context_scope, + ranges, &decl, location, is_external, is_artificial, is_static_member); + + m_variables.insert(std::make_pair(var_uid, var_sp)); + return var_sp; +} + +size_t +SymbolFilePDB::ParseVariables(const lldb_private::SymbolContext &sc, + const llvm::pdb::PDBSymbol &pdb_symbol, + lldb_private::VariableList *variable_list) { + size_t num_added = 0; + + if (auto pdb_data = llvm::dyn_cast(&pdb_symbol)) { + VariableListSP local_variable_list_sp; + + auto result = m_variables.find(pdb_data->getSymIndexId()); + if (result != m_variables.end()) { + if (variable_list) + variable_list->AddVariableIfUnique(result->second); + } else { + // Prepare right VariableList for this variable. + if (auto lexical_parent = pdb_data->getLexicalParent()) { + switch (lexical_parent->getSymTag()) { + case PDB_SymType::Exe: + assert(sc.comp_unit); + LLVM_FALLTHROUGH; + case PDB_SymType::Compiland: { + if (sc.comp_unit) { + local_variable_list_sp = sc.comp_unit->GetVariableList(false); + if (!local_variable_list_sp) { + local_variable_list_sp = std::make_shared(); + sc.comp_unit->SetVariableList(local_variable_list_sp); + } + } + } break; + case PDB_SymType::Block: + case PDB_SymType::Function: { + if (sc.function) { + Block *block = sc.function->GetBlock(true).FindBlockByID( + lexical_parent->getSymIndexId()); + if (block) { + local_variable_list_sp = block->GetBlockVariableList(false); + if (!local_variable_list_sp) { + local_variable_list_sp = std::make_shared(); + block->SetVariableList(local_variable_list_sp); + } + } + } + } break; + default: + break; + } + } + + if (local_variable_list_sp) { + if (auto var_sp = ParseVariableForPDBData(sc, *pdb_data)) { + local_variable_list_sp->AddVariableIfUnique(var_sp); + if (variable_list) + variable_list->AddVariableIfUnique(var_sp); + ++num_added; + } + } + } + } + + if (auto results = pdb_symbol.findAllChildren()) { + while (auto result = results->getNext()) + num_added += ParseVariables(sc, *result, variable_list); + } + + return num_added; +} + uint32_t SymbolFilePDB::FindGlobalVariables( const lldb_private::ConstString &name, const lldb_private::CompilerDeclContext *parent_decl_ctx, bool append, uint32_t max_matches, lldb_private::VariableList &variables) { - return uint32_t(); + if (!append) + variables.Clear(); + if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) + return 0; + if (name.IsEmpty()) + return 0; + + auto results = + m_global_scope_up->findChildren(PDB_SymType::Data, name.GetStringRef(), + PDB_NameSearchFlags::NS_CaseSensitive); + if (!results) + return 0; + + uint32_t matches = 0; + size_t old_size = variables.GetSize(); + while (auto result = results->getNext()) { + auto pdb_data = llvm::dyn_cast(result.get()); + if (max_matches > 0 && matches >= max_matches) + break; + + SymbolContext sc; + sc.module_sp = m_obj_file->GetModule(); + lldbassert(sc.module_sp.get()); + + sc.comp_unit = ParseCompileUnitForUID(pdb_data->getCompilandId()).get(); + // FIXME: We are not able to determine the compile unit. + if (sc.comp_unit == nullptr) + continue; + + ParseVariables(sc, *pdb_data, &variables); + matches = variables.GetSize() - old_size; + } + + return matches; } uint32_t SymbolFilePDB::FindGlobalVariables(const lldb_private::RegularExpression ®ex, bool append, uint32_t max_matches, lldb_private::VariableList &variables) { - return uint32_t(); + if (!regex.IsValid()) + return 0; + auto results = m_global_scope_up->findAllChildren(); + if (!results) + return 0; + + uint32_t matches = 0; + size_t old_size = variables.GetSize(); + while (auto pdb_data = results->getNext()) { + if (max_matches > 0 && matches >= max_matches) + break; + + auto var_name = pdb_data->getName(); + if (var_name.empty()) + continue; + if (!regex.Execute(var_name)) + continue; + SymbolContext sc; + sc.module_sp = m_obj_file->GetModule(); + lldbassert(sc.module_sp.get()); + + sc.comp_unit = ParseCompileUnitForUID(pdb_data->getCompilandId()).get(); + // FIXME: We are not able to determine the compile unit. + if (sc.comp_unit == nullptr) + continue; + + ParseVariables(sc, *pdb_data, &variables); + matches = variables.GetSize() - old_size; + } + + return matches; } bool SymbolFilePDB::ResolveFunction(const llvm::pdb::PDBSymbolFunc &pdb_func,