mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-10 18:11:19 +00:00
[lldb-mi] Fix the handling of files in -data-info-line and -symbol-list-lines.
This fixes -data-info-line and -symbol-list-lines to parse the filename and line correctly when line entries don't have the optional column number and the filename contains a Windows drive letter. It also fixes -symbol-list-lines when code from header files is generated. Reviewed by: abidh, ki.stfu Subscribers: lldb-commits Differential Revision: http://reviews.llvm.org/D12115 llvm-svn: 247899
This commit is contained in:
parent
2e98d57ad4
commit
b01310008f
@ -1,5 +1,5 @@
|
||||
LEVEL = ../../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
CXX_SOURCES := main.cpp symbol_list_lines_inline_test.cpp symbol_list_lines_inline_test2.cpp
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
||||
|
@ -39,6 +39,19 @@ class MiSymbolTestCase(lldbmi_testcase.MiTestCaseBase):
|
||||
self.runCmd("-symbol-list-lines main.cpp")
|
||||
self.expect("\^done,lines=\[\{pc=\"0x0*%x\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"\d+\"\})+\]" % (addr, line))
|
||||
|
||||
# Test that -symbol-list-lines doesn't include lines from other sources
|
||||
# by checking the first and last line, and making sure the other lines
|
||||
# are between 30 and 39.
|
||||
sline = line_number('symbol_list_lines_inline_test2.cpp', '// FUNC_gfunc2')
|
||||
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\"\}\]" % (sline, eline))
|
||||
##FIXME: This doesn't work for symbol_list_lines_inline_test.cpp due to clang bug llvm.org/pr24716
|
||||
##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 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 \"")
|
||||
|
@ -7,8 +7,12 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
extern int j;
|
||||
extern int gfunc(int i);
|
||||
extern int gfunc2(int i);
|
||||
int
|
||||
main(int argc, char const *argv[])
|
||||
main()
|
||||
{ // FUNC_main
|
||||
return 0;
|
||||
int i = gfunc(j) + gfunc2(j);
|
||||
return i == 0;
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
// Skip lines so we can make sure we're not seeing any lines from
|
||||
// symbol_list_lines_inline_test.h included in -symbol-list-lines
|
||||
// symbol_list_lines_inline_test.cpp, by checking that all the lines
|
||||
// are between 30 and 39.
|
||||
// line 5
|
||||
// line 6
|
||||
// line 7
|
||||
// line 8
|
||||
// line 9
|
||||
// line 10
|
||||
// line 11
|
||||
// line 12
|
||||
// line 13
|
||||
// line 14
|
||||
// line 15
|
||||
// line 16
|
||||
// line 17
|
||||
// line 18
|
||||
// line 19
|
||||
// line 20
|
||||
// line 21
|
||||
// line 22
|
||||
// line 23
|
||||
// line 24
|
||||
// line 25
|
||||
// line 26
|
||||
// line 27
|
||||
// line 28
|
||||
// line 29
|
||||
#include "symbol_list_lines_inline_test.h"
|
||||
int
|
||||
gfunc(int i)
|
||||
{ // FUNC_gfunc
|
||||
return ns::ifunc(i);
|
||||
}
|
||||
namespace ns
|
||||
{
|
||||
S s; // STRUCT_s
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
namespace ns
|
||||
{
|
||||
inline int
|
||||
ifunc(int i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
struct S
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
S()
|
||||
: a(3)
|
||||
, b(4)
|
||||
{
|
||||
}
|
||||
int
|
||||
mfunc()
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
extern S s;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
// Skip lines so we can make sure we're not seeing any lines from
|
||||
// symbol_list_lines_inline_test.h included in -symbol-list-lines
|
||||
// symbol_list_lines_inline_test2.cpp, by checking that all the lines
|
||||
// are between 30 and 39.
|
||||
// line 5
|
||||
// line 6
|
||||
// line 7
|
||||
// line 8
|
||||
// line 9
|
||||
// line 10
|
||||
// line 11
|
||||
// line 12
|
||||
// line 13
|
||||
// line 14
|
||||
// line 15
|
||||
// line 16
|
||||
// line 17
|
||||
// line 18
|
||||
// line 19
|
||||
// line 20
|
||||
// line 21
|
||||
// line 22
|
||||
// line 23
|
||||
// line 24
|
||||
// line 25
|
||||
// line 26
|
||||
// line 27
|
||||
// line 28
|
||||
// line 29
|
||||
#include "symbol_list_lines_inline_test.h"
|
||||
int j = 2;
|
||||
int
|
||||
gfunc2(int i)
|
||||
{ // FUNC_gfunc2
|
||||
i += ns::s.mfunc();
|
||||
i += ns::ifunc(i);
|
||||
return i == 0; // END_gfunc2
|
||||
}
|
@ -65,6 +65,7 @@ set(LLDB_MI_SOURCES
|
||||
MIDriverBase.cpp
|
||||
MIDriverMain.cpp
|
||||
MIDriverMgr.cpp
|
||||
MIUtilParse.cpp
|
||||
MIUtilDateTimeStd.cpp
|
||||
MIUtilDebug.cpp
|
||||
MIUtilFileStd.cpp
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "MICmdArgValConsume.h"
|
||||
#include "MICmnLLDBDebugSessionInfoVarObj.h"
|
||||
#include "MICmnLLDBUtilSBValue.h"
|
||||
#include "MIUtilParse.h"
|
||||
|
||||
//++ ------------------------------------------------------------------------------------
|
||||
// Details: CMICmdCmdDataEvaluateExpression constructor.
|
||||
@ -1641,6 +1642,53 @@ CMICmdCmdDataInfoLine::Execute()
|
||||
return MIstatus::success;
|
||||
}
|
||||
|
||||
//++ ------------------------------------------------------------------------------------
|
||||
// Details: Helper function for parsing a line entry returned from lldb for the command:
|
||||
// target modules lookup -v <location>
|
||||
// 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 MIUtilParse::CRegexParser g_lineentry_nocol_regex(
|
||||
"^ *LineEntry: \\[(0x[0-9a-fA-F]+)-(0x[0-9a-fA-F]+)\\): (.+):([0-9]+)$");
|
||||
static MIUtilParse::CRegexParser g_lineentry_col_regex(
|
||||
"^ *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)
|
||||
|
||||
MIUtilParse::CRegexParser::Match match(6);
|
||||
|
||||
// First try matching the LineEntry with the column,
|
||||
// then try without the column.
|
||||
const bool ok = g_lineentry_col_regex.Execute(input, match) ||
|
||||
g_lineentry_nocol_regex.Execute(input, match);
|
||||
if (ok)
|
||||
{
|
||||
start = match.GetMatchAtIndex(1);
|
||||
end = match.GetMatchAtIndex(2);
|
||||
file = match.GetMatchAtIndex(3);
|
||||
line = match.GetMatchAtIndex(4);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
//++ ------------------------------------------------------------------------------------
|
||||
// Details: The invoker requires this function. The command prepares a MI Record Result
|
||||
// for the work carried out in the Execute().
|
||||
@ -1672,58 +1720,25 @@ CMICmdCmdDataInfoLine::Acknowledge()
|
||||
// String looks like:
|
||||
// LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1]
|
||||
const CMIUtilString &rLine(vecLines[i]);
|
||||
CMIUtilString strStart;
|
||||
CMIUtilString strEnd;
|
||||
CMIUtilString strFile;
|
||||
CMIUtilString strLine;
|
||||
|
||||
// LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1]
|
||||
// ^^^^^^^^^ -- property
|
||||
const size_t nPropertyStartPos = rLine.find_first_not_of(' ');
|
||||
const size_t nPropertyEndPos = rLine.find(':');
|
||||
const size_t nPropertyLen = nPropertyEndPos - nPropertyStartPos;
|
||||
const CMIUtilString strProperty(rLine.substr(nPropertyStartPos, nPropertyLen).c_str());
|
||||
|
||||
// Skip all except LineEntry
|
||||
if (!CMIUtilString::Compare(strProperty, "LineEntry"))
|
||||
if (!ParseLLDBLineEntry(rLine.c_str(), strStart, strEnd, strFile, strLine))
|
||||
continue;
|
||||
|
||||
// LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1]
|
||||
// ^^^^^^^^^^^^^^^^^^ -- start address
|
||||
const size_t nStartAddressStartPos = rLine.find('[');
|
||||
const size_t nStartAddressEndPos = rLine.find('-');
|
||||
const size_t nStartAddressLen = nStartAddressEndPos - nStartAddressStartPos - 1;
|
||||
const CMIUtilString strStartAddress(rLine.substr(nStartAddressStartPos + 1, nStartAddressLen).c_str());
|
||||
const CMICmnMIValueConst miValueConst(strStartAddress);
|
||||
const CMICmnMIValueConst miValueConst(strStart);
|
||||
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 size_t nEndAddressEndPos = rLine.find(')');
|
||||
const size_t nEndAddressLen = nEndAddressEndPos - nStartAddressEndPos - 1;
|
||||
const CMIUtilString strEndAddress(rLine.substr(nStartAddressEndPos + 1, nEndAddressLen).c_str());
|
||||
const CMICmnMIValueConst miValueConst2(strEndAddress);
|
||||
CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken,
|
||||
CMICmnMIResultRecord::eResultClass_Done,
|
||||
miValueResult);
|
||||
const CMICmnMIValueConst miValueConst2(strEnd);
|
||||
const CMICmnMIValueResult miValueResult2("end", miValueConst2);
|
||||
miRecordResult.Add(miValueResult2);
|
||||
|
||||
// LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1]
|
||||
// ^^^^^^^^^^^^^ -- file
|
||||
// ^ -- line
|
||||
// ^ -- column (optional)
|
||||
const size_t nFileStartPos = rLine.find_first_not_of(' ', nEndAddressEndPos + 2);
|
||||
const size_t nFileOrLineEndPos = rLine.rfind(':');
|
||||
const size_t nFileOrLineStartPos = rLine.rfind(':', nFileOrLineEndPos - 1);
|
||||
const size_t nFileEndPos = nFileStartPos < nFileOrLineStartPos ? nFileOrLineStartPos : nFileOrLineEndPos;
|
||||
const size_t nFileLen = nFileEndPos - nFileStartPos;
|
||||
const CMIUtilString strFile(rLine.substr(nFileStartPos, nFileLen).c_str());
|
||||
const CMICmnMIValueConst miValueConst3(strFile);
|
||||
const CMICmnMIValueResult miValueResult3("file", miValueConst3);
|
||||
miRecordResult.Add(miValueResult3);
|
||||
|
||||
// LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1]
|
||||
// ^ -- line
|
||||
const size_t nLineStartPos = nFileEndPos + 1;
|
||||
const size_t nLineEndPos = rLine.find(':', nLineStartPos);
|
||||
const size_t nLineLen = nLineEndPos != std::string::npos ? nLineEndPos - nLineStartPos
|
||||
: std::string::npos;
|
||||
const CMIUtilString strLine(rLine.substr(nLineStartPos, nLineLen).c_str());
|
||||
const CMICmnMIValueConst miValueConst4(strLine);
|
||||
const CMICmnMIValueResult miValueResult4("line", miValueConst4);
|
||||
miRecordResult.Add(miValueResult4);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "MICmnMIResultRecord.h"
|
||||
#include "MICmnMIValueList.h"
|
||||
#include "MICmnMIValueTuple.h"
|
||||
#include "MIUtilParse.h"
|
||||
|
||||
//++ ------------------------------------------------------------------------------------
|
||||
// Details: CMICmdCmdSymbolListLines constructor.
|
||||
@ -81,6 +82,10 @@ CMICmdCmdSymbolListLines::Execute()
|
||||
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()));
|
||||
|
||||
CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance());
|
||||
@ -90,6 +95,77 @@ CMICmdCmdSymbolListLines::Execute()
|
||||
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 MIUtilParse::CRegexParser g_lineentry_header_regex(
|
||||
"^ *Line table for (.+) in `(.+)$");
|
||||
// ^1=file ^2=module
|
||||
|
||||
MIUtilParse::CRegexParser::Match match(3);
|
||||
|
||||
const bool ok = g_lineentry_header_regex.Execute(input, match);
|
||||
if (ok)
|
||||
file = match.GetMatchAtIndex(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 MIUtilParse::CRegexParser g_lineentry_nocol_regex(
|
||||
"^ *(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)
|
||||
|
||||
MIUtilParse::CRegexParser::Match match(5);
|
||||
|
||||
// First try matching the LineEntry with the column,
|
||||
// then try without the column.
|
||||
const bool ok = g_lineentry_col_regex.Execute(input, match) ||
|
||||
g_lineentry_nocol_regex.Execute(input, match);
|
||||
if (ok)
|
||||
{
|
||||
addr = match.GetMatchAtIndex(1);
|
||||
file = match.GetMatchAtIndex(2);
|
||||
line = match.GetMatchAtIndex(3);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
//++ ------------------------------------------------------------------------------------
|
||||
// Details: The invoker requires this function. The command prepares a MI Record Result
|
||||
// for the work carried out in the Execute().
|
||||
@ -117,29 +193,43 @@ CMICmdCmdSymbolListLines::Acknowledge()
|
||||
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;
|
||||
|
||||
// Skip entries which don't match the desired source.
|
||||
if (strWantFile != strFile)
|
||||
continue;
|
||||
|
||||
// 0x0000000100000e70: /path/to/file:3[:4]
|
||||
// ^^^^^^^^^^^^^^^^^^ -- pc
|
||||
const size_t nAddrEndPos = rLine.find(':');
|
||||
const CMIUtilString strAddr(rLine.substr(0, nAddrEndPos).c_str());
|
||||
const CMICmnMIValueConst miValueConst(strAddr);
|
||||
const CMICmnMIValueResult miValueResult("pc", miValueConst);
|
||||
CMICmnMIValueTuple miValueTuple(miValueResult);
|
||||
|
||||
// 0x0000000100000e70: /path/to/file:3[:4]
|
||||
// ^ -- line
|
||||
const size_t nLineOrColumnStartPos = rLine.rfind(':');
|
||||
const CMIUtilString strLineOrColumn(rLine.substr(nLineOrColumnStartPos + 1).c_str());
|
||||
const size_t nPathOrLineStartPos = rLine.rfind(':', nLineOrColumnStartPos - 1);
|
||||
const size_t nPathOrLineLen = nLineOrColumnStartPos - nPathOrLineStartPos - 1;
|
||||
const CMIUtilString strPathOrLine(rLine.substr(nPathOrLineStartPos + 1, nPathOrLineLen).c_str());
|
||||
const CMIUtilString strLine(strPathOrLine.IsNumber() ? strPathOrLine : strLineOrColumn);
|
||||
const CMICmnMIValueConst miValueConst2(strLine);
|
||||
const CMICmnMIValueResult miValueResult2("line", miValueConst2);
|
||||
miValueTuple.Add(miValueResult2);
|
||||
|
75
lldb/tools/lldb-mi/MIUtilParse.cpp
Normal file
75
lldb/tools/lldb-mi/MIUtilParse.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
//===-- MIUtilParse.cpp ----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Third party headers:
|
||||
#include <memory>
|
||||
|
||||
// In-house headers:
|
||||
#include "MIUtilParse.h"
|
||||
|
||||
//++ ------------------------------------------------------------------------------------
|
||||
// Details: CRegexParser constructor.
|
||||
// Type: Method.
|
||||
// Args: regexStr - Pointer to the regular expression to compile.
|
||||
// Return: None.
|
||||
// Throws: None.
|
||||
//--
|
||||
MIUtilParse::CRegexParser::CRegexParser(const char *regexStr)
|
||||
: m_isValid(llvm_regcomp(&m_emma, regexStr, REG_EXTENDED) == 0)
|
||||
{
|
||||
}
|
||||
|
||||
//++ ------------------------------------------------------------------------------------
|
||||
// Details: CRegexParser destructor.
|
||||
// Type: Method.
|
||||
// Args: None.
|
||||
// Return: None.
|
||||
// Throws: None.
|
||||
//--
|
||||
MIUtilParse::CRegexParser::~CRegexParser()
|
||||
{
|
||||
// Free up memory held within regex.
|
||||
if (m_isValid)
|
||||
llvm_regfree(&m_emma);
|
||||
}
|
||||
|
||||
//++ ------------------------------------------------------------------------------------
|
||||
// Details: CRegexParser regex executer.
|
||||
// Match the input against the regular expression. Return an error
|
||||
// if the number of matches is less than minMatches. If the default
|
||||
// minMatches value of 0 is passed, an error will be returned if
|
||||
// the number of matches is less than the maxMatches value used to
|
||||
// initialize Match.
|
||||
// Type: Method.
|
||||
// Args: input (R) - Pointer to UTF8 text data to be parsed.
|
||||
// match (RW) - Reference to Match class.
|
||||
// minMatches (R) - Minimum number of regex matches expected.
|
||||
// Return: bool - True = minimum matches were met,
|
||||
// false = minimum matches were not met or regex failed.
|
||||
// Throws: None.
|
||||
//--
|
||||
bool
|
||||
MIUtilParse::CRegexParser::Execute(const char *input, Match& match, size_t minMatches)
|
||||
{
|
||||
if (!m_isValid)
|
||||
return false;
|
||||
|
||||
std::unique_ptr<llvm_regmatch_t[]> matches(new llvm_regmatch_t[match.m_maxMatches]); // Array of matches
|
||||
|
||||
if (llvm_regexec(&m_emma, input, match.m_maxMatches, matches.get(), 0) != 0)
|
||||
return false;
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < match.m_maxMatches && matches[i].rm_so >= 0; i++)
|
||||
{
|
||||
const int n = matches[i].rm_eo - matches[i].rm_so;
|
||||
match.m_matchStrs[i].assign(input + matches[i].rm_so, n);
|
||||
}
|
||||
return i >= minMatches;
|
||||
}
|
93
lldb/tools/lldb-mi/MIUtilParse.h
Normal file
93
lldb/tools/lldb-mi/MIUtilParse.h
Normal file
@ -0,0 +1,93 @@
|
||||
//===-- MIUtilParse.h ------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
|
||||
// Third party headers:
|
||||
#include "../lib/Support/regex_impl.h"
|
||||
|
||||
// In-house headers:
|
||||
#include "MIUtilString.h"
|
||||
|
||||
namespace MIUtilParse
|
||||
{
|
||||
|
||||
//++ ============================================================================
|
||||
// Details: MI common code utility class. Used to parse the output
|
||||
// returned from lldb commands using regex.
|
||||
//--
|
||||
class CRegexParser
|
||||
{
|
||||
public:
|
||||
// Helper class for keeping track of regex matches.
|
||||
class Match
|
||||
{
|
||||
friend CRegexParser;
|
||||
public:
|
||||
/* ctor */ explicit Match(size_t nmatches)
|
||||
: m_matchStrs(nmatches), m_maxMatches(nmatches)
|
||||
{
|
||||
}
|
||||
size_t
|
||||
GetMatchCount() const
|
||||
{
|
||||
return m_matchStrs.size();
|
||||
}
|
||||
CMIUtilString
|
||||
GetMatchAtIndex(size_t i) const
|
||||
{
|
||||
if (m_matchStrs.size() > i)
|
||||
return m_matchStrs[i];
|
||||
return CMIUtilString();
|
||||
}
|
||||
private:
|
||||
CMIUtilString::VecString_t m_matchStrs;
|
||||
const size_t m_maxMatches;
|
||||
};
|
||||
|
||||
// Methods:
|
||||
// Compile the regular expression.
|
||||
/* ctor */ explicit CRegexParser(const char *regexStr);
|
||||
|
||||
// Free the memory used by the regular expression.
|
||||
/* dtor */ ~CRegexParser();
|
||||
|
||||
// No copies
|
||||
CRegexParser(const CRegexParser&) = delete;
|
||||
void operator=(CRegexParser&) = delete;
|
||||
|
||||
// Return the match at the index.
|
||||
int
|
||||
GetMatchCount(const Match& match) const
|
||||
{
|
||||
if (m_isValid)
|
||||
return match.GetMatchCount();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
IsValid() const
|
||||
{
|
||||
return m_isValid;
|
||||
}
|
||||
|
||||
// Match the input against the regular expression. Return an error
|
||||
// if the number of matches is less than minMatches. If the default
|
||||
// minMatches value of 0 is passed, an error will be returned if
|
||||
// the number of matches is less than the maxMatches value used to
|
||||
// initialize Match.
|
||||
bool
|
||||
Execute(const char *input, Match& match, size_t minMatches = 0);
|
||||
|
||||
private:
|
||||
llvm_regex_t m_emma;
|
||||
const bool m_isValid;
|
||||
};
|
||||
|
||||
}
|
@ -54,6 +54,19 @@ CMIUtilString::CMIUtilString(const char *const *vpData)
|
||||
{
|
||||
}
|
||||
|
||||
//++ ------------------------------------------------------------------------------------
|
||||
// Details: CMIUtilString constructor.
|
||||
// Type: Method.
|
||||
// Args: vpData - Pointer to UTF8 text data.
|
||||
// nLen - Length of string.
|
||||
// Return: None.
|
||||
// Throws: None.
|
||||
//--
|
||||
CMIUtilString::CMIUtilString(const char *vpData, size_t nLen)
|
||||
: std::string(vpData, nLen)
|
||||
{
|
||||
}
|
||||
|
||||
//++ ------------------------------------------------------------------------------------
|
||||
// Details: CMIUtilString assignment operator.
|
||||
// Type: Method.
|
||||
|
@ -44,6 +44,7 @@ class CMIUtilString : public std::string
|
||||
/* ctor */ CMIUtilString();
|
||||
/* ctor */ CMIUtilString(const char *vpData);
|
||||
/* ctor */ CMIUtilString(const char *const *vpData);
|
||||
/* ctor */ CMIUtilString(const char *vpData, size_t nLen);
|
||||
//
|
||||
bool ExtractNumber(MIint64 &vwrNumber) const;
|
||||
CMIUtilString FindAndReplace(const CMIUtilString &vFind, const CMIUtilString &vReplaceWith) const;
|
||||
|
Loading…
Reference in New Issue
Block a user