Замяната на изтриване на C++ оператор не винаги се използва

Имам някои 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 не използва моята заменена функция за изтриване?


person Dtor    schedule 08.04.2018    source източник
comment
Защото ptr_ е nullptr?   -  person user202729    schedule 08.04.2018
comment
Добра мисъл, но не, иначе нямаше да имам проблеми с несъответствието. Току-що го стартирах отново в gdb и отпечатах ptr_, за да се уверя, че не е нула   -  person Dtor    schedule 08.04.2018
comment
Помислете дали да имате минимално възпроизводим пример, вместо да ни карате да гадаем какъв вид UB сте извикали някъде другаде.   -  person user202729    schedule 08.04.2018
comment
Абсолютно, публикувайте минимално възпроизводим пример.   -  person Jive Dadson    schedule 08.04.2018
comment
И какво има в заглавния файл? Какъв е ТЕСТЪТ (макро?)?   -  person user202729    schedule 08.04.2018
comment
Не е добра идея да се извежда към стандартни потоци при глобални претоварвания на оператори new и delete. Буферите, използвани от изходните потоци, могат (и често го правят) да използват претоварените оператори. Резултатът може да бъде безкрайно рекурсивен (извиква се operator delete(), който извиква операторите на потока, които извикват operator delete() и т.н.). Преминаването през рекурсивни функции с дебъгер може да бъде объркващо (напр. прекъсването на ред може да прекъсне само при дълбоко рекурсивно повикване, а не там, където очаквате).   -  person Peter    schedule 08.04.2018
comment
@user202729: заглавният файл е част от gtest библиотеката. Може да се изтегли онлайн, не мисля, че би било конструктивно за мен да публикувам целия изходен код на gtest.   -  person Dtor    schedule 08.04.2018
comment
@Питър, да, това е добра точка, повярвайте ми, попаднах на безкрайната рекурсия, докато правех това. Но с начина, по който отпечатах, това не причинява този тип рекурсия, програмата работи до завършване   -  person Dtor    schedule 08.04.2018
comment
@user202729 Предпоставката на проблема е за свързване във външна библиотека. Нямам проблем, ако не направя връзка във външна библиотека, но и в този случай нямам нищо полезно. Трябва ли просто да не задавам въпроси, които са сложни?   -  person Dtor    schedule 08.04.2018
comment
Хмм... не можете да го възпроизведете без да включите библиотеката? Добре тогава, може да е грешка/функция/и т.н. с библиотеката. Можете да запазите въпроса в текущото му състояние, но (също) попитайте във форума на библиотеката може да е по-добре.   -  person user202729    schedule 08.04.2018
comment
Не мисля, че това има нещо общо със самия google-test. Погледнах кода в google-test, който не прави това, което очаквах (и публикувах този блок от код във въпроса). По-вероятно има нещо в начина, по който го свързвам, което го кара да не използва моята заменена функция, поради което публикувах в стека, надявайки се, че някой може да хвърли светлина върху това, което не разбирам погрешно относно свързването/пренастройването.   -  person Dtor    schedule 08.04.2018
comment
Ако проблемът идва със свързване към външна библиотека, обяснението може да е, че библиотеката е компилирана/свързана и използва свои собствени (напр. по подразбиране за вашата реализация) оператори new и delete. Въпреки че стандартът изисква всички компилационни единици в една програма да използват едни и същи версии на тези функции (т.е. това, което правите, теоретично не е проблем), някои реализации са по-малко умни в това отношение, когато се използват библиотеки за връзки. Може да искате да проверите дали е известно, че mingw има такива проблеми (не знам направо).   -  person Peter    schedule 08.04.2018
comment
@Peter благодаря, че ме насочи в тази посока, успях да намеря и потвърдя, че mingw наистина има грешка, и успях да намеря заобиколно решение   -  person Dtor    schedule 09.04.2018


Отговори (1)


Изглежда, че това е грешка в MinGW: MinGW грешка #634

Заобиколно решение е да свържете статичната версия на libstdc++, вместо да й позволите да свърже динамичната библиотека. Не е най-идеалното решение, но е достатъчно добро за моите модулни тестове и ми позволява да отменям правилно.

Промених чрез команда за компилиране/свързване към следното, за да направя това:

/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 /cygdrive/c/cygwin64/lib/gcc/x86_64-w64-mingw32/6.4.0/libstdc++.a

Много благодаря на Питър, че ме насочи по правилния път към намирането на това.

person Dtor    schedule 09.04.2018