├── .flake8 ├── README.md └── stack-inspector.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 100 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stack-inspector 2 | 3 | A `gdb` command to inspect the size of objects on the stack. 4 | 5 | ![](https://i.imgur.com/uiDEJab.png) 6 | 7 | ## how to 8 | 9 | Use `gdb` to navigate to a certain stack frame (run until your stack overflows or set a breakpoint somewhere). Then, simply run: 10 | ```gdb 11 | source stack-inspector.py 12 | stack-inspector 13 | ``` 14 | -------------------------------------------------------------------------------- /stack-inspector.py: -------------------------------------------------------------------------------- 1 | import gdb 2 | from collections import OrderedDict, namedtuple 3 | 4 | 5 | ANSI_BOLD = "\x1b[1m" 6 | ANSI_GREEN = "\x1b[32m" 7 | ANSI_MAGENTA = "\x1b[35m" 8 | ANSI_CYAN = "\x1b[36m" 9 | ANSI_RESET = "\x1b[0m" 10 | 11 | 12 | Symbol = namedtuple('Symbol', ['size', 'typename']) 13 | 14 | 15 | def analyze_frame(frame_nr, frame): 16 | info = frame.find_sal() 17 | 18 | if info.symtab: 19 | if frame.function(): 20 | function_name = frame.function().name 21 | else: 22 | function_name = "?" 23 | print(" {bold}#{frame_nr:<3}{reset} " 24 | "{green}{function}{reset}" 25 | " @ " 26 | "{filename}:{line}\n".format( 27 | frame_nr=frame_nr, 28 | filename=info.symtab.filename, 29 | line=info.line, 30 | function=function_name, 31 | bold=ANSI_BOLD, 32 | green=ANSI_GREEN, 33 | reset=ANSI_RESET)) 34 | else: 35 | print(" {bold}#{frame_nr:<3}{reset} Could not retrieve frame information\n".format( 36 | frame_nr=frame_nr, 37 | bold=ANSI_BOLD, 38 | green=ANSI_GREEN, 39 | reset=ANSI_RESET)) 40 | return 41 | 42 | try: 43 | block = frame.block() 44 | except RuntimeError: 45 | print("Could not retrieve block information") 46 | return 47 | 48 | if frame.type() == gdb.INLINE_FRAME: 49 | print(" Frame is inlined.") 50 | print() 51 | return 52 | 53 | symbols = {} 54 | while block: 55 | if not (block.is_global or block.is_static): 56 | for symbol in block: 57 | # We only show symbols which are on the call stack 58 | # - function arguments 59 | # - local variables (which need frame information, no static variables) 60 | if symbol.is_argument or \ 61 | (symbol.is_variable and symbol.addr_class != gdb.SYMBOL_LOC_STATIC): 62 | if symbol.name not in symbols: 63 | symbols[symbol.name] = Symbol(symbol.type.sizeof, symbol.type) 64 | 65 | block = block.superblock 66 | 67 | symbols = OrderedDict(sorted(symbols.items(), 68 | key=lambda s: s[1].size, 69 | reverse=True)) 70 | 71 | total_size = 0 72 | for name, (size, typename) in symbols.items(): 73 | print(" {bold}{size:>14,}{reset} {name} ({cyan}{typename}{reset})".format( 74 | size=size, 75 | name=name, 76 | typename=typename, 77 | cyan=ANSI_CYAN, 78 | magenta=ANSI_MAGENTA, 79 | bold=ANSI_BOLD, 80 | reset=ANSI_RESET 81 | )) 82 | if size: 83 | total_size += size 84 | 85 | print() 86 | 87 | return total_size 88 | 89 | 90 | class StackVisualizer(gdb.Command): 91 | """Inspect the stack for large objects""" 92 | 93 | def __init__(self): 94 | super(StackVisualizer, self).__init__("stack-inspector", gdb.COMMAND_STACK) 95 | 96 | def invoke(self, arg, from_tty): 97 | try: 98 | frame = gdb.selected_frame() 99 | except gdb.error: 100 | print("[stack-inspector] could not retrieve frame information (no stack).") 101 | return 102 | 103 | backtrace = [] 104 | 105 | while frame: 106 | backtrace.append(frame) 107 | frame = frame.older() 108 | 109 | print() 110 | total_size = 0 111 | for frame_nr, frame in enumerate(backtrace): 112 | frame_size = analyze_frame(frame_nr, frame) 113 | if frame_size: 114 | total_size += frame_size 115 | 116 | print("Total size: {size:,}".format(size=total_size)) 117 | 118 | 119 | StackVisualizer() 120 | --------------------------------------------------------------------------------