|
|||||||
Отладка многопоточного приложения в GDB
Время создания: 27.02.2020 13:20
Текстовые метки: linux, gdb, отладка, дебаг, debug, многопоточность, тред, thread
Раздел: Компьютер - Программирование - Отладчик GDB
Запись: xintrea/mytetra_syncro/master/base/1582798800104lawvo3v/text.html на raw.github.com
|
|||||||
|
|||||||
Вопрос: Возникла необходимость научиться отлаживать многопоточное приложение через 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) вывести не удается... Ответ: Для отладки (по крайней мере для комфортной отладки) необходимо собирать с отладочной информацией, в gcc для этого используется ключ -g: g++ -pthread -g thr.cpp -o thr Можно либо сразу запустить процесс под gdb: gdb ./thr либо привязаться к уже запущенному процессу: gdb -p <pid_of_already_running_process> В первом случае, после запуска отладчика собственно процесс нужно запустить коммандой 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. Если данная комбинация клавиш не работает, можно поступить следующим образом. Вначале надо выяснить PID процесса программы, которая запущена под отладчиком. Это делается вв дополнительной консоли, например так: $ ps aux | grep <programm_name> Узнать номер сигнала SIGINT можно так: $ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP В Linux номер сигнала SIGINT равен 2. Затем надо послать программе сигнал SIGINT: $ kill -s 2 <PID> В результате программа будет остановлена и управление перехватит отладчик. Определение обстановки После остановки в произвольной точке стоит посмотреть, где же мы находимся, для этого есть команды 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<long, std::ratio<1l, 1000l> > (__rtime=...) at /usr/lib/gcc/x86_64-pc-linux-gnu/6.4.0/include/g++-v6/thread:323 #2 0x0000555555555140 in <lambda()>::operator()(void) const (__closure=0x55555576bc28) at thr.cpp:8 #3 0x0000555555556524 in std::_Bind_simple<main()::<lambda()>()>::_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<main()::<lambda()>()>::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<std::_Bind_simple<main()::<lambda()>()> >::_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<long, std::ratio<1l, 1000l> > (__rtime=...) at /usr/lib/gcc/x86_64-pc-linux-gnu/6.4.0/include/g++-v6/thread:328 328 } (gdb) my world ro <lambda()>::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» |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|