Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 13 of 13

Thread: Using CrashReport when not connected to USB

  1. #1
    Senior Member houtson's Avatar
    Join Date
    Aug 2015
    Location
    Scotland
    Posts
    240

    Using CrashReport when not connected to USB

    I'm trying to track an intermittent fault on a teensy (micromod) project that runs off USB for most of the time.

    The fault only happens very occasionally, when it does it locks-up then auto reboots after the 8 seconds.

    CrashReport looks great and I imagine will be very helpful in tracking the problem down but as I'm off USB by the time I get it connected up the CrashReport is gone and I haven't been lucky enough to catch it while connected to USB.

    I don't have an SD card on the project. I was thinking I could check for a CrashReport at startup and dump it to EEPROM and then later print it to Serial once I connect to USB?

    Has anyone done that already or is there anything I have mis-understood CrashReport?

    Would something like this in setup()work to dump the report at the end of EEPROM (adapted from one of defragster's comments):
    Code:
    if (CrashReport) {
    	  EEPROM.put( EEPROM.end()-(1+sizeof(CrashReport)), CrashReport ); 
    	}
    If I did this any thoughts on how I could then print to serial, the project has lots of buttons so easy to initiate a diagnostics dump to serial when connect to USB but how would I recover the start point for the CrashReport on EEPROM and stream it to serial?

    Would I be better allocating a fixed location to start the report (and if so any guide on what a reasonable sizeof would be?

    Thanks for any help, Paul

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    16,370
    CrashReport prints the contents as a STREAM class. So anything that can take a STREAM of output could capture it.

    The sizeof(CrashReport) isn't a thing AFAIK - it can be tested as a bool, and then dynamically ejects bytes when called to parse the Crash.

    @KurtE wrote a Stream to MEMORY, so early setup() output could be captured before USB was online. It was on a thread and I thought it was COOL - but forgot about it before I played with it. I asked him again and he clued me in to the location - but again I didn't get to it those months ago.

    Using that STREAM to RAM code would allow copying it from RAM buffer to EEPROM.

  3. #3
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,893
    CrashReport prints the contents as a STREAM class.
    Actually CrashReport derives from Printable not Stream. Anyway, you can easily capture the content of a printable object with the following helper class. The constructor of the StringDumper "prints" the content of the passed in printable object (e.g. a CrashReport) to an Arduino String. For easy usage it provides a cast to const char*.

    Code:
    class StringDumper : private Print
    {
     public:
        StringDumper(const Printable& p)
        {
            this->println(p);
        }
        operator const char*() const
        {
            return buf.c_str();
        }
    
     protected:
        size_t write(uint8_t b) override
        {
            buf.append((char)b);
            return 1;
        }
        String buf;
    };


    You can use the class to store the crash report to EEProm like this:
    Code:
    void storeReport()
    {
        StringDumper report(CrashReport);   
    
        for (size_t i = 0; i < strlen(report); i++) // store th report in the eeprom
        {
            EEPROM.put(i, report[i]);
        }
        EEPROM.put(strlen(report), '\0'); // add \0 as terminator
    }
    Here a complete test sketch. I'd add some flag in the eeprom to check if a report is available before trying to print it.
    Code:
    #include "Arduino.h"
    #include "EEProm.h"
    
    class StringDumper : public Print
    {
     public:
        StringDumper(const Printable& p)
        {
            this->println(p);
        }
        operator const char*() const
        {
            return buf.c_str();
        }
    
     protected:
        size_t write(uint8_t b) override
        {
            buf.append((char)b);
            return 1;
        }
        String buf;
    };
    
    void storeReport()
    {
        StringDumper report(CrashReport);
    
        size_t length = strlen(report);
    
        for (size_t i = 0; i < strlen(report); i++) // store the report in the eeprom
        {
            EEPROM.put(i, report[i]);
        }
        EEPROM.put(strlen(report), '\0'); // add \0 as terminator
    }
    
    void printReport()
    {
        Serial.println("Stored:");
    
        for (int i = 0; EEPROM[i] != '\0'; i++)
        {
            Serial.print((char)EEPROM[i]);
        }
    }
    
    void setup()
    {
        if (CrashReport)
        {
            storeReport();
    
            // stop executing here
            pinMode(LED_BUILTIN, OUTPUT);
            while (true)
            {
                digitalToggle(LED_BUILTIN);
                delay(50);
            }
        }
    
        // you'd only call this on demand (button etc). For testing uncomment the following (after crashing the sketch and writing the report to the eeprom at least once)
        // while (!Serial) {}
        // printReport();
    
        pinMode(LED_BUILTIN, OUTPUT);
    }
    
    void loop()
    {
        int* p = nullptr;
        // *p = 42;  //uncomment to crash the sketch
    
        digitalToggleFast(LED_BUILTIN);
        delay(250);
    }
    Last edited by luni; 04-08-2022 at 09:25 AM.

  4. #4
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    16,370
    Quote Originally Posted by luni View Post
    Actually CrashReport derives from Printable not Stream. Anyway, you can easily capture the content of a printable object with the following helper class. The constructor of the StringDumper "prints" the content of the passed in printable object (e.g. a CrashReport) to an Arduino String. For easy usage it provides a cast to const char*.

    ...
    Of course it does - says right in the file - thanks for follow up and usable solution!
    Code:
    class CrashReportClass: public Printable {

  5. #5
    Senior Member houtson's Avatar
    Join Date
    Aug 2015
    Location
    Scotland
    Posts
    240
    Thanks @Defragster, that helps.

    As a quick hack I've used StreamUtils to write it to EEPROM on restart and then read it out at leisure.

    I don't think there is an end of report marker and the CrashReports have different lengths so I read it out 120 chars at a time with repeated calls to .readString() until I get garbage from a fixed starting address (100).

    A few snippets for any others following in the future.

    Cheers, Paul

    Code:
    #include <EEPROM.h>
    #include <StreamUtils.h>
    
    // CrashReport
    EepromStream crashReportLog(100, (EEPROM.length() - 101));
    
    void setup() {
      // check for CrashReport and dump to EEPROM
      if (CrashReport) crashReportLog.print(CrashReport);
    .......
    }
    
    // dump 120 chars from the EEPROM record of the CrashReport
    void dumpCrashReport() {
      if (Serial) Serial.print(crashReportLog.readString());  
    }

  6. #6
    Senior Member houtson's Avatar
    Join Date
    Aug 2015
    Location
    Scotland
    Posts
    240
    @luni - thanks, our posts crossed - your solution looks much better (esp. the terminator). Cheers, Paul

  7. #7
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,893
    Yes, the streamUtils library seems to be an overkill for this. (However the library looks very interesting for other stuff).

    The code shown in #3 could be done much simpler, unfortunately a bug sneaked into the EEPROM library (see https://github.com/PaulStoffregen/EEPROM/pull/10). With the fixed lib you can simply do:

    Code:
    #include "EEPROM.h"
    
    class StringDumper : private Print
    {
     public:
        StringDumper(const Printable& p)
        {
            this->println(p);
        }
        
        String dumpedString;
    
     protected:
        size_t write(uint8_t b) override
        {
            dumpedString.append((char)b);
            return 1;
        }    
    };
    
    void storeReport()
    {
        StringDumper report(CrashReport);
        EEPROM.put(0,report.dumpedString);
    }
    
    void printReport()
    {
        String report;
        EEPROM.get(0,report);
        Serial.println(report);
    }
    ....

  8. #8
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,893
    I had a closer look at the StreamUtils library which is quite nice. The StringStream is really useful for converting printables like a crashreport to Strings:

    Code:
    #include "StreamUtils.h"
    
    void setup()
    {
        if (CrashReport)
        {
            StringStream strstream;
            strstream.println(CrashReport);
            EEPROM.put(0, strstream.str());
        }
    
        // while (!Serial) {}
        // String report;
        // EEPROM.get(0, report);   // make sure you have stored a report when calling this
        // Serial.println(report);
    }
    
    void loop(){
    }
    Please note: for testing you need the current EEPROM library from the gitHub repo (https://github.com/PaulStoffregen/EEPROM) which fixed the issue with storing Strings.

  9. #9
    Senior Member houtson's Avatar
    Join Date
    Aug 2015
    Location
    Scotland
    Posts
    240
    @luni, I used your code at #3 - it works great. I added a marker like you suggested for 'report available' and also one for 'report read' so you can see new crash reports. Thanks Paul.

  10. #10
    Senior Member
    Join Date
    Oct 2019
    Posts
    337
    I used the info from this thread to log the crash report to the SD card, as I have a device that runs in the car and does crash from time to time for some unknown reason.
    Here is a basic sketch I used to accomplish this:

    Code:
    #include "SD.h"
    #include "MTP_Teensy.h"
    #include "StreamUtils.h"
    
    StringStream strstream;
    bool logCrashReport = false;
    
    #define CS_SD BUILTIN_SDCARD  // Works on T_3.6 and T_4.1
    //#define CS_SD 10  // Works on SPI with this CS pin
    void setup()
    {
      Serial.begin(9600);
      /*
      while (!Serial && millis() < 5000) {
        // wait for serial port to connect.
      }
      */
    
      if(CrashReport){
        
        strstream.println(CrashReport);
        logCrashReport = true;
        
        }
      
    
      // mandatory to begin the MTP session.
      MTP.begin();
    
      // Add SD Card
      if (SD.begin(CS_SD)) {
        MTP.addFilesystem(SD, "SD Card");
        Serial.println("Added SD card using built in SDIO, or given SPI CS");
      } else {
        Serial.println("No SD Card");
      }
      Serial.println("\nSetup done");
    
      if(logCrashReport){
        File dataFile = SD.open("CrahReport.txt", FILE_WRITE);
        // if the file is available, write to it:
        if (dataFile) {
          dataFile.println(strstream.str());
          dataFile.close();
          // print to the serial port too:
          Serial.println(strstream.str());
          } else {
          // if the file isn't open, pop up an error:
           Serial.println("error opening CrashReport.txt");
            }
        }
    }
    
    
    void loop() {
      MTP.loop();  //This is mandatory to be placed in the loop code.
    
      int* p = nullptr;
       //*p = 42;  //uncomment to crash the sketch
      digitalToggleFast(LED_BUILTIN);
      delay(250);
     }

  11. #11
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    10,976
    @Rezo - looks good.

    My guess is that if you were not trying to write it to two places, you would not need the Stream stuff, but simply print the CrashReport to the SD file.

    Also wondering in your case, if you fail to open the SD card, you don't output it to Serial?

  12. #12
    Senior Member
    Join Date
    Oct 2019
    Posts
    337
    @KurtE

    Thanks!
    I used the serial stuff while debugging on the computer.
    But in the field its standalone - 12v power and CAN to request/respond for data.
    Question is, how long is CrashReport available for? Duration of the post reset power cycle? If that's the case I can just log it later on without Stream.

  13. #13
    Senior Member houtson's Avatar
    Join Date
    Aug 2015
    Location
    Scotland
    Posts
    240

    WatchDog + CrashReport + Breadcrumbs + working off USB

    I've got CrashReport working well saving to eeprom using method at #3, Also been using the breadcrumbs in 1.57 which are a really useful addition.

    I'm now trying to chase down an intermittent 'hang'.

    I've added a watchdog using WDT_T4 with a TeensyMicroMod, using a simple set up with WDT1.

    Any ideas on how to best use breadcrumbs with the watchdog (while disconnected from usb)?

    Is there a way to configure CrashReport to catch the watchdog reset and report back the breadcrumbs at the time of the hang?

    I can possibly work around this using the WDT1 trigger callback to force a crash - is that the best way to do it?

    Cheers Paul

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •