diff --git a/Marlin/M100_Free_Mem_Chk.cpp b/Marlin/M100_Free_Mem_Chk.cpp index de61d5bb22..08236ff95e 100644 --- a/Marlin/M100_Free_Mem_Chk.cpp +++ b/Marlin/M100_Free_Mem_Chk.cpp @@ -38,6 +38,11 @@ * M100 C x Corrupts x locations within the free memory block. This is useful to check the * correctness of the M100 F and M100 D commands. * + * Also, there are two support functions that can be called from a developer's C code. + * + * uint16_t check_for_free_memory_corruption(char * const ptr); + * void M100_dump_free_memory(char *ptr, char *sp); + * * Initial version by Roxy-3D */ #define M100_FREE_MEMORY_DUMPER // Enable for the `M110 D` Dump sub-command @@ -47,7 +52,7 @@ #if ENABLED(M100_FREE_MEMORY_WATCHER) -#define TEST_BYTE 0xE5 +#define TEST_BYTE ((uint8_t) 0xE5) extern char* __brkval; extern size_t __heap_start, __heap_end, __flp; @@ -61,6 +66,7 @@ extern char __bss_end; // #define END_OF_HEAP() (__brkval ? __brkval : &__bss_end) +int check_for_free_memory_corruption(char *title); // Location of a variable on its stack frame. Returns a value above // the stack (once the function returns to the caller). @@ -70,7 +76,7 @@ char* top_of_stack() { } // Count the number of test bytes at the specified location. -int16_t count_test_bytes(const char * const ptr) { +int16_t count_test_bytes(const uint8_t * const ptr) { for (uint16_t i = 0; i < 32000; i++) if (ptr[i] != TEST_BYTE) return i - 1; @@ -78,27 +84,6 @@ int16_t count_test_bytes(const char * const ptr) { return -1; } -// Return a count of free memory blocks. -uint16_t free_memory_is_corrupted(char * const ptr, const uint16_t size) { - // Find the longest block of test bytes in the given buffer - uint16_t block_cnt = 0; - for (uint16_t i = 0; i < size; i++) { - if (ptr[i] == TEST_BYTE) { - const uint16_t j = count_test_bytes(ptr + i); - if (j > 8) { - //SERIAL_ECHOPAIR("Found ", j); - //SERIAL_ECHOLNPAIR(" bytes free at 0x", hex_word((uint16_t)ptr + i)); - i += j; - block_cnt++; - } - } - } - //if (block_cnt > 1) { - // SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area."); - // SERIAL_ECHOLNPAIR("\nLargest free block is ", max_cnt); - //} - return block_cnt; -} // // M100 sub-commands @@ -114,13 +99,13 @@ uint16_t free_memory_is_corrupted(char * const ptr, const uint16_t size) { * the block. If so, it may indicate memory corruption due to a bad pointer. * Unexpected bytes are flagged in the right column. */ - void dump_free_memory(char *ptr, char *sp) { + void dump_free_memory(uint8_t *ptr, uint8_t *sp) { // // Start and end the dump on a nice 16 byte boundary // (even though the values are not 16-byte aligned). // - ptr = (char*)((uint16_t)ptr & 0xFFF0); // Align to 16-byte boundary - sp = (char*)((uint16_t)sp | 0x000F); // Align sp to the 15th byte (at or above sp) + ptr = (uint8_t *)((uint16_t)ptr & 0xFFF0); // Align to 16-byte boundary + sp = (uint8_t *)((uint16_t)sp | 0x000F); // Align sp to the 15th byte (at or above sp) // Dump command main loop while (ptr < sp) { @@ -131,14 +116,30 @@ uint16_t free_memory_is_corrupted(char * const ptr, const uint16_t size) { print_hex_byte(ptr[i]); SERIAL_CHAR(' '); } + safe_delay(25); SERIAL_CHAR('|'); // Point out non test bytes for (uint8_t i = 0; i < 16; i++) SERIAL_CHAR(ptr[i] == TEST_BYTE ? ' ' : '?'); SERIAL_EOL; ptr += 16; + safe_delay(25); idle(); } } + +void M100_dump_routine( char *title, char *start, char *end) { +unsigned char c; +int i; + +// +// Round the start and end locations to produce full lines of output +// + start = (char*) ((uint16_t) start & 0xfff0); + end = (char*) ((uint16_t) end | 0x000f); + + SERIAL_ECHOLN(title); + dump_free_memory( start, end ); +} #endif // M100_FREE_MEMORY_DUMPER /** @@ -172,7 +173,7 @@ void free_memory_pool_report(const char * const ptr, const uint16_t size) { SERIAL_ECHOPAIR("\nLargest free block is ", max_cnt); SERIAL_ECHOLNPAIR(" bytes at 0x", hex_word((uint16_t)max_addr)); } - SERIAL_ECHOLNPAIR("free_memory_is_corrupted() = ", free_memory_is_corrupted(ptr, size)); + SERIAL_ECHOLNPAIR("check_for_free_memory_corruption() = ", check_for_free_memory_corruption("M100 F ")); } #if ENABLED(M100_FREE_MEMORY_CORRUPTOR) @@ -202,13 +203,17 @@ void free_memory_pool_report(const char * const ptr, const uint16_t size) { * M100 I * Init memory for the M100 tests. (Automatically applied on the first M100.) */ -void init_free_memory(char *ptr, int16_t size) { +void init_free_memory(uint8_t *ptr, int16_t size) { SERIAL_ECHOLNPGM("Initializing free memory block.\n\n"); size -= 250; // -250 to avoid interrupt activity that's altered the stack. - if (size < 0) return; + if (size < 0) { + SERIAL_ECHOLNPGM("Unable to initialize.\n"); + return; + } - ptr += 8; + ptr += 8; // move a few bytes away from the heap just because we don't want + // to be altering memory that close to it. memset(ptr, TEST_BYTE, size); SERIAL_ECHO(size); @@ -217,8 +222,7 @@ void init_free_memory(char *ptr, int16_t size) { for (uint16_t i = 0; i < size; i++) { if (ptr[i] != TEST_BYTE) { SERIAL_ECHOPAIR("? address : 0x", hex_word((uint16_t)ptr + i)); - SERIAL_ECHOPAIR("=", hex_byte(ptr[i])); - SERIAL_EOL; SERIAL_EOL; + SERIAL_ECHOLNPAIR("=", hex_byte(ptr[i])); } } } @@ -228,9 +232,9 @@ void init_free_memory(char *ptr, int16_t size) { */ void gcode_M100() { SERIAL_ECHOPAIR("\n__brkval : 0x", hex_word((uint16_t)__brkval)); - SERIAL_ECHOPAIR("\n__bss_end : 0x", hex_word((uint16_t)&__bss_end)); + SERIAL_ECHOPAIR("\n__bss_end: 0x", hex_word((uint16_t)&__bss_end)); - char *ptr = END_OF_HEAP(), *sp = top_of_stack(); + uint8_t *ptr = END_OF_HEAP(), *sp = top_of_stack(); SERIAL_ECHOPAIR("\nstart of free space : 0x", hex_word((uint16_t)ptr)); SERIAL_ECHOLNPAIR("\nStack Pointer : 0x", hex_word((uint16_t)sp)); @@ -243,10 +247,8 @@ void gcode_M100() { } #if ENABLED(M100_FREE_MEMORY_DUMPER) - if (code_seen('D')) return dump_free_memory(ptr, sp); - #endif if (code_seen('F')) @@ -260,4 +262,67 @@ void gcode_M100() { #endif } +int check_for_free_memory_corruption(char *title) { + char *sp, *ptr; + int block_cnt = 0, i, j, n; + + SERIAL_ECHO(title); + + ptr = __brkval ? __brkval : &__bss_end; + sp = top_of_stack(); + + n = sp - ptr; + SERIAL_ECHOPAIR("\nfmc() n=", n); + SERIAL_ECHOPAIR("\n&__brkval: 0x", hex_word((uint16_t)&__brkval)); + SERIAL_ECHOPAIR("=0x", hex_word((uint16_t)__brkval)); + SERIAL_ECHOPAIR("\n__bss_end: 0x", hex_word((uint16_t)&__bss_end)); + SERIAL_ECHOPAIR(" sp=", hex_word(sp)); + + if (sp < ptr) { + SERIAL_ECHOPGM(" sp < Heap "); +// SET_INPUT_PULLUP(63); // if the developer has a switch wired up to their controller board +// safe_delay(5); // this code can be enabled to pause the display as soon as the +// while ( READ(63)) // malfunction is detected. It is currently defaulting to a switch +// idle(); // being on pin-63 which is unassigend and available on most controller +// safe_delay(20); // boards. +// while ( !READ(63)) +// idle(); + safe_delay(20); + M100_dump_routine( " Memory corruption detected with sp 8) { +// SERIAL_ECHOPAIR("Found ", j); +// SERIAL_ECHOLNPAIR(" bytes free at 0x", hex_word((uint16_t)(ptr + i))); + + i += j; + block_cnt++; + SERIAL_ECHOPAIR(" (", block_cnt); + SERIAL_ECHOPAIR(") found=", j); + SERIAL_ECHOPGM(" "); + } + } + } + SERIAL_ECHOPAIR(" block_found=", block_cnt); + + if ((block_cnt!=1) || (__brkval != 0x0000)) + SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area."); + + if ((block_cnt==0)) // Make sure the special case of no free blocks shows up as an + block_cnt = -1; // error to the calling code! + + if (block_cnt==1) { + SERIAL_ECHOPGM(" return=0\n"); // if the block_cnt is 1, nothing has broken up the free memory + return 0; // area and it is appropriate to say 'no corruption'. + } + SERIAL_ECHOPGM(" return=true\n"); + return block_cnt; + } + #endif // M100_FREE_MEMORY_WATCHER + +