Created
March 29, 2025 16:58
-
-
Save rnapier/f6f47d446846332621564702149d9307 to your computer and use it in GitHub Desktop.
Read .o's and output stack allocations per-function
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! env python3 | |
import os | |
import re | |
import subprocess | |
import argparse | |
parser = argparse.ArgumentParser( | |
description="Process .o files to output stack allocation info." | |
) | |
parser.add_argument("paths", nargs="+", | |
help="List of directories and/or .o files to process") | |
parser.add_argument("-s", "--size", type=int, default=10*1024, | |
help="Minimum stack allocation size to output (default: 1024)") | |
parser.add_argument("-n", "--no-demangle", action="store_true", | |
help="Don't demangle symbols") | |
args = parser.parse_args() | |
func_pattern = re.compile( | |
r'^(?P<addr>[0-9a-f]+)\s+<(?P<name>[^>]+)>:\n' | |
r'(?P<body>(?:.+\n)+?)(?=\n)', | |
re.MULTILINE) | |
alloc_pattern = re.compile( | |
r'sub\s+sp,\s+sp,\s+#(0x[0-9a-f]+|\d+)(?:,\s+lsl\s+#(\d+))?', | |
re.IGNORECASE) | |
def process_file(file_path, min_size, no_demangle): | |
file_name = os.path.basename(file_path) | |
try: | |
output = subprocess.check_output(['objdump', '-d', file_path], | |
text=True) | |
except subprocess.CalledProcessError as e: | |
print(f"Error processing {file_path}: {e}") | |
return | |
for m in func_pattern.finditer(output): | |
name = m.group('name') | |
body = m.group('body') | |
alloc = None | |
for line in body.splitlines(): | |
am = alloc_pattern.search(line) | |
if am: | |
imm = int(am.group(1), 0) | |
shift = int(am.group(2)) if am.group(2) else 0 | |
alloc = imm << shift | |
break | |
if alloc is not None and alloc >= min_size: | |
if no_demangle: | |
demangled = name | |
else: | |
try: | |
demangled = subprocess.check_output( | |
['xcrun', 'swift-demangle', '--compact', '--simplified', name], | |
text=True).strip() | |
except subprocess.CalledProcessError: | |
demangled = name | |
print(f"{alloc}\t\t{file_name}\t{demangled}") | |
for path in args.paths: | |
if os.path.isdir(path): | |
for filename in os.listdir(path): | |
if filename.endswith('.o'): | |
file_path = os.path.join(path, filename) | |
process_file(file_path, args.size, args.no_demangle) | |
elif os.path.isfile(path): | |
if path.endswith('.o'): | |
process_file(path, args.size, args.no_demangle) | |
else: | |
print(f"Skipping {path}: not an .o file") | |
else: | |
print(f"Skipping {path}: not a valid file or directory") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment