Generating a string with call stack?

I know that Photon is running FreeRTOS under the hood, and for certain compilation configurations it will generate the string information to print out the call stack for debug purposes.

Has anyone tried to gain access to that information from the Photon environment?

I am getting some rogue log messages and it would be nice to know how it’s getting there.

What system version and what are these rogue messages?
How does your LogHandler definition look?

The rogue messages were coming from my code, I just wasn't sure what the call stack looked like, because some unexpected things were getting skipped (which was the bug). I added more .trace logs and got what I needed, but it would be nice to know if there's a way to glean the call stack somehow. It would make a REALLY handy tool if it's hiding out somewhere.

I’m pretty sure normal binaries built with any of the cloud compilers have optimization turned on too high and all of the symbols are stripped so save code space, so it’s not practical to get a usable call stack.

When you’re running debug builds and using the GDB debugger you can see full call stacks, but that’s because it has access to the the full, non-stripped elf files. User code does not.

2 Likes

Hi @HEng

Don’t laugh but I have faked this out by adding debug messages to the start and end every function in question.That way I get a “stack trace” in the debug log or serial port output for my code at least. This can be hard if there are multiple early returns from a function, but that is not a best practice.

As @rickkas7 said, you need to build locally using gcc so that you have the symbol table and full ELF files in order to debug some complicated things.

2 Likes

The solution to that is to create a small class that logs in the constructor and destructor, then stack allocate an instance of the class in the function. It will automatically log the exit regardless of how many returns there are.


class DebugTrace {
public:
	inline DebugTrace(const char *name) : name(name) {
		Log.info("entering %s", name.c_str());
	}
	inline ~DebugTrace() {
		Log.info("exiting %s", name.c_str());
	}

protected:
	String name;
};

void myFunction() {
	DebugTrace debugTrace("myFunction");
	
	Log.info("do something here");
}

The log output would look something like:

entering myFunction
do something here
exiting myFunction
2 Likes

There actually is a compiler directive __FUNCTION__ to save you the hassle of having to provide the function name (e.g. myFunction) manually.

2 Likes

True! I forgot about that! But you still need to pass it as a parameter, not put it in the DebugTrace constructor.

1 Like

I have something similar, although it's pretty lazy around code that works and quite dense in code that doesn't (or didn't at one time)!

Thanks @rickkas7 and @ScruffR those are great tricks : )

May I suggest then something like:

#define   __MY_STACKTRACE DebugTrace debugTrace("__FUNCTION__");

that you can slap into every function

Off topic, but I’d like to thank whoever set it up so that when you disable SerialLogHandler, the compiler automatically knows to dump all of the logging strings and overhead!

(that saved me a lot of work by not having to scrub those for production release)

We use the constructor/destructor trick a lot at my work in the test harness to log tests starting and finishing and when finished to clean up after themselves. It’s a good trick worth knowing!

I love this approach! I tried it but when the destructor executes the name is no longer there? Instead of ‘myFunction’ supposedly stored in the private field ‘name’, I’ll get what looks like a pointer… IE: ‘8i’