Задача: Обнаружим дедлок bash-скриптом и отдадим на выход exitcode 1.
Вос/произведем простейший дедлок, запустим ./ily.bin deadlock_off 1000000
ily.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#include <iostream> #include <thread> #include <mutex> // множитель. Увелич.кратно 10 для увеличения продолжительности работы программы, можно передать в argv long SIZE = 10000; std::mutex mu1, mu2; // выставить в 1 для дедлока, можно передать в argv int deadlock_is_on = 0; static void shared_func1() { std::lock_guard<std::mutex> guardA(mu1); std::lock_guard<std::mutex> guardB(mu2); std::cout << std::this_thread::get_id() << std::endl; } static void shared_func_deadlock() { std::lock_guard<std::mutex> guardA(mu2); std::lock_guard<std::mutex> guardB(mu1); std::cout << std::this_thread::get_id() << std::endl; } void function_1() { long neg_size = -SIZE; for (int i = 0; i > neg_size; i--) shared_func1(); } int main(int argc, char* argv[]) { std::cout << "Pass X as command line argument to turn deadlocks on" << std::endl; if (argc > 1) if (std::string(argv[1]) == "X") deadlock_is_on = 1; if (argc > 2) SIZE = atoi(argv[2]); std::thread t1(function_1); for (int i = 0; i < SIZE; i++) if (deadlock_is_on) shared_func_deadlock(); else shared_func1(); t1.join(); return 0; } |
Дедлок вычислим силами GDB – для этого подготовим следующий скрипт (futex_gdb_py), так как gdb поддерживает питон (через import gdb находим pthread_mutex_lock прямо в gdb)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
python import sys sys.path.append('.') import gdb class Thread(): def __init__(self): self.frames = [] self.waitOnThread = None self.threadId = None def __getitem__(self): return self.waitOnThread threads = {} for process in gdb.inferiors(): for thread in process.threads(): trd = Thread() trd.threadId = thread.ptid[1] #[1] - threadId; [0] - process pid thread.switch() frame = gdb.selected_frame() while frame: frame.select() name = frame.name() if name is None: name = "??" if "pthread_mutex_lock" in name: trd.waitOnThread = int(gdb.execute("print mutex.__data.__owner", to_string=True).split()[2]) trd.frames.append(name) frame = frame.older() threads[trd.threadId] = trd for (tid,thread) in threads.items(): if thread.waitOnThread: # нашли дедлок if thread.waitOnThread in threads and threads[thread.waitOnThread].waitOnThread == thread.threadId: sys.exit(1) # Получим на выходе из gdb exitcode "1" end thread apply all where |
команда x/3d
команда x/3d
команда thread 4 показывает что процесс 20860 ждет mutex2
команда thread 5 показывает что процесс 20861 ждет mutex1:
И, собственно сам скрипт bash, для простоты берём pid запущенного процесса по имени ily.bin:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#!/bin/bash function run() { bin_name=$1 bin_pid=$(pgrep $bin_name) gdb -q $bin_name $bin_pid -x futex_gdb_py -batch exitcode=$? if [ $exitcode = 0 ]; then echo "Passed!" elif [ $exitcode = 1 ]; then echo "Failed!" fi } echo "Running test #1" run ily.bin |