1 /* Copyright (c) 2005-2008, 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
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * ---
31 * Author: Markus Gutschke, Carl Crous
32 *
33 * Code to extract a core dump snapshot of the current process.
34 */
35 #include "coredumper/coredumper.h"
36
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdint.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <unistd.h>
45
46 #include "elfcore.h"
47 #include "linux_syscall_support.h"
48 #include "linuxthreads.h"
49 #include "thread_lister.h"
50
51 static const char *const no_args_bzip2[] = {"bzip2", NULL};
52 static const char *const no_args_gzip[] = {"gzip", NULL};
53 static const char *const no_args_compress[] = {"compress", NULL};
54 const struct CoredumperCompressor COREDUMPER_COMPRESSED[] = {{"/bin/bzip2", no_args_bzip2, ".bz2"},
55 {"/usr/bin/bzip2", no_args_bzip2, ".bz2"},
56 {"bzip2", no_args_bzip2, ".bz2"},
57 {"/bin/gzip", no_args_gzip, ".gz"},
58 {"/usr/bin/gzip", no_args_gzip, ".gz"},
59 {"gzip", no_args_gzip, ".gz"},
60 {"/bin/compress", no_args_compress, ".Z"},
61 {"/usr/bin/compress", no_args_compress, ".Z"},
62 {"compress", no_args_compress, ".Z"},
63 {"", 0, ""},
64 {0, 0, 0}};
65 const struct CoredumperCompressor COREDUMPER_BZIP2_COMPRESSED[] = {{"/bin/bzip2", no_args_bzip2, ".bz2"},
66 {"/usr/bin/bzip2", no_args_bzip2, ".bz2"},
67 {"bzip2", no_args_bzip2, ".bz2"},
68 {0, 0, 0}};
69 const struct CoredumperCompressor COREDUMPER_GZIP_COMPRESSED[] = {{"/bin/gzip", no_args_gzip, ".gz"},
70 {"/usr/bin/gzip", no_args_gzip, ".gz"},
71 {"gzip", no_args_gzip, ".gz"},
72 {0, 0, 0}};
73 const struct CoredumperCompressor COREDUMPER_COMPRESS_COMPRESSED[] = {{"/bin/compress", no_args_compress, ".Z"},
74 {"/usr/bin/compress", no_args_compress, ".Z"},
75 {"compress", no_args_compress, ".Z"},
76 {0, 0, 0}};
77 const struct CoredumperCompressor COREDUMPER_TRY_BZIP2_COMPRESSED[] = {{"/bin/bzip2", no_args_bzip2, ".bz2"},
78 {"/usr/bin/bzip2", no_args_bzip2, ".bz2"},
79 {"bzip2", no_args_bzip2, ".bz2"},
80 {"", 0, ""},
81 {0, 0, 0}};
82 const struct CoredumperCompressor COREDUMPER_TRY_GZIP_COMPRESSED[] = {{"/bin/gzip", no_args_gzip, ".gz"},
83 {"/usr/bin/gzip", no_args_gzip, ".gz"},
84 {"gzip", no_args_gzip, ".gz"},
85 {"", 0, ""},
86 {0, 0, 0}};
87 const struct CoredumperCompressor COREDUMPER_TRY_COMPRESS_COMPRESSED[] = {{"/bin/compress", no_args_compress, ".Z"},
88 {"/usr/bin/compress", no_args_compress, ".Z"},
89 {"compress", no_args_compress, ".Z"},
90 {"", 0, ""},
91 {0, 0, 0}};
92 const struct CoredumperCompressor COREDUMPER_UNCOMPRESSED[] = {{"", 0, ""}, {0, 0, 0}};
93
94 #ifndef DUMPER
95 /* If the target platform lacks the necessary support for generating core dumps
96 * on the fly, or if nobody has ported the code, then return an error.
97 */
98 typedef void *Frame;
99 #define FRAME(f) \
100 void *f = &&label; \
101 label:
102
InternalGetCoreDump(void * frame,int num_threads,pid_t * thread_pids,va_list ap)103 int InternalGetCoreDump(void *frame, int num_threads, pid_t *thread_pids, va_list ap) {
104 errno = EINVAL;
105 return -1;
106 }
107 #endif
108
109 /* Internal helper method used by GetCoreDump().
110 */
GetCoreDumpFunction(void * frame,const struct CoreDumpParameters * params)111 static int GetCoreDumpFunction(void *frame, const struct CoreDumpParameters *params) {
112 return ListAllProcessThreads(frame, InternalGetCoreDump, params, NULL, getenv("PATH"));
113 }
114
115 /* Returns a file handle that can be read to obtain a snapshot of the
116 * current state of this process. If a core file could not be
117 * generated for any reason, -1 is returned.
118 *
119 * This function momentarily suspends all threads, while creating a
120 * COW copy of the process's address space.
121 *
122 * This function is neither reentrant nor async signal safe. Callers
123 * should wrap a mutex around the invocation of this function, if necessary.
124 *
125 * The current implementation tries very hard to do behave reasonably when
126 * called from a signal handler, but no guarantees are made that this will
127 * always work.
128 */
GetCoreDump(void)129 int GetCoreDump(void) {
130 FRAME(frame);
131 struct CoreDumpParameters params;
132 ClearCoreDumpParameters(¶ms);
133 return GetCoreDumpFunction(&frame, ¶ms);
134 }
135
GetCoreDumpWith(const struct CoreDumpParameters * params)136 int GetCoreDumpWith(const struct CoreDumpParameters *params) {
137 FRAME(frame);
138 if ((params->flags & COREDUMPER_FLAG_LIMITED) || (params->flags & COREDUMPER_FLAG_LIMITED_BY_PRIORITY)) {
139 errno = EINVAL;
140 return -1;
141 }
142 return GetCoreDumpFunction(&frame, params);
143 }
144
145 /* Attempts to compress the core file on the fly, if a suitable compressor
146 * could be located. Sets "selected_compressor" to the compressor that
147 * was picked.
148 */
GetCompressedCoreDump(const struct CoredumperCompressor compressors[],struct CoredumperCompressor ** selected_compressor)149 int GetCompressedCoreDump(const struct CoredumperCompressor compressors[],
150 struct CoredumperCompressor **selected_compressor) {
151 FRAME(frame);
152 struct CoreDumpParameters params;
153 ClearCoreDumpParameters(¶ms);
154 SetCoreDumpCompressed(¶ms, compressors, selected_compressor);
155 return GetCoreDumpFunction(&frame, ¶ms);
156 }
157
158 /* Re-runs fn until it doesn't cause EINTR.
159 */
160 #define NO_INTR(fn) \
161 do { \
162 } while ((fn) < 0 && errno == EINTR)
163
164 /* Internal helper method used by WriteCoreDump().
165 */
WriteCoreDumpFunction(void * frame,const struct CoreDumpParameters * params,const char * file_name)166 static int WriteCoreDumpFunction(void *frame, const struct CoreDumpParameters *params, const char *file_name) {
167 return ListAllProcessThreads(frame, InternalGetCoreDump, params, file_name, getenv("PATH"));
168 }
169
170 /* Writes the core file to disk. This is a convenience method wrapping
171 * GetCoreDump(). If a core file could not be generated for any reason,
172 * -1 is returned. On success, zero is returned.
173 */
WriteCoreDump(const char * file_name)174 int WriteCoreDump(const char *file_name) {
175 FRAME(frame);
176 struct CoreDumpParameters params;
177 ClearCoreDumpParameters(¶ms);
178 return WriteCoreDumpFunction(&frame, ¶ms, file_name);
179 }
180
WriteCoreDumpWith(const struct CoreDumpParameters * params,const char * file_name)181 int WriteCoreDumpWith(const struct CoreDumpParameters *params, const char *file_name) {
182 FRAME(frame);
183 return WriteCoreDumpFunction(&frame, params, file_name);
184 }
185
186 /* Callers might need to restrict the maximum size of the core file. This
187 * convenience method provides the necessary support to emulate "ulimit -c".
188 */
WriteCoreDumpLimited(const char * file_name,size_t max_length)189 int WriteCoreDumpLimited(const char *file_name, size_t max_length) {
190 FRAME(frame);
191 struct CoreDumpParameters params;
192 ClearCoreDumpParameters(¶ms);
193 SetCoreDumpLimited(¶ms, max_length);
194 return WriteCoreDumpFunction(&frame, ¶ms, file_name);
195 }
196
197 /* This will limit the size of the core file by reducing or removing the
198 * largest memory segments first, effectively prioritizing the smaller memory
199 * segments. This behavior is preferred when the process has a large heap and
200 * you would like to preserve the relatively small stack.
201 */
WriteCoreDumpLimitedByPriority(const char * file_name,size_t max_length)202 int WriteCoreDumpLimitedByPriority(const char *file_name, size_t max_length) {
203 FRAME(frame);
204 struct CoreDumpParameters params;
205 ClearCoreDumpParameters(¶ms);
206 SetCoreDumpLimitedByPriority(¶ms, max_length);
207 return WriteCoreDumpFunction(&frame, ¶ms, file_name);
208 }
209
210 /* Attempts to compress the core file on the fly, if a suitable compressor
211 * could be located. Sets "selected_compressor" to the compressor that
212 * was picked. The filename automatically has a suitable suffix appended
213 * to it. Normally this would be ".bz2" for bzip2 compression ".gz" for
214 * gzip compression, or ".Z" for compress compression. This behavior can
215 * be changed by defining custom CoredumperCompressor descriptions.
216 */
WriteCompressedCoreDump(const char * file_name,size_t max_length,const struct CoredumperCompressor compressors[],struct CoredumperCompressor ** selected_compressor)217 int WriteCompressedCoreDump(const char *file_name, size_t max_length, const struct CoredumperCompressor compressors[],
218 struct CoredumperCompressor **selected_compressor) {
219 FRAME(frame);
220 struct CoreDumpParameters params;
221 ClearCoreDumpParameters(¶ms);
222 SetCoreDumpCompressed(¶ms, compressors, selected_compressor);
223 SetCoreDumpLimited(¶ms, max_length);
224 return WriteCoreDumpFunction(&frame, ¶ms, file_name);
225 }
226
ClearCoreDumpParametersInternal(struct CoreDumpParameters * params,size_t size)227 void ClearCoreDumpParametersInternal(struct CoreDumpParameters *params, size_t size) {
228 memset(params, 0, size);
229 params->size = size;
230 SetCoreDumpParameter(params, max_length, SIZE_MAX);
231 }
232
SetCoreDumpLimited(struct CoreDumpParameters * params,size_t max_length)233 int SetCoreDumpLimited(struct CoreDumpParameters *params, size_t max_length) {
234 if (params->flags & COREDUMPER_FLAG_LIMITED_BY_PRIORITY) {
235 errno = EINVAL;
236 return -1;
237 }
238 params->flags |= COREDUMPER_FLAG_LIMITED;
239 SetCoreDumpParameter(params, max_length, max_length);
240 return 0;
241 }
242
SetCoreDumpCompressed(struct CoreDumpParameters * params,const struct CoredumperCompressor * compressors,struct CoredumperCompressor ** selected_compressor)243 int SetCoreDumpCompressed(struct CoreDumpParameters *params, const struct CoredumperCompressor *compressors,
244 struct CoredumperCompressor **selected_compressor) {
245 if (params->flags & COREDUMPER_FLAG_LIMITED_BY_PRIORITY) {
246 errno = EINVAL;
247 return -1;
248 }
249 SetCoreDumpParameter(params, compressors, compressors);
250 SetCoreDumpParameter(params, selected_compressor, selected_compressor);
251 return 0;
252 }
253
SetCoreDumpLimitedByPriority(struct CoreDumpParameters * params,size_t max_length)254 int SetCoreDumpLimitedByPriority(struct CoreDumpParameters *params, size_t max_length) {
255 if (((params->flags & COREDUMPER_FLAG_LIMITED) && !(params->flags & COREDUMPER_FLAG_LIMITED_BY_PRIORITY)) ||
256 params->compressors != NULL) {
257 errno = EINVAL;
258 return -1;
259 }
260 SetCoreDumpParameter(params, flags, params->flags | COREDUMPER_FLAG_LIMITED | COREDUMPER_FLAG_LIMITED_BY_PRIORITY);
261 SetCoreDumpParameter(params, max_length, max_length);
262 return 0;
263 }
264
SetCoreDumpNotes(struct CoreDumpParameters * params,struct CoredumperNote * notes,int note_count)265 int SetCoreDumpNotes(struct CoreDumpParameters *params, struct CoredumperNote *notes, int note_count) {
266 SetCoreDumpParameter(params, notes, notes);
267 SetCoreDumpParameter(params, note_count, note_count);
268 return 0;
269 }
270
SetCoreDumpCallback(struct CoreDumpParameters * params,int (* fn)(void *),void * arg)271 int SetCoreDumpCallback(struct CoreDumpParameters *params, int (*fn)(void *), void *arg) {
272 SetCoreDumpParameter(params, callback_fn, fn);
273 SetCoreDumpParameter(params, callback_arg, arg);
274 return 0;
275 }
276