Что могло привести к сбою std :: difftime на SIGBUS?

Сегодня, к своему ужасу, я осознал, что моя программа моделирования на C ++ вылетела после 12 дней работы, всего за несколько строк до ее завершения, в результате чего у меня не осталось ничего, кроме (усеченного) дампа ядра.

Анализ дампа ядра с помощью gdb показал, что

Программа завершена сигналом SIGBUS, Ошибка шины.

и что сбой произошел в следующей строке моего кода:

seconds = std::difftime(stopTime, startTime); // seconds is of type double

Переменные stopTime и startTime относятся к типу std::time_t, и мне удалось извлечь их значения во время сбоя из дампа ядра:

startTime: 1426863332
stopTime:  1427977226

Трассировка стека над вызовом difftime выглядит так:

#0  0x.. in _dl_fixup () from /lib64/ld-linux-x86-64.so.2
#1  0x.. in _dl_runtime_resolve () from /lib64/ld-linux-x86-64.so.2

Я написал небольшую программу для воспроизведения ошибки, но безуспешно. Простой вызов std::difftime(stopTime, startTime) с указанными выше значениями не вызывает сбоя SIGBUS. Конечно, я не хочу, чтобы это повторилось снова. Я уже несколько раз успешно выполнял одну и ту же программу (хотя и с разными аргументами) со сравнимым временем выполнения. Что может вызвать эту проблему и как я могу предотвратить ее в будущем?

Вот дополнительная информация о системе.

GCC: (SUSE Linux) 4.8.1 20130909 [gcc-4_8-branch revision 202388]  
Linux Kernel: 3.11.10-25-desktop, x86_64
C++ standard library: 6.0.18

Изменить

Вот еще один контекст. Во-первых, полная трассировка стека (многоточие [..] у меня):

#0  0x00007f309a4a5bca in _dl_fixup () from /lib64/ld-linux-x86-64.so.2
#1  0x00007f309a4ac195 in _dl_runtime_resolve () from /lib64/ld-linux-x86-64.so.2
#2  0x0000000000465453 in CStopwatch::getTime (this=0x7fff0db48c60, delimiterHourMinuteSecondsBy="") at [..] CStopwatch.cpp:86
#3  0x00000000004652a9 in CStopwatch::stop (this=0x7fff0db48c60) at [..] CStopwatch.cpp:51
#4  0x0000000000479a0c in main (argc=33, argv=0x7fff0db499c8) at [..] coherent_ofdm_tsync_mse.cpp:998

Проблема возникает в объекте класса CStopwatch, который создается в начале программы. Секундомер запускается в main () на самом верху. После завершения моделирования вызывается функция CStopwatch::stop( ).

Конструктор класса секундомера:

/*
 * Initialize start and stop time on construction
 */
CStopwatch::CStopwatch()
{
  this->startTime = std::time_t( 0 );
  this->stopTime = std::time_t( 0 );
  this->isRunning = false;
}

Функция CStopwatch::stop( )

/*
 * Stop the timer and return the elapsed time as a string
 */
std::string CStopwatch::stop( )
{
  if ( this->isRunning ) {
    this->stopTime = std::time( 0 );
  }
  this->isRunning = false;

  return getTime( );
}

Функция CStopwatch::getTime()

/*
 * Return the elapsed time as a string
 */
std::string CStopwatch::getTime( std::string delimiterHourMinuteSecondsBy )
{
  std::ostringstream timeString;

// ...some string init      

  // time in seconds
  double seconds;

  if ( this->isRunning ){
    // return instantaneous time
    seconds = std::difftime(time(0), startTime);
  } else {
    // return stopped time
    seconds = std::difftime(stopTime, startTime); // <-- line where the
                                                  // program crashed
  }

  // ..convert seconds into a string

  return timeString.str( );
}

В начале программы вызывается CStopwatch::start( )

/*
 * Start the timer, if watch is already running, this is effectively a reset
 */
void CStopwatch::start( )
{
  this->startTime = std::time( 0 );
  this->isRunning = true;
}

