1 // Copyright 2012 The Kyua Authors.
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 "utils/stacktrace.hpp"
30
31 extern "C" {
32 #include <sys/resource.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35
36 #include <signal.h>
37 #include <unistd.h>
38 }
39
40 #include <iostream>
41 #include <sstream>
42
43 #include <atf-c++.hpp>
44
45 #include "utils/datetime.hpp"
46 #include "utils/env.hpp"
47 #include "utils/fs/operations.hpp"
48 #include "utils/fs/path.hpp"
49 #include "utils/optional.ipp"
50 #include "utils/process/executor.ipp"
51 #include "utils/process/child.ipp"
52 #include "utils/process/operations.hpp"
53 #include "utils/process/status.hpp"
54 #include "utils/sanity.hpp"
55 #include "utils/test_utils.ipp"
56
57 namespace datetime = utils::datetime;
58 namespace executor = utils::process::executor;
59 namespace fs = utils::fs;
60 namespace process = utils::process;
61
62 using utils::none;
63 using utils::optional;
64
65
66 namespace {
67
68
69 /// Functor to execute a binary in a subprocess.
70 ///
71 /// The provided binary is copied to the current work directory before being
72 /// executed and the copy is given the name chosen by the user. The copy is
73 /// necessary so that we have a deterministic location for where core files may
74 /// be dumped (if they happen to be dumped in the current directory).
75 class crash_me {
76 /// Path to the binary to execute.
77 const fs::path _binary;
78
79 /// Name of the binary after being copied.
80 const fs::path _copy_name;
81
82 public:
83 /// Constructor.
84 ///
85 /// \param binary_ Path to binary to execute.
86 /// \param copy_name_ Name of the binary after being copied. If empty,
87 /// use the leaf name of binary_.
crash_me(const fs::path & binary_,const std::string & copy_name_="")88 explicit crash_me(const fs::path& binary_,
89 const std::string& copy_name_ = "") :
90 _binary(binary_),
91 _copy_name(copy_name_.empty() ? binary_.leaf_name() : copy_name_)
92 {
93 }
94
95 /// Runs the binary.
96 void
operator ()(void) const97 operator()(void) const UTILS_NORETURN
98 {
99 atf::utils::copy_file(_binary.str(), _copy_name.str());
100
101 const std::vector< std::string > args;
102 process::exec(_copy_name, args);
103 }
104
105 /// Runs the binary.
106 ///
107 /// This interface is exposed to support passing crash_me to the executor.
108 void
operator ()(const fs::path &) const109 operator()(const fs::path& /* control_directory */) const
110 UTILS_NORETURN
111 {
112 (*this)(); // Delegate to ensure the two entry points remain in sync.
113 }
114 };
115
116
117 static void child_exit(const fs::path&) UTILS_NORETURN;
118
119
120 /// Subprocess that exits cleanly.
121 static void
child_exit(const fs::path &)122 child_exit(const fs::path& /* control_directory */)
123 {
124 ::_exit(EXIT_SUCCESS);
125 }
126
127
128 static void child_pause(const fs::path&) UTILS_NORETURN;
129
130
131 /// Subprocess that just blocks.
132 static void
child_pause(const fs::path &)133 child_pause(const fs::path& /* control_directory */)
134 {
135 sigset_t mask;
136 sigemptyset(&mask);
137 for (;;) {
138 ::sigsuspend(&mask);
139 }
140 std::abort();
141 }
142
143
144 /// Generates a core dump, if possible.
145 ///
146 /// \post If this fails to generate a core file, the test case is marked as
147 /// skipped. The caller can rely on this when attempting further checks on the
148 /// core dump by assuming that the core dump exists somewhere.
149 ///
150 /// \param test_case Pointer to the caller test case, needed to obtain the path
151 /// to the source directory.
152 /// \param base_name Name of the binary to execute, which will be a copy of a
153 /// helper binary that always crashes. This name should later be part of
154 /// the core filename.
155 ///
156 /// \return The status of the crashed binary.
157 static process::status
generate_core(const atf::tests::tc * test_case,const char * base_name)158 generate_core(const atf::tests::tc* test_case, const char* base_name)
159 {
160 utils::prepare_coredump_test(test_case);
161
162 const fs::path helper = fs::path(test_case->get_config_var("srcdir")) /
163 "stacktrace_helper";
164
165 const process::status status = process::child::fork_files(
166 crash_me(helper, base_name),
167 fs::path("unused.out"), fs::path("unused.err"))->wait();
168 ATF_REQUIRE(status.signaled());
169 if (!status.coredump())
170 ATF_SKIP("Test failed to generate core dump");
171 return status;
172 }
173
174
175 /// Generates a core dump, if possible.
176 ///
177 /// \post If this fails to generate a core file, the test case is marked as
178 /// skipped. The caller can rely on this when attempting further checks on the
179 /// core dump by assuming that the core dump exists somewhere.
180 ///
181 /// \param test_case Pointer to the caller test case, needed to obtain the path
182 /// to the source directory.
183 /// \param base_name Name of the binary to execute, which will be a copy of a
184 /// helper binary that always crashes. This name should later be part of
185 /// the core filename.
186 /// \param executor_handle Executor to use to generate the core dump.
187 ///
188 /// \return The exit handle of the subprocess so that a stacktrace can be
189 /// executed reusing this context later on.
190 static executor::exit_handle
generate_core(const atf::tests::tc * test_case,const char * base_name,executor::executor_handle & executor_handle)191 generate_core(const atf::tests::tc* test_case, const char* base_name,
192 executor::executor_handle& executor_handle)
193 {
194 utils::prepare_coredump_test(test_case);
195
196 const fs::path helper = fs::path(test_case->get_config_var("srcdir")) /
197 "stacktrace_helper";
198
199 const executor::exec_handle exec_handle = executor_handle.spawn(
200 crash_me(helper, base_name), datetime::delta(60, 0), none, none, none);
201 const executor::exit_handle exit_handle = executor_handle.wait(exec_handle);
202
203 if (!exit_handle.status())
204 ATF_SKIP("Test failed to generate core dump (timed out)");
205 const process::status& status = exit_handle.status().get();
206 ATF_REQUIRE(status.signaled());
207 if (!status.coredump())
208 ATF_SKIP("Test failed to generate core dump");
209
210 return exit_handle;
211 }
212
213
214 /// Creates a script.
215 ///
216 /// \param script Path to the script to create.
217 /// \param contents Contents of the script.
218 static void
create_script(const char * script,const std::string & contents)219 create_script(const char* script, const std::string& contents)
220 {
221 atf::utils::create_file(script, "#! /bin/sh\n\n" + contents);
222 ATF_REQUIRE(::chmod(script, 0755) != -1);
223 }
224
225
226 } // anonymous namespace
227
228
229 ATF_TEST_CASE_WITHOUT_HEAD(unlimit_core_size);
ATF_TEST_CASE_BODY(unlimit_core_size)230 ATF_TEST_CASE_BODY(unlimit_core_size)
231 {
232 utils::require_run_coredump_tests(this);
233
234 struct rlimit rl;
235 rl.rlim_cur = 0;
236 rl.rlim_max = RLIM_INFINITY;
237 if (::setrlimit(RLIMIT_CORE, &rl) == -1)
238 skip("Failed to lower the core size limit");
239
240 ATF_REQUIRE(utils::unlimit_core_size());
241
242 const fs::path helper = fs::path(get_config_var("srcdir")) /
243 "stacktrace_helper";
244 const process::status status = process::child::fork_files(
245 crash_me(helper),
246 fs::path("unused.out"), fs::path("unused.err"))->wait();
247 ATF_REQUIRE(status.signaled());
248 if (!status.coredump())
249 fail("Core not dumped as expected");
250 }
251
252
253 ATF_TEST_CASE_WITHOUT_HEAD(unlimit_core_size__hard_is_zero);
ATF_TEST_CASE_BODY(unlimit_core_size__hard_is_zero)254 ATF_TEST_CASE_BODY(unlimit_core_size__hard_is_zero)
255 {
256 utils::require_run_coredump_tests(this);
257
258 struct rlimit rl;
259 rl.rlim_cur = 0;
260 rl.rlim_max = 0;
261 if (::setrlimit(RLIMIT_CORE, &rl) == -1)
262 skip("Failed to lower the core size limit");
263
264 ATF_REQUIRE(!utils::unlimit_core_size());
265
266 const fs::path helper = fs::path(get_config_var("srcdir")) /
267 "stacktrace_helper";
268 const process::status status = process::child::fork_files(
269 crash_me(helper),
270 fs::path("unused.out"), fs::path("unused.err"))->wait();
271 ATF_REQUIRE(status.signaled());
272 ATF_REQUIRE(!status.coredump());
273 }
274
275
276 ATF_TEST_CASE_WITHOUT_HEAD(find_gdb__use_builtin);
ATF_TEST_CASE_BODY(find_gdb__use_builtin)277 ATF_TEST_CASE_BODY(find_gdb__use_builtin)
278 {
279 utils::builtin_gdb = "/path/to/gdb";
280 optional< fs::path > gdb = utils::find_gdb();
281 ATF_REQUIRE(gdb);
282 ATF_REQUIRE_EQ("/path/to/gdb", gdb.get().str());
283 }
284
285
286 ATF_TEST_CASE_WITHOUT_HEAD(find_gdb__search_builtin__ok);
ATF_TEST_CASE_BODY(find_gdb__search_builtin__ok)287 ATF_TEST_CASE_BODY(find_gdb__search_builtin__ok)
288 {
289 atf::utils::create_file("custom-name", "");
290 ATF_REQUIRE(::chmod("custom-name", 0755) != -1);
291 const fs::path exp_gdb = fs::path("custom-name").to_absolute();
292
293 utils::setenv("PATH", "/non-existent/location:.:/bin");
294
295 utils::builtin_gdb = "custom-name";
296 optional< fs::path > gdb = utils::find_gdb();
297 ATF_REQUIRE(gdb);
298 ATF_REQUIRE_EQ(exp_gdb, gdb.get());
299 }
300
301
302 ATF_TEST_CASE_WITHOUT_HEAD(find_gdb__search_builtin__fail);
ATF_TEST_CASE_BODY(find_gdb__search_builtin__fail)303 ATF_TEST_CASE_BODY(find_gdb__search_builtin__fail)
304 {
305 utils::setenv("PATH", ".");
306 utils::builtin_gdb = "foo";
307 optional< fs::path > gdb = utils::find_gdb();
308 ATF_REQUIRE(!gdb);
309 }
310
311
312 ATF_TEST_CASE_WITHOUT_HEAD(find_gdb__bogus_value);
ATF_TEST_CASE_BODY(find_gdb__bogus_value)313 ATF_TEST_CASE_BODY(find_gdb__bogus_value)
314 {
315 utils::builtin_gdb = "";
316 optional< fs::path > gdb = utils::find_gdb();
317 ATF_REQUIRE(!gdb);
318 }
319
320
321 ATF_TEST_CASE_WITHOUT_HEAD(find_core__found__short);
ATF_TEST_CASE_BODY(find_core__found__short)322 ATF_TEST_CASE_BODY(find_core__found__short)
323 {
324 const process::status status = generate_core(this, "short");
325 INV(status.coredump());
326 const optional< fs::path > core_name = utils::find_core(
327 fs::path("short"), status, fs::path("."));
328 if (!core_name)
329 fail("Core dumped, but no candidates found");
330 ATF_REQUIRE(core_name.get().str().find("core") != std::string::npos);
331 ATF_REQUIRE(fs::exists(core_name.get()));
332 }
333
334
335 ATF_TEST_CASE_WITHOUT_HEAD(find_core__found__long);
ATF_TEST_CASE_BODY(find_core__found__long)336 ATF_TEST_CASE_BODY(find_core__found__long)
337 {
338 const process::status status = generate_core(
339 this, "long-name-that-may-be-truncated-in-some-systems");
340 INV(status.coredump());
341 const optional< fs::path > core_name = utils::find_core(
342 fs::path("long-name-that-may-be-truncated-in-some-systems"),
343 status, fs::path("."));
344 if (!core_name)
345 fail("Core dumped, but no candidates found");
346 ATF_REQUIRE(core_name.get().str().find("core") != std::string::npos);
347 ATF_REQUIRE(fs::exists(core_name.get()));
348 }
349
350
351 ATF_TEST_CASE_WITHOUT_HEAD(find_core__not_found);
ATF_TEST_CASE_BODY(find_core__not_found)352 ATF_TEST_CASE_BODY(find_core__not_found)
353 {
354 const process::status status = process::status::fake_signaled(SIGILL, true);
355 const optional< fs::path > core_name = utils::find_core(
356 fs::path("missing"), status, fs::path("."));
357 if (core_name)
358 fail("Core not dumped, but candidate found: " + core_name.get().str());
359 }
360
361
362 ATF_TEST_CASE(dump_stacktrace__integration);
ATF_TEST_CASE_HEAD(dump_stacktrace__integration)363 ATF_TEST_CASE_HEAD(dump_stacktrace__integration)
364 {
365 set_md_var("require.progs", utils::builtin_gdb);
366 }
ATF_TEST_CASE_BODY(dump_stacktrace__integration)367 ATF_TEST_CASE_BODY(dump_stacktrace__integration)
368 {
369 executor::executor_handle handle = executor::setup();
370
371 executor::exit_handle exit_handle = generate_core(this, "short", handle);
372 INV(exit_handle.status());
373 INV(exit_handle.status().get().coredump());
374
375 std::ostringstream output;
376 utils::dump_stacktrace(fs::path("short"), handle, exit_handle);
377
378 // It is hard to validate the execution of an arbitrary GDB of which we do
379 // not know anything. Just assume that the backtrace, at the very least,
380 // prints a couple of frame identifiers.
381 ATF_REQUIRE(!atf::utils::grep_file("#0", exit_handle.stdout_file().str()));
382 ATF_REQUIRE( atf::utils::grep_file("#0", exit_handle.stderr_file().str()));
383 ATF_REQUIRE(!atf::utils::grep_file("#1", exit_handle.stdout_file().str()));
384 ATF_REQUIRE( atf::utils::grep_file("#1", exit_handle.stderr_file().str()));
385
386 exit_handle.cleanup();
387 handle.cleanup();
388 }
389
390
391 ATF_TEST_CASE_WITHOUT_HEAD(dump_stacktrace__ok);
ATF_TEST_CASE_BODY(dump_stacktrace__ok)392 ATF_TEST_CASE_BODY(dump_stacktrace__ok)
393 {
394 utils::setenv("PATH", ".");
395 create_script("fake-gdb", "echo 'frame 1'; echo 'frame 2'; "
396 "echo 'some warning' 1>&2; exit 0");
397 utils::builtin_gdb = "fake-gdb";
398
399 executor::executor_handle handle = executor::setup();
400 executor::exit_handle exit_handle = generate_core(this, "short", handle);
401 INV(exit_handle.status());
402 INV(exit_handle.status().get().coredump());
403
404 utils::dump_stacktrace(fs::path("short"), handle, exit_handle);
405
406 // Note how all output is expected on stderr even for the messages that the
407 // script decided to send to stdout.
408 ATF_REQUIRE(atf::utils::grep_file("exited with signal [0-9]* and dumped",
409 exit_handle.stderr_file().str()));
410 ATF_REQUIRE(atf::utils::grep_file("^frame 1$",
411 exit_handle.stderr_file().str()));
412 ATF_REQUIRE(atf::utils::grep_file("^frame 2$",
413 exit_handle.stderr_file().str()));
414 ATF_REQUIRE(atf::utils::grep_file("^some warning$",
415 exit_handle.stderr_file().str()));
416 ATF_REQUIRE(atf::utils::grep_file("GDB exited successfully",
417 exit_handle.stderr_file().str()));
418
419 exit_handle.cleanup();
420 handle.cleanup();
421 }
422
423
424 ATF_TEST_CASE_WITHOUT_HEAD(dump_stacktrace__cannot_find_core);
ATF_TEST_CASE_BODY(dump_stacktrace__cannot_find_core)425 ATF_TEST_CASE_BODY(dump_stacktrace__cannot_find_core)
426 {
427 // Make sure we can find a GDB binary so that we don't fail the test for
428 // the wrong reason.
429 utils::setenv("PATH", ".");
430 utils::builtin_gdb = "fake-gdb";
431 atf::utils::create_file("fake-gdb", "unused");
432
433 executor::executor_handle handle = executor::setup();
434 executor::exit_handle exit_handle = generate_core(this, "short", handle);
435
436 const optional< fs::path > core_name = utils::find_core(
437 fs::path("short"),
438 exit_handle.status().get(),
439 exit_handle.work_directory());
440 if (core_name) {
441 // This is needed even if we provide a different basename to
442 // dump_stacktrace below because the system policies may be generating
443 // core dumps by PID, not binary name.
444 std::cout << "Removing core dump: " << core_name << '\n';
445 fs::unlink(core_name.get());
446 }
447
448 utils::dump_stacktrace(fs::path("fake"), handle, exit_handle);
449
450 atf::utils::cat_file(exit_handle.stdout_file().str(), "stdout: ");
451 atf::utils::cat_file(exit_handle.stderr_file().str(), "stderr: ");
452 ATF_REQUIRE(atf::utils::grep_file("Cannot find any core file",
453 exit_handle.stderr_file().str()));
454
455 exit_handle.cleanup();
456 handle.cleanup();
457 }
458
459
460 ATF_TEST_CASE_WITHOUT_HEAD(dump_stacktrace__cannot_find_gdb);
ATF_TEST_CASE_BODY(dump_stacktrace__cannot_find_gdb)461 ATF_TEST_CASE_BODY(dump_stacktrace__cannot_find_gdb)
462 {
463 utils::setenv("PATH", ".");
464 utils::builtin_gdb = "missing-gdb";
465
466 executor::executor_handle handle = executor::setup();
467 executor::exit_handle exit_handle = generate_core(this, "short", handle);
468
469 utils::dump_stacktrace(fs::path("fake"), handle, exit_handle);
470
471 ATF_REQUIRE(atf::utils::grep_file(
472 "Cannot find GDB binary; builtin was 'missing-gdb'",
473 exit_handle.stderr_file().str()));
474
475 exit_handle.cleanup();
476 handle.cleanup();
477 }
478
479
480 ATF_TEST_CASE_WITHOUT_HEAD(dump_stacktrace__gdb_fail);
ATF_TEST_CASE_BODY(dump_stacktrace__gdb_fail)481 ATF_TEST_CASE_BODY(dump_stacktrace__gdb_fail)
482 {
483 utils::setenv("PATH", ".");
484 create_script("fake-gdb", "echo 'foo'; echo 'bar' 1>&2; exit 1");
485 const std::string gdb = (fs::current_path() / "fake-gdb").str();
486 utils::builtin_gdb = gdb.c_str();
487
488 executor::executor_handle handle = executor::setup();
489 executor::exit_handle exit_handle = generate_core(this, "short", handle);
490
491 atf::utils::create_file((exit_handle.work_directory() / "fake.core").str(),
492 "Invalid core file, but not read");
493 utils::dump_stacktrace(fs::path("fake"), handle, exit_handle);
494
495 ATF_REQUIRE(atf::utils::grep_file("^foo$",
496 exit_handle.stderr_file().str()));
497 ATF_REQUIRE(atf::utils::grep_file("^bar$",
498 exit_handle.stderr_file().str()));
499 ATF_REQUIRE(atf::utils::grep_file("GDB failed; see output above",
500 exit_handle.stderr_file().str()));
501
502 exit_handle.cleanup();
503 handle.cleanup();
504 }
505
506
507 ATF_TEST_CASE_WITHOUT_HEAD(dump_stacktrace__gdb_timeout);
ATF_TEST_CASE_BODY(dump_stacktrace__gdb_timeout)508 ATF_TEST_CASE_BODY(dump_stacktrace__gdb_timeout)
509 {
510 utils::setenv("PATH", ".");
511 create_script("fake-gdb", "while :; do sleep 1; done");
512 const std::string gdb = (fs::current_path() / "fake-gdb").str();
513 utils::builtin_gdb = gdb.c_str();
514 utils::gdb_timeout = datetime::delta(1, 0);
515
516 executor::executor_handle handle = executor::setup();
517 executor::exit_handle exit_handle = generate_core(this, "short", handle);
518
519 atf::utils::create_file((exit_handle.work_directory() / "fake.core").str(),
520 "Invalid core file, but not read");
521 utils::dump_stacktrace(fs::path("fake"), handle, exit_handle);
522
523 ATF_REQUIRE(atf::utils::grep_file("GDB timed out",
524 exit_handle.stderr_file().str()));
525
526 exit_handle.cleanup();
527 handle.cleanup();
528 }
529
530
531 ATF_TEST_CASE_WITHOUT_HEAD(dump_stacktrace_if_available__append);
ATF_TEST_CASE_BODY(dump_stacktrace_if_available__append)532 ATF_TEST_CASE_BODY(dump_stacktrace_if_available__append)
533 {
534 utils::setenv("PATH", ".");
535 create_script("fake-gdb", "echo 'frame 1'; exit 0");
536 utils::builtin_gdb = "fake-gdb";
537
538 executor::executor_handle handle = executor::setup();
539 executor::exit_handle exit_handle = generate_core(this, "short", handle);
540
541 atf::utils::create_file(exit_handle.stdout_file().str(), "Pre-stdout");
542 atf::utils::create_file(exit_handle.stderr_file().str(), "Pre-stderr");
543
544 utils::dump_stacktrace_if_available(fs::path("short"), handle, exit_handle);
545
546 ATF_REQUIRE(atf::utils::grep_file("Pre-stdout",
547 exit_handle.stdout_file().str()));
548 ATF_REQUIRE(atf::utils::grep_file("Pre-stderr",
549 exit_handle.stderr_file().str()));
550 ATF_REQUIRE(atf::utils::grep_file("frame 1",
551 exit_handle.stderr_file().str()));
552
553 exit_handle.cleanup();
554 handle.cleanup();
555 }
556
557
558 ATF_TEST_CASE_WITHOUT_HEAD(dump_stacktrace_if_available__no_status);
ATF_TEST_CASE_BODY(dump_stacktrace_if_available__no_status)559 ATF_TEST_CASE_BODY(dump_stacktrace_if_available__no_status)
560 {
561 executor::executor_handle handle = executor::setup();
562 const executor::exec_handle exec_handle = handle.spawn(
563 child_pause, datetime::delta(0, 100000), none, none, none);
564 executor::exit_handle exit_handle = handle.wait(exec_handle);
565 INV(!exit_handle.status());
566
567 utils::dump_stacktrace_if_available(fs::path("short"), handle, exit_handle);
568 ATF_REQUIRE(atf::utils::compare_file(exit_handle.stdout_file().str(), ""));
569 ATF_REQUIRE(atf::utils::compare_file(exit_handle.stderr_file().str(), ""));
570
571 exit_handle.cleanup();
572 handle.cleanup();
573 }
574
575
576 ATF_TEST_CASE_WITHOUT_HEAD(dump_stacktrace_if_available__no_coredump);
ATF_TEST_CASE_BODY(dump_stacktrace_if_available__no_coredump)577 ATF_TEST_CASE_BODY(dump_stacktrace_if_available__no_coredump)
578 {
579 executor::executor_handle handle = executor::setup();
580 const executor::exec_handle exec_handle = handle.spawn(
581 child_exit, datetime::delta(60, 0), none, none, none);
582 executor::exit_handle exit_handle = handle.wait(exec_handle);
583 INV(exit_handle.status());
584 INV(exit_handle.status().get().exited());
585 INV(exit_handle.status().get().exitstatus() == EXIT_SUCCESS);
586
587 utils::dump_stacktrace_if_available(fs::path("short"), handle, exit_handle);
588 ATF_REQUIRE(atf::utils::compare_file(exit_handle.stdout_file().str(), ""));
589 ATF_REQUIRE(atf::utils::compare_file(exit_handle.stderr_file().str(), ""));
590
591 exit_handle.cleanup();
592 handle.cleanup();
593 }
594
595
ATF_INIT_TEST_CASES(tcs)596 ATF_INIT_TEST_CASES(tcs)
597 {
598 ATF_ADD_TEST_CASE(tcs, unlimit_core_size);
599 ATF_ADD_TEST_CASE(tcs, unlimit_core_size__hard_is_zero);
600
601 ATF_ADD_TEST_CASE(tcs, find_gdb__use_builtin);
602 ATF_ADD_TEST_CASE(tcs, find_gdb__search_builtin__ok);
603 ATF_ADD_TEST_CASE(tcs, find_gdb__search_builtin__fail);
604 ATF_ADD_TEST_CASE(tcs, find_gdb__bogus_value);
605
606 ATF_ADD_TEST_CASE(tcs, find_core__found__short);
607 ATF_ADD_TEST_CASE(tcs, find_core__found__long);
608 ATF_ADD_TEST_CASE(tcs, find_core__not_found);
609
610 ATF_ADD_TEST_CASE(tcs, dump_stacktrace__integration);
611 ATF_ADD_TEST_CASE(tcs, dump_stacktrace__ok);
612 ATF_ADD_TEST_CASE(tcs, dump_stacktrace__cannot_find_core);
613 ATF_ADD_TEST_CASE(tcs, dump_stacktrace__cannot_find_gdb);
614 ATF_ADD_TEST_CASE(tcs, dump_stacktrace__gdb_fail);
615 ATF_ADD_TEST_CASE(tcs, dump_stacktrace__gdb_timeout);
616
617 ATF_ADD_TEST_CASE(tcs, dump_stacktrace_if_available__append);
618 ATF_ADD_TEST_CASE(tcs, dump_stacktrace_if_available__no_status);
619 ATF_ADD_TEST_CASE(tcs, dump_stacktrace_if_available__no_coredump);
620 }
621