madf: (Default)
[personal profile] madf
Цілий тиждень, починаючи з минулої неділі, я кожного вечора після роботи сідав за комп, логінився через два шелла на віддалений сервер і боровся з багом.
Є у мене один секретний проект який потихеньку починає виходити у люди, і от треба було запустити його на FreeBSD. На лінуксах все працювало нормально і нічого не віщало біди...

Проект використовує моднявий нині протокол серіалізації і RPC Thrift (розроблений у Facebook і відданий суспільству у Apache). Реалізація протоколу виявилась гнилою, паскудною, і нагадує вихлоп трудів сотень макак яких випадково пустили за клавіатури. Чого тільки варте змішання стилів, використання std::tr1::function, std::auto_ptr, boost::shared_ptr? А низькорівнева робота з мережею, при живому Boost.ASIO? І все це на фоні сотень фабрик і адапторів. Та що там казати...
Але, принаймні, все те кубло змій, жаб і червів працювало. А під FreeBSD раптом відмовило. Точніше не зовсім відмовило, воно продовжило працювати рівно до тих пір поки клієнт залишається під’єднаним. Як тільки клієнт від’єднуєьтся — все. Хана. Приблизно така:
Thread 7 (Thread 803009400 (LWP 100514)):
#0 0x0000000801f6f54c in thr_kill () from /lib/libc.so.7
#1 0x0000000802011c9b in abort () from /lib/libc.so.7
#2 0x0000000801d0d965 in _Unwind_Resume () from /lib/libgcc_s.so.1
#3 0x00000008040d8d4d in apache::thrift::TDispatchProcessor::process (this=0x80302f820, in=..., out=..., connectionContext=0x0) at TDispatchProcessor.h:121
...

— там ще близько двадцяти фреймів стеку. Ну, власне, зрозуміло в чому проблема — неловлений exception. Та от тільки... exception насправді ловлений. Спочатку строго по типу, потім по std::exception і в кінці кінців по трьом крапкам:
    try {                                                                       
      for (;;) {                                                                
        if (eventHandler_ != NULL) {                                            
          eventHandler_->processContext(connectionContext, client);             
        }                                                                       
        if (!processor->process(inputProtocol, outputProtocol,                  
                                connectionContext) ||                           
          // Peek ahead, is the remote side closed?                             
            !inputProtocol->getTransport()->peek()) {                           
          break;                                                                
        }                                                                       
      }                                                                         
    } catch (const TTransportException& ttx) {                                  
      string errStr = string("TSimpleServer client died: ") + ttx.what();       
      GlobalOutput(errStr.c_str());                                             
    } catch (const std::exception& x) {                                         
      GlobalOutput.printf("TSimpleServer exception: %s: %s",                    
                          typeid(x).name(), x.what());                          
    } catch (...) {                                                             
      GlobalOutput("TSimpleServer uncaught exception.");                        
    }

Розумієте? Exception пролітає через три крапки як вода крізь сито! І навіть коли я ззовні поставив ще один try/catch — нічого не змінилось! Звісно я грішив на кубло змій. В такій каші я варився п’ять днів. І зварився. Вчора прийшов і просто опустив руки. Бо перевірив усе і навіть під valgrind поганяв. Ніц не знайшов. Все чисто. І багтрекери всі перерив, і багато схожих проблем бачив, але жодна із них не мала до мене ніякого відношення. Я просто не знав що робити.
При чому exceptions із мого коду ловились нормально. І я почав гратися — весь цей thrift жив у плагіні у вигляді shared object, я почав туди додавати свої функції і методи які просто кидали exception. І дивитись що вийде. І якось доекспериментувався до того, що якщо просто викликати функції що кидає exception то не зважаючи на try/catch матиму abort. А якщо одразу після (sic!) цієї функції кинути ще одне exception — все починає працювати! Просто чорна магія.
Сьогодні продовжив гратись і експериментувати. І знову таки випадково визначив, що все падає коли на рівні вище від функції що кидає exception є boost::shared_ptr. Почав грішити на Boost (ну тіпа там exception у деструкторі під час розгортання стеку), колупатись в коді і багтреккері. Без толку. Потім після багатьох невдалих спроб мені вдалось відтворити проблему не на великому проекті (який я на той час уже добряче пошматував), а на відносно невеликому тестовому. І я визначив що падає не тільки коли маю boost::shared_ptr а взагалі коли маю будь-який об’єкт з custom destructor. Навіть якщо той destructor пустий. З дефолтовим не падає.
Enjoy:
[faust@test /usr/home/faust/testex]$ cat testex.cpp 
#include <string>

void bar()
{
    throw 1;
}

void foo()
{
    std::string client;
    bar();
}

int main() {
    try {
        foo();
    } catch(...) {
    }
    return 0;
}
[faust@test /usr/home/faust/testex]$ /usr/local/bin/g++46 testex.cpp -o testex
[faust@test /usr/home/faust/testex]$ ./testex 
Abort trap: 6 (core dumped)
[faust@test /usr/home/faust/testex]$ g++ testex.cpp -o testex
[faust@test /usr/home/faust/testex]$ ./testex 
[faust@test /usr/home/faust/testex]$ 
[faust@test /usr/home/faust/testex]$ /usr/local/bin/g++46 --version           
g++46 (FreeBSD Ports Collection) 4.6.3
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

[faust@test /usr/home/faust/testex]$ g++ --version           
g++ (GCC) 4.2.1 20070831 patched [FreeBSD]
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

[faust@test /usr/home/faust/testex]$

Зі шлангом теж, звісно, все в порядку. Але там свої заморочки...

Вся ця історія коштувала мені недосипання, червоних очей зранку і відчуття власного безсилля. Реалізація exceptions у C++ на низькому рівні досі для мене є загадкою.
Все, завтра поїду до друга сніг кидати і варити глінтвейн у казанку.
А з нового тижня займусь проблемою Boost + Clang + C++11.
This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

Profile

madf: (Default)
madf

April 2018

S M T W T F S
1234567
891011121314
15161718192021
22232425262728
2930     

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jun. 28th, 2025 03:54 am
Powered by Dreamwidth Studios