diff --git a/lldb/test/tools/lldb-mi/data/TestMiData.py b/lldb/test/tools/lldb-mi/data/TestMiData.py index c26c16e0c6e6..42efeed72a6f 100644 --- a/lldb/test/tools/lldb-mi/data/TestMiData.py +++ b/lldb/test/tools/lldb-mi/data/TestMiData.py @@ -1,338 +1,338 @@ """ Test lldb-mi -data-xxx commands. """ import lldb_shared import unittest2 import lldbmi_testcase from lldbtest import * class MiDataTestCase(lldbmi_testcase.MiTestCaseBase): mydir = TestBase.compute_mydir(__file__) @lldbmi_test @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_data_disassemble(self): """Test that 'lldb-mi --interpreter' works for -data-disassemble.""" 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 an address for disassembling: use main self.runCmd("-data-evaluate-expression main") - self.expect("\^done,value=\"0x[0-9a-f]+\"") - addr = int(self.child.after.split("\"")[1], 16) + 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) # Test -data-disassemble: try to disassemble some address self.runCmd("-data-disassemble -s %#x -e %#x -- 0" % (addr, addr + 0x10)) self.expect("\^done,asm_insns=\[{address=\"0x0*%x\",func-name=\"main\",offset=\"0\",size=\"[1-9]+\",inst=\".+?\"}," % addr) # Run to hello_world self.runCmd("-break-insert -f hello_world") self.expect("\^done,bkpt={number=\"2\"") self.runCmd("-exec-continue") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Get an address for disassembling: use hello_world self.runCmd("-data-evaluate-expression hello_world") - self.expect("\^done,value=\"0x[0-9a-f]+\"") - addr = int(self.child.after.split("\"")[1], 16) + self.expect("\^done,value=\"0x[0-9a-f]+ \(a.out`hello_world\(\) at main.cpp:[0-9]+\)\"") + addr = int(self.child.after.split("\"")[1].split(" ")[0], 16) # Test -data-disassemble: try to disassemble some address self.runCmd("-data-disassemble -s %#x -e %#x -- 0" % (addr, addr + 0x10)) # This matches a line similar to: # Darwin: {address="0x0000000100000f18",func-name="hello_world()",offset="8",size="7",inst="leaq 0x65(%rip), %rdi; \"Hello, World!\\n\""}, # Linux: {address="0x0000000000400642",func-name="hello_world()",offset="18",size="5",inst="callq 0x4004d0; symbol stub for: printf"} # To match the escaped characters in the ouptut, we must use four backslashes per matches backslash # See https://docs.python.org/2/howto/regex.html#the-backslash-plague self.expect([ "{address=\"0x[0-9a-f]+\",func-name=\"hello_world\(\)\",offset=\"[0-9]+\",size=\"[0-9]+\",inst=\".+?; \\\\\"Hello, World!\\\\\\\\n\\\\\"\"}", "{address=\"0x[0-9a-f]+\",func-name=\"hello_world\(\)\",offset=\"[0-9]+\",size=\"[0-9]+\",inst=\".+?; symbol stub for: printf\"}" ]) @lldbmi_test @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @unittest2.skip("-data-evaluate-expression doesn't work on globals") #FIXME: the global case worked before refactoring def test_lldbmi_data_read_memory_bytes_global(self): """Test that -data-read-memory-bytes can access global buffers.""" 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 address of char[] (global) self.runCmd("-data-evaluate-expression &g_CharArray") self.expect("\^done,value=\"0x[0-9a-f]+\"") addr = int(self.child.after.split("\"")[1], 16) size = 5 # Test that -data-read-memory-bytes works for char[] type (global) self.runCmd("-data-read-memory-bytes %#x %d" % (addr, size)) self.expect("\^done,memory=\[{begin=\"0x0*%x\",offset=\"0x0+\",end=\"0x0*%x\",contents=\"1112131400\"}\]" % (addr, addr + size)) # Get address of static char[] self.runCmd("-data-evaluate-expression &s_CharArray") self.expect("\^done,value=\"0x[0-9a-f]+\"") addr = int(self.child.after.split("\"")[1], 16) size = 5 # Test that -data-read-memory-bytes works for static char[] type self.runCmd("-data-read-memory-bytes %#x %d" % (addr, size)) self.expect("\^done,memory=\[{begin=\"0x0*%x\",offset=\"0x0+\",end=\"0x0*%x\",contents=\"1112131400\"}\]" % (addr, addr + size)) @lldbmi_test @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_data_read_memory_bytes_local(self): """Test that -data-read-memory-bytes can access local buffers.""" self.spawnLldbMi(args = None) # Load executable self.runCmd('-file-exec-and-symbols %s' % self.myexe) self.expect(r'\^done') # Run to BP_local_array_test_inner line = line_number('main.cpp', '// BP_local_array_test_inner') self.runCmd('-break-insert main.cpp:%d' % line) self.expect(r'\^done,bkpt=\{number="1"') self.runCmd('-exec-run') self.expect(r'\^running') self.expect(r'\*stopped,reason="breakpoint-hit"') # Get address of local char[] self.runCmd('-data-evaluate-expression "(void *)&array"') self.expect(r'\^done,value="0x[0-9a-f]+"') addr = int(self.child.after.split('"')[1], 16) size = 4 # Test that an unquoted hex literal address works self.runCmd('-data-read-memory-bytes %#x %d' % (addr, size)) self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) # Test that a double-quoted hex literal address works self.runCmd('-data-read-memory-bytes "%#x" %d' % (addr, size)) self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) # Test that unquoted expressions work self.runCmd('-data-read-memory-bytes &array %d' % size) self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) # This doesn't work, and perhaps that makes sense, but it does work on GDB self.runCmd('-data-read-memory-bytes array 4') self.expect(r'\^error') #self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) self.runCmd('-data-read-memory-bytes &array[2] 2') self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="0304"\}\]' % (addr + 2, addr + size)) self.runCmd('-data-read-memory-bytes first_element_ptr %d' % size) self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) # Test that double-quoted expressions work self.runCmd('-data-read-memory-bytes "&array" %d' % size) self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) self.runCmd('-data-read-memory-bytes "&array[0] + 1" 3') self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="020304"\}\]' % (addr + 1, addr + size)) self.runCmd('-data-read-memory-bytes "first_element_ptr + 1" 3') self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="020304"\}\]' % (addr + 1, addr + size)) # Test the -o (offset) option self.runCmd('-data-read-memory-bytes -o 1 &array 3') self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="020304"\}\]' % (addr + 1, addr + size)) # Test the --thread option self.runCmd('-data-read-memory-bytes --thread 1 &array 4') self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) # Test the --thread option with an invalid value self.runCmd('-data-read-memory-bytes --thread 999 &array 4') self.expect(r'\^error') # Test the --frame option (current frame) self.runCmd('-data-read-memory-bytes --frame 0 &array 4') self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) # Test the --frame option (outer frame) self.runCmd('-data-read-memory-bytes --frame 1 &array 4') self.expect(r'\^done,memory=\[\{begin="0x[0-9a-f]+",offset="0x0+",end="0x[0-9a-f]+",contents="05060708"\}\]') # Test the --frame option with an invalid value self.runCmd('-data-read-memory-bytes --frame 999 &array 4') self.expect(r'\^error') # Test all the options at once self.runCmd('-data-read-memory-bytes --thread 1 --frame 1 -o 2 &array 2') self.expect(r'\^done,memory=\[\{begin="0x[0-9a-f]+",offset="0x0+",end="0x[0-9a-f]+",contents="0708"\}\]') # Test that an expression that references undeclared variables doesn't work self.runCmd('-data-read-memory-bytes "&undeclared_array1 + undeclared_array2[1]" 2') self.expect(r'\^error') # Test that the address argument is required self.runCmd('-data-read-memory-bytes') self.expect(r'\^error') # Test that the count argument is required self.runCmd('-data-read-memory-bytes &array') self.expect(r'\^error') # Test that the address and count arguments are required when other options are present self.runCmd('-data-read-memory-bytes --thread 1') self.expect(r'\^error') self.runCmd('-data-read-memory-bytes --thread 1 --frame 0') self.expect(r'\^error') # Test that the count argument is required when other options are present self.runCmd('-data-read-memory-bytes --thread 1 &array') self.expect(r'\^error') @lldbmi_test @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_data_list_register_names(self): """Test that 'lldb-mi --interpreter' works for -data-list-register-names.""" 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\"") # Test -data-list-register-names: try to get all registers self.runCmd("-data-list-register-names") self.expect("\^done,register-names=\[\".+?\",") # Test -data-list-register-names: try to get specified registers self.runCmd("-data-list-register-names 0") self.expect("\^done,register-names=\[\".+?\"\]") @lldbmi_test @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_data_list_register_values(self): """Test that 'lldb-mi --interpreter' works for -data-list-register-values.""" 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\"") # Test -data-list-register-values: try to get all registers self.runCmd("-data-list-register-values x") self.expect("\^done,register-values=\[{number=\"0\",value=\"0x[0-9a-f]+\"") # Test -data-list-register-values: try to get specified registers self.runCmd("-data-list-register-values x 0") self.expect("\^done,register-values=\[{number=\"0\",value=\"0x[0-9a-f]+\"}\]") @lldbmi_test @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on 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) + 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") @lldbmi_test @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_data_evaluate_expression(self): """Test that 'lldb-mi --interpreter' works for -data-evaluate-expression.""" self.spawnLldbMi(args = None) # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") line = line_number('main.cpp', '// BP_local_2d_array_test') self.runCmd('-break-insert main.cpp:%d' % line) self.expect("\^done,bkpt={number=\"1\"") self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Check 2d array self.runCmd("-data-evaluate-expression array2d") self.expect("\^done,value=\"\{\[0\] = \{\[0\] = 1, \[1\] = 2, \[2\] = 3\}, \[1\] = \{\[0\] = 4, \[1\] = 5, \[2\] = 6\}\}\"") diff --git a/lldb/test/tools/lldb-mi/symbol/TestMiSymbol.py b/lldb/test/tools/lldb-mi/symbol/TestMiSymbol.py index aec5ef13860d..6b0fe28897d7 100644 --- a/lldb/test/tools/lldb-mi/symbol/TestMiSymbol.py +++ b/lldb/test/tools/lldb-mi/symbol/TestMiSymbol.py @@ -1,72 +1,72 @@ """ Test lldb-mi -symbol-xxx commands. """ import lldb_shared import lldbmi_testcase from lldbtest import * class MiSymbolTestCase(lldbmi_testcase.MiTestCaseBase): mydir = TestBase.compute_mydir(__file__) @lldbmi_test @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_symbol_list_lines_file(self): """Test that 'lldb-mi --interpreter' works for -symbol-list-lines when file exists.""" 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 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) + 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 -symbol-list-lines works on valid data 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\"\}(,\{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 ##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 \"") # Test that -symbol-list-lines fails when file is specified using relative path self.runCmd("-symbol-list-lines ./main.cpp") self.expect("\^error,message=\"warning: No source filenames matched '\./main\.cpp'\. error: no source filenames matched any command arguments \"") # Test that -symbol-list-lines works when file is specified using absolute path import os path = os.path.join(os.getcwd(), "main.cpp") self.runCmd("-symbol-list-lines \"%s\"" % path) self.expect("\^done,lines=\[\{pc=\"0x0*%x\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"\d+\"\})+\]" % (addr, line)) # Test that -symbol-list-lines fails when file doesn't exist self.runCmd("-symbol-list-lines unknown_dir/main.cpp") self.expect("\^error,message=\"warning: No source filenames matched 'unknown_dir/main\.cpp'\. error: no source filenames matched any command arguments \"") diff --git a/lldb/tools/lldb-mi/MICmnLLDBDebugger.cpp b/lldb/tools/lldb-mi/MICmnLLDBDebugger.cpp index 91585eb263b8..8b293bd4079a 100644 --- a/lldb/tools/lldb-mi/MICmnLLDBDebugger.cpp +++ b/lldb/tools/lldb-mi/MICmnLLDBDebugger.cpp @@ -1,793 +1,883 @@ //===-- MICmnLLDBDebugger.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 "lldb/API/SBTarget.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBTypeSummary.h" +#include "lldb/API/SBTypeCategory.h" +#include "lldb/API/SBTypeNameSpecifier.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBType.h" // In-house headers: #include "MICmnLLDBDebugger.h" #include "MICmnResources.h" #include "MICmnLog.h" #include "MIDriverBase.h" #include "MICmnThreadMgrStd.h" #include "MICmnLLDBDebuggerHandleEvents.h" #include "MICmnLLDBDebugSessionInfo.h" #include "MIUtilSingletonHelper.h" +//++ ------------------------------------------------------------------------------------ +// MI private summary providers +static inline bool +MI_char_summary_provider(lldb::SBValue value, lldb::SBTypeSummaryOptions options, lldb::SBStream &stream) +{ + bool is_signed; + if (!value.IsValid()) + return false; + + lldb::SBType value_type = value.GetType(); + if(!value_type.IsValid()) + return false; + + lldb::BasicType type_code = value_type.GetBasicType(); + if (type_code == lldb::eBasicTypeSignedChar) + stream.Printf("%d %s", (int)value.GetValueAsSigned(), value.GetValue()); + else if (type_code == lldb::eBasicTypeUnsignedChar) + stream.Printf("%u %s", (unsigned)value.GetValueAsUnsigned(), value.GetValue()); + else + return false; + + return true; +} + +//++ ------------------------------------------------------------------------------------ +// MI summary helper routines +static inline bool +MI_add_summary(lldb::SBTypeCategory category, const char *typeName, lldb::SBTypeSummary::FormatCallback cb, + uint32_t options, bool regex = false) +{ + lldb::SBTypeSummary summary = lldb::SBTypeSummary::CreateWithCallback(cb, options); + return summary.IsValid() ? category.AddTypeSummary(lldb::SBTypeNameSpecifier(typeName, regex), summary) : false; +} + //++ ------------------------------------------------------------------------------------ // Details: CMICmnLLDBDebugger constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmnLLDBDebugger::CMICmnLLDBDebugger() : m_constStrThisThreadId("MI debugger event") { } //++ ------------------------------------------------------------------------------------ // Details: CMICmnLLDBDebugger destructor. // Type: Overridable. // Args: None. // Return: None. // Throws: None. //-- CMICmnLLDBDebugger::~CMICmnLLDBDebugger() { Shutdown(); } //++ ------------------------------------------------------------------------------------ // Details: Initialize resources for *this debugger object. // Type: Method. // Args: None. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::Initialize() { m_clientUsageRefCnt++; if (m_bInitialized) return MIstatus::success; bool bOk = MIstatus::success; CMIUtilString errMsg; ClrErrorDescription(); if (m_pClientDriver == nullptr) { bOk = false; errMsg = MIRSRC(IDS_LLDBDEBUGGER_ERR_CLIENTDRIVER); } // Note initialization order is important here as some resources depend on previous MI::ModuleInit(IDS_MI_INIT_ERR_LOG, bOk, errMsg); MI::ModuleInit(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); MI::ModuleInit(IDS_MI_INIT_ERR_THREADMGR, bOk, errMsg); MI::ModuleInit(IDS_MI_INIT_ERR_OUTOFBANDHANDLER, bOk, errMsg); MI::ModuleInit(IDS_MI_INIT_ERR_DEBUGSESSIONINFO, bOk, errMsg); // Note order is important here! if (bOk) lldb::SBDebugger::Initialize(); if (bOk && !InitSBDebugger()) { bOk = false; if (!errMsg.empty()) errMsg += ", "; errMsg += GetErrorDescription().c_str(); } if (bOk && !InitSBListener()) { bOk = false; if (!errMsg.empty()) errMsg += ", "; errMsg += GetErrorDescription().c_str(); } bOk = bOk && InitStdStreams(); - + bOk = bOk && RegisterMISummaryProviders(); m_bInitialized = bOk; if (!bOk && !HaveErrorDescription()) { CMIUtilString strInitError(CMIUtilString::Format(MIRSRC(IDS_MI_INIT_ERR_LLDBDEBUGGER), errMsg.c_str())); SetErrorDescription(strInitError); } return bOk; } //++ ------------------------------------------------------------------------------------ // Details: Release resources for *this debugger object. // Type: Method. // Args: None. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::Shutdown() { if (--m_clientUsageRefCnt > 0) return MIstatus::success; if (!m_bInitialized) return MIstatus::success; m_bInitialized = false; ClrErrorDescription(); bool bOk = MIstatus::success; CMIUtilString errMsg; // Explicitly delete the remote target in case MI needs to exit prematurely otherwise // LLDB debugger may hang in its Destroy() fn waiting on events lldb::SBTarget sbTarget = CMICmnLLDBDebugSessionInfo::Instance().GetTarget(); m_lldbDebugger.DeleteTarget(sbTarget); // Debug: May need this but does seem to work without it so commented out the fudge 19/06/2014 // It appears we need to wait as hang does not occur when hitting a debug breakpoint here // const std::chrono::milliseconds time( 1000 ); // std::this_thread::sleep_for( time ); lldb::SBDebugger::Destroy(m_lldbDebugger); lldb::SBDebugger::Terminate(); m_pClientDriver = nullptr; m_mapBroadcastClassNameToEventMask.clear(); m_mapIdToEventMask.clear(); // Note shutdown order is important here MI::ModuleShutdown(IDS_MI_INIT_ERR_DEBUGSESSIONINFO, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_OUTOFBANDHANDLER, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_THREADMGR, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_LOG, bOk, errMsg); if (!bOk) { SetErrorDescriptionn(MIRSRC(IDS_MI_SHTDWN_ERR_LLDBDEBUGGER), errMsg.c_str()); } return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Return the LLDB debugger instance created for this debug session. // Type: Method. // Args: None. // Return: lldb::SBDebugger & - LLDB debugger object reference. // Throws: None. //-- lldb::SBDebugger & CMICmnLLDBDebugger::GetTheDebugger() { return m_lldbDebugger; } //++ ------------------------------------------------------------------------------------ // Details: Return the LLDB listener instance created for this debug session. // Type: Method. // Args: None. // Return: lldb::SBListener & - LLDB listener object reference. // Throws: None. //-- lldb::SBListener & CMICmnLLDBDebugger::GetTheListener() { return m_lldbListener; } //++ ------------------------------------------------------------------------------------ // Details: Set the client driver that wants to use *this LLDB debugger. Call this function // prior to Initialize(). // Type: Method. // Args: vClientDriver - (R) A driver. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::SetDriver(const CMIDriverBase &vClientDriver) { m_pClientDriver = const_cast(&vClientDriver); return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Get the client driver that is use *this LLDB debugger. // Type: Method. // Args: vClientDriver - (R) A driver. // Return: CMIDriverBase & - A driver instance. // Throws: None. //-- CMIDriverBase & CMICmnLLDBDebugger::GetDriver() const { return *m_pClientDriver; } //++ ------------------------------------------------------------------------------------ // Details: Wait until all events have been handled. // This function works in pair with CMICmnLLDBDebugger::MonitorSBListenerEvents // that handles events from queue. When all events were handled and queue is // empty the MonitorSBListenerEvents notifies this function that it's ready to // go on. To synchronize them the m_mutexEventQueue and // m_conditionEventQueueEmpty are used. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- void CMICmnLLDBDebugger::WaitForHandleEvent() { std::unique_lock lock(m_mutexEventQueue); lldb::SBEvent event; if (ThreadIsActive() && m_lldbListener.PeekAtNextEvent(event)) m_conditionEventQueueEmpty.wait(lock); } //++ ------------------------------------------------------------------------------------ // Details: Check if need to rebroadcast stop event. This function will return true if // debugger is in synchronouse mode. In such case the // CMICmnLLDBDebugger::RebroadcastStopEvent should be called to rebroadcast // a new stop event (if any). // Type: Method. // Args: None. // Return: bool - True = Need to rebroadcast stop event, false = otherwise. // Throws: None. //-- bool CMICmnLLDBDebugger::CheckIfNeedToRebroadcastStopEvent() { CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); if (!rSessionInfo.GetDebugger().GetAsync()) { const bool include_expression_stops = false; m_nLastStopId = CMICmnLLDBDebugSessionInfo::Instance().GetProcess().GetStopID(include_expression_stops); return true; } return false; } //++ ------------------------------------------------------------------------------------ // Details: Rebroadcast stop event if needed. This function should be called only if the // CMICmnLLDBDebugger::CheckIfNeedToRebroadcastStopEvent() returned true. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- void CMICmnLLDBDebugger::RebroadcastStopEvent() { lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); const bool include_expression_stops = false; const uint32_t nStopId = process.GetStopID(include_expression_stops); if (m_nLastStopId != nStopId) { lldb::SBEvent event = process.GetStopEventForStopID(nStopId); process.GetBroadcaster().BroadcastEvent(event); } } //++ ------------------------------------------------------------------------------------ // Details: Initialize the LLDB Debugger object. // Type: Method. // Args: None. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::InitSBDebugger() { m_lldbDebugger = lldb::SBDebugger::Create(false); if (!m_lldbDebugger.IsValid()) { SetErrorDescription(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDDEBUGGER)); return MIstatus::failure; } m_lldbDebugger.GetCommandInterpreter().SetPromptOnQuit(false); return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Set the LLDB Debugger's std in, err and out streams. (Not implemented left // here for reference. Was called in the CMICmnLLDBDebugger::Initialize() ) // Type: Method. // Args: None. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::InitStdStreams() { // This is not required when operating the MI driver's code as it has its own // streams. Setting the Stdin for the lldbDebugger especially on LINUX will cause // another thread to run and partially consume stdin data meant for MI stdin handler // m_lldbDebugger.SetErrorFileHandle( m_pClientDriver->GetStderr(), false ); // m_lldbDebugger.SetOutputFileHandle( m_pClientDriver->GetStdout(), false ); // m_lldbDebugger.SetInputFileHandle( m_pClientDriver->GetStdin(), false ); return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Set up the events from the SBDebugger's we would like to listen to. // Type: Method. // Args: None. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::InitSBListener() { m_lldbListener = m_lldbDebugger.GetListener(); if (!m_lldbListener.IsValid()) { SetErrorDescription(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDLISTENER)); return MIstatus::failure; } const CMIUtilString strDbgId("CMICmnLLDBDebugger1"); MIuint eventMask = lldb::SBTarget::eBroadcastBitBreakpointChanged | lldb::SBTarget::eBroadcastBitModulesLoaded | lldb::SBTarget::eBroadcastBitModulesUnloaded | lldb::SBTarget::eBroadcastBitWatchpointChanged | lldb::SBTarget::eBroadcastBitSymbolsLoaded; bool bOk = RegisterForEvent(strDbgId, CMIUtilString(lldb::SBTarget::GetBroadcasterClassName()), eventMask); eventMask = lldb::SBThread::eBroadcastBitStackChanged; bOk = bOk && RegisterForEvent(strDbgId, CMIUtilString(lldb::SBThread::GetBroadcasterClassName()), eventMask); eventMask = lldb::SBProcess::eBroadcastBitStateChanged | lldb::SBProcess::eBroadcastBitInterrupt | lldb::SBProcess::eBroadcastBitSTDOUT | lldb::SBProcess::eBroadcastBitSTDERR | lldb::SBProcess::eBroadcastBitProfileData; bOk = bOk && RegisterForEvent(strDbgId, CMIUtilString(lldb::SBProcess::GetBroadcasterClassName()), eventMask); eventMask = lldb::SBCommandInterpreter::eBroadcastBitQuitCommandReceived | lldb::SBCommandInterpreter::eBroadcastBitThreadShouldExit | lldb::SBCommandInterpreter::eBroadcastBitAsynchronousOutputData | lldb::SBCommandInterpreter::eBroadcastBitAsynchronousErrorData; bOk = bOk && RegisterForEvent(strDbgId, m_lldbDebugger.GetCommandInterpreter().GetBroadcaster(), eventMask); return bOk; } //++ ------------------------------------------------------------------------------------ // Details: Register with the debugger, the SBListener, the type of events you are interested // in. Others, like commands, may have already set the mask. // Type: Method. // Args: vClientName - (R) ID of the client who wants these events set. // vBroadcasterClass - (R) The SBBroadcaster's class name. // vEventMask - (R) The mask of events to listen for. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::RegisterForEvent(const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, const MIuint vEventMask) { MIuint existingMask = 0; if (!BroadcasterGetMask(vBroadcasterClass, existingMask)) return MIstatus::failure; if (!ClientSaveMask(vClientName, vBroadcasterClass, vEventMask)) return MIstatus::failure; const char *pBroadCasterName = vBroadcasterClass.c_str(); MIuint eventMask = vEventMask; eventMask += existingMask; const MIuint result = m_lldbListener.StartListeningForEventClass(m_lldbDebugger, pBroadCasterName, eventMask); if (result == 0) { SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_STARTLISTENER), pBroadCasterName)); return MIstatus::failure; } return BroadcasterSaveMask(vBroadcasterClass, eventMask); } //++ ------------------------------------------------------------------------------------ // Details: Register with the debugger, the SBListener, the type of events you are interested // in. Others, like commands, may have already set the mask. // Type: Method. // Args: vClientName - (R) ID of the client who wants these events set. // vBroadcaster - (R) An SBBroadcaster's derived class. // vEventMask - (R) The mask of events to listen for. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::RegisterForEvent(const CMIUtilString &vClientName, const lldb::SBBroadcaster &vBroadcaster, const MIuint vEventMask) { const char *pBroadcasterName = vBroadcaster.GetName(); if (pBroadcasterName == nullptr) { SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_BROADCASTER_NAME), MIRSRC(IDS_WORD_INVALIDNULLPTR))); return MIstatus::failure; } CMIUtilString broadcasterName(pBroadcasterName); if (broadcasterName.length() == 0) { SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_BROADCASTER_NAME), MIRSRC(IDS_WORD_INVALIDEMPTY))); return MIstatus::failure; } MIuint existingMask = 0; if (!BroadcasterGetMask(broadcasterName, existingMask)) return MIstatus::failure; if (!ClientSaveMask(vClientName, broadcasterName, vEventMask)) return MIstatus::failure; MIuint eventMask = vEventMask; eventMask += existingMask; const MIuint result = m_lldbListener.StartListeningForEvents(vBroadcaster, eventMask); if (result == 0) { SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_STARTLISTENER), pBroadcasterName)); return MIstatus::failure; } return BroadcasterSaveMask(broadcasterName, eventMask); } //++ ------------------------------------------------------------------------------------ // Details: Unregister with the debugger, the SBListener, the type of events you are no // longer interested in. Others, like commands, may still remain interested so // an event may not necessarily be stopped. // Type: Method. // Args: vClientName - (R) ID of the client who no longer requires these events. // vBroadcasterClass - (R) The SBBroadcaster's class name. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::UnregisterForEvent(const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass) { MIuint clientsEventMask = 0; if (!ClientGetTheirMask(vClientName, vBroadcasterClass, clientsEventMask)) return MIstatus::failure; if (!ClientRemoveTheirMask(vClientName, vBroadcasterClass)) return MIstatus::failure; const MIuint otherClientsEventMask = ClientGetMaskForAllClients(vBroadcasterClass); MIuint newEventMask = 0; for (MIuint i = 0; i < 32; i++) { const MIuint bit = 1 << i; const MIuint clientBit = bit & clientsEventMask; const MIuint othersBit = bit & otherClientsEventMask; if ((clientBit != 0) && (othersBit == 0)) { newEventMask += clientBit; } } const char *pBroadCasterName = vBroadcasterClass.c_str(); if (!m_lldbListener.StopListeningForEventClass(m_lldbDebugger, pBroadCasterName, newEventMask)) { SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_STOPLISTENER), vClientName.c_str(), pBroadCasterName)); return MIstatus::failure; } return BroadcasterSaveMask(vBroadcasterClass, otherClientsEventMask); } //++ ------------------------------------------------------------------------------------ // Details: Given the SBBroadcaster class name retrieve it's current event mask. // Type: Method. // Args: vBroadcasterClass - (R) The SBBroadcaster's class name. // vEventMask - (W) The mask of events to listen for. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::BroadcasterGetMask(const CMIUtilString &vBroadcasterClass, MIuint &vwEventMask) const { vwEventMask = 0; if (vBroadcasterClass.empty()) { SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDBROADCASTER), vBroadcasterClass.c_str())); return MIstatus::failure; } const MapBroadcastClassNameToEventMask_t::const_iterator it = m_mapBroadcastClassNameToEventMask.find(vBroadcasterClass); if (it != m_mapBroadcastClassNameToEventMask.end()) { vwEventMask = (*it).second; } return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Remove the event mask for the specified SBBroadcaster class name. // Type: Method. // Args: vBroadcasterClass - (R) The SBBroadcaster's class name. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::BroadcasterRemoveMask(const CMIUtilString &vBroadcasterClass) { MapBroadcastClassNameToEventMask_t::const_iterator it = m_mapBroadcastClassNameToEventMask.find(vBroadcasterClass); if (it != m_mapBroadcastClassNameToEventMask.end()) { m_mapBroadcastClassNameToEventMask.erase(it); } return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Given the SBBroadcaster class name save it's current event mask. // Type: Method. // Args: vBroadcasterClass - (R) The SBBroadcaster's class name. // vEventMask - (R) The mask of events to listen for. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::BroadcasterSaveMask(const CMIUtilString &vBroadcasterClass, const MIuint vEventMask) { if (vBroadcasterClass.empty()) { SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDBROADCASTER), vBroadcasterClass.c_str())); return MIstatus::failure; } BroadcasterRemoveMask(vBroadcasterClass); MapPairBroadcastClassNameToEventMask_t pr(vBroadcasterClass, vEventMask); m_mapBroadcastClassNameToEventMask.insert(pr); return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Iterate all the clients who have registered event masks against particular // SBBroadcasters and build up the mask that is for all of them. // Type: Method. // Args: vBroadcasterClass - (R) The broadcaster to retrieve the mask for. // Return: MIuint - Event mask. // Throws: None. //-- MIuint CMICmnLLDBDebugger::ClientGetMaskForAllClients(const CMIUtilString &vBroadcasterClass) const { MIuint mask = 0; MapIdToEventMask_t::const_iterator it = m_mapIdToEventMask.begin(); while (it != m_mapIdToEventMask.end()) { const CMIUtilString &rId((*it).first); if (rId.find(vBroadcasterClass) != std::string::npos) { const MIuint clientsMask = (*it).second; mask |= clientsMask; } // Next ++it; } return mask; } //++ ------------------------------------------------------------------------------------ // Details: Given the client save its particular event requirements. // Type: Method. // Args: vClientName - (R) The Client's unique ID. // vBroadcasterClass - (R) The SBBroadcaster's class name targeted for the events. // vEventMask - (R) The mask of events to listen for. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::ClientSaveMask(const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, const MIuint vEventMask) { if (vClientName.empty()) { SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME), vClientName.c_str())); return MIstatus::failure; } CMIUtilString strId(vBroadcasterClass); strId += vClientName; ClientRemoveTheirMask(vClientName, vBroadcasterClass); MapPairIdToEventMask_t pr(strId, vEventMask); m_mapIdToEventMask.insert(pr); return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Given the client remove it's particular event requirements. // Type: Method. // Args: vClientName - (R) The Client's unique ID. // vBroadcasterClass - (R) The SBBroadcaster's class name. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::ClientRemoveTheirMask(const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass) { if (vClientName.empty()) { SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME), vClientName.c_str())); return MIstatus::failure; } CMIUtilString strId(vBroadcasterClass); strId += vClientName; const MapIdToEventMask_t::const_iterator it = m_mapIdToEventMask.find(strId); if (it != m_mapIdToEventMask.end()) { m_mapIdToEventMask.erase(it); } return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Retrieve the client's event mask used for on a particular SBBroadcaster. // Type: Method. // Args: vClientName - (R) The Client's unique ID. // vBroadcasterClass - (R) The SBBroadcaster's class name. // vwEventMask - (W) The client's mask. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::ClientGetTheirMask(const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, MIuint &vwEventMask) { vwEventMask = 0; if (vClientName.empty()) { SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME), vClientName.c_str())); return MIstatus::failure; } const CMIUtilString strId(vBroadcasterClass + vClientName); const MapIdToEventMask_t::const_iterator it = m_mapIdToEventMask.find(strId); if (it != m_mapIdToEventMask.end()) { vwEventMask = (*it).second; } SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_CLIENTNOTREGISTERED), vClientName.c_str())); return MIstatus::failure; } //++ ------------------------------------------------------------------------------------ // Details: Momentarily wait for an events being broadcast and inspect those that do // come this way. Check if the target should exit event if so start shutting // down this thread and the application. Any other events pass on to the // Out-of-band handler to further determine what kind of event arrived. // This function runs in the thread "MI debugger event". // Type: Method. // Args: vrbIsAlive - (W) False = yes exit event monitoring thread, true = continue. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmnLLDBDebugger::MonitorSBListenerEvents(bool &vrbIsAlive) { vrbIsAlive = true; // Lock the mutex of event queue // Note that it should be locked while we are in CMICmnLLDBDebugger::MonitorSBListenerEvents to // avoid a race condition with CMICmnLLDBDebugger::WaitForHandleEvent std::unique_lock lock(m_mutexEventQueue); lldb::SBEvent event; const bool bGotEvent = m_lldbListener.GetNextEvent(event); if (!bGotEvent) { // Notify that we are finished and unlock the mutex of event queue before sleeping m_conditionEventQueueEmpty.notify_one(); lock.unlock(); // Wait a bit to reduce CPU load const std::chrono::milliseconds time(1); std::this_thread::sleep_for(time); return MIstatus::success; } assert(event.IsValid()); assert(event.GetBroadcaster().IsValid()); // Debugging m_pLog->WriteLog(CMIUtilString::Format("##### An event occurred: %s", event.GetBroadcasterClass())); bool bHandledEvent = false; bool bOk = false; { // Lock Mutex before handling events so that we don't disturb a running cmd CMIUtilThreadLock lock(CMICmnLLDBDebugSessionInfo::Instance().GetSessionMutex()); bOk = CMICmnLLDBDebuggerHandleEvents::Instance().HandleEvent(event, bHandledEvent); } if (!bHandledEvent) { const CMIUtilString msg(CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_WRN_UNKNOWN_EVENT), event.GetBroadcasterClass())); m_pLog->WriteLog(msg); } if (!bOk) m_pLog->WriteLog(CMICmnLLDBDebuggerHandleEvents::Instance().GetErrorDescription()); return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The main worker method for this thread. // Type: Method. // Args: vrbIsAlive - (W) True = *this thread is working, false = thread has exited. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmnLLDBDebugger::ThreadRun(bool &vrbIsAlive) { return MonitorSBListenerEvents(vrbIsAlive); } //++ ------------------------------------------------------------------------------------ // Details: Let this thread clean up after itself. // Type: Method. // Args: // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::ThreadFinish() { return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Retrieve *this thread object's name. // Type: Overridden. // Args: None. // Return: CMIUtilString & - Text. // Throws: None. //-- const CMIUtilString & CMICmnLLDBDebugger::ThreadGetName() const { return m_constStrThisThreadId; } + +//++ ------------------------------------------------------------------------------------ +// Details: Loads lldb-mi formatters +// Type: Method. +// Args: None. +// Return: true - Functionality succeeded. +// false - Functionality failed. +// Throws: None. +//-- +bool +CMICmnLLDBDebugger::LoadMIFormatters(lldb::SBTypeCategory miCategory) +{ + if (!MI_add_summary(miCategory, "char", MI_char_summary_provider, + lldb::eTypeOptionHideValue | lldb::eTypeOptionSkipPointers)) + return false; + + if (!MI_add_summary(miCategory, "unsigned char", MI_char_summary_provider, + lldb::eTypeOptionHideValue | lldb::eTypeOptionSkipPointers)) + return false; + + if (!MI_add_summary(miCategory, "signed char", MI_char_summary_provider, + lldb::eTypeOptionHideValue | lldb::eTypeOptionSkipPointers)) + return false; + + return true; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Registers lldb-mi custom summary providers +// Type: Method. +// Args: None. +// Return: true - Functionality succeeded. +// false - Functionality failed. +// Throws: None. +//-- +bool +CMICmnLLDBDebugger::RegisterMISummaryProviders() +{ + static const char* miCategoryName = "lldb-mi"; + lldb::SBTypeCategory miCategory = m_lldbDebugger.CreateCategory(miCategoryName); + if (!miCategory.IsValid()) + return false; + + if (!LoadMIFormatters(miCategory)) + { + m_lldbDebugger.DeleteCategory(miCategoryName); + return false; + } + miCategory.SetEnabled(true); + return true; +} diff --git a/lldb/tools/lldb-mi/MICmnLLDBDebugger.h b/lldb/tools/lldb-mi/MICmnLLDBDebugger.h index 5451f91068b0..83bc3c06bf69 100644 --- a/lldb/tools/lldb-mi/MICmnLLDBDebugger.h +++ b/lldb/tools/lldb-mi/MICmnLLDBDebugger.h @@ -1,113 +1,114 @@ //===-- MICmnLLDBDebugger.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 #include #include #include "lldb/API/SBDebugger.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBEvent.h" // In-house headers: #include "MICmnBase.h" #include "MIUtilThreadBaseStd.h" #include "MIUtilSingletonBase.h" // Declarations: class CMIDriverBase; class CMICmnLLDBDebuggerHandleEvents; //++ ============================================================================ // Details: MI proxy/adapter for the LLDB public SBDebugger API. The CMIDriver // requires *this object. Command classes make calls on *this object // to facilitate their work effort. The instance runs in its own worker // thread. // A singleton class. //-- class CMICmnLLDBDebugger : public CMICmnBase, public CMIUtilThreadActiveObjBase, public MI::ISingleton { friend class MI::ISingleton; // Methods: public: bool Initialize() override; bool Shutdown() override; bool SetDriver(const CMIDriverBase &vClientDriver); CMIDriverBase &GetDriver() const; lldb::SBDebugger &GetTheDebugger(); lldb::SBListener &GetTheListener(); void WaitForHandleEvent(); bool CheckIfNeedToRebroadcastStopEvent(); void RebroadcastStopEvent(); // MI Commands can use these functions to listen for events they require bool RegisterForEvent(const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, const MIuint vEventMask); bool UnregisterForEvent(const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass); bool RegisterForEvent(const CMIUtilString &vClientName, const lldb::SBBroadcaster &vBroadcaster, const MIuint vEventMask); bool UnregisterForEvent(const CMIUtilString &vClientName, const lldb::SBBroadcaster &vBroadcaster); // Overridden: public: // From CMIUtilThreadActiveObjBase const CMIUtilString &ThreadGetName() const override; // Overridden: protected: // From CMIUtilThreadActiveObjBase bool ThreadRun(bool &vrIsAlive) override; bool ThreadFinish() override; // Typedefs: private: typedef std::map MapBroadcastClassNameToEventMask_t; typedef std::pair MapPairBroadcastClassNameToEventMask_t; typedef std::map MapIdToEventMask_t; typedef std::pair MapPairIdToEventMask_t; // Methods: private: /* ctor */ CMICmnLLDBDebugger(); /* ctor */ CMICmnLLDBDebugger(const CMICmnLLDBDebugger &); void operator=(const CMICmnLLDBDebugger &); bool InitSBDebugger(); bool InitSBListener(); bool InitStdStreams(); bool MonitorSBListenerEvents(bool &vrbYesExit); bool BroadcasterGetMask(const CMIUtilString &vBroadcasterClass, MIuint &vEventMask) const; bool BroadcasterRemoveMask(const CMIUtilString &vBroadcasterClass); bool BroadcasterSaveMask(const CMIUtilString &vBroadcasterClass, const MIuint vEventMask); MIuint ClientGetMaskForAllClients(const CMIUtilString &vBroadcasterClass) const; bool ClientSaveMask(const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, const MIuint vEventMask); bool ClientRemoveTheirMask(const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass); bool ClientGetTheirMask(const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, MIuint &vwEventMask); - + bool LoadMIFormatters(lldb::SBTypeCategory miCategory); + bool RegisterMISummaryProviders(); // Overridden: private: // From CMICmnBase /* dtor */ ~CMICmnLLDBDebugger() override; // Attributes: private: CMIDriverBase *m_pClientDriver; // The driver that wants to use *this LLDB debugger lldb::SBDebugger m_lldbDebugger; // SBDebugger is the primordial object that creates SBTargets and provides access to them lldb::SBListener m_lldbListener; // API clients can register its own listener to debugger events const CMIUtilString m_constStrThisThreadId; MapBroadcastClassNameToEventMask_t m_mapBroadcastClassNameToEventMask; MapIdToEventMask_t m_mapIdToEventMask; std::mutex m_mutexEventQueue; std::condition_variable m_conditionEventQueueEmpty; uint32_t m_nLastStopId; }; diff --git a/lldb/tools/lldb-mi/MICmnLLDBUtilSBValue.cpp b/lldb/tools/lldb-mi/MICmnLLDBUtilSBValue.cpp index c9ff2c2cb960..54dabd4e1010 100644 --- a/lldb/tools/lldb-mi/MICmnLLDBUtilSBValue.cpp +++ b/lldb/tools/lldb-mi/MICmnLLDBUtilSBValue.cpp @@ -1,664 +1,516 @@ //===-- MICmnLLDBUtilSBValue.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 #include "lldb/API/SBTypeSummary.h" // In-house headers: #include "MICmnLLDBUtilSBValue.h" #include "MICmnLLDBDebugSessionInfo.h" #include "MICmnMIValueConst.h" #include "MICmnMIValueTuple.h" #include "MIUtilString.h" //++ ------------------------------------------------------------------------------------ // Details: CMICmnLLDBUtilSBValue constructor. // Type: Method. // Args: vrValue - (R) The LLDB value object. // vbHandleCharType - (R) True = Yes return text molding to char type, // False = just return data. // Return: None. // Throws: None. //-- CMICmnLLDBUtilSBValue::CMICmnLLDBUtilSBValue(const lldb::SBValue &vrValue, const bool vbHandleCharType /* = false */, const bool vbHandleArrayType /* = true */) : m_rValue(const_cast(vrValue)) , m_pUnkwn("??") , m_pComposite("{...}") , m_bHandleCharType(vbHandleCharType) , m_bHandleArrayType(vbHandleArrayType) { m_bValidSBValue = m_rValue.IsValid(); } //++ ------------------------------------------------------------------------------------ // Details: CMICmnLLDBUtilSBValue destructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmnLLDBUtilSBValue::~CMICmnLLDBUtilSBValue() { } //++ ------------------------------------------------------------------------------------ // Details: Retrieve from the LLDB SB Value object the name of the variable. If the name // is invalid (or the SBValue object invalid) then "??" is returned. // Type: Method. // Args: None. // Return: CMIUtilString - Name of the variable or "??" for unknown. // Throws: None. //-- CMIUtilString CMICmnLLDBUtilSBValue::GetName() const { const char *pName = m_bValidSBValue ? m_rValue.GetName() : nullptr; const CMIUtilString text((pName != nullptr) ? pName : m_pUnkwn); return text; } //++ ------------------------------------------------------------------------------------ // Details: Retrieve from the LLDB SB Value object the value of the variable described in // text. If the value is invalid (or the SBValue object invalid) then "??" is // returned. // Type: Method. // Args: None. // Return: CMIUtilString - Text description of the variable's value or "??". // Throws: None. //-- CMIUtilString CMICmnLLDBUtilSBValue::GetValue(const bool vbExpandAggregates /* = false */) const { if (!m_bValidSBValue) return m_pUnkwn; CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); bool bPrintExpandAggregates = false; bPrintExpandAggregates = rSessionInfo.SharedDataRetrieve(rSessionInfo.m_constStrPrintExpandAggregates, bPrintExpandAggregates) && bPrintExpandAggregates; const bool bHandleArrayTypeAsSimple = m_bHandleArrayType && !vbExpandAggregates && !bPrintExpandAggregates; CMIUtilString value; const bool bIsSimpleValue = GetSimpleValue(bHandleArrayTypeAsSimple, value); if (bIsSimpleValue) return value; if (!vbExpandAggregates && !bPrintExpandAggregates) return m_pComposite; bool bPrintAggregateFieldNames = false; bPrintAggregateFieldNames = !rSessionInfo.SharedDataRetrieve(rSessionInfo.m_constStrPrintAggregateFieldNames, bPrintAggregateFieldNames) || bPrintAggregateFieldNames; CMICmnMIValueTuple miValueTuple; const bool bOk = GetCompositeValue(bPrintAggregateFieldNames, miValueTuple); if (!bOk) return m_pUnkwn; value = miValueTuple.GetString(); return value; } //++ ------------------------------------------------------------------------------------ // Details: Retrieve from the LLDB SB Value object the value of the variable described in // text if it has a simple format (not composite). // Type: Method. // Args: vwrValue - (W) The SBValue in a string format. // Return: MIstatus::success - Function succeeded. // MIstatus::failure - Function failed. // Throws: None. //-- bool CMICmnLLDBUtilSBValue::GetSimpleValue(const bool vbHandleArrayType, CMIUtilString &vwrValue) const { const MIuint nChildren = m_rValue.GetNumChildren(); if (nChildren == 0) { - if (m_bHandleCharType && IsCharType()) - { - vwrValue = GetSimpleValueChar(); - return MIstatus::success; - } - else - { - const char *pValue = m_rValue.GetValue(); - vwrValue = pValue != nullptr ? pValue : m_pUnkwn; - return MIstatus::success; - } + vwrValue = GetValueSummary(!m_bHandleCharType && IsCharType(), m_pUnkwn); + return MIstatus::success; } else if (IsPointerType()) { - if (m_bHandleCharType && IsPointeeCharType()) - { - vwrValue = GetSimpleValueCStringPointer(); - return MIstatus::success; - } - else - { - const char *pValue = m_rValue.GetValue(); - vwrValue = pValue != nullptr ? pValue : m_pUnkwn; - return MIstatus::success; - } + vwrValue = GetValueSummary(!m_bHandleCharType && IsPointeeCharType(), m_pUnkwn); + return MIstatus::success; } else if (IsArrayType()) { CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); bool bPrintCharArrayAsString = false; bPrintCharArrayAsString = rSessionInfo.SharedDataRetrieve(rSessionInfo.m_constStrPrintCharArrayAsString, bPrintCharArrayAsString) && bPrintCharArrayAsString; if (bPrintCharArrayAsString && m_bHandleCharType && IsFirstChildCharType()) { - vwrValue = GetSimpleValueCStringArray(); + vwrValue = GetValueSummary(false); return MIstatus::success; } else if (vbHandleArrayType) { vwrValue = CMIUtilString::Format("[%u]", nChildren); return MIstatus::success; } } else { // Treat composite value which has registered summary // (for example with AddCXXSummary) as simple value - vwrValue = GetValueSummary(); + vwrValue = GetValueSummary(false); if (!vwrValue.empty()) return MIstatus::success; } // Composite variable type i.e. struct return MIstatus::failure; } -//++ ------------------------------------------------------------------------------------ -// Details: Retrieve from the LLDB SB Value object the char value of the variable. -// Type: Method. -// Args: None. -// Return: CMIUtilString - The char value of the variable. -// Throws: None. -//-- -CMIUtilString -CMICmnLLDBUtilSBValue::GetSimpleValueChar() const -{ - const CMIUtilString& summary = GetValueSummary(); - if (!summary.empty()) - return summary; - - const uint64_t value = m_rValue.GetValueAsUnsigned(); - if (value == 0) - { - const uint64_t nFailValue = 1; - if (nFailValue == m_rValue.GetValueAsUnsigned(nFailValue)) - return m_pUnkwn; - } - - const lldb::BasicType eType = m_rValue.GetType().GetBasicType(); - switch (eType) - { - default: - assert(0 && "value must be a char type"); - case lldb::eBasicTypeChar: - case lldb::eBasicTypeSignedChar: - case lldb::eBasicTypeUnsignedChar: - { - const CMIUtilString prefix(CMIUtilString::ConvertToPrintableASCII((char)value)); - return CMIUtilString::Format("%" PRIu8 " '%s'", (uint8_t)value, prefix.c_str()); - } - case lldb::eBasicTypeChar16: - { - const CMIUtilString prefix(CMIUtilString::ConvertToPrintableASCII((char16_t)value)); - return CMIUtilString::Format("U+%04" PRIx16 " u'%s'", (uint16_t)value, prefix.c_str()); - } - case lldb::eBasicTypeChar32: - { - const CMIUtilString prefix(CMIUtilString::ConvertToPrintableASCII((char32_t)value)); - return CMIUtilString::Format("U+%08" PRIx32 " U'%s'", (uint32_t)value, prefix.c_str()); - } - } -} - -//++ ------------------------------------------------------------------------------------ -// Details: Retrieve from the LLDB SB Value object of type char* the c-string value. -// Type: Method. -// Args: None. -// Return: CMIUtilString - The c-string value of the variable. -// Throws: None. -//-- -CMIUtilString -CMICmnLLDBUtilSBValue::GetSimpleValueCStringPointer() const -{ - const CMIUtilString& summary = GetValueSummary(); - if (!summary.empty()) - return summary; - - const char *value = m_rValue.GetValue(); - if (value == nullptr) - return m_pUnkwn; - - lldb::SBValue child = m_rValue.GetChildAtIndex(0); - const lldb::BasicType eType = child.GetType().GetBasicType(); - switch (eType) - { - default: - assert(0 && "child must be a char type"); - case lldb::eBasicTypeChar: - case lldb::eBasicTypeSignedChar: - case lldb::eBasicTypeUnsignedChar: - { - const CMIUtilString prefix(ReadCStringFromHostMemory(child)); - // Note code that has const in will not show the text suffix to the string pointer - // i.e. const char * pMyStr = "blah"; ==> "0x00007000"" <-- Eclipse shows this - // but char * pMyStr = "blah"; ==> "0x00007000" "blah"" <-- Eclipse shows this - return CMIUtilString::Format("%s \"%s\"", value, prefix.c_str()); - } - case lldb::eBasicTypeChar16: - { - const CMIUtilString prefix(ReadCStringFromHostMemory(child)); - return CMIUtilString::Format("%s u\"%s\"", value, prefix.c_str()); - } - case lldb::eBasicTypeChar32: - { - const CMIUtilString prefix(ReadCStringFromHostMemory(child)); - return CMIUtilString::Format("%s U\"%s\"", value, prefix.c_str()); - } - } -} - -//++ ------------------------------------------------------------------------------------ -// Details: Retrieve from the LLDB SB Value object of type char[] the c-string value. -// Type: Method. -// Args: None. -// Return: CMIUtilString - The c-string value of the variable. -// Throws: None. -//-- -CMIUtilString -CMICmnLLDBUtilSBValue::GetSimpleValueCStringArray() const -{ - const CMIUtilString& summary = GetValueSummary(); - if (!summary.empty()) - return summary; - - const MIuint nChildren = m_rValue.GetNumChildren(); - lldb::SBValue child = m_rValue.GetChildAtIndex(0); - const lldb::BasicType eType = child.GetType().GetBasicType(); - switch (eType) - { - default: - assert(0 && "value must be a char[] type"); - case lldb::eBasicTypeChar: - case lldb::eBasicTypeSignedChar: - case lldb::eBasicTypeUnsignedChar: - { - const CMIUtilString prefix(ReadCStringFromHostMemory(m_rValue, nChildren)); - // TODO: to match char* it should be the following - // return CMIUtilString::Format("[%u] \"%s\"", nChildren, prefix.c_str()); - return CMIUtilString::Format("\"%s\"", prefix.c_str()); - } - case lldb::eBasicTypeChar16: - { - const CMIUtilString prefix(ReadCStringFromHostMemory(m_rValue, nChildren)); - return CMIUtilString::Format("u\"%s\"", prefix.c_str()); - } - case lldb::eBasicTypeChar32: - { - const CMIUtilString prefix(ReadCStringFromHostMemory(m_rValue, nChildren)); - return CMIUtilString::Format("U\"%s\"", prefix.c_str()); - } - } -} - bool CMICmnLLDBUtilSBValue::GetCompositeValue(const bool vbPrintFieldNames, CMICmnMIValueTuple &vwrMiValueTuple, const MIuint vnDepth /* = 1 */) const { const MIuint nMaxDepth = 10; const MIuint nChildren = m_rValue.GetNumChildren(); for (MIuint i = 0; i < nChildren; ++i) { const lldb::SBValue member = m_rValue.GetChildAtIndex(i); const CMICmnLLDBUtilSBValue utilMember(member, m_bHandleCharType, m_bHandleArrayType); const bool bHandleArrayTypeAsSimple = false; CMIUtilString value; const bool bIsSimpleValue = utilMember.GetSimpleValue(bHandleArrayTypeAsSimple, value); if (bIsSimpleValue) { // OK. Value is simple (not composite) and was successfully got } else if (vnDepth < nMaxDepth) { // Need to get value from composite type CMICmnMIValueTuple miValueTuple; const bool bOk = utilMember.GetCompositeValue(vbPrintFieldNames, miValueTuple, vnDepth + 1); if (!bOk) // Can't obtain composite type value = m_pUnkwn; else // OK. Value is composite and was successfully got value = miValueTuple.GetString(); } else { // Need to get value from composite type, but vnMaxDepth is reached value = m_pComposite; } const bool bNoQuotes = true; const CMICmnMIValueConst miValueConst(value, bNoQuotes); if (vbPrintFieldNames) { const bool bUseSpacing = true; const CMICmnMIValueResult miValueResult(utilMember.GetName(), miValueConst, bUseSpacing); vwrMiValueTuple.Add(miValueResult, bUseSpacing); } else { const bool bUseSpacing = false; vwrMiValueTuple.Add(miValueConst, bUseSpacing); } } return MIstatus::success; } +// Returns value or value + summary, depending on valueOnly parameter value. +// If result is an empty string returns failVal. CMIUtilString -CMICmnLLDBUtilSBValue::GetValueSummary() const -{ - CMIUtilString valSummary; - if (m_rValue.IsValid()) +CMICmnLLDBUtilSBValue::GetValueSummary(bool valueOnly, const CMIUtilString& failVal) const +{ + if (!m_rValue.IsValid()) + return failVal; + + CMIUtilString value, valSummary; + const char* c_value = m_rValue.GetValue(); + if (valueOnly) + return c_value == nullptr ? failVal : c_value; + + const char* c_summary = m_rValue.GetSummary(); + if (c_value) + value = c_value; + else if (c_summary == nullptr) + return failVal; + + if (c_summary && c_summary[0]) { -#ifndef LLDB_DISABLE_PYTHON - const char *c_str = m_rValue.GetSummary(); - if (c_str && c_str[0]) + valSummary = c_summary; + lldb::SBTypeSummary summary = m_rValue.GetTypeSummary(); + if (summary.IsValid() && summary.DoesPrintValue(m_rValue) && !value.empty()) { - valSummary = c_str; - lldb::SBTypeSummary summary = m_rValue.GetTypeSummary(); - if (summary.IsValid() && summary.DoesPrintValue(m_rValue)) - { - c_str = m_rValue.GetValue(); - if (c_str && c_str[0]) - { - valSummary.insert(0, std::string(c_str) + " "); - } - } + valSummary.insert(0, value + " "); } -#endif + return valSummary; } - return valSummary; + // no summary - return just value + return value; } //++ ------------------------------------------------------------------------------------ // Details: Check that basic type is a char type. Char type can be signed or unsigned. // Type: Static. // Args: eType - type to check // Return: bool - True = Yes is a char type, false = some other type. // Throws: None. //-- bool CMICmnLLDBUtilSBValue::IsCharBasicType(lldb::BasicType eType) { switch (eType) { case lldb::eBasicTypeChar: case lldb::eBasicTypeSignedChar: case lldb::eBasicTypeUnsignedChar: case lldb::eBasicTypeChar16: case lldb::eBasicTypeChar32: return true; default: return false; } } //++ ------------------------------------------------------------------------------------ // Details: Retrieve the flag stating whether this value object is a char type or some // other type. Char type can be signed or unsigned. // Type: Method. // Args: None. // Return: bool - True = Yes is a char type, false = some other type. // Throws: None. //-- bool CMICmnLLDBUtilSBValue::IsCharType() const { const lldb::BasicType eType = m_rValue.GetType().GetBasicType(); return IsCharBasicType(eType); } //++ ------------------------------------------------------------------------------------ // Details: Retrieve the flag stating whether first child value object of *this object is // a char type or some other type. Returns false if there are not children. Char // type can be signed or unsigned. // Type: Method. // Args: None. // Return: bool - True = Yes is a char type, false = some other type. // Throws: None. //-- bool CMICmnLLDBUtilSBValue::IsFirstChildCharType() const { const MIuint nChildren = m_rValue.GetNumChildren(); // Is it a basic type if (nChildren == 0) return false; const lldb::SBValue member = m_rValue.GetChildAtIndex(0); const CMICmnLLDBUtilSBValue utilValue(member); return utilValue.IsCharType(); } //++ ------------------------------------------------------------------------------------ // Details: Retrieve the flag stating whether pointee object of *this object is // a char type or some other type. Returns false if there are not children. Char // type can be signed or unsigned. // Type: Method. // Args: None. // Return: bool - True = Yes is a char type, false = some other type. // Throws: None. //-- bool CMICmnLLDBUtilSBValue::IsPointeeCharType() const { const MIuint nChildren = m_rValue.GetNumChildren(); // Is it a basic type if (nChildren == 0) return false; const lldb::BasicType eType = m_rValue.GetType().GetPointeeType().GetBasicType(); return IsCharBasicType(eType); } //++ ------------------------------------------------------------------------------------ // Details: Retrieve the flag stating whether this value object is a integer type or some // other type. Char type can be signed or unsigned and short or long/very long. // Type: Method. // Args: None. // Return: bool - True = Yes is a integer type, false = some other type. // Throws: None. //-- bool CMICmnLLDBUtilSBValue::IsIntegerType() const { const lldb::BasicType eType = m_rValue.GetType().GetBasicType(); return ((eType == lldb::eBasicTypeShort) || (eType == lldb::eBasicTypeUnsignedShort) || (eType == lldb::eBasicTypeInt) || (eType == lldb::eBasicTypeUnsignedInt) || (eType == lldb::eBasicTypeLong) || (eType == lldb::eBasicTypeUnsignedLong) || (eType == lldb::eBasicTypeLongLong) || (eType == lldb::eBasicTypeUnsignedLongLong) || (eType == lldb::eBasicTypeInt128) || (eType == lldb::eBasicTypeUnsignedInt128)); } //++ ------------------------------------------------------------------------------------ // Details: Retrieve the flag stating whether this value object is a pointer type or some // other type. // Type: Method. // Args: None. // Return: bool - True = Yes is a pointer type, false = some other type. // Throws: None. //-- bool CMICmnLLDBUtilSBValue::IsPointerType() const { return m_rValue.GetType().IsPointerType(); } //++ ------------------------------------------------------------------------------------ // Details: Retrieve the flag stating whether this value object is an array type or some // other type. // Type: Method. // Args: None. // Return: bool - True = Yes is an array type, false = some other type. // Throws: None. //-- bool CMICmnLLDBUtilSBValue::IsArrayType() const { return m_rValue.GetType().IsArrayType(); } //++ ------------------------------------------------------------------------------------ // Details: Retrieve the C string data of value object by read the memory where the // variable is held. // Type: Method. // Args: vrValue - (R) LLDB SBValue variable object. // Return: CMIUtilString - Text description of the variable's value. // Throws: None. //-- template CMIUtilString CMICmnLLDBUtilSBValue::ReadCStringFromHostMemory(lldb::SBValue &vrValue, const MIuint vnMaxLen) const { std::string result; lldb::addr_t addr = vrValue.GetLoadAddress(), end_addr = addr + vnMaxLen * sizeof(charT); lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); lldb::SBError error; while (addr < end_addr) { charT ch; const MIuint64 nReadBytes = process.ReadMemory(addr, &ch, sizeof(ch), error); if (error.Fail() || nReadBytes != sizeof(ch)) return m_pUnkwn; else if (ch == 0) break; result.append(CMIUtilString::ConvertToPrintableASCII(ch, true /* bEscapeQuotes */)); addr += sizeof(ch); } return result; } //++ ------------------------------------------------------------------------------------ // Details: Retrieve the state of the value object's name. // Type: Method. // Args: None. // Return: bool - True = yes name is indeterminate, false = name is valid. // Throws: None. //-- bool CMICmnLLDBUtilSBValue::IsNameUnknown() const { const CMIUtilString name(GetName()); return (name == m_pUnkwn); } //++ ------------------------------------------------------------------------------------ // Details: Retrieve the state of the value object's value data. // Type: Method. // Args: None. // Return: bool - True = yes value is indeterminate, false = value valid. // Throws: None. //-- bool CMICmnLLDBUtilSBValue::IsValueUnknown() const { const CMIUtilString value(GetValue()); return (value == m_pUnkwn); } //++ ------------------------------------------------------------------------------------ // Details: Retrieve the value object's type name if valid. // Type: Method. // Args: None. // Return: CMIUtilString - The type name or "??". // Throws: None. //-- CMIUtilString CMICmnLLDBUtilSBValue::GetTypeName() const { const char *pName = m_bValidSBValue ? m_rValue.GetTypeName() : nullptr; const CMIUtilString text((pName != nullptr) ? pName : m_pUnkwn); return text; } //++ ------------------------------------------------------------------------------------ // Details: Retrieve the value object's display type name if valid. // Type: Method. // Args: None. // Return: CMIUtilString - The type name or "??". // Throws: None. //-- CMIUtilString CMICmnLLDBUtilSBValue::GetTypeNameDisplay() const { const char *pName = m_bValidSBValue ? m_rValue.GetDisplayTypeName() : nullptr; const CMIUtilString text((pName != nullptr) ? pName : m_pUnkwn); return text; } //++ ------------------------------------------------------------------------------------ // Details: Retrieve whether the value object's is valid or not. // Type: Method. // Args: None. // Return: bool - True = valid, false = not valid. // Throws: None. //-- bool CMICmnLLDBUtilSBValue::IsValid() const { return m_bValidSBValue; } //++ ------------------------------------------------------------------------------------ // Details: Retrieve the value object' has a name. A value object can be valid but still // have no name which suggest it is not a variable. // Type: Method. // Args: None. // Return: bool - True = valid, false = not valid. // Throws: None. //-- bool CMICmnLLDBUtilSBValue::HasName() const { bool bHasAName = false; const char *pName = m_bValidSBValue ? m_rValue.GetDisplayTypeName() : nullptr; if (pName != nullptr) { bHasAName = (CMIUtilString(pName).length() > 0); } return bHasAName; } //++ ------------------------------------------------------------------------------------ // Details: Determine if the value object' represents a LLDB variable i.e. "$0". // Type: Method. // Args: None. // Return: bool - True = Yes LLDB variable, false = no. // Throws: None. //-- bool CMICmnLLDBUtilSBValue::IsLLDBVariable() const { return (GetName().at(0) == '$'); } diff --git a/lldb/tools/lldb-mi/MICmnLLDBUtilSBValue.h b/lldb/tools/lldb-mi/MICmnLLDBUtilSBValue.h index 66e5870145ff..36264da1e16d 100644 --- a/lldb/tools/lldb-mi/MICmnLLDBUtilSBValue.h +++ b/lldb/tools/lldb-mi/MICmnLLDBUtilSBValue.h @@ -1,72 +1,69 @@ //===-- MICmnLLDBUtilSBValue.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 "lldb/API/SBValue.h" // In-house headers: #include "MIDataTypes.h" #include "MICmnMIValueTuple.h" // Declarations: class CMIUtilString; //++ ============================================================================ // Details: Utility helper class to lldb::SBValue. Using a lldb::SBValue extract // value object information to help form verbose debug information. //-- class CMICmnLLDBUtilSBValue { // Methods: public: /* ctor */ CMICmnLLDBUtilSBValue(const lldb::SBValue &vrValue, const bool vbHandleCharType = false, const bool vbHandleArrayType = true); /* dtor */ ~CMICmnLLDBUtilSBValue(); // CMIUtilString GetName() const; CMIUtilString GetValue(const bool vbExpandAggregates = false) const; CMIUtilString GetTypeName() const; CMIUtilString GetTypeNameDisplay() const; bool IsCharType() const; bool IsFirstChildCharType() const; bool IsPointeeCharType() const; bool IsIntegerType() const; bool IsPointerType() const; bool IsArrayType() const; bool IsLLDBVariable() const; bool IsNameUnknown() const; bool IsValueUnknown() const; bool IsValid() const; bool HasName() const; // Methods: private: template CMIUtilString ReadCStringFromHostMemory(lldb::SBValue &vrValue, const MIuint vnMaxLen = UINT32_MAX) const; bool GetSimpleValue(const bool vbHandleArrayType, CMIUtilString &vrValue) const; - CMIUtilString GetSimpleValueChar() const; - CMIUtilString GetSimpleValueCStringPointer() const; - CMIUtilString GetSimpleValueCStringArray() const; bool GetCompositeValue(const bool vbPrintFieldNames, CMICmnMIValueTuple &vwrMiValueTuple, const MIuint vnDepth = 1) const; - CMIUtilString GetValueSummary() const; + CMIUtilString GetValueSummary(bool valueOnly, const CMIUtilString& failVal = CMIUtilString()) const; // Statics: private: static bool IsCharBasicType(lldb::BasicType eType); // Attributes: private: lldb::SBValue &m_rValue; const char *m_pUnkwn; const char *m_pComposite; bool m_bValidSBValue; // True = SBValue is a valid object, false = not valid. bool m_bHandleCharType; // True = Yes return text molding to char type, false = just return data. bool m_bHandleArrayType; // True = Yes return special stub for array type, false = just return data. };