| //===-- MICmdCmdSymbol.cpp --------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // Overview: CMICmdCmdSymbolListLines implementation. |
| |
| // Third Party Headers: |
| #include "lldb/API/SBCommandInterpreter.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/Regex.h" |
| |
| // In-house headers: |
| #include "MICmdArgValFile.h" |
| #include "MICmdCmdSymbol.h" |
| #include "MICmnLLDBDebugSessionInfo.h" |
| #include "MICmnMIResultRecord.h" |
| #include "MICmnMIValueList.h" |
| #include "MICmnMIValueTuple.h" |
| |
| //++ |
| //------------------------------------------------------------------------------------ |
| // Details: CMICmdCmdSymbolListLines constructor. |
| // Type: Method. |
| // Args: None. |
| // Return: None. |
| // Throws: None. |
| //-- |
| CMICmdCmdSymbolListLines::CMICmdCmdSymbolListLines() |
| : m_constStrArgNameFile("file") { |
| // Command factory matches this name with that received from the stdin stream |
| m_strMiCmd = "symbol-list-lines"; |
| |
| // Required by the CMICmdFactory when registering *this command |
| m_pSelfCreatorFn = &CMICmdCmdSymbolListLines::CreateSelf; |
| } |
| |
| //++ |
| //------------------------------------------------------------------------------------ |
| // Details: CMICmdCmdSymbolListLines destructor. |
| // Type: Overrideable. |
| // Args: None. |
| // Return: None. |
| // Throws: None. |
| //-- |
| CMICmdCmdSymbolListLines::~CMICmdCmdSymbolListLines() {} |
| |
| //++ |
| //------------------------------------------------------------------------------------ |
| // 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 CMICmdCmdSymbolListLines::ParseArgs() { |
| m_setCmdArgs.Add(new CMICmdArgValFile(m_constStrArgNameFile, true, true)); |
| return 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. |
| // Synopsis: -symbol-list-lines file |
| // Ref: |
| // http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Symbol-Query.html#GDB_002fMI-Symbol-Query |
| // Type: Overridden. |
| // Args: None. |
| // Return: MIstatus::success - Functional succeeded. |
| // MIstatus::failure - Functional failed. |
| // Throws: None. |
| //-- |
| bool CMICmdCmdSymbolListLines::Execute() { |
| CMICMDBASE_GETOPTION(pArgFile, File, m_constStrArgNameFile); |
| |
| const CMIUtilString &strFilePath(pArgFile->GetValue()); |
| const CMIUtilString strCmd(CMIUtilString::Format( |
| "source info --file \"%s\"", strFilePath.AddSlashes().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 the header returned from lldb for the |
| // command: |
| // target modules dump line-table <file> |
| // where the header is of the format: |
| // Line table for /path/to/file in `/path/to/module |
| // Args: input - (R) Input string to parse. |
| // file - (W) String representing the file. |
| // Return: bool - True = input was parsed successfully, false = input could not |
| // be parsed. |
| // Throws: None. |
| //-- |
| static bool ParseLLDBLineAddressHeader(const char *input, CMIUtilString &file) { |
| // Match LineEntry using regex. |
| static llvm::Regex g_lineentry_header_regex(llvm::StringRef( |
| "^ *Lines found for file (.+) in compilation unit (.+) in `(.+)$")); |
| // ^1=file ^2=cu |
| // ^3=module |
| |
| llvm::SmallVector<llvm::StringRef, 4> match; |
| |
| const bool ok = g_lineentry_header_regex.match(input, &match); |
| if (ok) |
| file = match[1]; |
| return ok; |
| } |
| |
| //++ |
| //------------------------------------------------------------------------------------ |
| // Details: Helper function for parsing a line entry returned from lldb for the |
| // command: |
| // target modules dump line-table <file> |
| // where the line entry is of the format: |
| // 0x0000000100000e70: /path/to/file:3002[:4] |
| // addr file line column(opt) |
| // Args: input - (R) Input string to parse. |
| // addr - (W) String representing the pc 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 ParseLLDBLineAddressEntry(const char *input, CMIUtilString &addr, |
| 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( |
| "^ *\\[(0x[0-9a-fA-F]+)-(0x[0-9a-fA-F]+)\\): (.+):([0-9]+)$")); |
| static llvm::Regex g_lineentry_col_regex(llvm::StringRef( |
| "^ *\\[(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<llvm::StringRef, 6> 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) { |
| addr = match[1]; |
| file = match[3]; |
| line = match[4]; |
| } |
| return ok; |
| } |
| |
| //++ |
| //------------------------------------------------------------------------------------ |
| // 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 CMICmdCmdSymbolListLines::Acknowledge() { |
| if (m_lldbResult.GetErrorSize() > 0) { |
| const char *pLldbErr = m_lldbResult.GetError(); |
| const CMIUtilString strMsg(CMIUtilString(pLldbErr).StripCRAll()); |
| const CMICmnMIValueConst miValueConst(strMsg); |
| const CMICmnMIValueResult miValueResult("message", miValueConst); |
| const CMICmnMIResultRecord miRecordResult( |
| m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, |
| miValueResult); |
| m_miResultRecord = miRecordResult; |
| } else { |
| CMIUtilString::VecString_t vecLines; |
| const CMIUtilString strLldbMsg(m_lldbResult.GetOutput()); |
| const MIuint nLines(strLldbMsg.SplitLines(vecLines)); |
| |
| // Parse the file from the header. |
| const CMIUtilString &rWantFile(vecLines[0]); |
| CMIUtilString strWantFile; |
| if (!ParseLLDBLineAddressHeader(rWantFile.c_str(), strWantFile)) { |
| // Unexpected error - parsing failed. |
| // MI print "%s^error,msg=\"Command '-symbol-list-lines'. Error: Line |
| // address header is absent or has an unknown format.\"" |
| const CMICmnMIValueConst miValueConst(CMIUtilString::Format( |
| MIRSRC(IDS_CMD_ERR_SOME_ERROR), m_cmdData.strMiCmd.c_str(), |
| "Line address header 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; |
| } |
| |
| // Parse the line address entries. |
| CMICmnMIValueList miValueList(true); |
| for (MIuint i = 1; i < nLines; ++i) { |
| // String looks like: |
| // 0x0000000100000e70: /path/to/file:3[:4] |
| const CMIUtilString &rLine(vecLines[i]); |
| CMIUtilString strAddr; |
| CMIUtilString strFile; |
| CMIUtilString strLine; |
| |
| if (!ParseLLDBLineAddressEntry(rLine.c_str(), strAddr, strFile, strLine)) |
| continue; |
| |
| const CMICmnMIValueConst miValueConst(strAddr); |
| const CMICmnMIValueResult miValueResult("pc", miValueConst); |
| CMICmnMIValueTuple miValueTuple(miValueResult); |
| |
| const CMICmnMIValueConst miValueConst2(strLine); |
| const CMICmnMIValueResult miValueResult2("line", miValueConst2); |
| miValueTuple.Add(miValueResult2); |
| |
| miValueList.Add(miValueTuple); |
| } |
| |
| // MI print "%s^done,lines=[{pc=\"%d\",line=\"%d\"}...]" |
| const CMICmnMIValueResult miValueResult("lines", miValueList); |
| const CMICmnMIResultRecord miRecordResult( |
| m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, |
| 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 *CMICmdCmdSymbolListLines::CreateSelf() { |
| return new CMICmdCmdSymbolListLines(); |
| } |