From b414e0b946fb874b2cee74539cc343be90bee886 Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Thu, 10 Mar 2016 12:28:53 +1100
Subject: [PATCH] Add 'filteroutput.py' tool to automatically do addr2line
 lookups on likely hex values

---
 common.mk             |   5 +-
 utils/filteroutput.py | 105 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 109 insertions(+), 1 deletion(-)
 create mode 100755 utils/filteroutput.py

diff --git a/common.mk b/common.mk
index 8da70cd..c8fee7a 100644
--- a/common.mk
+++ b/common.mk
@@ -75,6 +75,9 @@ FLAVOR ?= release # or debug
 # Compiler names, etc. assume gdb
 CROSS ?= xtensa-lx106-elf-
 
+# Path to the filteroutput.py tool
+FILTEROUTPUT ?= $(ROOT)/utils/filteroutput.py
+
 AR = $(CROSS)ar
 CC = $(CROSS)gcc
 CPP = $(CROSS)cpp
@@ -385,7 +388,7 @@ size: $(PROGRAM_OUT)
 	$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)
 
 test: flash
-	screen $(ESPPORT) 115200
+	$(FILTEROUTPUT) --port $(ESPPORT) --baud 115200 --elf $(PROGRAM_OUT)
 
 # the rebuild target is written like this so it can be run in a parallel build
 # environment without causing weird side effects
diff --git a/utils/filteroutput.py b/utils/filteroutput.py
new file mode 100755
index 0000000..5e9b1cf
--- /dev/null
+++ b/utils/filteroutput.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+#
+# A thin Python wrapper around addr2line, can monitor esp-open-rtos
+# output and uses gdb to convert any suitable looking hex numbers
+# found in the output into function and line numbers.
+#
+# Works with a serial port if the --port option is supplied.
+# Otherwise waits for input on stdin.
+#
+import serial
+import argparse
+import re
+import os
+import os.path
+import subprocess
+import termios
+import sys
+import time
+
+# Try looking up anything in the executable address space
+RE_EXECADDR = r"(0x)?40([0-9]|[a-z]){6}"
+
+def find_elf_file():
+    out_files = []
+    for top,_,files in os.walk('.', followlinks=False):
+        for f in files:
+            if f.endswith(".out"):
+                out_files.append(os.path.join(top,f))
+    if len(out_files) == 1:
+        return out_files[0]
+    elif len(out_files) > 1:
+        print("Found multiple .out files: %s. Please specify one with the --elf option." % out_files)
+    else:
+        print("No .out file found under current directory. Please specify one with the --elf option.")
+    sys.exit(1)
+
+def main():
+    parser = argparse.ArgumentParser(description='esp-open-rtos output filter tool', prog='filteroutput')
+    parser.add_argument(
+        '--elf', '-e',
+        help="ELF file (*.out file) to load symbols from (if not supplied, will search for one)"),
+    parser.add_argument(
+        '--port', '-p',
+        help='Serial port to monitor (will monitor stdin if None)',
+        default=None)
+    parser.add_argument(
+        '--baud', '-b',
+        help='Baud rate for serial port',
+        type=int,
+        default=74880)
+    parser.add_argument(
+        '--reset-on-connect', '-r',
+        help='Reset ESP8266 (via DTR) on serial connect. (Linux resets even if not set, except when using NodeMCU-style auto-reset circuit.)',
+        action='store_true')
+
+    args = parser.parse_args()
+
+    if args.elf is None:
+        args.elf = find_elf_file()
+    elif not os.path.exists(args.elf):
+        print("ELF file '%s' not found" % args.elf)
+        sys.exit(1)
+
+    if args.port is not None:
+        print("Opening %s at %dbps..." % (args.port, args.baud))
+        port = serial.Serial(args.port, baudrate=args.baud)
+        if args.reset_on_connect:
+            print("Resetting...")
+            port.setDTR(False)
+            time.sleep(0.1)
+            port.setDTR(True)
+
+    else:
+        print("Reading from stdin...")
+        port = sys.stdin
+        # disable echo
+	try:
+            old_attr = termios.tcgetattr(sys.stdin.fileno())
+	    attr = termios.tcgetattr(sys.stdin.fileno())
+	    attr[3] = attr[3] & ~termios.ECHO
+	    termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, attr)
+	except termios.error:
+	     pass
+
+    try:
+        while True:
+            line = port.readline()
+            if line == '':
+                break
+            print(line.strip())
+            for match in re.finditer(RE_EXECADDR, line, re.IGNORECASE):
+                addr = match.group(0)
+                if not addr.startswith("0x"):
+                    addr = "0x"+addr
+                    # keeping addr2line and feeding it addresses on stdin didn't seem to work smoothly
+                addr2line = subprocess.check_output(["xtensa-lx106-elf-addr2line","-pfia","-e","%s" % args.elf, addr], cwd=".").strip()
+                if not addr2line.endswith(": ?? ??:0"):
+                    print("\n%s\n" % addr2line.strip())
+    finally:
+        if args.port is None:
+            # restore echo
+            termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old_attr)
+
+if __name__ == "__main__":
+    main()