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 47namespace { 48 49using namespace google_breakpad; 50 51// Argument for the writer function. 52struct 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. 83struct FindCrashLwpContext { 84 const SolarisLwp *lwp_lister; 85 uintptr_t crashing_stack_bottom; 86 int crashing_lwpid; 87 88 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. 99bool 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. 122int 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 134bool 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 154bool 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 178bool 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 198bool 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. 231bool 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 272bool 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 312bool 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 340bool 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. 381struct LwpInfoCallbackCtx { 382 MinidumpFileWriter *minidump_writer; 383 const WriterArgument *writer_args; 384 TypedMDRVA<MDRawThreadList> *list; 385 int lwp_index; 386}; 387 388bool 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 420bool 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 447bool 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 491struct ModuleInfoCallbackCtx { 492 MinidumpFileWriter *minidump_writer; 493 const WriterArgument *writer_args; 494 TypedMDRVA<MDRawModuleList> *list; 495 int module_index; 496}; 497 498bool 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 538bool 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 561bool 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 576bool 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 628bool 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 645bool 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 663class AutoLwpResumer { 664 public: 665 AutoLwpResumer(SolarisLwp *lwp) : lwp_(lwp) {} 666 ~AutoLwpResumer() { lwp_->ControlAllLwps(false); } 667 private: 668 SolarisLwp *lwp_; 669}; 670 671// Prototype of writer functions. 672typedef bool (*WriteStreamFN)(MinidumpFileWriter *, 673 const WriterArgument *, 674 MDRawDirectory *); 675 676// Function table to writer a full minidump. 677const 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) { 688void* 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 743namespace google_breakpad { 744 745MinidumpGenerator::MinidumpGenerator() { 746} 747 748MinidumpGenerator::~MinidumpGenerator() { 749} 750 751// Write minidump into file. 752// It runs in a different thread from the crashing thread. 753bool 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