views:

617

answers:

11

Hi, I have a c++ code, I use MSC9 to compile it. It keeps crashing randomly. For example it crashes if it is called from Perl using `` but it does not crash when It is called from command line or from Ultimate++.

I mean calling it from perl eg. f.exe arg1 arg2 arg3

Stack trace does not show much. Tracing the program line by line proved that the program fails at the end when returning...

So it is like that

int funcname()
{
    return 0; <-- crashing after that...
}

I guess the stack is damaged, and after the stack is unwound, it crashes..

What can cause it? The program uses pcre, stl and iterators. Can an iterator break the stack? How would you catch an error like that?

Can it be a compiler bug?

Note: the debug version does not crash, only the release version...

The bug seems to be related this pvector class.

I have a struct similar to this:

struct complexstr
{
 pvector<int> v;
 string v2;
 hash_map<string> hm;
 vector<string> vs; // similar
 int i;
};

It seems to fail because this line:

complexstr s1;
complexstr s2;

s2=s1; // it seems to fail here, if this is not there... there is no error.

I think the problem is with the class below... std::copy is correct in pvector operator=(const pvector &pv), right?

pvector is a perl compatible vector... Its indexes can be larger than allocated size of the vector.

Update1: I received suggestions that there is a leak in the assignment. I changed the assignment... That is how it looks now:

 pvector& operator=(const pvector &pv)
  {
    delete [] m_rgArray;  
    m_rgArray=new value_type[pv.allocated];
    m_nIndex=pv.m_nIndex;
    allocated=pv.allocated;
    std::copy(pv.m_rgArray, pv.m_rgArray + pv.allocated, m_rgArray);  
    return *this;
  }

Note: by adding & to the return type, the crash still remained. However, after removing the leak, adding delete [] m_rgArray; , the program no longer crashes. I do not understand. As far as I know leaks do not cause crashes. So the problem seems to be solved(?). The question mark shows my surprise. Update2: No, the problem came back. It just disappeared for a while. Update3: I think I have found it. I used an utility from Microsoft debugging tools called gflags.exe and windbg.exe to find the exact location. I used gflags.exe /p /enable myprog.exe /full to turn on exceptions for heap bugs.. At the moment, I think the bug was caused by FindClose(handle); where handle was a random value, not initialiased.

Old version:

 template<class _Ty>
  class pvector
  {
    public:
    _Ty * m_rgArray; // Declare array
    int m_nIndex; // Index to array
    int allocated;
    _Ty undefvalue;
    typedef _Ty value_type;
    typedef value_type & reference;
    typedef const value_type & const_reference;
    typedef custom_iterator<_Ty> iterator;
    typedef custom_iterator<_Ty> const_iterator;
    typedef int difference_type;
    typedef int size_type;
    //typedef typename pvector_type_traits<_Ty>::default_value default_value;

    pvector() : m_nIndex(0) 
    { // init index to 0
      m_rgArray = new value_type[10];
      allocated = 10;
      fill(0);
    }

    pvector(size_type s) : m_nIndex(0) 
    { // init index to 0
      size_type defsize = 10;
      if (s>10)
      {
        defsize = s;
      }
      m_rgArray = new value_type[defsize];
      allocated = defsize;
      fill(0);
    }
      pvector(pvector const& pv)
    : m_rgArray(new value_type[pv.allocated]),
    m_nIndex(pv.m_nIndex),allocated(pv.allocated)
    {
     std::copy(pv.m_rgArray, pv.m_rgArray + pv.allocated, m_rgArray);     
    }

    pvector operator=(const pvector &pv)
    {
    m_rgArray=new value_type[pv.allocated];
    m_nIndex=pv.m_nIndex;
    allocated=pv.allocated;
    std::copy(pv.m_rgArray, pv.m_rgArray + pv.allocated, m_rgArray);  
    return *this;
    }
    void clear()
    {
       m_nIndex=0; 
       fill(allocated);    
    }

    ~pvector() {
     delete []m_rgArray; 
    }

    size_type size() const
    { // return length of sequence
      return m_nIndex;
    }

    size_type max_size() const
    { // return maximum possible length of sequence
      return 0;
    }

    void fill(size_type si)
    {
      for (size_type i = si;i<allocated;i ++ )
      {
        m_rgArray[i] = pvector_type_traits<_Ty>::default_value();
      }
    }

    bool empty() const
    { // test if sequence is empty
      return (m_nIndex > 0 ? false : true);
    }

    iterator begin()
    { // return iterator for beginning of mutable sequence
      return iterator(&m_rgArray[0]);
    }

    const_iterator begin() const
    {
      return const_iterator(&m_rgArray[0]); 
    }

    iterator end()
    { // return iterator for end of mutable sequence
      return iterator(&m_rgArray[m_nIndex]);
    }

    const_iterator end() const
    {
      return const_iterator(&m_rgArray[m_nIndex]);
    }
    reference operator[](size_type i)
    {
      if (m_nIndex>i)
      {
        return m_rgArray[i];
      }
      else if (i >= allocated)
        {
          resize(i * 2);
        }
        m_nIndex = i + 1;
      return m_rgArray[i];
    } 
    void resize(size_type s)
    {
      value_type * m_rgArray2;
      size_type old_allocated = allocated;
      allocated = s;
      m_rgArray2 = new value_type[allocated];
        //if (allocated>m_nIndex)
        //{
        // m_nIndex=allocated;
       // }
       // cout <<"m_nIndex" << m_nIndex << "allocated" << allocated << endl;
      if (m_nIndex>allocated)
      {
        m_nIndex=allocated;
      }
      for (size_type i = 0;i<m_nIndex;i ++ )
      {
        m_rgArray2[i] = m_rgArray[i];
      }
      delete []m_rgArray;
      m_rgArray = m_rgArray2;
      fill(old_allocated);
    }

    reference back()
    {
      return &m_rgArray[m_nIndex - 1]; 
    }

    const_reference back() const
    {
      return m_rgArray[m_nIndex - 1]; 
    }

    void push_back(const _Ty &_Val)
    { // insert element at end
      if (size() < allocated)
        m_rgArray[m_nIndex ++ ] = _Val;
      else
        {
        resize(allocated * 2);
        m_rgArray[m_nIndex ++ ] = _Val; 
      }
    }

  };
