Software debugging

Hey

Is it some how possible to debug the code line by line, in other words if my Arduino code crash for some reason, is it possible to debug it and find out which part of the code it was when it crashed ?

I've used watchdog interrupt before and could it be possible to for example Serial.print something like function, line numbers etc. where the program was when watchdog interrupt was executed ?

//Subroutine for Watchdog Timer
void watchdogSetup(void)
{
	cli();  //Disables All Interrupts

	//Watchdog Configuration mode:
	WDTCSR |= (1<<WDCE) | (1<<WDE);
	
	//Watchdog settings:
	WDTCSR = (1<<WDIE) | (1<<WDE) | (0<<WDP3) | (1<<WDP2) | (0<<WDP1) | (1<<WDP0);  //500ms
	
	sei();  //Enables All Interrupts
}

//Watchdog Timer Interrupt
ISR(WDT_vect)
{
	//do something when watchdog reboots

}

TommiP

Well, your function disables interrupts, so that will hamper Serial.print
You could turn an LED on to show code activity.

CrossRoads:
Well, your function disables interrupts, so that will hamper Serial.print
You could turn an LED on to show code activity.

Well, if we forget that watchdog interrupt example I just wrote and all mistakes I wrote in it, is then possible some how with correctly coded watchdog interrupt example?

TommiP

could it be possible to for example Serial.print something like function, line numbers etc. where the program was when watchdog interrupt was executed ?

Programs on a PC can be compiled in release mode or in debug mode. There are extra files generated in debug mode that contain things like line of code, line number, etc. There are far less compiler optimizations done, too.

The Arduino doesn't have a way to use the debug stuff, so there is only the release mode compilation option. Function names don't exist. Line numbers don't exist. The things that you want to see just are not available to be seen.

Well, your function disables interrupts, so that will hamper Serial.print

That is not actually true, any more. Starting in 1.5.6, the HardwareSerial class now calls the interrupt handler that would be called, if the output buffer is full, as often as needed to make room for the new data. So, Serial.print() in an ISR no longer results in a hung program when the output buffer gets full.

PaulS:
Programs on a PC can be compiled in release mode or in debug mode. There are extra files generated in debug mode that contain things like line of code, line number, etc. There are far less compiler optimizations done, too.

The Arduino doesn't have a way to use the debug stuff, so there is only the release mode compilation option. Function names don't exist. Line numbers don't exist. The things that you want to see just are not available to be seen.

And it doesn't make any difference if I'm using Atmel Studio for programming Arduino ?

TommiP

How does this work and can it be used like I described in my first post ?

#define __ASSERT_USE_STDERR 
#include <assert.h>
 
int led = 13;
 
void setup() // the setup routine runs once when you press reset:
{                
    pinMode(led, OUTPUT);  // initialize the digital pin as an output.
    Serial.begin(115200);  // initialize serial communication at 9600 bits per second.
}
 
void loop() // the loop routine runs over and over again forever:
{
    for (uint8_t i = 0; i < 3; i++) 
    {
        digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
        delay(1000);               // wait for a second
        digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
        delay(1000);               // wait for a second
    } 
    assert(false); // make assertion failed.
}
 
// handle diagnostic informations given by assertion and abort program execution:
void __assert(const char *__func, const char *__file, int __lineno, const char *__sexp) 
{
    // transmit diagnostic informations through serial link. 
    Serial.println(__func);
    //Serial.println(__file);
    Serial.println(__lineno, DEC);
    Serial.println(__sexp);
    Serial.flush();
    // abort program execution.
    abort();
}

It will return at least line number..

TommiP

PaulS:
So, Serial.print() in an ISR no longer results in a hung program when the output buffer gets full.

Is this considered to be progress ?

Now it will be even harder to explain how to write sensible ISRs

...R

Is this considered to be progress ?

Not by me.

Now it will be even harder to explain how to write sensible ISRs

I don't think anything has really changed. Using Serial.print() in an ISR is still a bad idea. It simply won't hang people out to dry if they ignore the "interrupts must be fast" message. Too bad, that.

At least, they didn't make delay() work in an ISR. 8)

