xref: /minix3/external/bsd/kyua-testers/dist/stacktrace_test.c (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
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 
48 /// Ensures that the given expression does not return a kyua_error_t.
49 ///
50 /// \param expr Expression to evaluate.
51 #define RE(expr) ATF_REQUIRE(!kyua_error_is_set(expr))
52 
53 
54 /// Ensures that the given expression does not return a kyua_error_t.
55 ///
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)
59 
60 
61 /// Generates a core dump.
62 ///
63 /// Due to the complexity of this interface, you should probably use
64 /// generate_core() instead.
65 ///
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
68 /// return.
69 ///
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.
76 ///
77 /// \return The PID of the crashed binary.
78 static pid_t
generate_core_aux(const atf_tc_t * tc,const kyua_run_params_t * run_params,const char * helper_path,const char * exec_path,const char * helper_name)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)
82 {
83     const char* srcdir = atf_tc_get_config_var(tc, "srcdir");
84 
85     char* src_helper;
86     RE(kyua_fs_concat(&src_helper, srcdir, "stacktrace_helper", NULL));
87     atf_utils_copy_file(src_helper, helper_path);
88     free(src_helper);
89 
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.
94 
95     pid_t pid;
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);
100     }
101     RE(error);
102 
103     int status; bool timed_out;
104     RE_MSG(kyua_run_wait(pid, &status, &timed_out),
105            "wait failed; unexpected problem during exec?");
106 
107     ATF_REQUIRE(WIFSIGNALED(status));
108     if (!WCOREDUMP(status))
109         atf_tc_skip("Test failed to generate core dump");
110     return pid;
111 }
112 
113 
114 /// Creates a script.
115 ///
116 /// \param script Path to the script to create.
117 /// \param contents Contents of the script.
118 static void
create_script(const char * script,const char * contents)119 create_script(const char* script, const char* contents)
120 {
121     atf_utils_create_file(script, "#! /bin/sh\n\n%s\n", contents);
122     ATF_REQUIRE(chmod(script, 0755) != -1);
123 }
124 
125 
126 /// Generates a core file.
127 ///
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.
132 ///
133 /// \return PID of the process that generated the core file.
134 static pid_t
generate_core(const atf_tc_t * tc,const char * work_directory,const char * program_name)135 generate_core(const atf_tc_t* tc, const char* work_directory,
136               const char* program_name)
137 {
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;
143     }
144 
145     char* copy_to; char* exec_path;
146     RE(kyua_text_printf(&copy_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,
149                                         program_name);
150     free(exec_path);
151     free(copy_to);
152     return pid;
153 }
154 
155 
156 /// Prepares and runs kyua_stacktrace_dump().
157 ///
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.
164 static void
do_dump(const atf_tc_t * tc,const char * work_directory,const char * program_name,const char * output_name,const int timeout_seconds)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)
168 {
169     const pid_t pid = generate_core(tc, work_directory, program_name);
170 
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.
175 
176     kyua_stacktrace_gdb_timeout = timeout_seconds;
177 
178     FILE* output = fopen(output_name, "w");
179     ATF_REQUIRE(output != NULL);
180     kyua_stacktrace_dump(program_name, pid, &run_params, output);
181     fclose(output);
182     atf_utils_cat_file(output_name, "dump output: ");
183 }
184 
185 
186 ATF_TC_WITHOUT_HEAD(find_core__found__short);
ATF_TC_BODY(find_core__found__short,tc)187 ATF_TC_BODY(find_core__found__short, tc)
188 {
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);
195 }
196 
197 
198 ATF_TC_WITHOUT_HEAD(find_core__found__long);
ATF_TC_BODY(find_core__found__long,tc)199 ATF_TC_BODY(find_core__found__long, tc)
200 {
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);
209 }
210 
211 
212 ATF_TC_WITHOUT_HEAD(find_core__not_found);
ATF_TC_BODY(find_core__not_found,tc)213 ATF_TC_BODY(find_core__not_found, tc)
214 {
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);
218 }
219 
220 
221 ATF_TC_WITHOUT_HEAD(dump__integration);
ATF_TC_BODY(dump__integration,tc)222 ATF_TC_BODY(dump__integration, tc)
223 {
224     do_dump(tc, "dir", "short", "stacktrace", 10);
225 
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"));
230 }
231 
232 
233 ATF_TC_WITHOUT_HEAD(dump__ok);
ATF_TC_BODY(dump__ok,tc)234 ATF_TC_BODY(dump__ok, tc)
235 {
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";
240 
241     do_dump(tc, ".", "short", "stacktrace", 10);
242 
243     ATF_REQUIRE(atf_utils_grep_file("dumped core; attempting to gather",
244                                     "stacktrace"));
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"));
249 }
250 
251 
252 ATF_TC_WITHOUT_HEAD(dump__cannot_find_core);
ATF_TC_BODY(dump__cannot_find_core,tc)253 ATF_TC_BODY(dump__cannot_find_core, tc)
254 {
255     kyua_run_params_t run_params;
256     kyua_run_params_init(&run_params);
257 
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);
262     fclose(output);
263     atf_utils_cat_file("stacktrace", "dump output: ");
264 
265     ATF_REQUIRE(atf_utils_grep_file("Cannot find any core file", "stacktrace"));
266 }
267 
268 
269 ATF_TC_WITHOUT_HEAD(dump__cannot_find_gdb);
ATF_TC_BODY(dump__cannot_find_gdb,tc)270 ATF_TC_BODY(dump__cannot_find_gdb, tc)
271 {
272     RE(kyua_env_set("PATH", "."));
273     kyua_stacktrace_gdb = "missing-gdb";
274 
275     do_dump(tc, ".", "dont-care", "stacktrace", 10);
276 
277     ATF_REQUIRE(atf_utils_grep_file("execvp failed", "stacktrace"));
278 }
279 
280 
281 ATF_TC_WITHOUT_HEAD(dump__gdb_fail);
ATF_TC_BODY(dump__gdb_fail,tc)282 ATF_TC_BODY(dump__gdb_fail, tc)
283 {
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";
287 
288     do_dump(tc, ".", "short", "stacktrace", 10);
289 
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"));
294 }
295 
296 
297 ATF_TC_WITHOUT_HEAD(dump__gdb_times_out);
ATF_TC_BODY(dump__gdb_times_out,tc)298 ATF_TC_BODY(dump__gdb_times_out, tc)
299 {
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";
304 
305     do_dump(tc, ".", "short", "stacktrace", 1);
306 
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"));
310 }
311 
312 
ATF_TP_ADD_TCS(tp)313 ATF_TP_ADD_TCS(tp)
314 {
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);
318 
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);
325 
326     return atf_no_error();
327 }
328