C++, C, and Clobbered variables, a possible solution and reasons?

As I’ve said before (in previous posts about much the same, and it is a continuing problem) I have a relatively complex system. STM32 microprocessor (doesn’t matter which), external memory (QSPI through the OctoSPI interface, ends up memory mapped).

Programming is a C++ overlay calling C support routines (CubeMXIDE) and FreeRTOS.
ALL system programming is in C++ structures (occasional virtual routines, mostly for displays), Calls to hal routines (ST provided drivers) are surrounded by code, generally protected by a semaphore (and not the STMicro routines, apparently a big difference).

ST Micro’s main.c routine sets up a task then calls that task. Call to the task never returns, so the default task is executing. Default task is used to call a C++ routine, which initializes a variety of subsystems (registry, packet, display drivers, HAL drivers (mine, not theirs), etc.

The C++ routine then calls another routine which sets up the main application task, then returns (back to the C++ routine which then returns back to the default task. Default task is never used again, and is fitted with a 10 second delay.

Problem: adding additional data arrays to a task called from the C++ routine crashes the system. This is in a task (not pointer derived, but set up for an instance of a variable). Expanding that variable (by adding data, and it was another class) caused the crash.

Symptoms: Execute program, go into expanded task, then change a variable. Changing that variable causes other data in that program to change. (possibly related to what the variable was). It wipes out other programming in the system.

So I have working (previously) code that adding data to causes a hard fault, because data within the program for that item is corrupted.

I think I know what the problem is, but my reasoning involves guessing. It’s how C++ memory management and C (FreeRTOS memory management work). The ultimate answer is that they don’t talk to each other.

FreeRTOS: heap 4, lots and lots of memory (256K). Program heap trimmed so that malloc failed hook does not happen (requires modification to malloc part of program). Therefore, heap is ok.

Now, looking at C++ classes. I’m not sure where that memory is allocated (mixed C, C++ program). What is seems like is that the C++ classes have no idea of what FreeRtos is doing, so you get pointers to a C++ area, and pointers to a FreeRTOS area. Adding data to a C++ class expands the class, but FreeRTOS (and possibly the linker) have no idea of what’s going on.

The solution to this problem is to find the class that is causing the problem, then change that instance to a pointer, not compile time assigned variable.

If the problem is in registry, which is declared as an instance of REGISTRY, then registry needs to be changed to a pointer, NEW is used, and the program works.

I’m likely missing something, but this is a recurring problem, even though I do have a fix (yeah, change it all to pointers).

FreeRTOS has its own memory area in off chip memory, the memory manager is heap4, and some data areas are directed through another memory manager (mine) to a third memory area.

I’m still puzzled.

I use NEW for the memory assignments to classes, CubeMXIDE has thread protection, I certainly shouldn’t be running out of memory, but I still suspect that memory assigned and needed in C++ classes (even normal class variables) are not picked up by either the compiler or FreeRTOS.

It is a puzzlement.

You need to understand that tasks are given a fixed and limited amount of stack memory (defined when you create that task) and ALL variables declared in a function use some of that memory for the task they are being called in.

This is particularly true for any variable that is (or class that has) an array as part of it, as those can use memory up fast, depending on the size of the array.

This is EXACTLY the same between C and C++, a C++ object with members declared as part of the object uses memory exactly the same as a C structure. Only if you explicitly place the data on the heap, with either malloc() or new() or in static memory by defining it outside a function, is it not placed on the stack.

Yes, many (but not all) C++ classes may put their large structures out in the heap, but only if they have been programmed to do so. There is nothing “automatic” about this.

Understanding the core language definitions of where data will be stored is vital for avoiding these sorts of issues, and you need to make sure you give task enough stack to handle what they use.

If you really have problems with that, it might be worth upgrading to versions of the processors that have hardware stack limit checking, as you get an immediate trap when you overrun the stack as opposed to a random crash.

1 Like

Ok, the problem comes out to a definition of the word “heap”.

I come from an assembly language stack pointer environment, where heap has a quite specific definition.

That’s not how FreeRTOS uses it (and I suppose you could argue that they are the same). The difference in FreeRTOS is that it takes all class and all variable assignments NOT using a pointer and puts them on that “heap”.

That explains lots.

Malloc (and the like) subtract from the “heap”, which is why adding error tracking to pvMalloc (please! return the amount that could not be requested when in error) helped.

On the other hand, normal (ran out of stack) instances do not seem to cause an error. I rather wish we could have that error trap. I don’t think that stack high water mark causes an error.

What I’ll do in my own code is simply go with pointers to classes, unless there’s a good reason not to. That would seem to minimize (if not eliminate) the problem.

Is the information you gave me in the documentation, explicitly? I don’t remember ever finding it, and (now) it makes much sense, since I know what your design assumptions are…
Thanks

I do not think this is correct. The only thing that is put on heap is what you allocate using pvPortMalloc.

Can you share one such code snippet (before and after)?

C and C++ have a definition of the heap, and that is memory allocated via malloc and family (FreeRTOS can use its one version of a heap with pvPortMaloc), and what is put on the stack. This is basically in the language standards.

“new” in C++ is basically a wrapper for calling malloc, so OBJECTS created with it will be on the heap. Note, just defining a pointer doesn’t put the object on the heap, but creating the object that the pointer points to with new does. (Description a bit simplified)

If you are using CM4 and CM7 based processors (like most of the ST family is) then the hardware cannot “trap” on an out-of-bounds stack, you would need to upgrade to something with a CM33 to have that ability. FreeRTOS does have some checks that MIGHT catch a stack overflow AFTER it happens (as long as the overflow doesn’t kill the system before it gets to the check).

Sharing the code is not practical. There’s too much of it. What I had was a C++ class as part of the FreeRTOS default task (which C++ class was accessed by an instance of, not a pointer). Writing to a variable that was part of the C++ class overwrote the data in another class also belonging to the FreeRTOS task.

Changing the two (one was sufficient) class references to pointers, using NEW to create and instantiate an instance of that class, then running the usual class setup routines to initialize variables, etc., then worked well. The part that originally failed was a variable initialization routine in one class.

This problem happened when the first C++ class had a large data array added to it.

you mean that you had something like

{

MyClass classInstance;

}

either in global scope or as an automatic variable on either the startup or a task stack, and changing that to

{

MyClass *classInstance = new(MyClass);

}

relieved the problem?

As it turns out, I’m actually using CM33 processors. I think you mean the memory management unit, but I think it’s limited in how many separate areas it can monitor.

The current program before fixing crashed with an invalid pointer. Single stepping through the source code showed that there were two lines with assignments. Executing those two lines showed that a variable (happened to be the heap manager that I wrote) changed values. i.e. assign eeprom.address in the init routine and the variables in the xheap class change.

Crashing, in this case, was a hard fault for memory access.

The CM33 has a SPLIM register, which will trigger an exception if the stack pointer is moved outside the limits of the stack. (This is different then the MPU) You should see in vRestoreContextOfFirstTask and in PendSV_Handler code that sets the psplim register that should trap when the sp gets out of bounds. This should give you a system exception for overruning the bounds of the stack at the moment it occurs. That may look like a “crash”, but looking in the debugger should show where the code exceeded the task stack.

yes it did. (gratuitous extra characters because of 20 character limit)

Ok, there are several pitfalls here. A global class instance will be initialized by the C++ startup code right after reboot, so not all modules required for clean init may be available yet. class instance variables on the main() startup stack will be gone and overtrampled once the scheduler starts. Instances created in automatic stack frames will implicitly be deleted once the function goes out of scope, which may be a blessing, but also a curse.

I generally shy away from class instances not built via new() in embedded sytems.

I find global class instances not that bad to use, and we them a lot. The key is you do need to make sure you guarantee the creation order (if the constructor needs the other object initialized).

We tend to use a singleton pattern for the few objects that need to be initialized before the objects that use them (there are created as static local variables of an access function that just returns a reference to that object).

We try to shy from ANY objects created with new, as then you need run-time testing that things worked right.

Why, yes, MISRA has a point there: IF your system has enough memory to serve every possible runtime scenario without memory reuse, you can get away with purely static code analysis. Not always realistically possible, though.

It’s not only about guaranteeing the creation order, btw. I remember a fairly subtle issue in which an object constructor indirectly relied on an already working peripheral which could or could not have been properly initialized depending on a number of factors such as whether the DUT came out of cold or warm boot or how close the peripheral’s defaults were to the requested behavior. Took a lot of hours to pinpoint problems that originated in the misassumption that the peripheral was already operational before being properly initialized.

The key to success here is understanding and keeping in mind when and in what context a constructor is being invoked. C++ tends to lure the developer into a “I can forget about the details and focus on the big picture as the runtime does all the dirt work for me” attitude. Not true for embedded.

It might life easier to pseudo-dynamically allocate objects once during init phase.
Heap usage is deterministic following this rule.
You can’t be trapped by Static Initialization Order Fiasco easily (mitigated by C++20 constinit or by applying non-standard compiler extensions manipulating construction order) and as @RAc pointed out you gain more control over more complex init dependencies.
But sure, it depends …

I manage things with an empty constructor. It’s perhaps not so elegant, but it does work.

@madyn - Probably you have already checked this, but make sure sbrk is set up correctly (it is not in many ST examples). See for details https://nadler.com/embedded/newlibAndFreeRTOS.html
Hope that helps,
Best Regards, Dave