Parses strace's output and indents filesystem access calls, colorizing and indenting equally all access to the same file descriptor.
Created
January 9, 2021 15:14
-
-
Save depau/a812290bbc01ae5f3b3015a02e91b20d to your computer and use it in GitHub Desktop.
Strace indent and colorize
This file contains 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
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
import random | |
import re | |
import sys | |
import traceback | |
from typing import List | |
class Color: | |
BLACK = "\033[0;30m" | |
RED = "\033[0;31m" | |
GREEN = "\033[0;32m" | |
BROWN = "\033[0;33m" | |
BLUE = "\033[0;34m" | |
PURPLE = "\033[0;35m" | |
CYAN = "\033[0;36m" | |
LIGHT_GRAY = "\033[0;37m" | |
DARK_GRAY = "\033[1;30m" | |
LIGHT_RED = "\033[1;31m" | |
LIGHT_GREEN = "\033[1;32m" | |
YELLOW = "\033[1;33m" | |
LIGHT_BLUE = "\033[1;34m" | |
LIGHT_PURPLE = "\033[1;35m" | |
LIGHT_CYAN = "\033[1;36m" | |
LIGHT_WHITE = "\033[1;37m" | |
BOLD = "\033[1m" | |
FAINT = "\033[2m" | |
ITALIC = "\033[3m" | |
UNDERLINE = "\033[4m" | |
BLINK = "\033[5m" | |
NEGATIVE = "\033[7m" | |
CROSSED = "\033[9m" | |
RESET = "\033[0m" | |
# cancel SGR codes if we don't write to a terminal | |
if not __import__("sys").stdout.isatty(): | |
for _ in dir(): | |
if isinstance(_, str) and _[0] != "_": | |
locals()[_] = "" | |
else: | |
# set Windows console in VT mode | |
if __import__("platform").system() == "Windows": | |
kernel32 = __import__("ctypes").windll.kernel32 | |
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7) | |
del kernel32 | |
usable_colors = [ | |
Color.RED, | |
Color.GREEN, | |
Color.BROWN, | |
Color.BLUE, | |
Color.PURPLE, | |
Color.CYAN, | |
Color.LIGHT_GRAY, | |
Color.DARK_GRAY, | |
Color.LIGHT_RED, | |
Color.LIGHT_GREEN, | |
Color.YELLOW, | |
Color.LIGHT_BLUE, | |
Color.LIGHT_PURPLE, | |
Color.LIGHT_CYAN, | |
Color.LIGHT_WHITE, | |
] | |
# Placeholder for when the identifier is returned by the syscall | |
# noinspection PyPep8Naming | |
class POS_RETURN: | |
pass | |
id_field_pos = { | |
"openat": POS_RETURN, | |
"mmap": 4, ## Special case handled separately | |
"pread64": 0, | |
"read": 0, | |
"close": 0, | |
'fstat': 0, | |
"mprotect": 0, | |
"munmap": 0, | |
"lseek": 0, | |
"write": 0, | |
} | |
ident_indent = {} | |
ident_color = {} | |
indent = 2 | |
max_indent = 0 | |
mmap_fd_addrs = {} | |
def get_or_add_ident(ident: str) -> (int, str): | |
global max_indent | |
if ident in ident_indent: | |
return ident_indent[ident], ident_color[ident] | |
max_indent += indent | |
color = random.choice(usable_colors) | |
ident_indent[ident] = max_indent | |
ident_color[ident] = color | |
return max_indent, color | |
def del_ident(ident: str): | |
global max_indent | |
if ident in ident_indent: | |
if ident_indent[ident] == max_indent: | |
max_indent -= indent | |
del ident_indent[ident] | |
if ident in ident_color: | |
del ident_color[ident] | |
def split_unescaped(string: str) -> List[str]: | |
return re.split(r'(?<!\\),', string) | |
class StraceLine: | |
def __init__(self, line: str): | |
self.line = line.strip() | |
self.ident = None | |
self.func = None | |
self.args = None | |
self.ret = None | |
self._parse() | |
def _parse(self): | |
match = re.search(r"(\w+)\((.*)\)\s*=\s*(.+)", self.line) | |
self.func = match.group(1) | |
self.args = split_unescaped(match.group(2)) | |
self.ret = match.group(3) | |
if self.func not in id_field_pos: | |
return | |
pos = id_field_pos[self.func] | |
if pos == POS_RETURN: | |
self.ident = self.ret | |
else: | |
try: | |
self.ident = self.args[pos] | |
except KeyError: | |
traceback.print_exc() | |
print(f"Error refers to line: '{self.line}'") | |
if self.func == 'mmap': | |
addr = self.ret if self.args[0] == 'NULL' else self.args[0] | |
# Try to match existing fd if fd is -1 | |
if self.ident == '-1': | |
if addr in mmap_fd_addrs: | |
self.ident = mmap_fd_addrs[addr] | |
else: | |
# Store addresses to match munmap | |
mmap_fd_addrs[addr] = self.ident | |
if self.func == 'munmap': | |
if self.ident in mmap_fd_addrs: | |
self.ident = mmap_fd_addrs[self.ident] | |
def main(): | |
f = sys.stdin | |
try: | |
if len(sys.argv) > 1: | |
f = open(sys.argv[1]) | |
for line in f.readlines(): | |
if not line or not line.strip(): | |
continue | |
if line.startswith("+++"): | |
print(line) | |
continue | |
try: | |
line = StraceLine(line) | |
except: | |
print(f"Error refers to line: '{line}'") | |
raise | |
if not line.ident: | |
print(line.line) | |
continue | |
# noinspection PyShadowingNames | |
indent, color = get_or_add_ident(line.ident) | |
# Randomize color after close | |
if line.func == "close": | |
del_ident(line.ident) | |
print(" " * indent + color + line.line + Color.RESET) | |
finally: | |
f.close() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment