Determining Cause of Spark Core Crash

So at the moment I have an issue in my code which is causing the Spark core to crash(Flashing red light of death). However I really do not know exactly what is causing this crash. I speculate it could be a memory leak issue but have no way of knowing.

I have determined exactly which line in my code causes the crash but still not sure why its happening. The line that crashes the module is calling a method in an external class which returns a String. The full method in the external class runs and gets all the way to the return in that method(Serial print runs) but the next print right after the method call in the calling class does not run. Here is sort of an example:

Class A:
String s = B.doSomethingCool
Serial.println("got here in class A")

Class B:
String B::doSomethingCool(){
Serial.println("All Done")
return "you got it";
}

In this case All Done would print to the log but got here in class A does not. The full function in the other class runs but as soon as the return gets back to Class A it breaks and I do not know why.

Is there some way to determine the cause of the crash?

Thanks,
Travis

@IOTrav, you really need to use the search function :stuck_out_tongue: Look here:

2 Likes

Hi @IOTrav

I am really surprised the compiler lets you write return "you got it"; since the return type is String and you are returning a pointer to a const char array in flash.

Try changing it to

return String("you got it");

So you explicitly call the String constructor which will copy your const char array into RAM.

That said, returning String objects should be done with care and forethought to avoid memory problems.

Hi @peekay123 and @bko

Sorry I way over simplified my example. I do create an actual String object in the class and then return the String so it is more like:

B::doSomethingCool(){
String b = "you got it"
return b
}

I guess my real question here is, is there a way to determine the cause of a crash on the Spark Core through some type of debugging utility/hardware?

I have been very careful to never use new to create variables or alloc memory ever so all my memory usage in ram should always be recycled correct? This feels like an exhaustion of memory but I cannot see how.

Thanks

Dont I feel like an idiot:

http://docs.spark.io/troubleshooting/#troubleshoot-by-color-flashing-red

2 Likes

Is there a way to determine the amount of free ram in code? I saw a post on reading amount of flash available but not RAM. I need to print the amount of RAM available out the Serial port in my code for debugging purposes.

Thanks

Hi @bko and @peekay123,

I have looked over my code for a few days now and I know exactly which line in the code causes the failure. However I do not know why. The line that causes the failure is a call on a method from another class from my application class. The application calls this method which returns a String. I did prints in the method in the other class and it gets to the return line in the method, however as soon as it returns the constructed string to the Application class I get a Hard Fault SOS flash on the Spark LED.

Now to add to this complexity this method actually runs without failure in another context.

So what would cause a Hard Fault when a String is returned from one class to another? That is for sure the cause of the failure.

So a hard fault is generally caused by following a bad pointer to something and trying to do something to it.

How are you using the return value in the application class?

Can you show us your actual code or a boiled down but compilable test case? It is really hard to figure out from the snippets.

1 Like

Thank you @bko

So here is a boiled down look. I really cant post everything because it is pretty big.

Call from Application class:

String serverResponse = aesClient.cbcDecrypt(response.body, response.responseLength); 

Method in aesClient class:

String aes_client::cbcDecrypt(String message, size_t len){
	//Base 64 Decode data
	const char *messageChars = message.c_str();
	int length = len/4 *3;
	if (messageChars[length - 1] == '=') (length)--;
	if (messageChars[length - 2] == '=') (length)--;

	unsigned char messageBuf[length];
	size_t* decodeLength;
	base64_decode(message.c_str(), message.length(), decodeLength, messageBuf);

	//Copy Base 64 decoded data into buffer for decryption
	unsigned char data[length];
	memset(data, 0, sizeof data);
	memcpy(data, messageBuf, length);

	//get round key for decryption
	memcpy(oIV, nIV, 16);

	//Create output buffer for decryption.
	unsigned char oBuf[length];

	//Make sure buffers are empty
	memset(oBuf, 0, length);

//	Serial.print("Decrypt IV oIV: ");
//	hexPrint(oIV, 16);
//
//	Serial.print("Decrypt IV nIV: ");
//	hexPrint(nIV, 16);

	//Decrypt message into output buffer
	aes_setkey_dec(&mAES, mKey, 128);
	aes_crypt_cbc(&mAES, AES_DECRYPT, length, oIV, data, oBuf);

	//Store first 16 decrypted characters as round key for next cbc encrypted send
	memcpy(nIV, oBuf, 16);

	if(validatePacket(oBuf)){
		//Get checkin interval from packet
		checkInInterval = ((oBuf[devIDLen+16] *256)+oBuf[devIDLen+17])*1000;

		//Get message out of packet
		char messageArray[(sizeof oBuf - (devIDLen+18))+1];
		//Make sure messageArray is empty
		memset(messageArray, 0, sizeof messageArray);

		for(int i = 0; i < sizeof messageArray; i++){
			messageArray[i] = oBuf[i+18+devIDLen];
		}
		String message(messageArray);
		//Extrapolate message from received data.  All messages are appended with ~ so trim off information past that symbol
		int end = message.indexOf("~");
		String trimmed = message.substring(0,end);
		Serial.print("Length of return String after Decrypt: ");
		Serial.println(trimmed.length());
		delay(100);
		return trimmed;

	}else{

		//Get message out of packet
		char messageArray[(sizeof oBuf - (devIDLen+18))+1];
		//Make sure messageArray is empty
		memset(messageArray, 0, sizeof messageArray);

		for(int i = 0; i < sizeof messageArray; i++){
			messageArray[i] = oBuf[i+18+devIDLen];
		}
		String message(messageArray);
		//Extrapolate message from received data.  All messages are appended with ~ so trim off information past that symbol
		int end = message.indexOf("~");
		String trimmed = message.substring(0,end);
		Serial.println(trimmed);

		return "Fail";
	}
}

