Index: packages/Python/lldbsuite/test/help/TestHelp.py =================================================================== --- packages/Python/lldbsuite/test/help/TestHelp.py +++ packages/Python/lldbsuite/test/help/TestHelp.py @@ -133,13 +133,19 @@ 'symtab']) @no_debug_info_test - def test_help_image_du_line_should_work(self): - """Command 'help image du line' is not ambiguous and should work.""" + def test_help_image_du_line_table_should_work(self): + """Command 'help image du line-t' is not ambiguous and should work.""" # 'image' is an alias for 'target modules'. - self.expect("help image du line", + self.expect("help image du line-t", substrs = ['Dump the line table for one or more compilation units']) @no_debug_info_test + def test_help_target_modules_du_line_entries_should_work(self): + """Command 'help target modules du line-e' is not ambiguous and should work.""" + self.expect("help target modules du line-e", + substrs = ['Dump the line entries for one or more files']) + + @no_debug_info_test def test_help_target_variable_syntax(self): """Command 'help target variable' should display ...""" self.expect("help target variable", Index: packages/Python/lldbsuite/test/tools/lldb-mi/symbol/TestMiSymbol.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-mi/symbol/TestMiSymbol.py +++ packages/Python/lldbsuite/test/tools/lldb-mi/symbol/TestMiSymbol.py @@ -48,12 +48,19 @@ eline = line_number('symbol_list_lines_inline_test2.cpp', '// END_gfunc2') self.runCmd("-symbol-list-lines symbol_list_lines_inline_test2.cpp") self.expect("\^done,lines=\[\{pc=\"0x[0-9a-f]+\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"3\d\"\})*,\{pc=\"0x[0-9a-f]+\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"3\d\"\})*\]" % (sline, eline)) - ##FIXME: This doesn't work for symbol_list_lines_inline_test.cpp due to clang bug llvm.org/pr24716 + ##FIXME: This doesn't work for symbol_list_lines_inline_test.cpp due to clang bug llvm.org/pr24716 (fixed in newer versions of clang) ##sline = line_number('symbol_list_lines_inline_test.cpp', '// FUNC_gfunc') ##eline = line_number('symbol_list_lines_inline_test.cpp', '// STRUCT_s') ##self.runCmd("-symbol-list-lines symbol_list_lines_inline_test.cpp") ##self.expect("\^done,lines=\[\{pc=\"0x[0-9a-f]+\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"3\d\"\})*,\{pc=\"0x[0-9a-f]+\",line=\"%d\"\}\]" % (sline, eline)) + # Test that -symbol-list-lines works on header files by checking the first + # and last line, and making sure the other lines are under 29. + sline = line_number('symbol_list_lines_inline_test.h', '// FUNC_ifunc') + eline = line_number('symbol_list_lines_inline_test.h', '// FUNC_mfunc') + self.runCmd("-symbol-list-lines symbol_list_lines_inline_test.h") + self.expect("\^done,lines=\[\{pc=\"0x[0-9a-f]+\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"\d\"\})*(,\{pc=\"0x[0-9a-f]+\",line=\"1\d\"\})*,\{pc=\"0x[0-9a-f]+\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"2\d\"\})*\]" % (sline, eline)) + # Test that -symbol-list-lines fails when file doesn't exist self.runCmd("-symbol-list-lines unknown_file") self.expect("\^error,message=\"warning: No source filenames matched 'unknown_file'\. error: no source filenames matched any command arguments \"") Index: packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test.h =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test.h +++ packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test.h @@ -2,7 +2,7 @@ { inline int ifunc(int i) -{ +{ // FUNC_ifunc return i; } struct S @@ -17,7 +17,7 @@ int mfunc() { - return a + b; + return a + b; // FUNC_mfunc } }; extern S s; Index: source/Commands/CommandObjectTarget.cpp =================================================================== --- source/Commands/CommandObjectTarget.cpp +++ source/Commands/CommandObjectTarget.cpp @@ -1524,6 +1524,91 @@ return num_matches; } +static uint32_t +DumpFileLineEntries (CommandInterpreter &interpreter, + Stream &strm, + Module *module, + const FileSpec &file_spec, + bool load_addresses) +{ + uint32_t num_matches = 0; + if (module) + { + // Look through all the compilation units (CUs) for ones that contain + // lines of code from this source file. + assert(file_spec.GetFilename().AsCString()); + bool has_path = (file_spec.GetDirectory().AsCString() != 0); + int ncus = module->GetNumCompileUnits(); + for (int i = 0; i < ncus; i++) + { + // Look for a matching source file in this CU. + CompUnitSP cu_sp(module->GetCompileUnitAtIndex(i)); + if (!cu_sp) + continue; + CompileUnit *cu = cu_sp.get(); + const FileSpecList &cu_file_list = cu->GetSupportFiles(); + size_t file_idx = cu_file_list.FindFileIndex(0, file_spec, has_path); + if (file_idx == UINT32_MAX) + // No such file in this CU. + continue; + + // Update the file to how it appears in the CU. + const FileSpec &cu_file_spec = cu_file_list.GetFileSpecAtIndex(file_idx); + + // Dump all the line entries for the file in the CU. + const ConstString &file_spec_name = file_spec.GetFilename(); + const ConstString &module_file_name = module->GetFileSpec().GetFilename(); + uint32_t line = 0; + bool cu_header_printed = false; + while (true) + { + LineEntry line_entry; + + // Find the lowest index of a line entry with a line equal to + // or higher than 'line'. + uint32_t start_idx = 0; + start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec, + /*exact=*/false, &line_entry); + if (start_idx == UINT32_MAX) + // No more line entries for our file in this CU. + break; + + // Loop through to find any other entries for this line, dumping each. + line = line_entry.line; + do + { + assert(lldb_private::FileSpec::Equal(cu_file_spec, line_entry.file, has_path)); + if (cu_header_printed == false) + { + if (num_matches > 0) + strm << "\n\n"; + strm << "Lines for file " << file_spec_name + << " in compilation unit " << cu->GetFilename() + << " in `" << module_file_name << "\n"; + cu_header_printed = true; + } + line_entry.GetDescription(&strm, + lldb::eDescriptionLevelBrief, + cu, + interpreter.GetExecutionContext().GetTargetPtr(), + /*show_address_only=*/false); + strm << "\n"; + num_matches++; + + // Anymore after this one? + start_idx++; + start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec, + /*exact=*/true, &line_entry); + } while (start_idx != UINT32_MAX); + + // Try the next higher line, starting over at start_idx 0. + line++; + } + } + } + return num_matches; +} + static void DumpFullpath (Stream &strm, const FileSpec *file_spec_ptr, uint32_t width) { @@ -2622,6 +2707,80 @@ } }; +class CommandObjectTargetModulesDumpLineEntries : public CommandObjectTargetModulesSourceFileAutoComplete +{ +public: + CommandObjectTargetModulesDumpLineEntries (CommandInterpreter &interpreter) : + CommandObjectTargetModulesSourceFileAutoComplete (interpreter, + "target modules dump line-entries", + "Dump the line entries for one or more files.", + NULL, + eCommandRequiresTarget) + { + } + + ~CommandObjectTargetModulesDumpLineEntries () override + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) override + { + Target *target = m_exe_ctx.GetTargetPtr(); + uint32_t total_num_dumped = 0; + + uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + result.GetOutputStream().SetAddressByteSize(addr_byte_size); + result.GetErrorStream().SetAddressByteSize(addr_byte_size); + + if (command.GetArgumentCount() == 0) + { + result.AppendErrorWithFormat ("\nSyntax: %s\n", m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + } + else + { + // Dump specified images (by basename or fullpath) + const char *arg_cstr; + for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx) + { + FileSpec file_spec(arg_cstr, false); + + const ModuleList &target_modules = target->GetImages(); + Mutex::Locker modules_locker(target_modules.GetMutex()); + const size_t num_modules = target_modules.GetSize(); + if (num_modules > 0) + { + uint32_t num_dumped = 0; + for (uint32_t i = 0; i 0) + result.SetStatus (eReturnStatusSuccessFinishResult); + else + { + result.AppendError ("no source filenames matched any command arguments"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + #pragma mark CommandObjectTargetModulesDump @@ -2645,6 +2804,7 @@ LoadSubCommand ("sections", CommandObjectSP (new CommandObjectTargetModulesDumpSections (interpreter))); LoadSubCommand ("symfile", CommandObjectSP (new CommandObjectTargetModulesDumpSymfile (interpreter))); LoadSubCommand ("line-table", CommandObjectSP (new CommandObjectTargetModulesDumpLineTable (interpreter))); + LoadSubCommand ("line-entries", CommandObjectSP (new CommandObjectTargetModulesDumpLineEntries (interpreter))); } ~CommandObjectTargetModulesDump() override Index: tools/lldb-mi/MICmdCmdSymbol.cpp =================================================================== --- tools/lldb-mi/MICmdCmdSymbol.cpp +++ tools/lldb-mi/MICmdCmdSymbol.cpp @@ -82,11 +82,7 @@ CMICMDBASE_GETOPTION(pArgFile, File, m_constStrArgNameFile); const CMIUtilString &strFilePath(pArgFile->GetValue()); - // FIXME: this won't work for header files! To try and use existing - // commands to get this to work for header files would be too slow. - // Instead, this code should be rewritten to use APIs and/or support - // should be added to lldb which would work for header files. - const CMIUtilString strCmd(CMIUtilString::Format("target modules dump line-table \"%s\"", strFilePath.AddSlashes().c_str())); + const CMIUtilString strCmd(CMIUtilString::Format("target modules dump line-entries \"%s\"", strFilePath.AddSlashes().c_str())); CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); const lldb::ReturnStatus rtn = rSessionInfo.GetDebugger().GetCommandInterpreter().HandleCommand(strCmd.c_str(), m_lldbResult); @@ -110,10 +106,10 @@ { // Match LineEntry using regex. static MIUtilParse::CRegexParser g_lineentry_header_regex( - "^ *Line table for (.+) in `(.+)$"); - // ^1=file ^2=module + "^ *Lines for file (.+) in compilation unit (.+) in `(.+)$"); + // ^1=file ^2=cu ^3=module - MIUtilParse::CRegexParser::Match match(3); + MIUtilParse::CRegexParser::Match match(4); const bool ok = g_lineentry_header_regex.Execute(input, match); if (ok) @@ -146,12 +142,12 @@ // Match LineEntry using regex. static MIUtilParse::CRegexParser g_lineentry_nocol_regex( - "^ *(0x[0-9a-fA-F]+): (.+):([0-9]+)$"); + "^ *\\[(0x[0-9a-fA-F]+)-(0x[0-9a-fA-F]+)\\): (.+):([0-9]+)$"); static MIUtilParse::CRegexParser g_lineentry_col_regex( - "^ *(0x[0-9a-fA-F]+): (.+):([0-9]+):[0-9]+$"); - // ^1=addr ^2=f ^3=line ^4=:col(opt) + "^ *\\[(0x[0-9a-fA-F]+)-(0x[0-9a-fA-F]+)\\): (.+):([0-9]+):[0-9]+$"); + // ^1=start ^2=end ^3=f ^4=line ^5=:col(opt) - MIUtilParse::CRegexParser::Match match(5); + MIUtilParse::CRegexParser::Match match(6); // First try matching the LineEntry with the column, // then try without the column. @@ -160,8 +156,8 @@ if (ok) { addr = match.GetMatchAtIndex(1); - file = match.GetMatchAtIndex(2); - line = match.GetMatchAtIndex(3); + file = match.GetMatchAtIndex(3); + line = match.GetMatchAtIndex(4); } return ok; } @@ -222,10 +218,6 @@ if (!ParseLLDBLineAddressEntry(rLine.c_str(), strAddr, strFile, strLine)) continue; - // Skip entries which don't match the desired source. - if (strWantFile != strFile) - continue; - const CMICmnMIValueConst miValueConst(strAddr); const CMICmnMIValueResult miValueResult("pc", miValueConst); CMICmnMIValueTuple miValueTuple(miValueResult);