person Deve    schedule 02.04.2015    source источник
comment
Это полная трассировка стека? seconds переменная-член? Если да, то какое тогда значение this? Это указатель на действительный объект? Когда и где вы вызываете функцию? Не могли бы вы показать еще контекст?   -  person Some programmer dude    schedule 02.04.2015
comment
@JoachimPileborg Нет, #2 - это строка кода, которую я предоставил, а остальное - это некоторые вызовы внутри моего исходного кода, которые, я думаю, не могут быть поняты без (большого) контекста.   -  person Deve    schedule 02.04.2015
comment
Что ж, проблема, скорее всего, не в коде библиотеки, который оставляет ваш код причиной сбоя. Вам необходимо предоставить дополнительную информацию и контекст об окружающем коде.   -  person Some programmer dude    schedule 02.04.2015
comment
@JoachimPileborg Я попытался добавить, надеюсь, релевантный код   -  person Deve    schedule 02.04.2015


Ответы (3)


Есть только несколько причин, по которым программа может получать SIGBUS в Linux. Некоторые из них перечислены в ответах на этот вопрос.

Посмотрите /var/log/messages во время сбоя, вероятно, вы обнаружите, что произошел сбой диска или какая-то другая причина недовольства ядра.

Другая (маловероятная) возможность состоит в том, что кто-то обновил libstdc++.so.6 , когда ваша программа работала, и сделал это неправильно (путем записи поверх существующего файла, а не его удаления и создания на его месте нового файла).

person Employed Russian    schedule 04.04.2015
comment
Похоже, что сам исполняемый файл был заменен во время выполнения другой версией программы. Может ли это быть причиной SIGBUS? - person Deve; 16.04.2015
comment
@Deve Да, конечно, может. - person Employed Russian; 16.04.2015

Похоже, что std::difftime лениво загружается при первом обращении; если какое-то внутреннее состояние компоновщика среды выполнения было повреждено где-то еще в вашей программе, это могло вызвать это.

Обратите внимание, что _dl_runtime_resolve необходимо завершить до того, как вызов std::difftime может начаться, поэтому маловероятно, что ошибка будет связана с вашими значениями времени. Вы можете легко проверить, открыв основной файл в gdb:

(gdb) frame 2 # this is CStopwatch::getTime
(gdb) print this
(gdb) print *this

etc. etc.

Если gdb может читать и разрешать адрес, и значения выглядят нормальными, это определенно не вызывает SIGBUS во время выполнения. В качестве альтернативы, возможно, ваш стек разбился; если _dl_fixup готовит прыжок на батуте, а не просто занимается перемещением и т.д .; мы не можем быть уверены, не глядя на код, но можем проверить сам стек:

(gdb) print %rsp
(gdb) x/16xb $rsp-16 # print the top 16 bytes of the stack

Простым обходным решением является установка переменной среды LD_BIND_NOW и принудительное разрешение символа при запуске. Однако это просто скрывает проблему, потому что некоторая память все еще повреждена где-то, и мы только скрываем симптом.

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

person Useless    schedule 02.04.2015
comment
Повреждение внутреннего состояния компоновщика обычно приводит к SIGSEGV, а не SIGBUS. Этот ответ вряд ли будет правильным. - person Employed Russian; 05.04.2015

Невозможно сказать без дальнейшего контекста, но:

  • this может быть null или поврежден
  • startTime может быть пустой ссылкой
  • stopTime может быть пустой ссылкой

Я собирался предложить вам установить точку останова в строке и распечатать stopTime и startTime, но вы уже почти сделали это, просмотрев файл ядра.

Похоже, что при связывании функции что-то идет не так. Может быть, вы компилируете другой набор заголовков из стандартной библиотеки, на которую вы ссылаетесь?

Это может быть просто связано с памятью:

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

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

person abligh    schedule 02.04.2015
comment
Может быть, вы компилируете другой набор заголовков из стандартной библиотеки, на которую вы ссылаетесь? - Может быть, да. Я определенно скомпилировал программу на другой машине, чем та, которая терпит крах. Однако у них одна и та же версия GCC. Я проверю это - person Deve; 03.04.2015
comment
Оказывается, программа была скомпилирована против версии 6.0.19 стандартной библиотеки. Система, в которой произошел сбой, имеет версию 6.0.18 из libstdc++. Если это причина сбоя (который мне еще предстоит проверить), могу ли я решить эту проблему, статически связав библиотеку при компиляции? - person Deve; 03.04.2015
comment
Я думал, что он работает только на другой основной версии, которая может вызвать это, но да, вы можете статическую ссылку. Запустите ldd -v против двоичного файла, чтобы убедиться, что вы удалили зависимость. - person abligh; 03.04.2015