Имам някои C++ модулни тестове, използвайки google test. Събрахме малко код, за да заменим операторите new/delete, за да проверим за течове в тестовете на модула. Все пак има проблем. Някои от google test new/deletes използват моите заменени методи, но някои не го правят, така че получавам фалшиви грешки в кода за проследяване -- понякога виждам, че паметта е изтекла, въпреки че наистина е била изтрита, а понякога виждам, че 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. Ето min.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
Използвайки версия 1.8.0 на gtest, прекъснете в 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