teaching debugging

Sometimes when I read a request for help on here, and other forums, I'm often struck by the apparent lack of debugging skills of the applicant, implicit in the wording of their plea. Things like "I'm out of ideas as to what might be wrong" or similar.

There are a lot of resources out there on how to code available on the 'net and in bookstores, but far fewer on how to systematically debug that code.

I don't see any reason why debugging skills can't be taught, yet it seems they rarely are. The basic principles are not difficult, at least I wouldn't have thought so, yet my observation is that they don't necessarily come automatically to people, sometimes even to those who are quite bright and otherwise show considerable aptitude for programming.

The saying about "give a man a fish..." comes to mind. Perhaps we could put together some "how to fish" resources tailored for Arduino users? (I know Nick Gammon was preparing some "Introduction to Arduno programming" guides a while back, and I did suggest something along these lines to him back then, but not sure if anything came of it.)

Thoughts?

It might help to introduce the topic in a non-coding context; start with some situations from daily life where you would use debugging skills. One example I've used here before is - what do you do when a light doesn't come on in your house?

Or what do you do when your car won't start?

Once you show you audience that they actually know perfectly well what the debugging process is, you can introduce the tools (such as they are) that would be used in an arduino context.

Then perhaps, do a couple of worked examples showing the process that was used to debug a real piece of code. I recall finding a piece on C++ design really useful where, rather than showing some classes that appeared out of the air, the author showed all his attempts and backtracks that produced the final pristine design. "What I did to debug this, warts and all" might be more useful than a beautiful analytic track straight to the solution.

I wouldn't be inclined to spend much time on such a thing though - there are many posters that make it clear that they didn't read Nick's existing sticky so adding more stuff that will be ignored isn't a very appealing prospect.

In my experience, it's difficult to teach/learn "testing", much less "debugging."
Since debugging is essential "test in a way that enables you to figure out WHAT is wrong", this is a problem :frowning:
(although people who are good at debugging are not necessarily good at testing. Sigh.)

The concept of debugging can be taught, but the act of debugging is pretty much going to be a learned skill. People can't really debug their code unless they understand it completely. Then they can find problems with the code by using their knowledge of how it should be working.

I programmed for Microsoft for 15 years or so, and after a while, I got to the point where I could see an error or something happen unexpected in the program and instantly know what was wrong with the code before even looking back at the code. It's just something that is learned over time.

It can be taught, but there is a time when people just need to have experience.

And you have to look. And try. Or you get bugs like apple's recent embarassment:

LOFL :stuck_out_tongue_closed_eyes:

westfw:
And you have to look. And try. Or you get bugs like apple's recent embarassment:

Yeah that Apple bug is a classic :slight_smile:


Rob

westfw:
Since debugging is essential "test in a way that enables you to figure out WHAT is wrong", this is a problem :frowning:

Interesting to bring up the relationship between testing and debugging, although they are quite different things.

The purpose of a test is to evaluate the current condition of something, whereas debugging is a process to understand and fix something that isn't working as expected.

Having said that, systematic debugging typically involves a series of tests, where the outcome of each tests guides the next step in the process.

But this leads to another distinction: Systematic debugging vs non-systematic debugging.

Both systematic and non-systematic debugging can be successful. Indeed, CSGuy's "inspiration" debugging is an example of non-systematic debugging. It's entirely reliant on an "aha!" insight, and as such, there is no process or structure, and so there is nothing that can be taught there.

Many posters who appeal for help with their programs I suspect are also non-systematic debuggers, as they will often say things like "I am completely out of ideas -- please help guys", suggesting that an idea or inspiration as to what the problem might be is necessary to continue.

Sound systematic debugging methods, OTOH, do not rely on "new ideas" or "insight" proceed, as they generally progress by elimination through test and verification (or not) of the debugger's assumptions. The systematic approaches when formalised resemble algorithms. If applied diligently, they will (almost) always succeed, although perhaps converge slowly as a process. Less inspiration and more perspiration, as Edison might have said.

So to clarify, my suggestion is that systematic forms of debugging might be something useful to try to teach. Further, I suspect trying to teach any of the various non-systematic approaches wouldn't be very useful, as a) they are probably self-evident anyway, and b) they are usually what have already been exhausted by the time a poster asks for help. (Although I have my suspicions that some posters haven't applied any debugging method -- they wrote the code, compiled it, and found it didn't work as expected, and then were at a complete loss as to how to proceed.)

In conjunction with the "process" side of systematic debugging, specific tips and techniques particularly suitable for the Arduino environment could be discussed. Also, the more esoteric aspects of the subject like dealing with so-called "Heisenbugs" might be worth some discussion.

Anyway, just putting it out there (hence Bar Sport!)

wildbill:
I wouldn't be inclined to spend much time on such a thing though - there are many posters that make it clear that they didn't read Nick's existing sticky so adding more stuff that will be ignored isn't a very appealing prospect.

Fair comment too. :frowning:

