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():
@@ -15,12 +15,8 @@ class FibreSupport():
return
return
FibreSupport.list = []
FibreSupport.list = []
FibreSupport.active = {}
FibreSupport.active = {}
 
FibreSupport.threads = {}
FibreSupport.saved = True
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
# traverse runtime stack list to build internal list of fibres
_globalStackList = gdb.parse_and_eval("_globalStackList")
_globalStackList = gdb.parse_and_eval("_globalStackList")
first = _globalStackList['anchor'].address
first = _globalStackList['anchor'].address
@@ -34,21 +30,39 @@ class FibreSupport():
@@ -34,21 +30,39 @@ class FibreSupport():
currStack = str(gdb.parse_and_eval("Context::currStack"))
currStack = str(gdb.parse_and_eval("Context::currStack"))
# Cache the registers for this thread, in case it represents
# Cache the registers for this thread, in case it represents
# a fibre
# 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] = {
FibreSupport.active[currStack] = {
'rsp': str(gdb.parse_and_eval("$rsp")).split(None, 1)[0],
'rsp': rsp,
'rbp': str(gdb.parse_and_eval("$rbp")).split(None, 1)[0],
'rbp': rbp,
'rip': str(gdb.parse_and_eval("$rip")).split(None, 1)[0]
'rip': rip
}
}
orig_thread.switch()
orig_thread.switch()
# restore() is hooked to continue events via script hooks to 'fibre reset'
# restore() is hooked to continue events via script hooks to 'fibre reset'
def restore():
def restore():
FibreSupport.prep_frame()
orig_thread = gdb.selected_thread()
# restore original register context
for thread in gdb.selected_inferior().threads():
gdb.execute("set $rsp = " + str(FibreSupport.rsp))
thread.switch()
gdb.execute("set $rbp = " + str(FibreSupport.rbp))
rsp = FibreSupport.threads[thread.num]['rsp']
gdb.execute("set $rip = " + str(FibreSupport.rip))
rbp = FibreSupport.threads[thread.num]['rbp']
gdb.execute("set Context::currStack = " + str(FibreSupport.currStack))
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
FibreSupport.saved = False
def prep_frame():
def prep_frame():
@@ -139,32 +153,115 @@ class FibreSupport():
@@ -139,32 +153,115 @@ class FibreSupport():
class InfoFibres(gdb.Command):
class InfoFibres(gdb.Command):
"""Print list of fibres"""
"""Print list of fibres"""
 
header = " Idx \tTarget\tPtr \t\t Frame"
 
def __init__(self):
def __init__(self):
super(InfoFibres, self).__init__("info fibres", gdb.COMMAND_USER)
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):
def invoke(self, arg, from_tty):
if (not FibreSupport.saved):
if (not FibreSupport.saved):
return
return
curr = gdb.parse_and_eval("Context::currStack")
curr = str(gdb.parse_and_eval("Context::currStack"))
print(" Idx \tTarget\tPtr \t\t Frame")
try:
for i in range(len(FibreSupport.list)):
if (arg != None and int(arg) >= 0):
if (FibreSupport.list[i] == curr):
self.print_grouped_fibres(curr, int(arg))
print("* ", end='')
else:
else:
print(" ", end='')
self.print_all_fibres(curr)
print(str(i), "\tFibre\t", str(FibreSupport.list[i]), sep='', end='')
except ValueError:
with FibreSupport.get_frame(FibreSupport.list[i]) as frame:
self.print_all_fibres(curr)
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()
class Fibre(gdb.Command):
class Fibre(gdb.Command):
def __init__(self):
def __init__(self):
Loading