Pages: [1]   Go Down
Author Topic: IDE 1.0.5 v 1.5.7 - Instantiating a class causes program to reset (using 1.5.7)  (Read 599 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, this problem is weird & hard to explain, but I'll do my best. Thanks for trying to help me & others who may be seeing this too.

My full code is here (in case the code snippets below are not enough):
https://github.com/briggsm/ColorRing

My code compiles successfully using both IDE's 1.0.5 and 1.5.7

But after compiling my code with 1.5.7, it wasn't running properly and would reset itself - (but just before resetting I could see [with Serial.println() statements] that very weird things were happening (like variables had unexpected values in them))  It seemed almost as if a pointer was pointing into invalid space, or the code was jumping to somewhere it shouldn't. It's very hard to debug stuff like that! But I SEEMED to narrow it down to something:

If I instantiate my class using the "new" keyword (and "delete") it works fine, but using the standard/accepted way to instantiate a class, all these weird things happen.  Here the specific code I'm changing:

Original code (broken at runtime, using 1.5.7 IDE):
Code:
SetSeqPixels handSsp(strip, startPixelNum, 1, handSize[clkHand], 0, 0, 0, DESTRUCTIVE, CW, NONANIMATED, NOCLEAR, NOGRADIATE, GRADIATE_LASTPIXEL_LASTCOLOR, numColorsInSeries, colorSeriesArr);
handSsp.exec(NOSHOWSTRIP);

New code (works at runtime, using 1.5.7 IDE):
Code:
SetSeqPixels *handSsp = new SetSeqPixels(strip, startPixelNum, 1, handSize[clkHand], 0, 0, 0, DESTRUCTIVE, CW, NONANIMATED, NOCLEAR, NOGRADIATE, GRADIATE_LASTPIXEL_LASTCOLOR, numColorsInSeries, colorSeriesArr);
handSsp->exec(NOSHOWSTRIP);
delete(handSsp);

Also, for the record, this code is also broken at runtime, using 1.5.7 IDE (same "weird" problems, followed by reboot):
Code:
SetSeqPixels handSsp = SetSeqPixels(strip, startPixelNum, 1, handSize[clkHand], 0, 0, 0, DESTRUCTIVE, CW, NONANIMATED, NOCLEAR, NOGRADIATE, GRADIATE_LASTPIXEL_LASTCOLOR, numColorsInSeries, colorSeriesArr);
handSsp.exec(NOSHOWSTRIP);

Remember, all of my code compiles just fine. It's just during runtime that the different ways of instantiating my class is mattering.

I have a hunch it probably has something to do with the way I made my class.  Maybe I got away with something that compiling with IDE 1.0.5 was ok with, but compiling with IDE 1.5.7 causes these weird problems....  

Here's the code for the "SetSeqPixels.h" class:
Code:
#ifndef SETSEQPIXELS_H
#define SETSEQPIXELS_H

#include <Arduino.h>
#include "LightShowCmd.h"

#include "StandardCplusplus.h"
#include "serstream"
using namespace std;


class SetSeqPixels : public LightShowCmd {

private:
byte currPixelNum;

public:
SetSeqPixels();

SetSeqPixels(Adafruit_NeoPixel* strip,

byte startPixelNum,
byte numPixelsEachColor,
byte colorSeriesNumIter,
byte numPixelsToSkip,
word animDelay,
word pauseAfter,

// boolBits
bool destructive,
bool direction,
bool isAnim,
bool clearStripBefore,
bool gradiate,
bool gradiateLastPixelFirstColor,

byte numColorsInSeries,
PixelColor *colorSeriesArr);

SetSeqPixels(Adafruit_NeoPixel* strip, byte* stripCmdArray);

void init(Adafruit_NeoPixel* strip,

byte startPixelNum,
byte numPixelsEachColor,
byte colorSeriesNumIter,
byte numPixelsToSkip,
word animDelay,
word pauseAfter,

// boolBits
bool destructive,
bool direction,
bool isAnim,
bool clearStripBefore,
bool gradiate,
bool gradiateLastPixelFirstColor,

byte numColorsInSeries,
PixelColor *colorSeriesArr);

void step(boolean isShowStrip);
void reset();
int getCurrPixelColor();
};

#endif // SETSEQPIXELS_H

And the "LightShowCmd.h" base class from which "SetSeqPixels" derives itself from:
Code:
#ifndef LIGHTSHOWCMD_H
#define LIGHTSHOWCMD_H

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#include "PixelColor.h"
#include "Gradient.h"

#include "StandardCplusplus.h"
#include "serstream"
using namespace std;

#include "AllDefs.h"

class LightShowCmd {
protected:
// ======================
// === Cmd Parameters ===
// ======================

byte cmdType;
Adafruit_NeoPixel* strip;

byte startPixelNum;
byte endPixelNum;

byte numSections;
byte numPixelsEachColor;
byte colorSeriesNumIter;

byte numPixelsToSkip;
word numIter;

unsigned long animDelay;
word pauseAfter;

// boolBits
bool destructive;
bool direction;
bool wrap;
bool isAnim;
bool clearStripBefore;
bool gradiate;
bool gradiateLastPixelFirstColor;

byte numColorsInSeries;
PixelColor colorSeriesArr[7]; // up to MAX 7 different color series. (Should be enough for each type of cmd).


// ==============
// === Others ===
// ==============

// Gradiate
MultiGradient multiGradient;
PixelColor currPixelColor;

boolean isFirstTimeAfterFinished;
int cmdFinishedTime;

public:
boolean isPauseMode;
byte currIteration;
//string inOutStr;
        
LightShowCmd();
boolean isMoreSteps();

virtual void step(boolean isShowStrip) = 0;
void exec(boolean isShowStrip);

static byte fixPixelNum(byte pixelNum);
void showAndWait();

virtual void reset() = 0;

bool bitChecker(byte bit, byte by);
//void clearStrip(Adafruit_NeoPixel* strip);
void clearStrip();
byte getCmdType();
string getCmdTypeStr();
void lscResetCommon();
void lscStepPreCommon();
void lscStepPostCommon(boolean isShowStrip);

bool getIsAnim();
unsigned long getAnimDelay();
void setBoolBitVars(byte boolBits, bool &destructive, bool &direction, bool &wrap, bool &isAnim, bool &clearStripBefore, bool &gradiate, bool &gradiateLastPixelFirstColor);

};

#endif // LIGHTSHOWCMD_H

Another "hunch" is that it MAY be related to the "virtual" functions - it's just a hunch because I don't have a very good grasp of those things.  But everything else seems to be pretty standard/basic class stuff.

Another interesting observation, that may help shed light (but just makes me more confused):
There are 2 places in my code where there exists the "Original Code" (see above).  While debugging I'm making sure I'm calling just one (and not the other). But even the one that I'm not calling (never, ever!) - if I don't change that ALSO to use the "New Code" (see above), the program crashes (& hence reboots).  If I change it to the "New Code", (even though I never, ever call it!), the program runs fine.

Also, for the record, I tried to "Nightly Build" of the IDE today (17th September, 2014) and the same issue occurred).