You have to be able to read the code line by line and be able to follow how it (should) executes.
You need to know about conditional branches and loops, variables, arrays, structs and pointers and C++ Classes at some point going from just started to confident and able to add print statements.

And how to get there?

There's the foundations page for raw beginners to learn some terms and concepts:

and then they go through the examples (in the the IDE) but not in 1-2-3 order.
More like section 5 first then 1 then 2 then 3.. and then find a good tutorial on C string arrays/string.h

Arduino string.h library source is in your IDE source code.. A function reference is here:
http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html

that you get to from the main library page:
http://www.nongnu.org/avr-libc/user-manual/modules.html

If you got this far then you have probably done some debugging already.

pico:
Thoughts?

How about instead of
" publish your full code... put your code in quotation... read blink without delay ...format your code...

FAQ forum

Here is a start

  1. if(a=1)
    digitalWrite (pin1 ,a);
    Assigment (=) versus evaluation (==)

  2. if(a != 1 );
    digitalWrite(pin1,HIGH);

if(condition) statement;
Missing statement

  1. XYZ myClass;

compiler error XYZ does not name type

The compiler does not recognize variable type XYZ ( int, char etc. ) Most likely missing library.

  1. if(a ==1)
    digitalWrite(pin1,a);
    else if( a == 0)
    digitalWrite(pin1,a);

if it is not true , it must be false - it is digital

if(a) // if true
digitalWrite(pin1,HIGH); // LED ON
else
digitalWrite(pin1,LOW); // LED OFF

Sometimes you can get a little preemptive debug training in by telling someone to break that big (usually first) project down into pieces and add a piece at a time because it's generally easier to debug that way.

Vaclav:
4. if(a ==1)
digitalWrite(pin1,a);
else if( a == 0)
digitalWrite(pin1,a);

if it is not true , it must be false - it is digital

Well, not always..

if (a == 1)
    digitalWrite(pin1, a);
else if (a == 0)
    digitalWrite(pin1, a);
else
    Serial.println("Uh oh!  Invalid state. (a = %u)", a);

