JaredC
JaredC

Reputation: 5310

Pruning backtrace output with gdb script

My program has 100 threads, most of which are idle and share a very well defined backtrace when they are idle. Most of the time I am only interested in the threads that are not idle and therefore do not have the "common" backtrace. I thought using a gdb script would be a good way to do this.

define backtraces
    thread apply all bt
end

This script will simply print all the backtraces. Is there a way to store this output into a variable that I can then process, prune, and display only the relevant backtraces?

I naively tried:

define backtraces
    set $bts = thread apply all bt
    // do whatever processing here
end

But that fails with following as expected:

No symbol "thread" in current context.

Is there a better way to do this? Or good tutorials on how to power script in gdb?

Upvotes: 2

Views: 1457

Answers (3)

Euller Borges
Euller Borges

Reputation: 91

This is what I came out with based on JaredC wrote:

import gdb

class FilteredThreadBacktraceCommand(gdb.Command):
    """
    Implements a command that allows printing the backtrace only for threads
    that do not have given frames.

    The class must be instantiated with a list of the frames to ignore. If the
    stack for a thread has any of those frames then the stack for that thread
    will not be printed (a visual dot will be printed to show a thread was just
    ignored and provide a notion of progress).
    """
    def __init__(self, ignored_frames):
        super (FilteredThreadBacktraceCommand, self).__init__ ("fbt", gdb.COMMAND_STACK)
        self.ignored_frames = ignored_frames

    def invoke(self, arg, from_tty):
        args = gdb.string_to_argv(arg)
        if len(args) != 0:
            gdb.write("ERROR: invalid number of arguments.\n")
            return

        # This loops through all the Thread objects in the process
        for thread in gdb.selected_inferior().threads():

            # This is equivalent to 'thread X'
            thread.switch()

            f = gdb.newest_frame()
            frames = []
            invalid_thread = False
            while f is not None:
                if any(ignored_frame in f.name() for ignored_frame in self.ignored_frames):
                        invalid_thread = True
                        break
                frames.append(f)
                f = gdb.Frame.older(f)

            if invalid_thread:
                # Visual effect as iterating frames might take a while
                sys.stdout.write('.')
                sys.stdout.flush()
                continue

            print "\nThread %s:" % thread.num
            for i in range(len(frames)):
                f = frames[i]
                funcInfo = f.function()
                printStr = "#{} in {}".format(i, f.name())
                if f.function():
                    printStr += " at {}:{}".format(funcInfo.symtab.filename, funcInfo.line)
                else:
                    printStr += " at (???)"
                print(printStr)

 FilteredThreadBacktraceCommand(["os::PlatformEvent::park"])

You can provide the frames that you want to ignore to the class on creation. One could also make this generic so as to provide a name to the command to register as well so that you could have different commands with different filtering.

Upvotes: 0

JaredC
JaredC

Reputation: 5310

Using the links in Employed Russian's answer I was able to get something working. Here it is for posterity since it was completely non-obvious.

import gdb

# This loops through all the Thread objects in the process
for thread in gdb.selected_inferior().threads():

    # This is equivalent to 'thread X'
    thread.switch()       

    print "Thread %s" % thread.num

    # Just execute a raw gdb command
    gdb.execute('bt')

    framesNames = []
    f = gdb.newest_frame()
    while f is not None:
        framesNames.append(gdb.Frame.name(f))
        f = gdb.Frame.older(f)

    # do something with the name of each frame

If this were in a file named traces.py, then from gdb you can just execute the python:

source traces.py

There are other ways to invoke this python script too.

Upvotes: 2

Employed Russian
Employed Russian

Reputation: 213754

Is there a better way to do this?

You would need to use Python scripting to achieve your desired result.

The filtering backtrace is probably a good start.

Upvotes: 2

Related Questions