Also, for the record, I've tried this on 2 boards: Arduino Mega 2560 (with CC3000 shield) and the WildFire board.  Same issues on both.

Even though my program "seems" to be running ok now, I don't want to use the "new" and "delete" method (because I don't want to have to worry about garbage collections, fragmentation, etc), and I think generally it's not encouraged to use "new" and "delete" for Arduino projects, right?

Does anything stand out to you as the wrong way to do things?  Thank you for your thoughts!
« Last Edit: September 17, 2014, 06:53:38 am by briggsm » Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 653
Posts: 50881
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Does anything stand out to you as the wrong way to do things?
Yes.

Code:
#include "StandardCplusplus.h"
#include "serstream"
using namespace std;
You are using a microcontroller with limited memory, not a multi-processor PC with terabytes of memory.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi PaulS,

Thanks for your thoughts. However, memory (RAM specifically) is not an issue. I'm using both an Arduino Mega 2560 and WildFire board (from WickedDevice) to test this out on.  The WildFire board even has 2x the RAM as the Mega 2560.  I've put many "getFreeRam()" calls in my debug code to check the amount of free RAM at various locations throughout the code, and there's always plenty - even at the time of the crash/reset.

But "just to make sure", I took out the:
Code:
#include "StandardCplusplus.h"
#include "serstream"
using namespace std;
code (and of course all of the cout's) from the classes I'm trying to instantiate, but the original problem still exists.

Any other thoughts why I NEED to instantiate my class with the "new" keyword for the program to run without crashing/reseting?

Can anyone verify whether I'm doing the "virtual" stuff right or wrong?

Thanks again for your thoughts.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 653
Posts: 50881
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Any other thoughts why I NEED to instantiate my class with the "new" keyword for the program to run without crashing/reseting?
No. Behind the scenes, the same code should be being executed. The difference is where in memory the object is located - on the heap or on the stack, but I can't see that making a lot of difference. Unless you are doing something that corrupts the stack. Like, maybe a function is supposed to return a value, but there is no return statement. Moving the object onto the heap, by using new, prevents stack corruption from affecting it.

I don't really know. I'm just speculating, as you have an awful lot of complex (unnecessarily so) code to wade through.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi PaulS,

Thanks again for your thoughts. Hearing about the heap vs. stack gave me a direction to search for a solution.  I spent 3+ hours on it last night, but every time I thought I was narrowing in on the place of error, the location would change.  For example, a "byte" variable would be set, other code would be run, eventually this "byte" gets changed to a random value.  Somebody is overwriting that area of memory. When I narrowed it down to which statement caused this byte to change to a random number, comment it out & the code might work, but then some other variable in memory would get corrupted. Also just adding a new "temp" variable would change the location of the code which "causes" the corruption. (All this code was just slightly above the code for instantiating my class).  The ONLY thing that remained constant was when I instantiated my class with the "new" keyword, everything worked fine all the time. (and that's the instance that never even gets run at runtime.)

I have 5KB of free RAM at the time of the crashing, so the amount of RAM does not appear to be the issue.

I agree with you that my code is not pretty.  And it very well could be related to some bad code, but I'm pretty convinced right now that it has something to do with a compiler bug.  Main reason for this thought:

- The exact same code which causes the runtime failures when compiled with 1.5.7 IDE, runs perfectly fine when compiled with 1.0.5 IDE.

Unfortunately I was unable to prove this or able to pinpoint exactly where things are breaking. But I believe my code itself is ok.

Any other thoughts are welcome.
Logged

0
Offline Offline
Shannon Member
****
Karma: 221
Posts: 12710
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sounds like something corrupted the heap before the call to new.  If that
new is the only thing that is heap-allocating after initialization then a corrupted
heap might only show up thereafter if the allocated instance happens to alias an
existing object/array.

However you are using MemoryFree I believe so the heap can't be completely
smashed.

The problem with C is that its all too easy to write to somewhere random as
array bounds aren't checked, leaving a timebomb for later in the execution.
Logged

[ I won't respond to messages, use the forum please ]

Sweden
Offline Offline
Sr. Member
****
Karma: 11
Posts: 473
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Mark. If I understand you correctly you consider this to be a compiler error in Arduino 1.5.7 beta? If that is the case you should isolate the error and report it to the GCC avr team. The Ardunio team will not be able to help you more than confirm the compiler error and endorse your report. Best regards..
Logged

0
Offline Offline
Shannon Member
****
Karma: 221
Posts: 12710
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I never mentioned the compiler.  However is it different between 1.0.5 and 1.5.7?  Is it getting
different compile options between them?

Libraries - are they different?  I've only used 1.5.7 with the Due so haven't compared
1.0.5 with 1.5.7 directly at all.  Have you read all the release notes between 1.0.5 and 1.5.7?

Have you managed to get it repeatable with a small example?

You know 1.5.7 is in beta?  You may inadvertantly have become a beta-tester.
Logged

[ I won't respond to messages, use the forum please ]

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi @MarkT and @kowalski,

Thank you for more thoughts!  Just a clarification, when I instantiate the class with the "new" keyword (put it on the heap), everything WORKS. It's when I instantiate the class with the other method (not sure of the technical name) it doesn't work.  The "other" method is the one that looks like this:
 
Code:
MyClass myClass(x,y,z);

From what PaulS wrote, I understand that declaring the class this way puts it in RAM on the other end (Stack).  Not sure if this helps with debugging or not though.

I haven't been able to make it repeatable with a small example, unfortunately. The exact same class is instantiated successfully in OTHER areas of my code using the "MyClass myClass(x,y,y)" method.  That's how I instantiate most of my classes, but in 3 areas of my code, I cannot use that method - I need to use the "new" keyword.

Yes, I'm afraid I may have become a beta tester...  But I've already spent so much time trying to nail down this issue, and I feel like I don't have the tools and/or the knowledge, or time, to debug this type of issue.  I'll see what I can do though.

Also, I'm still only "suspecting" this to be a compiler bug, but it could certainly be a problem with my code, that just "happened" to work with the 1.0.5 compiler.

If anybody has any suggestions for how to go about debugging RAM space usage, and how to see if things are where they're supposed to be in RAM, and when they get overwriting - things like that - I'd appreciate them!  Thanks again.
Logged

0
Offline Offline
Shannon Member
****
Karma: 221
Posts: 12710
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That something works isn't proof there isn't a bug, only counterexamples are definitive.
So you always have to keep in mind the possibility that your code has a bug that happens,
so far, to only show up in 1.5.7.   There could also be a bug in 1.5.7.

Finding a simple piece of code that breaks on 1.5.7 only would enhance the probability of
the latter, finding your code falls over in 1.0.5 sometimes would imply the former. 

Its all detective work, trying to narrow down the possibilities - although some bugs are
due to complex interactions and are hard to isolate, which may be the case here.

Alas I can't find anything about running lint with the Arduino IDE, but you are checking
compiler warnings?
Logged

[ I won't respond to messages, use the forum please ]

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks MarkT,

I didn't know we could see compiler warnings until you mentioned it. So I turned them on. Ironically, I get a number of warnings in 1.0.5 (where my code runs fine), but NONE in 1.5.7 (where my code fails).  Most warnings (in 1.0.5) were because of using the F() function to store strings in Flash:
warning: only initialized variables can be placed into program memory area

A few like this:
warning: comparison between signed and unsigned integer expressions

A few unused variable warnings.

One like this:
warning: deprecated conversion from string constant to 'char*'

And 3 that caught my eye, like this:
warning: control reaches end of non-void function
warning: no return statement in function returning non-void
(though I fixed these 3, it did not solve the problem)

Just another data point:
- I downloaded and installed IDE 1.0.6 - and my code runs just fine, as it does in 1.0.5.
- I tried downloading 1.5.6 and earlier, but none of them work on my computer which uses OS X 10.9 (something about a java null pointer exception)
Logged

Pages: [1]   Go Up
Jump to: