Страницы

Поиск по вопросам

пятница, 31 января 2020 г.

gdb отладка многопоточного приложения

#cpp #linux #c #gdb


здравствуйте, возникла необходимость научиться отлаживать многопоточное приложение
через gdb... предположим, есть такое приложение с несколькими потоками:

int main() {
     static int i =0;
     std::thread([](){ while(true) { ++i; std::this_thread::sleep_for( std::chrono::milliseconds(700));
std::cout << "hello\n";}}).detach();
     std::thread([](){ while(true) { std::this_thread::sleep_for(std::chrono::milliseconds(600));
std::cout << "my\n";}}).detach();
     std::thread([](){ while(true) { std::this_thread::sleep_for(std::chrono::milliseconds(900));
std::cout << "world\n";}}).detach();
     std::thread([](){ while(true) { std::this_thread::sleep_for(std::chrono::milliseconds(700));
std::cout << "ro\n";}}).join();
}


подключаемся gdb -p number_of_pid процесс: 

Type "apropos word" to search for commands related to "word".
Attaching to process 9208
[New LWP 9209]
[New LWP 9210]
[New LWP 9211]
[New LWP 9212]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
0x00007f1bae990acd in pthread_join () from /lib64/libpthread.so.0


и приложение останавливается... подскажите как увидеть в отладчике значение переменной
i, если как только присоединяемся gdb, то все останавливается, а если делаем next,
потом, предположим, br в какой-то строке, затем далее n, то все идет по-прежнему, значение
переменной i(через print) вывести не удается...
    


Ответы

Ответ 1



Базовые заметки молодой хозяйке Для отладки (по крайней мере для комфортной отладки) необходимо собирать с отладочной информацией, в gcc для этого используется ключ -g: g++ -pthread -g thr.cpp -o thr Можно либо сразу запустить процесс под gdb: gdb ./thr либо привязаться к уже запущенному процессу: gdb -p В первом случае, после запуска отладчика собственно процесс нужно запустить коммандой run или r (gdb) r Starting program: /tmp/thr [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". [New Thread 0x7ffff6ee0700 (LWP 27964)] [New Thread 0x7ffff66df700 (LWP 27965)] [New Thread 0x7ffff5ede700 (LWP 27966)] [New Thread 0x7ffff56dd700 (LWP 27967)] my hello ro world ....... Процесс можно в любой момент приостановить SIGINT'ом или, другими словами, Ctrl+C. Определение обстановки После остановки в произвольной точке стоит посмотреть, где же мы находимся, для этого есть команды backtrace (b) и info threads (i th) Thread 1 "thr" received signal SIGINT, Interrupt. 0x00007ffff729a93d in pthread_join () from /lib64/libpthread.so.0 (gdb) bt #0 0x00007ffff729a93d in pthread_join () from /lib64/libpthread.so.0 #1 0x00007ffff7ab7537 in std::thread::join() () from /usr/lib/gcc/x86_64-pc-linux-gnu/6.4.0/libstdc++.so.6 #2 0x0000555555555310 in main () at thr.cpp:13 (gdb) i th Id Target Id Frame * 1 Thread 0x7ffff7f7d740 (LWP 28093) "thr" 0x00007ffff729a93d in pthread_join () from /lib64/libpthread.so.0 2 Thread 0x7ffff6ee0700 (LWP 28097) "thr" 0x00007ffff72a4e4d in nanosleep () from /lib64/libpthread.so.0 3 Thread 0x7ffff66df700 (LWP 28098) "thr" 0x00007ffff72a4e4d in nanosleep () from /lib64/libpthread.so.0 4 Thread 0x7ffff5ede700 (LWP 28099) "thr" 0x00007ffff72a4e4d in nanosleep () from /lib64/libpthread.so.0 5 Thread 0x7ffff56dd700 (LWP 28100) "thr" 0x00007ffff72a4e4d in nanosleep () from /lib64/libpthread.so.0 Как видно, gdb сейчас находится в контексте pthread_join() основного потока. Смена кадра стека и печать переменной Чтобы распечатать переменную (print) нужно переключиться на кадр, в котором она находится для этого есть команда frame (f), заодно можно посмотреть листинг(list): (gdb) f 2 #2 0x0000555555555310 in main () at thr.cpp:13 13 std::thread([](){ while(true) { std::this_thread::sleep_for(std::chrono::milliseconds(700)); std::cout << "ro\n";}}).join(); (gdb) l 8 std::this_thread::sleep_for( std::chrono::milliseconds(700)); 9 std::cout << "hello\n";} 10 }).detach(); 11 std::thread([](){ while(true) { std::this_thread::sleep_for(std::chrono::milliseconds(600)); std::cout << "my\n";}}).detach(); 12 std::thread([](){ while(true) { std::this_thread::sleep_for(std::chrono::milliseconds(900)); std::cout << "world\n";}}).detach(); 13 std::thread([](){ while(true) { std::this_thread::sleep_for(std::chrono::milliseconds(700)); std::cout << "ro\n";}}).join(); 14 } (gdb) p i $1 = 3 где двойка в frame 2 — это номер интересующего кадра в выводе bt. Пошаговая отладка потока Чтобы по-шагам отлаживать конкретный поток нужно переключить gdb в его контекст командой thread (thr): (gdb) thread 2 [Switching to thread 2 (Thread 0x7ffff6ee0700 (LWP 28097))] #0 0x00007ffff72a4e4d in nanosleep () from /lib64/libpthread.so.0 У каждого потока свой стек, поэтому не лишним будет снова посмотреть backtrace и, по необходимости, перейти в нужный кадр. (gdb) bt #0 0x00007ffff72a4e4d in nanosleep () from /lib64/libpthread.so.0 #1 0x0000555555556aa7 in std::this_thread::sleep_for > (__rtime=...) at /usr/lib/gcc/x86_64-pc-linux-gnu/6.4.0/include/g++-v6/thread:323 #2 0x0000555555555140 in ::operator()(void) const (__closure=0x55555576bc28) at thr.cpp:8 #3 0x0000555555556524 in std::_Bind_simple()>::_M_invoke<>(std::_Index_tuple<>) (this=0x55555576bc28) at /usr/lib/gcc/x86_64-pc-linux-gnu/6.4.0/include/g++-v6/functional:1391 #4 0x0000555555556394 in std::_Bind_simple()>::operator()(void) (this=0x55555576bc28) at /usr/lib/gcc/x86_64-pc-linux-gnu/6.4.0/include/g++-v6/functional:1380 #5 0x0000555555556292 in std::thread::_State_impl()> >::_M_run(void) (this=0x55555576bc20) at /usr/lib/gcc/x86_64-pc-linux-gnu/6.4.0/include/g++-v6/thread:197 #6 0x00007ffff7ab724e in ?? () from /usr/lib/gcc/x86_64-pc-linux-gnu/6.4.0/libstdc++.so.6 #7 0x00007ffff7299657 in start_thread () from /lib64/libpthread.so.0 #8 0x00007ffff6fd9c5f in clone () from /lib64/libc.so.6 После этого можно отлаживать поток привычным способом с помощью next/step (n/s): (gdb) n Single stepping until exit from function nanosleep, which has no line number information. my world ro std::this_thread::sleep_for > (__rtime=...) at /usr/lib/gcc/x86_64-pc-linux-gnu/6.4.0/include/g++-v6/thread:328 328 } (gdb) my world ro ::operator()(void) const (__closure=0x55555576bc28) at thr.cpp:9 9 std::cout << "hello\n";} (gdb) my ro world hello 7 ++i; (gdb) my world ro 8 std::this_thread::sleep_for( std::chrono::milliseconds(700)); (gdb) world my ro my ro 9 std::cout << "hello\n";} (gdb) my world ro hello 7 ++i; (gdb) my world ro 8 std::this_thread::sleep_for( std::chrono::milliseconds(700)); (gdb) p i $2 = 5 Стоит упомянуть пару замечаний: Ради пошаговый отладки код был слегка переформатирован, как видно в листинге выше. Касательно многопоточных приложений: при возврате управления процессу по n запускаются сразу все потоки, поэтому можно видеть дополнительный вывод почти после каждой комманды. К последующему прочтению/просмотру рекомендую, как минимум, букварь «Отладка с помощью GDB»

Комментариев нет:

Отправить комментарий