2013-05-20 17 views
9

Mam następujący komunikat "wyjątek pierwszej szansy", który pochodzi z biblioteki DLL, którą napisałem, działającej w pliku wykonywalnym, którego nie napisałem. Oznacza to, że DLL jest wtyczką. Przy pierwszym uruchomieniu tego wyjątku próba otwarcia pliku mapy pamięci współużytkowanej kończy się niepowodzeniem. Jeśli zignoruję wyjątki pierwszej szansy i po prostu uruchomię, aplikacja w końcu zawiesza się lub ulega awarii.Jak mogę debugować lub naprawiać problem nieskończonej pętli i sterty korupcji z udziałem boost :: interprocess managed_shared_memory?

First-chance exception at 0x76a7c41f in notmyexe.exe: Microsoft C++ exception: boost::interprocess::interprocess_exception at memory location 0x002bc644.. 

Po kilku godzinach wydaje się, że jest spowodowany blokiem kodu, który bez przerwy przechodzi w pętlę, dopóki nie zostanie spełniony oczekiwany warunek wyjątku. Okazuje się, że jeśli nigdy nie zostanie wyczyszczone, a następnie, w końcu, ten wyjątek zamienia się w inny stan wyjątku niskiego poziomu i/lub przekształca się w uszkodzenie sterty. Wszystko to jest po to, aby otworzyć wspólny obszar pamięci za pomocą Boost :: interprocess. Pierwszą rzeczą, która komplikuje sytuację, jest to, że w moim projekcie opartym na Visual C++ 2008 pierwszy wyjątek boost::interprocess::interprocess_exception pierwszej szansy nie jest generowany i identyfikowany jako lokalizacja, z której pochodzi, ponieważ kompilator Visual C++ 2008 nie może znaleźć złożonego wzmocnienia -tygodny kod szablonów. Jednak przez pojedyncze przejście przez widok asemblerowy znalazłem kod, który się podniósł.

Linia najwyższego poziomu od własnego kodu, który to wszystko zaczyna się psuć na to:

segment = new managed_shared_memory( open_or_create 
             , MEMORY_AREA_NAME 
             , SHARED_AREA_SIZE);   

Powyższy managed_shared_memory klasa jest od interprocess_fwd.hpp i jest standardową częścią doładowania pamięci dzielonej API/nagłówki. Ponieważ jest oparty na szablonie, powyższe rozwija się w około 2Kchars długie wyrażenie szablonu doładowania C++, które jest obcięte na różne długości przez linker i przez debugger. W Visual C++ 2008 nie ma już możliwości debugowania kodu źródłowego, wydaje się, że limity te są w grze.

Na przykład, kiedy wysadza otrzymuję ten stosu wywołań:

KernelBase.dll!76a7c41f() 
    [Frames below may be incorrect and/or missing, no symbols loaded for KernelBase.dll]  
    KernelBase.dll!76a7c41f() 
> msvcr90d.dll!_malloc_dbg(unsigned int nSize=2290875461, int nBlockUse=264, const char * szFileName=0x01fcb983, int nLine=1962999808) Line 160 + 0x1b bytes C++ 
    8bfc4d89() 

Nie rzeczywisty użytkownik końcowy napisane funkcje źródłowe pojawiają się w stosie zrzutu powyżej.

Jak mam to debugować? Po drugie, czy jest jakiś znany problem z interpretercją doładowania, z Visual C++ 2008? Po trzecie, co robi poniższy kod doładowania i dlaczego musi się bez końca zapętlać?

boost::interprocess::basic_managed_shared_memory<char, 
    boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, 
     boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>, 
     boost::interprocess::iset_index>::basic_managed_shared_memory<char,boo... 

Kolejne warstwy w dół, mamy do:

basic_managed_shared_memory (open_or_create_t, 
           const char *name, size_type size, 
           const void *addr = 0, const permissions& perm = permissions()) 
     : base_t() 
     , base2_t(open_or_create, name, size, read_write, addr, 
       create_open_func_t(get_this_pointer(), 
       ipcdetail::DoOpenOrCreate), perm) 
    {} 

