https://github.com/zhyty updated https://github.com/llvm/llvm-project/pull/66035:
>From d0538bc86ec02819ed9a24d06a2e7fc355447f4b Mon Sep 17 00:00:00 2001 From: Tom Yang <toy...@fb.com> Date: Mon, 11 Sep 2023 17:17:13 -0700 Subject: [PATCH] Add `target modules dump separate-debug-info` Summary: Add a new command ``` target modules dump separate-debug-info [-j] [<filename> [<filename> [...]]] ``` or ``` image dump separate-debug-info [-j] [<filename> [<filename> [...]]] ``` (since `image` is an alias for `target modules`). This lists the separate debug info files and their current status (loaded or not loaded) for the specified modules. This diff implements this command for mach-O files with OSO and ELF files with dwo. Example dwo: ``` (lldb) image dump separate-debug-info Symbol file: /home/toyang/workspace/dwo-scratch/a.out Type: "dwo" Dwo ID Dwo Path ------------------ ----------------------------------------- 0x9a429da5abb6faae /home/toyang/workspace/dwo-scratch/a-main.dwo 0xbcc129959e76ff33 /home/toyang/workspace/dwo-scratch/a-foo.dwo (lldb) image dump separate-debug-info -j [ { "separate-debug-info-files": [ { "comp_dir": "/home/toyang/workspace/dwo-scratch", "dwo_id": 11115620165179865774, "dwo_name": "a-main.dwo", "loaded": true, "resolved_dwo_path": "/home/toyang/workspace/dwo-scratch/a-main.dwo" }, { "comp_dir": "/home/toyang/workspace/dwo-scratch", "dwo_id": 13601198072221073203, "dwo_name": "a-foo.dwo", "loaded": true, "resolved_dwo_path": "/home/toyang/workspace/dwo-scratch/a-foo.dwo" } ], "symfile": "/home/toyang/workspace/dwo-scratch/a.out", "type": "dwo" } ] ``` Example dwo with missing dwo: ``` (lldb) image dump separate-debug-info Symbol file: /home/toyang/workspace/dwo-scratch/a.out Type: "dwo" Dwo ID Dwo Path ------------------ ----------------------------------------- 0x9a429da5abb6faae error: unable to locate .dwo debug file "/home/toyang/workspace/dwo-scratch/a-main.dwo" for skeleton DIE 0x0000000000000014 0xbcc129959e76ff33 error: unable to locate .dwo debug file "/home/toyang/workspace/dwo-scratch/a-foo.dwo" for skeleton DIE 0x000000000000003c (lldb) image dump separate-debug-info -j [ { "separate-debug-info-files": [ { "comp_dir": "/home/toyang/workspace/dwo-scratch", "dwo_id": 11115620165179865774, "dwo_name": "a-main.dwo", "error": "unable to locate .dwo debug file \"/home/toyang/workspace/dwo-scratch/a-main.dwo\" for skeleton DIE 0x0000000000000014", "loaded": false }, { "comp_dir": "/home/toyang/workspace/dwo-scratch", "dwo_id": 13601198072221073203, "dwo_name": "a-foo.dwo", "error": "unable to locate .dwo debug file \"/home/toyang/workspace/dwo-scratch/a-foo.dwo\" for skeleton DIE 0x000000000000003c", "loaded": false } ], "symfile": "/home/toyang/workspace/dwo-scratch/a.out", "type": "dwo" } ] ``` Example output with dwp: ``` (lldb) image dump separate-debug-info Symbol file: /home/toyang/workspace/dwo-scratch/a.out Type: "dwo" Dwo ID Dwo Path ------------------ ----------------------------------------- 0x9a429da5abb6faae /home/toyang/workspace/dwo-scratch/a.out.dwp(a-main.dwo) 0xbcc129959e76ff33 /home/toyang/workspace/dwo-scratch/a.out.dwp(a-foo.dwo) (lldb) image dump separate-debug-info -j [ { "separate-debug-info-files": [ { "comp_dir": "/home/toyang/workspace/dwo-scratch", "dwo_id": 11115620165179865774, "dwo_name": "a-main.dwo", "loaded": true, "resolved_dwo_path": "/home/toyang/workspace/dwo-scratch/a.out.dwp" }, { "comp_dir": "/home/toyang/workspace/dwo-scratch", "dwo_id": 13601198072221073203, "dwo_name": "a-foo.dwo", "loaded": true, "resolved_dwo_path": "/home/toyang/workspace/dwo-scratch/a.out.dwp" } ], "symfile": "/home/toyang/workspace/dwo-scratch/a.out", "type": "dwo" } ] ``` Example oso on my Mac (after manipulating the mod times with `touch`): ``` (lldb) image dump separate-debug-info Symbol file: /Users/toyang/workspace/scratch/a.out Type: "oso" Mod Time Oso Path ------------------ --------------------- 0x0000000064e64868 /Users/toyang/workspace/scratch/foo.a(foo.o) 0x0000000064e64868 /Users/toyang/workspace/scratch/foo.a(main.o) (lldb) image dump separate-debug-info -j [ { "separate-debug-info-files": [ { "loaded": true, "oso_mod_time": 1692813416, "oso_path": "/Users/toyang/workspace/scratch/foo.a(foo.o)", "so_file": "/Users/toyang/workspace/scratch/foo.cpp" }, { "loaded": true, "oso_mod_time": 1692813416, "oso_path": "/Users/toyang/workspace/scratch/foo.a(main.o)", "so_file": "/Users/toyang/workspace/scratch/main.cpp" } ], "symfile": "/Users/toyang/workspace/scratch/a.out", "type": "oso" } ] ``` Test Plan: Tested on Mac OS and Linux. ``` lldb-dotest -p TestDumpDwo lldb-dotest -p TestDumpOso ``` Reviewers: Subscribers: Tasks: Tags: --- lldb/include/lldb/Symbol/SymbolFile.h | 19 ++ lldb/source/Commands/CommandObjectTarget.cpp | 276 +++++++++++++++++- lldb/source/Commands/Options.td | 5 + .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 67 ++++- .../SymbolFile/DWARF/SymbolFileDWARF.h | 5 + .../DWARF/SymbolFileDWARFDebugMap.cpp | 33 ++- .../DWARF/SymbolFileDWARFDebugMap.h | 6 + lldb/source/Symbol/SymbolFile.cpp | 16 + .../dump-separate-debug-info/dwo/Makefile | 4 + .../dwo/TestDumpDwo.py | 68 +++++ .../dump-separate-debug-info/dwo/foo.cpp | 3 + .../target/dump-separate-debug-info/dwo/foo.h | 6 + .../dump-separate-debug-info/dwo/main.cpp | 3 + .../dump-separate-debug-info/oso/Makefile | 3 + .../oso/TestDumpOso.py | 68 +++++ .../dump-separate-debug-info/oso/foo.cpp | 3 + .../target/dump-separate-debug-info/oso/foo.h | 6 + .../dump-separate-debug-info/oso/main.cpp | 3 + 18 files changed, 581 insertions(+), 13 deletions(-) create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/dwo/Makefile create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.cpp create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.h create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/dwo/main.cpp create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/oso/Makefile create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/oso/TestDumpOso.py create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.cpp create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.h create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/oso/main.cpp diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index 8de752816cf94ee..5362ea77c10984b 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -22,6 +22,7 @@ #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/Target/Statistics.h" +#include "lldb/Utility/StructuredData.h" #include "lldb/Utility/XcodeSDK.h" #include "lldb/lldb-private.h" #include "llvm/ADT/DenseSet.h" @@ -377,6 +378,16 @@ class SymbolFile : public PluginInterface { virtual void Dump(Stream &s) = 0; + /// Return true if separate debug info files are supported and this function + /// succeeded, false otherwise. + /// + /// \param[out] d + /// If this function succeeded, then this will be a dictionary that + /// contains the keys "type", "symfile", and "separate-debug-info-files". + /// "type" can be used to assume the structure of each object in + /// "separate-debug-info-files". + bool ListSeparateDebugInfoFiles(StructuredData::Dictionary &d); + /// Metrics gathering functions /// Return the size in bytes of all debug information in the symbol file. @@ -459,6 +470,14 @@ class SymbolFile : public PluginInterface { virtual void GetCompileOptions( std::unordered_map<lldb::CompUnitSP, lldb_private::Args> &args) {} + /// If separate debug info files are supported and this function succeeded, + /// return some string representing the type of debug info. E.g. "dwo" or + /// "oso". Otherwise, this returns None. + virtual std::optional<ConstString> + GetSeparateDebugInfoFiles(StructuredData::Array &array) { + return std::nullopt; + }; + private: SymbolFile(const SymbolFile &) = delete; const SymbolFile &operator=(const SymbolFile &) = delete; diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index 33330ef0926d61f..6549f0dc2acb1e0 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -52,6 +52,7 @@ #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/State.h" +#include "lldb/Utility/StructuredData.h" #include "lldb/Utility/Timer.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-private-enumerations.h" @@ -61,6 +62,7 @@ #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendActions.h" #include "llvm/ADT/ScopeExit.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatAdapters.h" @@ -1462,6 +1464,87 @@ static bool DumpModuleSymbolFile(Stream &strm, Module *module) { return false; } +static bool GetSeparateDebugInfoList(StructuredData::Array &list, + Module *module) { + if (module) { + if (SymbolFile *symbol_file = module->GetSymbolFile(true)) { + StructuredData::Dictionary d; + if (symbol_file->ListSeparateDebugInfoFiles(d)) { + list.AddItem( + std::make_shared<StructuredData::Dictionary>(std::move(d))); + return true; + } + } + } + return false; +} + +static void DumpDwoFilesTable(Stream &strm, + StructuredData::Array &dwo_listings) { + strm.PutCString("Dwo ID Dwo Path"); + strm.EOL(); + strm.PutCString( + "------------------ -----------------------------------------"); + strm.EOL(); + dwo_listings.ForEach([&strm](StructuredData::Object *dwo) { + StructuredData::Dictionary *dict = dwo->GetAsDictionary(); + if (!dict) + return false; + + uint64_t dwo_id; + if (dict->GetValueForKeyAsInteger("dwo_id", dwo_id)) + strm.Printf("0x%16.16" PRIx64 " ", dwo_id); + else + strm.Printf("0x???????????????? "); + + llvm::StringRef error; + if (dict->GetValueForKeyAsString("error", error)) + strm << "error: " << error; + else { + llvm::StringRef resolved_dwo_path; + if (dict->GetValueForKeyAsString("resolved_dwo_path", + resolved_dwo_path)) { + strm << resolved_dwo_path; + if (resolved_dwo_path.ends_with(".dwp")) { + llvm::StringRef dwo_name; + if (dict->GetValueForKeyAsString("dwo_name", dwo_name)) + strm << "(" << dwo_name << ")"; + } + } + } + strm.EOL(); + return true; + }); +} + +static void DumpOsoFilesTable(Stream &strm, + StructuredData::Array &oso_listings) { + strm.PutCString("Mod Time Oso Path"); + strm.EOL(); + strm.PutCString("------------------ ---------------------"); + strm.EOL(); + oso_listings.ForEach([&strm](StructuredData::Object *oso) { + StructuredData::Dictionary *dict = oso->GetAsDictionary(); + if (!dict) + return false; + + uint32_t oso_mod_time; + if (dict->GetValueForKeyAsInteger("oso_mod_time", oso_mod_time)) + strm.Printf("0x%16.16" PRIx32 " ", oso_mod_time); + + llvm::StringRef error; + if (dict->GetValueForKeyAsString("error", error)) + strm << "error: " << error; + else { + llvm::StringRef oso_path; + if (dict->GetValueForKeyAsString("oso_path", oso_path)) + strm << oso_path; + } + strm.EOL(); + return true; + }); +} + static void DumpAddress(ExecutionContextScope *exe_scope, const Address &so_addr, bool verbose, bool all_ranges, Stream &strm) { @@ -2005,7 +2088,7 @@ class CommandObjectTargetModulesDumpSymtab result.GetOutputStream().EOL(); result.GetOutputStream().EOL(); } - if (INTERRUPT_REQUESTED(GetDebugger(), + if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted in dump all symtabs with {0} " "of {1} dumped.", num_dumped, num_modules)) break; @@ -2035,8 +2118,8 @@ class CommandObjectTargetModulesDumpSymtab result.GetOutputStream().EOL(); result.GetOutputStream().EOL(); } - if (INTERRUPT_REQUESTED(GetDebugger(), - "Interrupted in dump symtab list with {0} of {1} dumped.", + if (INTERRUPT_REQUESTED(GetDebugger(), + "Interrupted in dump symtab list with {0} of {1} dumped.", num_dumped, num_matches)) break; @@ -2099,7 +2182,7 @@ class CommandObjectTargetModulesDumpSections result.GetOutputStream().Format("Dumping sections for {0} modules.\n", num_modules); for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { - if (INTERRUPT_REQUESTED(GetDebugger(), + if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted in dump all sections with {0} of {1} dumped", image_idx, num_modules)) break; @@ -2120,7 +2203,7 @@ class CommandObjectTargetModulesDumpSections FindModulesByName(target, arg_cstr, module_list, true); if (num_matches > 0) { for (size_t i = 0; i < num_matches; ++i) { - if (INTERRUPT_REQUESTED(GetDebugger(), + if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted in dump section list with {0} of {1} dumped.", i, num_matches)) break; @@ -2265,7 +2348,7 @@ class CommandObjectTargetModulesDumpClangAST } for (size_t i = 0; i < num_matches; ++i) { - if (INTERRUPT_REQUESTED(GetDebugger(), + if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted in dump clang ast list with {0} of {1} dumped.", i, num_matches)) break; @@ -2406,9 +2489,9 @@ class CommandObjectTargetModulesDumpLineTable if (num_modules > 0) { uint32_t num_dumped = 0; for (ModuleSP module_sp : target_modules.ModulesNoLocking()) { - if (INTERRUPT_REQUESTED(GetDebugger(), + if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted in dump all line tables with " - "{0} of {1} dumped", num_dumped, + "{0} of {1} dumped", num_dumped, num_modules)) break; @@ -2462,6 +2545,176 @@ class CommandObjectTargetModulesDumpLineTable CommandOptions m_options; }; +#pragma mark CommandObjectTargetModulesDumpSeparateDebugInfoFiles +#define LLDB_OPTIONS_target_modules_dump_separate_debug_info +#include "CommandOptions.inc" + +// Image debug separate debug info dumping command + +class CommandObjectTargetModulesDumpSeparateDebugInfoFiles + : public CommandObjectTargetModulesModuleAutoComplete { +public: + CommandObjectTargetModulesDumpSeparateDebugInfoFiles( + CommandInterpreter &interpreter) + : CommandObjectTargetModulesModuleAutoComplete( + interpreter, "target modules dump separate-debug-info", + "List the separate debug info symbol files for one or more target " + "modules.", + nullptr, eCommandRequiresTarget) {} + + ~CommandObjectTargetModulesDumpSeparateDebugInfoFiles() override = default; + + Options *GetOptions() override { return &m_options; } + + class CommandOptions : public Options { + public: + CommandOptions() = default; + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 'j': + m_json.SetCurrentValue(true); + m_json.SetOptionWasSet(); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_json.Clear(); + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::ArrayRef(g_target_modules_dump_separate_debug_info_options); + } + + OptionValueBoolean m_json = false; + }; + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + Target &target = GetSelectedTarget(); + uint32_t num_dumped = 0; + + uint32_t addr_byte_size = target.GetArchitecture().GetAddressByteSize(); + result.GetOutputStream().SetAddressByteSize(addr_byte_size); + result.GetErrorStream().SetAddressByteSize(addr_byte_size); + + StructuredData::Array separate_debug_info_lists_by_module; + if (command.GetArgumentCount() == 0) { + // Dump all sections for all modules images + const ModuleList &target_modules = target.GetImages(); + std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex()); + const size_t num_modules = target_modules.GetSize(); + if (num_modules == 0) { + result.AppendError("the target has no associated executable images"); + return false; + } + for (ModuleSP module_sp : target_modules.ModulesNoLocking()) { + if (INTERRUPT_REQUESTED( + GetDebugger(), + "Interrupted in dumping all " + "separate debug info with {0} of {1} modules dumped", + num_dumped, num_modules)) + break; + + if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module, + module_sp.get())) + num_dumped++; + } + } else { + // Dump specified images (by basename or fullpath) + const char *arg_cstr; + for (int arg_idx = 0; + (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; + ++arg_idx) { + ModuleList module_list; + const size_t num_matches = + FindModulesByName(&target, arg_cstr, module_list, true); + if (num_matches > 0) { + for (size_t i = 0; i < num_matches; ++i) { + if (INTERRUPT_REQUESTED(GetDebugger(), + "Interrupted dumping {0} " + "of {1} requested modules", + i, num_matches)) + break; + Module *module = module_list.GetModulePointerAtIndex(i); + if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module, + module)) + num_dumped++; + } + } else + result.AppendWarningWithFormat( + "Unable to find an image that matches '%s'.\n", arg_cstr); + } + } + + if (num_dumped > 0) { + Stream &strm = result.GetOutputStream(); + if (m_options.m_json) { + separate_debug_info_lists_by_module.Dump(strm, + /*pretty_print=*/true); + } else { + // List the debug info files in human readable form. + separate_debug_info_lists_by_module.ForEach( + [&result, &strm](StructuredData::Object *obj) { + if (!obj) { + return false; + } + + // Each item in `separate_debug_info_lists_by_module` should be a + // valid structured data dictionary. + StructuredData::Dictionary *separate_debug_info_list = + obj->GetAsDictionary(); + if (!separate_debug_info_list) { + return false; + } + + llvm::StringRef type; + llvm::StringRef symfile; + StructuredData::Array *files; + assert(separate_debug_info_list->GetValueForKeyAsString("type", + type)); + assert(separate_debug_info_list->GetValueForKeyAsString("symfile", + symfile)); + assert(separate_debug_info_list->GetValueForKeyAsArray( + "separate-debug-info-files", files)); + + strm << "Symbol file: " << symfile; + strm.EOL(); + strm << "Type: \"" << type << "\""; + strm.EOL(); + if (type == "dwo") { + DumpDwoFilesTable(strm, *files); + } else if (type == "oso") { + DumpOsoFilesTable(strm, *files); + } else { + result.AppendWarningWithFormat( + "Found unsupported debug info type '%s'.\n", + type.str().c_str()); + } + return true; + }); + } + result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + result.AppendError("no matching executable images found"); + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + #pragma mark CommandObjectTargetModulesDump // Dump multi-word command for target modules @@ -2475,7 +2728,8 @@ class CommandObjectTargetModulesDump : public CommandObjectMultiword { "Commands for dumping information about one or more target " "modules.", "target modules dump " - "[objfile|symtab|sections|ast|symfile|line-table|pcm-info] " + "[objfile|symtab|sections|ast|symfile|line-table|pcm-info|separate-" + "debug-info] " "[<file1> <file2> ...]") { LoadSubCommand("objfile", CommandObjectSP( @@ -2499,6 +2753,10 @@ class CommandObjectTargetModulesDump : public CommandObjectMultiword { "pcm-info", CommandObjectSP( new CommandObjectTargetModulesDumpClangPCMInfo(interpreter))); + LoadSubCommand("separate-debug-info", + CommandObjectSP( + new CommandObjectTargetModulesDumpSeparateDebugInfoFiles( + interpreter))); } ~CommandObjectTargetModulesDump() override = default; diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index 04830b8b990efae..ce00d81db3c5efb 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -8,6 +8,11 @@ let Command = "target modules dump symtab" in { Desc<"Do not demangle symbol names before showing them.">; } +let Command = "target modules dump separate debug info" in { + def tm_json : Option<"json", "j">, Group<1>, + Desc<"Output the details in JSON format.">; +} + let Command = "help" in { def help_hide_aliases : Option<"hide-aliases", "a">, Desc<"Hide aliases in the command list.">; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 04c729e333a9854..6d89ea82844dc8a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -10,6 +10,7 @@ #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/Format.h" #include "llvm/Support/Threading.h" #include "lldb/Core/Module.h" @@ -24,6 +25,7 @@ #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Scalar.h" #include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StructuredData.h" #include "lldb/Utility/Timer.h" #include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" @@ -1740,11 +1742,10 @@ SymbolFileDWARF::GetDwoSymbolFileForCompileUnit( // it. Or it's absolute. found = FileSystem::Instance().Exists(dwo_file); + const char *comp_dir = + cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_comp_dir, nullptr); if (!found) { // It could be a relative path that also uses DW_AT_COMP_DIR. - const char *comp_dir = - cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_comp_dir, nullptr); - if (comp_dir) { dwo_file.SetFile(comp_dir, FileSpec::Style::native); if (!dwo_file.IsRelative()) { @@ -4214,6 +4215,66 @@ void SymbolFileDWARF::DumpClangAST(Stream &s) { clang->Dump(s.AsRawOstream()); } +std::optional<ConstString> +SymbolFileDWARF::GetSeparateDebugInfoFiles(StructuredData::Array &array) { + DWARFDebugInfo &info = DebugInfo(); + const size_t num_cus = info.GetNumUnits(); + for (size_t cu_idx = 0; cu_idx < num_cus; cu_idx++) { + DWARFUnit *unit = info.GetUnitAtIndex(cu_idx); + DWARFCompileUnit *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(unit); + if (dwarf_cu == nullptr) { + continue; + } + + // Check if this is a DWO unit by checking if it has a DWO ID. + // NOTE: it seems that `DWARFUnit::IsDWOUnit` is always false? + if (!dwarf_cu->GetDWOId().has_value()) + continue; + + StructuredData::DictionarySP dwo_data = + std::make_shared<StructuredData::Dictionary>(); + const uint64_t dwo_id = dwarf_cu->GetDWOId().value(); + dwo_data->AddIntegerItem("dwo_id", dwo_id); + + if (const DWARFBaseDIE die = dwarf_cu->GetUnitDIEOnly()) { + const char *dwo_name = GetDWOName(*dwarf_cu, *die.GetDIE()); + if (dwo_name) { + dwo_data->AddStringItem("dwo_name", dwo_name); + } else { + dwo_data->AddStringItem("error", "missing dwo name"); + } + + const char *comp_dir = die.GetDIE()->GetAttributeValueAsString( + dwarf_cu, DW_AT_comp_dir, nullptr); + if (comp_dir) { + dwo_data->AddStringItem("comp_dir", comp_dir); + } + } else { + dwo_data->AddStringItem( + "error", + llvm::formatv("unable to get unit DIE for DWARFUnit at {0:x}", + dwarf_cu->GetOffset()) + .str()); + } + + // If we have a DWO symbol file, that means we were able to successfully + // load it. + SymbolFile *dwo_symfile = dwarf_cu->GetDwoSymbolFile(); + if (dwo_symfile) { + dwo_data->AddStringItem( + "resolved_dwo_path", + dwo_symfile->GetObjectFile()->GetFileSpec().GetPath()); + } else { + dwo_data->AddStringItem("error", + dwarf_cu->GetDwoError().AsCString("unknown")); + } + dwo_data->AddBooleanItem("loaded", dwo_symfile != nullptr); + array.AddItem(dwo_data); + } + + return ConstString("dwo"); +} + SymbolFileDWARFDebugMap *SymbolFileDWARF::GetDebugMapSymfile() { if (m_debug_map_symfile == nullptr) { lldb::ModuleSP module_sp(m_debug_map_module_wp.lock()); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 191a5abcf265abd..9461c991975dac1 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -30,6 +30,7 @@ #include "lldb/Utility/ConstString.h" #include "lldb/Utility/Flags.h" #include "lldb/Utility/RangeMap.h" +#include "lldb/Utility/StructuredData.h" #include "lldb/lldb-private.h" #include "DWARFContext.h" @@ -282,6 +283,10 @@ class SymbolFileDWARF : public lldb_private::SymbolFileCommon { void DumpClangAST(lldb_private::Stream &s) override; + /// Retrieve external dwo file details. + std::optional<lldb_private::ConstString> GetSeparateDebugInfoFiles( + lldb_private::StructuredData::Array &array) override; + lldb_private::DWARFContext &GetDWARFContext() { return m_context; } const std::shared_ptr<SymbolFileDWARFDwo> &GetDwpSymbolFile(); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index eadedd32e1a4aaf..4be99b527c72265 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -18,8 +18,9 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Utility/RangeMap.h" #include "lldb/Utility/RegularExpression.h" -#include "lldb/Utility/Timer.h" #include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/Utility/Timer.h" //#define DEBUG_OSO_DMAP // DO NOT CHECKIN WITH THIS NOT COMMENTED OUT @@ -1271,6 +1272,36 @@ void SymbolFileDWARFDebugMap::DumpClangAST(Stream &s) { }); } +std::optional<ConstString> SymbolFileDWARFDebugMap::GetSeparateDebugInfoFiles( + lldb_private::StructuredData::Array &array) { + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) { + const auto &info = m_compile_unit_infos[cu_idx]; + StructuredData::DictionarySP oso_data = + std::make_shared<StructuredData::Dictionary>(); + oso_data->AddStringItem("so_file", info.so_file.GetPath()); + oso_data->AddStringItem("oso_path", info.oso_path); + oso_data->AddIntegerItem("oso_mod_time", + (uint32_t)llvm::sys::toTimeT(info.oso_mod_time)); + + bool loaded_successfully = false; + if (GetModuleByOSOIndex(cu_idx)) { + // If we have a valid pointer to the module, we successfully + // loaded the oso if there are no load errors. + if (!info.oso_load_error.Fail()) { + loaded_successfully = true; + } + } + if (!loaded_successfully) { + oso_data->AddStringItem("error", info.oso_load_error.AsCString()); + } + oso_data->AddBooleanItem("loaded", loaded_successfully); + array.AddItem(oso_data); + } + + return ConstString("oso"); +} + lldb::CompUnitSP SymbolFileDWARFDebugMap::GetCompileUnit(SymbolFileDWARF *oso_dwarf, DWARFCompileUnit &dwarf_cu) { if (oso_dwarf) { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h index 881fd4c45ff05a0..ee4350a8feb7eca 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -11,6 +11,7 @@ #include "DIERef.h" #include "lldb/Symbol/SymbolFile.h" +#include "lldb/Utility/ConstString.h" #include "lldb/Utility/RangeMap.h" #include "llvm/Support/Chrono.h" #include <bitset> @@ -19,6 +20,7 @@ #include <vector> #include "UniqueDWARFASTType.h" +#include "lldb/Utility/StructuredData.h" class SymbolFileDWARF; class DWARFCompileUnit; @@ -148,6 +150,10 @@ class SymbolFileDWARFDebugMap : public lldb_private::SymbolFileCommon { void DumpClangAST(lldb_private::Stream &s) override; + /// Get the external oso file details. + std::optional<lldb_private::ConstString> GetSeparateDebugInfoFiles( + lldb_private::StructuredData::Array &array) override; + // PluginInterface protocol llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } diff --git a/lldb/source/Symbol/SymbolFile.cpp b/lldb/source/Symbol/SymbolFile.cpp index b271efd07bfe36f..ba4d5c684dce8d3 100644 --- a/lldb/source/Symbol/SymbolFile.cpp +++ b/lldb/source/Symbol/SymbolFile.cpp @@ -18,6 +18,7 @@ #include "lldb/Symbol/VariableList.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StructuredData.h" #include "lldb/lldb-private.h" #include <future> @@ -162,6 +163,21 @@ void SymbolFile::AssertModuleLock() { SymbolFile::RegisterInfoResolver::~RegisterInfoResolver() = default; +bool SymbolFile::ListSeparateDebugInfoFiles(StructuredData::Dictionary &d) { + StructuredData::Array array; + std::optional<ConstString> debug_info_type = GetSeparateDebugInfoFiles(array); + if (!debug_info_type) { + return false; + } + + d.AddStringItem("type", debug_info_type.value()); + d.AddStringItem("symfile", GetMainObjectFile()->GetFileSpec().GetPath()); + d.AddItem("separate-debug-info-files", + std::make_shared<StructuredData::Array>(std::move(array))); + + return true; +} + Symtab *SymbolFileCommon::GetSymtab() { std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); // Fetch the symtab from the main object file. diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/dwo/Makefile b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/Makefile new file mode 100644 index 000000000000000..3b6d788b2b0130a --- /dev/null +++ b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/Makefile @@ -0,0 +1,4 @@ +CXX_SOURCES := main.cpp foo.cpp +CFLAGS_EXTRAS := -gsplit-dwarf + +include Makefile.rules diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py new file mode 100644 index 000000000000000..04da2e0386f52dc --- /dev/null +++ b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py @@ -0,0 +1,68 @@ +""" +Test 'target modules dump separate-debug-info' for dwo files. +""" + +import os +import json + +from lldbsuite.test import lldbtest, lldbutil +from lldbsuite.test.decorators import * + + +class TestDumpDWO(lldbtest.TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def get_dwos_from_command_output(self): + """Returns a dictionary of `symfile` -> {`dwo_name` -> dwo_info object}.""" + result = {} + output = json.loads(self.res.GetOutput()) + for symfile_entry in output: + dwo_dict = {} + for dwo_entry in symfile_entry["separate-debug-info-files"]: + dwo_dict[dwo_entry["dwo_name"]] = dwo_entry + result[symfile_entry["symfile"]] = dwo_dict + return result + + @skipIfRemote + @skipIfDarwin + def test_shows_dwo_loaded(self): + self.build() + exe = self.getBuildArtifact("a.out") + main_dwo = self.getBuildArtifact("main.dwo") + foo_dwo = self.getBuildArtifact("foo.dwo") + + # Make sure dwo files exist + self.assertTrue(os.path.exists(main_dwo), f'Make sure "{main_dwo}" file exists') + self.assertTrue(os.path.exists(foo_dwo), f'Make sure "{foo_dwo}" file exists') + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, lldbtest.VALID_TARGET) + + self.runCmd("target modules dump separate-debug-info --json") + + # Check the output + output = self.get_dwos_from_command_output() + self.assertTrue(output[exe]["main.dwo"]["loaded"]) + self.assertTrue(output[exe]["foo.dwo"]["loaded"]) + + @skipIfRemote + @skipIfDarwin + def test_shows_dwo_not_loaded(self): + self.build() + exe = self.getBuildArtifact("a.out") + main_dwo = self.getBuildArtifact("main.dwo") + foo_dwo = self.getBuildArtifact("foo.dwo") + + # REMOVE the dwo files + os.unlink(main_dwo) + os.unlink(foo_dwo) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, lldbtest.VALID_TARGET) + + self.runCmd("target modules dump separate-debug-info --json") + + # Check the output + output = self.get_dwos_from_command_output() + self.assertFalse(output[exe]["main.dwo"]["loaded"]) + self.assertFalse(output[exe]["foo.dwo"]["loaded"]) diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.cpp b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.cpp new file mode 100644 index 000000000000000..28e2b6e768df4e7 --- /dev/null +++ b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.cpp @@ -0,0 +1,3 @@ +#include "foo.h" + +int foo() { return 1; } diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.h b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.h new file mode 100644 index 000000000000000..4ec598ad513eb91 --- /dev/null +++ b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.h @@ -0,0 +1,6 @@ +#ifndef FOO_H +#define FOO_H + +int foo(); + +#endif diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/dwo/main.cpp b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/main.cpp new file mode 100644 index 000000000000000..8087e682432798b --- /dev/null +++ b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/main.cpp @@ -0,0 +1,3 @@ +#include "foo.h" + +int main() { return foo(); } diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/oso/Makefile b/lldb/test/API/commands/target/dump-separate-debug-info/oso/Makefile new file mode 100644 index 000000000000000..7df22699c57d573 --- /dev/null +++ b/lldb/test/API/commands/target/dump-separate-debug-info/oso/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp foo.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/oso/TestDumpOso.py b/lldb/test/API/commands/target/dump-separate-debug-info/oso/TestDumpOso.py new file mode 100644 index 000000000000000..4c55fd0e02470a2 --- /dev/null +++ b/lldb/test/API/commands/target/dump-separate-debug-info/oso/TestDumpOso.py @@ -0,0 +1,68 @@ +""" +Test 'target modules dump separate-debug-info' for oso files. +""" + +import os +import json + +from lldbsuite.test import lldbtest, lldbutil +from lldbsuite.test.decorators import * + + +class TestDumpOso(lldbtest.TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def get_osos_from_command_output(self): + """Returns a dictionary of `symfile` -> {`OSO_PATH` -> oso_info object}.""" + result = {} + output = json.loads(self.res.GetOutput()) + for symfile_entry in output: + oso_dict = {} + for oso_entry in symfile_entry["separate-debug-info-files"]: + oso_dict[oso_entry["oso_path"]] = oso_entry + result[symfile_entry["symfile"]] = oso_dict + return result + + @skipIfRemote + @skipUnlessDarwin + def test_shows_oso_loaded(self): + self.build(debug_info="dwarf") + exe = self.getBuildArtifact("a.out") + main_o = self.getBuildArtifact("main.o") + foo_o = self.getBuildArtifact("foo.o") + + # Make sure o files exist + self.assertTrue(os.path.exists(main_o), f'Make sure "{main_o}" file exists') + self.assertTrue(os.path.exists(foo_o), f'Make sure "{foo_o}" file exists') + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, lldbtest.VALID_TARGET) + + self.runCmd("target modules dump separate-debug-info --json") + + # Check the output + osos = self.get_osos_from_command_output() + self.assertTrue(osos[exe][main_o]["loaded"]) + self.assertTrue(osos[exe][foo_o]["loaded"]) + + @skipIfRemote + @skipUnlessDarwin + def test_shows_oso_not_loaded(self): + self.build(debug_info="dwarf") + exe = self.getBuildArtifact("a.out") + main_o = self.getBuildArtifact("main.o") + foo_o = self.getBuildArtifact("foo.o") + + # REMOVE the o files + os.unlink(main_o) + os.unlink(foo_o) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, lldbtest.VALID_TARGET) + + self.runCmd("target modules dump separate-debug-info --json") + + # Check the output + osos = self.get_osos_from_command_output() + self.assertFalse(osos[exe][main_o]["loaded"]) + self.assertFalse(osos[exe][foo_o]["loaded"]) diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.cpp b/lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.cpp new file mode 100644 index 000000000000000..28e2b6e768df4e7 --- /dev/null +++ b/lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.cpp @@ -0,0 +1,3 @@ +#include "foo.h" + +int foo() { return 1; } diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.h b/lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.h new file mode 100644 index 000000000000000..4ec598ad513eb91 --- /dev/null +++ b/lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.h @@ -0,0 +1,6 @@ +#ifndef FOO_H +#define FOO_H + +int foo(); + +#endif diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/oso/main.cpp b/lldb/test/API/commands/target/dump-separate-debug-info/oso/main.cpp new file mode 100644 index 000000000000000..8087e682432798b --- /dev/null +++ b/lldb/test/API/commands/target/dump-separate-debug-info/oso/main.cpp @@ -0,0 +1,3 @@ +#include "foo.h" + +int main() { return foo(); } _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits