C++: unexpected crash when pushing/popping a vector of strings

Started by
15 comments, last by MaulingMonkey 15 years, 10 months ago
I'm rather perplexed on the cause of this bug and I just had a co-worker come over and he couldn't find anything wrong with it either. Its really simple. I have a class that has an internal std::vector<std::string> member, and two public functions that allow you to push and pop strings from that vector. Here's the relevant snippets from the code:

// Header file
class LogManager
{
public:
   /** \brief Adds a function name to the call stack, intended to be called upon function entry
   *** \param name The name of the function being entered
   **/
   void LogFunctionEntry(std::string name);
   
   //! \brief Removes the most recent function entry that was logged
   void LogFunctionExit();

private:
   //! \brief Maintains a call stack of function names, used for debugging purposes on error
   std::vector<std::string> _call_stack;
};


// Source file

void tLogManager::LogFunctionEntry(string name)
{
   _call_stack.push_back(name);
}


void tLogManager::LogFunctionExit()
{
   if (_call_stack.empty() == true)
   {
      Warning(__FILE__, __LINE__, "attempted to log a function exit when no function entries "
         "were on the call stack\n");
   }
   else
   {
      _call_stack.pop_back();
   }
}


// Use case
void foo
{
   Log->LogFunctionEntry("entered foo");
   
   ......

   Log->LogFunctionExit();
}

Looks simple enough, doesn't it? The application that uses this code is a test suite that executes tests multiple times in a loop. Sometimes on a random iteration through that loop, the application will crash after calling LogFunctionExit(). The warning message in that function is never printed in the crash case (nor in the non-crash case). Somehow, popping a string from the back of a non-empty vector induces a crash on Windows. I ran windbg on the app and it didn't give me much useful information. In the call stack I see it in the LogFunctionExit function and then further down its in some STL code. What's even odder is that the crash occurs on the exact same call to LogFunctionExit() each time. There's no dubious code around the location of the crash at all, so I don't see how that could be at fault. I'm scratching my head at this point trying to figure out exactly what is causing this random crash, hence why I'm posting here to see if anyone else has some insight. My questions are: 1) Do you see anything wrong with the above code and/or usage example that could be causing this crash? 2) On a related note, would it be better if my argument to LogFunctionEntry was a const string reference instead of just a string pass-by-value? Thanks in advance

Hero of Allacrost - A free, open-source 2D RPG in development.
Latest release June, 2015 - GameDev annoucement

Advertisement
Are you using an instance of this class shared among more than 1 thread?
Quote:Original post by rip-off
Are you using an instance of this class shared among more than 1 thread?


Yes. The class has been used between multiple threads for some time now. It wasn't until I added these two functions and began using them that I started to see this crash.

Hero of Allacrost - A free, open-source 2D RPG in development.
Latest release June, 2015 - GameDev annoucement

Are you sure it happens directly because of this call? Two possibilities: an object destructor could crash, or memory corruption from previous undefined behaviour could happen to affect the vector's book-keeping data.
Modifying shared state across threads isn't safe. You need to implement some kind of protection so the threads don't thrash the data. Look into threading primitives. Alternatively, you could have a FunctionEntryLogger per thread, if you wanted.
Are you taking steps to ensure multiple threads don't try to access your log manager simultaneously?
I have synchronization objects protecting other shared data in my program but not my log manager. But I agree that the problem has to be due to the multi-threaded nature of this application, so I think now I can figure out how to make the necessary changes. Thanks everyone

Hero of Allacrost - A free, open-source 2D RPG in development.
Latest release June, 2015 - GameDev annoucement

Just a question, not directly related to your problem. What happens if a user does this:
void foo() {  Log->LogFunctionEntry( "foo" );  throw SomeException();  Log->LogFunctionExit();}
The function will exit, but it won't be popped from the logger's call stack.
Yeah a RAII guard would help.

class LogMessageGuard{public:    LogMessageGuard( const std::string& functionName, LogManager& manager ) : m_manager( manager )    {        m_manager.LogFunctionEntry( functionName );    }    ~LogMessageGuard()    {        m_manager.LogFunctionExit();    }private:    LogManager& m_manager;};void foo{   LogMessageGuard guard( "entered foo", *Log );         ......}
style comment:
"if (x == true)" is always redundant. You can just do "if (x)" and save evaluating whether (true == true) is true, or not.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

This topic is closed to new replies.

Advertisement