gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

C/C++ > Find this bug, win a Nintendo DS!

#38491 - cesium - Mon Mar 28, 2005 4:27 pm

I really need to squish this bug, and I've got a DS that I rarely use so... if you are the first person to find the root cause of the problem described below, I'll send you a Dual Screen. See the comments for details. Yell if you need the makefile.
cesium
Code:

//main.cpp
/*
   This code illustrates a problem I'm having with devkitarm-r11.
   I don't know if it is a bug in the compiler, or a bug in my code.
   I have distilled my code into the following templated class: DataSet,
   and a templated function: genDataSet.
   I think these are the bare minimum structures that produce the problems.
   I have observed this problem on actual hardware and in VisualBoyAdvance-SDL-1.7.2.

   The problem seemed to morph as I was chopping down the code. Here is a
   description of the current form as I have seen it using the Insight debugger
   connecting to VisualBoyAdvance-SDL-1.7.2.

   First off, a few definitions:
      I'll use the term "Step into" to refer to Insight's Step(S) function.
      I'll use the term "Step over" to refer to Insight's Next(N) function.
      I'll use the term "Step out of" to refer to Insight's Finish(F) function.
      
   -Compile and load up the code in VisualBoyAdvance and Insight.
   -Have Insight connect to VBA and run up until the line commented as: POINT A.
   -This is an assignment statement that calls the genDataSet function.
      The strange stuff happens during this assignment. The way I watch this
      assignment happen is to step into the function and then "step into the
      return process." That sounds odd, but I'll describe it.
   -Step into the function genDataSet. Run up until the line: POINT B.
   -Step into this return.
      You'll end up in a DataSet destructor. Step out of this destructor.
   -You're back at POINT B. Step into it again.
      You'll be in some odd looking code that says Unwind. Step out.
   -You're back at POINT B. Step in again.
      Now we're someplace interesting, the assignment function DataSet<T>::operator=()
   -Step over twice so that you get to POINT C, where the new operator is called.
   -Step into the new operator.
      Something odd happens, we end up at the bottom of main(). Is this bad? A debugger flaw?
      Step out of this point at the end of main.
   -We're back at POINT C. Step into the new operator again.
   -Now we get to the good part. Here we are at POINT D in the DataSet constructor.
      Step over the assignment: m_allocUnit=10
      I find that m_allocUnit now contains 0, not 10.
   -Step into POINT E, the call to NEW.
   -I get a SIGTRAP message from the debugger. I have no idea what's going on.
   -Step into the NEW function. n is not 0, it is not 10, it is 2560!! huh?

  If you can help me determine if this is a compiler or a programmer problem,
  I'd be very grateful.

  I'm interested in solutions like:
   "Hey, you dummy, you can't to that in C++, it's not allowed, here's why..."
   or
   "Yea, this is a known bug in gcc ver x.y.z, here's a workaround.

  I'm not interested in solutions like:
   "Why don't you just write your code like this, <snip> and then the
   problem goes away!"

  I'd like to know the root cause of this problem, since I have a few
  thousand lines of code that "boil down" to this example.

  Thanks,
  cesium

*/

//templated NEW function to help debug strange values passed to new.
template<class T> T* NEW(unsigned int n);
//templated container class.
template <class T> class DataSet  {
public:
   DataSet();
   DataSet(const DataSet& x);
   DataSet<T>& operator=(const DataSet<T>& x);
   ~DataSet();
private:
   unsigned int m_allocUnit;
   T* mp_data;
};
template <class T> DataSet<T>::DataSet() {
   m_allocUnit=10;//POINT D
               //1 to 9 seem to work, but 10 and above do not.
               //If this value is 10, the debugger shows it getting a
               //value of 0, but the call to NEW below gets 2560. doh!

   //mp_data=new T[m_allocUnit];//Usual way of allocating RAM,
                        
   mp_data=NEW<T>(m_allocUnit);//I used my NEW function to see what's happening.
}
template <class T> DataSet<T>::DataSet(const DataSet<T>& x) {
   m_allocUnit=x.m_allocUnit;
   mp_data=new T[m_allocUnit];
}
template<class T> DataSet<T>& DataSet<T>::operator=(const DataSet<T>& x) {
    if (&x != this) {
      m_allocUnit=x.m_allocUnit;
      T* p_t=new T[m_allocUnit];//POINT C
      delete mp_data;
      mp_data=p_t;
   }
   return *this;
}
template <class T> DataSet<T>::~DataSet() {
   delete mp_data;
}

//A templated function that returns a DataSet.
template<class T> DataSet<T> genDataSet(void);

int main(void) {
   //set is a DataSet of DataSets. You might do this to create a 2 dimensional vector.
   DataSet<DataSet<unsigned int> > set;
   set=genDataSet<DataSet<unsigned int> >();//POINT A
   return 0;
}
template<class T> DataSet<T> genDataSet(void) {
   DataSet<T> ret;
   DataSet<T> tSet;
   ret=tSet;
   return ret;   //POINT B
}
//Diagnostic routine to look at value passed to new.
template<class T> T* NEW(unsigned int n) {
   if (n==2560) {   //sometimes wierd values get passed to NEW.
      unsigned int dummy=0;
      while (1) {
         dummy++;   //trap for goofy value, when m_allocUnit was supposed to be 10.
      }
   }
   return new T[n];
}


#38521 - sajiimori - Mon Mar 28, 2005 9:51 pm

You're not using array delete in operator= and the destructor which means you've got a memory leak, but I don't know if that's related to the problem you're seeing.

#38527 - cesium - Mon Mar 28, 2005 10:53 pm

Quote:

You're not using array delete in operator= and the destructor which means you've got a memory leak, but I don't know if that's related to the problem you're seeing.


Did I botch the operator= ? This object contains a pointer to a list of T.
When you copy a DataSet, you allocate new memory, and release the old.
Code:

      T* p_t=new T[m_allocUnit];//POINT C
      delete mp_data;
      mp_data=p_t;
 

(I did not show the copy process where the new memory is filled from x.)

When the DataSet goes out of scope, it releases its memory via
Code:

delete mp_data

in the destructor. So I'm a little confused by your statement. Honestly, I've been looking at code for the past 4 days and it's all starting to look like gummie bears.

I'm happy to have you looking at my code, I'm really stumped.
Cheers,
cesium

#38540 - sajiimori - Mon Mar 28, 2005 11:33 pm

If you allocate an array like this:
Code:
Type* array = new Type[size];
then you must free it like this:
Code:
delete[] array;
If you don't use the brackets when deleting, the behavior is undefined and may vary between compilers. One common result is a memory leak.

The reason you have to use array delete is because it cannot be known at compile time how many objects there are, and the destructor has to be run on every object.

#38541 - cesium - Mon Mar 28, 2005 11:46 pm

Incredible! I haven't seen 'delete[]' in years. I totally forgot about it. I just looked at my MSVC 6.0 code that I use daily, and I don't use it anywhere! The debugger in MSVC doesn't say I get leaks, but I probably should do a quick survey and fix this.

I'll also do this for the GBA to see if I should start packing up the DS!
cesium

#38544 - cesium - Tue Mar 29, 2005 12:19 am

Hail sajiimori!
Oh man, a weight has been lifted off my back.
PM me your shipping address. The UPS store closes at 6pm Mountain.
cesium