xref: /freebsd-src/contrib/kyua/utils/stacktrace.cpp (revision b0d29bc47dba79f6f38e67eabadfb4b32ffd9390)
1*b0d29bc4SBrooks Davis // Copyright 2012 The Kyua Authors.
2*b0d29bc4SBrooks Davis // All rights reserved.
3*b0d29bc4SBrooks Davis //
4*b0d29bc4SBrooks Davis // Redistribution and use in source and binary forms, with or without
5*b0d29bc4SBrooks Davis // modification, are permitted provided that the following conditions are
6*b0d29bc4SBrooks Davis // met:
7*b0d29bc4SBrooks Davis //
8*b0d29bc4SBrooks Davis // * Redistributions of source code must retain the above copyright
9*b0d29bc4SBrooks Davis //   notice, this list of conditions and the following disclaimer.
10*b0d29bc4SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
11*b0d29bc4SBrooks Davis //   notice, this list of conditions and the following disclaimer in the
12*b0d29bc4SBrooks Davis //   documentation and/or other materials provided with the distribution.
13*b0d29bc4SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
14*b0d29bc4SBrooks Davis //   may be used to endorse or promote products derived from this software
15*b0d29bc4SBrooks Davis //   without specific prior written permission.
16*b0d29bc4SBrooks Davis //
17*b0d29bc4SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*b0d29bc4SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*b0d29bc4SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*b0d29bc4SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*b0d29bc4SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*b0d29bc4SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*b0d29bc4SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*b0d29bc4SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*b0d29bc4SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*b0d29bc4SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*b0d29bc4SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*b0d29bc4SBrooks Davis 
29*b0d29bc4SBrooks Davis #include "utils/stacktrace.hpp"
30*b0d29bc4SBrooks Davis 
31*b0d29bc4SBrooks Davis extern "C" {
32*b0d29bc4SBrooks Davis #include <sys/param.h>
33*b0d29bc4SBrooks Davis #include <sys/resource.h>
34*b0d29bc4SBrooks Davis 
35*b0d29bc4SBrooks Davis #include <unistd.h>
36*b0d29bc4SBrooks Davis }
37*b0d29bc4SBrooks Davis 
38*b0d29bc4SBrooks Davis #include <cerrno>
39*b0d29bc4SBrooks Davis #include <cstdlib>
40*b0d29bc4SBrooks Davis #include <cstring>
41*b0d29bc4SBrooks Davis #include <fstream>
42*b0d29bc4SBrooks Davis #include <iostream>
43*b0d29bc4SBrooks Davis #include <stdexcept>
44*b0d29bc4SBrooks Davis #include <string>
45*b0d29bc4SBrooks Davis #include <vector>
46*b0d29bc4SBrooks Davis 
47*b0d29bc4SBrooks Davis #include "utils/datetime.hpp"
48*b0d29bc4SBrooks Davis #include "utils/env.hpp"
49*b0d29bc4SBrooks Davis #include "utils/format/macros.hpp"
50*b0d29bc4SBrooks Davis #include "utils/fs/operations.hpp"
51*b0d29bc4SBrooks Davis #include "utils/fs/path.hpp"
52*b0d29bc4SBrooks Davis #include "utils/logging/macros.hpp"
53*b0d29bc4SBrooks Davis #include "utils/optional.ipp"
54*b0d29bc4SBrooks Davis #include "utils/process/executor.ipp"
55*b0d29bc4SBrooks Davis #include "utils/process/operations.hpp"
56*b0d29bc4SBrooks Davis #include "utils/process/status.hpp"
57*b0d29bc4SBrooks Davis #include "utils/sanity.hpp"
58*b0d29bc4SBrooks Davis 
59*b0d29bc4SBrooks Davis namespace datetime = utils::datetime;
60*b0d29bc4SBrooks Davis namespace executor = utils::process::executor;
61*b0d29bc4SBrooks Davis namespace fs = utils::fs;
62*b0d29bc4SBrooks Davis namespace process = utils::process;
63*b0d29bc4SBrooks Davis 
64*b0d29bc4SBrooks Davis using utils::none;
65*b0d29bc4SBrooks Davis using utils::optional;
66*b0d29bc4SBrooks Davis 
67*b0d29bc4SBrooks Davis 
68*b0d29bc4SBrooks Davis /// Built-in path to GDB.
69*b0d29bc4SBrooks Davis ///
70*b0d29bc4SBrooks Davis /// This is the value that should be passed to the find_gdb() function.  If this
71*b0d29bc4SBrooks Davis /// is an absolute path, then we use the binary specified by the variable; if it
72*b0d29bc4SBrooks Davis /// is a relative path, we look for the binary in the path.
73*b0d29bc4SBrooks Davis ///
74*b0d29bc4SBrooks Davis /// Test cases can override the value of this built-in constant to unit-test the
75*b0d29bc4SBrooks Davis /// behavior of the functions below.
76*b0d29bc4SBrooks Davis const char* utils::builtin_gdb = GDB;
77*b0d29bc4SBrooks Davis 
78*b0d29bc4SBrooks Davis 
79*b0d29bc4SBrooks Davis /// Maximum time the external GDB process is allowed to run for.
80*b0d29bc4SBrooks Davis datetime::delta utils::gdb_timeout(60, 0);
81*b0d29bc4SBrooks Davis 
82*b0d29bc4SBrooks Davis 
83*b0d29bc4SBrooks Davis namespace {
84*b0d29bc4SBrooks Davis 
85*b0d29bc4SBrooks Davis 
86*b0d29bc4SBrooks Davis /// Maximum length of the core file name, if known.
87*b0d29bc4SBrooks Davis ///
88*b0d29bc4SBrooks Davis /// Some operating systems impose a maximum length on the basename of the core
89*b0d29bc4SBrooks Davis /// file.  If MAXCOMLEN is defined, then we need to truncate the program name to
90*b0d29bc4SBrooks Davis /// this length before searching for the core file.  If no such limit is known,
91*b0d29bc4SBrooks Davis /// this is infinite.
92*b0d29bc4SBrooks Davis static const std::string::size_type max_core_name_length =
93*b0d29bc4SBrooks Davis #if defined(MAXCOMLEN)
94*b0d29bc4SBrooks Davis     MAXCOMLEN
95*b0d29bc4SBrooks Davis #else
96*b0d29bc4SBrooks Davis     std::string::npos
97*b0d29bc4SBrooks Davis #endif
98*b0d29bc4SBrooks Davis     ;
99*b0d29bc4SBrooks Davis 
100*b0d29bc4SBrooks Davis 
101*b0d29bc4SBrooks Davis /// Functor to execute GDB in a subprocess.
102*b0d29bc4SBrooks Davis class run_gdb {
103*b0d29bc4SBrooks Davis     /// Path to the GDB binary to use.
104*b0d29bc4SBrooks Davis     const fs::path& _gdb;
105*b0d29bc4SBrooks Davis 
106*b0d29bc4SBrooks Davis     /// Path to the program being debugged.
107*b0d29bc4SBrooks Davis     const fs::path& _program;
108*b0d29bc4SBrooks Davis 
109*b0d29bc4SBrooks Davis     /// Path to the dumped core.
110*b0d29bc4SBrooks Davis     const fs::path& _core_name;
111*b0d29bc4SBrooks Davis 
112*b0d29bc4SBrooks Davis public:
113*b0d29bc4SBrooks Davis     /// Constructs the functor.
114*b0d29bc4SBrooks Davis     ///
115*b0d29bc4SBrooks Davis     /// \param gdb_ Path to the GDB binary to use.
116*b0d29bc4SBrooks Davis     /// \param program_ Path to the program being debugged.  Can be relative to
117*b0d29bc4SBrooks Davis     ///     the given work directory.
118*b0d29bc4SBrooks Davis     /// \param core_name_ Path to the dumped core.  Use find_core() to deduce
119*b0d29bc4SBrooks Davis     ///     a valid candidate.  Can be relative to the given work directory.
run_gdb(const fs::path & gdb_,const fs::path & program_,const fs::path & core_name_)120*b0d29bc4SBrooks Davis     run_gdb(const fs::path& gdb_, const fs::path& program_,
121*b0d29bc4SBrooks Davis             const fs::path& core_name_) :
122*b0d29bc4SBrooks Davis         _gdb(gdb_), _program(program_), _core_name(core_name_)
123*b0d29bc4SBrooks Davis     {
124*b0d29bc4SBrooks Davis     }
125*b0d29bc4SBrooks Davis 
126*b0d29bc4SBrooks Davis     /// Executes GDB.
127*b0d29bc4SBrooks Davis     ///
128*b0d29bc4SBrooks Davis     /// \param control_directory Directory where we can store control files to
129*b0d29bc4SBrooks Davis     ///     not clobber any files created by the program being debugged.
130*b0d29bc4SBrooks Davis     void
operator ()(const fs::path & control_directory)131*b0d29bc4SBrooks Davis     operator()(const fs::path& control_directory)
132*b0d29bc4SBrooks Davis     {
133*b0d29bc4SBrooks Davis         const fs::path gdb_script_path = control_directory / "gdb.script";
134*b0d29bc4SBrooks Davis 
135*b0d29bc4SBrooks Davis         // Old versions of GDB, such as the one shipped by FreeBSD as of
136*b0d29bc4SBrooks Davis         // 11.0-CURRENT on 2014-11-26, do not support scripts on the command
137*b0d29bc4SBrooks Davis         // line via the '-ex' flag.  Instead, we have to create a script file
138*b0d29bc4SBrooks Davis         // and use that instead.
139*b0d29bc4SBrooks Davis         std::ofstream gdb_script(gdb_script_path.c_str());
140*b0d29bc4SBrooks Davis         if (!gdb_script) {
141*b0d29bc4SBrooks Davis             std::cerr << "Cannot create GDB script\n";
142*b0d29bc4SBrooks Davis             ::_exit(EXIT_FAILURE);
143*b0d29bc4SBrooks Davis         }
144*b0d29bc4SBrooks Davis         gdb_script << "backtrace\n";
145*b0d29bc4SBrooks Davis         gdb_script.close();
146*b0d29bc4SBrooks Davis 
147*b0d29bc4SBrooks Davis         utils::unsetenv("TERM");
148*b0d29bc4SBrooks Davis 
149*b0d29bc4SBrooks Davis         std::vector< std::string > args;
150*b0d29bc4SBrooks Davis         args.push_back("-batch");
151*b0d29bc4SBrooks Davis         args.push_back("-q");
152*b0d29bc4SBrooks Davis         args.push_back("-x");
153*b0d29bc4SBrooks Davis         args.push_back(gdb_script_path.str());
154*b0d29bc4SBrooks Davis         args.push_back(_program.str());
155*b0d29bc4SBrooks Davis         args.push_back(_core_name.str());
156*b0d29bc4SBrooks Davis 
157*b0d29bc4SBrooks Davis         // Force all GDB output to go to stderr.  We print messages to stderr
158*b0d29bc4SBrooks Davis         // when grabbing the stacktrace and we do not want GDB's output to end
159*b0d29bc4SBrooks Davis         // up split in two different files.
160*b0d29bc4SBrooks Davis         if (::dup2(STDERR_FILENO, STDOUT_FILENO) == -1) {
161*b0d29bc4SBrooks Davis             std::cerr << "Cannot redirect stdout to stderr\n";
162*b0d29bc4SBrooks Davis             ::_exit(EXIT_FAILURE);
163*b0d29bc4SBrooks Davis         }
164*b0d29bc4SBrooks Davis 
165*b0d29bc4SBrooks Davis         process::exec(_gdb, args);
166*b0d29bc4SBrooks Davis     }
167*b0d29bc4SBrooks Davis };
168*b0d29bc4SBrooks Davis 
169*b0d29bc4SBrooks Davis 
170*b0d29bc4SBrooks Davis }  // anonymous namespace
171*b0d29bc4SBrooks Davis 
172*b0d29bc4SBrooks Davis 
173*b0d29bc4SBrooks Davis /// Looks for the path to the GDB binary.
174*b0d29bc4SBrooks Davis ///
175*b0d29bc4SBrooks Davis /// \return The absolute path to the GDB binary if any, otherwise none.  Note
176*b0d29bc4SBrooks Davis /// that the returned path may or may not be valid: there is no guarantee that
177*b0d29bc4SBrooks Davis /// the path exists and is executable.
178*b0d29bc4SBrooks Davis optional< fs::path >
find_gdb(void)179*b0d29bc4SBrooks Davis utils::find_gdb(void)
180*b0d29bc4SBrooks Davis {
181*b0d29bc4SBrooks Davis     if (std::strlen(builtin_gdb) == 0) {
182*b0d29bc4SBrooks Davis         LW("The builtin path to GDB is bogus, which probably indicates a bug "
183*b0d29bc4SBrooks Davis            "in the build system; cannot gather stack traces");
184*b0d29bc4SBrooks Davis         return none;
185*b0d29bc4SBrooks Davis     }
186*b0d29bc4SBrooks Davis 
187*b0d29bc4SBrooks Davis     const fs::path gdb(builtin_gdb);
188*b0d29bc4SBrooks Davis     if (gdb.is_absolute())
189*b0d29bc4SBrooks Davis         return utils::make_optional(gdb);
190*b0d29bc4SBrooks Davis     else
191*b0d29bc4SBrooks Davis         return fs::find_in_path(gdb.c_str());
192*b0d29bc4SBrooks Davis }
193*b0d29bc4SBrooks Davis 
194*b0d29bc4SBrooks Davis 
195*b0d29bc4SBrooks Davis /// Looks for a core file for the given program.
196*b0d29bc4SBrooks Davis ///
197*b0d29bc4SBrooks Davis /// \param program The name of the binary that generated the core file.  Can be
198*b0d29bc4SBrooks Davis ///     either absolute or relative.
199*b0d29bc4SBrooks Davis /// \param status The exit status of the program.  This is necessary to gather
200*b0d29bc4SBrooks Davis ///     the PID.
201*b0d29bc4SBrooks Davis /// \param work_directory The directory from which the program was run.
202*b0d29bc4SBrooks Davis ///
203*b0d29bc4SBrooks Davis /// \return The path to the core file, if found; otherwise none.
204*b0d29bc4SBrooks Davis optional< fs::path >
find_core(const fs::path & program,const process::status & status,const fs::path & work_directory)205*b0d29bc4SBrooks Davis utils::find_core(const fs::path& program, const process::status& status,
206*b0d29bc4SBrooks Davis                  const fs::path& work_directory)
207*b0d29bc4SBrooks Davis {
208*b0d29bc4SBrooks Davis     std::vector< fs::path > candidates;
209*b0d29bc4SBrooks Davis 
210*b0d29bc4SBrooks Davis     candidates.push_back(work_directory /
211*b0d29bc4SBrooks Davis         (program.leaf_name().substr(0, max_core_name_length) + ".core"));
212*b0d29bc4SBrooks Davis     if (program.is_absolute()) {
213*b0d29bc4SBrooks Davis         candidates.push_back(program.branch_path() /
214*b0d29bc4SBrooks Davis             (program.leaf_name().substr(0, max_core_name_length) + ".core"));
215*b0d29bc4SBrooks Davis     }
216*b0d29bc4SBrooks Davis     candidates.push_back(work_directory / (F("core.%s") % status.dead_pid()));
217*b0d29bc4SBrooks Davis     candidates.push_back(fs::path("/cores") /
218*b0d29bc4SBrooks Davis                          (F("core.%s") % status.dead_pid()));
219*b0d29bc4SBrooks Davis 
220*b0d29bc4SBrooks Davis     for (std::vector< fs::path >::const_iterator iter = candidates.begin();
221*b0d29bc4SBrooks Davis          iter != candidates.end(); ++iter) {
222*b0d29bc4SBrooks Davis         if (fs::exists(*iter)) {
223*b0d29bc4SBrooks Davis             LD(F("Attempting core file candidate %s: found") % *iter);
224*b0d29bc4SBrooks Davis             return utils::make_optional(*iter);
225*b0d29bc4SBrooks Davis         } else {
226*b0d29bc4SBrooks Davis             LD(F("Attempting core file candidate %s: not found") % *iter);
227*b0d29bc4SBrooks Davis         }
228*b0d29bc4SBrooks Davis     }
229*b0d29bc4SBrooks Davis     return none;
230*b0d29bc4SBrooks Davis }
231*b0d29bc4SBrooks Davis 
232*b0d29bc4SBrooks Davis 
233*b0d29bc4SBrooks Davis /// Raises core size limit to its possible maximum.
234*b0d29bc4SBrooks Davis ///
235*b0d29bc4SBrooks Davis /// This is a best-effort operation.  There is no guarantee that the operation
236*b0d29bc4SBrooks Davis /// will yield a large-enough limit to generate any possible core file.
237*b0d29bc4SBrooks Davis ///
238*b0d29bc4SBrooks Davis /// \return True if the core size could be unlimited; false otherwise.
239*b0d29bc4SBrooks Davis bool
unlimit_core_size(void)240*b0d29bc4SBrooks Davis utils::unlimit_core_size(void)
241*b0d29bc4SBrooks Davis {
242*b0d29bc4SBrooks Davis     bool ok;
243*b0d29bc4SBrooks Davis 
244*b0d29bc4SBrooks Davis     struct ::rlimit rl;
245*b0d29bc4SBrooks Davis     if (::getrlimit(RLIMIT_CORE, &rl) == -1) {
246*b0d29bc4SBrooks Davis         const int original_errno = errno;
247*b0d29bc4SBrooks Davis         LW(F("getrlimit should not have failed but got: %s") %
248*b0d29bc4SBrooks Davis            std::strerror(original_errno));
249*b0d29bc4SBrooks Davis         ok = false;
250*b0d29bc4SBrooks Davis     } else {
251*b0d29bc4SBrooks Davis         if (rl.rlim_max == 0) {
252*b0d29bc4SBrooks Davis             LW("getrlimit returned 0 for RLIMIT_CORE rlim_max; cannot raise "
253*b0d29bc4SBrooks Davis                "soft core limit");
254*b0d29bc4SBrooks Davis             ok = false;
255*b0d29bc4SBrooks Davis         } else {
256*b0d29bc4SBrooks Davis             rl.rlim_cur = rl.rlim_max;
257*b0d29bc4SBrooks Davis             LD(F("Raising soft core size limit to %s (hard value)") %
258*b0d29bc4SBrooks Davis                rl.rlim_cur);
259*b0d29bc4SBrooks Davis             if (::setrlimit(RLIMIT_CORE, &rl) == -1) {
260*b0d29bc4SBrooks Davis                 const int original_errno = errno;
261*b0d29bc4SBrooks Davis                 LW(F("setrlimit should not have failed but got: %s") %
262*b0d29bc4SBrooks Davis                    std::strerror(original_errno));
263*b0d29bc4SBrooks Davis                 ok = false;
264*b0d29bc4SBrooks Davis             } else {
265*b0d29bc4SBrooks Davis                 ok = true;
266*b0d29bc4SBrooks Davis             }
267*b0d29bc4SBrooks Davis         }
268*b0d29bc4SBrooks Davis     }
269*b0d29bc4SBrooks Davis 
270*b0d29bc4SBrooks Davis     return ok;
271*b0d29bc4SBrooks Davis }
272*b0d29bc4SBrooks Davis 
273*b0d29bc4SBrooks Davis 
274*b0d29bc4SBrooks Davis /// Gathers a stacktrace of a crashed program.
275*b0d29bc4SBrooks Davis ///
276*b0d29bc4SBrooks Davis /// \param program The name of the binary that crashed and dumped a core file.
277*b0d29bc4SBrooks Davis ///     Can be either absolute or relative.
278*b0d29bc4SBrooks Davis /// \param executor_handle The executor handler to get the status from and
279*b0d29bc4SBrooks Davis ///     gdb handler from.
280*b0d29bc4SBrooks Davis /// \param exit_handle The exit handler to stream additional diagnostic
281*b0d29bc4SBrooks Davis ///     information from (stderr) and for redirecting to additional
282*b0d29bc4SBrooks Davis ///     information to gdb from.
283*b0d29bc4SBrooks Davis ///
284*b0d29bc4SBrooks Davis /// \post If anything goes wrong, the diagnostic messages are written to the
285*b0d29bc4SBrooks Davis /// output.  This function should not throw.
286*b0d29bc4SBrooks Davis void
dump_stacktrace(const fs::path & program,executor::executor_handle & executor_handle,const executor::exit_handle & exit_handle)287*b0d29bc4SBrooks Davis utils::dump_stacktrace(const fs::path& program,
288*b0d29bc4SBrooks Davis                        executor::executor_handle& executor_handle,
289*b0d29bc4SBrooks Davis                        const executor::exit_handle& exit_handle)
290*b0d29bc4SBrooks Davis {
291*b0d29bc4SBrooks Davis     PRE(exit_handle.status());
292*b0d29bc4SBrooks Davis     const process::status& status = exit_handle.status().get();
293*b0d29bc4SBrooks Davis     PRE(status.signaled() && status.coredump());
294*b0d29bc4SBrooks Davis 
295*b0d29bc4SBrooks Davis     std::ofstream gdb_err(exit_handle.stderr_file().c_str(), std::ios::app);
296*b0d29bc4SBrooks Davis     if (!gdb_err) {
297*b0d29bc4SBrooks Davis         LW(F("Failed to open %s to append GDB's output") %
298*b0d29bc4SBrooks Davis            exit_handle.stderr_file());
299*b0d29bc4SBrooks Davis         return;
300*b0d29bc4SBrooks Davis     }
301*b0d29bc4SBrooks Davis 
302*b0d29bc4SBrooks Davis     gdb_err << F("Process with PID %s exited with signal %s and dumped core; "
303*b0d29bc4SBrooks Davis                  "attempting to gather stack trace\n") %
304*b0d29bc4SBrooks Davis         status.dead_pid() % status.termsig();
305*b0d29bc4SBrooks Davis 
306*b0d29bc4SBrooks Davis     const optional< fs::path > gdb = utils::find_gdb();
307*b0d29bc4SBrooks Davis     if (!gdb) {
308*b0d29bc4SBrooks Davis         gdb_err << F("Cannot find GDB binary; builtin was '%s'\n") %
309*b0d29bc4SBrooks Davis             builtin_gdb;
310*b0d29bc4SBrooks Davis         return;
311*b0d29bc4SBrooks Davis     }
312*b0d29bc4SBrooks Davis 
313*b0d29bc4SBrooks Davis     const optional< fs::path > core_file = find_core(
314*b0d29bc4SBrooks Davis         program, status, exit_handle.work_directory());
315*b0d29bc4SBrooks Davis     if (!core_file) {
316*b0d29bc4SBrooks Davis         gdb_err << F("Cannot find any core file\n");
317*b0d29bc4SBrooks Davis         return;
318*b0d29bc4SBrooks Davis     }
319*b0d29bc4SBrooks Davis 
320*b0d29bc4SBrooks Davis     gdb_err.flush();
321*b0d29bc4SBrooks Davis     const executor::exec_handle exec_handle =
322*b0d29bc4SBrooks Davis         executor_handle.spawn_followup(
323*b0d29bc4SBrooks Davis             run_gdb(gdb.get(), program, core_file.get()),
324*b0d29bc4SBrooks Davis             exit_handle, gdb_timeout);
325*b0d29bc4SBrooks Davis     const executor::exit_handle gdb_exit_handle =
326*b0d29bc4SBrooks Davis         executor_handle.wait(exec_handle);
327*b0d29bc4SBrooks Davis 
328*b0d29bc4SBrooks Davis     const optional< process::status >& gdb_status = gdb_exit_handle.status();
329*b0d29bc4SBrooks Davis     if (!gdb_status) {
330*b0d29bc4SBrooks Davis         gdb_err << "GDB timed out\n";
331*b0d29bc4SBrooks Davis     } else {
332*b0d29bc4SBrooks Davis         if (gdb_status.get().exited() &&
333*b0d29bc4SBrooks Davis             gdb_status.get().exitstatus() == EXIT_SUCCESS) {
334*b0d29bc4SBrooks Davis             gdb_err << "GDB exited successfully\n";
335*b0d29bc4SBrooks Davis         } else {
336*b0d29bc4SBrooks Davis             gdb_err << "GDB failed; see output above for details\n";
337*b0d29bc4SBrooks Davis         }
338*b0d29bc4SBrooks Davis     }
339*b0d29bc4SBrooks Davis }
340*b0d29bc4SBrooks Davis 
341*b0d29bc4SBrooks Davis 
342*b0d29bc4SBrooks Davis /// Gathers a stacktrace of a program if it crashed.
343*b0d29bc4SBrooks Davis ///
344*b0d29bc4SBrooks Davis /// This is just a convenience function to allow appending the stacktrace to an
345*b0d29bc4SBrooks Davis /// existing file and to permit reusing the status as returned by auxiliary
346*b0d29bc4SBrooks Davis /// process-spawning functions.
347*b0d29bc4SBrooks Davis ///
348*b0d29bc4SBrooks Davis /// \param program The name of the binary that crashed and dumped a core file.
349*b0d29bc4SBrooks Davis ///     Can be either absolute or relative.
350*b0d29bc4SBrooks Davis /// \param executor_handle The executor handler to get the status from and
351*b0d29bc4SBrooks Davis ///     gdb handler from.
352*b0d29bc4SBrooks Davis /// \param exit_handle The exit handler to stream additional diagnostic
353*b0d29bc4SBrooks Davis ///     information from (stderr) and for redirecting to additional
354*b0d29bc4SBrooks Davis ///     information to gdb from.
355*b0d29bc4SBrooks Davis ///
356*b0d29bc4SBrooks Davis /// \throw std::runtime_error If the output file cannot be opened.
357*b0d29bc4SBrooks Davis ///
358*b0d29bc4SBrooks Davis /// \post If anything goes wrong with the stack gatheringq, the diagnostic
359*b0d29bc4SBrooks Davis /// messages are written to the output.
360*b0d29bc4SBrooks Davis void
dump_stacktrace_if_available(const fs::path & program,executor::executor_handle & executor_handle,const executor::exit_handle & exit_handle)361*b0d29bc4SBrooks Davis utils::dump_stacktrace_if_available(const fs::path& program,
362*b0d29bc4SBrooks Davis                                     executor::executor_handle& executor_handle,
363*b0d29bc4SBrooks Davis                                     const executor::exit_handle& exit_handle)
364*b0d29bc4SBrooks Davis {
365*b0d29bc4SBrooks Davis     const optional< process::status >& status = exit_handle.status();
366*b0d29bc4SBrooks Davis     if (!status || !status.get().signaled() || !status.get().coredump())
367*b0d29bc4SBrooks Davis         return;
368*b0d29bc4SBrooks Davis 
369*b0d29bc4SBrooks Davis     dump_stacktrace(program, executor_handle, exit_handle);
370*b0d29bc4SBrooks Davis }
371