+9  A: 

It can be a buffer overrun corrupting the stack. If while running the function writes outside the locally defined buffers it can overwrite the return address and then returning from the function will trigger a program crash.

You should look for statements that operate with addresses of local (stack-allocated) variables - buffer overruns on them are most likely the cause of the problem.

sharptooth
+3  A: 

Possibilities I can think of:

  • Different iterator / stl checking debugging settings between your project and whatever it is linking to. See Debug Iterator Support and Checked Iterator.
  • Different CRT settings between your project and whatever it is linking to. Use Dependency Walker to see mismatches.
  • Stack corruption because of bad code in the function, for example writing past the end of an array or string.
  • A multithreading issue causing corruption of the stack or variables.
  • Mismatching calling conventions (as you mentioned calling it from Perl)
MadKeithV
I do not allocate arrays on the stack, I think as I use vector stl class everywhere.
Aftershock
So that it cannot be an array overrun, can it ?
Aftershock
Why? I am calling it from Perl as a command line tool. Where is calling convention in there? There are just command line arguments.
Aftershock
Unless you post the actual code none of the above can be fully eliminated (albeit for different reasons than mentioned).
MadKeithV
A: 

Inside your function (funcname) or in one of the functions called by funcname you might have some code that corrupts the stack.

Most "practiced" stack corruption I encountered are memory copies or string copies, when the allocated target buffer doesn't fit the size of the data to be copied (misscalculation of size using sizeof and number of elements, not considering the end-of-string terminator when sizing the target buffer for string copying).

Another possibility, less often encountered: pointer aritmetics, when refering local variables or arrays and changing them through that pointer.

Cătălin Pitiș
+2  A: 

Do you have any stack-based objects with non-trivial destructors? Depending on your debugger it may be hard to tell when these are executing. It could be something related to that, in addition to everything mentioned in the other comments.

Mongoose
+1  A: 

I'm assuming the following function has more code to it int funcname() { return 0; <-- crashing after that... }

When the function returns, any stack variables will have their destructors called. It is likely that the crash is happening in one of the destructors.

How to track it down:- Put breakpoints in the destructors of any variables declared in the function . Step through each destructor. Keep in mind that when a destructor gets called, there's a whole chain of destructors from base classes that will automoatically get called and the corruption could be in any one of those.

Rich
A: 

Since your details are not specific on the crash I would suggest to debug the application using your IDE: In ProjectProperties->ConfigurationProperties->Debugging set the Command and Command Arguments as your perl / ultimate++ applications. Compile in debug mode and put breakpoints where you suspect the crash. It should fairly simple to locate the problem and come up with a meaningful message on the crash itself.

