Skip to content

Instantly share code, notes, and snippets.

@depau
Created January 9, 2021 15:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save depau/a812290bbc01ae5f3b3015a02e91b20d to your computer and use it in GitHub Desktop.
Save depau/a812290bbc01ae5f3b3015a02e91b20d to your computer and use it in GitHub Desktop.
Strace indent and colorize

Strace indent and colorize

Parses strace's output and indents filesystem access calls, colorizing and indenting equally all access to the same file descriptor.

screenshot

#!/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