From f519b94be7086852f2afe4ec248786b47968f7ff Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 6 Nov 2016 21:57:26 +0200 Subject: [PATCH 1/3] Add variable trace For debugging changes to variables, either normally or as a result of a memory corruption. --- build_keyboard.mk | 2 + quantum/variable_trace.c | 108 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 quantum/variable_trace.c diff --git a/build_keyboard.mk b/build_keyboard.mk index 282adcb1..461b17cd 100644 --- a/build_keyboard.mk +++ b/build_keyboard.mk @@ -180,6 +180,8 @@ ifeq ($(strip $(SERIAL_LINK_ENABLE)), yes) VAPTH += $(SERIAL_PATH) endif +SRC += $(QUANTUM_DIR)/variable_trace.c + # Optimize size but this may cause error "relocation truncated to fit" #EXTRALDFLAGS = -Wl,--relax diff --git a/quantum/variable_trace.c b/quantum/variable_trace.c new file mode 100644 index 00000000..dfa37bde --- /dev/null +++ b/quantum/variable_trace.c @@ -0,0 +1,108 @@ +#include "variable_trace.h" +#include +#include + +#ifdef NO_PRINT +#error "You need undef NO_PRINT to use the variable trace feature" +#endif + +#ifndef CONSOLE_ENABLE +#error "The console needs to be enabled in the makefile to use the variable trace feature" +#endif + + +#define NUM_TRACED_VARIABLES 1 +#define MAX_TRACE_SIZE 4 + +typedef struct { + const char* name; + void* addr; + unsigned size; + const char* func; + int line; + uint8_t last_value[MAX_TRACE_SIZE]; + +} traced_variable_t; + +static traced_variable_t traced_variables[NUM_TRACED_VARIABLES]; + +void add_traced_variable(const char* name, void* addr, unsigned size, const char* func, int line) { + verify_traced_variables(func, line); + if (size > MAX_TRACE_SIZE) { +#if defined(__AVR__) + xprintf("Traced variable \"%S\" exceeds the maximum size %d\n", name, size); +#else + xprintf("Traced variable \"%s\" exceeds the maximum size %d\n", name, size); +#endif + size = MAX_TRACE_SIZE; + } + int index = -1; + for (int i = 0; i < NUM_TRACED_VARIABLES; i++) { + if (index == -1 && traced_variables[i].addr == NULL){ + index = i; + } + else if (strcmp_P(name, traced_variables[i].name)==0) { + index = i; + break; + } + } + + if (index == -1) { + xprintf("You can only trace %d variables at the same time\n", NUM_TRACED_VARIABLES); + return; + } + + traced_variable_t* t = &traced_variables[index]; + t->name = name; + t->addr = addr; + t->size = size; + t->func = func; + t->line = line; + memcpy(&t->last_value[0], addr, size); + +} + +void remove_traced_variable(const char* name, const char* func, int line) { + verify_traced_variables(func, line); + for (int i = 0; i < NUM_TRACED_VARIABLES; i++) { + if (strcmp_P(name, traced_variables[i].name)==0) { + traced_variables[i].name = 0; + traced_variables[i].addr = NULL; + break; + } + } +} + +void verify_traced_variables(const char* func, int line) { + for (int i = 0; i < NUM_TRACED_VARIABLES; i++) { + traced_variable_t* t = &traced_variables[i]; + if (t->addr != NULL && t->name != NULL) { + if (memcmp(t->last_value, t->addr, t->size)!=0){ +#if defined(__AVR__) + xprintf("Traced variable \"%S\" has been modified\n", t->name); + xprintf("Between %S:%d\n", t->func, t->line); + xprintf("And %S:%d\n", func, line); + +#else + xprintf("Traced variable \"%s\" has been modified\n", t->name); + xprintf("Between %s:%d\n", t->func, t->line); + xprintf("And %s:%d\n", func, line); +#endif + xprintf("Previous value "); + for (int j=0; jsize;j++) { + print_hex8(t->last_value[j]); + } + xprintf("\nNew value "); + uint8_t* addr = (uint8_t*)(t->addr); + for (int j=0; jsize;j++) { + print_hex8(addr[j]); + } + xprintf("\n"); + memcpy(t->last_value, addr, t->size); + } + } + + t->func = func; + t->line = line; + } +} From a377017c95b826d83ac7a46ef176d39a58294b44 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 6 Nov 2016 22:11:24 +0200 Subject: [PATCH 2/3] Add possibility to control variable trace from make --- build_keyboard.mk | 8 +++++++- quantum/variable_trace.c | 10 ++++++---- quantum/variable_trace.h | 25 +++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 quantum/variable_trace.h diff --git a/build_keyboard.mk b/build_keyboard.mk index 461b17cd..61aebf39 100644 --- a/build_keyboard.mk +++ b/build_keyboard.mk @@ -180,7 +180,13 @@ ifeq ($(strip $(SERIAL_LINK_ENABLE)), yes) VAPTH += $(SERIAL_PATH) endif -SRC += $(QUANTUM_DIR)/variable_trace.c +ifneq ($(strip $(VARIABLE_TRACE)),) + SRC += $(QUANTUM_DIR)/variable_trace.c + OPT_DEFS += -DNUM_TRACED_VARIABLES=$(strip $(VARIABLE_TRACE)) +ifneq ($(strip $(MAX_VARIABLE_TRACE_SIZE)),) + OPT_DEFS += -DMAX_VARIABLE_TRACE_SIZE=$(strip $(MAX_VARIABLE_TRACE_SIZE)) +endif +endif # Optimize size but this may cause error "relocation truncated to fit" #EXTRALDFLAGS = -Wl,--relax diff --git a/quantum/variable_trace.c b/quantum/variable_trace.c index dfa37bde..de580244 100644 --- a/quantum/variable_trace.c +++ b/quantum/variable_trace.c @@ -12,7 +12,9 @@ #define NUM_TRACED_VARIABLES 1 -#define MAX_TRACE_SIZE 4 +#ifndef MAX_VARIABLE_TRACE_SIZE + #define MAX_VARIABLE_TRACE_SIZE 4 +#endif typedef struct { const char* name; @@ -20,7 +22,7 @@ typedef struct { unsigned size; const char* func; int line; - uint8_t last_value[MAX_TRACE_SIZE]; + uint8_t last_value[MAX_VARIABLE_TRACE_SIZE]; } traced_variable_t; @@ -28,13 +30,13 @@ static traced_variable_t traced_variables[NUM_TRACED_VARIABLES]; void add_traced_variable(const char* name, void* addr, unsigned size, const char* func, int line) { verify_traced_variables(func, line); - if (size > MAX_TRACE_SIZE) { + if (size > MAX_VARIABLE_TRACE_SIZE) { #if defined(__AVR__) xprintf("Traced variable \"%S\" exceeds the maximum size %d\n", name, size); #else xprintf("Traced variable \"%s\" exceeds the maximum size %d\n", name, size); #endif - size = MAX_TRACE_SIZE; + size = MAX_VARIABLE_TRACE_SIZE; } int index = -1; for (int i = 0; i < NUM_TRACED_VARIABLES; i++) { diff --git a/quantum/variable_trace.h b/quantum/variable_trace.h new file mode 100644 index 00000000..9899816f --- /dev/null +++ b/quantum/variable_trace.h @@ -0,0 +1,25 @@ +#ifndef VARIABLE_TRACE_H +#define VARIABLE_TRACE_H + +#include "print.h" + +#ifdef NUM_TRACED_VARIABLES + +#define ADD_TRACED_VARIABLE(name, addr, size) \ + add_traced_variable(PSTR(name), (void*)addr, size, PSTR(__FILE__), __LINE__) +#define REMOVE_TRACED_VARIABLE(name) remove_traced_variable(PSTR(name), PSTR(__FILE__), __LINE__) +#define VERIFY_TRACED_VARIABLES() verify_traced_variables(PSTR(__FILE__), __LINE__) + +#else + +#define ADD_TRACED_VARIABLE(name, addr, size) +#define REMOVE_TRACED_VARIABLE(name) +#define VERIFY_TRACED_VARIABLES() + +#endif + +// Don't call directly, use the macros instead +void add_traced_variable(const char* name, void* addr, unsigned size, const char* func, int line); +void remove_traced_variable(const char* name, const char* func, int line); +void verify_traced_variables(const char* func, int line); +#endif From 0ba3e523a7c124e4ce54dfd043dc32e72ad3233b Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 6 Nov 2016 22:44:43 +0200 Subject: [PATCH 3/3] Add documentation for the variable tracing --- quantum/variable_trace.h | 9 +++++++++ readme.md | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/quantum/variable_trace.h b/quantum/variable_trace.h index 9899816f..46bd8278 100644 --- a/quantum/variable_trace.h +++ b/quantum/variable_trace.h @@ -1,13 +1,22 @@ #ifndef VARIABLE_TRACE_H #define VARIABLE_TRACE_H +// For more information about the variable tracing see the readme. + #include "print.h" #ifdef NUM_TRACED_VARIABLES +// Start tracing a variable at the memory address addr +// The name can be anything and is used only for reporting +// The size should usually be the same size as the variable you are interested in #define ADD_TRACED_VARIABLE(name, addr, size) \ add_traced_variable(PSTR(name), (void*)addr, size, PSTR(__FILE__), __LINE__) + +// Stop tracing the variable with the given name #define REMOVE_TRACED_VARIABLE(name) remove_traced_variable(PSTR(name), PSTR(__FILE__), __LINE__) + +// Call to get messages when the variable has been changed #define VERIFY_TRACED_VARIABLES() verify_traced_variables(PSTR(__FILE__), __LINE__) #else diff --git a/readme.md b/readme.md index 60a94d7d..d5a259cc 100644 --- a/readme.md +++ b/readme.md @@ -343,6 +343,10 @@ This allows you to interface with a Bluefruit EZ-key to send keycodes wirelessly This allows you output audio on the C6 pin (needs abstracting). See the [audio section](#driving-a-speaker---audio-support) for more information. +`VARIABLE_TRACE` + +Use this to debug changes to variable values, see the [tracing variables](#tracing-variables) section for more information. + ### Customizing Makefile options on a per-keymap basis If your keymap directory has a file called `Makefile` (note the filename), any Makefile options you set in that file will take precedence over other Makefile options for your particular keyboard. @@ -1283,3 +1287,22 @@ If there are problems with the tests, you can find the executable in the `./buil It's not yet possible to do a full integration test, where you would compile the whole firmware and define a keymap that you are going to test. However there are plans for doing that, because writing tests that way would probably be easier, at least for people that are not used to unit testing. In that model you would emulate the input, and expect a certain output from the emulated keyboard. + +# Tracing variables + +Sometimes you might wonder why a variable gets changed and where, and this can be quite tricky to track down without having a debugger. It's of course possible to manually add print statements to track it, but you can also enable the variable trace feature. This works for both for variables that are changed by the code, and when the variable is changed by some memory corruption. + +To take the feature into use add `VARIABLE_TRACE=x` to the end of you make command. `x` represents the number of variables you want to trace, which is usually 1. + +Then at a suitable place in the code, call `ADD_TRACED_VARIABLE`, to begin the tracing. For example to trace all the layer changes, you can do this +```c +void matrix_init_user(void) { + ADD_TRACED_VARIABLE("layer", &layer_state, sizeof(layer_state)); +} +``` + +This will add a traced variable named "layer" (the name is just for your information), which tracks the memory location of `layer_state`. It tracks 4 bytes (the size of `layer_state`), so any modification to the variable will be reported. By default you can not specify a size bigger than 4, but you can change it by adding `MAX_VARIABLE_TRACE_SIZE=x` to the end of the make command line. + +In order to actually detect changes to the variables you should call `VERIFY_TRACED_VARIABLES` around the code that you think that modifies the variable. If a variable is modified it will tell you between which two `VERIFY_TRACED_VARIABLES` calls the modification happened. You can then add more calls to track it down further. I don't recommend spamming the codebase with calls. It's better to start with a few, and then keep adding them in a binary search fashion. You can also delete the ones you don't need, as each call need to store the file name and line number in the ROM, so you can run out of memory if you add too many calls. + +Also remember to delete all the tracing code ones you have found the bug, as you wouldn't want to create a pull request with tracing code. \ No newline at end of file