Putting __PRETTY_FUNCTION__ into PROGMEM. How?

So, I am having a little trouble debugging something I'm writing for a Mega2560. What it is isn't really critical, but I tripped up over something I thought I understood, and obviously I don't.

To summarise I've effectively been adding code to my software to track functions/methods entering and leaving using a relatively simple class as a local variable (so I can use the constructor and destructor to catch the enter and leave actions. The constructor takes a "const char *" argument which is supplied with the value __PRETTY_FUNCTION__ so that it knows where it is.

Now, this all works fine at one level except that all these __PRETTY_FUNCTION__ values are placed into SRAM, and so I have now blown through the whole 8 KBytes of available memory.

No problem (I think to myself) I'll create a "static const char func[] PROGMEM = __PRETTY_FUNCTION__;" before the stack frame tracking variable, and pass this into it in place of directly using __PRETTY_FUNCTION__. Obviously I need to remember that now I need to use the address provided to pull the actual text from program memory rather than SRAM, but that's easy.

Or so I thought.

It seems that (upon some digging about) __PRETTY_FUNCTION__ isn't actually a text constant anymore, but a hidden (almost underhand) variable defined by the compiler on your behalf. As a result the following line of code does not compile:

static const char func[] PROGMEM = __PRETTY_FUNCTION__;

with an error something along the lines of "I can't work out how big the data is", but the following line does compile:

static const char func[] PROGMEM = "__PRETTY_FUNCTION__";

See the difference?

Obviously the second is functionally pointless from my perspective, but proves that I'm not being totally foolish (as I was beginning to believe).

So the question is "How can you put the __PRETTY_FUNCTION__ values into PROGMEM so that I don't have to waste real memory unnecessarily?" Is it even possible?

Regards,
Jeff.

Edit/ Sorry, realised that I had inserted a superfluous asterix, *, into the definition of func above which made the actual definition wrong no matter how you looked at it. I have to put that down to frustration with the problem and having been round and round trying various things before realising that I wasn't wrong in what I was writing, but wrong in my assumption about the nature of __PRETTY_FUNCTION__. Apologies.

Hi Jeff, don't have time to test, but does changing const to constexpr make a difference?

Happy new year!

Using "constexpr" makes no difference.

Thinking there might be some compiler flags that control the handling of __FUNCTION__ and __PRETTY_FUNCTION__. Shall be looking into this today.

Jeff.

I think I'll have to put this one to bed as a solution to this would seem to be practically impossible. Essentially it seems that this boils down to the compiler not really being being Harvard architecture aware which isn't a big deal most of the time, especially now as modern micro controllers available resources are becoming more plentiful.

Time to move on from the old ATmega328 and 2560 I think. I have, unsurprisingly, seen this coming and the software I have been restructuring and rewriting has the aim of moving over to an ARM based MCU. The approach is to restructure on the platform I already had it working on, then migrate to the new hardware; approach my problems one at a time.

As for __PRETTY_FUNCTION__ and associated friends I've had to simply insert actual literal strings into the code in their place (there were a lot). Eventually, once the move to a new target platform starts this can be undone and returned to the tidier, simpler and more reliable approach I had intended.

Jeff

when i'm struggling to understand program flow i'll offten start with just adding Serial.println (__func__); to the top of each function. since it's generic, it could be a macro.

This is (pretty much) exactly what I am doing. The problem is not how to trace/debug the code but making the compiler and tools put the 'func' variable into PROGMEM. As it stands it places them into SRAM and uses up all the memory available rendering the approach useless. 8 KBytes doesn't go far, 2 KByes far less.

Jeff

what if you did Serial.println (F(__func__));

Near as I can tell, __PRETTY_FUNCTION__ and (__func__) are not macros but sting (i.e. const char[]) variables defined by the compiler. As such, you can't use the PROGMEM directive on them as the compiler has already decided where they will be stored and how they will be handled. As you said, perhaps there's a flag for the AVR version of gcc that changes this behavior. Maybe study that compiler's spec or find a discussion forum on it.

Hi,

The F() macro does not work because __PRETTY_FUNCTION__ isn't a literal text value (ie a sting in quotes), it is (and has been for some time now) been a variable created by the compiler called __func__ (or something like that).

Jeff.

Since __PRETTY_FUNCTION__ is compiler-generated, you'll have to rely on the linker to place the strings in flash only.

  1. Go to Preferences in the Arduino IDE and enable verbose output during compilation. Then close the IDE.
  2. Add the -Wl,--verbose flag to compiler.ldflags in ~/.arduino15/packages/arduino/hardware/avr/1.8.6/platform.txt:
    compiler.ldflags=-Wl,--verbose
    
  3. Reopen the IDE and compile your sketch for the appropriate board. Look for the linker script location in the linker output:
    opened script file ~/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/avr/bin/../lib/ldscripts/avr6.xn
    using external linker script:
    [...]
    
  4. Edit the linker script printed above to move all __PRETTY_FUNCTION__ strings to the text memory region:
    /* Internal text space or external memory.  */
    .text   :
    {
      *(.vectors)
      KEEP(*(.vectors))
      [...]
       *libprintf_flt.a:*(.progmem.data)
       *libc.a:*(.progmem.data)
       *(.progmem*)
       *(.rodata.*__PRETTY_FUNCTION__)  /* <--------------------- Add this line */
      . = ALIGN(2);
      [...]
    }  > text
    
  5. Cast all instances of __PRETTY_FUNCTION__ in your code to const __FlashStringHelper * before printing them.
    void my_amazing_funcion() {
      Serial.println(reinterpret_cast<const __FlashStringHelper *>(__PRETTY_FUNCTION__));
      Serial.println(__PRETTY_FUNCTION__); // This doesn't work
    }
    
    void setup() {
      Serial.begin(115200);
    }
    
    void loop() {
      my_amazing_funcion();
      delay(10000);
    }
    

Apologies for the slow my reply and thank you for your in-depth reply - way, way more detail than I had any right to expect.

Have I tried it? To be honest, no, but I can see your approach - very clever.

Now I understand what's going on I can restrict my "work around" to the source code level where I am likely not to forget whats going on.

My surprise at the initial problem is really an issue with my use of C and C++ which is now somewhat out of date (yes, I still though that __PRETTY_FUNCTION__ was replaced by a text literal).

I would say that finding out that the tool chain doesn't offer a tidier route towards a solution surprising - surely somebody has run into this issue before? That being said I have to recognise (had already recognised) that times are moving on and the days of cramming a solution into 2 or 8 KBytes of SRAM are passing away. Indeed, newer and more powerful MCUs are cheaper and do not require these sorts of gymnastics, I just need to dig into their datasheets a little more thoroughly until I know them as well as the AVR chips.

Regards,
Jeff.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.