अंतिम अद्यतन:2026-10-12
GDB multithread All-Stop डीबगिंग¶
जब हम gdb से multithreaded program debug करते हैं, तो threads किसी भी समय execute हो सकते हैं, जब हम किसी एक thread को debug करने की कोशिश करते हैं (उदाहरण के लिए: next), तो दूसरे thread पर switch हो जाता है, जो current thread में अगली line पर जाने की हमारी अपेक्षा के अनुरूप नहीं है।
Thread 2 "arrow-compute-n" hit Breakpoint 1, arrow::compute::NestedLoopJoinNode::InputReceived (this=0x5bbf90, input=0x5ba840, batch=...)
at /code/arrow/cpp/src/arrow/compute/exec/nested_loop_node.cc:399
399 size_t thread_index = thread_indexer_();
(gdb) n
[Switching to Thread 0x7ffff2c08700 (LWP 29121)]
Thread 3 "arrow-compute-n" hit Breakpoint 1, arrow::compute::NestedLoopJoinNode::InputReceived (this=0x5bbf90, input=0x5bb060, batch=...)
at /code/arrow/cpp/src/arrow/compute/exec/nested_loop_node.cc:399
399 size_t thread_index = thread_indexer_();
इस स्थिति में हमें क्या करना चाहिए?
वही है जो आज परिचय कराएँगे: scheduler-locking। GDB सभी threads को lockstep में execute नहीं कर सकता। चूंकि thread scheduling आपके debug target के operating system पर निर्भर करता है (GDB के नियंत्रण में नहीं होता), इसलिए current thread का single-step operation पूरा होने तक दूसरे threads कई statements execute कर सकते हैं। इसके अलावा, आमतौर पर जब program रुकता है, तो दूसरे threads statements के बीच में रुकते हैं, न कि साफ़ statement boundaries पर। continue या single-step करने के बाद आप पा सकते हैं कि आपका program किसी दूसरे thread में रुका है। ऐसा हर बार होता है जब पहला thread आपके requested operation को पूरा करने से पहले ही कोई दूसरा thread breakpoint, signal या exception के सामने आ जाता है।
और gdb द्वारा दिए गए scheduler-locking से आप scheduler को lock करके केवल एक ही thread के run होने की अनुमति देकर GDB के डिफ़ॉल्ट व्यवहार को बदल सकते हैं।
डिफ़ॉल्ट रूप से scheduler-locking की value replay होती है
Replay mode में व्यवहार on जैसा होता है, record mode में या सामान्य निष्पादन के दौरान व्यवहार off जैसा होता है। यह डिफ़ॉल्ट mode है।
- on
उदाहरण के लिए t 2 चलाने पर thread 2 पर switch होता है, इस समय on सेट करने पर केवल current thread में debugging होगी।
- off
on के विपरीत, कोई locking नहीं, कोई भी thread किसी भी समय run हो सकता है।
- step
Single-step के समय व्यवहार on जैसा, अन्यथा off जैसा। जब आप step करते हैं, तो current thread के अलावा दूसरे threads को कभी run होने का मौका नहीं मिलता, जबकि जब आप continue, until या finish जैसी commands का उपयोग करते हैं, तो वे पूरी तरह run हो सकते हैं।
उदाहरण के लिए: next, step जैसी commands आपके इच्छित process के अंदर debugging करेंगी, दूसरे threads hang रहेंगे; continue जैसी commands सभी threads को run करा देंगी।
उदाहरण¶
देखा जा सकता है कि step सेट करने पर, continue threads के बीच switch करता है।
(gdb) info threads
Id Target Id Frame
1 Thread 0x7ffff7e88780 (LWP 31464) "arrow-compute-n" 0x00007ffff37e3de2 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
2 Thread 0x7ffff3409700 (LWP 31494) "arrow-compute-n" arrow::compute::NestedLoopJoinNode::InputReceived (this=0x5bbf90, input=0x5bb060, batch=...)
at /code/arrow/cpp/src/arrow/compute/exec/nested_loop_node.cc:399
* 3 Thread 0x7ffff2c08700 (LWP 31495) "arrow-compute-n" arrow::compute::NestedLoopJoinNode::InputReceived (this=0x5bbf90, input=0x5bb060, batch=...)
at /code/arrow/cpp/src/arrow/compute/exec/nested_loop_node.cc:399
(gdb) c
Continuing.
[Switching to Thread 0x7ffff3409700 (LWP 31494)]
Thread 2 "arrow-compute-n" hit Breakpoint 1, arrow::compute::NestedLoopJoinNode::InputReceived (this=0x5bbf90, input=0x5bb060, batch=...)
at /code/arrow/cpp/src/arrow/compute/exec/nested_loop_node.cc:399
399 size_t thread_index = thread_indexer_();
(gdb) info threads
Id Target Id Frame
1 Thread 0x7ffff7e88780 (LWP 31464) "arrow-compute-n" 0x00007ffff37e3de2 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
* 2 Thread 0x7ffff3409700 (LWP 31494) "arrow-compute-n" arrow::compute::NestedLoopJoinNode::InputReceived (this=0x5bbf90, input=0x5bb060, batch=...)
at /code/arrow/cpp/src/arrow/compute/exec/nested_loop_node.cc:399
3 Thread 0x7ffff2c08700 (LWP 31495) "arrow-compute-n" 0x00007ffff605a372 in arrow::compute::NestedLoopJoinNode::InputReceived (this=0x5bbf90, input=0x5bb060, batch=...)
at /code/arrow/cpp/src/arrow/compute/exec/nested_loop_node.cc:399
फिर से continue करने पर अगले breakpoint पर पहुंचेंगे:
(gdb) info threads
Id Target Id Frame
1 Thread 0x7ffff7e88780 (LWP 31464) "arrow-compute-n" 0x00007ffff37e3a35 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
2 Thread 0x7ffff3409700 (LWP 31494) "arrow-compute-n" 0x00007ffff5781b50 in arrow::compute::NestedLoopJoinNode::OnBuildSideBatch(unsigned long, arrow::compute::ExecBatch)@plt ()
from /code/arrow/cpp/build/debug/libarrow.so.200
* 3 Thread 0x7ffff2c08700 (LWP 31495) "arrow-compute-n" 0x00007ffff5781b50 in arrow::compute::NestedLoopJoinNode::OnBuildSideBatch(unsigned long, arrow::compute::ExecBatch)@plt ()
from /code/arrow/cpp/build/debug/libarrow.so.200
(gdb) c
Continuing.
[Switching to Thread 0x7ffff3409700 (LWP 31494)]
Thread 2 "arrow-compute-n" hit Breakpoint 3, 0x00007ffff5781b50 in arrow::compute::NestedLoopJoinNode::OnBuildSideBatch(unsigned long, arrow::compute::ExecBatch)@plt ()
from /code/arrow/cpp/build/debug/libarrow.so.200
(gdb) info threads
Id Target Id Frame
1 Thread 0x7ffff7e88780 (LWP 31464) "arrow-compute-n" 0x00007ffff37e3a35 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
* 2 Thread 0x7ffff3409700 (LWP 31494) "arrow-compute-n" 0x00007ffff5781b50 in arrow::compute::NestedLoopJoinNode::OnBuildSideBatch(unsigned long, arrow::compute::ExecBatch)@plt ()
from /code/arrow/cpp/build/debug/libarrow.so.200
3 Thread 0x7ffff2c08700 (LWP 31495) "arrow-compute-n" 0x00007ffff5781b56 in arrow::compute::NestedLoopJoinNode::OnBuildSideBatch(unsigned long, arrow::compute::ExecBatch)@plt ()
from /code/arrow/cpp/build/debug/libarrow.so.200
देखा जा सकता है कि threads के बीच switch हो रहा है, अगर आप thread के अंदर रहना चाहते हैं, तो सीधे next कर सकते हैं, उदाहरण के लिए:
(gdb) t 2
[Switching to thread 2 (Thread 0x7ffff3409700 (LWP 32396))]
#0 0x00007ffff605ae0d in arrow::compute::NestedLoopJoinNode::OnBuildSideBatch (this=0x5bbf90, thread_index=1, batch=...) at /code/arrow/cpp/src/arrow/compute/exec/nested_loop_node.cc:487
487 if (!batch.length) {
(gdb) n
490 std::lock_guard<std::mutex> guard(build_side_mutex_);
(gdb) n
491 build_accumulator_.InsertBatch(std::move(batch));
अगर print operation चलाया जाए, और उससे function call हो, तो program रुक जाएगा, हमें अपेक्षित परिणाम का आउटपुट नहीं मिलेगा।
(gdb) p batch.ToString()
[Switching to Thread 0x7ffff2c08700 (LWP 32397)]
Thread 3 "arrow-compute-n" hit Breakpoint 1, arrow::compute::NestedLoopJoinNode::InputReceived (this=0x5bbf90, input=0x5ba840, batch=...)
at /code/arrow/cpp/src/arrow/compute/exec/nested_loop_node.cc:399
399 size_t thread_index = thread_indexer_();
The program stopped in another thread while making a function call from GDB.
Evaluation of the expression containing the function
(arrow::compute::ExecBatch::ToString() const) will be abandoned.
When the function is done executing, GDB will silently stop.
हम इस समय on सेट कर सकते हैं, तब print किया जा सकता है!
(gdb) set scheduler-locking on
(gdb) p batch.ToString()
$2 = "ExecBatch\n # Rows: 2\n 0: Array[null,4]\n 1: Array[true,false]\n"
https://sourceware.org/gdb/current/onlinedocs/gdb.html/All_002dStop-Mode.html
संबंधित विषय¶
- C++ Concurrency in Action रीडिंग गाइड | C++那些事
- C++ deadlock जांच | C++那些事
- C++ SIMD vectorization | C++那些事
- C++17 parallel STL | C++那些事
- C++ False Sharing और cache line | C++那些事
- Helgrind concurrency error detection | C++那些事
- libev event loop का सिद्धांत | C++那些事
- C++ std::thread kernel implementation | C++那些事
- C++ std::thread और jthread | C++那些事
- C++ multithreading की शुरुआत | C++那些事