Now, usually, your perspective is correct and what the OP intended with the conditional statement. However, this it the kind of ambiguity I like to point out, because the code isn't actually invalid and might not be an error at all. The OP would need to either clarify that a boolean condition isn't adequate, and maybe why (if it's not obvious), or they should've just written this:

digitalWrite(pin1, a);  // Why test the value at all?

I like to show people when they're doing something that gives the correct result, but for the wrong reasons (or just bad form.) I find I've learned a lot by similar lessons and like to pass that on. So far, I've gotten a lot of "OH, I didn't know that!" replies, so hopefully it's helping.

westfw:
And you have to look. And try. Or you get bugs like apple's recent embarassment:

Things like this are good exercises for newbies. (And the rest of us for that matter.) When presented in a forum like this, it becomes a kind of game to figure out the bug. For example, at least taken out of context, I see a few potential flaws in this...

  1. Several variables aren't defined. Now, they're probably defined in the "..." but then again maybe they're not. After all, we see one variable defined, then a snip, then code. Attention to detail would reveal that hashCtx, serverRandom, hashOut, and signedHashes weren't parameters and weren't defined. Are they global? Obviously the code wouldn't even compile if they weren't defined at all, making it unlikely this bug would make it into the wild, unless distributed as source rather than a compiled executable. But a good developer should be asking him/herself these questions regardless.

  2. What's UInt16? (I could be showing my ignorance here.) How's that different from the actual C99 type uint16_t? Was that a typo? (Again, it would fail to compile if it were actually mistake.)

  3. While not necessarily indicative of a problem, every developer should be suspicious of those pointers, custom types lacking pointers, and address-of operators. It's such a common source of errors that it should be reflex to examine them, even if compiler warnings are turned on and "should" complain if they don't match up.

  4. Obviously (and the point of this snippet, I'm sure) is the fact that there are two "goto fail;" statements after a conditional. The second is indented like it's supposed to be executed after the second conditional fails, but without block braces, the compiler will treat it as a statement that will be executed as a matter of course after passing the first two conditionals.

That's the Apple bug eh? A classic example of "reading the indentation" and a big goto fail for the coder :slight_smile:

Interesting that they use uint8_t but then UInt16. Maybe it makes sense in context.


Rob

I come with a hardware perspective background with many past years as a field engineer troubleshooting mini-computer systems. Peoples brains are wired differently and troubleshooting comes easier for some then others, and I came to it rather naturally like a fish to water, but did continue to improve with experience.

I tried always to approached a problem with taking time and first trying to accurately define the symptom(s) I'm seeing as there are often are more then one symptom and some are more fundamental or important then others. If a system uses a series of stages there is often time saved by 'half-splitting' the problem by seeing if the circuit is correct half way in and then working backwards or forwards from there. If there are many random symptoms then I would always suspect power or other basic infrastructure items. Often I would be forced to put myself into a zen like state and 'become the machine' :D. What could cause me to exhibit these symptoms and how can I prove or disprove that a possible failure is causing the symptom I'm seeing? Often using a mental process of elimination would be useful in reaching results quicker.

Anyway in many decades of working I never came across a good reference to teaching troubleshoot skills and success. And while I'm pretty much a self taught programmer and don't consider myself good at programming I have seen that I'm good a debugging my code once I do get it to compile but not work correctly.

And yea, I too think that testing and debugging are two very different activities.

Testing is a activity that is trying to prove if something (hardware or software) is working properly or not.
Where as debugging (troubleshooting) is an activity of trying to fix something that you know is not working correctly. I would think that comprehensive testing is the more difficult of those two activities as more things have to be consider and you can eliminate less initially.

SirNickity:

Vaclav:
4. if(a ==1)
digitalWrite(pin1,a);
else if( a == 0)
digitalWrite(pin1,a);

if it is not true , it must be false - it is digital

Well, not always..

if (a == 1)

digitalWrite(pin1, a);
else if (a == 0)
    digitalWrite(pin1, a);
else
    Serial.println("Uh oh!  Invalid state. (a = %u)", a);




Now, usually, your perspective is correct and what the OP intended with the conditional statement. However, this it the kind of ambiguity I like to point out, because the code isn't actually invalid and might not be an error at all. The OP would need to either clarify that a boolean condition isn't adequate, and maybe why (if it's not obvious), or they should've just written this:



digitalWrite(pin1, a);  // Why test the value at all?




I like to show people when they're doing something that gives the correct result, but for the wrong reasons (or just bad form.) I find I've learned a lot by similar lessons and like to pass that on. So far, I've gotten a lot of "OH, I didn't know that!" replies, so hopefully it's helping.

My comments were just to show an example what could be done on this site to improve / teach debugging skils.
To expand of this particular example - I recall a discussion where a statement was made to the effect that Arduino complier treats digital(Write) as an analog but with a value of 1 for HIGH. It is not a big deal how complier works on it, but if it is digitalWrite the value range should be 0 and 1 , hence checking for invalid value outside this range is either silly or shows lack of understanding what is digital.
Maybe it was a bad example since majority of codes posted here use anything but a single bit values anyway.

PS I think the Apple bug example should be put on display for using "peculiar" ( I did not say stupid!) variable / function naming method.

BTW using just the opposite - single letters for naming - should also be part of teachnig debuging.

Vaclav:
My comments were just to show an example what could be done on this site to improve / teach debugging skils.

Oh yeah, I absolutely figured that. I just figured I would jump on the opportunity to make conversation about how simple things sometimes aren't.

Vaclav:
To expand of this particular example - I recall a discussion where a statement was made to the effect that Arduino complier treats digital(Write) as an analog but with a value of 1 for HIGH. It is not a big deal how complier works on it, but if it is digitalWrite the value range should be 0 and 1 , hence checking for invalid value outside this range is either silly or shows lack of understanding what is digital.

Not quite sure what you're saying here. In case this is relevant, I do think it's sometimes advantageous to validate a "boolean" value. What if something in your code set a == 2? It might still technically work, but it could be a sign that some previous code is doing something wrong. I specifically remember writing a check like that recently and thinking, "aw, this is dumb... that will never happen." And it did, the very first time out of the gate after compiling my changes. That was humbling.... :cold_sweat: But it did end up pointing out a bug in a quick and obvious way.

Vaclav:
PS I think the Apple bug example should be put on display for using "peculiar" ( I did not say stupid!) variable / function naming method.

How's that? It actually looks very similar to how I name my functions, except I usually prefer something like:

ssl_pretend_to_validate()

... rather than...

 SSLPretendToValidate()

Mostly because of how often the capitalized letters blend together to form unintended words or abbreviations.

Vaclav:
I recall a discussion where a statement was made to the effect that Arduino complier treats digital(Write) as an analog but with a value of 1 for HIGH.

I have to wonder what AVR opcodes the compiler would use to treat a digital write as an analog write?

The hardware can do either with PWM capable pins and what if my code specifies a digital write but the pin number is in a variable? Does the compiler write split code that treats PWM pins differently from non-PWM pins? I don't think so!

GoForSmoke:

Vaclav:
I recall a discussion where a statement was made to the effect that Arduino complier treats digital(Write) as an analog but with a value of 1 for HIGH.

I have to wonder what AVR opcodes the compiler would use to treat a digital write as an analog write?

The hardware can do either with PWM capable pins and what if my code specifies a digital write but the pin number is in a variable? Does the compiler write split code that treats PWM pins differently from non-PWM pins? I don't think so!

This is more a function of the software in the analogWrite() function rather then the built in hardware of the AVR chip or the compiler decisions. If one attempts to use a analogWrite() on a non-pwm hardware pin then values sent from 0-127 will generate a LOW and values of 128-255 will generate a HIGH.