hello, there is a need to learn how to debug a multi-threaded application via gdb ... suppose there is such an application with several threads:

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(); } 

Connect the gdb -p number_of_pid process:

 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 

and the application stops ... tell me how to see the value of the variable i in the debugger, if as soon as we join gdb, everything stops, and if we do next, then, suppose br in some line, then n, then everything goes still, the value of the variable i (through print) cannot be displayed ...

    1 answer 1

    Base notes of a young mistress

    For debugging (at least for comfortable debugging) it is necessary to collect with debug information, in gcc the -g key is used for this:

     g++ -pthread -g thr.cpp -o thr 

    You can either immediately start the process under gdb:

     gdb ./thr 

    or bind to an already running process:

     gdb -p <pid_of_already_running_process> 

    In the first case, after running the debugger, the process itself needs to be run with the run or 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 ....... 

    The process can be suspended at any time by SIGINT or, in other words, Ctrl + C.

    Determination of the situation

    After stopping at an arbitrary point, it’s worth looking where we are, for this there are commands backtrace ( b ) and 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 

    As you can see, gdb is now in the context of the main thread's pthread_join() .

    Change stack frame and print variable

    To print a variable ( print ), you need to switch to the frame in which it is located for this, there is the frame command ( f ), at the same time you can see the listing ( 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) pi $1 = 3 

    where the two in frame 2 is the number of the frame of interest in the bt output.

    Step by step debugging flow

    To debug a specific thread step by step, you need to switch gdb to its context with the thread ( thr ) command:

     (gdb) thread 2 [Switching to thread 2 (Thread 0x7ffff6ee0700 (LWP 28097))] #0 0x00007ffff72a4e4d in nanosleep () from /lib64/libpthread.so.0 

    Each thread has its own stack, so it will not be superfluous to look at the backtrace again and, if necessary, go to the desired frame.

     (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 

    After that, you can debug the flow in the usual way with the help of 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) pi $2 = 5 

    A couple of notes worth mentioning are:

    • For the sake of step-by-step debugging, the code was slightly reformatted, as seen in the listing above.
    • Regarding multi-threaded applications: when control returns to process n all threads are started at once, so you can see additional output almost after each command.

    For later reading / viewing I recommend, at a minimum, the primer “ Debugging with GDB

    • Why do I need to do exactly frame 2? frame 3 gives out neponyatki - xperious
    • @xperious, the digit is the number of the frame of interest in the bt output; YMMV - Fat-Zer
    • hmm, info thread ... bt ... frame 2 ... thread 2 ... print i - writes that there is no i in context - xperious
    • @xperious, each thread has its own stack, so first thread, and then bt and so on: info thread ... thread 2 ... bt ... frame <of_bt> ... print i - Fat-Zer
    • voo, thanks for the clarification ... and then the primer ... that's literally a sequence of commands I just had to understand ... I read a bunch of stateek - I didn't understand ... and here with one example it became much clearer - xperious