Ошибка сегментации в функции std std :: _ Rb_tree_rebalance_for_erase () - PullRequest
1 голос
/ 20 мая 2010

(Примечание для любых будущих читателей: неудивительно, что ошибка в моем коде, а не в std :: _ Rb_tree_rebalance_for_erase ())

Я немного новичок в программировании и не уверен, как справиться с ошибкой сегментации, которая возникает из-за функции std. Я надеюсь, что я делаю что-то глупое (то есть неправильно использую контейнер), потому что я не знаю, как это исправить.

Точная ошибка

Программа получила сигнал EXC_BAD_ACCESS, Не удалось получить доступ к памяти.
Причина: KERN_INVALID_ADDRESS по адресу: 0x000000000000000c
0x00007fff8062b144 в std :: _ Rb_tree_rebalance_for_erase ()
(GDB) Backtrace
# 0 0x00007fff8062b144 в std :: _ Rb_tree_rebalance_for_erase ()
# 1 0x000000010000e593 в Simulation :: runEpidSim (this = 0x7fff5fbfcb20) в stl_tree.h: 1263
# 2 0x0000000100016078 в main () в main.cpp: 43

Функция, которая успешно завершается незадолго до ошибки сегментации, обновляет содержимое двух контейнеров. Одним из них является boost::unordered_multimap, называемый carriage; он содержит один или несколько struct Infection объектов. Другой контейнер имеет тип std::multiset< Event, std::less< Event > > EventPQ, называемый ce.

void Host::recover( int s, double recoverTime, EventPQ & ce ) {

  // Clearing all serotypes in carriage
  // and their associated recovery events in ce
  // and then updating susceptibility to each serotype
  double oldRecTime;
  int z;
  for ( InfectionMap::iterator itr = carriage.begin(); itr != carriage.end(); itr++ ) {
    z = itr->first;
    oldRecTime = (itr->second).recT;
    EventPQ::iterator epqItr = ce.find( Event(oldRecTime) );
    assert( epqItr != ce.end() );
    ce.erase( epqItr );
    immune[ z ]++; 
  }
  carriage.clear();
  calcSusc(); // a function that edits an array 
  cout << "Done with sync_recovery event." << endl;
}

Последняя строка cout << появляется непосредственно перед ошибкой сегмента.

До сих пор я думал, что попытка перебалансировки выполняется на ce сразу после этой функции, но я не уверен, почему перебалансировка будет неудачной.


Обновление

Я подтвердил, что ошибка seg исчезает (хотя программа немедленно завершает работу по другим причинам), когда я удаляю ce.erase( epqItr );. Я могу успешно удалить события в другом месте кода; код, который я использую для стирания элементов в ce, идентичен тому, что здесь.

Backtracing без оптимизации (спасибо, bdk) раскрывает гораздо больше информации:

Программа получила сигнал EXC_BAD_ACCESS, Не удалось получить доступ к памяти.
Причина: KERN_INVALID_ADDRESS по адресу: 0x000000000000000c
0x00007fff8062b144 в std :: _ Rb_tree_rebalance_for_erase ()
(GDB) Backtrace
# 0 0x00007fff8062b144 в std :: _ Rb_tree_rebalance_for_erase ()
# 1 0x00000001000053d2 в std :: _ Rb_tree, std :: less,> std :: allocator> :: erase (this = 0x7fff5fbfdfe8, __position = {_ M_node = 0x10107cb50}) в> stl_tree.h: 1263
# 2 0x0000000100005417 в std :: multiset, std :: allocator> :: erase (this = 0x7fff5fbfdfe8, __position = {_ M_node = 0x10107cb50}) в stl_multiset.h: 346 # 3 0x000000010000ba71 в Simulation :: runEpidSim (this = 0x7fff5fbfcb40) в Simulation.cpp: 426
# 4 0x000000010001fb31 в main () в main.cpp: 43

Если Xcode не читает номера строк неправильно, единственный stl_tree.h на моем жестком диске пуст в строке 1263.

Несколько человек попросили посмотреть функцию, которая вызывает восстановление. Это немного сложно:

struct updateRecovery{
updateRecovery( int s, double t, EventPQ & ce ) : s_(s), t_(t), ce_(ce) {}
  void operator() (boost::shared_ptr<Host> ptr ) {
   ptr->recover( s_, t_, ce_ );
  }
private:
  int s_;
  double t_;
  EventPQ & ce_;
};

// allHosts is a boost::multiindex container of boost::shared_ptr< Host > 
// currentEvents is the EventPQ container
// it is an iterator to a specific member of allHosts
allHosts.modify( it, updateRecovery( s, t, currentEvents ) );
cout << "done with recovery" << endl;

Последние cout отпечатки. Код работал раньше без этой конкретной версии функции восстановления.

Ной Робертс правильно указал, что проблема в Simulation.cpp, строка 426. Прыгните ниже для смущающего решения.

Ответы [ 3 ]

3 голосов
/ 20 мая 2010

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

Было бы полезно, если бы мы могли видеть больше контекста того, как ce используется до и после вызова для восстановления.

1 голос
/ 20 мая 2010

Проблема заключалась в том, что в строке 426 Simulation.cpp я пытался удалить событие в контейнере EventPQ currentEvents (a.k.a. ce), который только что удалила моя функция recover(). Итератор, очевидно, был признан недействительным. Тупой.

Уроки:

  • Отладка кода, который не был оптимизирован
  • Обратите пристальное внимание на то, что подразумевают не связанные с STD кадры

И на будущее: след памяти в valgrind

Я все еще озадачен, почему отладчик направил меня к явно пустой строке в stl_tree.h.

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

0 голосов
/ 20 мая 2010

Возможно, вызов assert не скомпилирован с вашей конфигурацией. Утверждения в производственном коде обычно являются Плохой Идеей [TM].

Вы также можете превышать границы immune.

Попробуйте:

    if (epqItr != ce.end()) 
    {
        ce.erase(epqItr);
        if (z is within immune's bounds)
        {
            ++immune[z]; 
        }
    }
...