Large global object of structure or class not working - why?

Go To Last Post
17 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, I am using g++ compiler with custom options.
The problem is that I have intermittent hanging (like memory overrun but most often it hangs completely) when I create a global object of given struct.
The kludge is to declare the object locally in main() (this way it is created on the stack) and assign its address to global pointer.
This is ugly thing and scares me because I wonder if similar misbehaviour can be expected from other global variables. The probleb doesnt exist in smaller versions of my project. Stack memory usage around 75%, very few local variables, minimal function nesting, rich in interrupts, ATMEGA128, 60% flash used, 95% pins connected to hardware.
It could be anything but this is the second time since half a year I had to modifymy large project to allocate large structs on the stack. There is something wrong maybe in the linker options or memory section, anything. I don't know where to learn. Any educated guesses?

avr-g++ -I"I:\ATMEGA\..\WinAVR-20081205\avr\include\avr"  -mmcu=atmega128 -Wall   -DATMEGA128 --pedantic  -ansi  -fno-exceptions  -ffunction-sections  -fdata-sections  -fno-threadsafe-statics -DF_CPU=16000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT FOX.o -MF dep/FOX.o.d  -c  ../FOX.c
avr-g++ -mmcu=atmega128 -Wl,--gc-sections  -lm  -Wl,-u,vfprintf  -Wl,-u,vfscanf -Wl,-Map=FOX.map FOX.o -lc -lm -lprintf_flt -lscanf_flt  -o FOX.elf
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature  FOX.elf FOX.hex
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O ihex FOX.elf FOX.eep || exit 0
avr-objdump -h -S FOX.elf > FOX.lss
struct GPSdata
{
	uint16_t year;
	uint8_t month;
	uint8_t day;

	uint8_t hour;
	uint8_t minute;
	uint8_t second;
	uint16_t milisecond;

	uint8_t fix_available;//0,1 //GPRMC, GPGLL
	uint8_t dgps_mode;//0,1,2,3 //GPGGA
	uint8_t fix_type;//1,2,3 (off, 2D, 3D) //GPGSA
	uint8_t n_satused;
	uint8_t n_satfound;

	float latitude;//5107.4753,N
	//gpsdata.latitude=latitude_deg+(latitude_min+latitude_min_frac*1e-4)/60.0;
	int8_t latitude_deg;//-90...90
	uint8_t latitude_min;//0...59
	uint16_t latitude_min_frac;//0...9999

	float longitude;//01702.0987,E
	//gpsdata.longitude=longitude_deg+(longitude_min+longitude_min_frac*1e-4)/60.0;
	int16_t longitude_deg;//-180...180
	uint8_t longitude_min;//0...59
	uint16_t longitude_min_frac;//0...9999

	float speed_knots;
	float speed_kmh;
	float true_course;
	float heading;
	float MSL_altitude;
	float WGS_altitude;

	float PDOP;
	float HDOP;
	float VDOP;

	GPSdata();
	void Print() const;

	bool PositionValid;
	bool SpeedValid;
	bool CourseValid;
	bool AltitudeValid;
	bool DateTimeValid;
	bool FlyOK;
	void VerifyValidity();

	bool FreshData;
	bool FreshData_RMC;

	uint32_t GPSGGA_time_seconds;
	uint32_t GPSGGA_time_seconds_32Hz;
	uint32_t GPSGGA_time_seconds_256Hz;

	uint32_t GetAge32Hz() const;
	uint32_t GetAge256Hz() const;
};

Problematic:

struct GPSdata
{...};
GPSdata GPS_DATA;

int main()
{
  ...
  return 0;
}

Good:

struct GPSdata
{...};
GPSdata * GPS_DATA_ptr=NULL;

int main()
{
  GPSdata GPS_DATA_ONSTACK;
  GPS_DATA_ptr=&GPS_DATA_ONSTACK;
  ...
  return 0;
}

Why?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It sounds like you are having a stack/data collision. If so, then how you create the object may not affect whether or not you are having the collision, only how it manifests itself.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Seeing the output from avr-size for both variants might give parts of the explanation.

Reading this might also shed some light on the terrain you are moving in: http://www.gnu.org/savannah-chec... (when you create your global, non-automatic, non-dynamic object it goes into the ".data" section.)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am wondering at which point the position of stack top is stored in the hex or map file. Clearly when placing struct as static it lands at bss section, growing it to some 2800bytes but still I see nothing more than 200-400 bytes of local stack variables in the functions. I suspect that some of the ctors is silently calling malloc that could be misconfigured, writing its heap data where it shouldn't. At the moment I am not using heap explicitly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Depending on how g++ packs the structure, it's only around 70 bytes' worth of data (67 by my count, if none of the multibyte elements are aligned on even multiples of their size). I shouldn't think there'd be a problem with even half a dozen of these things as globals or on-stack variables.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
it's only around 70 bytes' worth of data

Quote:
growing it to some 2800bytes

Clearly there is something in the program that is taking up much more than what the struct does.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kbosak wrote:
I am wondering at which point the position of stack top is stored in the hex or map file. Clearly when placing struct as static it lands at bss section, growing it to some 2800bytes but still I see nothing more than 200-400 bytes of local stack variables in the functions. I suspect that some of the ctors is silently calling malloc that could be misconfigured, writing its heap data where it shouldn't. At the moment I am not using heap explicitly.

I don't see why the constructor should be calling malloc() at all, but I do note that struct GPSdata has default constructor declared. If this constructor refers to other objects, then you could have a construction order problem. By putting your struct GPSdata on the stack in main() you are guaranteeing that all global objects have been constructed by the time you construct the GPSdata object.

Christopher Hicks
==

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK this was clearly a gcc bug "Global constructor calls above 64k - ID: 2209796"

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I don't see why the constructor should be calling malloc() at all

AFAIK, it is common for implementations of constructor to
1) call malloc, and
2) initialize the object

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AFAIK, says the constructors don't call malloc.
Unless you do

Osama * osama=new Osama[12];

Example:

struct SSS
{
   int y;
   float z;
};
int main()
{
     SSS sss;
     return 0;
}

Does what? Allocates on stack. What is the constructor? It pushes SSS::y and SSS::z on stack.
For sure no anmimals have been hurt during writing my code, and no dynamic allocation has been used. Proof? All the opperators operators new, delete and delete[] are causing linker problems in my configuration of g++. I have never provided them. I would have spotted if there would be any heap provided. Same for malloc and free. The issue is NOT about mysterious, silent memory allocation on heap. Only global static data (SRAM) allocation or static constants (FLASH) or stack (SRAM).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anyway, as I said, the problem was transient, ATMEGA(BIGFLASH) specific, appeared several times ONLY when the code was large, often changing -O3 to -Os helped while reducing code size. Perfect fit for mentioned "Global constructor calls above 64k - ID: 2209796". Apparently solved forever, just get a fresh avr-gcc.

Last Edited: Thu. Apr 2, 2009 - 12:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohanEkdahl wrote:
Quote:

I don't see why the constructor should be calling malloc() at all

AFAIK, it is common for implementations of constructor to
1) call malloc, and
2) initialize the object

Malloc is called implicitly during object construction only if the object in question is being constructed on the heap. This usually means that it is being constructed by "new". If it is a static, global or stack-allocated object then malloc is not required to allocate memory.
C_MyClass GlobalObject;
// global allocation made by linker, no malloc()

int main (void) {
    C_MyClass MyObject;
    // allocated on the stack, no malloc()
    
    C_MyClass *pMyObject = new C_MyClass;
    // pointer pMyObject allocated on stack,
    // instance of C_MyClass allocated on the
    // heap via malloc()

    // [...]
}

In the present context we've been told there is a behavioural difference depending on whether the object in question is allocated on the stack (ie local to a function) or as a global. In neither of these cases is there an implicit call to malloc() to allocate memory. In the first the required space is reserved on the stack just like any other local data, and then initialised. In the second the memory is allocated by the compiler/linker, and the constructor is called on that memory sometime before the program arrives at main().

Of course, things can get a little more complicated if an object allocated on the stack, for example, then itself allocates supplementary storage on the heap. This is a common design pattern for things like string or array classes which automatically expand and shrink their internal storage as required. In this case there would be explicit calls to malloc/free (or more likely new/delete) in the constructor (and other member functions) for the object.

This could be happening in the present case (we haven't been shown the body of the GPSData() constructor, only that it has been declared), but I kind of doubt it as it looks as if the data required by the object is all stored directly as member variables, none of which is a pointer.

CH
==
[Edited to tidy up the English in a couple of places.]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

AFAIK, says the constructors don't call malloc.

Quote:

Malloc is called implicitly during object construction only if the object in question is being constructed on the heap.

Yes. Of-course. Semi brain-fart on my account.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I don't see why the constructor should be calling malloc() at all, but I do note that struct GPSdata has default constructor declared. If this constructor refers to other objects, then you could have a construction order problem. By putting your struct GPSdata on the stack in main() you are guaranteeing that all global objects have been constructed by the time you construct the GPSdata object.

Oh why so? Aren't you aware that:
-we are not using DLL's ;-)
-by pure coincidence the whole code is "include all" approach, only one obj file.
-the order of constructing class members is well defined.
What might stink is the RELATIVE ORDER OF CREATION OF STATIC DATA IN DIFFERENT COMPILATION UNIT.
But we have only one here, moreover the GPSSTRUCT is just a plain stupid flatpack of builtin types and a few less-or-more public methods that operate on those data and an explicitly passed char array argument.
All very clean. No interruption deadlock, polled call from main(). Nothing that would go wrong, except that it is using C++ given the constructors.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kbosak wrote:
What might stink is the RELATIVE ORDER OF CREATION OF STATIC DATA IN DIFFERENT COMPILATION UNIT.
That was exactly what I was thinking of when I said "construction order problem".
kbosak wrote:
-by pure coincidence the whole code is "include all" approach, only one obj file.
I had not noticed that. As you say, pure coincidence.

I agree that with all the information we now have the problems are not likely to be down to construction order. But it is a gotcha that you should bear in mind when designing C++ code especially.

CH
==

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm having the same kind of issue, and my library (which I plan to publish open-source when it is more mature) is getting huge, so the issue appears randomly and is very hard to track. I defined the guards as per
https://www.avrfreaks.net/index.p...

I'm certain that I don't have global objects anywhere in the library. Only static pointers.

Yet it never solved my problem. The execution get stuck in some kind of loop before main and does various thing (I suspend it in JTAG). I getting a bit desperate.

Do you guys have an heuristic to track this kind of problem?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Show us the minimal code which causes problem. Look at generated assembly file to check what happens before main. Personally, I have relatively big c++ program, lot of separate compilation units, with global, local, static variables, and it works perfectly. So I assume that your problem is memory overrun or something like that. Do the program works in AVR Studio simulator? Did you check how it works after compiling for device with bigger SRAM?