xref: /netbsd-src/external/bsd/kyua-testers/dist/stacktrace.c (revision 754f425fc237c181450c91977727274098801c74)
1 // Copyright 2012 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 //   notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 //   notice, this list of conditions and the following disclaimer in the
12 //   documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 //   may be used to endorse or promote products derived from this software
15 //   without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #include "stacktrace.h"
30 
31 #include <sys/param.h>
32 #include <sys/wait.h>
33 
34 #include <assert.h>
35 #include <fcntl.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 
42 #include "defs.h"
43 #include "fs.h"
44 #include "env.h"
45 #include "error.h"
46 #include "run.h"
47 #include "text.h"
48 
49 
50 /// Built-in path to GDB.
51 ///
52 /// This should be an absolute path for deterministic behavior.  We also accept
53 /// a basename to cope with any issues that might arise from an invalid
54 /// configure check or a manual override of the GDB constant, in which case the
55 /// exec call below will (try to) locate the binary in the path.
56 ///
57 /// Note that the program pointed to by this variable is not required to exist.
58 /// If it does not, we fail gracefully.
59 ///
60 /// Test cases can override the value of this built-in constant to unit-test the
61 /// behavior of the functions below.
62 const char* kyua_stacktrace_gdb = GDB;
63 #undef GDB  // We really want to use the variable, not the macro.
64 
65 
66 /// Time to give to the external GDB process to produce a stack trace.
67 ///
68 /// Test cases can override the value of this built-in constant to unit-test the
69 /// behavior of the functions below.
70 unsigned long kyua_stacktrace_gdb_timeout = 300;
71 
72 
73 /// Maximum length of the core file name, if known.
74 ///
75 /// Some operating systems impose a maximum length on the basename of the core
76 /// file.  If MAXCOMLEN is defined, then we need to truncate the program name to
77 /// this length before searching for the core file.  If we cannot figure out
78 /// what this limit is, we set it to zero, which we consider later as
79 /// "unlimited".
80 #if !defined(MAXCOMLEN)
81 #   define MAXCOMLEN 0
82 #endif
83 
84 
85 static void run_gdb(const char* program, const char* core_name, FILE* output)
86     KYUA_DEFS_NORETURN;
87 
88 
89 /// Constructs the parameters to run GDB with.
90 ///
91 /// \param original_run_params Parameters used to run the binary that generated
92 ///     the core dump.
93 ///
94 /// \return The run parameters with which to run GDB.
95 static kyua_run_params_t
gdb_run_params(const kyua_run_params_t * original_run_params)96 gdb_run_params(const kyua_run_params_t* original_run_params)
97 {
98     kyua_run_params_t run_params = *original_run_params;
99     run_params.timeout_seconds = kyua_stacktrace_gdb_timeout;
100     return run_params;
101 }
102 
103 
104 /// Body of a subprocess to execute GDB.
105 ///
106 /// This should be called from the child created by a kyua_run_fork() call,
107 /// which means that we do not have to take care of isolating the process.
108 ///
109 /// \pre The caller must have flushed stdout before spawning this process, to
110 ///     prevent double-flushing and/or corruption of data.
111 ///
112 /// \param program Path to the program being debugged.  Can be relative to
113 ///     the given work directory.
114 /// \param core_name Path to the dumped core.  Use find_core() to deduce
115 ///     a valid candidate.  Can be relative to the given work directory.
116 /// \param output Stream to which to send the output of GDB.
117 static void
run_gdb(const char * program,const char * core_name,FILE * output)118 run_gdb(const char* program, const char* core_name, FILE* output)
119 {
120     // TODO(jmmv): Should be done by kyua_run_fork(), but doing so would change
121     // the semantics of the ATF interface.  Need to evaluate this carefully.
122     const kyua_error_t error = kyua_env_unset("TERM");
123     if (kyua_error_is_set(error)) {
124         kyua_error_warn(error, "Failed to unset TERM; GDB may misbehave");
125         free(error);
126     }
127 
128     (void)close(STDIN_FILENO);
129     const int input_fd = open("/dev/null", O_RDONLY);
130     assert(input_fd == STDIN_FILENO);
131 
132     const int output_fd = fileno(output);
133     assert(output_fd != -1);  // We expect a file-backed stream.
134     if (output_fd != STDOUT_FILENO) {
135         fflush(stdout);
136         (void)dup2(output_fd, STDOUT_FILENO);
137     }
138     if (output_fd != STDERR_FILENO) {
139         fflush(stderr);
140         (void)dup2(output_fd, STDERR_FILENO);
141     }
142     if (output_fd != STDOUT_FILENO && output_fd != STDERR_FILENO)
143         fclose(output);
144 
145     const char* const gdb_args[] = {
146         "gdb", "-batch", "-q", "-ex", "bt", program, core_name, NULL };
147     kyua_run_exec(kyua_stacktrace_gdb, gdb_args);
148 }
149 
150 
151 /// Truncates a string.
152 ///
153 /// \param source The string to truncate.
154 /// \param [out] buffer Output buffer into which to store the truncated text.
155 /// \param buffer_length Size of the buffer.
156 ///
157 /// \return A pointer to the buffer.
158 static const char*
slice(const char * source,char * buffer,const size_t buffer_length)159 slice(const char* source, char* buffer, const size_t buffer_length)
160 {
161     const size_t source_length = strlen(source);
162     if (source_length < buffer_length) {
163         strcpy(buffer, source);
164     } else {
165         memcpy(buffer, source, buffer_length - 1);
166         buffer[buffer_length - 1] = '\0';
167     }
168     return buffer;
169 }
170 
171 
172 static char* try_core(const char* format, ...) KYUA_DEFS_FORMAT_PRINTF(1, 2);
173 
174 
175 /// Generates a path and checks if it exists.
176 ///
177 /// \param format Formatting string for the path to generate.
178 /// \param ... Arguments to the formatting string.
179 ///
180 /// \return A dynamically-allocated string containing the generated path if
181 /// there were no errors and the file pointed to by such path exists; NULL
182 /// otherwise.  The returned string must be relesed with free() by the caller.
183 static char*
try_core(const char * format,...)184 try_core(const char* format, ...)
185 {
186     char* path;
187     va_list ap;
188 
189     va_start(ap, format);
190     kyua_error_t error = kyua_text_vprintf(&path, format, ap);
191     va_end(ap);
192     if (kyua_error_is_set(error)) {
193         // Something went really wrong (and should not have happened).  Ignore
194         // this core file candidate.
195         kyua_error_free(error);
196         return NULL;
197     }
198 
199     if (access(path, F_OK) == -1) {
200         free(path);
201         return NULL;
202     } else {
203         return path;
204     }
205 }
206 
207 
208 /// Simple version of basename() that operates on constant strings.
209 ///
210 /// This is not 100% compatible with basename() because it may return an
211 /// unexpected string if the path ends with a slash.  For our purposes, this
212 /// does not matter, so we can use this simplified trick.
213 ///
214 /// \param path Path from which to compute the basename.
215 ///
216 /// \return A pointer within the input path pointing at the last component.
217 static const char*
const_basename(const char * path)218 const_basename(const char* path)
219 {
220     const char* last_slash = strrchr(path, '/');
221     return last_slash == NULL ? path : last_slash + 1;
222 }
223 
224 
225 /// Looks for a core file for the given program.
226 ///
227 /// \param name The basename of the binary that generated the core.
228 /// \param directory The directory from which the program was run.  We expect to
229 ///     find the core file in this directory.
230 /// \param dead_pid PID of the process that generated the core.  This is needed
231 ///     in some platforms.
232 ///
233 /// \return The path to the core file if found; otherwise none.
234 char*
kyua_stacktrace_find_core(const char * name,const char * directory,const pid_t dead_pid)235 kyua_stacktrace_find_core(const char* name, const char* directory,
236                           const pid_t dead_pid)
237 {
238     char* candidate = NULL;
239 
240     // TODO(jmmv): Other than checking all these defaults, in NetBSD we should
241     // also inspect the value of the kern.defcorename sysctl(2) MIB and use that
242     // as the first candidate.
243     //
244     // In Linux, the way to determine the name is by looking at
245     // /proc/sys/kernel/core_{pattern,uses_pid} as described by core(5).
246     // Unfortunately, there does not seem to be a standard API to parse these
247     // files, which makes checking for core files quite difficult if the
248     // defaults have been modified.
249 
250     // Default NetBSD naming scheme.
251     if (candidate == NULL && MAXCOMLEN > 0) {
252         char truncated[MAXCOMLEN + 1];
253         candidate = try_core("%s/%s.core", directory,
254                              slice(name, truncated, sizeof(truncated)));
255     }
256 
257     // Common naming scheme without the MAXCOMLEN truncation.
258     if (candidate == NULL)
259         candidate = try_core("%s/%s.core", directory, name);
260 
261     // Common naming scheme found in Linux systems.
262     if (candidate == NULL)
263         candidate = try_core("%s/core.%d", directory, (int)dead_pid);
264 
265     // Default Mac OS X naming scheme.
266     if (candidate == NULL)
267         candidate = try_core("/cores/core.%d", (int)dead_pid);
268 
269     // Common naming scheme found in Linux systems.  Attempted last due to the
270     // genericity of the core file name.
271     if (candidate == NULL)
272         candidate = try_core("%s/core", directory);
273 
274     if (candidate != NULL) {
275         char* abs_candidate;
276         kyua_error_t error = kyua_fs_make_absolute(candidate, &abs_candidate);
277         if (kyua_error_is_set(error)) {
278             kyua_error_free(error);
279             return candidate;  // Return possibly-relative path as a best guess.
280         } else {
281             free(candidate);
282             return abs_candidate;
283         }
284     } else {
285         return candidate;
286     }
287 }
288 
289 
290 /// Gathers a stacktrace of a crashed program.
291 ///
292 /// \param program The name of the binary that crashed and dumped a core file.
293 ///     Can be either absolute or relative.
294 /// \param dead_pid The PID of the process that dumped core.
295 /// \param original_run_params Parameters with which the original binary was
296 ///     executed.  These are reused to run GDB, but adjusted with GDB-specific
297 ///     settings.  Of special interest, the work directory is used to search for
298 ///     the core file.
299 /// \param output Stream into which to dump the stack trace and any additional
300 ///     information.
301 ///
302 /// \post If anything goes wrong, the diagnostic messages are written to the
303 /// output.  This function returns no errors.
304 void
kyua_stacktrace_dump(const char * program,const pid_t dead_pid,const kyua_run_params_t * original_run_params,FILE * output)305 kyua_stacktrace_dump(const char* program, const pid_t dead_pid,
306                      const kyua_run_params_t* original_run_params, FILE* output)
307 {
308     fprintf(output, "Process with PID %d dumped core; attempting to gather "
309             "stack trace\n", dead_pid);
310 
311     const kyua_run_params_t run_params = gdb_run_params(original_run_params);
312 
313     kyua_error_t error = kyua_error_ok();
314 
315     char* core_file = kyua_stacktrace_find_core(const_basename(program),
316                                                 run_params.work_directory,
317                                                 dead_pid);
318     if (core_file == NULL) {
319         fprintf(output, "Cannot find any core file\n");
320         goto out;
321     }
322 
323     // We must flush the output stream right before invoking fork, so that the
324     // subprocess does not have any unflushed data.  Failure to do so results in
325     // the messages above being written twice to the output.
326     fflush(output);
327     pid_t pid;
328     error = kyua_run_fork(&run_params, &pid);
329     if (!kyua_error_is_set(error) && pid == 0) {
330         run_gdb(program, core_file, output);
331     }
332     assert(pid != -1 && pid != 0);
333     if (kyua_error_is_set(error))
334         goto out_core_file;
335 
336     int status; bool timed_out;
337     error = kyua_run_wait(pid, &status, &timed_out);
338     if (kyua_error_is_set(error))
339         goto out_core_file;
340 
341     if (timed_out) {
342         fprintf(output, "GDB failed; timed out\n");
343     } else {
344         if (WIFEXITED(status)) {
345             if (WEXITSTATUS(status) == EXIT_SUCCESS)
346                 fprintf(output, "GDB exited successfully\n");
347             else
348                 fprintf(output, "GDB failed with code %d; see output above for "
349                         "details\n", WEXITSTATUS(status));
350         } else {
351             assert(WIFSIGNALED(status));
352             fprintf(output, "GDB received signal %d; see output above for "
353                     "details\n", WTERMSIG(status));
354         }
355     }
356 
357 out_core_file:
358     free(core_file);
359 out:
360     if (kyua_error_is_set(error)) {
361         kyua_error_fprintf(output, error, "Failed to gather stacktrace");
362         free(error);
363     }
364 }
365