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