#!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0-or-later # Author: Oliver Smith # Copyright 2021 sysmocom - s.f.m.c. GmbH import argparse import sys import datetime def parse_args(): parser = argparse.ArgumentParser(prog="parse_strace") parser.add_argument("strace_file", help="an strace file generated by dump.sh") args = parser.parse_args() return args def parse_iov_str(iov): """ :param iov: strace iov string, looks like this: '[{iov_base="AT\0", iov_len=3}, {iov_base="[0] AT< +EXLCE:...' :returns: list of iov strings, e.g.: ["AT", "[0] AT< +EXLCE:...", ...] """ ret = [] assert iov.startswith("[{") rest = iov[2:] while rest: if rest.startswith("iov_base=\""): key_value, rest = rest.split(', iov_len', 1) value = key_value.split('"', 1)[1] ret += [value] elif rest.startswith("="): iov_len, rest = rest.split("}", 1) elif rest.startswith(", {"): _, rest = rest.split("{", 1) elif rest.startswith("]"): # parts at the end of writev are not interesting: iov count and # bytes written break else: assert False, f"failed to parse rest of iov:" \ f"\n\niov: {iov}" \ f"\n\nrest: {rest}" return ret def parse_at(cmd): is_at = False i = 0 for i in range(len(cmd["iov"])): iov_base = cmd["iov"][i] if iov_base.startswith("[0] AT"): is_at = True break if not is_at: return desc = "\n ".join(cmd["iov"][i:]) # Cut '[0 AT< ' desc = desc[8:] # Cut suffixes like ' (RIL_URC_READER, tid:502968382704)' desc = desc.split(" (RIL_URC_", 1)[0] desc = desc.split(" (RIL_CMD_", 1)[0] desc = desc.split(" (RIL_ATCI_", 1)[0] if desc.endswith("\\n\\0"): desc=desc[:-4] cmd["type"] = "at" cmd["desc"] = desc cmd["towards_modem"] = "AT>" in iov_base def parse_writev(strace_file): """ :param strace_file: path to strace file generated by dump.sh :returns: a list of writev calls found in the strace file, looks like this: [{"desc": "AT+EXCLE", # human readable short version "fd": "74" "iov": ["AT", "[0 AT< +EXLCE:...", ...], "pid": 985, "time": 1632220184.069225, "time_rel": 0.000077, "towards_modem": True, "type": "at"}, ...] type is one of: * "at" * "unknown" """ ret = [] failed = 0 with open(strace_file) as handle: for line in handle: if "writev(" not in line: continue cmd = {} rest = line.rstrip() # Cut pid, time, time_rel # rest = '985 1632220184.068694 (+ 0.000148) writev(...' # => ['985', '1632220184.068694', '(+', '0.000148)', 'writev(...' pid, time, _, time_rel, rest = rest.split(None, 4) cmd["pid"] = int(pid) cmd["time"] = float(time) cmd["time_rel"] = time_rel.split(")")[0] # Cut writev # rest = 'writev(74, [{iov_base="...' writev, rest = rest.split("(", 1) assert writev == "writev" # Cut fd # rest = '74, [{iov_base="...' cmd["fd"], rest = rest.split(", ", 1) cmd["iov"] = parse_iov_str(rest) cmd["type"] = "unknown" cmd["desc"] = cmd["iov"] cmd["towards_modem"] = True parse_at(cmd) ret += [cmd] return ret def print_cmds(cmds): """ :param cmds: return of parse_at_cmds() """ for cmd in cmds: if cmd["type"] == "unknown": continue direction = " ->" if cmd["towards_modem"] else "<- " time = datetime.datetime.fromtimestamp(cmd["time"]).strftime('%H:%M:%S') print(f"{time} (ril {direction} modem) {cmd['desc']}") def main(): args = parse_args() cmds = parse_writev(args.strace_file) print_cmds(cmds) if __name__ == "__main__": sys.exit(main())