M100 Fixes and Features

All the memory accesses need to be unsigned char in  order to avoid
false errors being detected.   Two new features are available for
developers to call into the M100 functionality to look for data
corruption.
This commit is contained in:
Roxy-3D 2017-04-13 18:07:10 -05:00 committed by Roxy-3D
parent 2f2835e622
commit 28fec61f92

View File

@ -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<Heap\n", (char *)0x1b80, 0x21ff );
}
// Scan through the range looking for the biggest block of 0xE5's we can find
for (i = 0; i < n; i++) {
if (*(ptr + i) == (char)0xe5) {
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++;
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