1 // Copyright 2012 Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
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.
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.
29 #include "stacktrace.h"
31 #include <sys/resource.h>
48 /// Ensures that the given expression does not return a kyua_error_t.
50 /// \param expr Expression to evaluate.
51 #define RE(expr) ATF_REQUIRE(!kyua_error_is_set(expr))
54 /// Ensures that the given expression does not return a kyua_error_t.
56 /// \param expr Expression to evaluate.
57 /// \param msg Failure message.
58 #define RE_MSG(expr, msg) ATF_REQUIRE_MSG(!kyua_error_is_set(expr), msg)
61 /// Generates a core dump.
63 /// Due to the complexity of this interface, you should probably use
64 /// generate_core() instead.
66 /// \post If this fails to generate a core file, the test case is marked as
67 /// skipped. The caller therefore can rely that a core dump has been created on
70 /// \param tc Pointer to the caller test case.
71 /// \param run_params Parameters for the execution of the helper.
72 /// \param helper_path Path to the created helper.
73 /// \param exec_path Name of the helper, prefixed with ./ so that it can be
74 /// executed from within the work directory.
75 /// \param helper_name Basename of the helper.
77 /// \return The PID of the crashed binary.
79 generate_core_aux(const atf_tc_t
* tc
, const kyua_run_params_t
* run_params
,
80 const char* helper_path
, const char* exec_path
,
81 const char* helper_name
)
83 const char* srcdir
= atf_tc_get_config_var(tc
, "srcdir");
86 RE(kyua_fs_concat(&src_helper
, srcdir
, "stacktrace_helper", NULL
));
87 atf_utils_copy_file(src_helper
, helper_path
);
90 // We use kyua_run_fork for this to better simulate the final use case of
91 // the stacktrace gathering, as test programs are run through these
92 // functions. Also, kyua_run_fork provides us with automatic unlimiting of
93 // resources so that core files can be generated.
96 const kyua_error_t error
= kyua_run_fork(run_params
, &pid
);
97 if (!kyua_error_is_set(error
) && pid
== 0) {
98 const char* const args
[] = { helper_name
, NULL
};
99 kyua_run_exec(exec_path
, args
);
103 int status
; bool timed_out
;
104 RE_MSG(kyua_run_wait(pid
, &status
, &timed_out
),
105 "wait failed; unexpected problem during exec?");
107 ATF_REQUIRE(WIFSIGNALED(status
));
108 if (!WCOREDUMP(status
))
109 atf_tc_skip("Test failed to generate core dump");
114 /// Creates a script.
116 /// \param script Path to the script to create.
117 /// \param contents Contents of the script.
119 create_script(const char* script
, const char* contents
)
121 atf_utils_create_file(script
, "#! /bin/sh\n\n%s\n", contents
);
122 ATF_REQUIRE(chmod(script
, 0755) != -1);
126 /// Generates a core file.
128 /// \param tc Pointer to the calling test case.
129 /// \param work_directory Name of the directory in which to place the binary
130 /// that will generate the stacktrace.
131 /// \param program_name Basename of the binary that will crash.
133 /// \return PID of the process that generated the core file.
135 generate_core(const atf_tc_t
* tc
, const char* work_directory
,
136 const char* program_name
)
138 kyua_run_params_t run_params
;
139 kyua_run_params_init(&run_params
);
140 if (strcmp(work_directory
, ".") != 0) {
141 ATF_REQUIRE(mkdir(work_directory
, 0755) != -1);
142 run_params
.work_directory
= work_directory
;
145 char* copy_to
; char* exec_path
;
146 RE(kyua_text_printf(©_to
, "%s/%s", work_directory
, program_name
));
147 RE(kyua_text_printf(&exec_path
, "./%s", program_name
));
148 const pid_t pid
= generate_core_aux(tc
, &run_params
, copy_to
, exec_path
,
156 /// Prepares and runs kyua_stacktrace_dump().
158 /// \param tc Pointer to the calling test case.
159 /// \param work_directory Name of the directory in which to place the binary
160 /// that will generate the stacktrace.
161 /// \param program_name Basename of the binary that will crash.
162 /// \param output_name Name of the file to which to write the stacktrace.
163 /// \param timeout_seconds Time to give GDB to complete.
165 do_dump(const atf_tc_t
* tc
, const char* work_directory
,
166 const char* program_name
, const char* output_name
,
167 const int timeout_seconds
)
169 const pid_t pid
= generate_core(tc
, work_directory
, program_name
);
171 kyua_run_params_t run_params
;
172 kyua_run_params_init(&run_params
);
173 run_params
.timeout_seconds
= timeout_seconds
+ 100; // Some large value.
174 run_params
.work_directory
= work_directory
; // Created by generate_core.
176 kyua_stacktrace_gdb_timeout
= timeout_seconds
;
178 FILE* output
= fopen(output_name
, "w");
179 ATF_REQUIRE(output
!= NULL
);
180 kyua_stacktrace_dump(program_name
, pid
, &run_params
, output
);
182 atf_utils_cat_file(output_name
, "dump output: ");
186 ATF_TC_WITHOUT_HEAD(find_core__found__short
);
187 ATF_TC_BODY(find_core__found__short
, tc
)
189 const pid_t pid
= generate_core(tc
, "dir", "short");
190 const char* core_name
= kyua_stacktrace_find_core("short", "dir", pid
);
191 if (core_name
== NULL
)
192 atf_tc_fail("Core dumped, but no candidates found");
193 ATF_REQUIRE(strstr(core_name
, "core") != NULL
);
194 ATF_REQUIRE(access(core_name
, F_OK
) != -1);
198 ATF_TC_WITHOUT_HEAD(find_core__found__long
);
199 ATF_TC_BODY(find_core__found__long
, tc
)
201 const pid_t pid
= generate_core(
202 tc
, "dir", "long-name-that-may-be-truncated-in-some-systems");
203 const char* core_name
= kyua_stacktrace_find_core(
204 "long-name-that-may-be-truncated-in-some-systems", "dir", pid
);
205 if (core_name
== NULL
)
206 atf_tc_fail("Core dumped, but no candidates found");
207 ATF_REQUIRE(strstr(core_name
, "core") != NULL
);
208 ATF_REQUIRE(access(core_name
, F_OK
) != -1);
212 ATF_TC_WITHOUT_HEAD(find_core__not_found
);
213 ATF_TC_BODY(find_core__not_found
, tc
)
215 const char* core_name
= kyua_stacktrace_find_core("missing", ".", 1);
216 if (core_name
!= NULL
)
217 atf_tc_fail("Core not dumped, but candidate found: %s", core_name
);
221 ATF_TC_WITHOUT_HEAD(dump__integration
);
222 ATF_TC_BODY(dump__integration
, tc
)
224 do_dump(tc
, "dir", "short", "stacktrace", 10);
226 // It is hard to validate the execution of an arbitrary GDB of which we know
227 // nothing anything. Just assume that the backtrace, at the very least,
228 // prints a frame identifier.
229 ATF_REQUIRE(atf_utils_grep_file("#0", "stacktrace"));
233 ATF_TC_WITHOUT_HEAD(dump__ok
);
234 ATF_TC_BODY(dump__ok
, tc
)
236 RE(kyua_env_set("PATH", "."));
237 create_script("fake-gdb", "echo 'frame 1'; echo 'frame 2'; "
238 "echo 'some warning' 1>&2; exit 0");
239 kyua_stacktrace_gdb
= "fake-gdb";
241 do_dump(tc
, ".", "short", "stacktrace", 10);
243 ATF_REQUIRE(atf_utils_grep_file("dumped core; attempting to gather",
245 ATF_REQUIRE(atf_utils_grep_file("frame 1", "stacktrace"));
246 ATF_REQUIRE(atf_utils_grep_file("frame 2", "stacktrace"));
247 ATF_REQUIRE(atf_utils_grep_file("some warning", "stacktrace"));
248 ATF_REQUIRE(atf_utils_grep_file("GDB exited successfully", "stacktrace"));
252 ATF_TC_WITHOUT_HEAD(dump__cannot_find_core
);
253 ATF_TC_BODY(dump__cannot_find_core
, tc
)
255 kyua_run_params_t run_params
;
256 kyua_run_params_init(&run_params
);
258 FILE* output
= fopen("stacktrace", "w");
259 ATF_REQUIRE(output
!= NULL
);
260 // This assumes that init(8) has never core dumped.
261 kyua_stacktrace_dump("missing", 1, &run_params
, output
);
263 atf_utils_cat_file("stacktrace", "dump output: ");
265 ATF_REQUIRE(atf_utils_grep_file("Cannot find any core file", "stacktrace"));
269 ATF_TC_WITHOUT_HEAD(dump__cannot_find_gdb
);
270 ATF_TC_BODY(dump__cannot_find_gdb
, tc
)
272 RE(kyua_env_set("PATH", "."));
273 kyua_stacktrace_gdb
= "missing-gdb";
275 do_dump(tc
, ".", "dont-care", "stacktrace", 10);
277 ATF_REQUIRE(atf_utils_grep_file("execvp failed", "stacktrace"));
281 ATF_TC_WITHOUT_HEAD(dump__gdb_fail
);
282 ATF_TC_BODY(dump__gdb_fail
, tc
)
284 RE(kyua_env_set("PATH", "."));
285 create_script("fake-gdb", "echo 'foo'; echo 'bar' 1>&2; exit 56");
286 kyua_stacktrace_gdb
= "fake-gdb";
288 do_dump(tc
, ".", "short", "stacktrace", 10);
290 ATF_REQUIRE(atf_utils_grep_file("foo", "stacktrace"));
291 ATF_REQUIRE(atf_utils_grep_file("bar", "stacktrace"));
292 ATF_REQUIRE(atf_utils_grep_file("GDB failed with code 56; see output above "
293 "for details", "stacktrace"));
297 ATF_TC_WITHOUT_HEAD(dump__gdb_times_out
);
298 ATF_TC_BODY(dump__gdb_times_out
, tc
)
300 RE(kyua_env_set("PATH", "."));
301 create_script("fake-gdb", "echo 'foo'; echo 'bar' 1>&2; "
302 "/bin/sleep 10; /usr/bin/sleep 10; exit 0");
303 kyua_stacktrace_gdb
= "fake-gdb";
305 do_dump(tc
, ".", "short", "stacktrace", 1);
307 ATF_REQUIRE(atf_utils_grep_file("foo", "stacktrace"));
308 ATF_REQUIRE(atf_utils_grep_file("bar", "stacktrace"));
309 ATF_REQUIRE(atf_utils_grep_file("GDB failed; timed out", "stacktrace"));
315 ATF_TP_ADD_TC(tp
, find_core__found__short
);
316 ATF_TP_ADD_TC(tp
, find_core__found__long
);
317 ATF_TP_ADD_TC(tp
, find_core__not_found
);
319 ATF_TP_ADD_TC(tp
, dump__integration
);
320 ATF_TP_ADD_TC(tp
, dump__ok
);
321 ATF_TP_ADD_TC(tp
, dump__cannot_find_core
);
322 ATF_TP_ADD_TC(tp
, dump__cannot_find_gdb
);
323 ATF_TP_ADD_TC(tp
, dump__gdb_fail
);
324 ATF_TP_ADD_TC(tp
, dump__gdb_times_out
);
326 return atf_no_error();