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
run_gdb(pid_t parent_pid,const char * gdb_path)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            (char*) NULL);
74 }
75 
76 static void
intermediate_process(pid_t parent_pid,const char * gdb_path)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
spawn_gdb(const char * gdb_path)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
toku_try_gdb_stack_trace(const char * gdb_path)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