Skip to content
Snippets Groups Projects

Add new argument to info fibres to enable grouping by line/stack

Merged Mohammed Bilal Akhtar requested to merge mbakhtar/KOS:fibre-grouping-initial into master
@@ -15,12 +15,8 @@ class FibreSupport():
return
FibreSupport.list = []
FibreSupport.active = {}
FibreSupport.threads = {}
FibreSupport.saved = True
# save register context for later continuation
FibreSupport.rsp = str(gdb.parse_and_eval("$rsp")).split(None, 1)[0]
FibreSupport.rbp = str(gdb.parse_and_eval("$rbp")).split(None, 1)[0]
FibreSupport.rip = str(gdb.parse_and_eval("$rip")).split(None, 1)[0]
FibreSupport.currStack = str(gdb.parse_and_eval("Context::currStack"))
# traverse runtime stack list to build internal list of fibres
_globalStackList = gdb.parse_and_eval("_globalStackList")
first = _globalStackList['anchor'].address
@@ -34,21 +30,39 @@ class FibreSupport():
currStack = str(gdb.parse_and_eval("Context::currStack"))
# Cache the registers for this thread, in case it represents
# a fibre
rsp = str(gdb.parse_and_eval("$rsp")).split(None, 1)[0]
rbp = str(gdb.parse_and_eval("$rbp")).split(None, 1)[0]
rip = str(gdb.parse_and_eval("$rip")).split(None, 1)[0]
FibreSupport.threads[thread.num] = {
'rsp': rsp,
'rbp': rbp,
'rip': rip,
'currStack': currStack
}
FibreSupport.active[currStack] = {
'rsp': str(gdb.parse_and_eval("$rsp")).split(None, 1)[0],
'rbp': str(gdb.parse_and_eval("$rbp")).split(None, 1)[0],
'rip': str(gdb.parse_and_eval("$rip")).split(None, 1)[0]
'rsp': rsp,
'rbp': rbp,
'rip': rip
}
orig_thread.switch()
# restore() is hooked to continue events via script hooks to 'fibre reset'
def restore():
FibreSupport.prep_frame()
# restore original register context
gdb.execute("set $rsp = " + str(FibreSupport.rsp))
gdb.execute("set $rbp = " + str(FibreSupport.rbp))
gdb.execute("set $rip = " + str(FibreSupport.rip))
gdb.execute("set Context::currStack = " + str(FibreSupport.currStack))
orig_thread = gdb.selected_thread()
for thread in gdb.selected_inferior().threads():
thread.switch()
rsp = FibreSupport.threads[thread.num]['rsp']
rbp = FibreSupport.threads[thread.num]['rbp']
rip = FibreSupport.threads[thread.num]['rip']
currStack = FibreSupport.threads[thread.num]['currStack']
FibreSupport.prep_frame()
# restore original register context
gdb.execute("set $rsp = " + str(rsp))
gdb.execute("set $rbp = " + str(rbp))
gdb.execute("set $rip = " + str(rip))
gdb.execute("set Context::currStack = " + str(currStack))
orig_thread.switch()
FibreSupport.saved = False
def prep_frame():
@@ -139,32 +153,115 @@ class FibreSupport():
class InfoFibres(gdb.Command):
"""Print list of fibres"""
header = " Idx \tTarget\tPtr \t\t Frame"
def __init__(self):
super(InfoFibres, self).__init__("info fibres", gdb.COMMAND_USER)
def get_frame_string(self, frame):
# Print instruction pointer
result = str(frame.read_register('rip')).split(None, 1)[0]
result += " in " + frame.name()
sal = frame.find_sal()
if sal is not None and sal.symtab is not None:
result += " at " + sal.symtab.filename + ":" + str(sal.line)
return result
def get_row_for(self, idx, curr, frame, print_frame_info=True):
result = ""
if (str(FibreSupport.list[idx]) == curr):
result += "* "
else:
result += " "
result += str(idx) + "\tFibre\t" + str(FibreSupport.list[idx])
if frame is not None and print_frame_info == True:
result += '\t' + self.get_frame_string(frame)
return result
def print_all_fibres(self, curr):
print(self.header)
for i in range(len(FibreSupport.list)):
with FibreSupport.get_frame(FibreSupport.list[i]) as frame:
print(self.get_row_for(i, curr, frame))
def print_grouped_fibres(self, curr, n):
all_fibres = FibreSupport.list
groups = {}
all_output = []
for i in range(len(FibreSupport.list)):
with FibreSupport.get_frame(FibreSupport.list[i]) as frame:
all_output.append((
self.get_row_for(i, curr, frame, False),
self.get_frame_string(frame),
))
if frame is None:
continue
# Find stack similarities (up to n frames)
newframe = frame
prevframe = None
rip_str = ""
for j in range(n+1):
rip_str += str(newframe.read_register('rip')).split(None, 1)[0] + ","
# Step one frame up ("older" in gdb's vernacular)
try:
prevframe = newframe
newframe = newframe.older()
if newframe == None:
break
except:
break
elem = (i, prevframe.name())
if rip_str in groups:
groups[rip_str].append(elem)
else:
groups[rip_str] = [elem]
grouped_indices = set()
sorted_groups = list(groups.items())
sorted(sorted_groups, key=lambda x: len(x[1])) # Smaller groups first
for rip, fibres in sorted_groups:
if len(fibres) <= 1:
# Skip groups of 1
continue
print("Fibres called by same line in ", fibres[0][1], " :")
indices = map(lambda x: x[0], fibres)
joined_indices = []
for i in indices:
grouped_indices.add(i)
if len(joined_indices) > 0 and (joined_indices[-1][1] + 1) == i:
start, end = joined_indices[-1]
joined_indices[-1] = (start, i)
else:
joined_indices.append((i,i))
joined_indices_strs = map(
lambda x: '%d-%d' % (x[0], x[1]) if x[0] < x[1] else str(x[0]),
joined_indices
)
print(', '.join(list(joined_indices_strs)))
print() # Extra newline
# Print anything not grouped
ungrouped = set(range(len(all_output))) - grouped_indices
if len(ungrouped) > 0:
print('All ungrouped fibres')
print(self.header)
for i in ungrouped:
fibre_info, frame_info = all_output[i]
print(fibre_info, '\t', frame_info)
def invoke(self, arg, from_tty):
if (not FibreSupport.saved):
return
curr = gdb.parse_and_eval("Context::currStack")
print(" Idx \tTarget\tPtr \t\t Frame")
for i in range(len(FibreSupport.list)):
if (FibreSupport.list[i] == curr):
print("* ", end='')
curr = str(gdb.parse_and_eval("Context::currStack"))
try:
if (arg != None and int(arg) >= 0):
self.print_grouped_fibres(curr, int(arg))
else:
print(" ", end='')
print(str(i), "\tFibre\t", str(FibreSupport.list[i]), sep='', end='')
with FibreSupport.get_frame(FibreSupport.list[i]) as frame:
if frame is not None:
# Print instruction pointer
print("\t", str(gdb.parse_and_eval("$rip")).split(None, 1)[0], end='')
print(" in ", frame.name(), sep='', end='')
sal = frame.find_sal()
if sal is not None and sal.symtab is not None:
print(" at ", sal.symtab.filename, ":", sal.line, sep='')
else:
print()
else:
print()
self.print_all_fibres(curr)
except ValueError:
self.print_all_fibres(curr)
class Fibre(gdb.Command):
def __init__(self):
Loading