Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Syntax error, script doesn't work #5

Closed
allopislozano opened this issue May 14, 2019 · 8 comments
Closed

Syntax error, script doesn't work #5

allopislozano opened this issue May 14, 2019 · 8 comments
Assignees

Comments

@allopislozano
Copy link

When I include the file in .gdbinit I get the following message:

File "/home/allopis/.gdb/gdb-colour-filter/colour_filter.py", line 21
    def __init__(self, *args, depth=0, **kwargs):
                                  ^
SyntaxError: invalid syntax

Regards!

@allopislozano
Copy link
Author

Looks like *args must be after depth parameter. After fixing that, I get a new error:

  File "/home/allopis/.gdb/gdb-colour-filter/colour_filter.py", line 158
    def __init__(self, frames: Iterator[Frame]):

Does this script require python3? my system uses python2 by default. If that's the issue, how can I tell gdb which python interpreter to use?

@daskol daskol self-assigned this May 15, 2019
@daskol
Copy link
Owner

daskol commented May 15, 2019

Yes, the script requires python 3.5+. It has internally used type hinting. The main reason why I prefer to use the latest python is that python 2 is deprecated and will not be supported since 2020. I highly recommend you to migrate from python 2 to 3.

Unfortunately, python embedded to gdb as a shared library (check with ldd /usr/bin/gdb). It means that you could not change version of interpreter without recompilation of gdb. Try to apply the patch below. It should solve your issue.

diff --git a/colour_filter.py b/colour_filter.py
index 3de0a5b..fbd7deb 100644
--- a/colour_filter.py
+++ b/colour_filter.py
@@ -2,10 +2,8 @@
 #   encoding: utf8
 #   file: colour_filter.py
 
-from typing import Iterator, Text
-
 from gdb import parameter as get_parameter
-from gdb import Frame, frame_filters, execute
+from gdb import frame_filters, execute
 from gdb.FrameDecorator import FrameDecorator
 
 
@@ -18,10 +16,10 @@ class FrameColorizer(FrameDecorator):
     Notes: There is not special support Frame.elided() property.
     """
 
-    def __init__(self, *args, depth=0, **kwargs):
+    def __init__(self, *args, **kwargs):
         super(FrameColorizer, self).__init__(*args, **kwargs)
 
-        self._depth = depth
+        self._depth = kwargs.get(depth, 0)
         self.frame = super(FrameColorizer, self).inferior_frame()
 
     def __str__(self):
@@ -93,7 +91,7 @@ class FrameColorizer(FrameDecorator):
             # Here we have something like
             # > raise + 272 in section .text of /usr/lib/libc.so.6
             # XXX: gdb.find_pc_line
-            symbol = gdb.execute('info symbol 0x%016x' % func, False, True)
+            symbol = execute('info symbol 0x%016x' % func, False, True)
 
             # But here we truncate layout in binary
             # > raise + 272
@@ -154,7 +152,7 @@ class FilterProxy:
     properly on the first and the sole call.
     """
 
-    def __init__(self, frames: Iterator[Frame]):
+    def __init__(self, frames):
         self.frames = (FrameColorizer(frame, depth=ix)
                        for ix, frame in enumerate(frames))
 
@@ -189,7 +187,7 @@ class ColourFilter:
         # dictionary.
         frame_filters[self.name] = self
 
-    def filter(self, iters: Iterator[Frame]) -> Iterator[Frame]:
+    def filter(self, iters):
         return FilterProxy(iters)
 
 

@allopislozano
Copy link
Author

After applying the patch, it looks like some color is there... for example $gdb prompt is shown in red, but when I try to show a backtrace I get this:
Python Exception <type 'exceptions.TypeError'> instance has no next() method:
And I cannot see the frames unfortunately :/

@daskol
Copy link
Owner

daskol commented Oct 21, 2019

Sorry for so long delay. Try to apply the patch below. The reason is that iterator protocol differs between Python 2 and Python 3.

diff --git a/colour_filter.py b/colour_filter.py
index 2970f2a..bddf1d0 100644
--- a/colour_filter.py
+++ b/colour_filter.py
@@ -163,6 +163,9 @@ class FilterProxy:
         return self
 
     def __next__(self):
+        return self.next()
+
+    def next(self):
         self.unroll_stack()
         raise StopIteration
 

@ryanmiao
Copy link

Sorry for so long delay. Try to apply the patch below. The reason is that iterator protocol differs between Python 2 and Python 3.

diff --git a/colour_filter.py b/colour_filter.py
index 2970f2a..bddf1d0 100644
--- a/colour_filter.py
+++ b/colour_filter.py
@@ -163,6 +163,9 @@ class FilterProxy:
         return self
 
     def __next__(self):
+        return self.next()
+
+    def next(self):
         self.unroll_stack()
         raise StopIteration
 

hi Dan, after apply this patch, the bt also got an error:

Python Exception <type 'exceptions.TypeError'> init() got an unexpected keyword argument 'depth':

@daskol
Copy link
Owner

daskol commented Jun 17, 2020

@ryanmiao GDB Python API was changed a bit. So, this patch should be usefull if you have applied the previous two.

