Full paths in DAP stackTrace responses

Vladimir Makaev noticed that, in some cases, a DAP stackTrace response
would include a relative path name for the "path" component.

This patch changes the frame decorator code to add a new DAP-specific
decorator, and changes the DAP entry point to frame filters to use it.
This decorator prefers the symtab's full name, and does not fall back
to the solib's name.

I'm not entirely happy with this patch, because if a user frame filter
uses FrameDecorator, it may still do the wrong thing.  It would be
better to have frame filters return symtab-like objects instead, or to
have a separate method to return the full path to the source file.

I also tend to think that the solib fallback behavior of
FrameDecorator is a mistake.  If this is ever needed, it seems to me
that it should be a separate method.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30665
This commit is contained in:
Tom Tromey
2023-07-24 08:48:00 -06:00
parent f131a57908
commit 65403bd0ed
2 changed files with 82 additions and 51 deletions
+67 -44
View File
@@ -16,34 +16,8 @@
import gdb
class FrameDecorator(object):
"""Basic implementation of a Frame Decorator"""
""" This base frame decorator decorates a frame or another frame
decorator, and provides convenience methods. If this object is
wrapping a frame decorator, defer to that wrapped object's method
if it has one. This allows for frame decorators that have
sub-classed FrameDecorator object, but also wrap other frame
decorators on the same frame to correctly execute.
E.g
If the result of frame filters running means we have one gdb.Frame
wrapped by multiple frame decorators, all sub-classed from
FrameDecorator, the resulting hierarchy will be:
Decorator1
-- (wraps) Decorator2
-- (wraps) FrameDecorator
-- (wraps) gdb.Frame
In this case we have two frame decorators, both of which are
sub-classed from FrameDecorator. If Decorator1 just overrides the
'function' method, then all of the other methods are carried out
by the super-class FrameDecorator. But Decorator2 may have
overriden other methods, so FrameDecorator will look at the
'base' parameter and defer to that class's methods. And so on,
down the chain."""
class _FrameDecoratorBase(object):
"""Base class of frame decorators."""
# 'base' can refer to a gdb.Frame or another frame decorator. In
# the latter case, the child class will have called the super
@@ -122,22 +96,6 @@ class FrameDecorator(object):
frame = self.inferior_frame()
return frame.pc()
def filename(self):
"""Return the filename associated with this frame, detecting
and returning the appropriate library name is this is a shared
library."""
if hasattr(self._base, "filename"):
return self._base.filename()
frame = self.inferior_frame()
sal = frame.find_sal()
if not sal.symtab or not sal.symtab.filename:
pc = frame.pc()
return gdb.solib_name(pc)
else:
return sal.symtab.filename
def frame_args(self):
"""Return an iterable of frame arguments for this frame, if
any. The iterable object contains objects conforming with the
@@ -198,6 +156,71 @@ class FrameDecorator(object):
return self._base
class FrameDecorator(_FrameDecoratorBase):
"""Basic implementation of a Frame Decorator
This base frame decorator decorates a frame or another frame
decorator, and provides convenience methods. If this object is
wrapping a frame decorator, defer to that wrapped object's method
if it has one. This allows for frame decorators that have
sub-classed FrameDecorator object, but also wrap other frame
decorators on the same frame to correctly execute.
E.g
If the result of frame filters running means we have one gdb.Frame
wrapped by multiple frame decorators, all sub-classed from
FrameDecorator, the resulting hierarchy will be:
Decorator1
-- (wraps) Decorator2
-- (wraps) FrameDecorator
-- (wraps) gdb.Frame
In this case we have two frame decorators, both of which are
sub-classed from FrameDecorator. If Decorator1 just overrides the
'function' method, then all of the other methods are carried out
by the super-class FrameDecorator. But Decorator2 may have
overriden other methods, so FrameDecorator will look at the
'base' parameter and defer to that class's methods. And so on,
down the chain."""
def filename(self):
"""Return the filename associated with this frame, detecting
and returning the appropriate library name is this is a shared
library."""
if hasattr(self._base, "filename"):
return self._base.filename()
frame = self.inferior_frame()
sal = frame.find_sal()
if not sal.symtab or not sal.symtab.filename:
pc = frame.pc()
return gdb.solib_name(pc)
else:
return sal.symtab.filename
class DAPFrameDecorator(_FrameDecoratorBase):
"""Like FrameDecorator, but has slightly different results
for the "filename" method."""
def filename(self):
"""Return the filename associated with this frame, detecting
and returning the appropriate library name is this is a shared
library."""
if hasattr(self._base, "filename"):
return self._base.filename()
frame = self.inferior_frame()
sal = frame.find_sal()
if sal.symtab is not None:
return sal.symtab.fullname()
return None
class SymValueWrapper(object):
"""A container class conforming to the Symbol/Value interface
which holds frame locals or frame arguments."""
+15 -7
View File
@@ -18,7 +18,7 @@
import gdb
from gdb.FrameIterator import FrameIterator
from gdb.FrameDecorator import FrameDecorator
from gdb.FrameDecorator import FrameDecorator, DAPFrameDecorator
import itertools
import collections
@@ -157,22 +157,26 @@ def _sort_list():
# Internal function that implements frame_iterator and
# execute_frame_filters. If ALWAYS is True, then this will always
# return an iterator.
def _frame_iterator(frame, frame_low, frame_high, always):
# execute_frame_filters. If DAP_SEMANTICS is True, then this will
# always return an iterator and will wrap frames in DAPFrameDecorator.
def _frame_iterator(frame, frame_low, frame_high, dap_semantics):
# Get a sorted list of frame filters.
sorted_list = list(_sort_list())
# Check to see if there are any frame-filters. If not, just
# return None and let default backtrace printing occur.
if not always and len(sorted_list) == 0:
if not dap_semantics and len(sorted_list) == 0:
return None
frame_iterator = FrameIterator(frame)
# Apply a basic frame decorator to all gdb.Frames. This unifies
# the interface.
frame_iterator = map(FrameDecorator, frame_iterator)
if dap_semantics:
decorator = DAPFrameDecorator
else:
decorator = FrameDecorator
frame_iterator = map(decorator, frame_iterator)
for ff in sorted_list:
frame_iterator = ff.filter(frame_iterator)
@@ -214,7 +218,11 @@ def frame_iterator(frame, frame_low, frame_high):
"""Helper function that will execute the chain of frame filters.
Each filter is executed in priority order. After the execution
completes, slice the iterator to frame_low - frame_high range. An
iterator is always returned.
iterator is always returned. The iterator will always yield
frame decorator objects, but note that these decorators have
slightly different semantics from the ordinary ones: they will
always return a fully-qualified 'filename' (if possible) and will
never substitute the objfile name.
Arguments:
frame: The initial frame.