Jump to content
  • Advertisement
Sign in to follow this  
Roots

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

This topic is 3665 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
Advertisement
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Are you taking steps to ensure multiple threads don't try to access your log manager simultaneously?

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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 );


......
}


Share this post


Link to post
Share on other sites
style comment:
"if (x == true)" is always redundant. You can just do "if (x)" and save evaluating whether (true == true) is true, or not.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!