Reputation: 5310
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
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
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
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