So, after all one way to handle it is to set a variable which will be increased by some known value in different parts of the software and if the program loop reach the end, it's will be get zero value and it starts over again. If the code will crash somewhere, the value if this variable will show which part of the code is was going before it was crashed.

How about that assert, how does it work? I've done some research regarding ot but I haven't found "assert for dummies" example yet so could someone tell me what is assert in a nut shell, please?

TommiP

TommiP:
And it doesn't make any difference if I'm using Atmel Studio for programming Arduino ?

TommiP

You can debug your code, line by line, in AtmelStudio using the simulator or through a device like ATMEL-ICE connected to a modified Arduino. However, your specific situation might not be easy to debug.

PaulS:
Programs on a PC can be compiled in release mode or in debug mode. There are extra files generated in debug mode that contain things like line of code, line number, etc. There are far less compiler optimizations done, too.

For printing debug message, there are all kinds of pre-defined macros that give the current source file, function, line number, etc as printable strings....

Regards,
Ray L.

JimEli:
You can debug your code, line by line, in AtmelStudio using the simulator or through a device like ATMEL-ICE connected to a modified Arduino. However, your specific situation might not be easy to debug.

Yes, I'm aware of that but I was trying to find the way how to debug the code without pc connection, while Arduino is running on it's own..

TommiP

Connect a few pins (SPI.transfer or shiftOut() ) to a MAX7219 and output numbers/letters that show where the code is. Might have to put some delays in with the output so you don't just see a blur of letters/numbers going by.
Lots of 1,2, 4 and 8-digit common cathode displays are available. Turn off the displayed characters as you get things debugged.

CrossRoads:
Connect a few pins (SPI.transfer or shiftOut() ) to a MAX7219 and output numbers/letters that show where the code is. Might have to put some delays in with the output so you don't just see a blur of letters/numbers going by.
Lots of 1,2, 4 and 8-digit common cathode displays are available. Turn off the displayed characters as you get things debugged.

That dramatically affects runtime timing.
What works better when timing matters, is to use a logical analyzer and strobe some i/o pins.
You can create some raw port i/o macros and do things like high/low transitions on pins, multiple transitions, or even use multiple pins and decode them in parallel on the analyzer.
I often use a single pulse to mark the beginning of something and double pulse to mark the end.

Lots of information can be communicated very quickly just a few pins without affecting the runtime timing much.

--- bill

bperrybap:
That dramatically affects runtime timing.
What works better when timing matters, is to use a logical analyzer and strobe some i/o pins.
You can create some raw port i/o macros and do things like high/low transitions on pins, multiple transitions, or even use multiple pins and decode them in parallel on the analyzer.
I often use a single pulse to mark the beginning of something and double pulse to mark the end.

Lots of information can be communicated very quickly just a few pins without affecting the runtime timing much.

--- bill

This option combined with seven segment display (if I don't have logical analyzer) for example could be the solution to get accurate enough off-line debugger. Thanks! :slight_smile:

TommiP

I can't help feeling that some logical thought and careful planning of program development would eliminate the need for the sort of thing the OP is searching for.

For example developing a program in steps with each step proved to work before moving on to the next step. Then a new problem could be isolated to the impact of a few extra lines of code.

...R

Robin2:
For example developing a program in steps with each step proved to work before moving on to the next step. Then a new problem could be isolated to the impact of a few extra lines of code.

...R

Of course this softwarer also use this method and writes his codes piece by piece and steps forwards after previous piece is working 100%.. ..although sometimes it's bit difficult.. :smiley:

TommiP

I just came across this in the C programming faq:
Include line number info with a debugging macro

Of course, you have to use serial.prints() instead of printf(). This will at least get you line information.

KeithRB:
I just came across this in the C programming faq:
Include line number info with a debugging macro

Of course, you have to use serial.prints() instead of printf(). This will at least get you line information.

But if you plan to do much of that in your code on an AVR based processor, you also need to wrap it all in a macro that automatically converts all the formatting strings to F() strings to avoid filling SRAM with your format strings.

You will need a wrapper macro for debug() that wraps the format strings with F() macros then calls the function version to do the work. And the function version needs to use progmem format strings and call the apprpriate AVR versions for the xxprintf() functions that support progmem format strings.

--- bill