Index: lit/tools/lldb-mi/data/data-info-line.test =================================================================== --- /dev/null +++ lit/tools/lldb-mi/data/data-info-line.test @@ -0,0 +1,36 @@ +# XFAIL: windows +# -> llvm.org/pr24452 +# +# RUN: %cc -o %t %p/inputs/data-info-line.c -g +# RUN: %lldbmi %t < %s | FileCheck %s + +# Test lldb-mi -data-info-line command. + +# Check that we have a valid target created via '%lldbmi %t'. +# CHECK: ^done + +-break-insert main +# CHECK: ^done,bkpt={number="1" + +-exec-run +# CHECK: ^running +# CHECK: *stopped,reason="breakpoint-hit" + +-data-info-line *0x0 +# Test that -data-info-line fails when invalid address is specified. +# CHECK: ^error,msg="Command 'data-info-line'. Error: The LineEntry is absent or has an unknown format." + +-data-info-line unknown_file:1 +# Test that -data-info-line fails when file is unknown. +# CHECK: ^error,msg="Command 'data-info-line'. Error: The LineEntry is absent or has an unknown format." + +-data-info-line data-info-line.c:bad_line +# Test that -data-info-line fails when line has invalid format. +# CHECK: ^error,msg="Command 'data-info-line'. Error: The LineEntry is absent or has an unknown format." + +-data-info-line data-info-line.c:0 +# Test that -data-info-line fails when invalid line is specified. +# CHECK: ^error,msg="Command 'data-info-line'. Error: The LineEntry is absent or has an unknown format." + +-data-info-line data-info-line.c:2 +# CHECK: ^done,start="0x{{[0-9a-f]+}}",end="0x{{[0-9a-f]+}}",file="{{.*}}data-info-line.c",line="{{[0-9]+}}" Index: lit/tools/lldb-mi/data/inputs/data-info-line.c =================================================================== --- /dev/null +++ lit/tools/lldb-mi/data/inputs/data-info-line.c @@ -0,0 +1,4 @@ +int main(void) { + int x = 0; + return 12345 + x; +} Index: lit/tools/lldb-mi/data/lit.local.cfg =================================================================== --- /dev/null +++ lit/tools/lldb-mi/data/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes = ['.test'] Index: packages/Python/lldbsuite/test/tools/lldb-mi/data/TestMiData.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-mi/data/TestMiData.py +++ packages/Python/lldbsuite/test/tools/lldb-mi/data/TestMiData.py @@ -331,62 +331,6 @@ self.expect( "\^done,register-values=\[{number=\"0\",value=\"0x[0-9a-f]+\"}\]") - @skipIfRemote # We do not currently support remote debugging via the MI. - @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows - @skipIfDarwin # pexpect is known to be unreliable on Darwin - @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races - def test_lldbmi_data_info_line(self): - """Test that 'lldb-mi --interpreter' works for -data-info-line.""" - - self.spawnLldbMi(args=None) - - # Load executable - self.runCmd("-file-exec-and-symbols %s" % self.myexe) - self.expect("\^done") - - # Run to main - self.runCmd("-break-insert -f main") - self.expect("\^done,bkpt={number=\"1\"") - self.runCmd("-exec-run") - self.expect("\^running") - self.expect("\*stopped,reason=\"breakpoint-hit\"") - - # Get the address of main and its line - self.runCmd("-data-evaluate-expression main") - self.expect( - "\^done,value=\"0x[0-9a-f]+ \(a.out`main at main.cpp:[0-9]+\)\"") - addr = int(self.child.after.split("\"")[1].split(" ")[0], 16) - line = line_number('main.cpp', '// FUNC_main') - - # Test that -data-info-line works for address - self.runCmd("-data-info-line *%#x" % addr) - self.expect( - "\^done,start=\"0x0*%x\",end=\"0x[0-9a-f]+\",file=\".+?main.cpp\",line=\"%d\"" % - (addr, line)) - - # Test that -data-info-line works for file:line - self.runCmd("-data-info-line main.cpp:%d" % line) - self.expect( - "\^done,start=\"0x0*%x\",end=\"0x[0-9a-f]+\",file=\".+?main.cpp\",line=\"%d\"" % - (addr, line)) - - # Test that -data-info-line fails when invalid address is specified - self.runCmd("-data-info-line *0x0") - self.expect( - "\^error,msg=\"Command 'data-info-line'\. Error: The LineEntry is absent or has an unknown format\.\"") - - # Test that -data-info-line fails when file is unknown - self.runCmd("-data-info-line unknown_file:1") - self.expect( - "\^error,msg=\"Command 'data-info-line'\. Error: The LineEntry is absent or has an unknown format\.\"") - - # Test that -data-info-line fails when line has invalid format - self.runCmd("-data-info-line main.cpp:bad_line") - self.expect( - "\^error,msg=\"error: invalid line number string 'bad_line'") - self.runCmd("-data-info-line main.cpp:0") - self.expect("\^error,msg=\"error: zero is an invalid line number") - @skipIfRemote # We do not currently support remote debugging via the MI. @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfDarwin # pexpect is known to be unreliable on Darwin Index: tools/lldb-mi/MICmdCmdData.h =================================================================== --- tools/lldb-mi/MICmdCmdData.h +++ tools/lldb-mi/MICmdCmdData.h @@ -34,7 +34,6 @@ #pragma once // Third party headers: -#include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBError.h" // In-house headers: @@ -42,6 +41,7 @@ #include "MICmnLLDBDebugSessionInfoVarObj.h" #include "MICmnMIValueList.h" #include "MICmnMIValueTuple.h" +#include "MICmnMIResultRecord.h" //++ //============================================================================ @@ -377,6 +377,6 @@ // Attributes: private: - lldb::SBCommandReturnObject m_lldbResult; const CMIUtilString m_constStrArgLocation; + CMICmnMIResultRecord m_resultRecord; }; Index: tools/lldb-mi/MICmdCmdData.cpp =================================================================== --- tools/lldb-mi/MICmdCmdData.cpp +++ tools/lldb-mi/MICmdCmdData.cpp @@ -19,15 +19,13 @@ // CMICmdCmdDataInfoLine implementation. // Third Party Headers: -#include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBInstruction.h" #include "lldb/API/SBInstructionList.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBThread.h" -#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Twine.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Support/Regex.h" -#include // For PRIx64 +#include // In-house headers: #include "MICmdArgValConsume.h" @@ -46,6 +44,12 @@ #include "MICmnMIResultRecord.h" #include "MICmnMIValueConst.h" +namespace { +CMIUtilString IntToHexAddrStr(uint32_t number) { + return CMIUtilString("0x" + llvm::Twine::utohexstr(number).str()); +} +} // namespace + //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdDataEvaluateExpression constructor. @@ -1588,7 +1592,9 @@ // Throws: None. //-- CMICmdCmdDataInfoLine::CMICmdCmdDataInfoLine() - : m_constStrArgLocation("location") { + : m_constStrArgLocation("location"), + m_resultRecord(m_cmdData.strMiCmdToken, + CMICmnMIResultRecord::eResultClass_Done) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "data-info-line"; @@ -1604,7 +1610,7 @@ // Return: None. // Throws: None. //-- -CMICmdCmdDataInfoLine::~CMICmdCmdDataInfoLine() {} +CMICmdCmdDataInfoLine::~CMICmdCmdDataInfoLine() = default; //++ //------------------------------------------------------------------------------------ @@ -1637,98 +1643,84 @@ bool CMICmdCmdDataInfoLine::Execute() { CMICMDBASE_GETOPTION(pArgLocation, String, m_constStrArgLocation); + lldb::SBLineEntry line; + bool found_line = false; const CMIUtilString &strLocation(pArgLocation->GetValue()); - CMIUtilString strCmdOptionsLocation; + lldb::SBTarget target = CMICmnLLDBDebugSessionInfo::Instance().GetTarget(); + if (strLocation.at(0) == '*') { // Parse argument: // *0x12345 - // ^^^^^^^ -- address - const CMIUtilString strAddress(strLocation.substr(1)); - strCmdOptionsLocation = - CMIUtilString::Format("--address %s", strAddress.c_str()); + // ^^^^^^^^^ -- address + lldb::addr_t address = 0x0; + if (llvm::StringRef(strLocation.substr(1)).getAsInteger(0, address)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_SOME_ERROR), + m_cmdData.strMiCmd.c_str(), + "Failed to parse address.")); + return MIstatus::failure; + } + line = target.ResolveFileAddress(address).GetLineEntry(); + // Check that found line is valid. + if (line.GetLine()) + found_line = true; } else { const size_t nLineStartPos = strLocation.rfind(':'); if ((nLineStartPos == std::string::npos) || (nLineStartPos == 0) || (nLineStartPos == strLocation.length() - 1)) { - SetError( - CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_LOCATION_FORMAT), - m_cmdData.strMiCmd.c_str(), strLocation.c_str()) - .c_str()); + SetError(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_INVALID_LOCATION_FORMAT), + m_cmdData.strMiCmd.c_str(), strLocation.c_str())); return MIstatus::failure; } // Parse argument: // hello.cpp:5 // ^^^^^^^^^ -- file // ^ -- line - const CMIUtilString strFile(strLocation.substr(0, nLineStartPos)); - const CMIUtilString strLine(strLocation.substr(nLineStartPos + 1)); - strCmdOptionsLocation = - CMIUtilString::Format("--file \"%s\" --line %s", - strFile.AddSlashes().c_str(), strLine.c_str()); + const CMIUtilString &strFile(strLocation.substr(0, nLineStartPos)); + uint32_t numLine = 0; + llvm::StringRef(strLocation.substr(nLineStartPos + 1)) + .getAsInteger(0, numLine); + lldb::SBSymbolContextList sc_cu_list = + target.FindCompileUnits(lldb::SBFileSpec(strFile.c_str(), false)); + for (uint32_t i = 0, e = sc_cu_list.GetSize(); i < e; ++i) { + const lldb::SBCompileUnit &cu = + sc_cu_list.GetContextAtIndex(i).GetCompileUnit(); + // Break if we have already found requested line. + if (found_line) + break; + for (uint32_t j = 0, e = cu.GetNumLineEntries(); j < e; ++j) { + const lldb::SBLineEntry &curLine = cu.GetLineEntryAtIndex(j); + if (curLine.GetLine() == numLine) { + line = curLine; + found_line = true; + break; + } + } + } } - const CMIUtilString strCmd(CMIUtilString::Format( - "target modules lookup -v %s", strCmdOptionsLocation.c_str())); - - CMICmnLLDBDebugSessionInfo &rSessionInfo( - CMICmnLLDBDebugSessionInfo::Instance()); - const lldb::ReturnStatus rtn = - rSessionInfo.GetDebugger().GetCommandInterpreter().HandleCommand( - strCmd.c_str(), m_lldbResult); - MIunused(rtn); - - return MIstatus::success; -} - -//++ -//------------------------------------------------------------------------------------ -// Details: Helper function for parsing a line entry returned from lldb for the -// command: -// target modules lookup -v -// where the line entry is of the format: -// LineEntry: \[0x0000000100000f37-0x0000000100000f45\): -// /path/file:3[:1] -// start end file -// line column(opt) -// Args: input - (R) Input string to parse. -// start - (W) String representing the start address. -// end - (W) String representing the end address. -// file - (W) String representing the file. -// line - (W) String representing the line. -// Return: bool - True = input was parsed successfully, false = input could not -// be parsed. -// Throws: None. -//-- -static bool ParseLLDBLineEntry(const char *input, CMIUtilString &start, - CMIUtilString &end, CMIUtilString &file, - CMIUtilString &line) { - // Note: Ambiguities arise because the column is optional, and - // because : can appear in filenames or as a byte in a multibyte - // UTF8 character. We keep those cases to a minimum by using regex - // to work on the string from both the left and right, so that what - // is remains is assumed to be the filename. - - // Match LineEntry using regex. - static llvm::Regex g_lineentry_nocol_regex(llvm::StringRef( - "^ *LineEntry: \\[(0x[0-9a-fA-F]+)-(0x[0-9a-fA-F]+)\\): (.+):([0-9]+)$")); - static llvm::Regex g_lineentry_col_regex( - llvm::StringRef("^ *LineEntry: \\[(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) - - llvm::SmallVector match; - - // First try matching the LineEntry with the column, - // then try without the column. - const bool ok = g_lineentry_col_regex.match(input, &match) || - g_lineentry_nocol_regex.match(input, &match); - if (ok) { - start = match[1]; - end = match[2]; - file = match[3]; - line = match[4]; + if (!found_line) { + SetError(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_SOME_ERROR), m_cmdData.strMiCmd.c_str(), + "The LineEntry is absent or has an unknown format.")); + return MIstatus::failure; } - return ok; + // Start address. + m_resultRecord.Add(CMICmnMIValueResult( + "start", CMICmnMIValueConst(IntToHexAddrStr( + line.GetStartAddress().GetFileAddress())))); + // End address. + m_resultRecord.Add(CMICmnMIValueResult( + "end", CMICmnMIValueConst(IntToHexAddrStr( + line.GetEndAddress().GetFileAddress())))); + // File. + std::unique_ptr upPath(new char[PATH_MAX]); + line.GetFileSpec().GetPath(upPath.get(), PATH_MAX); + m_resultRecord.Add(CMICmnMIValueResult( + "file", CMICmnMIValueConst(CMIUtilString(upPath.get())))); + // Line. + m_resultRecord.Add(CMICmnMIValueResult( + "line", CMICmnMIValueConst(std::to_string(line.GetLine())))); + return MIstatus::success; } //++ @@ -1743,66 +1735,7 @@ // Throws: None. //-- bool CMICmdCmdDataInfoLine::Acknowledge() { - if (m_lldbResult.GetErrorSize() > 0) { - const CMICmnMIValueConst miValueConst(m_lldbResult.GetError()); - const CMICmnMIValueResult miValueResult("msg", miValueConst); - const CMICmnMIResultRecord miRecordResult( - m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, - miValueResult); - m_miResultRecord = miRecordResult; - return MIstatus::success; - } else if (m_lldbResult.GetOutputSize() > 0) { - CMIUtilString::VecString_t vecLines; - const CMIUtilString strLldbMsg(m_lldbResult.GetOutput()); - const MIuint nLines(strLldbMsg.SplitLines(vecLines)); - - for (MIuint i = 0; i < nLines; ++i) { - // String looks like: - // LineEntry: \[0x0000000100000f37-0x0000000100000f45\): - // /path/to/file:3[:1] - const CMIUtilString &rLine(vecLines[i]); - CMIUtilString strStart; - CMIUtilString strEnd; - CMIUtilString strFile; - CMIUtilString strLine; - - if (!ParseLLDBLineEntry(rLine.c_str(), strStart, strEnd, strFile, - strLine)) - continue; - - const CMICmnMIValueConst miValueConst(strStart); - const CMICmnMIValueResult miValueResult("start", miValueConst); - CMICmnMIResultRecord miRecordResult( - m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, - miValueResult); - const CMICmnMIValueConst miValueConst2(strEnd); - const CMICmnMIValueResult miValueResult2("end", miValueConst2); - miRecordResult.Add(miValueResult2); - const CMICmnMIValueConst miValueConst3(strFile); - const CMICmnMIValueResult miValueResult3("file", miValueConst3); - miRecordResult.Add(miValueResult3); - const CMICmnMIValueConst miValueConst4(strLine); - const CMICmnMIValueResult miValueResult4("line", miValueConst4); - miRecordResult.Add(miValueResult4); - - // MI print "%s^done,start=\"%d\",end=\"%d\"",file=\"%s\",line=\"%d\" - m_miResultRecord = miRecordResult; - - return MIstatus::success; - } - } - - // MI print "%s^error,msg=\"Command '-data-info-line'. Error: The LineEntry is - // absent or has an unknown format.\"" - const CMICmnMIValueConst miValueConst(CMIUtilString::Format( - MIRSRC(IDS_CMD_ERR_SOME_ERROR), m_cmdData.strMiCmd.c_str(), - "The LineEntry is absent or has an unknown format.")); - const CMICmnMIValueResult miValueResult("msg", miValueConst); - const CMICmnMIResultRecord miRecordResult( - m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, - miValueResult); - m_miResultRecord = miRecordResult; - + m_miResultRecord = m_resultRecord; return MIstatus::success; }