PDA

View Full Version : Somthing is clobbering two stack locations?



Bill Greiman
11-08-2012, 06:05 PM
It appears that something in an isr is clobbering two locations near the beginning of the stack. Here is a sketch that demonstrates the possible problem. This is for beta7.


extern unsigned long _ebss;
extern unsigned long _estack;
#include <unistd.h>

void setup() {
// fill start of stack
uint32_t* stack = (uint32_t*)sbrk(0);
for (size_t i = 0; i < 1000; i++) stack[i] = 0X55555555;

// see if all is well
bool check1 = true;
for (size_t i = 0; i < 1000; i++) {
if (stack[i] != 0X55555555) check1 = false;
}
// wait for interrupts
delay(1000);
// check again
bool check2 = true;
for (size_t i = 0; i < 1000; i++) {
if (stack[i] != 0X55555555) check2 = false;
}
Serial.begin(9600);
while(!Serial) {}
delay(2000);
Serial.print("check1 ");
Serial.println(check1, DEC);
Serial.print("check2 ");
Serial.println(check2, DEC);
// print clobbered stack locations
for (size_t i = 0; i < 1000; i++) {
if (stack[i] == 0X55555555) continue;
Serial.print((int)&stack[i], HEX);
Serial.print(' ');
Serial.println(stack[i], HEX);
}
Serial.print("&_ebss ");
Serial.println((int)&_ebss, HEX);
Serial.print("sbrk(0) ");
Serial.println((int)sbrk(0), HEX);
Serial.print("stack ");
Serial.println((int)stack, HEX);
}
void loop() {}

This is typical output from the sketch:


check1 1
check2 0
1FFFEC50 2580
1FFFEC54 1080000
&_ebss 1FFFEC2C
sbrk(0) 1FFFEC2C
stack 1FFFEC2C

The sketch fills the beginning of the stack with a pattern of 0X55555555. It then checks to see if all is well and saves the result in check1.

The sketch then delays for a second, rechecks the stack and stores the result in check2.

The result is that two locations are overwritten after the delay. These locations seem to always have the same values.

This is far from the part of the stack that should be used.

JBeale
11-08-2012, 06:51 PM
I added some code to show the elapsed time in milliseconds, and the stack index [i] where the value is written.

extern unsigned long _ebss;
extern unsigned long _estack;
#include <unistd.h>

#define TMAX 2000 // how many milliseconds to test for stack change
byte dflag[500];

void setup() {
int j, ltime;
ltime = 0;

// fill start of stack
uint32_t* stack = (uint32_t*)sbrk(0);
for (size_t i = 0; i < 1000; i++) stack[i] = 0X55555555;

// see if all is well
bool check1 = true;
for (size_t i = 0; i < 1000; i++) {
if (stack[i] != 0X55555555) check1 = false;
}
bool check2 = true;
// wait for interrupts
for (j=0;j<TMAX;j++) {
delay(1);
// check again
for (size_t i = 0; i < 1000; i++) {
if (stack[i] != 0X55555555) {
check2 = false;
ltime = j;
j = TMAX; // force outer loop quit
}
}
}
Serial.begin(9600);
while(!Serial) {}
delay(2000);
Serial.print("check1 ");
Serial.println(check1, DEC);
Serial.print("check2 ");
Serial.println(check2, DEC);
Serial.print("msecs: ");
Serial.println(ltime, DEC);

// print clobbered stack locations
for (size_t i = 0; i < 1000; i++) {
if (stack[i] == 0X55555555) continue;
Serial.print((int)i);
Serial.print(": ");
Serial.print((int)&stack[i], HEX);
Serial.print(' ');
Serial.println(stack[i], HEX);
}
Serial.print("&_ebss ");
Serial.println((int)&_ebss, HEX);
Serial.print("sbrk(0) ");
Serial.println((int)sbrk(0), HEX);
Serial.print("stack ");
Serial.println((int)stack, HEX);
}

void loop() {}

Example results. Stack values are constant but "msecs" varies: 434, 613, 701, 361, etc.

check1 1
check2 0
msecs: 434
9: 1FFFEC50 1C200
10: 1FFFEC54 1080000
&_ebss 1FFFEC2C
sbrk(0) 1FFFEC2C
stack 1FFFEC2C



