From 398f96cdfe7e546ae151ff8eeda24b0c0652f2a5 Mon Sep 17 00:00:00 2001 From: Brent Perteet Date: Wed, 19 Nov 2025 09:28:12 -0600 Subject: [PATCH] initial commit --- app.py | 111 +++++++++++++++++++++++++++++++++++++++++ index.html | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 app.py create mode 100644 index.html diff --git a/app.py b/app.py new file mode 100644 index 0000000..9c39119 --- /dev/null +++ b/app.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +from flask import Flask, request, jsonify, send_from_directory +import subprocess +import re +import os + +app = Flask(__name__) + +TYPE_TO_GDB_FMT = { + "int8": "db", + "uint8": "dub", + "int16": "dh", + "uint16": "duh", + "int32": "dw", + "uint32": "duw", + "float": "f", + "double": "g", +} + +def run_gdb_once(gdb_path, target, addr, length, data_type): + if data_type not in TYPE_TO_GDB_FMT: + raise ValueError(f"Unsupported type '{data_type}'") + + fmt = TYPE_TO_GDB_FMT[data_type] + # Build gdb command: + # gdb -q --nx -ex "target remote ..." -ex "x/ " -ex "quit" + examine_cmd = f"x/{length}{fmt} {addr}" + + result = subprocess.run( + [ + gdb_path, + "--quiet", + "--nx", + "-ex", f"target remote {target}", + "-ex", examine_cmd, + "-ex", "quit", + ], + capture_output=True, + text=True, + ) + + if result.returncode != 0: + raise RuntimeError( + f"GDB exited with code {result.returncode}:\n{result.stderr or result.stdout}" + ) + + return result.stdout.splitlines() + +def parse_values(lines, data_type): + values = [] + line_re = re.compile(r"^0x[0-9a-fA-F]+:\s*(.*)$") + + for line in lines: + line = line.strip() + m = line_re.match(line) + if not m: + continue + rest = m.group(1) + tokens = rest.split() + for t in tokens: + try: + if data_type in ("float", "double"): + v = float(t) + else: + v = int(t, 10) + values.append(v) + except ValueError: + pass + + return values + +@app.route("/") +def index(): + return send_from_directory(os.path.dirname(__file__), "index.html") + +@app.route("/api/read_memory", methods=["POST"]) +def read_memory(): + data = request.get_json(force=True) + + gdb_path = data.get("gdb_path", "arm-none-eabi-gdb") + target = data.get("target", "localhost:3333") + addr = data.get("addr") + length = data.get("length") + data_type = data.get("type") + + if addr is None or length is None or data_type is None: + return jsonify({"error": "addr, length, and type are required"}), 400 + + try: + length = int(length) + except ValueError: + return jsonify({"error": "length must be an integer"}), 400 + + try: + lines = run_gdb_once(gdb_path, target, addr, length, data_type) + values = parse_values(lines, data_type) + if len(values) > length: + values = values[:length] + + return jsonify({ + "addr": addr, + "length": length, + "type": data_type, + "values": values, + }) + except Exception as e: + return jsonify({"error": str(e)}), 500 + +if __name__ == "__main__": + # For dev only — behind firewall, not on the open internet. + app.run(host="127.0.0.1", port=5000, debug=True) diff --git a/index.html b/index.html new file mode 100644 index 0000000..c0133b2 --- /dev/null +++ b/index.html @@ -0,0 +1,141 @@ + + + + + MCU Memory Plotter + + + + +

MCU Memory Plotter

+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ + + + + +