1 | /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
2 | // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4: |
3 | #ident "$Id$" |
4 | /*====== |
5 | This file is part of PerconaFT. |
6 | |
7 | |
8 | Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved. |
9 | |
10 | PerconaFT is free software: you can redistribute it and/or modify |
11 | it under the terms of the GNU General Public License, version 2, |
12 | as published by the Free Software Foundation. |
13 | |
14 | PerconaFT is distributed in the hope that it will be useful, |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | GNU General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU General Public License |
20 | along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. |
21 | |
22 | ---------------------------------------- |
23 | |
24 | PerconaFT is free software: you can redistribute it and/or modify |
25 | it under the terms of the GNU Affero General Public License, version 3, |
26 | as published by the Free Software Foundation. |
27 | |
28 | PerconaFT is distributed in the hope that it will be useful, |
29 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
30 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
31 | GNU Affero General Public License for more details. |
32 | |
33 | You should have received a copy of the GNU Affero General Public License |
34 | along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. |
35 | ======= */ |
36 | |
37 | #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved." |
38 | |
39 | #include <unistd.h> |
40 | #ifdef HAVE_SYS_PRCTL_H |
41 | #include <sys/prctl.h> |
42 | #endif |
43 | |
44 | #include <sys/wait.h> |
45 | #include <toku_race_tools.h> |
46 | #include "toku_crash.h" |
47 | #include "toku_atomic.h" |
48 | |
49 | enum { MAX_GDB_ARGS = 128 }; |
50 | |
51 | static void |
52 | run_gdb(pid_t parent_pid, const char *gdb_path) { |
53 | // 3 bytes per intbyte, null byte |
54 | char pid_buf[sizeof(pid_t) * 3 + 1]; |
55 | char exe_buf[sizeof(pid_buf) + sizeof("/proc//exe" )]; |
56 | |
57 | // Get pid and path to executable. |
58 | int n; |
59 | n = snprintf(pid_buf, sizeof(pid_buf), "%d" , parent_pid); |
60 | invariant(n >= 0 && n < (int)sizeof(pid_buf)); |
61 | n = snprintf(exe_buf, sizeof(exe_buf), "/proc/%d/exe" , parent_pid); |
62 | invariant(n >= 0 && n < (int)sizeof(exe_buf)); |
63 | |
64 | dup2(2, 1); // redirect output to stderr |
65 | // Arguments are not dynamic due to possible security holes. |
66 | execlp(gdb_path, gdb_path, "--batch" , "-n" , |
67 | "-ex" , "thread" , |
68 | "-ex" , "bt" , |
69 | "-ex" , "bt full" , |
70 | "-ex" , "thread apply all bt" , |
71 | "-ex" , "thread apply all bt full" , |
72 | exe_buf, pid_buf, |
73 | NULL); |
74 | } |
75 | |
76 | static void |
77 | intermediate_process(pid_t parent_pid, const char *gdb_path) { |
78 | // Disable generating of core dumps |
79 | #if defined(HAVE_SYS_PRCTL_H) |
80 | prctl(PR_SET_DUMPABLE, 0, 0, 0); |
81 | #endif |
82 | pid_t worker_pid = fork(); |
83 | if (worker_pid < 0) { |
84 | perror("spawn gdb fork: " ); |
85 | goto failure; |
86 | } |
87 | if (worker_pid == 0) { |
88 | // Child (debugger) |
89 | run_gdb(parent_pid, gdb_path); |
90 | // Normally run_gdb will not return. |
91 | // In case it does, kill the process. |
92 | goto failure; |
93 | } else { |
94 | pid_t timeout_pid = fork(); |
95 | if (timeout_pid < 0) { |
96 | perror("spawn timeout fork: " ); |
97 | kill(worker_pid, SIGKILL); |
98 | goto failure; |
99 | } |
100 | |
101 | if (timeout_pid == 0) { |
102 | sleep(5); // Timeout of 5 seconds |
103 | goto success; |
104 | } else { |
105 | pid_t exited_pid = wait(NULL); // Wait for first child to exit |
106 | if (exited_pid == worker_pid) { |
107 | // Kill slower child |
108 | kill(timeout_pid, SIGKILL); |
109 | goto success; |
110 | } else if (exited_pid == timeout_pid) { |
111 | // Kill slower child |
112 | kill(worker_pid, SIGKILL); |
113 | goto failure; // Timed out. |
114 | } else { |
115 | perror("error while waiting for gdb or timer to end: " ); |
116 | //Some failure. Kill everything. |
117 | kill(timeout_pid, SIGKILL); |
118 | kill(worker_pid, SIGKILL); |
119 | goto failure; |
120 | } |
121 | } |
122 | } |
123 | success: |
124 | _exit(EXIT_SUCCESS); |
125 | failure: |
126 | _exit(EXIT_FAILURE); |
127 | } |
128 | |
129 | static void |
130 | spawn_gdb(const char *gdb_path) { |
131 | pid_t parent_pid = getpid(); |
132 | #if defined(HAVE_SYS_PRCTL_H) |
133 | // On systems that require permission for the same user to ptrace, |
134 | // give permission for this process and (more importantly) all its children to debug this process. |
135 | prctl(PR_SET_PTRACER, parent_pid, 0, 0, 0); |
136 | #endif |
137 | fprintf(stderr, "Attempting to use gdb @[%s] on pid[%d]\n" , gdb_path, parent_pid); |
138 | fflush(stderr); |
139 | int intermediate_pid = fork(); |
140 | if (intermediate_pid < 0) { |
141 | perror("spawn_gdb intermediate process fork: " ); |
142 | } else if (intermediate_pid == 0) { |
143 | intermediate_process(parent_pid, gdb_path); |
144 | } else { |
145 | waitpid(intermediate_pid, NULL, 0); |
146 | } |
147 | } |
148 | |
149 | void |
150 | toku_try_gdb_stack_trace(const char *gdb_path) { |
151 | char default_gdb_path[] = "/usr/bin/gdb" ; |
152 | static bool started = false; |
153 | if (RUNNING_ON_VALGRIND) { |
154 | fprintf(stderr, "gdb stack trace skipped due to running under valgrind\n" ); |
155 | fflush(stderr); |
156 | } else if (toku_sync_bool_compare_and_swap(&started, false, true)) { |
157 | spawn_gdb(gdb_path ? gdb_path : default_gdb_path); |
158 | } |
159 | } |
160 | |
161 | |