Случайный сбой в деструкторе при очистке собственного (!) Члена строки - PullRequest
0 голосов
/ 27 января 2019

Я пытаюсь отследить ошибку, которая иногда приводит к сбою моего приложения в деструкторе этого тривиального класса C ++:

class CrashClass {

public:
         CrashClass(double r1, double s1, double r2, double s2, double r3, double s3, string dateTime) : mR1(r1), mS1(s1), mR2(r2), mS2(s2), mR3(r3), mS3(s3), mDateTime(dateTime) { }
         CrashClass() : mR1(0), mS1(0), mR2(0), mS2(0), mR3(0), mS3(0) { }
        ~CrashClass() {}

    string  GetDateTime()   { return mDateTime; }

private:
    double mR1, mS1, mR2, mS2, mR3, mS3;
    string mDateTime;
};

Куча этих объектов застряла в стандартном C ++ vector и использовалаво втором классе:

class MyClass {
    (...)

private:
    vector<CrashClass>    mCrashClassVec;
};

MyClass создается и освобождается многократно по мере необходимости.

Код использует C ++ 17 в последней версии Xcode 10.1 под macOS10.14.4.

Все это является частью вычислительного приложения, работающего в течение нескольких часов или дней.На 6-ядерном компьютере i7, выполняющем 12 параллельных вычислений (с использованием среды GCD macOS), это часто приводит к сбою через пару часов, когда освобожденный указатель

не был выделен

ошибка при вызове mCrashClassVec.clear() на элементе в MyClass, то есть

frame #0: 0x00007fff769a72f6 libsystem_kernel.dylib`__pthread_kill + 10
frame #1: 0x00000001004aa80d libsystem_pthread.dylib`pthread_kill + 284
frame #2: 0x00007fff769116a6 libsystem_c.dylib`abort + 127
frame #3: 0x00007fff76a1f977 libsystem_malloc.dylib`malloc_vreport + 545
frame #4: 0x00007fff76a1f738 libsystem_malloc.dylib`malloc_report + 151
frame #5: 0x0000000100069448 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::__libcpp_deallocate(__ptr=<unavailable>) at new:236 [opt]
frame #6: 0x0000000100069443 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::allocator<char>::deallocate(__p=<unavailable>) at memory:1796 [opt]
frame #7: 0x0000000100069443 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::allocator_traits<std::__1::allocator<char> >::deallocate(__p=<unavailable>) at memory:1555 [opt]
frame #8: 0x0000000100069443 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::~basic_string() at string:1941 [opt]
frame #9: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::~basic_string() at string:1936 [opt]
frame #10: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] CrashClass::~CrashClass(this=<unavailable>) at CrashClass.h:61 [opt]
frame #11: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] CrashClass::~CrashClass(this=<unavailable>) at CrashClass.h:61 [opt]
frame #12: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::allocator<CrashClass>::destroy(this=<unavailable>, __p=<unavailable>) at memory:1860 [opt]
frame #13: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] void std::__1::allocator_traits<std::__1::allocator<CrashClass> >::__destroy<CrashClass>(__a=<unavailable>, __p=<unavailable>) at memory:1727 [opt]
frame #14: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] void std::__1::allocator_traits<std::__1::allocator<CrashClass> >::destroy<CrashClass>(__a=<unavailable>, __p=<unavailable>) at memory:1595 [opt]
frame #15: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::__vector_base<CrashClass, std::__1::allocator<CrashClass> >::__destruct_at_end(this=<unavailable>, __new_last=0x00000001011ad000) at vector:413 [opt]
frame #16: 0x0000000100069429 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::__vector_base<CrashClass, std::__1::allocator<CrashClass> >::clear(this=<unavailable>) at vector:356 [opt]
frame #17: 0x0000000100069422 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::vector<CrashClass, std::__1::allocator<CrashClass> >::clear(this=<unavailable>) at vector:749 [opt]

Примечание: У очищаемого vector могут не быть элементов (пока).

В трассировке стека (bt all) я вижу другие потоки, выполняющие операции с их копиями векторов CrashClass, но, насколько я вижу из сравнения адресов в трассировке стека, все они на самом деле являются частнымикопий (как задумано), т.е. ни одна из этих данных не передается между потоками.

