Index: lldb/trunk/test/tools/lldb-mi/data/TestMiData.py =================================================================== --- lldb/trunk/test/tools/lldb-mi/data/TestMiData.py +++ lldb/trunk/test/tools/lldb-mi/data/TestMiData.py @@ -132,5 +132,52 @@ self.runCmd("-data-list-register-values x 0") self.expect("\^done,register-values=\[{number=\"0\",value=\"0x[0-9a-f]+\"}\]") + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @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]+\"") + addr = int(self.child.after.split("\"")[1], 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") + if __name__ == '__main__': unittest2.main() Index: lldb/trunk/test/tools/lldb-mi/data/main.cpp =================================================================== --- lldb/trunk/test/tools/lldb-mi/data/main.cpp +++ lldb/trunk/test/tools/lldb-mi/data/main.cpp @@ -12,6 +12,6 @@ int main(int argc, char const *argv[]) -{ +{ // FUNC_main return 0; } Index: lldb/trunk/tools/lldb-mi/MICmdCmdData.h =================================================================== --- lldb/trunk/tools/lldb-mi/MICmdCmdData.h +++ lldb/trunk/tools/lldb-mi/MICmdCmdData.h @@ -16,6 +16,7 @@ // CMICmdCmdDataListRegisterChanged interface. // CMICmdCmdDataWriteMemoryBytes interface. // CMICmdCmdDataWriteMemory interface. +// CMICmdCmdDataInfoLine interface. // // To implement new MI commands derive a new command class from the command base // class. To enable the new command for interpretation add the new command class @@ -29,6 +30,9 @@ #pragma once +// Third party headers: +#include + // In-house headers: #include "MICmdBase.h" #include "MICmnMIValueTuple.h" @@ -366,3 +370,34 @@ MIuint64 m_nCount; MIuchar *m_pBufferMemory; }; + +//++ ============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "data-info-line". +// See MIExtensions.txt for details. +//-- +class CMICmdCmdDataInfoLine : public CMICmdBase +{ + // Statics: + public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(void); + + // Methods: + public: + /* ctor */ CMICmdCmdDataInfoLine(void); + + // Overridden: + public: + // From CMICmdInvoker::ICmd + virtual bool Execute(void); + virtual bool Acknowledge(void); + virtual bool ParseArgs(void); + // From CMICmnBase + /* dtor */ virtual ~CMICmdCmdDataInfoLine(void); + + // Attributes: + private: + lldb::SBCommandReturnObject m_lldbResult; + const CMIUtilString m_constStrArgLocation; +}; Index: lldb/trunk/tools/lldb-mi/MICmdCmdData.cpp =================================================================== --- lldb/trunk/tools/lldb-mi/MICmdCmdData.cpp +++ lldb/trunk/tools/lldb-mi/MICmdCmdData.cpp @@ -16,9 +16,11 @@ // CMICmdCmdDataListRegisterChanged implementation. // CMICmdCmdDataWriteMemoryBytes implementation. // CMICmdCmdDataWriteMemory implementation. +// CMICmdCmdDataInfoLine implementation. // Third Party Headers: #include // For PRIx64 +#include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBInstruction.h" #include "lldb/API/SBInstructionList.h" @@ -1537,3 +1539,224 @@ { return new CMICmdCmdDataWriteMemory(); } + +//--------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------- + +//++ ------------------------------------------------------------------------------------ +// Details: CMICmdCmdDataInfoLine constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataInfoLine::CMICmdCmdDataInfoLine(void) + : m_constStrArgLocation("location") +{ + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "data-info-line"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdDataInfoLine::CreateSelf; +} + +//++ ------------------------------------------------------------------------------------ +// Details: CMICmdCmdDataInfoLine destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataInfoLine::~CMICmdCmdDataInfoLine(void) +{ +} + +//++ ------------------------------------------------------------------------------------ +// Details: The invoker requires this function. The parses the command line options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool +CMICmdCmdDataInfoLine::ParseArgs(void) +{ + bool bOk = m_setCmdArgs.Add(*(new CMICmdArgValString(m_constStrArgLocation, true, true))); + return (bOk && ParseValidateCmdOptions()); +} + +//++ ------------------------------------------------------------------------------------ +// Details: The invoker requires this function. The command does work in this function. +// The command is likely to communicate with the LLDB SBDebugger in here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool +CMICmdCmdDataInfoLine::Execute(void) +{ + CMICMDBASE_GETOPTION(pArgLocation, String, m_constStrArgLocation); + + const CMIUtilString &strLocation(pArgLocation->GetValue()); + CMIUtilString strCmdOptionsLocation; + if (strLocation.at(0) == '*') + { + // Parse argument: + // *0x12345 + // ^^^^^^^ -- address + const CMIUtilString strAddress(strLocation.c_str() + 1); + strCmdOptionsLocation = CMIUtilString::Format("--address %s", strAddress.c_str()); + } + else + { + const MIuint nLineStartPos = strLocation.rfind(':'); + if ((nLineStartPos == (MIuint)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()); + return MIstatus::failure; + } + // Parse argument: + // hello.cpp:5 + // ^^^^^^^^^ -- file + // ^ -- line + const CMIUtilString strFile(strLocation.substr(0, nLineStartPos).c_str()); + const CMIUtilString strLine(strLocation.substr(nLineStartPos + 1).c_str()); + strCmdOptionsLocation = CMIUtilString::Format("--file \"%s\" --line %s", strFile.AddSlashes().c_str(), strLine.c_str()); + } + 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: The invoker requires this function. The command prepares a MI Record Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool +CMICmdCmdDataInfoLine::Acknowledge(void) +{ + 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]); + + // LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1] + // ^^^^^^^^^ -- property + const MIuint nPropertyStartPos = rLine.find_first_not_of(' '); + const MIuint nPropertyEndPos = rLine.find(':'); + const MIuint nPropertyLen = nPropertyEndPos - nPropertyStartPos; + const CMIUtilString strProperty(rLine.substr(nPropertyStartPos, nPropertyLen).c_str()); + + // Skip all except LineEntry + if (!CMIUtilString::Compare(strProperty, "LineEntry")) + continue; + + // LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1] + // ^^^^^^^^^^^^^^^^^^ -- start address + const MIuint nStartAddressStartPos = rLine.find("["); + const MIuint nStartAddressEndPos = rLine.find("-"); + const MIuint nStartAddressLen = nStartAddressEndPos - nStartAddressStartPos - 1; + const CMIUtilString strStartAddress(rLine.substr(nStartAddressStartPos + 1, nStartAddressLen).c_str()); + const CMICmnMIValueConst miValueConst(strStartAddress); + const CMICmnMIValueResult miValueResult("start", miValueConst); + CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult); + + // LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1] + // ^^^^^^^^^^^^^^^^^^ -- end address + const MIuint nEndAddressEndPos = rLine.find(")"); + const MIuint nEndAddressLen = nEndAddressEndPos - nStartAddressEndPos - 1; + const CMIUtilString strEndAddress(rLine.substr(nStartAddressEndPos + 1, nEndAddressLen).c_str()); + const CMICmnMIValueConst miValueConst2(strEndAddress); + const CMICmnMIValueResult miValueResult2("end", miValueConst2); + bool bOk = miRecordResult.Add(miValueResult2); + if (!bOk) + return MIstatus::failure; + + // LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1] + // ^^^^^^^^^^^^^ -- file + // ^ -- line + // ^ -- column (optional) + const MIuint nFileStartPos = rLine.find_first_not_of(' ', nEndAddressEndPos + 2); + const MIuint nFileOrLineEndPos = rLine.rfind(':'); + const MIuint nFileOrLineStartPos = rLine.rfind(':', nFileOrLineEndPos - 1); + const MIuint nFileEndPos = nFileStartPos < nFileOrLineStartPos ? nFileOrLineStartPos : nFileOrLineEndPos; + const MIuint nFileLen = nFileEndPos - nFileStartPos; + const CMIUtilString strFile(rLine.substr(nFileStartPos, nFileLen).c_str()); + const CMICmnMIValueConst miValueConst3(strFile); + const CMICmnMIValueResult miValueResult3("file", miValueConst3); + bOk = miRecordResult.Add(miValueResult3); + if (!bOk) + return MIstatus::failure; + + // LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1] + // ^ -- line + const MIuint nLineStartPos = nFileEndPos + 1; + const MIuint nLineEndPos = rLine.find(':', nLineStartPos); + const MIuint nLineLen = nLineEndPos != (MIuint)std::string::npos ? nLineEndPos - nLineStartPos - 1 + : (MIuint)std::string::npos; + const CMIUtilString strLine(rLine.substr(nLineStartPos, nLineLen).c_str()); + const CMICmnMIValueConst miValueConst4(strLine); + const CMICmnMIValueResult miValueResult4("line", miValueConst4); + bOk = miRecordResult.Add(miValueResult4); + if (!bOk) + return MIstatus::failure; + + // 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; + + return MIstatus::success; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Required by the CMICmdFactory when registering *this command. The factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase * +CMICmdCmdDataInfoLine::CreateSelf(void) +{ + return new CMICmdCmdDataInfoLine(); +} Index: lldb/trunk/tools/lldb-mi/MICmdCommands.cpp =================================================================== --- lldb/trunk/tools/lldb-mi/MICmdCommands.cpp +++ lldb/trunk/tools/lldb-mi/MICmdCommands.cpp @@ -83,6 +83,7 @@ bOk &= Register(); bOk &= Register(); bOk &= Register(); + bOk &= Register(); bOk &= Register(); bOk &= Register(); bOk &= Register(); Index: lldb/trunk/tools/lldb-mi/MICmnResources.h =================================================================== --- lldb/trunk/tools/lldb-mi/MICmnResources.h +++ lldb/trunk/tools/lldb-mi/MICmnResources.h @@ -254,6 +254,7 @@ IDS_CMD_ERR_LLDB_ERR_READ_MEM_BYTES, IDS_CMD_ERR_INVALID_PROCESS, IDS_CMD_ERR_INVALID_PRINT_VALUES, + IDS_CMD_ERR_INVALID_LOCATION_FORMAT, IDS_CMD_ERR_INVALID_FORMAT_TYPE, IDS_CMD_ERR_BRKPT_INFO_OBJ_NOT_FOUND, IDS_CMD_ERR_LLDB_ERR_WRITE_MEM_BYTES, Index: lldb/trunk/tools/lldb-mi/MICmnResources.cpp =================================================================== --- lldb/trunk/tools/lldb-mi/MICmnResources.cpp +++ lldb/trunk/tools/lldb-mi/MICmnResources.cpp @@ -236,6 +236,7 @@ {IDS_CMD_ERR_LLDB_ERR_READ_MEM_BYTES, "Command '%s'. Unable to read memory block of %u bytes at address 0x%016" PRIx64 ": %s "}, {IDS_CMD_ERR_INVALID_PROCESS, "Command '%s'. Invalid process during debug session"}, {IDS_CMD_ERR_INVALID_PRINT_VALUES, "Command '%s'. Unknown value for PRINT_VALUES: must be: 0 or \"--no-values\", 1 or \"--all-values\", 2 or \"--simple-values\""}, + {IDS_CMD_ERR_INVALID_LOCATION_FORMAT, "Command '%s'. Invalid location format '%s'"}, {IDS_CMD_ERR_INVALID_FORMAT_TYPE, "Command '%s'. Invalid var format type '%s'"}, {IDS_CMD_ERR_BRKPT_INFO_OBJ_NOT_FOUND, "Command '%s'. Breakpoint information for breakpoint ID %d not found"}, {IDS_CMD_ERR_LLDB_ERR_WRITE_MEM_BYTES, "Command '%s'. Unable to write memory block of %u bytes at address 0x%016" PRIx64 ": %s "}, Index: lldb/trunk/tools/lldb-mi/MIExtensions.txt =================================================================== --- lldb/trunk/tools/lldb-mi/MIExtensions.txt +++ lldb/trunk/tools/lldb-mi/MIExtensions.txt @@ -1,4 +1,4 @@ --file-exec-and-symbols now takes two new (optional) options: +# -file-exec-and-symbols now takes two new (optional) options: Synopsis @@ -8,3 +8,26 @@ When debugging remote targets specify a remote-file for execution and a file from which symbols are read. The optional platform is the name of the platform, e.g., "remote-ios" or "ios-simulator". The remote-file is the on-device path to the exe. + +# -data-info-line + +Synopsis + + -data-info-line *
+ -data-info-line : + +Provides information about a source line. The input can be
like 0x12345678 or : +where file is a name of source file and line is the line number. As a result the command returns the following +fields: + start - address of the first instruction which refers to that source line + end - address of the last instruction which refers to that source line + file - the file name + line - the line number +The last two fields are useful in case you have specified a source line using its address. + +Example: + -data-info-line *0x100000f80 + ^done,start="0x0000000100000f80",end="0x0000000100000f94",file="/Users/IliaK/p/hello.cpp",line="15" + + -data-info-line hello.cpp:15 + ^done,start="0x0000000100000f80",end="0x0000000100000f94",file="/Users/IliaK/p/hello.cpp",line="15"