Tak czy inaczej, nie próbuj do debugowania tego w domu dzieci, oto co się dzieje:

enter image description here

Wreszcie, używając mojej ninja-podobnej zdolności do przejścia przez kilka milionów linii języka asemblerowego, pokonałem ograniczenia zła debuggera Visual C++ 2008 i znalazłem kod w qu estion.

To jest to, co wysadza w rzeczywistości: create_device<FileBased>(dev....

Niektóre kontekst tutaj: managed_open_or_create_impl.h linia 351 ...

else if(type == DoOpenOrCreate){ 
     //This loop is very ugly, but brute force is sometimes better 
     //than diplomacy. If someone knows how to open or create a 
     //file and know if we have really created it or just open it 
     //drop me a e-mail! 
     bool completed = false; 
     while(!completed){ 
      try{ 
       create_device<FileBased>(dev, id, size, perm, file_like_t()); // <-- KABOOM! 
       created  = true; 
       completed = true; 
      } 
      catch(interprocess_exception &ex){ 
       if(ex.get_error_code() != already_exists_error){ 
        throw; 
       } 
       else{ 
        try{ 
        DeviceAbstraction tmp(open_only, id, read_write); 
        dev.swap(tmp); 
        created  = false; 
        completed = true; 
        } 
        catch(interprocess_exception &e){ 
        if(e.get_error_code() != not_found_error){ 
         throw; 
        } 
        } 
        catch(...){ 
        throw; 
        } 
       } 
      } 
      catch(...){ 
       throw; 
      } 
      thread_yield(); 
     } 
     } 
+1

+1 za odręczną twarz z grymasem odręcznym. –

+0

Jest tu dużo do pochłaniania, ale czy rozważałeś możliwość, że wyjątek pierwszej szansy może być całkowicie łagodny? –

+0

Wprowadziłem kilka zmian i usunąłem to, co było bardzo nieprofesjonalne w tym pytaniu. Przepraszam za każdego, kto musiał przeczytać oryginalną wersję. –

Odpowiedz

2

Wierzę, że miałem kilka takich samych problemów. Spójrz na funkcję "shared_memory_object :: priv_open_or_create" w "\ boost \ interprocess \ shared_memory_object.hpp". U góry tej funkcji znajduje się inne wywołanie funkcji "create_tmp_and_clean_old_and_get_filename", które uruchamia łańcuch funkcji, który kończy usuwanie pliku pamięci współużytkowanej. Kończyłem przenoszenie tego wywołania funkcji niżej w funkcji priv_open_or_create wokół miejsca, w którym rozpoczyna się instrukcja case. Uważam, że używam doładowania 1.48. Oto ostateczna wersja tej funkcji, że modyfikacja:

inline bool shared_memory_object::priv_open_or_create 
    (ipcdetail::create_enum_t type, const char *filename, mode_t mode, const permissions &perm) 
{ 
    m_filename = filename; 
    std::string shmfile; 
    std::string root_tmp_name; 

    //Set accesses 
    if (mode != read_write && mode != read_only){ 
     error_info err = other_error; 
     throw interprocess_exception(err); 
    } 

    switch(type){ 
     case ipcdetail::DoOpen: 
      ipcdetail::get_tmp_base_dir(root_tmp_name); 
      shmfile = root_tmp_name; 
      shmfile += "/"; 
      shmfile += filename; 
      m_handle = ipcdetail::open_existing_file(shmfile.c_str(), mode, true); 
     break; 
     case ipcdetail::DoCreate: 
      ipcdetail::create_tmp_and_clean_old_and_get_filename(filename, shmfile); 
      m_handle = ipcdetail::create_new_file(shmfile.c_str(), mode, perm, true); 
     break; 
     case ipcdetail::DoOpenOrCreate: 
     ipcdetail::create_tmp_and_clean_old_and_get_filename(filename, shmfile); 
      m_handle = ipcdetail::create_or_open_file(shmfile.c_str(), mode, perm, true); 
     break; 
     default: 
     { 
      error_info err = other_error; 
      throw interprocess_exception(err); 
     } 
    } 

    //Check for error 
    if(m_handle == ipcdetail::invalid_file()){ 
     error_info err = system_error_code(); 
     this->priv_close(); 
     throw interprocess_exception(err); 
    } 

    m_mode = mode; 
    return true; 
} 

BTW, czy ktoś wie, oficjalne kanały mogę przejść, aby spróbować i dostać to sprawdzone i dodane do zwiększenia proszę dać mi znać, jak ja nienawidzę modyfikowania rzeczy jak to bez znajomości jego pełnego efektu.

Mam nadzieję, że to pomoże!

+0

Myślę, że lista dyskusyjna doładowania byłaby dobrym miejscem, aby opublikować to jako autor Boost :: Interprocess może powiedzieć, czy to byłaby dobra ogólna poprawka. –

0

Boost jest pełen zarówno niesamowite i przerażające rzeczy.

Prosty obejście w systemie Windows, może być, aby przełączyć się managed_windows_shared_memory zamiast managed_shared_memory można rozwiązać wiele bolesnego zderzenia/wiszą problemy, a jeden rodzaj wypadku/powiesić problemu wydaje się być spowodowany z kolei różnicami między zachowaniem systemu plików Windows i zachowaniem systemu plików uniksowych, aw szczególności, wydaje się, że przy podnoszeniu i managed_shared_memory w systemie Windows możliwe jest działanie wbrew ograniczeniom blokowania systemu plików Windows. Zostałem poinformowany, że starania, aby sobie z tym poradzić, zostały zakończone w BOost 1.53, ale używam Boost 1.53 i nadal mam ten problem.

Z regularną wersją managed_shared_memory w systemie Windows, utrwala się ona po okresie eksploatacji dowolnej aplikacji klienckiej lub serwerowej. Może to być pożądane w niektórych przypadkach, dlatego rozwiązanie to nie jest prawdziwą poprawką dla tych osób.

Jednak w moim przypadku tak naprawdę nie potrzebowałem tego, chociaż myślałem, że będzie to przydatne, okazuje się, że jest to więcej bólu niż jest warte, przynajmniej z obecną implementacją Boost w systemie Windows.

Chciałbym również zwrócić uwagę, że usunięcie pliku pamięci współdzielonej wydaje się być podstawową przyczyną stanu wyścigu, który powoduje problem występujący w powyższym pytaniu. Właściwa synchronizacja wokół tworzenia i sprawdzania oraz usuwania pliku wydaje się być niezbędna do implementacji systemu w realnym świecie, w szczególności wydaje się, że jest to problem druzgocący, jeśli twój master (serwer) usunie plik pamięci współużytkowanej podczas gdy niektórzy klienci wciąż go używają. Konieczne jest ponowne uruchomienie, aby usunąć wynikowy błąd systemu plików NTFS.

Jeśli znajdę prawdziwe rozwiązanie, opublikuję to, ale powyższe informacje są więcej niż mogłem znaleźć nigdzie indziej. Uważaj na managed_shared_memory i rozważ użycie opcji managed_windows_shared_memory i zapomnij o próbie uruchomienia idei "trwałej pamięci wspólnej". Zamiast tego należy użyć nietrwałego tylko okna: managed_windows_shared_memory.

Rozwiązanie to, zachowując klasę managed_shared_memory w mojej aplikacji, prawdopodobnie oznacza zawijanie całego dostępu do obiektu managed_shared_memory na jeszcze innym poziomie prymitywów synchronizacji międzyprocesowej lub nawet przy użyciu surowego muteksu Win32 API. Boost mógłby zrobić coś podobnego, ale prawdopodobnie wprowadziłby jeszcze bardziej przypadkową złożoność.

(Na marginesie: Czy jestem jedynym tutaj, który uważa, że ​​szablon-All-the-rzeczy zostało przeprowadzone zbyt daleko w powszechnym użyciu, a zwłaszcza w Boost te dni?)

Aktualizacja 2: Znalazłem alternatywny sposób zamrożenia managed_shared_memory, a to z kolei powoduje zawieszenie jakiejkolwiek aplikacji, z której go używasz. Nie spodziewałem się, że tak łatwo będzie stworzyć zakleszczenia z Boostem, ale jest to całkiem łatwe. Kod muteksa wewnątrz implementacji będzie zamarzał na zawsze, czekając na muteks, który został usunięty przez innego użytkownika zarządzanej pamięci współdzielonej. Ten niekończący się sen, czekający na muteks, który nigdy nie zostanie wydany, jest kolejną głęboką wadą projektu w tej dynamicznej implementacji międzyprocesowej, w której do tej pory zliczyłem kilka poważnych wad projektowych, przynajmniej w systemie Windows. Może to działa pięknie na Linuksie.

Kod, który wykazuje jest to metoda find(), zwany tak:

boost::interprocess::managed_shared_memory * segment; 
    std::pair<MyType*, std::size_t> f = segment->find<MyType>(name); 

Oto ślad stosu do impasu mutex (aka niekończącej czekaj, zamrożone zadania):

Tylko rozwiązaniem, gdy tu dotrzesz, jest usunięcie obszaru pamięci wspólnej, po zatrzymaniu lub zabiciu wszystkich zawieszonych procesów oczekujących na ten muteks.

> myapp.exe!boost::interprocess::winapi::sched_yield() Line 998 C++ 
    myapp.exe!boost::interprocess::ipcdetail::thread_yield() Line 60 + 0xe bytes C++ 
    myapp.exe!boost::interprocess::ipcdetail::spin_mutex::lock() Line 71 C++ 
    myapp.exe!boost::interprocess::ipcdetail::spin_recursive_mutex::lock() Line 91 C++ 
    myapp.exe!boost::interprocess::interprocess_recursive_mutex::lock() Line 161 C++ 
    myapp.exe!boost::interprocess::scoped_lock<boost::interprocess::interprocess_recursive_mutex>::lock() Line 280 C++ 
    myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_get_lock(bool use_lock=true) Line 1340 C++ 
    myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_generic_find<char>(const char * name=0x00394290, boost::interprocess::iset_index<boost::interprocess::ipcdetail::index_config<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0> > > & index={...}, boost::interprocess::ipcdetail::in_place_interface & table={...}, unsigned int & length=1343657312, boost::interprocess::ipcdetail::bool_<1> is_intrusive={...}, bool use_lock=true) Line 854 + 0x11 bytes C++ 
    myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_find_impl<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(const char * name=0x00394290, bool lock=true) Line 728 + 0x25 bytes C++ 
    myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(const char * name=0x00394290) Line 423 + 0x1e bytes C++ 
    myapp.exe!boost::interprocess::ipcdetail::basic_managed_memory_impl<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index,8>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(boost::interprocess::ipcdetail::char_ptr_holder<char> name={...}) Line 346 + 0x23 bytes C++ 
    myapp.exe!boost::interprocess::basic_managed_shared_memory<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(boost::interprocess::ipcdetail::char_ptr_holder<char> name={...}) Line 208 + 0x10 bytes C++ 
    myapp.exe!CCommonMemory::AllocateOrFindAreaMap(const char * name=0x00394290) Line 128 C++ 
+0

Didyou przez przypadek naprawić aktualizację 2 części? Mój kod konsumenta spać będzie bez końca, kiedy użyję tego MyDeque * Mydeque = segment.find ("dequeName"). – brownKnight

+0

Nie. Uznałem, że jakikolwiek kod, który jest tak sprytnie napisany, że nie mogę go odczytać, nie jest właściwym rozwiązaniem w mojej bazie kodu. Przełączony na coś głupszego i mniej trudnego do odczytania. DDE. Tak. DDE. W 20xx. –

+0

Nie jestem pewien, czy próbowałeś tego. Moja pierwsza aplikacja została zbudowana na 64-bitowym użyciu programu Visual Studio, a moja aplikacja konsumencka była w wersji 32-bitowej. Zmieniłem zarówno 64 kompilację, jak i teraz działa. Mam nadzieję, że będzie to dla ciebie pomocne. – brownKnight

Powiązane problemy