OpenCV 3.0 + Visual Studio Memory Leak Detector фалшив положителен резултат

Бих искал да създам проект с активиран детектор за изтичане на памет на Visual Studio (Детектор за изтичане на памет)

Винаги работеше добре и можех лесно да намеря течове на памет, като пусна куп тестове на моето приложение и след това проверих отчета.

Но след статично свързване на OpenCV 3.0 с моя проект получих някои фалшиви положителни резултати.

Например най-разочароващата грешка идва от StereoBMImpl::compute метод и извикване: ocl::useOpenCL()

След отстраняване на грешки открих източника на "теча":

TLSData<CoreTLSData>& getCoreTlsData()
{
    static TLSData<CoreTLSData> *value = new TLSData<CoreTLSData>();
    return *value;
}

След като анализирахме този код, знаем, че статичният обект се разпределя само веднъж и всичко трябва да е наред. Но сега имам куп фалшиви положителни доклади за изтичане на памет като:

{1370349} normal block at 0x0E74D560, 24 bytes long.
 Data: <                > FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 
{1370348} normal block at 0x0E74D4E0, 64 bytes long.
 Data: <` t             > 60 D5 74 0E CD CD CD CD CD CD CD CD CD CD CD CD 

И сега е много трудно да намеря някои реални течове на памет в моето приложение, защото има набор от фалшиви положителни резултати от OpenCV. Не мога също да стартирам автоматични тестове за изтичане на памет, защото изходът винаги съдържа някои течове.

Има ли някакъв начин за премахване на тези "псевдо" грешки (ако е възможно без промяна на изходния код на OpenCV)? Много е досадно.

Предполагам, че други детектори за течове на памет също ще докладват за някои подобни псевдо течове, защото операторът new се изпълнява без delete (обектът се почиства автоматично от операционната система).


person AdamF    schedule 29.09.2015    source източник
comment
Това не е фалшиво положително, това е действително изтичане на памет.   -  person James McNellis    schedule 01.10.2015
comment
@JamesMcNellis Съгласен съм, но за съжаление няма да бъде коригирано в OpenCv. Съобщих за този проблем ( github.com/Itseez/opencv/issues/5452), но беше маркирано като wontfix (това е заобиколно решение за фиаско на поръчката за статична инициализация/деструкция). Някакви идеи как да се справя с този проблем?   -  person AdamF    schedule 05.10.2015
comment
Ако модифицирането на OpenCv е опция, тогава можете да промените функцията getCoreTlsData(), за да прехвърлите изрично тип блок към оператора за отстраняване на грешки new, напр. new (_IGNORE_BLOCK, __FILE__, __LINE__) TlsData<CoreTLSData>(). Като алтернатива (но по-грозно), можете сами да освободите блока, преди да извършите проверката за теч, чрез delete &getCoreTlsData();. Но това ще изисква внимателно определяне на времето: трябва да се уверите, че сте направили това, след като OpenCv приключи с използването му.   -  person James McNellis    schedule 05.10.2015
comment
@JamesMcNellis благодаря много, не беше толкова просто, колкото предложихте, но ми показа начин да преустановя проверката за изтичане на памет. Описах моето решение по-долу.   -  person AdamF    schedule 06.10.2015


Отговори (3)


Реших проблема по доста мръсен начин, но не намерих по-добър. Решението изисква модифициране на един OpenCV файл (system.cpp). Ако сте намерили по-добър начин да го поправите, моля, оставете коментар. Предполагам, че повече хора могат да имат подобни проблеми.

Първо се опитах да разреша проблема с помощта на решение @JamesMcNellis (от коментар по-горе), като изрично маркирах блока като _IGNORE_BLOCK: new (_IGNORE_BLOCK, __FILE__, __LINE__). Беше наистина добро начало за решаването на този проблем. За съжаление класът за изтичане съдържа членове като напр. std::vector, така че проследяването на разпределенията от този вектор не е спряно.

Започнах да чета MSDN документация за функции от crtdbg.h и намерих начин да спра проверката за изтичане на памет за известно време. Възможно е чрез изчистване на флага '_CRTDBG_ALLOC_MEM_DF' с помощта на функция: _CrtSetDbgFlag. Проверете подробности с пример в MSDN: _CrtSetDbgFlag документация. Това решение вероятно има един недостатък (който знам), спира проверката на изтичане на памет за всички нишки.

Накрая, използвайки RAII и няколко макро дефиниции, създадох прост клас за управление на тази функционалност.

Всички промени, които приложих към официалните изходни кодове на 3.0.

Някъде отгоре (след precomp.hpp include) на system.cpp файл от OpenCV добавих проста машина:

#if defined(_MSC_VER) && defined(_DEBUG)
    #include <crtdbg.h>
    class MEMORY_LEAKS_CHECKING_SUSPENDER
    {
    public:
        MEMORY_LEAKS_CHECKING_SUSPENDER()
        {
            value = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
            int new_flag = value & (~_CRTDBG_ALLOC_MEM_DF);
            _CrtSetDbgFlag(new_flag);
        }

        ~MEMORY_LEAKS_CHECKING_SUSPENDER()
        {
            _CrtSetDbgFlag(value);
        }

    private:
        int value;
    };

    #define SUSPEND_MEMORY_LEAKS_CHECKING MEMORY_LEAKS_CHECKING_SUSPENDER suspend_memory_leaks_checking
#else
    #define SUSPEND_MEMORY_LEAKS_CHECKING
#endif

И всеки път, когато искам да преустановя проверката за изтичане на памет, трябва да добавя: PAUSE_MEMORY_LEAKS_CHECKING; Активирано е само в компилацията на Visual Studio Debug. Проследяването на изтичане на памет се активира автоматично след напускане на обхвата (деструктор на MEMORY_LEAKS_CHECKING_SUSPENDER клас).

В момента, за да суспендирам изтичането на паметта на OpenCV, добавих спиране на разпределенията във функции:

  • getTLSContainerStorage()
  • void* TLSDataContainer::getData() const
  • TLSData<CoreTLSData>& getCoreTlsData()
  • inline TLSStorage* TLSStorage::get()

(Последното спиране в TLSStorage вероятно е фиксирано в главното OpenCV хранилище - проверих накратко хранилището)

Всяка модификация е много проста (напр. за първо изтичане):

Преди модификация:

static TLSContainerStorage& getTLSContainerStorage()
{
    static TLSContainerStorage *tlsContainerStorage = new TLSContainerStorage();
    return *tlsContainerStorage;
}

След модификация:

static TLSContainerStorage& getTLSContainerStorage()
{
    SUSPEND_MEMORY_LEAKS_CHECKING;
    static TLSContainerStorage *tlsContainerStorage = new TLSContainerStorage();
    return *tlsContainerStorage;
}

Ако промените всички тези изрази и все още наблюдавате изтичане на памет и използвате OpenCV като отделно заредена dll, тогава се уверете, че правилно сте разтоварили тази dll с помощта на функцията FreeLibrary. За причината проверете DLLMain функцията в OpenCV system.cpp файл и cv::__termination използване на променлива.

person AdamF    schedule 06.10.2015
comment
Имах абсолютно същия проблем и вашето решение работи за мен. Мисля, че бихте могли да приемете собствения си отговор. - person rold2007; 04.02.2016
comment
AdamF, пробвал ли си това с OpenCV 3.2? Използвам го като споделена библиотека (DLL) и получавам изтичане на памет, дори ако единствената друга препратка към OpenCV в моя проект е създаването на cv::Mat. Опитах се да приложа вашето решение, но течовете продължават и не съм сигурен как да ги проследя. Освен това няма getTLSContainerStorage(), така че добавих вашия суспензор към статичен TlsStorage &getTlsStorage(), който предполагам го е заменил. Няма и вграден TLSStorage* TLSStorage::get(), така че поставих суспензора в void* getData(size_t slotIdx) const член на TlsStorage. Но все още тече. Същото в 3.3. - person Display Name; 24.07.2017
comment
@DisplayName Опитах се да осигуря бърза корекция за OpenCV 3.3, но за съжаление броят на такива течове в последната версия е много по-голям. Можете да опитате да промените библиотеката, но това не е правилният начин. Според мен разработчиците на OpenCV трябва да поправят тези течове или поне да добавят възможност за деактивиране на детектора за течове на MSVC в техния код. - person AdamF; 29.07.2017

Имах същия проблем в моя проект: статично изграждане - MFC & OpenCV. Тези решения не ми помогнаха. Тествах ги с версии на OpenCV: 3.4.3 и 4.0.1. Проблемът изчезна, когато всички функции на opencv бяха в собствените си dll. Така че моята конфигурация на проекта може да бъде следната: MFC build - статичен, OpenCV - динамичен

person victor kulichkin    schedule 08.03.2019

Добре, имам алтернативно решение:

Направете специална функция, да речем, prefetchOpenCvMemoryLeaks(), която създава/унищожава малка матрица, създава/унищожава малък прозорец и т.н., така че OpenCV течовете да са наситени.

Във външната main() функция запазете състоянието на купчината с _CrtMemCheckpoint(), извикайте целия проект, в края запазете отново състоянието на купчината и го сравнете със старото чрез _CrtMemDifference().

По този начин можете да проверите собствените си изтичания на памет, независимо от OpenCV тези.

person Hexagonal    schedule 12.03.2018