1 // Copyright (c) 2007, 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 // Author: Alfred Peng
31
32 #include <fcntl.h>
33 #include <sys/frame.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <sys/utsname.h>
37 #include <sys/wait.h>
38 #include <unistd.h>
39
40 #include <cstdlib>
41 #include <ctime>
42
43 #include "client/solaris/handler/minidump_generator.h"
44 #include "client/minidump_file_writer-inl.h"
45 #include "common/solaris/file_id.h"
46
47 namespace {
48
49 using namespace google_breakpad;
50
51 // Argument for the writer function.
52 struct WriterArgument {
53 MinidumpFileWriter *minidump_writer;
54
55 // Pid of the lwp who called WriteMinidumpToFile
56 int requester_pid;
57
58 // The stack bottom of the lwp which caused the dump.
59 // Mainly used to find the lwp id of the crashed lwp since signal
60 // handler may not be called in the lwp who caused it.
61 uintptr_t crashed_stack_bottom;
62
63 // Id of the crashing lwp.
64 int crashed_lwpid;
65
66 // Signal number when crash happened. Can be 0 if this is a requested dump.
67 int signo;
68
69 // The ebp of the signal handler frame on x86. Can be 0 if this is a
70 // requested dump.
71 uintptr_t sighandler_ebp;
72
73 // User context when crash happens. Can be NULL if this is a requested dump.
74 // This is actually an out parameter, but it will be filled in at the start
75 // of the writer LWP.
76 ucontext_t *sig_ctx;
77
78 // Used to get information about the lwps.
79 SolarisLwp *lwp_lister;
80 };
81
82 // Holding context information for the callback of finding the crashing lwp.
83 struct FindCrashLwpContext {
84 const SolarisLwp *lwp_lister;
85 uintptr_t crashing_stack_bottom;
86 int crashing_lwpid;
87
FindCrashLwpContext__anon77f7243e0111::FindCrashLwpContext88 FindCrashLwpContext() :
89 lwp_lister(NULL),
90 crashing_stack_bottom(0UL),
91 crashing_lwpid(-1) {
92 }
93 };
94
95 // Callback for list lwps.
96 // It will compare the stack bottom of the provided lwp with the stack
97 // bottom of the crashed lwp, it they are eqaul, this lwp is the one
98 // who crashed.
IsLwpCrashedCallback(lwpstatus_t * lsp,void * context)99 bool IsLwpCrashedCallback(lwpstatus_t *lsp, void *context) {
100 FindCrashLwpContext *crashing_context =
101 static_cast<FindCrashLwpContext *>(context);
102 const SolarisLwp *lwp_lister = crashing_context->lwp_lister;
103 const prgregset_t *gregs = &(lsp->pr_reg);
104 #if TARGET_CPU_SPARC
105 uintptr_t last_ebp = (*gregs)[R_FP];
106 #elif TARGET_CPU_X86
107 uintptr_t last_ebp = (*gregs)[EBP];
108 #endif
109 uintptr_t stack_bottom = lwp_lister->GetLwpStackBottom(last_ebp);
110 if (stack_bottom > last_ebp &&
111 stack_bottom == crashing_context->crashing_stack_bottom) {
112 // Got it. Stop iteration.
113 crashing_context->crashing_lwpid = lsp->pr_lwpid;
114 return false;
115 }
116
117 return true;
118 }
119
120 // Find the crashing lwpid.
121 // This is done based on stack bottom comparing.
FindCrashingLwp(uintptr_t crashing_stack_bottom,int requester_pid,const SolarisLwp * lwp_lister)122 int FindCrashingLwp(uintptr_t crashing_stack_bottom,
123 int requester_pid,
124 const SolarisLwp *lwp_lister) {
125 FindCrashLwpContext context;
126 context.lwp_lister = lwp_lister;
127 context.crashing_stack_bottom = crashing_stack_bottom;
128 CallbackParam<LwpCallback> callback_param(IsLwpCrashedCallback,
129 &context);
130 lwp_lister->Lwp_iter_all(lwp_lister->getpid(), &callback_param);
131 return context.crashing_lwpid;
132 }
133
WriteLwpStack(const SolarisLwp * lwp_lister,uintptr_t last_esp,UntypedMDRVA * memory,MDMemoryDescriptor * loc)134 bool WriteLwpStack(const SolarisLwp *lwp_lister,
135 uintptr_t last_esp,
136 UntypedMDRVA *memory,
137 MDMemoryDescriptor *loc) {
138 uintptr_t stack_bottom = lwp_lister->GetLwpStackBottom(last_esp);
139 if (stack_bottom >= last_esp) {
140 int size = stack_bottom - last_esp;
141 if (size > 0) {
142 if (!memory->Allocate(size))
143 return false;
144 memory->Copy(reinterpret_cast<void *>(last_esp), size);
145 loc->start_of_memory_range = last_esp;
146 loc->memory = memory->location();
147 }
148 return true;
149 }
150 return false;
151 }
152
153 #if TARGET_CPU_SPARC
WriteContext(MDRawContextSPARC * context,ucontext_t * sig_ctx)154 bool WriteContext(MDRawContextSPARC *context, ucontext_t *sig_ctx) {
155 assert(sig_ctx != NULL);
156 int* regs = sig_ctx->uc_mcontext.gregs;
157 context->context_flags = MD_CONTEXT_SPARC_FULL;
158
159 context->ccr = (unsigned int)(regs[0]);
160 context->pc = (unsigned int)(regs[REG_PC]);
161 context->npc = (unsigned int)(regs[REG_nPC]);
162 context->y = (unsigned int)(regs[REG_Y]);
163 context->asi = (unsigned int)(regs[19]);
164 context->fprs = (unsigned int)(regs[20]);
165
166 for ( int i = 0 ; i < 32; ++i ) {
167 context->g_r[i] = 0;
168 }
169
170 for ( int i = 1 ; i < 16; ++i ) {
171 context->g_r[i] = (uintptr_t)(sig_ctx->uc_mcontext.gregs[i + 3]);
172 }
173 context->g_r[30] = (uintptr_t)(((struct frame *)context->g_r[14])->fr_savfp);
174
175 return true;
176 }
177
WriteContext(MDRawContextSPARC * context,prgregset_t regs,prfpregset_t * fp_regs)178 bool WriteContext(MDRawContextSPARC *context, prgregset_t regs,
179 prfpregset_t *fp_regs) {
180 if (!context || !regs)
181 return false;
182
183 context->context_flags = MD_CONTEXT_SPARC_FULL;
184
185 context->ccr = (uintptr_t)(regs[32]);
186 context->pc = (uintptr_t)(regs[R_PC]);
187 context->npc = (uintptr_t)(regs[R_nPC]);
188 context->y = (uintptr_t)(regs[R_Y]);
189 context->asi = (uintptr_t)(regs[36]);
190 context->fprs = (uintptr_t)(regs[37]);
191 for ( int i = 0 ; i < 32 ; ++i ){
192 context->g_r[i] = (uintptr_t)(regs[i]);
193 }
194
195 return true;
196 }
197 #elif TARGET_CPU_X86
WriteContext(MDRawContextX86 * context,prgregset_t regs,prfpregset_t * fp_regs)198 bool WriteContext(MDRawContextX86 *context, prgregset_t regs,
199 prfpregset_t *fp_regs) {
200 if (!context || !regs)
201 return false;
202
203 context->context_flags = MD_CONTEXT_X86_FULL;
204
205 context->cs = regs[CS];
206 context->ds = regs[DS];
207 context->es = regs[ES];
208 context->fs = regs[FS];
209 context->gs = regs[GS];
210 context->ss = regs[SS];
211 context->edi = regs[EDI];
212 context->esi = regs[ESI];
213 context->ebx = regs[EBX];
214 context->edx = regs[EDX];
215 context->ecx = regs[ECX];
216 context->eax = regs[EAX];
217 context->ebp = regs[EBP];
218 context->eip = regs[EIP];
219 context->esp = regs[UESP];
220 context->eflags = regs[EFL];
221
222 return true;
223 }
224 #endif /* TARGET_CPU_XXX */
225
226 // Write information about a crashed Lwp.
227 // When a lwp crash, kernel will write something on the stack for processing
228 // signal. This makes the current stack not reliable, and our stack walker
229 // won't figure out the whole call stack for this. So we write the stack at the
230 // time of the crash into the minidump file, not the current stack.
WriteCrashedLwpStream(MinidumpFileWriter * minidump_writer,const WriterArgument * writer_args,const lwpstatus_t * lsp,MDRawThread * lwp)231 bool WriteCrashedLwpStream(MinidumpFileWriter *minidump_writer,
232 const WriterArgument *writer_args,
233 const lwpstatus_t *lsp,
234 MDRawThread *lwp) {
235 assert(writer_args->sig_ctx != NULL);
236
237 lwp->thread_id = lsp->pr_lwpid;
238
239 #if TARGET_CPU_SPARC
240 UntypedMDRVA memory(minidump_writer);
241 if (!WriteLwpStack(writer_args->lwp_lister,
242 writer_args->sig_ctx->uc_mcontext.gregs[REG_O6],
243 &memory,
244 &lwp->stack))
245 return false;
246
247 TypedMDRVA<MDRawContextSPARC> context(minidump_writer);
248 if (!context.Allocate())
249 return false;
250 lwp->thread_context = context.location();
251 memset(context.get(), 0, sizeof(MDRawContextSPARC));
252 return WriteContext(context.get(), writer_args->sig_ctx);
253 #elif TARGET_CPU_X86
254 UntypedMDRVA memory(minidump_writer);
255 if (!WriteLwpStack(writer_args->lwp_lister,
256 writer_args->sig_ctx->uc_mcontext.gregs[UESP],
257 &memory,
258 &lwp->stack))
259 return false;
260
261 TypedMDRVA<MDRawContextX86> context(minidump_writer);
262 if (!context.Allocate())
263 return false;
264 lwp->thread_context = context.location();
265 memset(context.get(), 0, sizeof(MDRawContextX86));
266 return WriteContext(context.get(),
267 (int *)&writer_args->sig_ctx->uc_mcontext.gregs,
268 &writer_args->sig_ctx->uc_mcontext.fpregs);
269 #endif
270 }
271
WriteLwpStream(MinidumpFileWriter * minidump_writer,const SolarisLwp * lwp_lister,const lwpstatus_t * lsp,MDRawThread * lwp)272 bool WriteLwpStream(MinidumpFileWriter *minidump_writer,
273 const SolarisLwp *lwp_lister,
274 const lwpstatus_t *lsp, MDRawThread *lwp) {
275 prfpregset_t fp_regs = lsp->pr_fpreg;
276 const prgregset_t *gregs = &(lsp->pr_reg);
277 UntypedMDRVA memory(minidump_writer);
278 #if TARGET_CPU_SPARC
279 if (!WriteLwpStack(lwp_lister,
280 (*gregs)[R_SP],
281 &memory,
282 &lwp->stack))
283 return false;
284
285 // Write context
286 TypedMDRVA<MDRawContextSPARC> context(minidump_writer);
287 if (!context.Allocate())
288 return false;
289 // should be the thread_id
290 lwp->thread_id = lsp->pr_lwpid;
291 lwp->thread_context = context.location();
292 memset(context.get(), 0, sizeof(MDRawContextSPARC));
293 #elif TARGET_CPU_X86
294 if (!WriteLwpStack(lwp_lister,
295 (*gregs)[UESP],
296 &memory,
297 &lwp->stack))
298 return false;
299
300 // Write context
301 TypedMDRVA<MDRawContextX86> context(minidump_writer);
302 if (!context.Allocate())
303 return false;
304 // should be the thread_id
305 lwp->thread_id = lsp->pr_lwpid;
306 lwp->thread_context = context.location();
307 memset(context.get(), 0, sizeof(MDRawContextX86));
308 #endif /* TARGET_CPU_XXX */
309 return WriteContext(context.get(), (int *)gregs, &fp_regs);
310 }
311
WriteCPUInformation(MDRawSystemInfo * sys_info)312 bool WriteCPUInformation(MDRawSystemInfo *sys_info) {
313 struct utsname uts;
314 char *major, *minor, *build;
315
316 sys_info->number_of_processors = sysconf(_SC_NPROCESSORS_CONF);
317 sys_info->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
318 if (uname(&uts) != -1) {
319 // Match "i86pc" as X86 architecture.
320 if (strcmp(uts.machine, "i86pc") == 0)
321 sys_info->processor_architecture = MD_CPU_ARCHITECTURE_X86;
322 else if (strcmp(uts.machine, "sun4u") == 0)
323 sys_info->processor_architecture = MD_CPU_ARCHITECTURE_SPARC;
324 }
325
326 major = uts.release;
327 minor = strchr(major, '.');
328 *minor = '\0';
329 ++minor;
330 sys_info->major_version = atoi(major);
331 sys_info->minor_version = atoi(minor);
332
333 build = strchr(uts.version, '_');
334 ++build;
335 sys_info->build_number = atoi(build);
336
337 return true;
338 }
339
WriteOSInformation(MinidumpFileWriter * minidump_writer,MDRawSystemInfo * sys_info)340 bool WriteOSInformation(MinidumpFileWriter *minidump_writer,
341 MDRawSystemInfo *sys_info) {
342 sys_info->platform_id = MD_OS_SOLARIS;
343
344 struct utsname uts;
345 if (uname(&uts) != -1) {
346 char os_version[512];
347 size_t space_left = sizeof(os_version);
348 memset(os_version, 0, space_left);
349 const char *os_info_table[] = {
350 uts.sysname,
351 uts.release,
352 uts.version,
353 uts.machine,
354 "OpenSolaris",
355 NULL
356 };
357 for (const char **cur_os_info = os_info_table;
358 *cur_os_info != NULL;
359 ++cur_os_info) {
360 if (cur_os_info != os_info_table && space_left > 1) {
361 strcat(os_version, " ");
362 --space_left;
363 }
364 if (space_left > strlen(*cur_os_info)) {
365 strcat(os_version, *cur_os_info);
366 space_left -= strlen(*cur_os_info);
367 } else {
368 break;
369 }
370 }
371
372 MDLocationDescriptor location;
373 if (!minidump_writer->WriteString(os_version, 0, &location))
374 return false;
375 sys_info->csd_version_rva = location.rva;
376 }
377 return true;
378 }
379
380 // Callback context for get writting lwp information.
381 struct LwpInfoCallbackCtx {
382 MinidumpFileWriter *minidump_writer;
383 const WriterArgument *writer_args;
384 TypedMDRVA<MDRawThreadList> *list;
385 int lwp_index;
386 };
387
LwpInformationCallback(lwpstatus_t * lsp,void * context)388 bool LwpInformationCallback(lwpstatus_t *lsp, void *context) {
389 bool success = true;
390 LwpInfoCallbackCtx *callback_context =
391 static_cast<LwpInfoCallbackCtx *>(context);
392
393 // The current lwp is the one to handle the crash. Ignore it.
394 if (lsp->pr_lwpid != pthread_self()) {
395 LwpInfoCallbackCtx *callback_context =
396 static_cast<LwpInfoCallbackCtx *>(context);
397 MDRawThread lwp;
398 memset(&lwp, 0, sizeof(MDRawThread));
399
400 if (lsp->pr_lwpid != callback_context->writer_args->crashed_lwpid ||
401 callback_context->writer_args->sig_ctx == NULL) {
402 success = WriteLwpStream(callback_context->minidump_writer,
403 callback_context->writer_args->lwp_lister,
404 lsp, &lwp);
405 } else {
406 success = WriteCrashedLwpStream(callback_context->minidump_writer,
407 callback_context->writer_args,
408 lsp, &lwp);
409 }
410 if (success) {
411 callback_context->list->CopyIndexAfterObject(
412 callback_context->lwp_index++,
413 &lwp, sizeof(MDRawThread));
414 }
415 }
416
417 return success;
418 }
419
WriteLwpListStream(MinidumpFileWriter * minidump_writer,const WriterArgument * writer_args,MDRawDirectory * dir)420 bool WriteLwpListStream(MinidumpFileWriter *minidump_writer,
421 const WriterArgument *writer_args,
422 MDRawDirectory *dir) {
423 // Get the lwp information.
424 const SolarisLwp *lwp_lister = writer_args->lwp_lister;
425 int lwp_count = lwp_lister->GetLwpCount();
426 if (lwp_count < 0)
427 return false;
428 TypedMDRVA<MDRawThreadList> list(minidump_writer);
429 if (!list.AllocateObjectAndArray(lwp_count - 1, sizeof(MDRawThread)))
430 return false;
431 dir->stream_type = MD_THREAD_LIST_STREAM;
432 dir->location = list.location();
433 list.get()->number_of_threads = lwp_count - 1;
434
435 LwpInfoCallbackCtx context;
436 context.minidump_writer = minidump_writer;
437 context.writer_args = writer_args;
438 context.list = &list;
439 context.lwp_index = 0;
440 CallbackParam<LwpCallback> callback_param(LwpInformationCallback,
441 &context);
442 int written =
443 lwp_lister->Lwp_iter_all(lwp_lister->getpid(), &callback_param);
444 return written == lwp_count;
445 }
446
WriteCVRecord(MinidumpFileWriter * minidump_writer,MDRawModule * module,const char * module_path,char * realname)447 bool WriteCVRecord(MinidumpFileWriter *minidump_writer,
448 MDRawModule *module,
449 const char *module_path,
450 char *realname) {
451 TypedMDRVA<MDCVInfoPDB70> cv(minidump_writer);
452
453 char path[PATH_MAX];
454 const char *module_name = module_path ? module_path : "<Unknown>";
455 snprintf(path, sizeof(path), "/proc/self/object/%s", module_name);
456
457 size_t module_name_length = strlen(realname);
458 if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(uint8_t)))
459 return false;
460 if (!cv.CopyIndexAfterObject(0, realname, module_name_length))
461 return false;
462
463 module->cv_record = cv.location();
464 MDCVInfoPDB70 *cv_ptr = cv.get();
465 memset(cv_ptr, 0, sizeof(MDCVInfoPDB70));
466 cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE;
467 cv_ptr->age = 0;
468
469 // Get the module identifier
470 FileID file_id(path);
471 unsigned char identifier[16];
472
473 if (file_id.ElfFileIdentifier(identifier)) {
474 cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 |
475 (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 |
476 (uint32_t)identifier[3];
477 cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5];
478 cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7];
479 cv_ptr->signature.data4[0] = identifier[8];
480 cv_ptr->signature.data4[1] = identifier[9];
481 cv_ptr->signature.data4[2] = identifier[10];
482 cv_ptr->signature.data4[3] = identifier[11];
483 cv_ptr->signature.data4[4] = identifier[12];
484 cv_ptr->signature.data4[5] = identifier[13];
485 cv_ptr->signature.data4[6] = identifier[14];
486 cv_ptr->signature.data4[7] = identifier[15];
487 }
488 return true;
489 }
490
491 struct ModuleInfoCallbackCtx {
492 MinidumpFileWriter *minidump_writer;
493 const WriterArgument *writer_args;
494 TypedMDRVA<MDRawModuleList> *list;
495 int module_index;
496 };
497
ModuleInfoCallback(const ModuleInfo & module_info,void * context)498 bool ModuleInfoCallback(const ModuleInfo &module_info, void *context) {
499 ModuleInfoCallbackCtx *callback_context =
500 static_cast<ModuleInfoCallbackCtx *>(context);
501 // Skip those modules without name, or those that are not modules.
502 if (strlen(module_info.name) == 0)
503 return true;
504
505 MDRawModule module;
506 memset(&module, 0, sizeof(module));
507 MDLocationDescriptor loc;
508 char path[PATH_MAX];
509 char buf[PATH_MAX];
510 char *realname;
511 int count;
512
513 snprintf(path, sizeof (path), "/proc/self/path/%s", module_info.name);
514 if ((count = readlink(path, buf, PATH_MAX)) < 0)
515 return false;
516 buf[count] = '\0';
517
518 if ((realname = strrchr(buf, '/')) == NULL)
519 return false;
520 realname++;
521
522 if (!callback_context->minidump_writer->WriteString(realname, 0, &loc))
523 return false;
524
525 module.base_of_image = (uint64_t)module_info.start_addr;
526 module.size_of_image = module_info.size;
527 module.module_name_rva = loc.rva;
528
529 if (!WriteCVRecord(callback_context->minidump_writer, &module,
530 module_info.name, realname))
531 return false;
532
533 callback_context->list->CopyIndexAfterObject(
534 callback_context->module_index++, &module, MD_MODULE_SIZE);
535 return true;
536 }
537
WriteModuleListStream(MinidumpFileWriter * minidump_writer,const WriterArgument * writer_args,MDRawDirectory * dir)538 bool WriteModuleListStream(MinidumpFileWriter *minidump_writer,
539 const WriterArgument *writer_args,
540 MDRawDirectory *dir) {
541 TypedMDRVA<MDRawModuleList> list(minidump_writer);
542 int module_count = writer_args->lwp_lister->GetModuleCount();
543
544 if (module_count <= 0 ||
545 !list.AllocateObjectAndArray(module_count, MD_MODULE_SIZE)) {
546 return false;
547 }
548
549 dir->stream_type = MD_MODULE_LIST_STREAM;
550 dir->location = list.location();
551 list.get()->number_of_modules = module_count;
552 ModuleInfoCallbackCtx context;
553 context.minidump_writer = minidump_writer;
554 context.writer_args = writer_args;
555 context.list = &list;
556 context.module_index = 0;
557 CallbackParam<ModuleCallback> callback(ModuleInfoCallback, &context);
558 return writer_args->lwp_lister->ListModules(&callback) == module_count;
559 }
560
WriteSystemInfoStream(MinidumpFileWriter * minidump_writer,const WriterArgument * writer_args,MDRawDirectory * dir)561 bool WriteSystemInfoStream(MinidumpFileWriter *minidump_writer,
562 const WriterArgument *writer_args,
563 MDRawDirectory *dir) {
564 TypedMDRVA<MDRawSystemInfo> sys_info(minidump_writer);
565
566 if (!sys_info.Allocate())
567 return false;
568
569 dir->stream_type = MD_SYSTEM_INFO_STREAM;
570 dir->location = sys_info.location();
571
572 return WriteCPUInformation(sys_info.get()) &&
573 WriteOSInformation(minidump_writer, sys_info.get());
574 }
575
WriteExceptionStream(MinidumpFileWriter * minidump_writer,const WriterArgument * writer_args,MDRawDirectory * dir)576 bool WriteExceptionStream(MinidumpFileWriter *minidump_writer,
577 const WriterArgument *writer_args,
578 MDRawDirectory *dir) {
579 // This happenes when this is not a crash, but a requested dump.
580 if (writer_args->sig_ctx == NULL)
581 return false;
582
583 TypedMDRVA<MDRawExceptionStream> exception(minidump_writer);
584 if (!exception.Allocate())
585 return false;
586
587 dir->stream_type = MD_EXCEPTION_STREAM;
588 dir->location = exception.location();
589 exception.get()->thread_id = writer_args->crashed_lwpid;
590 exception.get()->exception_record.exception_code = writer_args->signo;
591 exception.get()->exception_record.exception_flags = 0;
592
593 #if TARGET_CPU_SPARC
594 if (writer_args->sig_ctx != NULL) {
595 exception.get()->exception_record.exception_address =
596 writer_args->sig_ctx->uc_mcontext.gregs[REG_PC];
597 } else {
598 return true;
599 }
600
601 // Write context of the exception.
602 TypedMDRVA<MDRawContextSPARC> context(minidump_writer);
603 if (!context.Allocate())
604 return false;
605 exception.get()->thread_context = context.location();
606 memset(context.get(), 0, sizeof(MDRawContextSPARC));
607 return WriteContext(context.get(), writer_args->sig_ctx);
608 #elif TARGET_CPU_X86
609 if (writer_args->sig_ctx != NULL) {
610 exception.get()->exception_record.exception_address =
611 writer_args->sig_ctx->uc_mcontext.gregs[EIP];
612 } else {
613 return true;
614 }
615
616 // Write context of the exception.
617 TypedMDRVA<MDRawContextX86> context(minidump_writer);
618 if (!context.Allocate())
619 return false;
620 exception.get()->thread_context = context.location();
621 memset(context.get(), 0, sizeof(MDRawContextX86));
622 return WriteContext(context.get(),
623 (int *)&writer_args->sig_ctx->uc_mcontext.gregs,
624 NULL);
625 #endif
626 }
627
WriteMiscInfoStream(MinidumpFileWriter * minidump_writer,const WriterArgument * writer_args,MDRawDirectory * dir)628 bool WriteMiscInfoStream(MinidumpFileWriter *minidump_writer,
629 const WriterArgument *writer_args,
630 MDRawDirectory *dir) {
631 TypedMDRVA<MDRawMiscInfo> info(minidump_writer);
632
633 if (!info.Allocate())
634 return false;
635
636 dir->stream_type = MD_MISC_INFO_STREAM;
637 dir->location = info.location();
638 info.get()->size_of_info = sizeof(MDRawMiscInfo);
639 info.get()->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID;
640 info.get()->process_id = writer_args->requester_pid;
641
642 return true;
643 }
644
WriteBreakpadInfoStream(MinidumpFileWriter * minidump_writer,const WriterArgument * writer_args,MDRawDirectory * dir)645 bool WriteBreakpadInfoStream(MinidumpFileWriter *minidump_writer,
646 const WriterArgument *writer_args,
647 MDRawDirectory *dir) {
648 TypedMDRVA<MDRawBreakpadInfo> info(minidump_writer);
649
650 if (!info.Allocate())
651 return false;
652
653 dir->stream_type = MD_BREAKPAD_INFO_STREAM;
654 dir->location = info.location();
655
656 info.get()->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
657 MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
658 info.get()->dump_thread_id = getpid();
659 info.get()->requesting_thread_id = writer_args->requester_pid;
660 return true;
661 }
662
663 class AutoLwpResumer {
664 public:
AutoLwpResumer(SolarisLwp * lwp)665 AutoLwpResumer(SolarisLwp *lwp) : lwp_(lwp) {}
~AutoLwpResumer()666 ~AutoLwpResumer() { lwp_->ControlAllLwps(false); }
667 private:
668 SolarisLwp *lwp_;
669 };
670
671 // Prototype of writer functions.
672 typedef bool (*WriteStreamFN)(MinidumpFileWriter *,
673 const WriterArgument *,
674 MDRawDirectory *);
675
676 // Function table to writer a full minidump.
677 const WriteStreamFN writers[] = {
678 WriteLwpListStream,
679 WriteModuleListStream,
680 WriteSystemInfoStream,
681 WriteExceptionStream,
682 WriteMiscInfoStream,
683 WriteBreakpadInfoStream,
684 };
685
686 // Will call each writer function in the writers table.
687 //void* MinidumpGenerator::Write(void *argument) {
Write(void * argument)688 void* Write(void *argument) {
689 WriterArgument *writer_args = static_cast<WriterArgument *>(argument);
690
691 if (!writer_args->lwp_lister->ControlAllLwps(true))
692 return NULL;
693
694 AutoLwpResumer lwpResumer(writer_args->lwp_lister);
695
696 if (writer_args->sighandler_ebp != 0 &&
697 writer_args->lwp_lister->FindSigContext(writer_args->sighandler_ebp,
698 &writer_args->sig_ctx)) {
699 writer_args->crashed_stack_bottom =
700 writer_args->lwp_lister->GetLwpStackBottom(
701 #if TARGET_CPU_SPARC
702 writer_args->sig_ctx->uc_mcontext.gregs[REG_O6]
703 #elif TARGET_CPU_X86
704 writer_args->sig_ctx->uc_mcontext.gregs[UESP]
705 #endif
706 );
707
708 int crashed_lwpid = FindCrashingLwp(writer_args->crashed_stack_bottom,
709 writer_args->requester_pid,
710 writer_args->lwp_lister);
711 if (crashed_lwpid > 0)
712 writer_args->crashed_lwpid = crashed_lwpid;
713 }
714
715 MinidumpFileWriter *minidump_writer = writer_args->minidump_writer;
716 TypedMDRVA<MDRawHeader> header(minidump_writer);
717 TypedMDRVA<MDRawDirectory> dir(minidump_writer);
718 if (!header.Allocate())
719 return 0;
720
721 int writer_count = sizeof(writers) / sizeof(writers[0]);
722 // Need directory space for all writers.
723 if (!dir.AllocateArray(writer_count))
724 return 0;
725 header.get()->signature = MD_HEADER_SIGNATURE;
726 header.get()->version = MD_HEADER_VERSION;
727 header.get()->time_date_stamp = time(NULL);
728 header.get()->stream_count = writer_count;
729 header.get()->stream_directory_rva = dir.position();
730
731 int dir_index = 0;
732 MDRawDirectory local_dir;
733 for (int i = 0; i < writer_count; ++i) {
734 if ((*writers[i])(minidump_writer, writer_args, &local_dir))
735 dir.CopyIndex(dir_index++, &local_dir);
736 }
737
738 return 0;
739 }
740
741 } // namespace
742
743 namespace google_breakpad {
744
MinidumpGenerator()745 MinidumpGenerator::MinidumpGenerator() {
746 }
747
~MinidumpGenerator()748 MinidumpGenerator::~MinidumpGenerator() {
749 }
750
751 // Write minidump into file.
752 // It runs in a different thread from the crashing thread.
WriteMinidumpToFile(const char * file_pathname,int signo,uintptr_t sighandler_ebp,ucontext_t ** sig_ctx) const753 bool MinidumpGenerator::WriteMinidumpToFile(const char *file_pathname,
754 int signo,
755 uintptr_t sighandler_ebp,
756 ucontext_t **sig_ctx) const {
757 // The exception handler thread.
758 pthread_t handler_thread;
759
760 assert(file_pathname != NULL);
761
762 if (file_pathname == NULL)
763 return false;
764
765 MinidumpFileWriter minidump_writer;
766 if (minidump_writer.Open(file_pathname)) {
767 WriterArgument argument;
768 memset(&argument, 0, sizeof(argument));
769 SolarisLwp lwp_lister(getpid());
770 argument.lwp_lister = &lwp_lister;
771 argument.minidump_writer = &minidump_writer;
772 argument.requester_pid = getpid();
773 argument.crashed_lwpid = pthread_self();
774 argument.signo = signo;
775 argument.sighandler_ebp = sighandler_ebp;
776 argument.sig_ctx = NULL;
777
778 pthread_create(&handler_thread, NULL, Write, (void *)&argument);
779 pthread_join(handler_thread, NULL);
780 return true;
781 }
782
783 return false;
784 }
785
786 } // namespace google_breakpad
787