Also interesting: running at 24 MHz, "msecs" reported value is about half, and running at 96 MHz it is about double, the numbers I get at 48 MHz (shown above).

Bill Greiman
11-08-2012, 08:32 PM
Sorry I should have posted this in the bugs section.

Looks more serious than I first realized. The problem is near the start of the heap so String, SD.h, and anything else that uses malloc() will fail.

This sketch


void setup() {
uint32_t* heap = (uint32_t*)malloc(400);
for (size_t i = 0; i < 100; i++) heap[i] = 0X55555555;
Serial.begin(9600);
while (!Serial) {}
delay(2000);
for (size_t i = 0; i < 100; i++) {
if (heap[i] == 0X55555555) continue;
Serial.print((int)&heap[i], HEX);
Serial.print(' ');
Serial.println(heap[i], HEX);
}
}
void loop() {}

Prints this


1FFFF094 2580
1FFFF098 1080000

JBeale
11-08-2012, 09:11 PM
Note: only one byte in one heap location gets clobbered instead of two uint32 words, if you use Serial1 (UART) instead of Serial (USB):


check1 1
check2 0
msecs: 325
10: 1FFFEC54 555555
&_ebss 1FFFEC2C
sbrk(0) 1FFFEC2C
stack 1FFFEC2C



extern unsigned long _ebss;
extern unsigned long _estack;
#include <unistd.h>

#define TMAX 2000 // how many milliseconds to wait for stack to change
byte dflag[500];

void setup() {
int j, ltime;
ltime = 0;

// fill start of stack
uint32_t* stack = (uint32_t*)sbrk(0);
for (size_t i = 0; i < 1000; i++) stack[i] = 0X55555555;

// see if all is well
bool check1 = true;
for (size_t i = 0; i < 1000; i++) {
if (stack[i] != 0X55555555) check1 = false;
}
bool check2 = true;
// wait for interrupts
for (j=0;j<TMAX;j++) {
delay(1);
// check again
for (size_t i = 0; i < 1000; i++) {
if (stack[i] != 0X55555555) {
check2 = false;
ltime = j;
j = TMAX; // force outer loop quit
}
}
}
Serial1.begin(9600);
// while(!Serial) {}
delay(2000);
Serial1.print("check1 ");
Serial1.println(check1, DEC);
Serial1.print("check2 ");
Serial1.println(check2, DEC);
Serial1.print("msecs: ");
Serial1.println(ltime, DEC);

// print clobbered stack locations
for (size_t i = 0; i < 1000; i++) {
if (stack[i] == 0X55555555) continue;
Serial1.print((int)i);
Serial1.print(": ");
Serial1.print((int)&stack[i], HEX);
Serial1.print(' ');
Serial1.println(stack[i], HEX);
}
Serial1.print("&_ebss ");
Serial1.println((int)&_ebss, HEX);
Serial1.print("sbrk(0) ");
Serial1.println((int)sbrk(0), HEX);
Serial1.print("stack ");
Serial1.println((int)stack, HEX);
}

void loop() {}

JBeale
11-08-2012, 09:19 PM
Aha! And if you disconnect USB entirely, running from separate power to the "Vin" pin, the problem goes away.


check1 1
check2 1
msecs: 0
&_ebss 1FFFEC2C
sbrk(0) 1FFFEC2C
stack 1FFFEC2C


24

Paul
11-08-2012, 10:10 PM
Yikes. I can't look at this until tomorrow, but it sounds pretty serious. Will investigate as soon as possible.

Bill Greiman
11-08-2012, 10:23 PM
JBeale,

Great work! Looks like this bug is going to be squashed.

I would like to suggest that the Teensy 3.0 ResetHandler should initialize the area from _ebss to _estack with the 0X55 pattern in every byte.

A pattern like this in unused memory helps find bugs and simple programs can analyze stack/heap usage.

Bill Greiman
11-11-2012, 05:23 PM
This bug is caused by the linker script http://forum.pjrc.com/threads/219-Bug-Teensy-3-0-linker-script-causes-bss-to-overlap-heap.