From 1cfaa53c98b37d239878b7475910d2e5d8bc1bc6 Mon Sep 17 00:00:00 2001 From: Koen De Vleeschauwer Date: Tue, 24 Oct 2023 08:48:36 +0200 Subject: [PATCH] feature: memwatch: read memory while running --- src/Makefile | 6 ++++ src/include/memwatch.h | 30 +++++++++++++++++ src/main.c | 5 +++ src/memwatch.c | 65 +++++++++++++++++++++++++++++++++++++ src/target/cortexm.c | 73 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 179 insertions(+) create mode 100644 src/include/memwatch.h create mode 100644 src/memwatch.c diff --git a/src/Makefile b/src/Makefile index ca97790fa7b..faeb086f842 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,6 +2,7 @@ PROBE_HOST ?= native PLATFORM_DIR = platforms/$(PROBE_HOST) VPATH += $(PLATFORM_DIR) target ENABLE_DEBUG ?= +ENABLE_MEMWATCH ?= 1 SYS = $(shell $(CC) -dumpmachine) @@ -116,6 +117,11 @@ CFLAGS += -DPC_HOSTED=0 include platforms/common/Makefile.inc endif +ifeq ($(ENABLE_MEMWATCH), 1) +CFLAGS += -DENABLE_MEMWATCH +SRC += memwatch.c +endif + ifeq ($(ENABLE_RTT), 1) CFLAGS += -DENABLE_RTT SRC += rtt.c rtt_if.c diff --git a/src/include/memwatch.h b/src/include/memwatch.h new file mode 100644 index 00000000000..7bfc1d304f8 --- /dev/null +++ b/src/include/memwatch.h @@ -0,0 +1,30 @@ +#ifndef MEMWATCH_H +#define MEMWATCH_H + +#include +#include +#include + +#define MEMWATCH_NUM 8 +/* string length has to be long enough to store an address 0x20000000 */ +#define MEMWATCH_STRLEN 12 + +typedef enum memwatch_format { + MEMWATCH_FMT_SIGNED, + MEMWATCH_FMT_UNSIGNED, + MEMWATCH_FMT_HEX +} memwatch_format_e; + +typedef struct { + uint32_t addr; + uint32_t value; + char name[MEMWATCH_STRLEN]; + memwatch_format_e format; +} memwatch_s; + +extern memwatch_s memwatch_table[MEMWATCH_NUM]; +extern uint32_t memwatch_cnt; +extern bool memwatch_timestamp; +extern void poll_memwatch(target_s *cur_target); + +#endif diff --git a/src/main.c b/src/main.c index 496cfd02098..ec7d42ecb33 100644 --- a/src/main.c +++ b/src/main.c @@ -29,6 +29,7 @@ #include "gdb_packet.h" #include "morse.h" #include "command.h" +#include "memwatch.h" #ifdef ENABLE_RTT #include "rtt.h" #endif @@ -58,6 +59,10 @@ static void bmp_poll_loop(void) #ifdef ENABLE_RTT if (rtt_enabled) poll_rtt(cur_target); +#endif +#ifdef ENABLE_MEMWATCH + if (memwatch_cnt != 0) + poll_memwatch(cur_target); #endif } diff --git a/src/memwatch.c b/src/memwatch.c new file mode 100644 index 00000000000..997712e7955 --- /dev/null +++ b/src/memwatch.c @@ -0,0 +1,65 @@ +#include "general.h" +#include "gdb_packet.h" +#include "memwatch.h" +#include "rtt_if.h" +#if PC_HOSTED == 1 +#include +#else +#include "usb_serial.h" +#endif + +memwatch_s memwatch_table[MEMWATCH_NUM]; +uint32_t memwatch_cnt = 0; +bool memwatch_timestamp = false; + +#ifndef ENABLE_RTT +uint32_t rtt_write(const char *buf, uint32_t len) +{ +#if PC_HOSTED == 1 + return write(1, buf, len); +#else + uint32_t start_ms = platform_time_ms(); + while (usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, buf, len) <= 0) { + if (platform_time_ms() - start_ms >= 25) + return 0; /* drop silently */ + } + return len; +#endif +} +#endif + +void poll_memwatch(target_s *cur_target) +{ + uint32_t val; + char buf[64]; + char timestamp[64]; + uint32_t len; + if (!cur_target || (memwatch_cnt == 0)) + return; + + for (uint32_t i = 0; i < memwatch_cnt; i++) { + if (!target_mem_read(cur_target, &val, memwatch_table[i].addr, sizeof(val)) && + (val != memwatch_table[i].value)) { + if (memwatch_timestamp) + snprintf(timestamp, sizeof(timestamp), "%" PRIu32 " ", platform_time_ms()); + else + timestamp[0] = '\0'; + switch (memwatch_table[i].format) { + case MEMWATCH_FMT_SIGNED: + len = snprintf(buf, sizeof(buf), "%s%s %" PRId32 "\r\n", timestamp, memwatch_table[i].name, val); + break; + case MEMWATCH_FMT_UNSIGNED: + len = snprintf(buf, sizeof(buf), "%s%s %" PRIu32 "\r\n", timestamp, memwatch_table[i].name, val); + break; + case MEMWATCH_FMT_HEX: + default: + len = snprintf(buf, sizeof(buf), "%s%s 0x%" PRIx32 "\r\n", timestamp, memwatch_table[i].name, val); + break; + } + buf[sizeof(buf) - 1] = '\0'; + rtt_write(buf, len); + memwatch_table[i].value = val; + } + } + return; +} diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 3c4f1d2698f..122e1964049 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -41,6 +41,7 @@ #include "command.h" #include "gdb_packet.h" #include "semihosting.h" +#include "memwatch.h" #include "platform.h" #include @@ -53,6 +54,7 @@ #include #include #include +#include #if PC_HOSTED == 1 @@ -72,12 +74,18 @@ #endif static bool cortexm_vector_catch(target_s *t, int argc, const char **argv); +#ifdef ENABLE_MEMWATCH +static bool cortexm_memwatch(target_s *t, int argc, const char **argv); +#endif #if PC_HOSTED == 0 static bool cortexm_redirect_stdout(target_s *t, int argc, const char **argv); #endif const command_s cortexm_cmd_list[] = { {"vector_catch", cortexm_vector_catch, "Catch exception vectors"}, +#ifdef ENABLE_MEMWATCH + {"memwatch", cortexm_memwatch, "Print variable when value changes: [[NAME] [/d|/u|/x] ADDRESS]..."}, +#endif #if PC_HOSTED == 0 {"redirect_stdout", cortexm_redirect_stdout, "Redirect semihosting stdout to USB UART"}, #endif @@ -1376,6 +1384,71 @@ static bool cortexm_vector_catch(target_s *t, int argc, const char **argv) return true; } +#ifdef ENABLE_MEMWATCH +static bool cortexm_memwatch(target_s *t, int argc, const char **argv) +{ + memwatch_format_e fmt = MEMWATCH_FMT_HEX; + char name[MEMWATCH_STRLEN] = {0}; + memset(memwatch_table, 0, sizeof(memwatch_table)); + memwatch_cnt = 0; + memwatch_timestamp = false; + /* target has to support debugger memory access while running */ + if (target_mem_access_needs_halt(t)) { + gdb_out("memwatch not supported for target\n"); + return true; + } + for (int32_t i = 1; i < argc; i++) { + if (argv[i][0] == '/') { + /* format follows */ + switch (argv[i][1]) { + case 'd': + fmt = MEMWATCH_FMT_SIGNED; + break; + case 'u': + fmt = MEMWATCH_FMT_UNSIGNED; + break; + case 'x': + fmt = MEMWATCH_FMT_HEX; + break; + case 't': + memwatch_timestamp = true; + break; + default: + break; + } + } else if (isalpha(argv[i][0])) { + /* name follows */ + strncpy(name, argv[i], sizeof(name)); + name[sizeof(name) - 1] = '\0'; + } else if (isdigit(argv[i][0])) { + /* address follows */ + uint32_t addr = strtoul(argv[i], NULL, 0); + + /* add new name, format and address to memwatch table */ + if (name[0] == '\0') { + /* no name given, use address as name */ + snprintf(name, MEMWATCH_STRLEN, "0x%08" PRIx32, addr); + name[MEMWATCH_STRLEN - 1] = '\0'; + } + memwatch_table[memwatch_cnt].addr = addr; + memwatch_table[memwatch_cnt].value = 0xdeadbeef; + memwatch_table[memwatch_cnt].format = fmt; + memcpy(memwatch_table[memwatch_cnt].name, name, MEMWATCH_STRLEN); + memwatch_cnt++; + memset(name, 0, sizeof(name)); + gdb_outf("0x%x ", addr); + if (memwatch_cnt == MEMWATCH_NUM) + break; + } else { + gdb_outf("?"); + break; + } + } + gdb_outf("\n"); + return true; +} +#endif + #if PC_HOSTED == 0 static bool cortexm_redirect_stdout(target_s *t, int argc, const char **argv) {