xref: /minix3/external/bsd/kyua-testers/dist/stacktrace_test.c (revision 6e5a113837816c382dd4f2a25924f16fbbb1c493)
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(&copy_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