diff --git a/src/libfibre/libfibre.so-gdb.py b/src/libfibre/libfibre.so-gdb.py
index 8fa49c95a559983f6594e054f1059d990b150532..559539088a6f464bf17177b45a64831fb594593b 100644
--- a/src/libfibre/libfibre.so-gdb.py
+++ b/src/libfibre/libfibre.so-gdb.py
@@ -139,32 +139,124 @@ 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(gdb.parse_and_eval("$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 (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 + '\n'
+
+    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
+        frame_groups = {}
+        line_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
+                sal = frame.find_sal()
+
+                if sal is not None and sal.symtab is not None:
+                    # Find line similarities
+                    source_str = "%s:%d" % (sal.symtab.filename, sal.line)
+                    if source_str in line_groups:
+                        line_groups[source_str].append(i)
+                    else:
+                        line_groups[source_str] = [i]
+
+                    # Find stack similarities (up to n frames)
+                    newframe = frame
+                    for j in range(n+1):
+                        name = newframe.name()
+                        if name is None:
+                            continue
+                        if name in frame_groups:
+                            frame_groups[name].append(i)
+                        else:
+                            frame_groups[name] = [i]
+                        # Step one frame up
+                        try:
+                            newframe = newframe.older()
+                            if newframe == None:
+                                break
+                        except:
+                            break
+
+        grouped_indices = set()
+        # Finally print the groups
+        for line, indices in line_groups.items():
+            if len(indices) <= 1:
+                continue
+            print("Fibres at line ", line, " :")
+            print(self.header)
+            for i in indices:
+                fibre_info, frame_info = all_output[i]
+                print(fibre_info) # Consider if we should print frame_info
+                grouped_indices.add(i)
+            print()
+        line_grouped_indices = set(grouped_indices)
+        for frame, indices in frame_groups.items():
+            # Eliminate line grouped fibres
+            indices = set(indices) - line_grouped_indices
+            if len(indices) <= 1:
+                continue
+            print("Fibres called by ", frame, " :")
+            print(self.header)
+            for i in indices:
+                fibre_info, frame_info = all_output[i]
+                print(fibre_info, '\t', frame_info)
+                grouped_indices.add(i)
+            print()
+
+        # Print anything not grouped
+        ungrouped = set(range(len(all_output))) - grouped_indices
+        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='')
+        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):