У меня есть несколько модульных тестов С++, использующих тест Google. Собрал некоторый код для переопределения операторов new/delete для проверки утечек в модульных тестах. Однако есть проблема. Некоторые из новых/удаляемых тестов Google используют мои переопределенные методы, а некоторые нет, поэтому я получаю ложные ошибки в коде отслеживания - иногда вижу, что память была утеряна, хотя она действительно была удалена, а иногда вижу, что malloc возвращает
Вот мои минимальные новые/удаленные переопределения (просто печатает адреса для ручной проверки):
void * operator new(size_t size)
{
void * addr = malloc(size);
std::cout << " tracking create: " << addr << "(size " << size << ")" << std::endl;
return addr;
}
void * operator new[](size_t size)
{
void * addr = malloc(size);
std::cout << " tracking create: " << addr << "(size " << size << ")" << std::endl;
return addr;
}
void operator delete(void * addr) noexcept
{
std::cout << " tracking delete: " << addr << std::endl;
free(addr);
}
void operator delete[](void * addr) noexcept
{
std::cout << " tracking delete: " << addr << std::endl;
free(addr);
}
А вот тестовая строка Google, которая НЕ проходит через мое переопределенное удаление (gtest-port.h):
void reset(T* p = NULL) {
if (p != ptr_) {
if (IsTrue(sizeof(T) > 0)) { // Makes sure T is a complete type.
delete ptr_;
}
ptr_ = p;
}
}
Когда я прерываю строку delete ptr_
в gdb, а затем шаг, он переходит непосредственно к строке ptr_ = p
, поэтому нет ничего другого, что переопределяло бы это удаление.
Я создаю gtest как файл архива и связываю его, когда я создаю свои модульные тесты. В случае, если это имеет значение: я занимаюсь сборкой Windows с помощью mingw, используя cygwin.
Вот минимальный пример, 2 файла min.cpp и minmain.cpp. Вот мин.cpp:
#include <iostream>
#include <string>
// Overload the new/delete operators to check for memory errors
void * operator new(size_t size)
{
void * addr = malloc(size);
std::cout << " tracking create: " << addr << "(size " << size << ")" << std::endl;
return addr;
}
void * operator new[](size_t size)
{
void * addr = malloc(size);
std::cout << " tracking create: " << addr << "(size " << size << ")" << std::endl;
return addr;
}
void operator delete(void * addr) noexcept
{
std::cout << " tracking delete: " << addr << std::endl;
free(addr);
}
void operator delete[](void * addr) noexcept
{
std::cout << " tracking delete: " << addr << std::endl;
free(addr);
}
minmain.cpp:
#include "gtest/gtest.h"
TEST(MinTest, MinimalTest)
{
int test = 5;
test++;
test++;
test++;
ASSERT_EQ(test, 8);
}
int main(int argc, char *argv[])
{
char* t = new char();
t[0] = 't'; std::cout << "t is " << t[0] << std::endl;
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
скомпилировано с:
/bin/x86_64-w64-mingw32-g++.exe -std=c++11 -D_USE_MATH_DEFINES -g -Wall -I../third_party/googletest-1.8.0/googletest/include -c min.cpp -o min.o
чтобы создать min.o, затем скомпилируйте main и свяжите все вместе с помощью:
/bin/x86_64-w64-mingw32-g++.exe -std=c++11 -D_USE_MATH_DEFINES -g -Wall -I../third_party/googletest-1.8.0/googletest/include -o minmain minmain.cpp min.o ../third_party/googletest-1.8.0/googletest/make/gtest_main.a
Используя gtest версии 1.8.0, сделайте перерыв в gtest-port.h:1145, чтобы перейти к строке delete ptr_
, затем выполните шаг.
Вот пример вывода из приведенного выше примера (первые несколько строк вывода):
tracking create: 0x30e4c0(size 392)
tracking create: 0xa477e0(size 392)
tracking create: 0xa477e0(size 392)
tracking create: 0xa477e0(size 392)
tracking create: 0xa477e0(size 392)
tracking create: 0xa47b80(size 28)
tracking delete: 0xa47b80
Тот факт, что я получаю отслеживаемые создания на том же адресе без отслеживаемых удалений между ними, является проблемой, потому что между ними были удаления, позволяющие снова выделить тот же адрес, но эти удаления не прошли через мой переопределенный оператор удаления.
Почему эта строка delete ptr_;
в gtest не использует мою переопределенную функцию удаления?
ptr_
этоnullptr
? - person user202729   schedule 08.04.2018ptr_
, чтобы убедиться, что он не нулевой. - person Dtor   schedule 08.04.2018new
иdelete
. Буферы, используемые выходными потоками, могут (и часто используют) использовать перегруженные операторы. Результат может быть бесконечно рекурсивным (вызываетсяoperator delete()
, который вызывает операторы потока, которые вызываютoperator delete()
, и т. д.). Пошаговое выполнение рекурсивных функций с помощью отладчика может сбивать с толку (например, разрыв строки может прерываться только при глубоко рекурсивном вызове, а не там, где вы ожидаете). - person Peter   schedule 08.04.2018new
иdelete
. Хотя стандарт требует, чтобы все модули компиляции в программе использовали одни и те же версии этих функций (т. е. то, что вы делаете, теоретически не является проблемой), некоторые реализации менее умны в этом отношении, когда используются библиотеки ссылок. Возможно, вы захотите проверить, есть ли у mingw такие проблемы (я не знаю навскидку). - person Peter   schedule 08.04.2018