Page MenuHomePhabricator

No OneTemporary

File Metadata

Created
Jan 24 2020, 4:09 PM
This file is larger than 256 KB, so syntax highlighting was skipped.
Index: lldb/trunk/test/functionalities/inferior-assert/TestInferiorAssert.py
===================================================================
--- lldb/trunk/test/functionalities/inferior-assert/TestInferiorAssert.py (revision 219543)
+++ lldb/trunk/test/functionalities/inferior-assert/TestInferiorAssert.py (revision 219544)
@@ -1,264 +1,267 @@
"""Test that lldb functions correctly after the inferior has asserted."""
import os, time
import unittest2
import lldb, lldbutil
from lldbtest import *
class AssertingInferiorTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@unittest2.expectedFailure("rdar://15367233")
def test_inferior_asserting_dsym(self):
"""Test that lldb reliably catches the inferior asserting (command)."""
self.buildDsym()
self.inferior_asserting()
@expectedFailurei386 # llvm.org/pr17384: lldb needs to be aware of linux-vdso.so to unwind stacks properly'
@expectedFailureDarwin("rdar://15367233")
def test_inferior_asserting_dwarf(self):
"""Test that lldb reliably catches the inferior asserting (command)."""
self.buildDwarf()
self.inferior_asserting()
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
def test_inferior_asserting_registers_dsym(self):
"""Test that lldb reliably reads registers from the inferior after asserting (command)."""
self.buildDsym()
self.inferior_asserting_registers()
def test_inferior_asserting_register_dwarf(self):
"""Test that lldb reliably reads registers from the inferior after asserting (command)."""
self.buildDwarf()
self.inferior_asserting_registers()
@expectedFailurei386 # llvm.org/pr17384: lldb needs to be aware of linux-vdso.so to unwind stacks properly
@expectedFailureFreeBSD('llvm.org/pr18533') # PC in __assert frame is outside of function
@expectedFailureLinux('') # PC in __GI___assert_fail frame is just after the function (this is a no-return so there is no epilogue afterwards)
def test_inferior_asserting_disassemble(self):
"""Test that lldb reliably disassembles frames after asserting (command)."""
self.buildDefault()
self.inferior_asserting_disassemble()
@python_api_test
def test_inferior_asserting_python(self):
"""Test that lldb reliably catches the inferior asserting (Python API)."""
self.buildDefault()
self.inferior_asserting_python()
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@unittest2.expectedFailure("rdar://15367233")
def test_inferior_asserting_expr(self):
"""Test that the lldb expression interpreter can read from the inferior after asserting (command)."""
self.buildDsym()
self.inferior_asserting_expr()
@expectedFailurei386 # llvm.org/pr17384: lldb needs to be aware of linux-vdso.so to unwind stacks properly
@unittest2.expectedFailure("rdar://15367233")
def test_inferior_asserting_expr(self):
"""Test that the lldb expression interpreter can read from the inferior after asserting (command)."""
self.buildDwarf()
self.inferior_asserting_expr()
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@unittest2.expectedFailure("rdar://15367233")
def test_inferior_asserting_step(self):
"""Test that lldb functions correctly after stepping through a call to assert()."""
self.buildDsym()
self.inferior_asserting_step()
@expectedFailurei386 # llvm.org/pr17384: lldb needs to be aware of linux-vdso.so to unwind stacks properly
@expectedFailureDarwin("rdar://15367233")
def test_inferior_asserting_step(self):
"""Test that lldb functions correctly after stepping through a call to assert()."""
self.buildDwarf()
self.inferior_asserting_step()
def set_breakpoint(self, line):
lldbutil.run_break_set_by_file_and_line (self, "main.c", line, num_expected_locations=1, loc_exact=True)
def check_stop_reason(self):
stop_reason = 'stop reason = signal SIGABRT'
# The stop reason of the thread should be an abort signal or exception.
self.expect("thread list", STOPPED_DUE_TO_ASSERT,
substrs = ['stopped',
stop_reason])
return stop_reason
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Find the line number of the call to assert.
self.line = line_number('main.c', '// Assert here.')
def inferior_asserting(self):
"""Inferior asserts upon launching; lldb should catch the event and stop."""
exe = os.path.join(os.getcwd(), "a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
self.runCmd("run", RUN_SUCCEEDED)
stop_reason = self.check_stop_reason()
# And it should report a backtrace that includes the assert site.
self.expect("thread backtrace all",
substrs = [stop_reason, 'main', 'argc', 'argv'])
# And it should report the correct line number.
self.expect("thread backtrace all",
substrs = [stop_reason,
'main.c:%d' % self.line])
def inferior_asserting_python(self):
"""Inferior asserts upon launching; lldb should catch the event and stop."""
exe = os.path.join(os.getcwd(), "a.out")
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)
# Now launch the process, and do not stop at entry point.
# Both argv and envp are null.
process = target.LaunchSimple (None, None, self.get_process_working_directory())
if process.GetState() != lldb.eStateStopped:
self.fail("Process should be in the 'stopped' state, "
"instead the actual state is: '%s'" %
lldbutil.state_type_to_str(process.GetState()))
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal)
if not thread:
self.fail("Fail to stop the thread upon assert")
if self.TraceOn():
lldbutil.print_stacktrace(thread)
def inferior_asserting_registers(self):
"""Test that lldb can read registers after asserting."""
exe = os.path.join(os.getcwd(), "a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
self.runCmd("run", RUN_SUCCEEDED)
self.check_stop_reason()
# lldb should be able to read from registers from the inferior after asserting.
self.expect("register read eax",
substrs = ['eax = 0x'])
def inferior_asserting_disassemble(self):
"""Test that lldb can disassemble frames after asserting."""
exe = os.path.join(os.getcwd(), "a.out")
# Create a target by the debugger.
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)
# Launch the process, and do not stop at the entry point.
target.LaunchSimple (None, None, self.get_process_working_directory())
self.check_stop_reason()
process = target.GetProcess()
self.assertTrue(process.IsValid(), "current process is valid")
thread = process.GetThreadAtIndex(0)
self.assertTrue(thread.IsValid(), "current thread is valid")
# lldb should be able to disassemble frames from the inferior after asserting.
for frame in thread:
self.assertTrue(frame.IsValid(), "current frame is valid")
self.runCmd("frame select " + str(frame.GetFrameID()), RUN_SUCCEEDED)
# Don't expect the function name to be in the disassembly as the assert
# function might be a no-return function where the PC is past the end
# of the function and in the next function. We also can't back the PC up
# because we don't know how much to back it up by on targets with opcodes
# that have differing sizes
- self.expect("disassemble -a %s" % frame.GetPC(),
- substrs = ['->'])
+ pc_backup_offset = 1
+ if frame.GetFrameID() == 0:
+ pc_backup_offset = 0
+ self.expect("disassemble -a %s" % (frame.GetPC() - pc_backup_offset),
+ substrs = ['<%s>:' % frame.GetFunctionName()])
def check_expr_in_main(self, thread):
depth = thread.GetNumFrames()
for i in range(depth):
frame = thread.GetFrameAtIndex(i)
self.assertTrue(frame.IsValid(), "current frame is valid")
if self.TraceOn():
print "Checking if function %s is main" % frame.GetFunctionName()
if 'main' == frame.GetFunctionName():
frame_id = frame.GetFrameID()
self.runCmd("frame select " + str(frame_id), RUN_SUCCEEDED)
self.expect("p argc", substrs = ['(int)', ' = 1'])
self.expect("p hello_world", substrs = ['Hello'])
self.expect("p argv[0]", substrs = ['a.out'])
self.expect("p null_ptr", substrs = ['= 0x0'])
return True
return False
def inferior_asserting_expr(self):
"""Test that the lldb expression interpreter can read symbols after asserting."""
exe = os.path.join(os.getcwd(), "a.out")
# Create a target by the debugger.
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)
# Launch the process, and do not stop at the entry point.
target.LaunchSimple (None, None, self.get_process_working_directory())
self.check_stop_reason()
process = target.GetProcess()
self.assertTrue(process.IsValid(), "current process is valid")
thread = process.GetThreadAtIndex(0)
self.assertTrue(thread.IsValid(), "current thread is valid")
# The lldb expression interpreter should be able to read from addresses of the inferior after a call to assert().
self.assertTrue(self.check_expr_in_main(thread), "cannot find 'main' in the backtrace")
def inferior_asserting_step(self):
"""Test that lldb functions correctly after stepping through a call to assert()."""
exe = os.path.join(os.getcwd(), "a.out")
# Create a target by the debugger.
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)
# Launch the process, and do not stop at the entry point.
self.set_breakpoint(self.line)
target.LaunchSimple (None, None, self.get_process_working_directory())
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs = ['main.c:%d' % self.line,
'stop reason = breakpoint'])
self.runCmd("next")
stop_reason = self.check_stop_reason()
# lldb should be able to read from registers from the inferior after asserting.
if "x86_64" in self.getArchitecture():
self.expect("register read rbp", substrs = ['rbp = 0x'])
if "i386" in self.getArchitecture():
self.expect("register read ebp", substrs = ['ebp = 0x'])
process = target.GetProcess()
self.assertTrue(process.IsValid(), "current process is valid")
thread = process.GetThreadAtIndex(0)
self.assertTrue(thread.IsValid(), "current thread is valid")
# The lldb expression interpreter should be able to read from addresses of the inferior after a call to assert().
self.assertTrue(self.check_expr_in_main(thread), "cannot find 'main' in the backtrace")
# And it should report the correct line number.
self.expect("thread backtrace all",
substrs = [stop_reason,
'main.c:%d' % self.line])
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()
Index: lldb/trunk/test/functionalities/abbreviation/TestAbbreviations.py
===================================================================
--- lldb/trunk/test/functionalities/abbreviation/TestAbbreviations.py (revision 219543)
+++ lldb/trunk/test/functionalities/abbreviation/TestAbbreviations.py (revision 219544)
@@ -1,182 +1,182 @@
"""
Test some lldb command abbreviations.
"""
import os, time
import unittest2
import lldb
from lldbtest import *
import lldbutil
class AbbreviationsTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
@unittest2.skipIf(sys.platform.startswith("win32"), "one-shot script commands deadlock on Windows.")
def test_nonrunning_command_abbreviations (self):
self.expect("ap script",
startstr = "The following built-in commands may relate to 'script':",
substrs = ['breakpoint command add',
'breakpoint command list',
'breakpoint list',
'command alias',
'expression',
'script'])
self.runCmd("com a alias com al")
self.runCmd("alias gurp help")
self.expect("gurp target create",
substrs = ['Syntax: target create <cmd-options> <filename>'])
self.runCmd("com u gurp")
self.expect("gurp",
COMMAND_FAILED_AS_EXPECTED, error = True,
substrs = ["error: 'gurp' is not a valid command."])
# Only one matching command: execute it.
self.expect("h",
startstr = "The following is a list of built-in, permanent debugger commands:")
# Execute cleanup function during test tear down
def cleanup():
self.runCmd("command alias t thread select")
self.addTearDownHook(cleanup)
# Several matching commands: list them and error out.
self.runCmd("command unalias t")
self.expect("t",
COMMAND_FAILED_AS_EXPECTED, error = True,
substrs = ["Ambiguous command 't'. Possible matches:",
"target", "thread", "type"])
self.runCmd("com sou ./change_prompt.lldb")
self.expect("settings show prompt",
startstr = 'prompt (string) = "[with-three-trailing-spaces] "')
self.runCmd("settings clear prompt")
self.expect("settings show prompt",
startstr = 'prompt (string) = "(lldb) "')
self.expect("lo li",
startstr = "Logging categories for ")
self.runCmd("se se prompt 'Sycamore> '")
self.expect("se sh prompt",
startstr = 'prompt (string) = "Sycamore> "')
self.runCmd("se cl prompt")
self.expect("set sh prompt",
startstr = 'prompt (string) = "(lldb) "')
# We don't want to display the stdout if not in TraceOn() mode.
if not self.TraceOn():
self.HideStdout()
self.runCmd (r'''sc print "\n\n\tHello!\n"''')
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@dsym_test
def test_with_dsym (self):
self.buildDsym ()
self.running_abbreviations ()
@dwarf_test
def test_with_dwarf (self):
self.buildDwarf ()
self.running_abbreviations ()
def running_abbreviations (self):
exe = os.path.join (os.getcwd(), "a.out")
# Use "file", i.e., no abbreviation. We're exactly matching the command
# verbatim when dealing with remote testsuite execution.
# For more details, see TestBase.runCmd().
self.expect("file " + exe,
patterns = [ "Current executable set to .*a.out.*" ])
# By default, the setting interpreter.expand-regex-aliases is false.
self.expect("_regexp-br product", matching=False,
substrs = [ "breakpoint set --name" ])
match_object = lldbutil.run_break_set_command (self, "br s -n sum")
lldbutil.check_breakpoint_result (self, match_object, symbol_name='sum', symbol_match_exact=False, num_locations=1)
match_object = lldbutil.run_break_set_command (self, "br s -f main.cpp -l 32")
lldbutil.check_breakpoint_result (self, match_object, file_name='main.cpp', line_number=32, num_locations=1)
self.runCmd("br co a -s python 1 -o 'print frame'")
self.expect("br co l 1",
substrs = [ "Breakpoint 1:",
"Breakpoint commands:",
"print frame" ])
self.runCmd("br co del 1")
self.expect("breakpoint command list 1",
startstr = "Breakpoint 1 does not have an associated command.")
self.expect("br di",
startstr = 'All breakpoints disabled. (3 breakpoints)')
self.expect("bre e",
startstr = "All breakpoints enabled. (3 breakpoints)")
self.expect("break list",
substrs = ["1: name = 'product', locations = 1",
"2: name = 'sum', locations = 1",
"3: file = 'main.cpp', line = 32, locations = 1"])
self.expect("br cl -l 32 -f main.cpp",
startstr = "1 breakpoints cleared:",
substrs = ["3: file = 'main.cpp', line = 32, locations = 1"])
# Add a future to terminate the current process being debugged.
#
# The test framework relies on detecting either "run" or "process launch"
# command to automatically kill the inferior upon tear down.
# But we'll be using "pro la" command to launch the inferior.
self.addTearDownHook(lambda: self.runCmd("process kill"))
self.expect("pro la",
patterns = [ "Process .* launched: "])
self.expect("pro st",
patterns = [ "Process .* stopped",
"thread #1:",
"a.out",
"sum\(a=1238, b=78392\)",
"at main.cpp\:25",
"stop reason = breakpoint 2.1" ])
# ARCH, if not specified, defaults to x86_64.
if self.getArchitecture() in ["", 'x86_64', 'i386']:
self.expect("dis -f",
- startstr = "a.out`sum(int, int)",
- substrs = [' mov',
+ substrs = ['<sum(int, int)>:',
+ ' mov',
' addl ',
'ret'])
self.expect("i d l main.cpp",
patterns = ["Line table for .*main.cpp in `a.out"])
self.expect("i d se",
patterns = ["Dumping sections for [0-9]+ modules."])
self.expect("i d symf",
patterns = ["Dumping debug symbols for [0-9]+ modules."])
self.expect("i d symt",
patterns = ["Dumping symbol table for [0-9]+ modules."])
if sys.platform.startswith("darwin"):
self.expect("i li",
substrs = [ 'a.out',
'/usr/lib/dyld',
'/usr/lib/libSystem.B.dylib'])
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()
Index: lldb/trunk/include/lldb/Symbol/SymbolContext.h
===================================================================
--- lldb/trunk/include/lldb/Symbol/SymbolContext.h (revision 219543)
+++ lldb/trunk/include/lldb/Symbol/SymbolContext.h (revision 219544)
@@ -1,574 +1,575 @@
//===-- SymbolContext.h -----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_SymbolContext_h_
#define liblldb_SymbolContext_h_
#include <vector>
#include "lldb/lldb-private.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/Mangled.h"
#include "lldb/Symbol/LineEntry.h"
#include "lldb/Utility/Iterable.h"
namespace lldb_private {
class SymbolContextScope;
//----------------------------------------------------------------------
/// @class SymbolContext SymbolContext.h "lldb/Symbol/SymbolContext.h"
/// @brief Defines a symbol context baton that can be handed other debug
/// core functions.
///
/// Many debugger functions require a context when doing lookups. This
/// class provides a common structure that can be used as the result
/// of a query that can contain a single result. Examples of such
/// queries include
/// @li Looking up a load address.
//----------------------------------------------------------------------
class SymbolContext
{
public:
//------------------------------------------------------------------
/// Default constructor.
///
/// Initialize all pointer members to NULL and all struct members
/// to their default state.
//------------------------------------------------------------------
SymbolContext ();
//------------------------------------------------------------------
/// Construct with an object that knows how to reconstruct its
/// symbol context.
///
/// @param[in] sc_scope
/// A symbol context scope object that knows how to reconstruct
/// it's context.
//------------------------------------------------------------------
explicit
SymbolContext (SymbolContextScope *sc_scope);
//------------------------------------------------------------------
/// Construct with module, and optional compile unit, function,
/// block, line table, line entry and symbol.
///
/// Initialize all pointer to the specified values.
///
/// @param[in] module
/// A Module pointer to the module for this context.
///
/// @param[in] comp_unit
/// A CompileUnit pointer to the compile unit for this context.
///
/// @param[in] function
/// A Function pointer to the function for this context.
///
/// @param[in] block
/// A Block pointer to the deepest block for this context.
///
/// @param[in] line_entry
/// A LineEntry pointer to the line entry for this context.
///
/// @param[in] symbol
/// A Symbol pointer to the symbol for this context.
//------------------------------------------------------------------
explicit
SymbolContext (const lldb::TargetSP &target_sp,
const lldb::ModuleSP &module_sp,
CompileUnit *comp_unit = NULL,
Function *function = NULL,
Block *block = NULL,
LineEntry *line_entry = NULL,
Symbol *symbol = NULL);
// This version sets the target to a NULL TargetSP if you don't know it.
explicit
SymbolContext (const lldb::ModuleSP &module_sp,
CompileUnit *comp_unit = NULL,
Function *function = NULL,
Block *block = NULL,
LineEntry *line_entry = NULL,
Symbol *symbol = NULL);
~SymbolContext ();
//------------------------------------------------------------------
/// Copy constructor
///
/// Makes a copy of the another SymbolContext object \a rhs.
///
/// @param[in] rhs
/// A const SymbolContext object reference to copy.
//------------------------------------------------------------------
SymbolContext (const SymbolContext& rhs);
//------------------------------------------------------------------
/// Assignment operator.
///
/// Copies the address value from another SymbolContext object \a
/// rhs into \a this object.
///
/// @param[in] rhs
/// A const SymbolContext object reference to copy.
///
/// @return
/// A const SymbolContext object reference to \a this.
//------------------------------------------------------------------
const SymbolContext&
operator= (const SymbolContext& rhs);
//------------------------------------------------------------------
/// Clear the object's state.
///
/// Resets all pointer members to NULL, and clears any class objects
/// to their default state.
//------------------------------------------------------------------
void
Clear (bool clear_target);
//------------------------------------------------------------------
/// Dump a description of this object to a Stream.
///
/// Dump a description of the contents of this object to the
/// supplied stream \a s.
///
/// @param[in] s
/// The stream to which to dump the object description.
//------------------------------------------------------------------
void
Dump (Stream *s, Target *target) const;
//------------------------------------------------------------------
/// Dump the stop context in this object to a Stream.
///
/// Dump the best description of this object to the stream. The
/// information displayed depends on the amount and quality of the
/// information in this context. If a module, function, file and
/// line number are available, they will be dumped. If only a
/// module and function or symbol name with offset is available,
/// that will be output. Else just the address at which the target
/// was stopped will be displayed.
///
/// @param[in] s
/// The stream to which to dump the object description.
///
/// @param[in] so_addr
/// The resolved section offset address.
//------------------------------------------------------------------
bool
DumpStopContext (Stream *s,
ExecutionContextScope *exe_scope,
const Address &so_addr,
bool show_fullpaths,
bool show_module,
- bool show_inlined_frames) const;
+ bool show_inlined_frames,
+ bool show_function_arguments) const;
//------------------------------------------------------------------
/// Get the address range contained within a symbol context.
///
/// Address range priority is as follows:
/// - line_entry address range if line_entry is valid and eSymbolContextLineEntry is set in \a scope
/// - block address range if block is not NULL and eSymbolContextBlock is set in \a scope
/// - function address range if function is not NULL and eSymbolContextFunction is set in \a scope
/// - symbol address range if symbol is not NULL and eSymbolContextSymbol is set in \a scope
///
/// @param[in] scope
/// A mask of symbol context bits telling this function which
/// address ranges it can use when trying to extract one from
/// the valid (non-NULL) symbol context classes.
///
/// @param[in] range_idx
/// The address range index to grab. Since many functions and
/// blocks are not always contiguous, they may have more than
/// one address range.
///
/// @param[in] use_inline_block_range
/// If \a scope has the eSymbolContextBlock bit set, and there
/// is a valid block in the symbol context, return the block
/// address range for the containing inline function block, not
/// the deepest most block. This allows us to extract information
/// for the address range of the inlined function block, not
/// the deepest lexical block.
///
/// @param[out] range
/// An address range object that will be filled in if \b true
/// is returned.
///
/// @return
/// \b True if this symbol context contains items that describe
/// an address range, \b false otherwise.
//------------------------------------------------------------------
bool
GetAddressRange (uint32_t scope,
uint32_t range_idx,
bool use_inline_block_range,
AddressRange &range) const;
void
GetDescription(Stream *s,
lldb::DescriptionLevel level,
Target *target) const;
uint32_t
GetResolvedMask () const;
//------------------------------------------------------------------
/// Find a block that defines the function represented by this
/// symbol context.
///
/// If this symbol context points to a block that is an inlined
/// function, or is contained within an inlined function, the block
/// that defines the inlined function is returned.
///
/// If this symbol context has no block in it, or the block is not
/// itself an inlined function block or contained within one, we
/// return the top level function block.
///
/// This is a handy function to call when you want to get the block
/// whose variable list will include the arguments for the function
/// that is represented by this symbol context (whether the function
/// is an inline function or not).
///
/// @return
/// The block object pointer that defines the function that is
/// represented by this symbol context object, NULL otherwise.
//------------------------------------------------------------------
Block *
GetFunctionBlock ();
//------------------------------------------------------------------
/// If this symbol context represents a function that is a method,
/// return true and provide information about the method.
///
/// @param[out] language
/// If \b true is returned, the language for the method.
///
/// @param[out] is_instance_method
/// If \b true is returned, \b true if this is a instance method,
/// \b false if this is a static/class function.
///
/// @param[out] language_object_name
/// If \b true is returned, the name of the artificial variable
/// for the language ("this" for C++, "self" for ObjC).
///
/// @return
/// \b True if this symbol context represents a function that
/// is a method of a class, \b false otherwise.
//------------------------------------------------------------------
bool
GetFunctionMethodInfo (lldb::LanguageType &language,
bool &is_instance_method,
ConstString &language_object_name);
//------------------------------------------------------------------
/// Find a name of the innermost function for the symbol context.
///
/// For instance, if the symbol context contains an inlined block,
/// it will return the inlined function name.
///
/// @param[in] prefer_mangled
/// if \btrue, then the mangled name will be returned if there
/// is one. Otherwise the unmangled name will be returned if it
/// is available.
///
/// @return
/// The name of the function represented by this symbol context.
//------------------------------------------------------------------
ConstString
GetFunctionName (Mangled::NamePreference preference = Mangled::ePreferDemangled) const;
//------------------------------------------------------------------
/// Get the line entry that corresponds to the function.
///
/// If the symbol context contains an inlined block, the line entry
/// for the start address of the inlined function will be returned,
/// otherwise the line entry for the start address of the function
/// will be returned. This can be used after doing a
/// Module::FindFunctions(...) or ModuleList::FindFunctions(...)
/// call in order to get the correct line table information for
/// the symbol context.
/// it will return the inlined function name.
///
/// @param[in] prefer_mangled
/// if \btrue, then the mangled name will be returned if there
/// is one. Otherwise the unmangled name will be returned if it
/// is available.
///
/// @return
/// The name of the function represented by this symbol context.
//------------------------------------------------------------------
LineEntry
GetFunctionStartLineEntry () const;
//------------------------------------------------------------------
/// Find the block containing the inlined block that contains this block.
///
/// For instance, if the symbol context contains an inlined block,
/// it will return the inlined function name.
///
/// @param[in] curr_frame_pc
/// The address within the block of this object.
///
/// @param[out] next_frame_sc
/// A new symbol context that does what the title says it does.
///
/// @param[out] next_frame_addr
/// This is what you should report as the PC in \a next_frame_sc.
///
/// @return
/// \b true if this SymbolContext specifies a block contained in an
/// inlined block. If this returns \b true, \a next_frame_sc and
/// \a next_frame_addr will be filled in correctly.
//------------------------------------------------------------------
bool
GetParentOfInlinedScope (const Address &curr_frame_pc,
SymbolContext &next_frame_sc,
Address &inlined_frame_addr) const;
//------------------------------------------------------------------
// Member variables
//------------------------------------------------------------------
lldb::TargetSP target_sp; ///< The Target for a given query
lldb::ModuleSP module_sp; ///< The Module for a given query
CompileUnit * comp_unit; ///< The CompileUnit for a given query
Function * function; ///< The Function for a given query
Block * block; ///< The Block for a given query
LineEntry line_entry; ///< The LineEntry for a given query
Symbol * symbol; ///< The Symbol for a given query
};
class SymbolContextSpecifier
{
public:
typedef enum SpecificationType
{
eNothingSpecified = 0,
eModuleSpecified = 1 << 0,
eFileSpecified = 1 << 1,
eLineStartSpecified = 1 << 2,
eLineEndSpecified = 1 << 3,
eFunctionSpecified = 1 << 4,
eClassOrNamespaceSpecified = 1 << 5,
eAddressRangeSpecified = 1 << 6
} SpecificationType;
// This one produces a specifier that matches everything...
SymbolContextSpecifier (const lldb::TargetSP& target_sp);
~SymbolContextSpecifier();
bool
AddSpecification (const char *spec_string, SpecificationType type);
bool
AddLineSpecification (uint32_t line_no, SpecificationType type);
void
Clear();
bool
SymbolContextMatches(SymbolContext &sc);
bool
AddressMatches(lldb::addr_t addr);
void
GetDescription (Stream *s, lldb::DescriptionLevel level) const;
private:
lldb::TargetSP m_target_sp;
std::string m_module_spec;
lldb::ModuleSP m_module_sp;
std::unique_ptr<FileSpec> m_file_spec_ap;
size_t m_start_line;
size_t m_end_line;
std::string m_function_spec;
std::string m_class_name;
std::unique_ptr<AddressRange> m_address_range_ap;
uint32_t m_type; // Or'ed bits from SpecificationType
};
//----------------------------------------------------------------------
/// @class SymbolContextList SymbolContext.h "lldb/Symbol/SymbolContext.h"
/// @brief Defines a list of symbol context objects.
///
/// This class provides a common structure that can be used to contain
/// the result of a query that can contain a multiple results. Examples
/// of such queries include:
/// @li Looking up a function by name.
/// @li Finding all addresses for a specified file and line number.
//----------------------------------------------------------------------
class SymbolContextList
{
public:
//------------------------------------------------------------------
/// Default constructor.
///
/// Initialize with an empty list.
//------------------------------------------------------------------
SymbolContextList ();
//------------------------------------------------------------------
/// Destructor.
//------------------------------------------------------------------
~SymbolContextList ();
//------------------------------------------------------------------
/// Append a new symbol context to the list.
///
/// @param[in] sc
/// A symbol context to append to the list.
//------------------------------------------------------------------
void
Append (const SymbolContext& sc);
void
Append (const SymbolContextList& sc_list);
bool
AppendIfUnique (const SymbolContext& sc,
bool merge_symbol_into_function);
bool
MergeSymbolContextIntoFunctionContext (const SymbolContext& symbol_sc,
uint32_t start_idx = 0,
uint32_t stop_idx = UINT32_MAX);
uint32_t
AppendIfUnique (const SymbolContextList& sc_list,
bool merge_symbol_into_function);
//------------------------------------------------------------------
/// Clear the object's state.
///
/// Clears the symbol context list.
//------------------------------------------------------------------
void
Clear();
//------------------------------------------------------------------
/// Dump a description of this object to a Stream.
///
/// Dump a description of the contents of each symbol context in
/// the list to the supplied stream \a s.
///
/// @param[in] s
/// The stream to which to dump the object description.
//------------------------------------------------------------------
void
Dump(Stream *s, Target *target) const;
//------------------------------------------------------------------
/// Get accessor for a symbol context at index \a idx.
///
/// Dump a description of the contents of each symbol context in
/// the list to the supplied stream \a s.
///
/// @param[in] idx
/// The zero based index into the symbol context list.
///
/// @param[out] sc
/// A reference to the symbol context to fill in.
///
/// @return
/// Returns \b true if \a idx was a valid index into this
/// symbol context list and \a sc was filled in, \b false
/// otherwise.
//------------------------------------------------------------------
bool
GetContextAtIndex(size_t idx, SymbolContext& sc) const;
//------------------------------------------------------------------
/// Direct reference accessor for a symbol context at index \a idx.
///
/// The index \a idx must be a valid index, no error checking will
/// be done to ensure that it is valid.
///
/// @param[in] idx
/// The zero based index into the symbol context list.
///
/// @return
/// A const reference to the symbol context to fill in.
//------------------------------------------------------------------
SymbolContext&
operator [] (size_t idx)
{
return m_symbol_contexts[idx];
}
const SymbolContext&
operator [] (size_t idx) const
{
return m_symbol_contexts[idx];
}
//------------------------------------------------------------------
/// Get accessor for the last symbol context in the list.
///
/// @param[out] sc
/// A reference to the symbol context to fill in.
///
/// @return
/// Returns \b true if \a sc was filled in, \b false if the
/// list is empty.
//------------------------------------------------------------------
bool
GetLastContext(SymbolContext& sc) const;
bool
RemoveContextAtIndex (size_t idx);
//------------------------------------------------------------------
/// Get accessor for a symbol context list size.
///
/// @return
/// Returns the number of symbol context objects in the list.
//------------------------------------------------------------------
uint32_t
GetSize() const;
uint32_t
NumLineEntriesWithLine (uint32_t line) const;
void
GetDescription(Stream *s,
lldb::DescriptionLevel level,
Target *target) const;
protected:
typedef std::vector<SymbolContext> collection; ///< The collection type for the list.
//------------------------------------------------------------------
// Member variables.
//------------------------------------------------------------------
collection m_symbol_contexts; ///< The list of symbol contexts.
public:
typedef AdaptedIterable<collection, SymbolContext, vector_adapter> SymbolContextIterable;
SymbolContextIterable
SymbolContexts()
{
return SymbolContextIterable(m_symbol_contexts);
}
};
bool operator== (const SymbolContext& lhs, const SymbolContext& rhs);
bool operator!= (const SymbolContext& lhs, const SymbolContext& rhs);
bool operator== (const SymbolContextList& lhs, const SymbolContextList& rhs);
bool operator!= (const SymbolContextList& lhs, const SymbolContextList& rhs);
} // namespace lldb_private
#endif // liblldb_SymbolContext_h_
Index: lldb/trunk/include/lldb/Core/Mangled.h
===================================================================
--- lldb/trunk/include/lldb/Core/Mangled.h (revision 219543)
+++ lldb/trunk/include/lldb/Core/Mangled.h (revision 219544)
@@ -1,306 +1,307 @@
//===-- Mangled.h -----------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_Mangled_h_
#define liblldb_Mangled_h_
#if defined(__cplusplus)
#include "lldb/lldb-private.h"
#include "lldb/Core/ConstString.h"
#include <vector>
namespace lldb_private {
//----------------------------------------------------------------------
/// @class Mangled Mangled.h "lldb/Core/Mangled.h"
/// @brief A class that handles mangled names.
///
/// Designed to handle mangled names. The demangled version of any names
/// will be computed when the demangled name is accessed through the
/// Demangled() acccessor. This class can also tokenize the demangled
/// version of the name for powerful searches. Functions and symbols
/// could make instances of this class for their mangled names. Uniqued
/// string pools are used for the mangled, demangled, and token string
/// values to allow for faster comparisons and for efficient memory use.
//----------------------------------------------------------------------
class Mangled
{
public:
enum NamePreference
{
ePreferMangled,
- ePreferDemangled
+ ePreferDemangled,
+ ePreferDemangledWithoutArguments
};
//----------------------------------------------------------------------
/// Default constructor.
///
/// Initialize with both mangled and demangled names empty.
//----------------------------------------------------------------------
Mangled ();
//----------------------------------------------------------------------
/// Construct with name.
///
/// Constructor with an optional string and a boolean indicating if it is
/// the mangled version.
///
/// @param[in] name
/// The already const name to copy into this object.
///
/// @param[in] is_mangled
/// If \b true then \a name is a mangled name, if \b false then
/// \a name is demangled.
//----------------------------------------------------------------------
explicit
Mangled (const ConstString &name, bool is_mangled);
//----------------------------------------------------------------------
/// Construct with name.
///
/// Constructor with an optional string and auto-detect if \a name is
/// mangled or not.
///
/// @param[in] name
/// The already const name to copy into this object.
//----------------------------------------------------------------------
explicit
Mangled (const ConstString &name);
//----------------------------------------------------------------------
/// Destructor
///
/// Releases its ref counts on the mangled and demangled strings that
/// live in the global string pool.
//----------------------------------------------------------------------
~Mangled ();
//----------------------------------------------------------------------
/// Convert to pointer operator.
///
/// This allows code to check a Mangled object to see if it contains
/// a valid mangled name using code such as:
///
/// @code
/// Mangled mangled(...);
/// if (mangled)
/// { ...
/// @endcode
///
/// @return
/// A pointer to this object if either the mangled or unmangled
/// name is set, NULL otherwise.
//----------------------------------------------------------------------
operator
void*() const;
//----------------------------------------------------------------------
/// Logical NOT operator.
///
/// This allows code to check a Mangled object to see if it contains
/// an empty mangled name using code such as:
///
/// @code
/// Mangled mangled(...);
/// if (!mangled)
/// { ...
/// @endcode
///
/// @return
/// Returns \b true if the object has an empty mangled and
/// unmangled name, \b false otherwise.
//----------------------------------------------------------------------
bool
operator!() const;
//----------------------------------------------------------------------
/// Clear the mangled and demangled values.
//----------------------------------------------------------------------
void
Clear ();
//----------------------------------------------------------------------
/// Compare the mangled string values
///
/// Compares the Mangled::GetName() string in \a lhs and \a rhs.
///
/// @param[in] lhs
/// A const reference to the Left Hand Side object to compare.
///
/// @param[in] rhs
/// A const reference to the Right Hand Side object to compare.
///
/// @return
/// @li -1 if \a lhs is less than \a rhs
/// @li 0 if \a lhs is equal to \a rhs
/// @li 1 if \a lhs is greater than \a rhs
//----------------------------------------------------------------------
static int
Compare (const Mangled& lhs, const Mangled& rhs);
//----------------------------------------------------------------------
/// Dump a description of this object to a Stream \a s.
///
/// Dump a Mangled object to stream \a s. We don't force our
/// demangled name to be computed currently (we don't use the accessor).
///
/// @param[in] s
/// The stream to which to dump the object description.
//----------------------------------------------------------------------
void
Dump (Stream *s) const;
//----------------------------------------------------------------------
/// Dump a debug description of this object to a Stream \a s.
///
/// @param[in] s
/// The stream to which to dump the object description.
//----------------------------------------------------------------------
void
DumpDebug (Stream *s) const;
//----------------------------------------------------------------------
/// Demangled name get accessor.
///
/// @return
/// A const reference to the demangled name string object.
//----------------------------------------------------------------------
const ConstString&
GetDemangledName () const;
void
SetDemangledName (const ConstString &name)
{
m_demangled = name;
}
void
SetMangledName (const ConstString &name)
{
m_mangled = name;
}
//----------------------------------------------------------------------
/// Mangled name get accessor.
///
/// @return
/// A reference to the mangled name string object.
//----------------------------------------------------------------------
ConstString&
GetMangledName ()
{
return m_mangled;
}
//----------------------------------------------------------------------
/// Mangled name get accessor.
///
/// @return
/// A const reference to the mangled name string object.
//----------------------------------------------------------------------
const ConstString&
GetMangledName () const
{
return m_mangled;
}
//----------------------------------------------------------------------
/// Best name get accessor.
///
/// @param[in] preference
/// Which name would you prefer to get?
///
/// @return
/// A const reference to the preferred name string object if this
/// object has a valid name of that kind, else a const reference to the
/// other name is returned.
//----------------------------------------------------------------------
const ConstString&
GetName (NamePreference preference = ePreferDemangled) const;
//----------------------------------------------------------------------
/// Check if "name" matches either the mangled or demangled name.
///
/// @param[in] name
/// A name to match against both strings.
///
/// @return
/// \b True if \a name matches either name, \b false otherwise.
//----------------------------------------------------------------------
bool
NameMatches (const ConstString &name) const
{
if (m_mangled == name)
return true;
return GetDemangledName () == name;
}
bool
NameMatches (const RegularExpression& regex) const;
//----------------------------------------------------------------------
/// Get the memory cost of this object.
///
/// Return the size in bytes that this object takes in memory. This
/// returns the size in bytes of this object, not any shared string
/// values it may refer to.
///
/// @return
/// The number of bytes that this object occupies in memory.
///
/// @see ConstString::StaticMemorySize ()
//----------------------------------------------------------------------
size_t
MemorySize () const;
//----------------------------------------------------------------------
/// Set the string value in this object.
///
/// If \a is_mangled is \b true, then the mangled named is set to \a
/// name, else the demangled name is set to \a name.
///
/// @param[in] name
/// The already const version of the name for this object.
///
/// @param[in] is_mangled
/// If \b true then \a name is a mangled name, if \b false then
/// \a name is demangled.
//----------------------------------------------------------------------
void
SetValue (const ConstString &name, bool is_mangled);
//----------------------------------------------------------------------
/// Set the string value in this object.
///
/// This version auto detects if the string is mangled by inspecting the
/// string value and looking for common mangling prefixes.
///
/// @param[in] name
/// The already const version of the name for this object.
//----------------------------------------------------------------------
void
SetValue (const ConstString &name);
private:
//----------------------------------------------------------------------
/// Mangled member variables.
//----------------------------------------------------------------------
ConstString m_mangled; ///< The mangled version of the name
mutable ConstString m_demangled; ///< Mutable so we can get it on demand with a const version of this object
};
Stream& operator << (Stream& s, const Mangled& obj);
} // namespace lldb_private
#endif // #if defined(__cplusplus)
#endif // liblldb_Mangled_h_
Index: lldb/trunk/include/lldb/Core/Disassembler.h
===================================================================
--- lldb/trunk/include/lldb/Core/Disassembler.h (revision 219543)
+++ lldb/trunk/include/lldb/Core/Disassembler.h (revision 219544)
@@ -1,426 +1,474 @@
//===-- Disassembler.h ------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_Disassembler_h_
#define liblldb_Disassembler_h_
// C Includes
// C++ Includes
#include <vector>
#include <string>
// Other libraries and framework includes
// Project includes
#include "lldb/lldb-private.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/EmulateInstruction.h"
#include "lldb/Core/Opcode.h"
#include "lldb/Core/PluginInterface.h"
#include "lldb/Interpreter/OptionValue.h"
namespace lldb_private {
class Instruction
{
public:
Instruction (const Address &address,
lldb::AddressClass addr_class = lldb::eAddressClassInvalid);
virtual
~Instruction();
const Address &
GetAddress () const
{
return m_address;
}
const char *
GetMnemonic (const ExecutionContext* exe_ctx)
{
CalculateMnemonicOperandsAndCommentIfNeeded (exe_ctx);
return m_opcode_name.c_str();
}
const char *
GetOperands (const ExecutionContext* exe_ctx)
{
CalculateMnemonicOperandsAndCommentIfNeeded (exe_ctx);
return m_mnemonics.c_str();
}
const char *
GetComment (const ExecutionContext* exe_ctx)
{
CalculateMnemonicOperandsAndCommentIfNeeded (exe_ctx);
return m_comment.c_str();
}
virtual void
CalculateMnemonicOperandsAndComment (const ExecutionContext* exe_ctx) = 0;
lldb::AddressClass
GetAddressClass ();
void
SetAddress (const Address &addr)
{
// Invalidate the address class to lazily discover
// it if we need to.
m_address_class = lldb::eAddressClassInvalid;
m_address = addr;
}
+ //------------------------------------------------------------------
+ /// Dump the text representation of this Instruction to a Stream
+ ///
+ /// Print the (optional) address, (optional) bytes, opcode,
+ /// operands, and instruction comments to a stream.
+ ///
+ /// @param[in] s
+ /// The Stream to add the text to.
+ ///
+ /// @param[in] show_address
+ /// Whether the address (using disassembly_addr_format_spec formatting)
+ /// should be printed.
+ ///
+ /// @param[in] show_bytes
+ /// Whether the bytes of the assembly instruction should be printed.
+ ///
+ /// @param[in] max_opcode_byte_size
+ /// The size (in bytes) of the largest instruction in the list that
+ /// we are printing (for text justification/alignment purposes)
+ /// Only needed if show_bytes is true.
+ ///
+ /// @param[in] exe_ctx
+ /// The current execution context, if available. May be used in
+ /// the assembling of the operands+comments for this instruction.
+ /// Pass NULL if not applicable.
+ ///
+ /// @param[in] sym_ctx
+ /// The SymbolContext for this instruction.
+ /// Pass NULL if not available/computed.
+ /// Only needed if show_address is true.
+ ///
+ /// @param[in] prev_sym_ctx
+ /// The SymbolContext for the previous instruction. Depending on
+ /// the disassembly address format specification, a change in
+ /// Symbol / Function may mean that a line is printed with the new
+ /// symbol/function name.
+ /// Pass NULL if unavailable, or if this is the first instruction of
+ /// the InstructionList.
+ /// Only needed if show_address is true.
+ ///
+ /// @param[in] disassembly_addr_format_spec
+ /// The format specification for how addresses are printed.
+ /// Only needed if show_address is true.
+ //------------------------------------------------------------------
+
virtual void
Dump (Stream *s,
uint32_t max_opcode_byte_size,
bool show_address,
bool show_bytes,
- const ExecutionContext* exe_ctx);
+ const ExecutionContext* exe_ctx,
+ const SymbolContext *sym_ctx,
+ const SymbolContext *prev_sym_ctx,
+ const char *disassembly_addr_format_spec);
virtual bool
DoesBranch () = 0;
virtual size_t
Decode (const Disassembler &disassembler,
const DataExtractor& data,
lldb::offset_t data_offset) = 0;
virtual void
SetDescription (const char *) {} // May be overridden in sub-classes that have descriptions.
lldb::OptionValueSP
ReadArray (FILE *in_file, Stream *out_stream, OptionValue::Type data_type);
lldb::OptionValueSP
ReadDictionary (FILE *in_file, Stream *out_stream);
bool
DumpEmulation (const ArchSpec &arch);
virtual bool
TestEmulation (Stream *stream, const char *test_file_name);
bool
Emulate (const ArchSpec &arch,
uint32_t evaluate_options,
void *baton,
EmulateInstruction::ReadMemoryCallback read_mem_callback,
EmulateInstruction::WriteMemoryCallback write_mem_calback,
EmulateInstruction::ReadRegisterCallback read_reg_callback,
EmulateInstruction::WriteRegisterCallback write_reg_callback);
const Opcode &
GetOpcode () const
{
return m_opcode;
}
uint32_t
GetData (DataExtractor &data);
protected:
Address m_address; // The section offset address of this instruction
// We include an address class in the Instruction class to
// allow the instruction specify the eAddressClassCodeAlternateISA
// (currently used for thumb), and also to specify data (eAddressClassData).
// The usual value will be eAddressClassCode, but often when
// disassembling memory, you might run into data. This can
// help us to disassemble appropriately.
private:
lldb::AddressClass m_address_class; // Use GetAddressClass () accessor function!
protected:
Opcode m_opcode; // The opcode for this instruction
std::string m_opcode_name;
std::string m_mnemonics;
std::string m_comment;
bool m_calculated_strings;
void
CalculateMnemonicOperandsAndCommentIfNeeded (const ExecutionContext* exe_ctx)
{
if (!m_calculated_strings)
{
m_calculated_strings = true;
CalculateMnemonicOperandsAndComment(exe_ctx);
}
}
};
class InstructionList
{
public:
InstructionList();
~InstructionList();
size_t
GetSize() const;
uint32_t
GetMaxOpcocdeByteSize () const;
lldb::InstructionSP
GetInstructionAtIndex (size_t idx) const;
uint32_t
GetIndexOfNextBranchInstruction(uint32_t start) const;
uint32_t
GetIndexOfInstructionAtLoadAddress (lldb::addr_t load_addr, Target &target);
uint32_t
GetIndexOfInstructionAtAddress (const Address &addr);
void
Clear();
void
Append (lldb::InstructionSP &inst_sp);
void
Dump (Stream *s,
bool show_address,
bool show_bytes,
const ExecutionContext* exe_ctx);
private:
typedef std::vector<lldb::InstructionSP> collection;
typedef collection::iterator iterator;
typedef collection::const_iterator const_iterator;
collection m_instructions;
};
class PseudoInstruction :
public Instruction
{
public:
PseudoInstruction ();
virtual
~PseudoInstruction ();
virtual bool
DoesBranch ();
virtual void
CalculateMnemonicOperandsAndComment (const ExecutionContext* exe_ctx)
{
// TODO: fill this in and put opcode name into Instruction::m_opcode_name,
// mnemonic into Instruction::m_mnemonics, and any comment into
// Instruction::m_comment
}
virtual size_t
Decode (const Disassembler &disassembler,
const DataExtractor &data,
lldb::offset_t data_offset);
void
SetOpcode (size_t opcode_size, void *opcode_data);
virtual void
SetDescription (const char *description);
protected:
std::string m_description;
DISALLOW_COPY_AND_ASSIGN (PseudoInstruction);
};
class Disassembler :
public std::enable_shared_from_this<Disassembler>,
public PluginInterface
{
public:
enum
{
eOptionNone = 0u,
eOptionShowBytes = (1u << 0),
eOptionRawOuput = (1u << 1),
eOptionMarkPCSourceLine = (1u << 2), // Mark the source line that contains the current PC (mixed mode only)
eOptionMarkPCAddress = (1u << 3) // Mark the disassembly line the contains the PC
};
enum HexImmediateStyle
{
eHexStyleC,
eHexStyleAsm,
};
// FindPlugin should be lax about the flavor string (it is too annoying to have various internal uses of the
// disassembler fail because the global flavor string gets set wrong. Instead, if you get a flavor string you
// don't understand, use the default. Folks who care to check can use the FlavorValidForArchSpec method on the
// disassembler they got back.
static lldb::DisassemblerSP
FindPlugin (const ArchSpec &arch, const char *flavor, const char *plugin_name);
// This version will use the value in the Target settings if flavor is NULL;
static lldb::DisassemblerSP
FindPluginForTarget(const lldb::TargetSP target_sp, const ArchSpec &arch, const char *flavor, const char *plugin_name);
static lldb::DisassemblerSP
DisassembleRange (const ArchSpec &arch,
const char *plugin_name,
const char *flavor,
const ExecutionContext &exe_ctx,
const AddressRange &disasm_range,
bool prefer_file_cache);
static lldb::DisassemblerSP
DisassembleBytes (const ArchSpec &arch,
const char *plugin_name,
const char *flavor,
const Address &start,
const void *bytes,
size_t length,
uint32_t max_num_instructions,
bool data_from_file);
static bool
Disassemble (Debugger &debugger,
const ArchSpec &arch,
const char *plugin_name,
const char *flavor,
const ExecutionContext &exe_ctx,
const AddressRange &range,
uint32_t num_instructions,
uint32_t num_mixed_context_lines,
uint32_t options,
Stream &strm);
static bool
Disassemble (Debugger &debugger,
const ArchSpec &arch,
const char *plugin_name,
const char *flavor,
const ExecutionContext &exe_ctx,
const Address &start,
uint32_t num_instructions,
uint32_t num_mixed_context_lines,
uint32_t options,
Stream &strm);
static size_t
Disassemble (Debugger &debugger,
const ArchSpec &arch,
const char *plugin_name,
const char *flavor,
const ExecutionContext &exe_ctx,
SymbolContextList &sc_list,
uint32_t num_instructions,
uint32_t num_mixed_context_lines,
uint32_t options,
Stream &strm);
static bool
Disassemble (Debugger &debugger,
const ArchSpec &arch,
const char *plugin_name,
const char *flavor,
const ExecutionContext &exe_ctx,
const ConstString &name,
Module *module,
uint32_t num_instructions,
uint32_t num_mixed_context_lines,
uint32_t options,
Stream &strm);
static bool
Disassemble (Debugger &debugger,
const ArchSpec &arch,
const char *plugin_name,
const char *flavor,
const ExecutionContext &exe_ctx,
uint32_t num_instructions,
uint32_t num_mixed_context_lines,
uint32_t options,
Stream &strm);
//------------------------------------------------------------------
// Constructors and Destructors
//------------------------------------------------------------------
Disassembler(const ArchSpec &arch, const char *flavor);
virtual ~Disassembler();
typedef const char * (*SummaryCallback)(const Instruction& inst, ExecutionContext *exe_context, void *user_data);
static bool
PrintInstructions (Disassembler *disasm_ptr,
Debugger &debugger,
const ArchSpec &arch,
const ExecutionContext &exe_ctx,
uint32_t num_instructions,
uint32_t num_mixed_context_lines,
uint32_t options,
Stream &strm);
size_t
ParseInstructions (const ExecutionContext *exe_ctx,
const AddressRange &range,
Stream *error_strm_ptr,
bool prefer_file_cache);
size_t
ParseInstructions (const ExecutionContext *exe_ctx,
const Address &range,
uint32_t num_instructions,
bool prefer_file_cache);
virtual size_t
DecodeInstructions (const Address &base_addr,
const DataExtractor& data,
lldb::offset_t data_offset,
size_t num_instructions,
bool append,
bool data_from_file) = 0;
InstructionList &
GetInstructionList ();
const InstructionList &
GetInstructionList () const;
const ArchSpec &
GetArchitecture () const
{
return m_arch;
}
const char *
GetFlavor () const
{
return m_flavor.c_str();
}
virtual bool
FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor) = 0;
protected:
//------------------------------------------------------------------
// Classes that inherit from Disassembler can see and modify these
//------------------------------------------------------------------
const ArchSpec m_arch;
InstructionList m_instruction_list;
lldb::addr_t m_base_addr;
std::string m_flavor;
private:
//------------------------------------------------------------------
// For Disassembler only
//------------------------------------------------------------------
DISALLOW_COPY_AND_ASSIGN (Disassembler);
};
} // namespace lldb_private
#endif // liblldb_Disassembler_h_
Index: lldb/trunk/include/lldb/Core/Debugger.h
===================================================================
--- lldb/trunk/include/lldb/Core/Debugger.h (revision 219543)
+++ lldb/trunk/include/lldb/Core/Debugger.h (revision 219544)
@@ -1,455 +1,465 @@
//===-- Debugger.h ----------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_Debugger_h_
#define liblldb_Debugger_h_
#if defined(__cplusplus)
#include <stdint.h>
#include <stack>
#include "lldb/lldb-public.h"
#include "lldb/Core/Broadcaster.h"
#include "lldb/Core/Communication.h"
#include "lldb/Core/IOHandler.h"
#include "lldb/Core/Listener.h"
#include "lldb/Core/SourceManager.h"
#include "lldb/Core/UserID.h"
#include "lldb/Core/UserSettingsController.h"
#include "lldb/DataFormatters/FormatManager.h"
#include "lldb/Host/HostThread.h"
#include "lldb/Host/Terminal.h"
#include "lldb/Interpreter/OptionValueProperties.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Platform.h"
#include "lldb/Target/TargetList.h"
namespace llvm
{
namespace sys
{
class DynamicLibrary;
}
}
namespace lldb_private {
//----------------------------------------------------------------------
/// @class Debugger Debugger.h "lldb/Core/Debugger.h"
/// @brief A class to manage flag bits.
///
/// Provides a global root objects for the debugger core.
//----------------------------------------------------------------------
class Debugger :
public std::enable_shared_from_this<Debugger>,
public UserID,
public Properties,
public BroadcasterManager
{
friend class SourceManager; // For GetSourceFileCache.
public:
typedef llvm::sys::DynamicLibrary (*LoadPluginCallbackType) (const lldb::DebuggerSP &debugger_sp,
const FileSpec& spec,
Error& error);
static lldb::DebuggerSP
CreateInstance (lldb::LogOutputCallback log_callback = NULL, void *baton = NULL);
static lldb::TargetSP
FindTargetWithProcessID (lldb::pid_t pid);
static lldb::TargetSP
FindTargetWithProcess (Process *process);
static void
Initialize (LoadPluginCallbackType load_plugin_callback);
static void
Terminate ();
static void
SettingsInitialize ();
static void
SettingsTerminate ();
static void
Destroy (lldb::DebuggerSP &debugger_sp);
virtual
~Debugger ();
void Clear();
bool
GetAsyncExecution ();
void
SetAsyncExecution (bool async);
lldb::StreamFileSP
GetInputFile ()
{
return m_input_file_sp;
}
lldb::StreamFileSP
GetOutputFile ()
{
return m_output_file_sp;
}
lldb::StreamFileSP
GetErrorFile ()
{
return m_error_file_sp;
}
void
SetInputFileHandle (FILE *fh, bool tranfer_ownership);
void
SetOutputFileHandle (FILE *fh, bool tranfer_ownership);
void
SetErrorFileHandle (FILE *fh, bool tranfer_ownership);
void
SaveInputTerminalState();
void
RestoreInputTerminalState();
lldb::StreamSP
GetAsyncOutputStream ();
lldb::StreamSP
GetAsyncErrorStream ();
CommandInterpreter &
GetCommandInterpreter ()
{
assert (m_command_interpreter_ap.get());
return *m_command_interpreter_ap;
}
Listener &
GetListener ()
{
return m_listener;
}
// This returns the Debugger's scratch source manager. It won't be able to look up files in debug
// information, but it can look up files by absolute path and display them to you.
// To get the target's source manager, call GetSourceManager on the target instead.
SourceManager &
GetSourceManager ();
public:
lldb::TargetSP
GetSelectedTarget ()
{
return m_target_list.GetSelectedTarget ();
}
ExecutionContext
GetSelectedExecutionContext();
//------------------------------------------------------------------
/// Get accessor for the target list.
///
/// The target list is part of the global debugger object. This
/// the single debugger shared instance to control where targets
/// get created and to allow for tracking and searching for targets
/// based on certain criteria.
///
/// @return
/// A global shared target list.
//------------------------------------------------------------------
TargetList &
GetTargetList ()
{
return m_target_list;
}
PlatformList &
GetPlatformList ()
{
return m_platform_list;
}
void
DispatchInputInterrupt ();
void
DispatchInputEndOfFile ();
//------------------------------------------------------------------
// If any of the streams are not set, set them to the in/out/err
// stream of the top most input reader to ensure they at least have
// something
//------------------------------------------------------------------
void
AdoptTopIOHandlerFilesIfInvalid (lldb::StreamFileSP &in,
lldb::StreamFileSP &out,
lldb::StreamFileSP &err);
void
PushIOHandler (const lldb::IOHandlerSP& reader_sp);
bool
PopIOHandler (const lldb::IOHandlerSP& reader_sp);
// Synchronously run an input reader until it is done
void
RunIOHandler (const lldb::IOHandlerSP& reader_sp);
bool
IsTopIOHandler (const lldb::IOHandlerSP& reader_sp);
ConstString
GetTopIOHandlerControlSequence(char ch);
bool
HideTopIOHandler();
void
RefreshTopIOHandler();
static lldb::DebuggerSP
FindDebuggerWithID (lldb::user_id_t id);
static lldb::DebuggerSP
FindDebuggerWithInstanceName (const ConstString &instance_name);
static size_t
GetNumDebuggers();
static lldb::DebuggerSP
GetDebuggerAtIndex (size_t index);
static bool
FormatPrompt (const char *format,
const SymbolContext *sc,
const ExecutionContext *exe_ctx,
const Address *addr,
Stream &s,
ValueObject* valobj = NULL);
+ static bool
+ FormatDisassemblerAddress (const char *format,
+ const SymbolContext *sc,
+ const SymbolContext *prev_sc,
+ const ExecutionContext *exe_ctx,
+ const Address *addr,
+ Stream &s);
void
ClearIOHandlers ();
static int
TestDebuggerRefCount ();
bool
GetCloseInputOnEOF () const;
void
SetCloseInputOnEOF (bool b);
bool
EnableLog (const char *channel, const char **categories, const char *log_file, uint32_t log_options, Stream &error_stream);
void
SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton);
//----------------------------------------------------------------------
// Properties Functions
//----------------------------------------------------------------------
enum StopDisassemblyType
{
eStopDisassemblyTypeNever = 0,
eStopDisassemblyTypeNoSource,
eStopDisassemblyTypeAlways
};
virtual Error
SetPropertyValue (const ExecutionContext *exe_ctx,
VarSetOperationType op,
const char *property_path,
const char *value);
bool
GetAutoConfirm () const;
-
+
+ const char *
+ GetDisassemblyFormat() const;
+
const char *
GetFrameFormat() const;
-
+
const char *
GetThreadFormat() const;
lldb::ScriptLanguage
GetScriptLanguage() const;
bool
SetScriptLanguage (lldb::ScriptLanguage script_lang);
uint32_t
GetTerminalWidth () const;
bool
SetTerminalWidth (uint32_t term_width);
const char *
GetPrompt() const;
void
SetPrompt(const char *p);
bool
GetUseExternalEditor () const;
bool
SetUseExternalEditor (bool use_external_editor_p);
bool
GetUseColor () const;
bool
SetUseColor (bool use_color);
uint32_t
GetStopSourceLineCount (bool before) const;
StopDisassemblyType
GetStopDisassemblyDisplay () const;
uint32_t
GetDisassemblyLineCount () const;
bool
GetAutoOneLineSummaries () const;
bool
GetNotifyVoid () const;
const ConstString &
GetInstanceName()
{
return m_instance_name;
}
bool
LoadPlugin (const FileSpec& spec, Error& error);
void
ExecuteIOHanders();
bool
IsForwardingEvents ();
void
EnableForwardEvents (const lldb::ListenerSP &listener_sp);
void
CancelForwardEvents (const lldb::ListenerSP &listener_sp);
bool
IsHandlingEvents () const
{
return m_event_handler_thread.IsJoinable();
}
protected:
friend class CommandInterpreter;
bool
StartEventHandlerThread();
void
StopEventHandlerThread();
static lldb::thread_result_t
EventHandlerThread (lldb::thread_arg_t arg);
bool
StartIOHandlerThread();
void
StopIOHandlerThread();
static lldb::thread_result_t
IOHandlerThread (lldb::thread_arg_t arg);
void
DefaultEventHandler();
void
HandleBreakpointEvent (const lldb::EventSP &event_sp);
void
HandleProcessEvent (const lldb::EventSP &event_sp);
void
HandleThreadEvent (const lldb::EventSP &event_sp);
size_t
GetProcessSTDOUT (Process *process, Stream *stream);
size_t
GetProcessSTDERR (Process *process, Stream *stream);
SourceManager::SourceFileCache &
GetSourceFileCache ()
{
return m_source_file_cache;
}
lldb::StreamFileSP m_input_file_sp;
lldb::StreamFileSP m_output_file_sp;
lldb::StreamFileSP m_error_file_sp;
TerminalState m_terminal_state;
TargetList m_target_list;
PlatformList m_platform_list;
Listener m_listener;
std::unique_ptr<SourceManager> m_source_manager_ap; // This is a scratch source manager that we return if we have no targets.
SourceManager::SourceFileCache m_source_file_cache; // All the source managers for targets created in this debugger used this shared
// source file cache.
std::unique_ptr<CommandInterpreter> m_command_interpreter_ap;
IOHandlerStack m_input_reader_stack;
typedef std::map<std::string, lldb::StreamWP> LogStreamMap;
LogStreamMap m_log_streams;
lldb::StreamSP m_log_callback_stream_sp;
ConstString m_instance_name;
static LoadPluginCallbackType g_load_plugin_callback;
typedef std::vector<llvm::sys::DynamicLibrary> LoadedPluginsList;
LoadedPluginsList m_loaded_plugins;
HostThread m_event_handler_thread;
HostThread m_io_handler_thread;
lldb::ListenerSP m_forward_listener_sp;
void
InstanceInitialize ();
private:
// Use Debugger::CreateInstance() to get a shared pointer to a new
// debugger object
Debugger (lldb::LogOutputCallback m_log_callback, void *baton);
DISALLOW_COPY_AND_ASSIGN (Debugger);
};
} // namespace lldb_private
#endif // #if defined(__cplusplus)
#endif // liblldb_Debugger_h_
Index: lldb/trunk/include/lldb/Core/Address.h
===================================================================
--- lldb/trunk/include/lldb/Core/Address.h (revision 219543)
+++ lldb/trunk/include/lldb/Core/Address.h (revision 219544)
@@ -1,592 +1,593 @@
//===-- Address.h -----------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_Address_h_
#define liblldb_Address_h_
// C Includes
// C++ Includes
#include <atomic>
// Other libraries and framework includes
// Project includes
#include "lldb/lldb-private.h"
#include "lldb/Symbol/SymbolContextScope.h"
namespace lldb_private {
//----------------------------------------------------------------------
/// @class Address Address.h "lldb/Core/Address.h"
/// @brief A section + offset based address class.
///
/// The Address class allows addresses to be relative to a section
/// that can move during runtime due to images (executables, shared
/// libraries, bundles, frameworks) being loaded at different
/// addresses than the addresses found in the object file that
/// represents them on disk. There are currently two types of addresses
/// for a section:
/// @li file addresses
/// @li load addresses
///
/// File addresses represent the virtual addresses that are in the "on
/// disk" object files. These virtual addresses are converted to be
/// relative to unique sections scoped to the object file so that
/// when/if the addresses slide when the images are loaded/unloaded
/// in memory, we can easily track these changes without having to
/// update every object (compile unit ranges, line tables, function
/// address ranges, lexical block and inlined subroutine address
/// ranges, global and static variables) each time an image is loaded or
/// unloaded.
///
/// Load addresses represent the virtual addresses where each section
/// ends up getting loaded at runtime. Before executing a program, it
/// is common for all of the load addresses to be unresolved. When a
/// DynamicLoader plug-in receives notification that shared libraries
/// have been loaded/unloaded, the load addresses of the main executable
/// and any images (shared libraries) will be resolved/unresolved. When
/// this happens, breakpoints that are in one of these sections can be
/// set/cleared.
//----------------------------------------------------------------------
class Address
{
public:
//------------------------------------------------------------------
/// Dump styles allow the Address::Dump(Stream *,DumpStyle) const
/// function to display Address contents in a variety of ways.
//------------------------------------------------------------------
typedef enum {
DumpStyleInvalid, ///< Invalid dump style
DumpStyleSectionNameOffset, ///< Display as the section name + offset.
///< \code
/// // address for printf in libSystem.B.dylib as a section name + offset
/// libSystem.B.dylib.__TEXT.__text + 0x0005cfdf
/// \endcode
DumpStyleSectionPointerOffset, ///< Display as the section pointer + offset (debug output).
///< \code
/// // address for printf in libSystem.B.dylib as a section pointer + offset
/// (lldb::Section *)0x35cc50 + 0x000000000005cfdf \endcode
DumpStyleFileAddress, ///< Display as the file address (if any).
///< \code
/// // address for printf in libSystem.B.dylib as a file address
/// 0x000000000005dcff \endcode
DumpStyleModuleWithFileAddress, ///< Display as the file address with the module name prepended (if any).
///< \code
/// // address for printf in libSystem.B.dylib as a file address
/// libSystem.B.dylib[0x000000000005dcff] \endcode
DumpStyleLoadAddress, ///< Display as the load address (if resolved).
///< \code
/// // address for printf in libSystem.B.dylib as a load address
/// 0x00007fff8306bcff \endcode
DumpStyleResolvedDescription, ///< Display the details about what an address resolves to. This can
///< be anything from a symbol context summary (module, function/symbol,
///< and file and line), to information about what the pointer points to
///< if the address is in a section (section of pointers, c strings, etc).
DumpStyleResolvedDescriptionNoModule,
+ DumpStyleResolvedDescriptionNoFunctionArguments,
DumpStyleDetailedSymbolContext, ///< Detailed symbol context information for an address for all symbol
///< context members.
DumpStyleResolvedPointerDescription ///< Dereference a pointer at the current address and then lookup the
///< dereferenced address using DumpStyleResolvedDescription
} DumpStyle;
//------------------------------------------------------------------
/// Default constructor.
///
/// Initialize with a invalid section (NULL) and an invalid
/// offset (LLDB_INVALID_ADDRESS).
//------------------------------------------------------------------
Address () :
m_section_wp (),
m_offset (LLDB_INVALID_ADDRESS)
{
}
//------------------------------------------------------------------
/// Copy constructor
///
/// Makes a copy of the another Address object \a rhs.
///
/// @param[in] rhs
/// A const Address object reference to copy.
//------------------------------------------------------------------
Address (const Address& rhs) :
m_section_wp (rhs.m_section_wp),
m_offset(rhs.m_offset.load())
{
}
//------------------------------------------------------------------
/// Construct with a section pointer and offset.
///
/// Initialize the address with the supplied \a section and \a
/// offset.
///
/// @param[in] section
/// A section pointer to a valid lldb::Section, or NULL if the
/// address doesn't have a section or will get resolved later.
///
/// @param[in] offset
/// The offset in bytes into \a section.
//------------------------------------------------------------------
Address (const lldb::SectionSP &section_sp, lldb::addr_t offset) :
m_section_wp (), // Don't init with section_sp in case section_sp is invalid (the weak_ptr will throw)
m_offset (offset)
{
if (section_sp)
m_section_wp = section_sp;
}
//------------------------------------------------------------------
/// Construct with a virtual address and section list.
///
/// Initialize and resolve the address with the supplied virtual
/// address \a file_addr.
///
/// @param[in] file_addr
/// A virtual file address.
///
/// @param[in] section_list
/// A list of sections, one of which may contain the \a file_addr.
//------------------------------------------------------------------
Address (lldb::addr_t file_addr, const SectionList * section_list);
Address (lldb::addr_t abs_addr);
//------------------------------------------------------------------
/// Assignment operator.
///
/// Copies the address value from another Address object \a rhs
/// into \a this object.
///
/// @param[in] rhs
/// A const Address object reference to copy.
///
/// @return
/// A const Address object reference to \a this.
//------------------------------------------------------------------
#ifndef SWIG
const Address&
operator= (const Address& rhs);
#endif
//------------------------------------------------------------------
/// Clear the object's state.
///
/// Sets the section to an invalid value (NULL) and an invalid
/// offset (LLDB_INVALID_ADDRESS).
//------------------------------------------------------------------
void
Clear ()
{
m_section_wp.reset();
m_offset = LLDB_INVALID_ADDRESS;
}
//------------------------------------------------------------------
/// Compare two Address objects.
///
/// @param[in] lhs
/// The Left Hand Side const Address object reference.
///
/// @param[in] rhs
/// The Right Hand Side const Address object reference.
///
/// @return
/// @li -1 if lhs < rhs
/// @li 0 if lhs == rhs
/// @li 1 if lhs > rhs
//------------------------------------------------------------------
static int
CompareFileAddress (const Address& lhs, const Address& rhs);
static int
CompareLoadAddress (const Address& lhs, const Address& rhs, Target *target);
static int
CompareModulePointerAndOffset (const Address& lhs, const Address& rhs);
// For use with std::map, std::multi_map
class ModulePointerAndOffsetLessThanFunctionObject
{
public:
ModulePointerAndOffsetLessThanFunctionObject () {}
bool
operator() (const Address& a, const Address& b) const
{
return Address::CompareModulePointerAndOffset(a, b) < 0;
}
};
//------------------------------------------------------------------
/// Dump a description of this object to a Stream.
///
/// Dump a description of the contents of this object to the
/// supplied stream \a s. There are many ways to display a section
/// offset based address, and \a style lets the user choose.
///
/// @param[in] s
/// The stream to which to dump the object description.
///
/// @param[in] style
/// The display style for the address.
///
/// @param[in] fallback_style
/// The display style for the address.
///
/// @return
/// Returns \b true if the address was able to be displayed.
/// File and load addresses may be unresolved and it may not be
/// possible to display a valid value, \b false will be returned
/// in such cases.
///
/// @see Address::DumpStyle
//------------------------------------------------------------------
bool
Dump (Stream *s,
ExecutionContextScope *exe_scope,
DumpStyle style,
DumpStyle fallback_style = DumpStyleInvalid,
uint32_t addr_byte_size = UINT32_MAX) const;
lldb::AddressClass
GetAddressClass () const;
//------------------------------------------------------------------
/// Get the file address.
///
/// If an address comes from a file on disk that has section
/// relative addresses, then it has a virtual address that is
/// relative to unique section in the object file.
///
/// @return
/// The valid file virtual address, or LLDB_INVALID_ADDRESS if
/// the address doesn't have a file virtual address (image is
/// from memory only with no representation on disk).
//------------------------------------------------------------------
lldb::addr_t
GetFileAddress () const;
//------------------------------------------------------------------
/// Get the load address.
///
/// If an address comes from a file on disk that has section
/// relative addresses, then it has a virtual address that is
/// relative to unique section in the object file. Sections get
/// resolved at runtime by DynamicLoader plug-ins as images
/// (executables and shared libraries) get loaded/unloaded. If a
/// section is loaded, then the load address can be resolved.
///
/// @return
/// The valid load virtual address, or LLDB_INVALID_ADDRESS if
/// the address is currently not loaded.
//------------------------------------------------------------------
lldb::addr_t
GetLoadAddress (Target *target) const;
//------------------------------------------------------------------
/// Get the load address as a callable code load address.
///
/// This function will first resolve its address to a load address.
/// Then, if the address turns out to be in code address, return the
/// load address that would be required to call or return to. The
/// address might have extra bits set (bit zero will be set to Thumb
/// functions for an ARM target) that are required when changing the
/// program counter to setting a return address.
///
/// @return
/// The valid load virtual address, or LLDB_INVALID_ADDRESS if
/// the address is currently not loaded.
//------------------------------------------------------------------
lldb::addr_t
GetCallableLoadAddress (Target *target, bool is_indirect = false) const;
//------------------------------------------------------------------
/// Get the load address as an opcode load address.
///
/// This function will first resolve its address to a load address.
/// Then, if the address turns out to be in code address, return the
/// load address for an opcode. This address object might have
/// extra bits set (bit zero will be set to Thumb functions for an
/// ARM target) that are required for changing the program counter
/// and this function will remove any bits that are intended for
/// these special purposes. The result of this function can be used
/// to safely write a software breakpoint trap to memory.
///
/// @return
/// The valid load virtual address with extra callable bits
/// removed, or LLDB_INVALID_ADDRESS if the address is currently
/// not loaded.
//------------------------------------------------------------------
lldb::addr_t
GetOpcodeLoadAddress (Target *target) const;
//------------------------------------------------------------------
/// Get the section relative offset value.
///
/// @return
/// The current offset, or LLDB_INVALID_ADDRESS if this address
/// doesn't contain a valid offset.
//------------------------------------------------------------------
lldb::addr_t
GetOffset () const { return m_offset; }
//------------------------------------------------------------------
/// Check if an address is section offset.
///
/// When converting a virtual file or load address into a section
/// offset based address, we often need to know if, given a section
/// list, if the address was able to be converted to section offset.
/// This function returns true if the current value contained in
/// this object is section offset based.
///
/// @return
/// Returns \b true if the address has a valid section and
/// offset, \b false otherwise.
//------------------------------------------------------------------
bool
IsSectionOffset() const
{
return IsValid() && (GetSection().get() != NULL);
}
//------------------------------------------------------------------
/// Check if the object state is valid.
///
/// A valid Address object contains either a section pointer and
/// and offset (for section offset based addresses), or just a valid
/// offset (for absolute addresses that have no section).
///
/// @return
/// Returns \b true if the offset is valid, \b false
/// otherwise.
//------------------------------------------------------------------
bool
IsValid() const
{
return m_offset != LLDB_INVALID_ADDRESS;
}
//------------------------------------------------------------------
/// Get the memory cost of this object.
///
/// @return
/// The number of bytes that this object occupies in memory.
//------------------------------------------------------------------
size_t
MemorySize () const;
//------------------------------------------------------------------
/// Resolve a file virtual address using a section list.
///
/// Given a list of sections, attempt to resolve \a addr as a
/// an offset into one of the file sections.
///
/// @return
/// Returns \b true if \a addr was able to be resolved, \b false
/// otherwise.
//------------------------------------------------------------------
bool
ResolveAddressUsingFileSections (lldb::addr_t addr, const SectionList *sections);
//------------------------------------------------------------------
/// Set the address to represent \a load_addr.
///
/// The address will attempt to find a loaded section within
/// \a target that contains \a load_addr. If successful, this
/// address object will have a valid section and offset. Else this
/// address object will have no section (NULL) and the offset will
/// be \a load_addr.
///
/// @param[in] load_addr
/// A load address from a current process.
///
/// @param[in] target
/// The target to use when trying resolve the address into
/// a section + offset. The Target's SectionLoadList object
/// is used to resolve the address.
///
/// @return
/// Returns \b true if the load address was resolved to be
/// section/offset, \b false otherwise. It is often ok for an
/// address no not resolve to a section in a module, this often
/// happens for JIT'ed code, or any load addresses on the stack
/// or heap.
//------------------------------------------------------------------
bool
SetLoadAddress (lldb::addr_t load_addr, Target *target);
bool
SetOpcodeLoadAddress (lldb::addr_t load_addr, Target *target);
bool
SetCallableLoadAddress (lldb::addr_t load_addr, Target *target);
//------------------------------------------------------------------
/// Get accessor for the module for this address.
///
/// @return
/// Returns the Module pointer that this address is an offset
/// in, or NULL if this address doesn't belong in a module, or
/// isn't resolved yet.
//------------------------------------------------------------------
lldb::ModuleSP
GetModule () const;
//------------------------------------------------------------------
/// Get const accessor for the section.
///
/// @return
/// Returns the const lldb::Section pointer that this address is an
/// offset in, or NULL if this address is absolute.
//------------------------------------------------------------------
lldb::SectionSP
GetSection () const { return m_section_wp.lock(); }
//------------------------------------------------------------------
/// Set accessor for the offset.
///
/// @param[in] offset
/// A new offset value for this object.
///
/// @return
/// Returns \b true if the offset changed, \b false otherwise.
//------------------------------------------------------------------
bool
SetOffset (lldb::addr_t offset)
{
bool changed = m_offset != offset;
m_offset = offset;
return changed;
}
void
SetRawAddress (lldb::addr_t addr)
{
m_section_wp.reset();
m_offset = addr;
}
bool
Slide (int64_t offset)
{
if (m_offset != LLDB_INVALID_ADDRESS)
{
m_offset += offset;
return true;
}
return false;
}
//------------------------------------------------------------------
/// Set accessor for the section.
///
/// @param[in] section
/// A new lldb::Section pointer to use as the section base. Can
/// be NULL for absolute addresses that are not relative to
/// any section.
//------------------------------------------------------------------
void
SetSection (const lldb::SectionSP &section_sp)
{
m_section_wp = section_sp;
}
void
ClearSection ()
{
m_section_wp.reset();
}
//------------------------------------------------------------------
/// Reconstruct a symbol context from an address.
///
/// This class doesn't inherit from SymbolContextScope because many
/// address objects have short lifespans. Address objects that are
/// section offset can reconstruct their symbol context by looking
/// up the address in the module found in the section.
///
/// @see SymbolContextScope::CalculateSymbolContext(SymbolContext*)
//------------------------------------------------------------------
uint32_t
CalculateSymbolContext (SymbolContext *sc,
uint32_t resolve_scope = lldb::eSymbolContextEverything) const;
lldb::ModuleSP
CalculateSymbolContextModule () const;
CompileUnit *
CalculateSymbolContextCompileUnit () const;
Function *
CalculateSymbolContextFunction () const;
Block *
CalculateSymbolContextBlock () const;
Symbol *
CalculateSymbolContextSymbol () const;
bool
CalculateSymbolContextLineEntry (LineEntry &line_entry) const;
//------------------------------------------------------------------
// Returns true if the section should be valid, but isn't because
// the shared pointer to the section can't be reconstructed from
// a weak pointer that contains a valid weak reference to a section.
// Returns false if the section weak pointer has no reference to
// a section, or if the section is still valid
//------------------------------------------------------------------
bool
SectionWasDeleted() const;
protected:
//------------------------------------------------------------------
// Member variables.
//------------------------------------------------------------------
lldb::SectionWP m_section_wp; ///< The section for the address, can be NULL.
std::atomic<lldb::addr_t> m_offset; ///< Offset into section if \a m_section_wp is valid...
//------------------------------------------------------------------
// Returns true if the m_section_wp once had a reference to a valid
// section shared pointer, but no longer does. This can happen if
// we have an address from a module that gets unloaded and deleted.
// This function should only be called if GetSection() returns an
// empty shared pointer and you want to know if this address used to
// have a valid section.
//------------------------------------------------------------------
bool
SectionWasDeletedPrivate() const;
};
//----------------------------------------------------------------------
// NOTE: Be careful using this operator. It can correctly compare two
// addresses from the same Module correctly. It can't compare two
// addresses from different modules in any meaningful way, but it will
// compare the module pointers.
//
// To sum things up:
// - works great for addresses within the same module
// - it works for addresses across multiple modules, but don't expect the
// address results to make much sense
//
// This basically lets Address objects be used in ordered collection
// classes.
//----------------------------------------------------------------------
bool operator< (const Address& lhs, const Address& rhs);
bool operator> (const Address& lhs, const Address& rhs);
bool operator== (const Address& lhs, const Address& rhs);
bool operator!= (const Address& lhs, const Address& rhs);
} // namespace lldb_private
#endif // liblldb_Address_h_
Index: lldb/trunk/include/lldb/Core/StreamString.h
===================================================================
--- lldb/trunk/include/lldb/Core/StreamString.h (revision 219543)
+++ lldb/trunk/include/lldb/Core/StreamString.h (revision 219544)
@@ -1,64 +1,67 @@
//===-- StreamString.h ------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_StreamString_h_
#define liblldb_StreamString_h_
#include <string>
#include "lldb/Core/Stream.h"
namespace lldb_private {
class StreamString : public Stream
{
public:
StreamString ();
StreamString (uint32_t flags,
uint32_t addr_size,
lldb::ByteOrder byte_order);
virtual
~StreamString ();
virtual void
Flush ();
virtual size_t
Write (const void *s, size_t length);
void
Clear();
bool
Empty() const;
const char *
GetData () const;
size_t
GetSize() const;
+ size_t
+ GetSizeOfLastLine () const;
+
std::string &
GetString();
const std::string &
GetString() const;
void
FillLastLineToColumn (uint32_t column, char fill_char);
protected:
std::string m_packet;
};
} // namespace lldb_private
#endif // #ifndef liblldb_StreamString_h_
Index: lldb/trunk/source/Symbol/SymbolContext.cpp
===================================================================
--- lldb/trunk/source/Symbol/SymbolContext.cpp (revision 219543)
+++ lldb/trunk/source/Symbol/SymbolContext.cpp (revision 219544)
@@ -1,1205 +1,1211 @@
//===-- SymbolContext.cpp ---------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Host/Host.h"
#include "lldb/Interpreter/Args.h"
#include "lldb/Symbol/Block.h"
#include "lldb/Symbol/ClangASTContext.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolFile.h"
#include "lldb/Symbol/SymbolVendor.h"
#include "lldb/Target/Target.h"
using namespace lldb;
using namespace lldb_private;
SymbolContext::SymbolContext() :
target_sp (),
module_sp (),
comp_unit (nullptr),
function (nullptr),
block (nullptr),
line_entry (),
symbol (nullptr)
{
}
SymbolContext::SymbolContext(const ModuleSP& m, CompileUnit *cu, Function *f, Block *b, LineEntry *le, Symbol *s) :
target_sp (),
module_sp (m),
comp_unit (cu),
function (f),
block (b),
line_entry (),
symbol (s)
{
if (le)
line_entry = *le;
}
SymbolContext::SymbolContext(const TargetSP &t, const ModuleSP& m, CompileUnit *cu, Function *f, Block *b, LineEntry *le, Symbol *s) :
target_sp (t),
module_sp (m),
comp_unit (cu),
function (f),
block (b),
line_entry (),
symbol (s)
{
if (le)
line_entry = *le;
}
SymbolContext::SymbolContext(const SymbolContext& rhs) :
target_sp (rhs.target_sp),
module_sp (rhs.module_sp),
comp_unit (rhs.comp_unit),
function (rhs.function),
block (rhs.block),
line_entry (rhs.line_entry),
symbol (rhs.symbol)
{
}
SymbolContext::SymbolContext (SymbolContextScope *sc_scope) :
target_sp (),
module_sp (),
comp_unit (nullptr),
function (nullptr),
block (nullptr),
line_entry (),
symbol (nullptr)
{
sc_scope->CalculateSymbolContext (this);
}
SymbolContext::~SymbolContext ()
{
}
const SymbolContext&
SymbolContext::operator= (const SymbolContext& rhs)
{
if (this != &rhs)
{
target_sp = rhs.target_sp;
module_sp = rhs.module_sp;
comp_unit = rhs.comp_unit;
function = rhs.function;
block = rhs.block;
line_entry = rhs.line_entry;
symbol = rhs.symbol;
}
return *this;
}
void
SymbolContext::Clear(bool clear_target)
{
if (clear_target)
target_sp.reset();
module_sp.reset();
comp_unit = nullptr;
function = nullptr;
block = nullptr;
line_entry.Clear();
symbol = nullptr;
}
bool
SymbolContext::DumpStopContext
(
Stream *s,
ExecutionContextScope *exe_scope,
const Address &addr,
bool show_fullpaths,
bool show_module,
- bool show_inlined_frames
+ bool show_inlined_frames,
+ bool show_function_arguments
) const
{
bool dumped_something = false;
if (show_module && module_sp)
{
if (show_fullpaths)
*s << module_sp->GetFileSpec();
else
*s << module_sp->GetFileSpec().GetFilename();
s->PutChar('`');
dumped_something = true;
}
if (function != nullptr)
{
SymbolContext inline_parent_sc;
Address inline_parent_addr;
- if (function->GetMangled().GetName())
+ if (show_function_arguments == false && function->GetMangled().GetName(Mangled::ePreferDemangledWithoutArguments))
+ {
+ dumped_something = true;
+ function->GetMangled().GetName(Mangled::ePreferDemangledWithoutArguments).Dump(s);
+ }
+ else if (function->GetMangled().GetName())
{
dumped_something = true;
function->GetMangled().GetName().Dump(s);
}
if (addr.IsValid())
{
const addr_t function_offset = addr.GetOffset() - function->GetAddressRange().GetBaseAddress().GetOffset();
if (function_offset)
{
dumped_something = true;
s->Printf(" + %" PRIu64, function_offset);
}
}
if (GetParentOfInlinedScope (addr, inline_parent_sc, inline_parent_addr))
{
dumped_something = true;
Block *inlined_block = block->GetContainingInlinedBlock();
const InlineFunctionInfo* inlined_block_info = inlined_block->GetInlinedFunctionInfo();
s->Printf (" [inlined] %s", inlined_block_info->GetName().GetCString());
lldb_private::AddressRange block_range;
if (inlined_block->GetRangeContainingAddress(addr, block_range))
{
const addr_t inlined_function_offset = addr.GetOffset() - block_range.GetBaseAddress().GetOffset();
if (inlined_function_offset)
{
s->Printf(" + %" PRIu64, inlined_function_offset);
}
}
const Declaration &call_site = inlined_block_info->GetCallSite();
if (call_site.IsValid())
{
s->PutCString(" at ");
call_site.DumpStopContext (s, show_fullpaths);
}
if (show_inlined_frames)
{
s->EOL();
s->Indent();
- return inline_parent_sc.DumpStopContext (s, exe_scope, inline_parent_addr, show_fullpaths, show_module, show_inlined_frames);
+ return inline_parent_sc.DumpStopContext (s, exe_scope, inline_parent_addr, show_fullpaths, show_module, show_inlined_frames, show_function_arguments);
}
}
else
{
if (line_entry.IsValid())
{
dumped_something = true;
s->PutCString(" at ");
if (line_entry.DumpStopContext(s, show_fullpaths))
dumped_something = true;
}
}
}
else if (symbol != nullptr)
{
if (symbol->GetMangled().GetName())
{
dumped_something = true;
if (symbol->GetType() == eSymbolTypeTrampoline)
s->PutCString("symbol stub for: ");
symbol->GetMangled().GetName().Dump(s);
}
if (addr.IsValid() && symbol->ValueIsAddress())
{
const addr_t symbol_offset = addr.GetOffset() - symbol->GetAddress().GetOffset();
if (symbol_offset)
{
dumped_something = true;
s->Printf(" + %" PRIu64, symbol_offset);
}
}
}
else if (addr.IsValid())
{
addr.Dump(s, exe_scope, Address::DumpStyleModuleWithFileAddress);
dumped_something = true;
}
return dumped_something;
}
void
SymbolContext::GetDescription(Stream *s, lldb::DescriptionLevel level, Target *target) const
{
if (module_sp)
{
s->Indent(" Module: file = \"");
module_sp->GetFileSpec().Dump(s);
*s << '"';
if (module_sp->GetArchitecture().IsValid())
s->Printf (", arch = \"%s\"", module_sp->GetArchitecture().GetArchitectureName());
s->EOL();
}
if (comp_unit != nullptr)
{
s->Indent("CompileUnit: ");
comp_unit->GetDescription (s, level);
s->EOL();
}
if (function != nullptr)
{
s->Indent(" Function: ");
function->GetDescription (s, level, target);
s->EOL();
Type *func_type = function->GetType();
if (func_type)
{
s->Indent(" FuncType: ");
func_type->GetDescription (s, level, false);
s->EOL();
}
}
if (block != nullptr)
{
std::vector<Block *> blocks;
blocks.push_back (block);
Block *parent_block = block->GetParent();
while (parent_block)
{
blocks.push_back (parent_block);
parent_block = parent_block->GetParent();
}
std::vector<Block *>::reverse_iterator pos;
std::vector<Block *>::reverse_iterator begin = blocks.rbegin();
std::vector<Block *>::reverse_iterator end = blocks.rend();
for (pos = begin; pos != end; ++pos)
{
if (pos == begin)
s->Indent(" Blocks: ");
else
s->Indent(" ");
(*pos)->GetDescription(s, function, level, target);
s->EOL();
}
}
if (line_entry.IsValid())
{
s->Indent(" LineEntry: ");
line_entry.GetDescription (s, level, comp_unit, target, false);
s->EOL();
}
if (symbol != nullptr)
{
s->Indent(" Symbol: ");
symbol->GetDescription(s, level, target);
s->EOL();
}
}
uint32_t
SymbolContext::GetResolvedMask () const
{
uint32_t resolved_mask = 0;
if (target_sp) resolved_mask |= eSymbolContextTarget;
if (module_sp) resolved_mask |= eSymbolContextModule;
if (comp_unit) resolved_mask |= eSymbolContextCompUnit;
if (function) resolved_mask |= eSymbolContextFunction;
if (block) resolved_mask |= eSymbolContextBlock;
if (line_entry.IsValid()) resolved_mask |= eSymbolContextLineEntry;
if (symbol) resolved_mask |= eSymbolContextSymbol;
return resolved_mask;
}
void
SymbolContext::Dump(Stream *s, Target *target) const
{
*s << (void *)this << ": ";
s->Indent();
s->PutCString("SymbolContext");
s->IndentMore();
s->EOL();
s->IndentMore();
s->Indent();
*s << "Module = " << (void *)module_sp.get() << ' ';
if (module_sp)
module_sp->GetFileSpec().Dump(s);
s->EOL();
s->Indent();
*s << "CompileUnit = " << (void *)comp_unit;
if (comp_unit != nullptr)
*s << " {0x" << comp_unit->GetID() << "} " << *(static_cast<FileSpec*> (comp_unit));
s->EOL();
s->Indent();
*s << "Function = " << (void *)function;
if (function != nullptr)
{
*s << " {0x" << function->GetID() << "} " << function->GetType()->GetName() << ", address-range = ";
function->GetAddressRange().Dump(s, target, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress);
s->EOL();
s->Indent();
Type* func_type = function->GetType();
if (func_type)
{
*s << " Type = ";
func_type->Dump (s, false);
}
}
s->EOL();
s->Indent();
*s << "Block = " << (void *)block;
if (block != nullptr)
*s << " {0x" << block->GetID() << '}';
// Dump the block and pass it a negative depth to we print all the parent blocks
//if (block != NULL)
// block->Dump(s, function->GetFileAddress(), INT_MIN);
s->EOL();
s->Indent();
*s << "LineEntry = ";
line_entry.Dump (s, target, true, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress, true);
s->EOL();
s->Indent();
*s << "Symbol = " << (void *)symbol;
if (symbol != nullptr && symbol->GetMangled())
*s << ' ' << symbol->GetMangled().GetName().AsCString();
s->EOL();
s->IndentLess();
s->IndentLess();
}
bool
lldb_private::operator== (const SymbolContext& lhs, const SymbolContext& rhs)
{
return lhs.function == rhs.function
&& lhs.symbol == rhs.symbol
&& lhs.module_sp.get() == rhs.module_sp.get()
&& lhs.comp_unit == rhs.comp_unit
&& lhs.target_sp.get() == rhs.target_sp.get()
&& LineEntry::Compare(lhs.line_entry, rhs.line_entry) == 0;
}
bool
lldb_private::operator!= (const SymbolContext& lhs, const SymbolContext& rhs)
{
return lhs.function != rhs.function
|| lhs.symbol != rhs.symbol
|| lhs.module_sp.get() != rhs.module_sp.get()
|| lhs.comp_unit != rhs.comp_unit
|| lhs.target_sp.get() != rhs.target_sp.get()
|| LineEntry::Compare(lhs.line_entry, rhs.line_entry) != 0;
}
bool
SymbolContext::GetAddressRange (uint32_t scope,
uint32_t range_idx,
bool use_inline_block_range,
AddressRange &range) const
{
if ((scope & eSymbolContextLineEntry) && line_entry.IsValid())
{
range = line_entry.range;
return true;
}
if ((scope & eSymbolContextBlock) && (block != nullptr))
{
if (use_inline_block_range)
{
Block *inline_block = block->GetContainingInlinedBlock();
if (inline_block)
return inline_block->GetRangeAtIndex (range_idx, range);
}
else
{
return block->GetRangeAtIndex (range_idx, range);
}
}
if ((scope & eSymbolContextFunction) && (function != nullptr))
{
if (range_idx == 0)
{
range = function->GetAddressRange();
return true;
}
}
if ((scope & eSymbolContextSymbol) && (symbol != nullptr))
{
if (range_idx == 0)
{
if (symbol->ValueIsAddress())
{
range.GetBaseAddress() = symbol->GetAddress();
range.SetByteSize (symbol->GetByteSize());
return true;
}
}
}
range.Clear();
return false;
}
bool
SymbolContext::GetParentOfInlinedScope (const Address &curr_frame_pc,
SymbolContext &next_frame_sc,
Address &next_frame_pc) const
{
next_frame_sc.Clear(false);
next_frame_pc.Clear();
if (block)
{
//const addr_t curr_frame_file_addr = curr_frame_pc.GetFileAddress();
// In order to get the parent of an inlined function we first need to
// see if we are in an inlined block as "this->block" could be an
// inlined block, or a parent of "block" could be. So lets check if
// this block or one of this blocks parents is an inlined function.
Block *curr_inlined_block = block->GetContainingInlinedBlock();
if (curr_inlined_block)
{
// "this->block" is contained in an inline function block, so to
// get the scope above the inlined block, we get the parent of the
// inlined block itself
Block *next_frame_block = curr_inlined_block->GetParent();
// Now calculate the symbol context of the containing block
next_frame_block->CalculateSymbolContext (&next_frame_sc);
// If we get here we weren't able to find the return line entry using the nesting of the blocks and
// the line table. So just use the call site info from our inlined block.
AddressRange range;
if (curr_inlined_block->GetRangeContainingAddress (curr_frame_pc, range))
{
// To see there this new frame block it, we need to look at the
// call site information from
const InlineFunctionInfo* curr_inlined_block_inlined_info = curr_inlined_block->GetInlinedFunctionInfo();
next_frame_pc = range.GetBaseAddress();
next_frame_sc.line_entry.range.GetBaseAddress() = next_frame_pc;
next_frame_sc.line_entry.file = curr_inlined_block_inlined_info->GetCallSite().GetFile();
next_frame_sc.line_entry.line = curr_inlined_block_inlined_info->GetCallSite().GetLine();
next_frame_sc.line_entry.column = curr_inlined_block_inlined_info->GetCallSite().GetColumn();
return true;
}
else
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYMBOLS));
if (log)
{
log->Printf ("warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64,
curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress());
}
#ifdef LLDB_CONFIGURATION_DEBUG
else
{
ObjectFile *objfile = NULL;
if (module_sp)
{
SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor();
if (symbol_vendor)
{
SymbolFile *symbol_file = symbol_vendor->GetSymbolFile();
if (symbol_file)
objfile = symbol_file->GetObjectFile();
}
}
if (objfile)
{
Host::SystemLog (Host::eSystemLogWarning,
"warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64 " in %s\n",
curr_inlined_block->GetID(),
curr_frame_pc.GetFileAddress(),
objfile->GetFileSpec().GetPath().c_str());
}
else
{
Host::SystemLog (Host::eSystemLogWarning,
"warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64 "\n",
curr_inlined_block->GetID(),
curr_frame_pc.GetFileAddress());
}
}
#endif
}
}
}
return false;
}
Block *
SymbolContext::GetFunctionBlock ()
{
if (function)
{
if (block)
{
// If this symbol context has a block, check to see if this block
// is itself, or is contained within a block with inlined function
// information. If so, then the inlined block is the block that
// defines the function.
Block *inlined_block = block->GetContainingInlinedBlock();
if (inlined_block)
return inlined_block;
// The block in this symbol context is not inside an inlined
// block, so the block that defines the function is the function's
// top level block, which is returned below.
}
// There is no block information in this symbol context, so we must
// assume that the block that is desired is the top level block of
// the function itself.
return &function->GetBlock(true);
}
return nullptr;
}
bool
SymbolContext::GetFunctionMethodInfo (lldb::LanguageType &language,
bool &is_instance_method,
ConstString &language_object_name)
{
Block *function_block = GetFunctionBlock ();
if (function_block)
{
clang::DeclContext *decl_context = function_block->GetClangDeclContext();
if (decl_context)
{
return ClangASTContext::GetClassMethodInfoForDeclContext (decl_context,
language,
is_instance_method,
language_object_name);
}
}
language = eLanguageTypeUnknown;
is_instance_method = false;
language_object_name.Clear();
return false;
}
ConstString
SymbolContext::GetFunctionName (Mangled::NamePreference preference) const
{
if (function)
{
if (block)
{
Block *inlined_block = block->GetContainingInlinedBlock();
if (inlined_block)
{
const InlineFunctionInfo *inline_info = inlined_block->GetInlinedFunctionInfo();
if (inline_info)
return inline_info->GetName();
}
}
return function->GetMangled().GetName(preference);
}
else if (symbol && symbol->ValueIsAddress())
{
return symbol->GetMangled().GetName(preference);
}
else
{
// No function, return an empty string.
return ConstString();
}
}
LineEntry
SymbolContext::GetFunctionStartLineEntry () const
{
LineEntry line_entry;
Address start_addr;
if (block)
{
Block *inlined_block = block->GetContainingInlinedBlock();
if (inlined_block)
{
if (inlined_block->GetStartAddress (start_addr))
{
if (start_addr.CalculateSymbolContextLineEntry (line_entry))
return line_entry;
}
return LineEntry();
}
}
if (function)
{
if (function->GetAddressRange().GetBaseAddress().CalculateSymbolContextLineEntry(line_entry))
return line_entry;
}
return LineEntry();
}
//----------------------------------------------------------------------
//
// SymbolContextSpecifier
//
//----------------------------------------------------------------------
SymbolContextSpecifier::SymbolContextSpecifier (const TargetSP &target_sp) :
m_target_sp (target_sp),
m_module_spec (),
m_module_sp (),
m_file_spec_ap (),
m_start_line (0),
m_end_line (0),
m_function_spec (),
m_class_name (),
m_address_range_ap (),
m_type (eNothingSpecified)
{
}
SymbolContextSpecifier::~SymbolContextSpecifier()
{
}
bool
SymbolContextSpecifier::AddLineSpecification (uint32_t line_no, SpecificationType type)
{
bool return_value = true;
switch (type)
{
case eNothingSpecified:
Clear();
break;
case eLineStartSpecified:
m_start_line = line_no;
m_type |= eLineStartSpecified;
break;
case eLineEndSpecified:
m_end_line = line_no;
m_type |= eLineEndSpecified;
break;
default:
return_value = false;
break;
}
return return_value;
}
bool
SymbolContextSpecifier::AddSpecification (const char *spec_string, SpecificationType type)
{
bool return_value = true;
switch (type)
{
case eNothingSpecified:
Clear();
break;
case eModuleSpecified:
{
// See if we can find the Module, if so stick it in the SymbolContext.
FileSpec module_file_spec(spec_string, false);
ModuleSpec module_spec (module_file_spec);
lldb::ModuleSP module_sp (m_target_sp->GetImages().FindFirstModule (module_spec));
m_type |= eModuleSpecified;
if (module_sp)
m_module_sp = module_sp;
else
m_module_spec.assign (spec_string);
}
break;
case eFileSpecified:
// CompUnits can't necessarily be resolved here, since an inlined function might show up in
// a number of CompUnits. Instead we just convert to a FileSpec and store it away.
m_file_spec_ap.reset (new FileSpec (spec_string, false));
m_type |= eFileSpecified;
break;
case eLineStartSpecified:
m_start_line = Args::StringToSInt32(spec_string, 0, 0, &return_value);
if (return_value)
m_type |= eLineStartSpecified;
break;
case eLineEndSpecified:
m_end_line = Args::StringToSInt32(spec_string, 0, 0, &return_value);
if (return_value)
m_type |= eLineEndSpecified;
break;
case eFunctionSpecified:
m_function_spec.assign(spec_string);
m_type |= eFunctionSpecified;
break;
case eClassOrNamespaceSpecified:
Clear();
m_class_name.assign (spec_string);
m_type = eClassOrNamespaceSpecified;
break;
case eAddressRangeSpecified:
// Not specified yet...
break;
}
return return_value;
}
void
SymbolContextSpecifier::Clear()
{
m_module_spec.clear();
m_file_spec_ap.reset();
m_function_spec.clear();
m_class_name.clear();
m_start_line = 0;
m_end_line = 0;
m_address_range_ap.reset();
m_type = eNothingSpecified;
}
bool
SymbolContextSpecifier::SymbolContextMatches(SymbolContext &sc)
{
if (m_type == eNothingSpecified)
return true;
if (m_target_sp.get() != sc.target_sp.get())
return false;
if (m_type & eModuleSpecified)
{
if (sc.module_sp)
{
if (m_module_sp.get() != nullptr)
{
if (m_module_sp.get() != sc.module_sp.get())
return false;
}
else
{
FileSpec module_file_spec (m_module_spec.c_str(), false);
if (!FileSpec::Equal (module_file_spec, sc.module_sp->GetFileSpec(), false))
return false;
}
}
}
if (m_type & eFileSpecified)
{
if (m_file_spec_ap.get())
{
// If we don't have a block or a comp_unit, then we aren't going to match a source file.
if (sc.block == nullptr && sc.comp_unit == nullptr)
return false;
// Check if the block is present, and if so is it inlined:
bool was_inlined = false;
if (sc.block != nullptr)
{
const InlineFunctionInfo *inline_info = sc.block->GetInlinedFunctionInfo();
if (inline_info != nullptr)
{
was_inlined = true;
if (!FileSpec::Equal (inline_info->GetDeclaration().GetFile(), *(m_file_spec_ap.get()), false))
return false;
}
}
// Next check the comp unit, but only if the SymbolContext was not inlined.
if (!was_inlined && sc.comp_unit != nullptr)
{
if (!FileSpec::Equal (*(sc.comp_unit), *(m_file_spec_ap.get()), false))
return false;
}
}
}
if (m_type & eLineStartSpecified
|| m_type & eLineEndSpecified)
{
if (sc.line_entry.line < m_start_line || sc.line_entry.line > m_end_line)
return false;
}
if (m_type & eFunctionSpecified)
{
// First check the current block, and if it is inlined, get the inlined function name:
bool was_inlined = false;
ConstString func_name(m_function_spec.c_str());
if (sc.block != nullptr)
{
const InlineFunctionInfo *inline_info = sc.block->GetInlinedFunctionInfo();
if (inline_info != nullptr)
{
was_inlined = true;
const Mangled &name = inline_info->GetMangled();
if (!name.NameMatches (func_name))
return false;
}
}
// If it wasn't inlined, check the name in the function or symbol:
if (!was_inlined)
{
if (sc.function != nullptr)
{
if (!sc.function->GetMangled().NameMatches(func_name))
return false;
}
else if (sc.symbol != nullptr)
{
if (!sc.symbol->GetMangled().NameMatches(func_name))
return false;
}
}
}
return true;
}
bool
SymbolContextSpecifier::AddressMatches(lldb::addr_t addr)
{
if (m_type & eAddressRangeSpecified)
{
}
else
{
Address match_address (addr, nullptr);
SymbolContext sc;
m_target_sp->GetImages().ResolveSymbolContextForAddress(match_address, eSymbolContextEverything, sc);
return SymbolContextMatches(sc);
}
return true;
}
void
SymbolContextSpecifier::GetDescription (Stream *s, lldb::DescriptionLevel level) const
{
char path_str[PATH_MAX + 1];
if (m_type == eNothingSpecified)
{
s->Printf ("Nothing specified.\n");
}
if (m_type == eModuleSpecified)
{
s->Indent();
if (m_module_sp)
{
m_module_sp->GetFileSpec().GetPath (path_str, PATH_MAX);
s->Printf ("Module: %s\n", path_str);
}
else
s->Printf ("Module: %s\n", m_module_spec.c_str());
}
if (m_type == eFileSpecified && m_file_spec_ap.get() != nullptr)
{
m_file_spec_ap->GetPath (path_str, PATH_MAX);
s->Indent();
s->Printf ("File: %s", path_str);
if (m_type == eLineStartSpecified)
{
s->Printf (" from line %" PRIu64 "", (uint64_t)m_start_line);
if (m_type == eLineEndSpecified)
s->Printf ("to line %" PRIu64 "", (uint64_t)m_end_line);
else
s->Printf ("to end");
}
else if (m_type == eLineEndSpecified)
{
s->Printf (" from start to line %" PRIu64 "", (uint64_t)m_end_line);
}
s->Printf (".\n");
}
if (m_type == eLineStartSpecified)
{
s->Indent();
s->Printf ("From line %" PRIu64 "", (uint64_t)m_start_line);
if (m_type == eLineEndSpecified)
s->Printf ("to line %" PRIu64 "", (uint64_t)m_end_line);
else
s->Printf ("to end");
s->Printf (".\n");
}
else if (m_type == eLineEndSpecified)
{
s->Printf ("From start to line %" PRIu64 ".\n", (uint64_t)m_end_line);
}
if (m_type == eFunctionSpecified)
{
s->Indent();
s->Printf ("Function: %s.\n", m_function_spec.c_str());
}
if (m_type == eClassOrNamespaceSpecified)
{
s->Indent();
s->Printf ("Class name: %s.\n", m_class_name.c_str());
}
if (m_type == eAddressRangeSpecified && m_address_range_ap.get() != nullptr)
{
s->Indent();
s->PutCString ("Address range: ");
m_address_range_ap->Dump (s, m_target_sp.get(), Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress);
s->PutCString ("\n");
}
}
//----------------------------------------------------------------------
//
// SymbolContextList
//
//----------------------------------------------------------------------
SymbolContextList::SymbolContextList() :
m_symbol_contexts()
{
}
SymbolContextList::~SymbolContextList()
{
}
void
SymbolContextList::Append(const SymbolContext& sc)
{
m_symbol_contexts.push_back(sc);
}
void
SymbolContextList::Append (const SymbolContextList& sc_list)
{
collection::const_iterator pos, end = sc_list.m_symbol_contexts.end();
for (pos = sc_list.m_symbol_contexts.begin(); pos != end; ++pos)
m_symbol_contexts.push_back (*pos);
}
uint32_t
SymbolContextList::AppendIfUnique (const SymbolContextList& sc_list, bool merge_symbol_into_function)
{
uint32_t unique_sc_add_count = 0;
collection::const_iterator pos, end = sc_list.m_symbol_contexts.end();
for (pos = sc_list.m_symbol_contexts.begin(); pos != end; ++pos)
{
if (AppendIfUnique (*pos, merge_symbol_into_function))
++unique_sc_add_count;
}
return unique_sc_add_count;
}
bool
SymbolContextList::AppendIfUnique (const SymbolContext& sc, bool merge_symbol_into_function)
{
collection::iterator pos, end = m_symbol_contexts.end();
for (pos = m_symbol_contexts.begin(); pos != end; ++pos)
{
if (*pos == sc)
return false;
}
if (merge_symbol_into_function
&& sc.symbol != nullptr
&& sc.comp_unit == nullptr
&& sc.function == nullptr
&& sc.block == nullptr
&& sc.line_entry.IsValid() == false)
{
if (sc.symbol->ValueIsAddress())
{
for (pos = m_symbol_contexts.begin(); pos != end; ++pos)
{
// Don't merge symbols into inlined function symbol contexts
if (pos->block && pos->block->GetContainingInlinedBlock())
continue;
if (pos->function)
{
if (pos->function->GetAddressRange().GetBaseAddress() == sc.symbol->GetAddress())
{
// Do we already have a function with this symbol?
if (pos->symbol == sc.symbol)
return false;
if (pos->symbol == nullptr)
{
pos->symbol = sc.symbol;
return false;
}
}
}
}
}
}
m_symbol_contexts.push_back(sc);
return true;
}
bool
SymbolContextList::MergeSymbolContextIntoFunctionContext (const SymbolContext& symbol_sc,
uint32_t start_idx,
uint32_t stop_idx)
{
if (symbol_sc.symbol != nullptr
&& symbol_sc.comp_unit == nullptr
&& symbol_sc.function == nullptr
&& symbol_sc.block == nullptr
&& symbol_sc.line_entry.IsValid() == false)
{
if (symbol_sc.symbol->ValueIsAddress())
{
const size_t end = std::min<size_t>(m_symbol_contexts.size(), stop_idx);
for (size_t i=start_idx; i<end; ++i)
{
const SymbolContext &function_sc = m_symbol_contexts[i];
// Don't merge symbols into inlined function symbol contexts
if (function_sc.block && function_sc.block->GetContainingInlinedBlock())
continue;
if (function_sc.function)
{
if (function_sc.function->GetAddressRange().GetBaseAddress() == symbol_sc.symbol->GetAddress())
{
// Do we already have a function with this symbol?
if (function_sc.symbol == symbol_sc.symbol)
return true; // Already have a symbol context with this symbol, return true
if (function_sc.symbol == nullptr)
{
// We successfully merged this symbol into an existing symbol context
m_symbol_contexts[i].symbol = symbol_sc.symbol;
return true;
}
}
}
}
}
}
return false;
}
void
SymbolContextList::Clear()
{
m_symbol_contexts.clear();
}
void
SymbolContextList::Dump(Stream *s, Target *target) const
{
*s << (void *)this << ": ";
s->Indent();
s->PutCString("SymbolContextList");
s->EOL();
s->IndentMore();
collection::const_iterator pos, end = m_symbol_contexts.end();
for (pos = m_symbol_contexts.begin(); pos != end; ++pos)
{
//pos->Dump(s, target);
pos->GetDescription(s, eDescriptionLevelVerbose, target);
}
s->IndentLess();
}
bool
SymbolContextList::GetContextAtIndex(size_t idx, SymbolContext& sc) const
{
if (idx < m_symbol_contexts.size())
{
sc = m_symbol_contexts[idx];
return true;
}
return false;
}
bool
SymbolContextList::GetLastContext(SymbolContext& sc) const
{
if (!m_symbol_contexts.empty())
{
sc = m_symbol_contexts.back();
return true;
}
return false;
}
bool
SymbolContextList::RemoveContextAtIndex (size_t idx)
{
if (idx < m_symbol_contexts.size())
{
m_symbol_contexts.erase(m_symbol_contexts.begin() + idx);
return true;
}
return false;
}
uint32_t
SymbolContextList::GetSize() const
{
return m_symbol_contexts.size();
}
uint32_t
SymbolContextList::NumLineEntriesWithLine (uint32_t line) const
{
uint32_t match_count = 0;
const size_t size = m_symbol_contexts.size();
for (size_t idx = 0; idx<size; ++idx)
{
if (m_symbol_contexts[idx].line_entry.line == line)
++match_count;
}
return match_count;
}
void
SymbolContextList::GetDescription(Stream *s,
lldb::DescriptionLevel level,
Target *target) const
{
const size_t size = m_symbol_contexts.size();
for (size_t idx = 0; idx<size; ++idx)
m_symbol_contexts[idx].GetDescription (s, level, target);
}
bool
lldb_private::operator== (const SymbolContextList& lhs, const SymbolContextList& rhs)
{
const uint32_t size = lhs.GetSize();
if (size != rhs.GetSize())
return false;
SymbolContext lhs_sc;
SymbolContext rhs_sc;
for (uint32_t i=0; i<size; ++i)
{
lhs.GetContextAtIndex(i, lhs_sc);
rhs.GetContextAtIndex(i, rhs_sc);
if (lhs_sc != rhs_sc)
return false;
}
return true;
}
bool
lldb_private::operator!= (const SymbolContextList& lhs, const SymbolContextList& rhs)
{
return !(lhs == rhs);
}
Index: lldb/trunk/source/Symbol/Variable.cpp
===================================================================
--- lldb/trunk/source/Symbol/Variable.cpp (revision 219543)
+++ lldb/trunk/source/Symbol/Variable.cpp (revision 219544)
@@ -1,904 +1,906 @@
//===-- Variable.cpp --------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/Symbol/Variable.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/Stream.h"
#include "lldb/Core/RegularExpression.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Core/ValueObjectVariable.h"
#include "lldb/Symbol/Block.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/ABI.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/Target.h"
using namespace lldb;
using namespace lldb_private;
//----------------------------------------------------------------------
// Variable constructor
//----------------------------------------------------------------------
Variable::Variable
(
lldb::user_id_t uid,
const char *name,
const char *mangled, // The mangled variable name for variables in namespaces
const lldb::SymbolFileTypeSP &symfile_type_sp,
ValueType scope,
SymbolContextScope *context,
Declaration* decl_ptr,
const DWARFExpression& location,
bool external,
bool artificial
) :
UserID(uid),
m_name(name),
m_mangled (ConstString(mangled), true),
m_symfile_type_sp(symfile_type_sp),
m_scope(scope),
m_owner_scope(context),
m_declaration(decl_ptr),
m_location(location),
m_external(external),
m_artificial(artificial)
{
}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
Variable::~Variable()
{
}
const ConstString&
Variable::GetName() const
{
if (m_mangled)
return m_mangled.GetName();
return m_name;
}
bool
Variable::NameMatches (const RegularExpression& regex) const
{
if (regex.Execute (m_name.AsCString()))
return true;
return m_mangled.NameMatches (regex);
}
Type *
Variable::GetType()
{
if (m_symfile_type_sp)
return m_symfile_type_sp->GetType();
return nullptr;
}
void
Variable::Dump(Stream *s, bool show_context) const
{
s->Printf("%p: ", static_cast<const void*>(this));
s->Indent();
*s << "Variable" << (const UserID&)*this;
if (m_name)
*s << ", name = \"" << m_name << "\"";
if (m_symfile_type_sp)
{
Type *type = m_symfile_type_sp->GetType();
if (type)
{
*s << ", type = {" << type->GetID() << "} " << (void*)type << " (";
type->DumpTypeName(s);
s->PutChar(')');
}
}
if (m_scope != eValueTypeInvalid)
{
s->PutCString(", scope = ");
switch (m_scope)
{
case eValueTypeVariableGlobal: s->PutCString(m_external ? "global" : "static"); break;
case eValueTypeVariableArgument: s->PutCString("parameter"); break;
case eValueTypeVariableLocal: s->PutCString("local"); break;
default: *s << "??? (" << m_scope << ')';
}
}
if (show_context && m_owner_scope != nullptr)
{
s->PutCString(", context = ( ");
m_owner_scope->DumpSymbolContext(s);
s->PutCString(" )");
}
bool show_fullpaths = false;
m_declaration.Dump(s, show_fullpaths);
if (m_location.IsValid())
{
s->PutCString(", location = ");
lldb::addr_t loclist_base_addr = LLDB_INVALID_ADDRESS;
if (m_location.IsLocationList())
{
SymbolContext variable_sc;
m_owner_scope->CalculateSymbolContext(&variable_sc);
if (variable_sc.function)
loclist_base_addr = variable_sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
}
ABI *abi = nullptr;
if (m_owner_scope)
{
ModuleSP module_sp (m_owner_scope->CalculateSymbolContextModule());
if (module_sp)
abi = ABI::FindPlugin (module_sp->GetArchitecture()).get();
}
m_location.GetDescription(s, lldb::eDescriptionLevelBrief, loclist_base_addr, abi);
}
if (m_external)
s->PutCString(", external");
if (m_artificial)
s->PutCString(", artificial");
s->EOL();
}
bool
Variable::DumpDeclaration (Stream *s, bool show_fullpaths, bool show_module)
{
bool dumped_declaration_info = false;
if (m_owner_scope)
{
SymbolContext sc;
m_owner_scope->CalculateSymbolContext(&sc);
sc.block = nullptr;
sc.line_entry.Clear();
bool show_inlined_frames = false;
+ const bool show_function_arguments = true;
dumped_declaration_info = sc.DumpStopContext (s,
nullptr,
Address(),
show_fullpaths,
show_module,
- show_inlined_frames);
+ show_inlined_frames,
+ show_function_arguments);
if (sc.function)
s->PutChar(':');
}
if (m_declaration.DumpStopContext (s, false))
dumped_declaration_info = true;
return dumped_declaration_info;
}
size_t
Variable::MemorySize() const
{
return sizeof(Variable);
}
void
Variable::CalculateSymbolContext (SymbolContext *sc)
{
if (m_owner_scope)
m_owner_scope->CalculateSymbolContext(sc);
else
sc->Clear(false);
}
bool
Variable::LocationIsValidForFrame (StackFrame *frame)
{
// Is the variable is described by a single location?
if (!m_location.IsLocationList())
{
// Yes it is, the location is valid.
return true;
}
if (frame)
{
Function *function = frame->GetSymbolContext(eSymbolContextFunction).function;
if (function)
{
TargetSP target_sp (frame->CalculateTarget());
addr_t loclist_base_load_addr = function->GetAddressRange().GetBaseAddress().GetLoadAddress (target_sp.get());
if (loclist_base_load_addr == LLDB_INVALID_ADDRESS)
return false;
// It is a location list. We just need to tell if the location
// list contains the current address when converted to a load
// address
return m_location.LocationListContainsAddress (loclist_base_load_addr,
frame->GetFrameCodeAddress().GetLoadAddress (target_sp.get()));
}
}
return false;
}
bool
Variable::LocationIsValidForAddress (const Address &address)
{
// Be sure to resolve the address to section offset prior to
// calling this function.
if (address.IsSectionOffset())
{
SymbolContext sc;
CalculateSymbolContext(&sc);
if (sc.module_sp == address.GetModule())
{
// Is the variable is described by a single location?
if (!m_location.IsLocationList())
{
// Yes it is, the location is valid.
return true;
}
if (sc.function)
{
addr_t loclist_base_file_addr = sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
return false;
// It is a location list. We just need to tell if the location
// list contains the current address when converted to a load
// address
return m_location.LocationListContainsAddress (loclist_base_file_addr,
address.GetFileAddress());
}
}
}
return false;
}
bool
Variable::IsInScope (StackFrame *frame)
{
switch (m_scope)
{
case eValueTypeRegister:
case eValueTypeRegisterSet:
return frame != nullptr;
case eValueTypeConstResult:
case eValueTypeVariableGlobal:
case eValueTypeVariableStatic:
return true;
case eValueTypeVariableArgument:
case eValueTypeVariableLocal:
if (frame)
{
// We don't have a location list, we just need to see if the block
// that this variable was defined in is currently
Block *deepest_frame_block = frame->GetSymbolContext(eSymbolContextBlock).block;
if (deepest_frame_block)
{
SymbolContext variable_sc;
CalculateSymbolContext (&variable_sc);
// Check for static or global variable defined at the compile unit
// level that wasn't defined in a block
if (variable_sc.block == nullptr)
return true;
if (variable_sc.block == deepest_frame_block)
return true;
return variable_sc.block->Contains (deepest_frame_block);
}
}
break;
default:
break;
}
return false;
}
Error
Variable::GetValuesForVariableExpressionPath (const char *variable_expr_path,
ExecutionContextScope *scope,
GetVariableCallback callback,
void *baton,
VariableList &variable_list,
ValueObjectList &valobj_list)
{
Error error;
if (variable_expr_path && callback)
{
switch (variable_expr_path[0])
{
case '*':
{
error = Variable::GetValuesForVariableExpressionPath (variable_expr_path + 1,
scope,
callback,
baton,
variable_list,
valobj_list);
if (error.Success())
{
for (uint32_t i=0; i<valobj_list.GetSize(); )
{
Error tmp_error;
ValueObjectSP valobj_sp (valobj_list.GetValueObjectAtIndex(i)->Dereference(tmp_error));
if (tmp_error.Fail())
{
variable_list.RemoveVariableAtIndex (i);
valobj_list.RemoveValueObjectAtIndex (i);
}
else
{
valobj_list.SetValueObjectAtIndex (i, valobj_sp);
++i;
}
}
}
else
{
error.SetErrorString ("unknown error");
}
return error;
}
break;
case '&':
{
error = Variable::GetValuesForVariableExpressionPath (variable_expr_path + 1,
scope,
callback,
baton,
variable_list,
valobj_list);
if (error.Success())
{
for (uint32_t i=0; i<valobj_list.GetSize(); )
{
Error tmp_error;
ValueObjectSP valobj_sp (valobj_list.GetValueObjectAtIndex(i)->AddressOf(tmp_error));
if (tmp_error.Fail())
{
variable_list.RemoveVariableAtIndex (i);
valobj_list.RemoveValueObjectAtIndex (i);
}
else
{
valobj_list.SetValueObjectAtIndex (i, valobj_sp);
++i;
}
}
}
else
{
error.SetErrorString ("unknown error");
}
return error;
}
break;
default:
{
static RegularExpression g_regex ("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)");
RegularExpression::Match regex_match(1);
if (g_regex.Execute(variable_expr_path, &regex_match))
{
std::string variable_name;
if (regex_match.GetMatchAtIndex(variable_expr_path, 1, variable_name))
{
variable_list.Clear();
if (callback (baton, variable_name.c_str(), variable_list))
{
uint32_t i=0;
while (i < variable_list.GetSize())
{
VariableSP var_sp (variable_list.GetVariableAtIndex (i));
ValueObjectSP valobj_sp;
if (var_sp)
{
ValueObjectSP variable_valobj_sp(ValueObjectVariable::Create (scope, var_sp));
if (variable_valobj_sp)
{
const char *variable_sub_expr_path = variable_expr_path + variable_name.size();
if (*variable_sub_expr_path)
{
const char* first_unparsed = nullptr;
ValueObject::ExpressionPathScanEndReason reason_to_stop;
ValueObject::ExpressionPathEndResultType final_value_type;
ValueObject::GetValueForExpressionPathOptions options;
ValueObject::ExpressionPathAftermath final_task_on_target;
valobj_sp = variable_valobj_sp->GetValueForExpressionPath (variable_sub_expr_path,
&first_unparsed,
&reason_to_stop,
&final_value_type,
options,
&final_task_on_target);
if (!valobj_sp)
{
error.SetErrorStringWithFormat ("invalid expression path '%s' for variable '%s'",
variable_sub_expr_path,
var_sp->GetName().GetCString());
}
}
else
{
// Just the name of a variable with no extras
valobj_sp = variable_valobj_sp;
}
}
}
if (!var_sp || !valobj_sp)
{
variable_list.RemoveVariableAtIndex (i);
}
else
{
valobj_list.Append(valobj_sp);
++i;
}
}
if (variable_list.GetSize() > 0)
{
error.Clear();
return error;
}
}
}
}
error.SetErrorStringWithFormat ("unable to extract a variable name from '%s'", variable_expr_path);
}
break;
}
}
error.SetErrorString ("unknown error");
return error;
}
bool
Variable::DumpLocationForAddress (Stream *s, const Address &address)
{
// Be sure to resolve the address to section offset prior to
// calling this function.
if (address.IsSectionOffset())
{
SymbolContext sc;
CalculateSymbolContext(&sc);
if (sc.module_sp == address.GetModule())
{
ABI *abi = nullptr;
if (m_owner_scope)
{
ModuleSP module_sp (m_owner_scope->CalculateSymbolContextModule());
if (module_sp)
abi = ABI::FindPlugin (module_sp->GetArchitecture()).get();
}
const addr_t file_addr = address.GetFileAddress();
if (sc.function)
{
if (sc.function->GetAddressRange().ContainsFileAddress(address))
{
addr_t loclist_base_file_addr = sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
return false;
return m_location.DumpLocationForAddress (s,
eDescriptionLevelBrief,
loclist_base_file_addr,
file_addr,
abi);
}
}
return m_location.DumpLocationForAddress (s,
eDescriptionLevelBrief,
LLDB_INVALID_ADDRESS,
file_addr,
abi);
}
}
return false;
}
static void
PrivateAutoComplete (StackFrame *frame,
const std::string &partial_path,
const std::string &prefix_path, // Anything that has been resolved already will be in here
const ClangASTType& clang_type,
StringList &matches,
bool &word_complete);
static void
PrivateAutoCompleteMembers (StackFrame *frame,
const std::string &partial_member_name,
const std::string &partial_path,
const std::string &prefix_path, // Anything that has been resolved already will be in here
const ClangASTType& clang_type,
StringList &matches,
bool &word_complete);
static void
PrivateAutoCompleteMembers (StackFrame *frame,
const std::string &partial_member_name,
const std::string &partial_path,
const std::string &prefix_path, // Anything that has been resolved already will be in here
const ClangASTType& clang_type,
StringList &matches,
bool &word_complete)
{
// We are in a type parsing child members
const uint32_t num_bases = clang_type.GetNumDirectBaseClasses();
if (num_bases > 0)
{
for (uint32_t i = 0; i < num_bases; ++i)
{
ClangASTType base_class_type (clang_type.GetDirectBaseClassAtIndex (i, nullptr));
PrivateAutoCompleteMembers (frame,
partial_member_name,
partial_path,
prefix_path,
base_class_type.GetCanonicalType(),
matches,
word_complete);
}
}
const uint32_t num_vbases = clang_type.GetNumVirtualBaseClasses();
if (num_vbases > 0)
{
for (uint32_t i = 0; i < num_vbases; ++i)
{
ClangASTType vbase_class_type (clang_type.GetVirtualBaseClassAtIndex(i,nullptr));
PrivateAutoCompleteMembers (frame,
partial_member_name,
partial_path,
prefix_path,
vbase_class_type.GetCanonicalType(),
matches,
word_complete);
}
}
// We are in a type parsing child members
const uint32_t num_fields = clang_type.GetNumFields();
if (num_fields > 0)
{
for (uint32_t i = 0; i < num_fields; ++i)
{
std::string member_name;
ClangASTType member_clang_type = clang_type.GetFieldAtIndex (i, member_name, nullptr, nullptr, nullptr);
if (partial_member_name.empty() ||
member_name.find(partial_member_name) == 0)
{
if (member_name == partial_member_name)
{
PrivateAutoComplete (frame,
partial_path,
prefix_path + member_name, // Anything that has been resolved already will be in here
member_clang_type.GetCanonicalType(),
matches,
word_complete);
}
else
{
matches.AppendString (prefix_path + member_name);
}
}
}
}
}
static void
PrivateAutoComplete (StackFrame *frame,
const std::string &partial_path,
const std::string &prefix_path, // Anything that has been resolved already will be in here
const ClangASTType& clang_type,
StringList &matches,
bool &word_complete)
{
// printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path = '%s'\n", prefix_path.c_str(), partial_path.c_str());
std::string remaining_partial_path;
const lldb::TypeClass type_class = clang_type.GetTypeClass();
if (partial_path.empty())
{
if (clang_type.IsValid())
{
switch (type_class)
{
default:
case eTypeClassArray:
case eTypeClassBlockPointer:
case eTypeClassBuiltin:
case eTypeClassComplexFloat:
case eTypeClassComplexInteger:
case eTypeClassEnumeration:
case eTypeClassFunction:
case eTypeClassMemberPointer:
case eTypeClassReference:
case eTypeClassTypedef:
case eTypeClassVector:
{
matches.AppendString (prefix_path);
word_complete = matches.GetSize() == 1;
}
break;
case eTypeClassClass:
case eTypeClassStruct:
case eTypeClassUnion:
if (prefix_path.back() != '.')
matches.AppendString (prefix_path + '.');
break;
case eTypeClassObjCObject:
case eTypeClassObjCInterface:
break;
case eTypeClassObjCObjectPointer:
case eTypeClassPointer:
{
bool omit_empty_base_classes = true;
if (clang_type.GetNumChildren (omit_empty_base_classes) > 0)
matches.AppendString (prefix_path + "->");
else
{
matches.AppendString (prefix_path);
word_complete = true;
}
}
break;
}
}
else
{
if (frame)
{
const bool get_file_globals = true;
VariableList *variable_list = frame->GetVariableList(get_file_globals);
if (variable_list)
{
const size_t num_variables = variable_list->GetSize();
for (size_t i=0; i<num_variables; ++i)
{
Variable *variable = variable_list->GetVariableAtIndex(i).get();
matches.AppendString (variable->GetName().AsCString());
}
}
}
}
}
else
{
const char ch = partial_path[0];
switch (ch)
{
case '*':
if (prefix_path.empty())
{
PrivateAutoComplete (frame,
partial_path.substr(1),
std::string("*"),
clang_type,
matches,
word_complete);
}
break;
case '&':
if (prefix_path.empty())
{
PrivateAutoComplete (frame,
partial_path.substr(1),
std::string("&"),
clang_type,
matches,
word_complete);
}
break;
case '-':
if (partial_path[1] == '>' && !prefix_path.empty())
{
switch (type_class)
{
case lldb::eTypeClassPointer:
{
ClangASTType pointee_type(clang_type.GetPointeeType());
if (partial_path[2])
{
// If there is more after the "->", then search deeper
PrivateAutoComplete (frame,
partial_path.substr(2),
prefix_path + "->",
pointee_type.GetCanonicalType(),
matches,
word_complete);
}
else
{
// Nothing after the "->", so list all members
PrivateAutoCompleteMembers (frame,
std::string(),
std::string(),
prefix_path + "->",
pointee_type.GetCanonicalType(),
matches,
word_complete);
}
}
default:
break;
}
}
break;
case '.':
if (clang_type.IsValid())
{
switch (type_class)
{
case lldb::eTypeClassUnion:
case lldb::eTypeClassStruct:
case lldb::eTypeClassClass:
if (partial_path[1])
{
// If there is more after the ".", then search deeper
PrivateAutoComplete (frame,
partial_path.substr(1),
prefix_path + ".",
clang_type,
matches,
word_complete);
}
else
{
// Nothing after the ".", so list all members
PrivateAutoCompleteMembers (frame,
std::string(),
partial_path,
prefix_path + ".",
clang_type,
matches,
word_complete);
}
default:
break;
}
}
break;
default:
if (isalpha(ch) || ch == '_' || ch == '$')
{
const size_t partial_path_len = partial_path.size();
size_t pos = 1;
while (pos < partial_path_len)
{
const char curr_ch = partial_path[pos];
if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$')
{
++pos;
continue;
}
break;
}
std::string token(partial_path, 0, pos);
remaining_partial_path = partial_path.substr(pos);
if (clang_type.IsValid())
{
PrivateAutoCompleteMembers (frame,
token,
remaining_partial_path,
prefix_path,
clang_type,
matches,
word_complete);
}
else if (frame)
{
// We haven't found our variable yet
const bool get_file_globals = true;
VariableList *variable_list = frame->GetVariableList(get_file_globals);
if (!variable_list)
break;
const size_t num_variables = variable_list->GetSize();
for (size_t i=0; i<num_variables; ++i)
{
Variable *variable = variable_list->GetVariableAtIndex(i).get();
if (!variable)
continue;
const char *variable_name = variable->GetName().AsCString();
if (strstr(variable_name, token.c_str()) == variable_name)
{
if (strcmp (variable_name, token.c_str()) == 0)
{
Type *variable_type = variable->GetType();
if (variable_type)
{
ClangASTType variable_clang_type (variable_type->GetClangForwardType());
PrivateAutoComplete (frame,
remaining_partial_path,
prefix_path + token, // Anything that has been resolved already will be in here
variable_clang_type.GetCanonicalType(),
matches,
word_complete);
}
else
{
matches.AppendString (prefix_path + variable_name);
}
}
else if (remaining_partial_path.empty())
{
matches.AppendString (prefix_path + variable_name);
}
}
}
}
}
break;
}
}
}
size_t
Variable::AutoComplete (const ExecutionContext &exe_ctx,
const char *partial_path_cstr,
StringList &matches,
bool &word_complete)
{
word_complete = false;
std::string partial_path;
std::string prefix_path;
ClangASTType clang_type;
if (partial_path_cstr && partial_path_cstr[0])
partial_path = partial_path_cstr;
PrivateAutoComplete (exe_ctx.GetFramePtr(),
partial_path,
prefix_path,
clang_type,
matches,
word_complete);
return matches.GetSize();
}
Index: lldb/trunk/source/API/SBInstructionList.cpp
===================================================================
--- lldb/trunk/source/API/SBInstructionList.cpp (revision 219543)
+++ lldb/trunk/source/API/SBInstructionList.cpp (revision 219544)
@@ -1,132 +1,146 @@
//===-- SBInstructionList.cpp -----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/API/SBInstructionList.h"
#include "lldb/API/SBInstruction.h"
#include "lldb/API/SBStream.h"
#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
#include "lldb/Core/Stream.h"
+#include "lldb/Symbol/SymbolContext.h"
using namespace lldb;
using namespace lldb_private;
SBInstructionList::SBInstructionList () :
m_opaque_sp()
{
}
SBInstructionList::SBInstructionList(const SBInstructionList &rhs) :
m_opaque_sp (rhs.m_opaque_sp)
{
}
const SBInstructionList &
SBInstructionList::operator = (const SBInstructionList &rhs)
{
if (this != &rhs)
m_opaque_sp = rhs.m_opaque_sp;
return *this;
}
SBInstructionList::~SBInstructionList ()
{
}
bool
SBInstructionList::IsValid () const
{
return m_opaque_sp.get() != NULL;
}
size_t
SBInstructionList::GetSize ()
{
if (m_opaque_sp)
return m_opaque_sp->GetInstructionList().GetSize();
return 0;
}
SBInstruction
SBInstructionList::GetInstructionAtIndex (uint32_t idx)
{
SBInstruction inst;
if (m_opaque_sp && idx < m_opaque_sp->GetInstructionList().GetSize())
inst.SetOpaque (m_opaque_sp->GetInstructionList().GetInstructionAtIndex (idx));
return inst;
}
void
SBInstructionList::Clear ()
{
m_opaque_sp.reset();
}
void
SBInstructionList::AppendInstruction (SBInstruction insn)
{
}
void
SBInstructionList::SetDisassembler (const lldb::DisassemblerSP &opaque_sp)
{
m_opaque_sp = opaque_sp;
}
void
SBInstructionList::Print (FILE *out)
{
if (out == NULL)
return;
}
bool
SBInstructionList::GetDescription (lldb::SBStream &description)
{
if (m_opaque_sp)
{
size_t num_instructions = GetSize ();
if (num_instructions)
{
// Call the ref() to make sure a stream is created if one deesn't
// exist already inside description...
Stream &sref = description.ref();
const uint32_t max_opcode_byte_size = m_opaque_sp->GetInstructionList().GetMaxOpcocdeByteSize();
+ const char *disassemble_format = "${addr-file-or-load}: ";
+ SymbolContext sc;
+ SymbolContext prev_sc;
for (size_t i=0; i<num_instructions; ++i)
{
Instruction *inst = m_opaque_sp->GetInstructionList().GetInstructionAtIndex (i).get();
if (inst == NULL)
break;
- inst->Dump (&sref, max_opcode_byte_size, true, false, NULL);
+
+ const Address &addr = inst->GetAddress();
+ prev_sc = sc;
+ ModuleSP module_sp (addr.GetModule());
+ if (module_sp)
+ {
+ module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc);
+ }
+
+ inst->Dump (&sref, max_opcode_byte_size, true, false, NULL, &sc, &prev_sc, disassemble_format);
sref.EOL();
}
return true;
}
}
return false;
}
bool
SBInstructionList::DumpEmulationForAllInstructions (const char *triple)
{
if (m_opaque_sp)
{
size_t len = GetSize();
for (size_t i = 0; i < len; ++i)
{
if (!GetInstructionAtIndex((uint32_t) i).DumpEmulation (triple))
return false;
}
}
return true;
}
Index: lldb/trunk/source/API/SBInstruction.cpp
===================================================================
--- lldb/trunk/source/API/SBInstruction.cpp (revision 219543)
+++ lldb/trunk/source/API/SBInstruction.cpp (revision 219544)
@@ -1,248 +1,261 @@
//===-- SBInstruction.cpp ---------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/API/SBInstruction.h"
#include "lldb/API/SBAddress.h"
#include "lldb/API/SBFrame.h"
#include "lldb/API/SBInstruction.h"
#include "lldb/API/SBStream.h"
#include "lldb/API/SBTarget.h"
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Core/DataExtractor.h"
#include "lldb/Core/Disassembler.h"
#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Core/Module.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
using namespace lldb;
using namespace lldb_private;
SBInstruction::SBInstruction ()
{
}
SBInstruction::SBInstruction (const lldb::InstructionSP& inst_sp) :
m_opaque_sp (inst_sp)
{
}
SBInstruction::SBInstruction(const SBInstruction &rhs) :
m_opaque_sp (rhs.m_opaque_sp)
{
}
const SBInstruction &
SBInstruction::operator = (const SBInstruction &rhs)
{
if (this != &rhs)
m_opaque_sp = rhs.m_opaque_sp;
return *this;
}
SBInstruction::~SBInstruction ()
{
}
bool
SBInstruction::IsValid()
{
return (m_opaque_sp.get() != NULL);
}
SBAddress
SBInstruction::GetAddress()
{
SBAddress sb_addr;
if (m_opaque_sp && m_opaque_sp->GetAddress().IsValid())
sb_addr.SetAddress(&m_opaque_sp->GetAddress());
return sb_addr;
}
const char *
SBInstruction::GetMnemonic(SBTarget target)
{
if (m_opaque_sp)
{
Mutex::Locker api_locker;
ExecutionContext exe_ctx;
TargetSP target_sp (target.GetSP());
if (target_sp)
{
api_locker.Lock (target_sp->GetAPIMutex());
target_sp->CalculateExecutionContext (exe_ctx);
exe_ctx.SetProcessSP(target_sp->GetProcessSP());
}
return m_opaque_sp->GetMnemonic(&exe_ctx);
}
return NULL;
}
const char *
SBInstruction::GetOperands(SBTarget target)
{
if (m_opaque_sp)
{
Mutex::Locker api_locker;
ExecutionContext exe_ctx;
TargetSP target_sp (target.GetSP());
if (target_sp)
{
api_locker.Lock (target_sp->GetAPIMutex());
target_sp->CalculateExecutionContext (exe_ctx);
exe_ctx.SetProcessSP(target_sp->GetProcessSP());
}
return m_opaque_sp->GetOperands(&exe_ctx);
}
return NULL;
}
const char *
SBInstruction::GetComment(SBTarget target)
{
if (m_opaque_sp)
{
Mutex::Locker api_locker;
ExecutionContext exe_ctx;
TargetSP target_sp (target.GetSP());
if (target_sp)
{
api_locker.Lock (target_sp->GetAPIMutex());
target_sp->CalculateExecutionContext (exe_ctx);
exe_ctx.SetProcessSP(target_sp->GetProcessSP());
}
return m_opaque_sp->GetComment(&exe_ctx);
}
return NULL;
}
size_t
SBInstruction::GetByteSize ()
{
if (m_opaque_sp)
return m_opaque_sp->GetOpcode().GetByteSize();
return 0;
}
SBData
SBInstruction::GetData (SBTarget target)
{
lldb::SBData sb_data;
if (m_opaque_sp)
{
DataExtractorSP data_extractor_sp (new DataExtractor());
if (m_opaque_sp->GetData (*data_extractor_sp))
{
sb_data.SetOpaque (data_extractor_sp);
}
}
return sb_data;
}
bool
SBInstruction::DoesBranch ()
{
if (m_opaque_sp)
return m_opaque_sp->DoesBranch ();
return false;
}
void
SBInstruction::SetOpaque (const lldb::InstructionSP &inst_sp)
{
m_opaque_sp = inst_sp;
}
bool
SBInstruction::GetDescription (lldb::SBStream &s)
{
if (m_opaque_sp)
{
+ SymbolContext sc;
+ const Address &addr = m_opaque_sp->GetAddress();
+ ModuleSP module_sp (addr.GetModule());
+ if (module_sp)
+ module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc);
// Use the "ref()" instead of the "get()" accessor in case the SBStream
// didn't have a stream already created, one will get created...
- m_opaque_sp->Dump (&s.ref(), 0, true, false, NULL);
+ const char *disassemble_format = "${addr-file-or-load}: ";
+ m_opaque_sp->Dump (&s.ref(), 0, true, false, NULL, &sc, NULL, disassemble_format);
return true;
}
return false;
}
void
SBInstruction::Print (FILE *out)
{
if (out == NULL)
return;
if (m_opaque_sp)
{
+ SymbolContext sc;
+ const Address &addr = m_opaque_sp->GetAddress();
+ ModuleSP module_sp (addr.GetModule());
+ if (module_sp)
+ module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc);
StreamFile out_stream (out, false);
- m_opaque_sp->Dump (&out_stream, 0, true, false, NULL);
+ const char *disassemble_format = "${addr-file-or-load}: ";
+ m_opaque_sp->Dump (&out_stream, 0, true, false, NULL, &sc, NULL, disassemble_format);
}
}
bool
SBInstruction::EmulateWithFrame (lldb::SBFrame &frame, uint32_t evaluate_options)
{
if (m_opaque_sp)
{
lldb::StackFrameSP frame_sp (frame.GetFrameSP());
if (frame_sp)
{
lldb_private::ExecutionContext exe_ctx;
frame_sp->CalculateExecutionContext (exe_ctx);
lldb_private::Target *target = exe_ctx.GetTargetPtr();
lldb_private::ArchSpec arch = target->GetArchitecture();
return m_opaque_sp->Emulate (arch,
evaluate_options,
(void *) frame_sp.get(),
&lldb_private::EmulateInstruction::ReadMemoryFrame,
&lldb_private::EmulateInstruction::WriteMemoryFrame,
&lldb_private::EmulateInstruction::ReadRegisterFrame,
&lldb_private::EmulateInstruction::WriteRegisterFrame);
}
}
return false;
}
bool
SBInstruction::DumpEmulation (const char *triple)
{
if (m_opaque_sp && triple)
{
lldb_private::ArchSpec arch (triple, NULL);
return m_opaque_sp->DumpEmulation (arch);
}
return false;
}
bool
SBInstruction::TestEmulation (lldb::SBStream &output_stream, const char *test_file)
{
if (!m_opaque_sp.get())
m_opaque_sp.reset (new PseudoInstruction());
return m_opaque_sp->TestEmulation (output_stream.get(), test_file);
}
lldb::AddressClass
SBInstruction::GetAddressClass ()
{
if (m_opaque_sp.get())
return m_opaque_sp->GetAddressClass();
return eAddressClassInvalid;
}
Index: lldb/trunk/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp
===================================================================
--- lldb/trunk/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp (revision 219543)
+++ lldb/trunk/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp (revision 219544)
@@ -1,681 +1,682 @@
//===-- UnwindAssemblyInstEmulation.cpp --------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "UnwindAssemblyInstEmulation.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Core/DataExtractor.h"
#include "lldb/Core/Disassembler.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/Target.h"
using namespace lldb;
using namespace lldb_private;
//-----------------------------------------------------------------------------------------------
// UnwindAssemblyInstEmulation method definitions
//-----------------------------------------------------------------------------------------------
bool
UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& range,
Thread& thread,
UnwindPlan& unwind_plan)
{
if (range.GetByteSize() > 0 &&
range.GetBaseAddress().IsValid() &&
m_inst_emulator_ap.get())
{
// The instruction emulation subclass setup the unwind plan for the
// first instruction.
m_inst_emulator_ap->CreateFunctionEntryUnwind (unwind_plan);
// CreateFunctionEntryUnwind should have created the first row. If it
// doesn't, then we are done.
if (unwind_plan.GetRowCount() == 0)
return false;
ExecutionContext exe_ctx;
thread.CalculateExecutionContext(exe_ctx);
const bool prefer_file_cache = true;
DisassemblerSP disasm_sp (Disassembler::DisassembleRange (m_arch,
NULL,
NULL,
exe_ctx,
range,
prefer_file_cache));
Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
if (disasm_sp)
{
m_range_ptr = &range;
m_thread_ptr = &thread;
m_unwind_plan_ptr = &unwind_plan;
const uint32_t addr_byte_size = m_arch.GetAddressByteSize();
const bool show_address = true;
const bool show_bytes = true;
m_inst_emulator_ap->GetRegisterInfo (unwind_plan.GetRegisterKind(),
unwind_plan.GetInitialCFARegister(),
m_cfa_reg_info);
m_fp_is_cfa = false;
m_register_values.clear();
m_pushed_regs.clear();
// Initialize the CFA with a known value. In the 32 bit case
// it will be 0x80000000, and in the 64 bit case 0x8000000000000000.
// We use the address byte size to be safe for any future address sizes
m_initial_sp = (1ull << ((addr_byte_size * 8) - 1));
RegisterValue cfa_reg_value;
cfa_reg_value.SetUInt (m_initial_sp, m_cfa_reg_info.byte_size);
SetRegisterValue (m_cfa_reg_info, cfa_reg_value);
const InstructionList &inst_list = disasm_sp->GetInstructionList ();
const size_t num_instructions = inst_list.GetSize();
if (num_instructions > 0)
{
Instruction *inst = inst_list.GetInstructionAtIndex (0).get();
const addr_t base_addr = inst->GetAddress().GetFileAddress();
// Make a copy of the current instruction Row and save it in m_curr_row
// so we can add updates as we process the instructions.
UnwindPlan::RowSP last_row = unwind_plan.GetLastRow();
UnwindPlan::Row *newrow = new UnwindPlan::Row;
if (last_row.get())
*newrow = *last_row.get();
m_curr_row.reset(newrow);
// Once we've seen the initial prologue instructions complete, save a
// copy of the CFI at that point into prologue_completed_row for possible
// use later.
int instructions_since_last_prologue_insn = 0; // # of insns since last CFI was update
bool reinstate_prologue_next_instruction = false; // Next iteration, re-install the prologue row of CFI
bool last_instruction_restored_return_addr_reg = false; // re-install the prologue row of CFI if the next instruction is a branch immediate
bool return_address_register_has_been_saved = false; // if we've seen the ra register get saved yet
UnwindPlan::RowSP prologue_completed_row; // copy of prologue row of CFI
// cache the pc register number (in whatever register numbering this UnwindPlan uses) for
// quick reference during instruction parsing.
uint32_t pc_reg_num = LLDB_INVALID_REGNUM;
RegisterInfo pc_reg_info;
if (m_inst_emulator_ap->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc_reg_info))
pc_reg_num = pc_reg_info.kinds[unwind_plan.GetRegisterKind()];
else
pc_reg_num = LLDB_INVALID_REGNUM;
// cache the return address register number (in whatever register numbering this UnwindPlan uses) for
// quick reference during instruction parsing.
uint32_t ra_reg_num = LLDB_INVALID_REGNUM;
RegisterInfo ra_reg_info;
if (m_inst_emulator_ap->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, ra_reg_info))
ra_reg_num = ra_reg_info.kinds[unwind_plan.GetRegisterKind()];
else
ra_reg_num = LLDB_INVALID_REGNUM;
for (size_t idx=0; idx<num_instructions; ++idx)
{
m_curr_row_modified = false;
m_curr_insn_restored_a_register = false;
inst = inst_list.GetInstructionAtIndex (idx).get();
if (inst)
{
if (log && log->GetVerbose ())
{
StreamString strm;
- inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize (), show_address, show_bytes, NULL);
+ const char *disassemble_format = "${frame.pc}: ";
+ inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize (), show_address, show_bytes, NULL, NULL, NULL, disassemble_format);
log->PutCString (strm.GetData());
}
m_inst_emulator_ap->SetInstruction (inst->GetOpcode(),
inst->GetAddress(),
exe_ctx.GetTargetPtr());
m_inst_emulator_ap->EvaluateInstruction (eEmulateInstructionOptionIgnoreConditions);
// Were there any changes to the CFI while evaluating this instruction?
if (m_curr_row_modified)
{
reinstate_prologue_next_instruction = false;
m_curr_row->SetOffset (inst->GetAddress().GetFileAddress() + inst->GetOpcode().GetByteSize() - base_addr);
// Append the new row
unwind_plan.AppendRow (m_curr_row);
// Allocate a new Row for m_curr_row, copy the current state into it
UnwindPlan::Row *newrow = new UnwindPlan::Row;
*newrow = *m_curr_row.get();
m_curr_row.reset(newrow);
// If m_curr_insn_restored_a_register == true, we're looking at an epilogue instruction.
// Set instructions_since_last_prologue_insn to a very high number so we don't append
// any of these epilogue instructions to our prologue_complete row.
if (m_curr_insn_restored_a_register == false && instructions_since_last_prologue_insn < 8)
instructions_since_last_prologue_insn = 0;
else
instructions_since_last_prologue_insn = 99;
UnwindPlan::Row::RegisterLocation pc_regloc;
UnwindPlan::Row::RegisterLocation ra_regloc;
// While parsing the instructions of this function, if we've ever
// seen the return address register (aka lr on arm) in a non-IsSame() state,
// it has been saved on the stack. If it's ever back to IsSame(), we've
// executed an epilogue.
if (ra_reg_num != LLDB_INVALID_REGNUM
&& m_curr_row->GetRegisterInfo (ra_reg_num, ra_regloc)
&& !ra_regloc.IsSame())
{
return_address_register_has_been_saved = true;
}
// If the caller's pc is "same", we've just executed an epilogue and we return to the caller
// after this instruction completes executing.
// If there are any instructions past this, there must have been flow control over this
// epilogue so we'll reinstate the original prologue setup instructions.
if (prologue_completed_row.get()
&& pc_reg_num != LLDB_INVALID_REGNUM
&& m_curr_row->GetRegisterInfo (pc_reg_num, pc_regloc)
&& pc_regloc.IsSame())
{
if (log && log->GetVerbose())
log->Printf("UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly -- pc is <same>, restore prologue instructions.");
reinstate_prologue_next_instruction = true;
}
else if (prologue_completed_row.get()
&& return_address_register_has_been_saved
&& ra_reg_num != LLDB_INVALID_REGNUM
&& m_curr_row->GetRegisterInfo (ra_reg_num, ra_regloc)
&& ra_regloc.IsSame())
{
if (log && log->GetVerbose())
log->Printf("UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly -- lr is <same>, restore prologue instruction if the next instruction is a branch immediate.");
last_instruction_restored_return_addr_reg = true;
}
}
else
{
// If the previous instruction was a return-to-caller (epilogue), and we're still executing
// instructions in this function, there must be a code path that jumps over that epilogue.
// Also detect the case where we epilogue & branch imm to another function (tail-call opt)
// instead of a normal pop lr-into-pc exit.
// Reinstate the frame setup from the prologue.
if (reinstate_prologue_next_instruction
|| (m_curr_insn_is_branch_immediate && last_instruction_restored_return_addr_reg))
{
if (log && log->GetVerbose())
log->Printf("UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly -- Reinstating prologue instruction set");
UnwindPlan::Row *newrow = new UnwindPlan::Row;
*newrow = *prologue_completed_row.get();
m_curr_row.reset(newrow);
m_curr_row->SetOffset (inst->GetAddress().GetFileAddress() + inst->GetOpcode().GetByteSize() - base_addr);
unwind_plan.AppendRow(m_curr_row);
newrow = new UnwindPlan::Row;
*newrow = *m_curr_row.get();
m_curr_row.reset(newrow);
reinstate_prologue_next_instruction = false;
last_instruction_restored_return_addr_reg = false;
m_curr_insn_is_branch_immediate = false;
}
// clear both of these if either one wasn't set
if (last_instruction_restored_return_addr_reg)
{
last_instruction_restored_return_addr_reg = false;
}
if (m_curr_insn_is_branch_immediate)
{
m_curr_insn_is_branch_immediate = false;
}
// Stop updating the prologue instructions if we've seen 8 non-prologue instructions
// in a row.
if (instructions_since_last_prologue_insn++ < 8)
{
UnwindPlan::Row *newrow = new UnwindPlan::Row;
*newrow = *m_curr_row.get();
prologue_completed_row.reset(newrow);
if (log && log->GetVerbose())
log->Printf("UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly -- saving a copy of the current row as the prologue row.");
}
}
}
}
}
// FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions.
// I'll fix that but for now, just clear the list and it will go away nicely.
disasm_sp->GetInstructionList().Clear();
}
if (log && log->GetVerbose ())
{
StreamString strm;
lldb::addr_t base_addr = range.GetBaseAddress().GetLoadAddress(thread.CalculateTarget().get());
strm.Printf ("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):", base_addr, base_addr + range.GetByteSize());
unwind_plan.Dump(strm, &thread, base_addr);
log->PutCString (strm.GetData());
}
return unwind_plan.GetRowCount() > 0;
}
return false;
}
bool
UnwindAssemblyInstEmulation::AugmentUnwindPlanFromCallSite (AddressRange& func,
Thread& thread,
UnwindPlan& unwind_plan)
{
return false;
}
bool
UnwindAssemblyInstEmulation::GetFastUnwindPlan (AddressRange& func,
Thread& thread,
UnwindPlan &unwind_plan)
{
return false;
}
bool
UnwindAssemblyInstEmulation::FirstNonPrologueInsn (AddressRange& func,
const ExecutionContext &exe_ctx,
Address& first_non_prologue_insn)
{
return false;
}
UnwindAssembly *
UnwindAssemblyInstEmulation::CreateInstance (const ArchSpec &arch)
{
std::unique_ptr<EmulateInstruction> inst_emulator_ap (EmulateInstruction::FindPlugin (arch, eInstructionTypePrologueEpilogue, NULL));
// Make sure that all prologue instructions are handled
if (inst_emulator_ap.get())
return new UnwindAssemblyInstEmulation (arch, inst_emulator_ap.release());
return NULL;
}
//------------------------------------------------------------------
// PluginInterface protocol in UnwindAssemblyParser_x86
//------------------------------------------------------------------
ConstString
UnwindAssemblyInstEmulation::GetPluginName()
{
return GetPluginNameStatic();
}
uint32_t
UnwindAssemblyInstEmulation::GetPluginVersion()
{
return 1;
}
void
UnwindAssemblyInstEmulation::Initialize()
{
PluginManager::RegisterPlugin (GetPluginNameStatic(),
GetPluginDescriptionStatic(),
CreateInstance);
}
void
UnwindAssemblyInstEmulation::Terminate()
{
PluginManager::UnregisterPlugin (CreateInstance);
}
ConstString
UnwindAssemblyInstEmulation::GetPluginNameStatic()
{
static ConstString g_name("inst-emulation");
return g_name;
}
const char *
UnwindAssemblyInstEmulation::GetPluginDescriptionStatic()
{
return "Instruction emulation based unwind information.";
}
uint64_t
UnwindAssemblyInstEmulation::MakeRegisterKindValuePair (const RegisterInfo &reg_info)
{
lldb::RegisterKind reg_kind;
uint32_t reg_num;
if (EmulateInstruction::GetBestRegisterKindAndNumber (&reg_info, reg_kind, reg_num))
return (uint64_t)reg_kind << 24 | reg_num;
return 0ull;
}
void
UnwindAssemblyInstEmulation::SetRegisterValue (const RegisterInfo &reg_info, const RegisterValue &reg_value)
{
m_register_values[MakeRegisterKindValuePair (reg_info)] = reg_value;
}
bool
UnwindAssemblyInstEmulation::GetRegisterValue (const RegisterInfo &reg_info, RegisterValue &reg_value)
{
const uint64_t reg_id = MakeRegisterKindValuePair (reg_info);
RegisterValueMap::const_iterator pos = m_register_values.find(reg_id);
if (pos != m_register_values.end())
{
reg_value = pos->second;
return true; // We had a real value that comes from an opcode that wrote
// to it...
}
// We are making up a value that is recognizable...
reg_value.SetUInt(reg_id, reg_info.byte_size);
return false;
}
size_t
UnwindAssemblyInstEmulation::ReadMemory (EmulateInstruction *instruction,
void *baton,
const EmulateInstruction::Context &context,
lldb::addr_t addr,
void *dst,
size_t dst_len)
{
Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
if (log && log->GetVerbose ())
{
StreamString strm;
strm.Printf ("UnwindAssemblyInstEmulation::ReadMemory (addr = 0x%16.16" PRIx64 ", dst = %p, dst_len = %" PRIu64 ", context = ",
addr,
dst,
(uint64_t)dst_len);
context.Dump(strm, instruction);
log->PutCString (strm.GetData ());
}
memset (dst, 0, dst_len);
return dst_len;
}
size_t
UnwindAssemblyInstEmulation::WriteMemory (EmulateInstruction *instruction,
void *baton,
const EmulateInstruction::Context &context,
lldb::addr_t addr,
const void *dst,
size_t dst_len)
{
if (baton && dst && dst_len)
return ((UnwindAssemblyInstEmulation *)baton)->WriteMemory (instruction, context, addr, dst, dst_len);
return 0;
}
size_t
UnwindAssemblyInstEmulation::WriteMemory (EmulateInstruction *instruction,
const EmulateInstruction::Context &context,
lldb::addr_t addr,
const void *dst,
size_t dst_len)
{
DataExtractor data (dst,
dst_len,
instruction->GetArchitecture ().GetByteOrder(),
instruction->GetArchitecture ().GetAddressByteSize());
Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
if (log && log->GetVerbose ())
{
StreamString strm;
strm.PutCString ("UnwindAssemblyInstEmulation::WriteMemory (");
data.Dump(&strm, 0, eFormatBytes, 1, dst_len, UINT32_MAX, addr, 0, 0);
strm.PutCString (", context = ");
context.Dump(strm, instruction);
log->PutCString (strm.GetData());
}
const bool can_replace = true;
const bool cant_replace = false;
switch (context.type)
{
default:
case EmulateInstruction::eContextInvalid:
case EmulateInstruction::eContextReadOpcode:
case EmulateInstruction::eContextImmediate:
case EmulateInstruction::eContextAdjustBaseRegister:
case EmulateInstruction::eContextRegisterPlusOffset:
case EmulateInstruction::eContextAdjustPC:
case EmulateInstruction::eContextRegisterStore:
case EmulateInstruction::eContextRegisterLoad:
case EmulateInstruction::eContextRelativeBranchImmediate:
case EmulateInstruction::eContextAbsoluteBranchRegister:
case EmulateInstruction::eContextSupervisorCall:
case EmulateInstruction::eContextTableBranchReadMemory:
case EmulateInstruction::eContextWriteRegisterRandomBits:
case EmulateInstruction::eContextWriteMemoryRandomBits:
case EmulateInstruction::eContextArithmetic:
case EmulateInstruction::eContextAdvancePC:
case EmulateInstruction::eContextReturnFromException:
case EmulateInstruction::eContextPopRegisterOffStack:
case EmulateInstruction::eContextAdjustStackPointer:
break;
case EmulateInstruction::eContextPushRegisterOnStack:
{
uint32_t reg_num = LLDB_INVALID_REGNUM;
bool is_return_address_reg = false;
const uint32_t unwind_reg_kind = m_unwind_plan_ptr->GetRegisterKind();
if (context.info_type == EmulateInstruction::eInfoTypeRegisterToRegisterPlusOffset)
{
reg_num = context.info.RegisterToRegisterPlusOffset.data_reg.kinds[unwind_reg_kind];
if (context.info.RegisterToRegisterPlusOffset.data_reg.kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_RA)
is_return_address_reg = true;
}
else
{
assert (!"unhandled case, add code to handle this!");
}
if (reg_num != LLDB_INVALID_REGNUM)
{
if (m_pushed_regs.find (reg_num) == m_pushed_regs.end())
{
m_pushed_regs[reg_num] = addr;
const int32_t offset = addr - m_initial_sp;
m_curr_row->SetRegisterLocationToAtCFAPlusOffset (reg_num, offset, cant_replace);
m_curr_row_modified = true;
if (is_return_address_reg)
{
// This push was pushing the return address register,
// so this is also how we will unwind the PC...
RegisterInfo pc_reg_info;
if (instruction->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc_reg_info))
{
uint32_t pc_reg_num = pc_reg_info.kinds[unwind_reg_kind];
if (pc_reg_num != LLDB_INVALID_REGNUM)
{
m_curr_row->SetRegisterLocationToAtCFAPlusOffset (pc_reg_num, offset, can_replace);
m_curr_row_modified = true;
}
}
}
}
}
}
break;
}
return dst_len;
}
bool
UnwindAssemblyInstEmulation::ReadRegister (EmulateInstruction *instruction,
void *baton,
const RegisterInfo *reg_info,
RegisterValue &reg_value)
{
if (baton && reg_info)
return ((UnwindAssemblyInstEmulation *)baton)->ReadRegister (instruction, reg_info, reg_value);
return false;
}
bool
UnwindAssemblyInstEmulation::ReadRegister (EmulateInstruction *instruction,
const RegisterInfo *reg_info,
RegisterValue &reg_value)
{
bool synthetic = GetRegisterValue (*reg_info, reg_value);
Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
if (log && log->GetVerbose ())
{
StreamString strm;
strm.Printf ("UnwindAssemblyInstEmulation::ReadRegister (name = \"%s\") => synthetic_value = %i, value = ", reg_info->name, synthetic);
reg_value.Dump(&strm, reg_info, false, false, eFormatDefault);
log->PutCString(strm.GetData());
}
return true;
}
bool
UnwindAssemblyInstEmulation::WriteRegister (EmulateInstruction *instruction,
void *baton,
const EmulateInstruction::Context &context,
const RegisterInfo *reg_info,
const RegisterValue &reg_value)
{
if (baton && reg_info)
return ((UnwindAssemblyInstEmulation *)baton)->WriteRegister (instruction, context, reg_info, reg_value);
return false;
}
bool
UnwindAssemblyInstEmulation::WriteRegister (EmulateInstruction *instruction,
const EmulateInstruction::Context &context,
const RegisterInfo *reg_info,
const RegisterValue &reg_value)
{
Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
if (log && log->GetVerbose ())
{
StreamString strm;
strm.Printf ("UnwindAssemblyInstEmulation::WriteRegister (name = \"%s\", value = ", reg_info->name);
reg_value.Dump(&strm, reg_info, false, false, eFormatDefault);
strm.PutCString (", context = ");
context.Dump(strm, instruction);
log->PutCString(strm.GetData());
}
const bool must_replace = true;
SetRegisterValue (*reg_info, reg_value);
switch (context.type)
{
case EmulateInstruction::eContextInvalid:
case EmulateInstruction::eContextReadOpcode:
case EmulateInstruction::eContextImmediate:
case EmulateInstruction::eContextAdjustBaseRegister:
case EmulateInstruction::eContextRegisterPlusOffset:
case EmulateInstruction::eContextAdjustPC:
case EmulateInstruction::eContextRegisterStore:
case EmulateInstruction::eContextRegisterLoad:
case EmulateInstruction::eContextAbsoluteBranchRegister:
case EmulateInstruction::eContextSupervisorCall:
case EmulateInstruction::eContextTableBranchReadMemory:
case EmulateInstruction::eContextWriteRegisterRandomBits:
case EmulateInstruction::eContextWriteMemoryRandomBits:
case EmulateInstruction::eContextArithmetic:
case EmulateInstruction::eContextAdvancePC:
case EmulateInstruction::eContextReturnFromException:
case EmulateInstruction::eContextPushRegisterOnStack:
// {
// const uint32_t reg_num = reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
// if (reg_num != LLDB_INVALID_REGNUM)
// {
// const bool can_replace_only_if_unspecified = true;
//
// m_curr_row.SetRegisterLocationToUndefined (reg_num,
// can_replace_only_if_unspecified,
// can_replace_only_if_unspecified);
// m_curr_row_modified = true;
// }
// }
break;
case EmulateInstruction::eContextRelativeBranchImmediate:
{
{
m_curr_insn_is_branch_immediate = true;
}
}
break;
case EmulateInstruction::eContextPopRegisterOffStack:
{
const uint32_t reg_num = reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
if (reg_num != LLDB_INVALID_REGNUM)
{
m_curr_row->SetRegisterLocationToSame (reg_num, must_replace);
m_curr_row_modified = true;
m_curr_insn_restored_a_register = true;
}
}
break;
case EmulateInstruction::eContextSetFramePointer:
if (!m_fp_is_cfa)
{
m_fp_is_cfa = true;
m_cfa_reg_info = *reg_info;
const uint32_t cfa_reg_num = reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
assert (cfa_reg_num != LLDB_INVALID_REGNUM);
m_curr_row->SetCFARegister(cfa_reg_num);
m_curr_row->SetCFAOffset(m_initial_sp - reg_value.GetAsUInt64());
m_curr_row_modified = true;
}
break;
case EmulateInstruction::eContextAdjustStackPointer:
// If we have created a frame using the frame pointer, don't follow
// subsequent adjustments to the stack pointer.
if (!m_fp_is_cfa)
{
m_curr_row->SetCFAOffset (m_initial_sp - reg_value.GetAsUInt64());
m_curr_row_modified = true;
}
break;
}
return true;
}
Index: lldb/trunk/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp
===================================================================
--- lldb/trunk/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp (revision 219543)
+++ lldb/trunk/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp (revision 219544)
@@ -1,864 +1,872 @@
//===-- DisassemblerLLVMC.cpp -----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "DisassemblerLLVMC.h"
#include "llvm-c/Disassembler.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDisassembler.h"
#include "llvm/MC/MCExternalSymbolizer.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCRelocationInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MemoryObject.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/ADT/SmallString.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/DataExtractor.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/Stream.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Core/RegularExpression.h"
using namespace lldb;
using namespace lldb_private;
class InstructionLLVMC : public lldb_private::Instruction
{
public:
InstructionLLVMC (DisassemblerLLVMC &disasm,
const lldb_private::Address &address,
AddressClass addr_class) :
Instruction (address, addr_class),
m_disasm_sp (disasm.shared_from_this()),
m_does_branch (eLazyBoolCalculate),
m_is_valid (false),
m_using_file_addr (false)
{
}
virtual
~InstructionLLVMC ()
{
}
virtual bool
DoesBranch ()
{
if (m_does_branch == eLazyBoolCalculate)
{
GetDisassemblerLLVMC().Lock(this, NULL);
DataExtractor data;
if (m_opcode.GetData(data))
{
bool is_alternate_isa;
lldb::addr_t pc = m_address.GetFileAddress();
DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa);
const uint8_t *opcode_data = data.GetDataStart();
const size_t opcode_data_len = data.GetByteSize();
llvm::MCInst inst;
const size_t inst_size = mc_disasm_ptr->GetMCInst (opcode_data,
opcode_data_len,
pc,
inst);
// Be conservative, if we didn't understand the instruction, say it might branch...
if (inst_size == 0)
m_does_branch = eLazyBoolYes;
else
{
const bool can_branch = mc_disasm_ptr->CanBranch(inst);
if (can_branch)
m_does_branch = eLazyBoolYes;
else
m_does_branch = eLazyBoolNo;
}
}
GetDisassemblerLLVMC().Unlock();
}
return m_does_branch == eLazyBoolYes;
}
DisassemblerLLVMC::LLVMCDisassembler *
GetDisasmToUse (bool &is_alternate_isa)
{
is_alternate_isa = false;
DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC();
if (llvm_disasm.m_alternate_disasm_ap.get() != NULL)
{
const AddressClass address_class = GetAddressClass ();
if (address_class == eAddressClassCodeAlternateISA)
{
is_alternate_isa = true;
return llvm_disasm.m_alternate_disasm_ap.get();
}
}
return llvm_disasm.m_disasm_ap.get();
}
virtual size_t
Decode (const lldb_private::Disassembler &disassembler,
const lldb_private::DataExtractor &data,
lldb::offset_t data_offset)
{
// All we have to do is read the opcode which can be easy for some
// architectures
bool got_op = false;
DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC();
const ArchSpec &arch = llvm_disasm.GetArchitecture();
const lldb::ByteOrder byte_order = data.GetByteOrder();
const uint32_t min_op_byte_size = arch.GetMinimumOpcodeByteSize();
const uint32_t max_op_byte_size = arch.GetMaximumOpcodeByteSize();
if (min_op_byte_size == max_op_byte_size)
{
// Fixed size instructions, just read that amount of data.
if (!data.ValidOffsetForDataOfSize(data_offset, min_op_byte_size))
return false;
switch (min_op_byte_size)
{
case 1:
m_opcode.SetOpcode8 (data.GetU8 (&data_offset), byte_order);
got_op = true;
break;
case 2:
m_opcode.SetOpcode16 (data.GetU16 (&data_offset), byte_order);
got_op = true;
break;
case 4:
m_opcode.SetOpcode32 (data.GetU32 (&data_offset), byte_order);
got_op = true;
break;
case 8:
m_opcode.SetOpcode64 (data.GetU64 (&data_offset), byte_order);
got_op = true;
break;
default:
m_opcode.SetOpcodeBytes(data.PeekData(data_offset, min_op_byte_size), min_op_byte_size);
got_op = true;
break;
}
}
if (!got_op)
{
bool is_alternate_isa = false;
DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa);
const llvm::Triple::ArchType machine = arch.GetMachine();
if (machine == llvm::Triple::arm || machine == llvm::Triple::thumb)
{
if (machine == llvm::Triple::thumb || is_alternate_isa)
{
uint32_t thumb_opcode = data.GetU16(&data_offset);
if ((thumb_opcode & 0xe000) != 0xe000 || ((thumb_opcode & 0x1800u) == 0))
{
m_opcode.SetOpcode16 (thumb_opcode, byte_order);
m_is_valid = true;
}
else
{
thumb_opcode <<= 16;
thumb_opcode |= data.GetU16(&data_offset);
m_opcode.SetOpcode16_2 (thumb_opcode, byte_order);
m_is_valid = true;
}
}
else
{
m_opcode.SetOpcode32 (data.GetU32(&data_offset), byte_order);
m_is_valid = true;
}
}
else
{
// The opcode isn't evenly sized, so we need to actually use the llvm
// disassembler to parse it and get the size.
uint8_t *opcode_data = const_cast<uint8_t *>(data.PeekData (data_offset, 1));
const size_t opcode_data_len = data.BytesLeft(data_offset);
const addr_t pc = m_address.GetFileAddress();
llvm::MCInst inst;
llvm_disasm.Lock(this, NULL);
const size_t inst_size = mc_disasm_ptr->GetMCInst(opcode_data,
opcode_data_len,
pc,
inst);
llvm_disasm.Unlock();
if (inst_size == 0)
m_opcode.Clear();
else
{
m_opcode.SetOpcodeBytes(opcode_data, inst_size);
m_is_valid = true;
}
}
}
return m_opcode.GetByteSize();
}
void
AppendComment (std::string &description)
{
if (m_comment.empty())
m_comment.swap (description);
else
{
m_comment.append(", ");
m_comment.append(description);
}
}
virtual void
CalculateMnemonicOperandsAndComment (const lldb_private::ExecutionContext *exe_ctx)
{
DataExtractor data;
const AddressClass address_class = GetAddressClass ();
if (m_opcode.GetData(data))
{
char out_string[512];
DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC();
DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr;
if (address_class == eAddressClassCodeAlternateISA)
mc_disasm_ptr = llvm_disasm.m_alternate_disasm_ap.get();
else
mc_disasm_ptr = llvm_disasm.m_disasm_ap.get();
lldb::addr_t pc = m_address.GetFileAddress();
m_using_file_addr = true;
const bool data_from_file = GetDisassemblerLLVMC().m_data_from_file;
bool use_hex_immediates = true;
Disassembler::HexImmediateStyle hex_style = Disassembler::eHexStyleC;
if (exe_ctx)
{
Target *target = exe_ctx->GetTargetPtr();
if (target)
{
use_hex_immediates = target->GetUseHexImmediates();
hex_style = target->GetHexImmediateStyle();
if (!data_from_file)
{
const lldb::addr_t load_addr = m_address.GetLoadAddress(target);
if (load_addr != LLDB_INVALID_ADDRESS)
{
pc = load_addr;
m_using_file_addr = false;
}
}
}
}
llvm_disasm.Lock(this, exe_ctx);
const uint8_t *opcode_data = data.GetDataStart();
const size_t opcode_data_len = data.GetByteSize();
llvm::MCInst inst;
size_t inst_size = mc_disasm_ptr->GetMCInst (opcode_data,
opcode_data_len,
pc,
inst);
if (inst_size > 0)
{
mc_disasm_ptr->SetStyle(use_hex_immediates, hex_style);
mc_disasm_ptr->PrintMCInst(inst, out_string, sizeof(out_string));
}
llvm_disasm.Unlock();
if (inst_size == 0)
{
m_comment.assign ("unknown opcode");
inst_size = m_opcode.GetByteSize();
StreamString mnemonic_strm;
lldb::offset_t offset = 0;
lldb::ByteOrder byte_order = data.GetByteOrder();
switch (inst_size)
{
case 1:
{
const uint8_t uval8 = data.GetU8 (&offset);
m_opcode.SetOpcode8 (uval8, byte_order);
m_opcode_name.assign (".byte");
mnemonic_strm.Printf("0x%2.2x", uval8);
}
break;
case 2:
{
const uint16_t uval16 = data.GetU16(&offset);
m_opcode.SetOpcode16(uval16, byte_order);
m_opcode_name.assign (".short");
mnemonic_strm.Printf("0x%4.4x", uval16);
}
break;
case 4:
{
const uint32_t uval32 = data.GetU32(&offset);
m_opcode.SetOpcode32(uval32, byte_order);
m_opcode_name.assign (".long");
mnemonic_strm.Printf("0x%8.8x", uval32);
}
break;
case 8:
{
const uint64_t uval64 = data.GetU64(&offset);
m_opcode.SetOpcode64(uval64, byte_order);
m_opcode_name.assign (".quad");
mnemonic_strm.Printf("0x%16.16" PRIx64, uval64);
}
break;
default:
if (inst_size == 0)
return;
else
{
const uint8_t *bytes = data.PeekData(offset, inst_size);
if (bytes == NULL)
return;
m_opcode_name.assign (".byte");
m_opcode.SetOpcodeBytes(bytes, inst_size);
mnemonic_strm.Printf("0x%2.2x", bytes[0]);
for (uint32_t i=1; i<inst_size; ++i)
mnemonic_strm.Printf(" 0x%2.2x", bytes[i]);
}
break;
}
m_mnemonics.swap(mnemonic_strm.GetString());
return;
}
else
{
if (m_does_branch == eLazyBoolCalculate)
{
const bool can_branch = mc_disasm_ptr->CanBranch(inst);
if (can_branch)
m_does_branch = eLazyBoolYes;
else
m_does_branch = eLazyBoolNo;
}
}
static RegularExpression s_regex("[ \t]*([^ ^\t]+)[ \t]*([^ ^\t].*)?", REG_EXTENDED);
RegularExpression::Match matches(3);
if (s_regex.Execute(out_string, &matches))
{
matches.GetMatchAtIndex(out_string, 1, m_opcode_name);
matches.GetMatchAtIndex(out_string, 2, m_mnemonics);
}
}
}
bool
IsValid () const
{
return m_is_valid;
}
bool
UsingFileAddress() const
{
return m_using_file_addr;
}
size_t
GetByteSize () const
{
return m_opcode.GetByteSize();
}
DisassemblerLLVMC &
GetDisassemblerLLVMC ()
{
return *(DisassemblerLLVMC *)m_disasm_sp.get();
}
protected:
DisassemblerSP m_disasm_sp; // for ownership
LazyBool m_does_branch;
bool m_is_valid;
bool m_using_file_addr;
};
DisassemblerLLVMC::LLVMCDisassembler::LLVMCDisassembler (const char *triple, unsigned flavor, DisassemblerLLVMC &owner):
m_is_valid(true)
{
std::string Error;
const llvm::Target *curr_target = llvm::TargetRegistry::lookupTarget(triple, Error);
if (!curr_target)
{
m_is_valid = false;
return;
}
m_instr_info_ap.reset(curr_target->createMCInstrInfo());
m_reg_info_ap.reset (curr_target->createMCRegInfo(triple));
std::string features_str;
m_subtarget_info_ap.reset(curr_target->createMCSubtargetInfo(triple, "",
features_str));
std::unique_ptr<llvm::MCRegisterInfo> reg_info(curr_target->createMCRegInfo(triple));
m_asm_info_ap.reset(curr_target->createMCAsmInfo(*reg_info, triple));
if (m_instr_info_ap.get() == NULL || m_reg_info_ap.get() == NULL || m_subtarget_info_ap.get() == NULL || m_asm_info_ap.get() == NULL)
{
m_is_valid = false;
return;
}
m_context_ap.reset(new llvm::MCContext(m_asm_info_ap.get(), m_reg_info_ap.get(), 0));
m_disasm_ap.reset(curr_target->createMCDisassembler(*m_subtarget_info_ap.get(), *m_context_ap.get()));
if (m_disasm_ap.get() && m_context_ap.get())
{
std::unique_ptr<llvm::MCRelocationInfo> RelInfo(curr_target->createMCRelocationInfo(triple, *m_context_ap.get()));
if (!RelInfo)
{
m_is_valid = false;
return;
}
std::unique_ptr<llvm::MCSymbolizer> symbolizer_up(curr_target->createMCSymbolizer(triple, NULL,
DisassemblerLLVMC::SymbolLookupCallback,
(void *) &owner,
m_context_ap.get(), RelInfo.release()));
m_disasm_ap->setSymbolizer(std::move(symbolizer_up));
unsigned asm_printer_variant;
if (flavor == ~0U)
asm_printer_variant = m_asm_info_ap->getAssemblerDialect();
else
{
asm_printer_variant = flavor;
}
m_instr_printer_ap.reset(curr_target->createMCInstPrinter(asm_printer_variant,
*m_asm_info_ap.get(),
*m_instr_info_ap.get(),
*m_reg_info_ap.get(),
*m_subtarget_info_ap.get()));
if (m_instr_printer_ap.get() == NULL)
{
m_disasm_ap.reset();
m_is_valid = false;
}
}
else
m_is_valid = false;
}
DisassemblerLLVMC::LLVMCDisassembler::~LLVMCDisassembler()
{
}
namespace {
// This is the memory object we use in GetInstruction.
class LLDBDisasmMemoryObject : public llvm::MemoryObject {
const uint8_t *m_bytes;
uint64_t m_size;
uint64_t m_base_PC;
public:
LLDBDisasmMemoryObject(const uint8_t *bytes, uint64_t size, uint64_t basePC) :
m_bytes(bytes), m_size(size), m_base_PC(basePC) {}
uint64_t getBase() const { return m_base_PC; }
uint64_t getExtent() const { return m_size; }
int readByte(uint64_t addr, uint8_t *byte) const {
if (addr - m_base_PC >= m_size)
return -1;
*byte = m_bytes[addr - m_base_PC];
return 0;
}
};
} // End Anonymous Namespace
uint64_t
DisassemblerLLVMC::LLVMCDisassembler::GetMCInst (const uint8_t *opcode_data,
size_t opcode_data_len,
lldb::addr_t pc,
llvm::MCInst &mc_inst)
{
LLDBDisasmMemoryObject memory_object (opcode_data, opcode_data_len, pc);
llvm::MCDisassembler::DecodeStatus status;
uint64_t new_inst_size;
status = m_disasm_ap->getInstruction(mc_inst,
new_inst_size,
memory_object,
pc,
llvm::nulls(),
llvm::nulls());
if (status == llvm::MCDisassembler::Success)
return new_inst_size;
else
return 0;
}
uint64_t
DisassemblerLLVMC::LLVMCDisassembler::PrintMCInst (llvm::MCInst &mc_inst,
char *dst,
size_t dst_len)
{
llvm::StringRef unused_annotations;
llvm::SmallString<64> inst_string;
llvm::raw_svector_ostream inst_stream(inst_string);
m_instr_printer_ap->printInst (&mc_inst, inst_stream, unused_annotations);
inst_stream.flush();
const size_t output_size = std::min(dst_len - 1, inst_string.size());
std::memcpy(dst, inst_string.data(), output_size);
dst[output_size] = '\0';
return output_size;
}
void
DisassemblerLLVMC::LLVMCDisassembler::SetStyle (bool use_hex_immed, HexImmediateStyle hex_style)
{
m_instr_printer_ap->setPrintImmHex(use_hex_immed);
switch(hex_style)
{
case eHexStyleC: m_instr_printer_ap->setPrintImmHex(llvm::HexStyle::C); break;
case eHexStyleAsm: m_instr_printer_ap->setPrintImmHex(llvm::HexStyle::Asm); break;
}
}
bool
DisassemblerLLVMC::LLVMCDisassembler::CanBranch (llvm::MCInst &mc_inst)
{
return m_instr_info_ap->get(mc_inst.getOpcode()).mayAffectControlFlow(mc_inst, *m_reg_info_ap.get());
}
bool
DisassemblerLLVMC::FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor)
{
llvm::Triple triple = arch.GetTriple();
if (flavor == NULL || strcmp (flavor, "default") == 0)
return true;
if (triple.getArch() == llvm::Triple::x86 || triple.getArch() == llvm::Triple::x86_64)
{
if (strcmp (flavor, "intel") == 0 || strcmp (flavor, "att") == 0)
return true;
else
return false;
}
else
return false;
}
Disassembler *
DisassemblerLLVMC::CreateInstance (const ArchSpec &arch, const char *flavor)
{
if (arch.GetTriple().getArch() != llvm::Triple::UnknownArch)
{
std::unique_ptr<DisassemblerLLVMC> disasm_ap (new DisassemblerLLVMC(arch, flavor));
if (disasm_ap.get() && disasm_ap->IsValid())
return disasm_ap.release();
}
return NULL;
}
DisassemblerLLVMC::DisassemblerLLVMC (const ArchSpec &arch, const char *flavor_string) :
Disassembler(arch, flavor_string),
m_exe_ctx (NULL),
m_inst (NULL),
m_data_from_file (false)
{
if (!FlavorValidForArchSpec (arch, m_flavor.c_str()))
{
m_flavor.assign("default");
}
const char *triple = arch.GetTriple().getTriple().c_str();
unsigned flavor = ~0U;
// So far the only supported flavor is "intel" on x86. The base class will set this
// correctly coming in.
if (arch.GetTriple().getArch() == llvm::Triple::x86
|| arch.GetTriple().getArch() == llvm::Triple::x86_64)
{
if (m_flavor == "intel")
{
flavor = 1;
}
else if (m_flavor == "att")
{
flavor = 0;
}
}
ArchSpec thumb_arch(arch);
if (arch.GetTriple().getArch() == llvm::Triple::arm)
{
std::string thumb_arch_name (thumb_arch.GetTriple().getArchName().str());
// Replace "arm" with "thumb" so we get all thumb variants correct
if (thumb_arch_name.size() > 3)
{
thumb_arch_name.erase(0,3);
thumb_arch_name.insert(0, "thumb");
}
else
{
thumb_arch_name = "thumbv7";
}
thumb_arch.GetTriple().setArchName(llvm::StringRef(thumb_arch_name.c_str()));
}
// Cortex-M3 devices (e.g. armv7m) can only execute thumb (T2) instructions,
// so hardcode the primary disassembler to thumb mode. Same for Cortex-M4 (armv7em).
//
// Handle the Cortex-M0 (armv6m) the same; the ISA is a subset of the T and T32
// instructions defined in ARMv7-A.
if (arch.GetTriple().getArch() == llvm::Triple::arm
&& (arch.GetCore() == ArchSpec::Core::eCore_arm_armv7m
|| arch.GetCore() == ArchSpec::Core::eCore_arm_armv7em
|| arch.GetCore() == ArchSpec::Core::eCore_arm_armv6m))
{
triple = thumb_arch.GetTriple().getTriple().c_str();
}
m_disasm_ap.reset (new LLVMCDisassembler(triple, flavor, *this));
if (!m_disasm_ap->IsValid())
{
// We use m_disasm_ap.get() to tell whether we are valid or not, so if this isn't good for some reason,
// we reset it, and then we won't be valid and FindPlugin will fail and we won't get used.
m_disasm_ap.reset();
}
// For arm CPUs that can execute arm or thumb instructions, also create a thumb instruction disassembler.
if (arch.GetTriple().getArch() == llvm::Triple::arm)
{
std::string thumb_triple(thumb_arch.GetTriple().getTriple());
m_alternate_disasm_ap.reset(new LLVMCDisassembler(thumb_triple.c_str(), flavor, *th