diff --git a/colour_filter.py b/colour_filter.py
index d5b070d..74f5b92 100644
--- a/colour_filter.py
+++ b/colour_filter.py
@@ -16,10 +16,10 @@ class FrameColorizer(FrameDecorator):
     Notes: There is not special support Frame.elided() property.
     """
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, *args, depth=0, **kwargs):
         super(FrameColorizer, self).__init__(*args, **kwargs)
 
-        self._depth = kwargs.get(depth, 0)
+        self._depth = depth
         self.frame = super(FrameColorizer, self).inferior_frame()
 
     def __str__(self):

Also, it would be better to use Python 3 with GDB since Python 2 is deprecated.

@daskol daskol closed this as completed Jun 18, 2020
@maksimovichsam
Copy link

I was facing some issues with Python 2 even after all these patches, but I fixed them and got colored backtrace to work with the file below, for those still facing issues.

#!/usr/bin/env gdb
#   encoding: utf8
#   file: colour_filter.py

from gdb import parameter as get_parameter
from gdb import frame_filters, execute
from gdb.FrameDecorator import FrameDecorator


class FrameColorizer(FrameDecorator):
    """FrameColorizer repeats all actions to get all common frame attribute and
    then spices a bit output with colours. Format of output string is following.

    #<depth> <address> in <function> (<frame_args>) at <filename>[:<line>]

    Notes: There is not special support Frame.elided() property.
    """

    def __init__(self, frame, depth=0):
        super(FrameColorizer, self).__init__(frame)

        self._depth = depth
        self.frame = super(FrameColorizer, self).inferior_frame()

    def __str__(self):
        is_print_address = get_parameter('print address')

        part1 = self.depth()
        part2 = self.function() + ' \033[1;37m(' + self.frame_args() + ')\033[0m'
        part3 = self.filename() + self.line()

        if is_print_address:
            part1 += '  ' + self.address() + ' in '
        else:
            part1 += ' '

        parts = part1 + part2 + ' at ' + part3

        screen_width = self.get_screen_width()
        if screen_width is not None and len(parts) > screen_width:
            shift_width = int(self.length(part1)) - 1
            shift_width -= 3 * int(is_print_address)  # compensate ' in ' part
            value = part1 + part2 + '\n'
            value += ' ' * shift_width + ' at ' + part3
        else:
            value = parts

        return value

    def address(self):
        address = super(FrameColorizer, self).address()
        return '\033[1;30m0x%016x\033[0m' % address

    def depth(self):
        return '\033[1;37m#%-3d\033[0m' % self._depth

    def filename(self):
        filename = super(FrameColorizer, self).filename()
        return '\033[0;36m%s\033[0m' % filename

    def frame_args(self):
        try:
            block = self.frame.block()
        except RuntimeError:
            block = None

        while block is not None:
            if block.function is not None:
                break
            block = block.superblock

        if block is None:
            return ''

        args = []

        for sym in block:
            if not sym.is_argument:
                continue;
            val = sym.value(self.frame)
            arg = '%s=%s' % (sym, val) if str(val) else str(sym)
            args.append(arg)

        return ', '.join(args)

    def function(self):
        func = super(FrameColorizer, self).function()

        # GDB could somehow resolve function name by its address.
        # See details here https://cygwin.com/ml/gdb/2017-12/msg00013.html
        if isinstance(func, int):
            # Here we have something like
            # > raise + 272 in section .text of /usr/lib/libc.so.6
            # XXX: gdb.find_pc_line
            symbol = execute('info symbol 0x%016x' % func, False, True)

            # But here we truncate layout in binary
            # > raise + 272
            name = symbol[:symbol.find('in section')].strip()

            # Check if we in format
            # > smthing + offset
            parts = name.rsplit(' ', 1)
            # > raise
            if len(parts) == 1:
                return name

            try:
                offset = hex(int(parts[1]))
            except ValueError:
                return name

            return '\033[1;34m' + parts[0] + ' ' + offset + '\033[0m'
        else:
            return '\033[1;34m' + str(func) + '\033[0m'

    def get_screen_width(self):
        """Get screen width from GDB. Source format is following
        """
        return get_parameter('width')

    def line(self):
        value = super(FrameColorizer, self).line()
        return '\033[0;35m:%d\033[0m' % value if value else ''

    @staticmethod
    def length(colored_string):
        """This function calculates length of string with terminal control
        sequences.
        """
        start = 0
        term_seq_len = 0

        while True:
            begin = colored_string.find('\033', start)

            if begin == -1:
                break

            end = colored_string.find('m', begin)

            if end == -1:
                end = len(s)

            term_seq_len += end - begin + 1
            start = end

        return len(colored_string) - term_seq_len


class FilterProxy:
    """FilterProxy class keep ensures that frame iterator will be comsumed
    properly on the first and the sole call.
    """

    def __init__(self, frames):
        self.frames = (FrameColorizer(frame, ix)
                       for ix, frame in enumerate(frames))

    def __iter__(self):
        return self

    def next(self):
        self.unroll_stack()
        raise StopIteration

    def unroll_stack(self):
        output = (str(frame) for frame in self.frames)
        print('\n'.join(output))


class ColourFilter:

    def __init__(self, name='backtrace-filter', priority=0, enabled=True):
        """Frame filter with the lower priority that consumes every frame and
        colouring output.

        :param name: The name of the filter that GDB will display.
        :param priority: The priority of the filter relative to other filters.
        :param enabled: A boolean that indicates whether this filter is enabled
        and should be executed.
        """
        self.name = name
        self.priority = priority
        self.enabled = enabled

        # Register this frame filter with the global frame_filters
        # dictionary.
        frame_filters[self.name] = self

    def filter(self, iters):
        return FilterProxy(iters)


ColourFilter()  # register colour filter forcibly

@daskol
Copy link
Owner

daskol commented Oct 18, 2022

@maksimovichsam Nice! However, I still recommend to switch to Python 3 or newer version of GDB with built-in colour support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants