xref: /aosp_15_r20/external/google-breakpad/src/client/solaris/handler/minidump_generator.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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