Мой код ниже предназначен для простой системы регистрации ошибок, которая ведет себя так же, как printf.
Весь мой код работал нормально в среде gtest, но теперь, когда я выхожу из детерминированной точки в программе (один из моих тестов), она ломается. Мой код работал нормально, пока я не написал это, и это мой первый набег на cstdarg, так что это наиболее подозрительно.
MyMutex error_lock;
#define MAX_ERR_MSG_SIZE 128
#define MAX_ERRORS 3
class ErrorQueue
{
std::queue<char*> errors;
std::list<char*> old_errors;
public:
void push(char * msg)
{
if (errors.size() >= MAX_ERRORS)
{
pop();
}
errors.push(msg);
}
void pop()
{
if (old_errors.size() >= MAX_ERRORS)
{
delete [] old_errors.front();
old_errors.pop_front();
}
old_errors.push_back(errors.front());
errors.pop();
}
char * front()
{
return errors.front();
}
size_t size()
{
return errors.size();
}
~ErrorQueue()
{
while(!errors.empty())
{
delete [] errors.front();
errors.pop();
}
while (!old_errors.empty())
{
delete [] old_errors.front();
old_errors.pop_front();
}
}
};
static ErrorQueue errors;void WriteCallError(const char * error_message, ...)
{
char err_buffer[MAX_ERR_MSG_SIZE];
va_list args;
va_start(args,error_message);
std::vsnprintf(err_buffer,MAX_ERR_MSG_SIZE,error_message,args);
va_end(args);
char * err_string = new char[MAX_ERR_MSG_SIZE];
memcpy(err_string,err_buffer,MAX_ERR_MSG_SIZE);
{
error_lock.Lock();
errors.push(err_string);
error_lock.Unlock();
}
}
Я вызываю WriteCallError много раз в другом месте кода, и через определенное количество раз он рвется и сообщает мне, что стек разбит.
Где моя вина? Есть ли какое-то забавное взаимодействие между cstdarg и gtest? Здесь достаточно информации?
Редактировать:
Используя простое основное, я попытался выделить его:
int main (int argc, char ** argv)
{
int i = 0;
while (1)
{
WriteCallError("Breaks on %d",i++);
}
}
Это не приведет к разрушению стека.
Я думаю, что ваша проблема в том, что vsnprintf не записывает завершающий нулевой символ в массив, если данные, которые вы пишете, выходят за указанный вами предел. Когда вы позже получите доступ к этой C-строке, так как терминатора нет, она будет читать за пределами допустимых данных и в неизвестную память.
Вы не заметили это в своем тесте, потому что не превышаете ограничение в 128 символов.
Быстрое исправление состоит в том, чтобы гарантировать правильное завершение массива после использования vsnprintf:
void WriteCallError(const char * error_message, ...)
{
char err_buffer[MAX_ERR_MSG_SIZE];
va_list args;
va_start(args,error_message);
std::vsnprintf(err_buffer,MAX_ERR_MSG_SIZE,error_message,args);
va_end(args);
// Fix here
err_buffer[MAX_ERR_MSG_SIZE - 1] = 0;
char * err_string = new char[MAX_ERR_MSG_SIZE];
memcpy(err_string,err_buffer,MAX_ERR_MSG_SIZE);
{
error_lock.Lock();
errors.push(err_string);
error_lock.Unlock();
}
}
Отвечая, чтобы я мог закрыть вопрос, так как я решил ошибку:
Это произошло в коде, похожем на это:
TEST(MyTest,Case1)
{
MyStruct1 object1;
memset(&object1,0,sizeof(object1));
MyStruct2 object2[1];
memset(&object2[0],0,sizeof(object1));
object1.object2_ptr = object2;// DO SOME TESTING
}
Так как object1 & object2 — переменные стека, а object1 больше, чем object2. Я слишком долго выделял память, когда записывал memset. Когда я оставил часть этой функции в стеке, указатель стека закричал, что происходит что-то сумасшедшее, и сказал, что я разбиваю стек.
Простое исправление:
TEST(MyTest,Case1)
{
MyStruct1 object1;
memset(&object1,0,sizeof(MyStruct1));
MyStruct2 object2[1];
memset(&object2[0],0,sizeof(MyStruct2));
object1.object2_ptr = object2;// DO SOME TESTING
}