From acadfc8cb535cb645be8c64af628af74c339fcdf Mon Sep 17 00:00:00 2001 From: Oliver Smith Date: Fri, 15 Oct 2021 15:58:56 +0200 Subject: [PATCH] strace-rild: add Add scripts used to figure out IMS related AT commands. --- src/trace-rild/README.md | 11 ++ src/trace-rild/dump.sh | 14 +++ src/trace-rild/dump_run_on_phone.sh | 66 ++++++++++++ src/trace-rild/parse_strace.py | 153 ++++++++++++++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 src/trace-rild/README.md create mode 100755 src/trace-rild/dump.sh create mode 100644 src/trace-rild/dump_run_on_phone.sh create mode 100755 src/trace-rild/parse_strace.py diff --git a/src/trace-rild/README.md b/src/trace-rild/README.md new file mode 100644 index 0000000..612ace6 --- /dev/null +++ b/src/trace-rild/README.md @@ -0,0 +1,11 @@ +Scripts to run strace + tcpdump on mtkfusionrild and to extract AT commands +from the strace dump. + +Preparation: +- Rooted Android +- Termux installed, sshd enabled and running +- strace, tsu, tcpdump installed + +Usage: +1. run dump.sh +2. run parse_strace.py to get AT commands form the strace dump diff --git a/src/trace-rild/dump.sh b/src/trace-rild/dump.sh new file mode 100755 index 0000000..7064c47 --- /dev/null +++ b/src/trace-rild/dump.sh @@ -0,0 +1,14 @@ +#!/bin/sh -ex +# SPDX-License-Identifier: GPL-2.0-or-later +# Author: Oliver Smith +# Copyright 2021 sysmocom - s.f.m.c. GmbH + +dump_name="$(date "+%Y-%m-%d_%H%M%S")" + +adb push dump_run_on_phone.sh /mnt/sdcard/Download/ +adb forward tcp:8022 tcp:8022 +ssh -p 8022 localhost \ + sh -e /mnt/sdcard/Download/dump_run_on_phone.sh "$dump_name" + +mkdir -p dump +adb pull /mnt/sdcard/Download/dump/"$dump_name" dump/"$dump_name" diff --git a/src/trace-rild/dump_run_on_phone.sh b/src/trace-rild/dump_run_on_phone.sh new file mode 100644 index 0000000..b619f4d --- /dev/null +++ b/src/trace-rild/dump_run_on_phone.sh @@ -0,0 +1,66 @@ +#!/bin/sh -e +# SPDX-License-Identifier: GPL-2.0-or-later +# Author: Oliver Smith +# Copyright 2021 sysmocom - s.f.m.c. GmbH +# +# dump.sh copies this file to Android and runs it + +rild="mtkfusionrild" +strsize="1000" +dump_name="$1" +pid="$(sudo ps ax | grep "$rild" | grep -v grep | head -n1 | awk '{print $1}')" +outdir="/mnt/sdcard/Download/dump/$1" + +clean_up() { + sudo killall -9 strace tcpdump || true 2>/dev/null +} + +if [ -d "$outdir" ]; then + echo "ERROR: outdir already exists: $outdir" + exit 1 +fi + +if [ -z "$pid" ]; then + echo "ERROR: failed to get pid of $rild" + exit 1 +fi + +mkdir -p "$outdir" +set -x + +clean_up + +sudo tcpdump \ + -U \ + -i any \ + -n not port 8022 \ + -s0 \ + -w "$outdir/$1.pcap" \ + & + +sudo strace \ + -f \ + -o "$outdir/$1_$rild.strace" \ + -p "$pid" \ + -r \ + -s "$strsize" \ + -ttt \ + -v \ + -y \ + & + +sleep 1 + +set +x +echo +echo +echo "rild: $rild (pid: $pid)" +echo "Capturing to: $outdir" +echo "Press return to stop capturing" +echo +echo +read foo + +set -x +clean_up +echo "Capture stopped" diff --git a/src/trace-rild/parse_strace.py b/src/trace-rild/parse_strace.py new file mode 100755 index 0000000..e7e3ad5 --- /dev/null +++ b/src/trace-rild/parse_strace.py @@ -0,0 +1,153 @@ +#!/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())