Hope this helps.

da_m_n
note: it did not crash in Ultimate++ Ide.. only when it was called from Perl...
Aftershock
I don't know what is wrong - i just said how you can debug the application. Since in one instance it works I would suspect the input parameters not being what you expect them to be.
da_m_n
A: 

Your assignment operator should be returning a reference:

pvector& operator=(const pvector &pv)  {

I doubt that would cause the problem, but give it a shot.

Kieveli
I changed that, it did not solve the problem.
Aftershock
A: 

What does the debugger say? After the return line, it runs through all the destructors for objects going out of scope, and one of them is almost certainly screwing up. So put a breakpoint at the return line, and then step through with the debugger until you get to the crash.

jalf
A: 

My guess is some problem with your pvector overrunning an array during the resize. I'm trying to read the code to find if that's true, but I don't see anything obvious. If all you really want is a vector that grows to fit whatever index is accessed, you don't need to write the whole thing yourself. You can extend std::vector instead and just use the reserve()/resize() methods and let STL handle all the copying and memory management and iterators. The following should work:

template<typename StoredType>
class pvector : public std::vector<StoredType>
{
public:
    typedef typename std::vector<StoredType, std::allocator<StoredType> >::reference reference;
    typedef typename std::vector<StoredType, std::allocator<StoredType> >::size_type size_type;

    reference at(size_type n)
    {
        size_type need = n+1;
        if(need > std::vector<StoredType>::capacity())
        {
            std::vector<StoredType>::reserve(need * 2);
            std::vector<StoredType>::resize(need);
        }
        else if(need > std::vector<StoredType>::size())
        {
            std::vector<StoredType>::resize(need);
        }
        return std::vector<StoredType>::at(n);
    }

    reference operator[](size_type n)
    {
        return at(n);
    }
};

I tested it with GCC 4.1.2 on Linux, so hopefully it'll compile on Windows too.

Edit: Here's a new version that doesn't inherit, since it seems experts agree it's not a good idea (I learned something new, yay). You can implement the rest of the methods you want and just pass-through to m_vector:

template<typename StoredType>
class pvector
{
public:
    typedef StoredType& reference;
    typedef int size_type;

    reference at(size_type n)
    {
        int need = n+1;
        if(need >= m_vector.capacity())
        {
            m_vector.reserve(need * 2);
            m_vector.resize(need);
        }
        else if(need >= m_vector.size())
        {
            m_vector.resize(need);
        }
        return m_vector.at(n);
    }

    reference operator[](size_type n)
    {
        return at(n);
    }

    size_type capacity() { return m_vector.capacity(); }
    size_type size() { return m_vector.size(); }

private:
    std::vector<StoredType> m_vector;
};
CodeGoat
I did not do this because I read somewhere that stl is not written well enough to inherit it /extend it
Aftershock
Hmm... I guess you're referring to the "virtual destructor" problem. That's the only thing I can find that says why it's bad to inherit from STL. If you're worried about it, the above could be implemented by having the vector contained inside the class instead of inheriting from it. Rewriting everything that vector already does seems like a waste of effort to me.
CodeGoat
Also, I think that as long as you don't try to pass the above class off to something that takes a normal vector (no polymorphism) you will be fine.
CodeGoat
ok. I may use it next time.
Aftershock
+2  A: 

There is a lot wrong with this code:

  • naming - I already pointed out the problem with _Ty, but why do some members begin with m_ and others not. and some local variables also begin with m_. Not good.

  • Assignment op doesn't return a reference - as already pointed out.

  • Assignment op has a memory leak, at the first line, you assign to m_rgArray, which already has contents - these are leaked.

That's for starters. None of these should cause the crash, but they all need fixing. To fix all problems, I would start again, write one function at a time, plus a test for it. Run the test and if it works, write the next function, the next test, and so on. It is worth putting a lot of effort into template classes like this, because if they are right, they are very useful, but if wrong the source of continual pain.

anon
Agreed, and when writing those test, be sure to test the big three specifically. Test that you handle assignment, copy construction and destruction properly, that all the required dtors get called, and that nothing is leaked or double-deleted.
jalf
+1  A: 

Another comments about your operator=

  • You are not handling self assignment properly
  • In the same operator I think you are leaking, I would suggest you to use boost::scoped_array instead of a normal pointer.

I don't know if it is this what is giving you problems but due to it seems you have a memory corruption issue it might be something to star with.

fco.javier.sanz