Note that Length of return string after Decrypt print runs just before return in method so it does not break until the String is passed back to the Application class. If you need more information please let me know, I will be happy to post.

@IOTrav, would it not be better to toss around pointers to Strings instead of Strings themselves? That would possibly take the strain off the heap. It also seems odd that your code uses char[] type strings to then convert them to String types for return. IMO, keeping everything is the string array format and passing pointers is faster and uses less memory. :smiley:

Can you say for sure that when you call String message(messageArray); that the char array messageArray is properly zero teminated? I am not seeing it.

If you don't have a proper zero at the end of that char array, the String constructor will read memory until finds a zero, which could be quite a large amount of data.

Hey @bko

Here is my latest. I memset message array to all 0s and then populate the array. It is for sure null terminated. Take a look:

String aes_client::cbcDecrypt(String message, size_t len){
	//Base 64 Decode data
	const char *messageChars = message.c_str();
	int length = len/4 *3;
	if (messageChars[length - 1] == '=') (length)--;
	if (messageChars[length - 2] == '=') (length)--;

	unsigned char messageBuf[length];
	size_t* decodeLength;
	base64_decode(message.c_str(), message.length(), decodeLength, messageBuf);

	//Copy Base 64 decoded data into buffer for decryption
	unsigned char data[length];
	memset(data, 0, sizeof data);
	memcpy(data, messageBuf, length);

	//get round key for decryption
	memcpy(oIV, nIV, 16);

	//Create output buffer for decryption.
	unsigned char oBuf[length];

	//Make sure buffers are empty
	memset(oBuf, 0, length);

//	Serial.print("Decrypt IV oIV: ");
//	hexPrint(oIV, 16);
//
//	Serial.print("Decrypt IV nIV: ");
//	hexPrint(nIV, 16);

	//Decrypt message into output buffer
	aes_setkey_dec(&mAES, mKey, 128);
	aes_crypt_cbc(&mAES, AES_DECRYPT, length, oIV, data, oBuf);

	//Store first 16 decrypted characters as round key for next cbc encrypted send
	memcpy(nIV, oBuf, 16);

	if(validatePacket(oBuf)){
		//Get checkin interval from packet
		checkInInterval = ((oBuf[devIDLen+16] *256)+oBuf[devIDLen+17])*1000;

		//Get message out of packet
		char messageArray[(sizeof oBuf - (devIDLen+18))+1];
		//Make sure messageArray is empty
		memset(messageArray, 0, sizeof messageArray);

		for(int i = 0; i < (sizeof oBuf - (devIDLen+18)); i++){
			messageArray[i] = oBuf[i+18+devIDLen];
		}
		String message(messageArray);
		//Extrapolate message from received data.  All messages are appended with ~ so trim off information past that symbol
		int end = message.indexOf("~");
		String trimmed = message.substring(0,end);
		Serial.print("Length of return String after Decrypt: ");
		Serial.println(trimmed.length());
		delay(100);
		Serial.print("Decrypted: ");
		Serial.println(trimmed);
		delay(100);
		return trimmed;

	}else{

		//Get message out of packet
		char messageArray[(sizeof oBuf - (devIDLen+18))+1];
		//Make sure messageArray is empty
		memset(messageArray, 0, sizeof messageArray);

		for(int i = 0; i < sizeof messageArray; i++){
			messageArray[i] = oBuf[i+18+devIDLen];
		}
		String message(messageArray);
		//Extrapolate message from received data.  All messages are appended with ~ so trim off information past that symbol
		int end = message.indexOf("~");
		String trimmed = message.substring(0,end);
		Serial.println(trimmed);

		return "Fail";
	}
}

I’ve edited your post to properly format the code. Please check out this post, so you know how to do this yourself in the future. Thanks in advance! ~Jordy

HI @peekay123

Could you post what my decrypt method should look like as far as passed variables and returns? Would you just pass a buffer to the function and then read that buffer in the calling class? Short code snippet of what you recommend?

Thanks

Morning,

Not sure if it’s occurring in this case, but if validatePacket returns false then you are returning the C string "Fail"
rather than a String object. Someone more knowledgeable than me will have to comment on whether the
Serial.println will have time to get the message to you before the crash, but perhaps put a delay in as you do
above to check this case is not happening,

cheers

Richard

1 Like