Грешка при сегментиране в std функция std::_Rb_tree_rebalance_for_erase ()

(Забележка за всички бъдещи читатели: Грешката, не е изненадващо, е в моя код, а не std::_Rb_tree_rebalance_for_erase () )

Донякъде съм нов в програмирането и не съм сигурен как да се справя с грешка в сегментирането, която изглежда идва от std функция. Надявам се, че правя нещо глупаво (т.е. злоупотребявам с контейнер), защото нямам представа как да го поправя.

Точната грешка е

Програма Получен сигнал exc_bad_access, не може да получи достъп до паметта.
Причина: kern_invalid_address на адрес: 0x000000000000000c
0x00007ff8062b144 в std :: _ rb_tree_rebalance_for_erase ()
(gdb) backtrace
#ttREF_TRE _TRE _TRE _TRE _TRE _TRE _TRE _TRE _TRE _TRY )
#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, е идентичен с този тук.

Обратното проследяване без оптимизация (благодаря, bdk) разкрива много повече информация:

Програма Получен сигнал exc_bad_access, не може да получи достъп до паметта.
Причина: kern_invalid_address на адрес: 0x000000000000000c
0x00007ff8062b144 в std :: _ rb_tree_rebalance_for_erase ()
(gdb) backtrace
#ttREF_TRE _TRE _TRE _TRE _TRE _TRE _TRE _TRE _TRE _TRY )
#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 :: alocator> :: erase (this = 0x7fff5fbfdfe8, __position = {_ m_node = 0x10107cb50}) при stl_multiset.h: 346 #3 0x0000010000ba71 в Simulation: 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. Отидете по-долу за неудобно решение.


person Sarah    schedule 19.05.2010    source източник
comment
Единственият stl_tree.h, който се показва в моя твърд диск (също единственият файл, съдържащ '_Rb_tree_rebalance_for_erase' на моя HD) е празен на ред 1263 (?!). Използвам gcc 4.2.1 (Apple build 5646) на i686-apple-darwin10. Функцията rebalance_for_erase е дефинирана на редове 299-429.   -  person Sarah    schedule 20.05.2010
comment
Ако поставите cout в извикващия код веднага след извикването на Host::recover, това разпечатва ли се? Освен това опитайте да компилирате с изключени всички оптимизации и вграждане и може да получите по-полезно проследяване на стека   -  person bdk    schedule 20.05.2010
comment
сигурни ли сте, че assert е активирано? Може би вместо това опитайте да хвърлите изключение. също така проверете дали имунният масив не се презаписва извън границите   -  person Anycorn    schedule 20.05.2010
comment
Би било добре, ако можете да публикувате функцията, която извиква recover(). Бих искал да видя какво ще се случи с ce след като recover() завърши.   -  person Alex Korban    schedule 20.05.2010
comment
Това може да е резултат от всяка друга част от вашия код, която записва извън паметта си. Може би бихте могли да опитате програма за проверка на паметта като Memcheck: valgrind.org/docs/manual/mc- manual.html   -  person Thomas    schedule 20.05.2010
comment
@Томас. Благодаря, не бях чувал. Ще започне работа с него.   -  person Sarah    schedule 20.05.2010
comment
Simulation::runEpidSim (this=0x7fff5fbfcb40) в Simulation.cpp:426 звучи така, сякаш кодът, който сте поставили по-горе, не е проблемният код. Вашата по-рано поставена грешка беше подобна, но каза, че е някъде другаде: #1 0x000000010000e593 в Simulation::runEpidSim (this=0x7fff5fbfcb20) в stl_tree.h:1263 Как изглежда Simulation::runEpidSim и какво има в Simulation.cpp:426?   -  person Edward Strange    schedule 20.05.2010
comment
Страхотни погледи тук. Точно когато искам да задам въпрос на @Sarah, някой ме подтиква към него. ^^ Но да, чувствам, че проблемът е някъде по линията, когато предавате невалиден итератор. Може да е в Simulation.cpp:426 или някъде по-високо в проследяването на стека. Но EXC_BAD_ACCESS обикновено е дебел намек за дерефериране (или искане на стандартна функция да дереферира) невалиден итератор.   -  person wilhelmtell    schedule 20.05.2010
comment
@Noah: Прав си. На този ред се опитвам да изтрия събитие, което току-що е било изтрито във функцията recover(). Масов провал. Благодаря ти. Ще преработя въпроса си, така че да е полезно за други в бъдещето.   -  person Sarah    schedule 20.05.2010


Отговори (3)


Вероятно държите итератор в ce през извикването, за да възстановите. Ако се случи възстановяването да премахне този елемент, итераторът ще бъде анулиран и всяка бъдеща употреба (да речем опит за изтриването му) може да доведе до грешка на сегмента.

Ще помогне, ако можем да видим повече контекст за това как ce се използва преди и след повикването за възстановяване.

person Mark B    schedule 19.05.2010
comment
Прав си! (Трябва да разгледам как и кога се случва повторното балансиране на дървото.) - person Sarah; 20.05.2010

Проблемът беше, че на ред 426 на Simulation.cpp се опитах да изтрия събитие в контейнера EventPQ currentEvents (известен още като ce), което моята recover() функция току-що беше изтрила. Итераторът очевидно е бил невалиден. тъпо.

Уроци:

  • Отстраняване на грешки в код, който не е оптимизиран
  • Обърнете специално внимание на това, което означават несвързаните със стандартните рамки

И за в бъдеще: Проследете паметта във valgrind

Все още съм объркан защо дебъгерът ме препрати към очевидно празен ред в stl_tree.h.

Изразявам огромна благодарност за хората, които ми помогнаха да се справя с това. Ще преработя въпроса си, така че да е по-сбит за бъдещите читатели.

person Sarah    schedule 19.05.2010
comment
Това е общо правило, което трябва да следвате, когато попаднете в катастрофа. Погледнете стека за извиквания отгоре надолу, докато не достигнете собствения си код и започнете да търсите там. Обикновено ще откриете, че бъгът е причинен някъде в този stacktrace във вашия собствен код, а не в библиотеката. Когато това не е така, обикновено ще откриете, че сте ударили UB преди половин час и точно сега се натъквате на каквото и да е причинило това. Всъщност никога не съм се сблъсквал с грешка в библиотеката. Попадам на МНОГО грешки в компилатора, но не мога да се сетя за грешка в библиотеката. - person Edward Strange; 20.05.2010

Може би извикването на assert не е компилирано с вашата конфигурация. Твърденията в производствения код обикновено са лоша идея[TM].

Може също да превишавате границите на immune.

Опитвам:

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