lauantai 24. helmikuuta 2018

Loop forever


I ran onto an interesting bug just now. I've this entire debug module (as in C source), highly configurable, that can be used with many of my projects. Since I don't really like to dive into JTAG debuggers unless I really have to, I like that debug module a lot. Over time I've build quite a few features in it that help me to get things done.

Just now I took a very simple piece of a branch from one project and moved it to another one. Simple copy-paste of already proven code, no problems whatsoever, right?

Not so. When I loaded this program to the MCU, it just hung immediately.

Since the only changes I had made were to this single module, approximate location of problem was immediately evident - those new changes. Just about 20 copied lines of code. Should be simple.

Yeah, not so. Cursory review of changes revealed nothing. Nothing wrong here. So, eventually I went with JTAG debugger route - which in this case means openOCD and GDB. Both of these are extremely powerful tools, but highly hampered by lack of usable (or maybe I should say, easily usable) GUI or IDEs for my purposes (I have mentioned that I'm kinda old-fashioned, preferring text editors and makefiles?)  Wait, let me paraphrase that last: I haven't managed to find a GUI for them that I'd find usable enough, as there actually might be one I haven't seen or tried yet.

But anyway, after some debugging, these revealed that program had inserted it into infinite loop within this new code. I don't have the generated assembly handy here, but it essentially boiled down to:

<compare something with something>
<if not equal, jump to this same instruction again>

...what?

I readily admit, I am not familiar enough with ARM assembly to read it fluently. And I couldn't write my way out of wet paper bag [in assembly] either. No reason to, really, except these occasional cases when my expectations don't meet reality. And crashes, of course, as having pointer to offensive instruction is makes debugging so much easier.

The problem here was, the C code read something like this:

if (debug_string_array[0])
    printDebug(debug_string_array);
debug_string_array[0] = 0;

No loops here. Yet program somehow entered infinite loop anyway. So what is going on here?

I dug a bit deeper then.

printDebug(char *str)
{
#ifdef NO_SYNCHRONOUS_DEBUG
  printDebug(str);
#else
  printDebugS(str);
#endif
}

Oh, right, there's the problem. Silly copy paste issue elsewhere in the code which just happened to be realized now, and compiler helpfully optimized the recursive part out, leaving just the infinite loop.

Although irrelevant here, synchronous debug, in this context, means that debug output is sent out immediately, before returning from function, as opposed to "asynchronous" where it's buffered and sent out via serial transmit interrupt. Both of these have their uses but I won't go there in depth right now, as it isn't the topic here.

Code above isn't the actual source, mind you, but quick, cleaned-up rewrite of it.


Ei kommentteja:

Lähetä kommentti