Естественно ошибка возникает только в полном производственном режиме, т.е. все попытки воспроизвести сбой

  • работает в режиме DEBUG ,
  • работает в режиме Lldb (Xcode) Address Sanitizer (для много часов / ночь),
  • работает под Lldb (Xcode's) Thпрочитайте Sanitizer (для много часов / всю ночь),
  • запустите урезанную версию класса с оставленным / реплицированным только критическим кодом,

не удалось и не вызвало сбой.

Почему может освободить простой элемент, выделенный в стеке не удалось с освобожденным указателем , не было выделено ошибка?

Также дополнительные советы о том, какчтобы отладить это или вызвать ошибку в более надежном для дальнейшего изучения, очень приветствуются.

Обновление 5/2019

Ошибка все еще периодически прерывается сбой приложенияи я начинаю верить, что проблемы, с которыми я сталкиваюсь, на самом деле вызваны ошибкой повреждения данных Intel в последних моделях процессоров.

https://mjtsai.com/blog/2019/05/17/microarchitectural-data-sampling-mds-mitigation/

https://mjtsai.com/blog/2017/06/27/bug-in-skylake-and-kaby-lake-hyper-threading/

https://www.tomshardware.com/news/hyperthreading-kaby-lake-skylake-skylake-x,34876.html

1 Ответ

0 голосов
/ 28 января 2019

Вы можете попробовать несколько хитростей:

  • Запустите рабочую версию, используя один поток, на еще более длительный период (скажем, неделю или 2), чтобы проверить, не произойдет ли сбой.
  • Убедитесь, что вы не используете всю доступную оперативную память, принимая во внимание тот факт, что у вас может быть фрагментация памяти.
  • Убедитесь, что ваша программа не имеет утечки памяти или не увеличивает использование памяти, чем дольше она работает.
  • Добавьте некоторое отслеживание, добавив дополнительное значение, установите значение для чего-нибудь известного в деструкторе (чтобы вы могли распознать шаблон, если вы сделаете двойное удаление).
  • Попробуйте запустить программу под другой платформой и компилятором.
  • Ваш компилятор или библиотека могут содержать ошибки.Попробуйте другую (более свежую) версию.
  • Удалите код из исходной версии, пока он не выйдет из строя.Это работает лучше, если вы можете последовательно получать сбой с последовательностью, которая каким-то образом повреждает память.
  • После сбоя запустите программу с точно такими же данными (для каждого потока) и посмотрите, всегда ли происходит сбойто же место.
  • Переписать или проверить любой небезопасный код в вашем приложении.Избегайте приведения типов, printf и другой старой функции переменной аргумента и любой небезопасной функции strcpy и аналогичной.
  • Используйте проверенную версию STL.
  • Попробуйте неоптимизированную версию выпуска.
  • Попробуйте оптимизированную версию отладки.
  • Узнайте различия между версией DEBUG и RELEASE для вашего компилятора.
  • Переписать проблемный код с нуля.Возможно, в нем не будет ошибки.
  • Проверьте данные в случае сбоя.
  • Проверьте обработку ошибок / исключений, чтобы убедиться, что вы игнорируете какую-то потенциальную проблему.
  • Проверкакак ваша программа ведет себя, когда ей не хватает памяти, не хватает места на диске, когда выдается исключение…
  • Убедитесь, что ваш отладчик останавливается на каждом обработанном исключении или нет.
  • Убедитесь, что вашПрограмма компилируется и запускается без предупреждений или того, что вы их понимаете и уверены, что это не имеет значения.
  • Проверьте данные при сбое, чтобы увидеть, хорошо ли они выглядят.
  • Вы можете зарезервировать память для уменьшения фрагментациии перераспределение.Если ваша программа работает в течение нескольких часов, может оказаться, что память слишком фрагментирована, и система не сможет найти достаточно большой блок.
  • Поскольку ваша программа многопоточная, убедитесь, что ваша среда выполнения такжесовместим с этим.
  • Убедитесь, что вы не делитесь данными между потоками или что они надежно защищены.
...