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