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/resource.h> 32 #include <sys/stat.h> 33 #include <sys/wait.h> 34 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include <atf-c.h> 40 41 #include "env.h" 42 #include "error.h" 43 #include "fs.h" 44 #include "run.h" 45 #include "text.h" 46 47 #if !defined(WCOREDUMP) && defined(__minix) 48 #define WCOREDUMP(x) ((x) & 0x80) 49 #endif /*!defined(WCOREDUMP) && defined(__minix) */ 50 51 52 /// Ensures that the given expression does not return a kyua_error_t. 53 /// 54 /// \param expr Expression to evaluate. 55 #define RE(expr) ATF_REQUIRE(!kyua_error_is_set(expr)) 56 57 58 /// Ensures that the given expression does not return a kyua_error_t. 59 /// 60 /// \param expr Expression to evaluate. 61 /// \param msg Failure message. 62 #define RE_MSG(expr, msg) ATF_REQUIRE_MSG(!kyua_error_is_set(expr), msg) 63 64 65 /// Generates a core dump. 66 /// 67 /// Due to the complexity of this interface, you should probably use 68 /// generate_core() instead. 69 /// 70 /// \post If this fails to generate a core file, the test case is marked as 71 /// skipped. The caller therefore can rely that a core dump has been created on 72 /// return. 73 /// 74 /// \param tc Pointer to the caller test case. 75 /// \param run_params Parameters for the execution of the helper. 76 /// \param helper_path Path to the created helper. 77 /// \param exec_path Name of the helper, prefixed with ./ so that it can be 78 /// executed from within the work directory. 79 /// \param helper_name Basename of the helper. 80 /// 81 /// \return The PID of the crashed binary. 82 static pid_t 83 generate_core_aux(const atf_tc_t* tc, const kyua_run_params_t* run_params, 84 const char* helper_path, const char* exec_path, 85 const char* helper_name) 86 { 87 const char* srcdir = atf_tc_get_config_var(tc, "srcdir"); 88 89 char* src_helper; 90 RE(kyua_fs_concat(&src_helper, srcdir, "stacktrace_helper", NULL)); 91 atf_utils_copy_file(src_helper, helper_path); 92 free(src_helper); 93 94 // We use kyua_run_fork for this to better simulate the final use case of 95 // the stacktrace gathering, as test programs are run through these 96 // functions. Also, kyua_run_fork provides us with automatic unlimiting of 97 // resources so that core files can be generated. 98 99 pid_t pid; 100 const kyua_error_t error = kyua_run_fork(run_params, &pid); 101 if (!kyua_error_is_set(error) && pid == 0) { 102 const char* const args[] = { helper_name, NULL }; 103 kyua_run_exec(exec_path, args); 104 } 105 RE(error); 106 107 int status; bool timed_out; 108 RE_MSG(kyua_run_wait(pid, &status, &timed_out), 109 "wait failed; unexpected problem during exec?"); 110 111 ATF_REQUIRE(WIFSIGNALED(status)); 112 if (!WCOREDUMP(status)) 113 atf_tc_skip("Test failed to generate core dump"); 114 return pid; 115 } 116 117 118 /// Creates a script. 119 /// 120 /// \param script Path to the script to create. 121 /// \param contents Contents of the script. 122 static void 123 create_script(const char* script, const char* contents) 124 { 125 atf_utils_create_file(script, "#! /bin/sh\n\n%s\n", contents); 126 ATF_REQUIRE(chmod(script, 0755) != -1); 127 } 128 129 130 /// Generates a core file. 131 /// 132 /// \param tc Pointer to the calling test case. 133 /// \param work_directory Name of the directory in which to place the binary 134 /// that will generate the stacktrace. 135 /// \param program_name Basename of the binary that will crash. 136 /// 137 /// \return PID of the process that generated the core file. 138 static pid_t 139 generate_core(const atf_tc_t* tc, const char* work_directory, 140 const char* program_name) 141 { 142 kyua_run_params_t run_params; 143 kyua_run_params_init(&run_params); 144 if (strcmp(work_directory, ".") != 0) { 145 ATF_REQUIRE(mkdir(work_directory, 0755) != -1); 146 run_params.work_directory = work_directory; 147 } 148 149 char* copy_to; char* exec_path; 150 RE(kyua_text_printf(©_to, "%s/%s", work_directory, program_name)); 151 RE(kyua_text_printf(&exec_path, "./%s", program_name)); 152 const pid_t pid = generate_core_aux(tc, &run_params, copy_to, exec_path, 153 program_name); 154 free(exec_path); 155 free(copy_to); 156 return pid; 157 } 158 159 160 /// Prepares and runs kyua_stacktrace_dump(). 161 /// 162 /// \param tc Pointer to the calling test case. 163 /// \param work_directory Name of the directory in which to place the binary 164 /// that will generate the stacktrace. 165 /// \param program_name Basename of the binary that will crash. 166 /// \param output_name Name of the file to which to write the stacktrace. 167 /// \param timeout_seconds Time to give GDB to complete. 168 static void 169 do_dump(const atf_tc_t* tc, const char* work_directory, 170 const char* program_name, const char* output_name, 171 const int timeout_seconds) 172 { 173 const pid_t pid = generate_core(tc, work_directory, program_name); 174 175 kyua_run_params_t run_params; 176 kyua_run_params_init(&run_params); 177 run_params.timeout_seconds = timeout_seconds + 100; // Some large value. 178 run_params.work_directory = work_directory; // Created by generate_core. 179 180 kyua_stacktrace_gdb_timeout = timeout_seconds; 181 182 FILE* output = fopen(output_name, "w"); 183 ATF_REQUIRE(output != NULL); 184 kyua_stacktrace_dump(program_name, pid, &run_params, output); 185 fclose(output); 186 atf_utils_cat_file(output_name, "dump output: "); 187 } 188 189 190 ATF_TC_WITHOUT_HEAD(find_core__found__short); 191 ATF_TC_BODY(find_core__found__short, tc) 192 { 193 const pid_t pid = generate_core(tc, "dir", "short"); 194 const char* core_name = kyua_stacktrace_find_core("short", "dir", pid); 195 if (core_name == NULL) 196 atf_tc_fail("Core dumped, but no candidates found"); 197 ATF_REQUIRE(strstr(core_name, "core") != NULL); 198 ATF_REQUIRE(access(core_name, F_OK) != -1); 199 } 200 201 202 ATF_TC_WITHOUT_HEAD(find_core__found__long); 203 ATF_TC_BODY(find_core__found__long, tc) 204 { 205 const pid_t pid = generate_core( 206 tc, "dir", "long-name-that-may-be-truncated-in-some-systems"); 207 const char* core_name = kyua_stacktrace_find_core( 208 "long-name-that-may-be-truncated-in-some-systems", "dir", pid); 209 if (core_name == NULL) 210 atf_tc_fail("Core dumped, but no candidates found"); 211 ATF_REQUIRE(strstr(core_name, "core") != NULL); 212 ATF_REQUIRE(access(core_name, F_OK) != -1); 213 } 214 215 216 ATF_TC_WITHOUT_HEAD(find_core__not_found); 217 ATF_TC_BODY(find_core__not_found, tc) 218 { 219 const char* core_name = kyua_stacktrace_find_core("missing", ".", 1); 220 if (core_name != NULL) 221 atf_tc_fail("Core not dumped, but candidate found: %s", core_name); 222 } 223 224 225 ATF_TC_WITHOUT_HEAD(dump__integration); 226 ATF_TC_BODY(dump__integration, tc) 227 { 228 do_dump(tc, "dir", "short", "stacktrace", 10); 229 230 // It is hard to validate the execution of an arbitrary GDB of which we know 231 // nothing anything. Just assume that the backtrace, at the very least, 232 // prints a frame identifier. 233 ATF_REQUIRE(atf_utils_grep_file("#0", "stacktrace")); 234 } 235 236 237 ATF_TC_WITHOUT_HEAD(dump__ok); 238 ATF_TC_BODY(dump__ok, tc) 239 { 240 RE(kyua_env_set("PATH", ".")); 241 create_script("fake-gdb", "echo 'frame 1'; echo 'frame 2'; " 242 "echo 'some warning' 1>&2; exit 0"); 243 kyua_stacktrace_gdb = "fake-gdb"; 244 245 do_dump(tc, ".", "short", "stacktrace", 10); 246 247 ATF_REQUIRE(atf_utils_grep_file("dumped core; attempting to gather", 248 "stacktrace")); 249 ATF_REQUIRE(atf_utils_grep_file("frame 1", "stacktrace")); 250 ATF_REQUIRE(atf_utils_grep_file("frame 2", "stacktrace")); 251 ATF_REQUIRE(atf_utils_grep_file("some warning", "stacktrace")); 252 ATF_REQUIRE(atf_utils_grep_file("GDB exited successfully", "stacktrace")); 253 } 254 255 256 ATF_TC_WITHOUT_HEAD(dump__cannot_find_core); 257 ATF_TC_BODY(dump__cannot_find_core, tc) 258 { 259 kyua_run_params_t run_params; 260 kyua_run_params_init(&run_params); 261 262 FILE* output = fopen("stacktrace", "w"); 263 ATF_REQUIRE(output != NULL); 264 // This assumes that init(8) has never core dumped. 265 kyua_stacktrace_dump("missing", 1, &run_params, output); 266 fclose(output); 267 atf_utils_cat_file("stacktrace", "dump output: "); 268 269 ATF_REQUIRE(atf_utils_grep_file("Cannot find any core file", "stacktrace")); 270 } 271 272 273 ATF_TC_WITHOUT_HEAD(dump__cannot_find_gdb); 274 ATF_TC_BODY(dump__cannot_find_gdb, tc) 275 { 276 RE(kyua_env_set("PATH", ".")); 277 kyua_stacktrace_gdb = "missing-gdb"; 278 279 do_dump(tc, ".", "dont-care", "stacktrace", 10); 280 281 ATF_REQUIRE(atf_utils_grep_file("execvp failed", "stacktrace")); 282 } 283 284 285 ATF_TC_WITHOUT_HEAD(dump__gdb_fail); 286 ATF_TC_BODY(dump__gdb_fail, tc) 287 { 288 RE(kyua_env_set("PATH", ".")); 289 create_script("fake-gdb", "echo 'foo'; echo 'bar' 1>&2; exit 56"); 290 kyua_stacktrace_gdb = "fake-gdb"; 291 292 do_dump(tc, ".", "short", "stacktrace", 10); 293 294 ATF_REQUIRE(atf_utils_grep_file("foo", "stacktrace")); 295 ATF_REQUIRE(atf_utils_grep_file("bar", "stacktrace")); 296 ATF_REQUIRE(atf_utils_grep_file("GDB failed with code 56; see output above " 297 "for details", "stacktrace")); 298 } 299 300 301 ATF_TC_WITHOUT_HEAD(dump__gdb_times_out); 302 ATF_TC_BODY(dump__gdb_times_out, tc) 303 { 304 RE(kyua_env_set("PATH", ".")); 305 create_script("fake-gdb", "echo 'foo'; echo 'bar' 1>&2; " 306 "/bin/sleep 10; /usr/bin/sleep 10; exit 0"); 307 kyua_stacktrace_gdb = "fake-gdb"; 308 309 do_dump(tc, ".", "short", "stacktrace", 1); 310 311 ATF_REQUIRE(atf_utils_grep_file("foo", "stacktrace")); 312 ATF_REQUIRE(atf_utils_grep_file("bar", "stacktrace")); 313 ATF_REQUIRE(atf_utils_grep_file("GDB failed; timed out", "stacktrace")); 314 } 315 316 317 ATF_TP_ADD_TCS(tp) 318 { 319 ATF_TP_ADD_TC(tp, find_core__found__short); 320 ATF_TP_ADD_TC(tp, find_core__found__long); 321 ATF_TP_ADD_TC(tp, find_core__not_found); 322 323 ATF_TP_ADD_TC(tp, dump__integration); 324 ATF_TP_ADD_TC(tp, dump__ok); 325 ATF_TP_ADD_TC(tp, dump__cannot_find_core); 326 ATF_TP_ADD_TC(tp, dump__cannot_find_gdb); 327 ATF_TP_ADD_TC(tp, dump__gdb_fail); 328 ATF_TP_ADD_TC(tp, dump__gdb_times_out); 329 330 return atf_no_error(); 331 } 332