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/*======
5This file is part of PerconaFT.
6
7
8Copyright (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
49enum { MAX_GDB_ARGS = 128 };
50
51static void
52run_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
76static void
77intermediate_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 }
123success:
124 _exit(EXIT_SUCCESS);
125failure:
126 _exit(EXIT_FAILURE);
127}
128
129static void
130spawn_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
149void
150toku_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