forked from OSchip/llvm-project
[Dexter] Add --source-dir-root flag
Summary: This allows to run dexter tests with separately compiled binaries that are specified via --binary if the source file location changed between compilation and dexter test run. Reviewers: TWeaver, jmorse, probinson, #debug-info Reviewed By: jmorse Subscribers: #debug-info, cmtice, llvm-commits Tags: #llvm, #debug-info Differential Revision: https://reviews.llvm.org/D81319
This commit is contained in:
parent
ecdf48f15b
commit
53d6bfef32
|
@ -7,10 +7,13 @@
|
||||||
"""Base class for all debugger interface implementations."""
|
"""Base class for all debugger interface implementations."""
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
import unittest
|
||||||
|
|
||||||
from dex.dextIR import DebuggerIR, ValueIR
|
from types import SimpleNamespace
|
||||||
|
from dex.dextIR import DebuggerIR, FrameIR, LocIR, StepIR, ValueIR
|
||||||
from dex.utils.Exceptions import DebuggerException
|
from dex.utils.Exceptions import DebuggerException
|
||||||
from dex.utils.Exceptions import NotYetLoadedDebuggerException
|
from dex.utils.Exceptions import NotYetLoadedDebuggerException
|
||||||
from dex.utils.ReturnCode import ReturnCode
|
from dex.utils.ReturnCode import ReturnCode
|
||||||
|
@ -19,6 +22,11 @@ from dex.utils.ReturnCode import ReturnCode
|
||||||
class DebuggerBase(object, metaclass=abc.ABCMeta):
|
class DebuggerBase(object, metaclass=abc.ABCMeta):
|
||||||
def __init__(self, context):
|
def __init__(self, context):
|
||||||
self.context = context
|
self.context = context
|
||||||
|
# Note: We can't already read values from options
|
||||||
|
# as DebuggerBase is created before we initialize options
|
||||||
|
# to read potential_debuggers.
|
||||||
|
self.options = self.context.options
|
||||||
|
|
||||||
self._interface = None
|
self._interface = None
|
||||||
self.has_loaded = False
|
self.has_loaded = False
|
||||||
self._loading_error = NotYetLoadedDebuggerException()
|
self._loading_error = NotYetLoadedDebuggerException()
|
||||||
|
@ -116,16 +124,27 @@ class DebuggerBase(object, metaclass=abc.ABCMeta):
|
||||||
def clear_breakpoints(self):
|
def clear_breakpoints(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def add_breakpoint(self, file_, line):
|
def add_breakpoint(self, file_, line):
|
||||||
pass
|
return self._add_breakpoint(self._external_to_debug_path(file_), line)
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
|
def _add_breakpoint(self, file_, line):
|
||||||
|
pass
|
||||||
|
|
||||||
def add_conditional_breakpoint(self, file_, line, condition):
|
def add_conditional_breakpoint(self, file_, line, condition):
|
||||||
pass
|
return self._add_conditional_breakpoint(
|
||||||
|
self._external_to_debug_path(file_), line, condition)
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
|
def _add_conditional_breakpoint(self, file_, line, condition):
|
||||||
|
pass
|
||||||
|
|
||||||
def delete_conditional_breakpoint(self, file_, line, condition):
|
def delete_conditional_breakpoint(self, file_, line, condition):
|
||||||
|
return self._delete_conditional_breakpoint(
|
||||||
|
self._external_to_debug_path(file_), line, condition)
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def _delete_conditional_breakpoint(self, file_, line, condition):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
|
@ -140,8 +159,14 @@ class DebuggerBase(object, metaclass=abc.ABCMeta):
|
||||||
def go(self) -> ReturnCode:
|
def go(self) -> ReturnCode:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def get_step_info(self, watches, step_index):
|
def get_step_info(self, watches, step_index):
|
||||||
|
step_info = self._get_step_info(watches, step_index)
|
||||||
|
for frame in step_info.frames:
|
||||||
|
frame.loc.path = self._debug_to_external_path(frame.loc.path)
|
||||||
|
return step_info
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def _get_step_info(self, watches, step_index):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractproperty
|
@abc.abstractproperty
|
||||||
|
@ -159,3 +184,86 @@ class DebuggerBase(object, metaclass=abc.ABCMeta):
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def evaluate_expression(self, expression, frame_idx=0) -> ValueIR:
|
def evaluate_expression(self, expression, frame_idx=0) -> ValueIR:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _external_to_debug_path(self, path):
|
||||||
|
root_dir = self.options.source_root_dir
|
||||||
|
if not root_dir or not path:
|
||||||
|
return path
|
||||||
|
assert path.startswith(root_dir)
|
||||||
|
return path[len(root_dir):].lstrip(os.path.sep)
|
||||||
|
|
||||||
|
def _debug_to_external_path(self, path):
|
||||||
|
if not path or not self.options.source_root_dir:
|
||||||
|
return path
|
||||||
|
for file in self.options.source_files:
|
||||||
|
if path.endswith(self._external_to_debug_path(file)):
|
||||||
|
return file
|
||||||
|
return path
|
||||||
|
|
||||||
|
class TestDebuggerBase(unittest.TestCase):
|
||||||
|
|
||||||
|
class MockDebugger(DebuggerBase):
|
||||||
|
|
||||||
|
def __init__(self, context, *args):
|
||||||
|
super().__init__(context, *args)
|
||||||
|
self.step_info = None
|
||||||
|
self.breakpoint_file = None
|
||||||
|
|
||||||
|
def _add_breakpoint(self, file, line):
|
||||||
|
self.breakpoint_file = file
|
||||||
|
|
||||||
|
def _get_step_info(self, watches, step_index):
|
||||||
|
return self.step_info
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
TestDebuggerBase.MockDebugger.__abstractmethods__ = set()
|
||||||
|
self.options = SimpleNamespace(source_root_dir = '', source_files = [])
|
||||||
|
context = SimpleNamespace(options = self.options)
|
||||||
|
self.dbg = TestDebuggerBase.MockDebugger(context)
|
||||||
|
|
||||||
|
def _new_step(self, paths):
|
||||||
|
frames = [
|
||||||
|
FrameIR(
|
||||||
|
function=None,
|
||||||
|
is_inlined=False,
|
||||||
|
loc=LocIR(path=path, lineno=0, column=0)) for path in paths
|
||||||
|
]
|
||||||
|
return StepIR(step_index=0, stop_reason=None, frames=frames)
|
||||||
|
|
||||||
|
def _step_paths(self, step):
|
||||||
|
return [frame.loc.path for frame in step.frames]
|
||||||
|
|
||||||
|
def test_add_breakpoint_no_source_root_dir(self):
|
||||||
|
self.options.source_root_dir = ''
|
||||||
|
self.dbg.add_breakpoint('/root/some_file', 12)
|
||||||
|
self.assertEqual('/root/some_file', self.dbg.breakpoint_file)
|
||||||
|
|
||||||
|
def test_add_breakpoint_with_source_root_dir(self):
|
||||||
|
self.options.source_root_dir = '/my_root'
|
||||||
|
self.dbg.add_breakpoint('/my_root/some_file', 12)
|
||||||
|
self.assertEqual('some_file', self.dbg.breakpoint_file)
|
||||||
|
|
||||||
|
def test_add_breakpoint_with_source_root_dir_slash_suffix(self):
|
||||||
|
self.options.source_root_dir = '/my_root/'
|
||||||
|
self.dbg.add_breakpoint('/my_root/some_file', 12)
|
||||||
|
self.assertEqual('some_file', self.dbg.breakpoint_file)
|
||||||
|
|
||||||
|
def test_get_step_info_no_source_root_dir(self):
|
||||||
|
self.dbg.step_info = self._new_step(['/root/some_file'])
|
||||||
|
self.assertEqual(['/root/some_file'],
|
||||||
|
self._step_paths(self.dbg.get_step_info([], 0)))
|
||||||
|
|
||||||
|
def test_get_step_info_no_frames(self):
|
||||||
|
self.options.source_root_dir = '/my_root'
|
||||||
|
self.dbg.step_info = self._new_step([])
|
||||||
|
self.assertEqual([],
|
||||||
|
self._step_paths(self.dbg.get_step_info([], 0)))
|
||||||
|
|
||||||
|
def test_get_step_info(self):
|
||||||
|
self.options.source_root_dir = '/my_root'
|
||||||
|
self.options.source_files = ['/my_root/some_file']
|
||||||
|
self.dbg.step_info = self._new_step(
|
||||||
|
[None, '/other/file', '/dbg/some_file'])
|
||||||
|
self.assertEqual([None, '/other/file', '/my_root/some_file'],
|
||||||
|
self._step_paths(self.dbg.get_step_info([], 0)))
|
||||||
|
|
|
@ -100,6 +100,11 @@ def add_debugger_tool_arguments(parser, context, defaults):
|
||||||
default=None,
|
default=None,
|
||||||
display_default=defaults.arch,
|
display_default=defaults.arch,
|
||||||
help='target architecture')
|
help='target architecture')
|
||||||
|
defaults.source_root_dir = ''
|
||||||
|
parser.add_argument(
|
||||||
|
'--source-root-dir',
|
||||||
|
default=None,
|
||||||
|
help='prefix path to ignore when matching debug info and source files.')
|
||||||
|
|
||||||
|
|
||||||
def handle_debugger_tool_base_options(context, defaults): # noqa
|
def handle_debugger_tool_base_options(context, defaults): # noqa
|
||||||
|
|
|
@ -76,18 +76,18 @@ class DbgEng(DebuggerBase):
|
||||||
x.RemoveFlags(breakpoint.BreakpointFlags.DEBUG_BREAKPOINT_ENABLED)
|
x.RemoveFlags(breakpoint.BreakpointFlags.DEBUG_BREAKPOINT_ENABLED)
|
||||||
self.client.Control.RemoveBreakpoint(x)
|
self.client.Control.RemoveBreakpoint(x)
|
||||||
|
|
||||||
def add_breakpoint(self, file_, line):
|
def _add_breakpoint(self, file_, line):
|
||||||
# Breakpoint setting/deleting is not supported by dbgeng at this moment
|
# Breakpoint setting/deleting is not supported by dbgeng at this moment
|
||||||
# but is something that should be considered in the future.
|
# but is something that should be considered in the future.
|
||||||
# TODO: this method is called in the DefaultController but has no effect.
|
# TODO: this method is called in the DefaultController but has no effect.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def add_conditional_breakpoint(self, file_, line, condition):
|
def _add_conditional_breakpoint(self, file_, line, condition):
|
||||||
# breakpoint setting/deleting is not supported by dbgeng at this moment
|
# breakpoint setting/deleting is not supported by dbgeng at this moment
|
||||||
# but is something that should be considered in the future.
|
# but is something that should be considered in the future.
|
||||||
raise NotImplementedError('add_conditional_breakpoint is not yet implemented by dbgeng')
|
raise NotImplementedError('add_conditional_breakpoint is not yet implemented by dbgeng')
|
||||||
|
|
||||||
def delete_conditional_breakpoint(self, file_, line, condition):
|
def _delete_conditional_breakpoint(self, file_, line, condition):
|
||||||
# breakpoint setting/deleting is not supported by dbgeng at this moment
|
# breakpoint setting/deleting is not supported by dbgeng at this moment
|
||||||
# but is something that should be considered in the future.
|
# but is something that should be considered in the future.
|
||||||
raise NotImplementedError('delete_conditional_breakpoint is not yet implemented by dbgeng')
|
raise NotImplementedError('delete_conditional_breakpoint is not yet implemented by dbgeng')
|
||||||
|
@ -106,7 +106,7 @@ class DbgEng(DebuggerBase):
|
||||||
# We never go -- we always single step.
|
# We never go -- we always single step.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_step_info(self, watches, step_index):
|
def _get_step_info(self, watches, step_index):
|
||||||
frames = self.step_info
|
frames = self.step_info
|
||||||
state_frames = []
|
state_frames = []
|
||||||
|
|
||||||
|
|
|
@ -103,12 +103,12 @@ class LLDB(DebuggerBase):
|
||||||
def clear_breakpoints(self):
|
def clear_breakpoints(self):
|
||||||
self._target.DeleteAllBreakpoints()
|
self._target.DeleteAllBreakpoints()
|
||||||
|
|
||||||
def add_breakpoint(self, file_, line):
|
def _add_breakpoint(self, file_, line):
|
||||||
if not self._target.BreakpointCreateByLocation(file_, line):
|
if not self._target.BreakpointCreateByLocation(file_, line):
|
||||||
raise DebuggerException(
|
raise DebuggerException(
|
||||||
'could not add breakpoint [{}:{}]'.format(file_, line))
|
'could not add breakpoint [{}:{}]'.format(file_, line))
|
||||||
|
|
||||||
def add_conditional_breakpoint(self, file_, line, condition):
|
def _add_conditional_breakpoint(self, file_, line, condition):
|
||||||
bp = self._target.BreakpointCreateByLocation(file_, line)
|
bp = self._target.BreakpointCreateByLocation(file_, line)
|
||||||
if bp:
|
if bp:
|
||||||
bp.SetCondition(condition)
|
bp.SetCondition(condition)
|
||||||
|
@ -116,7 +116,7 @@ class LLDB(DebuggerBase):
|
||||||
raise DebuggerException(
|
raise DebuggerException(
|
||||||
'could not add breakpoint [{}:{}]'.format(file_, line))
|
'could not add breakpoint [{}:{}]'.format(file_, line))
|
||||||
|
|
||||||
def delete_conditional_breakpoint(self, file_, line, condition):
|
def _delete_conditional_breakpoint(self, file_, line, condition):
|
||||||
bp_count = self._target.GetNumBreakpoints()
|
bp_count = self._target.GetNumBreakpoints()
|
||||||
bps = [self._target.GetBreakpointAtIndex(ix) for ix in range(0, bp_count)]
|
bps = [self._target.GetBreakpointAtIndex(ix) for ix in range(0, bp_count)]
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ class LLDB(DebuggerBase):
|
||||||
self._process.Continue()
|
self._process.Continue()
|
||||||
return ReturnCode.OK
|
return ReturnCode.OK
|
||||||
|
|
||||||
def get_step_info(self, watches, step_index):
|
def _get_step_info(self, watches, step_index):
|
||||||
frames = []
|
frames = []
|
||||||
state_frames = []
|
state_frames = []
|
||||||
|
|
||||||
|
|
|
@ -111,14 +111,14 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst
|
||||||
for bp in self._debugger.Breakpoints:
|
for bp in self._debugger.Breakpoints:
|
||||||
bp.Delete()
|
bp.Delete()
|
||||||
|
|
||||||
def add_breakpoint(self, file_, line):
|
def _add_breakpoint(self, file_, line):
|
||||||
self._debugger.Breakpoints.Add('', file_, line)
|
self._debugger.Breakpoints.Add('', file_, line)
|
||||||
|
|
||||||
def add_conditional_breakpoint(self, file_, line, condition):
|
def _add_conditional_breakpoint(self, file_, line, condition):
|
||||||
column = 1
|
column = 1
|
||||||
self._debugger.Breakpoints.Add('', file_, line, column, condition)
|
self._debugger.Breakpoints.Add('', file_, line, column, condition)
|
||||||
|
|
||||||
def delete_conditional_breakpoint(self, file_, line, condition):
|
def _delete_conditional_breakpoint(self, file_, line, condition):
|
||||||
for bp in self._debugger.Breakpoints:
|
for bp in self._debugger.Breakpoints:
|
||||||
for bound_bp in bp.Children:
|
for bound_bp in bp.Children:
|
||||||
if (bound_bp.File == file_ and bound_bp.FileLine == line and
|
if (bound_bp.File == file_ and bound_bp.FileLine == line and
|
||||||
|
@ -146,7 +146,7 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst
|
||||||
raise Error('attempted to access stack frame {} out of {}'
|
raise Error('attempted to access stack frame {} out of {}'
|
||||||
.format(idx, len(stack_frames)))
|
.format(idx, len(stack_frames)))
|
||||||
|
|
||||||
def get_step_info(self, watches, step_index):
|
def _get_step_info(self, watches, step_index):
|
||||||
thread = self._debugger.CurrentThread
|
thread = self._debugger.CurrentThread
|
||||||
stackframes = thread.StackFrames
|
stackframes = thread.StackFrames
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// REQUIRES: lldb
|
||||||
|
// UNSUPPORTED: system-windows
|
||||||
|
//
|
||||||
|
// RUN: %dexter --fail-lt 1.0 -w \
|
||||||
|
// RUN: --builder 'clang' --debugger 'lldb' \
|
||||||
|
// RUN: --cflags "-O0 -glldb -fdebug-prefix-map=%S=/changed" \
|
||||||
|
// RUN: --source-root-dir=%S -- %s
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
int main() {
|
||||||
|
int x = 42;
|
||||||
|
printf("hello world: %d\n", x); // DexLabel('check')
|
||||||
|
}
|
||||||
|
|
||||||
|
// DexExpectWatchValue('x', 42, on_line='check')
|
Loading…
Reference in New Issue