minidump_generator.cc revision 9dcc03f47d10a9b34fbeb80fcb4d2fce968a5c67
1// Copyright (c) 2006, 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#include <cstdio>
31
32#include <mach/host_info.h>
33#include <mach/mach_vm.h>
34#include <mach/vm_statistics.h>
35#include <mach-o/dyld.h>
36#include <mach-o/loader.h>
37#include <sys/sysctl.h>
38#include <sys/resource.h>
39#include <mach/mach_vm.h>
40
41#include <CoreFoundation/CoreFoundation.h>
42
43#include "client/mac/handler/minidump_generator.h"
44#include "client/minidump_file_writer-inl.h"
45#include "common/mac/file_id.h"
46#include "common/mac/string_utilities.h"
47
48using MacStringUtils::ConvertToString;
49using MacStringUtils::IntegerValueAtIndex;
50
51namespace google_breakpad {
52
53// constructor when generating from within the crashed process
54MinidumpGenerator::MinidumpGenerator()
55    : exception_type_(0),
56      exception_code_(0),
57      exception_subcode_(0),
58      exception_thread_(0),
59      crashing_task_(mach_task_self()),
60      handler_thread_(mach_thread_self()),
61      dynamic_images_(NULL) {
62  GatherSystemInformation();
63}
64
65// constructor when generating from a different process than the
66// crashed process
67MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
68                                     mach_port_t handler_thread)
69    : exception_type_(0),
70      exception_code_(0),
71      exception_subcode_(0),
72      exception_thread_(0),
73      crashing_task_(crashing_task),
74      handler_thread_(handler_thread) {
75  if (crashing_task != mach_task_self()) {
76    dynamic_images_ = new DynamicImages(crashing_task_);
77  } else {
78    dynamic_images_ = NULL;
79  }
80
81  GatherSystemInformation();
82}
83
84MinidumpGenerator::~MinidumpGenerator() {
85  delete dynamic_images_;
86}
87
88char MinidumpGenerator::build_string_[16];
89int MinidumpGenerator::os_major_version_ = 0;
90int MinidumpGenerator::os_minor_version_ = 0;
91int MinidumpGenerator::os_build_number_ = 0;
92
93// static
94void MinidumpGenerator::GatherSystemInformation() {
95  // If this is non-zero, then we've already gathered the information
96  if (os_major_version_)
97    return;
98
99  // This code extracts the version and build information from the OS
100  CFStringRef vers_path =
101    CFSTR("/System/Library/CoreServices/SystemVersion.plist");
102  CFURLRef sys_vers =
103    CFURLCreateWithFileSystemPath(NULL,
104                                  vers_path,
105                                  kCFURLPOSIXPathStyle,
106                                  false);
107  CFDataRef data;
108  SInt32 error;
109  CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL,
110                                           &error);
111
112  if (!data)
113    return;
114
115  CFDictionaryRef list = static_cast<CFDictionaryRef>
116    (CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable,
117                                     NULL));
118  if (!list)
119    return;
120
121  CFStringRef build_version = static_cast<CFStringRef>
122    (CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
123  CFStringRef product_version = static_cast<CFStringRef>
124    (CFDictionaryGetValue(list, CFSTR("ProductVersion")));
125  string build_str = ConvertToString(build_version);
126  string product_str = ConvertToString(product_version);
127
128  CFRelease(list);
129  CFRelease(sys_vers);
130  CFRelease(data);
131
132  strlcpy(build_string_, build_str.c_str(), sizeof(build_string_));
133
134  // Parse the string that looks like "10.4.8"
135  os_major_version_ = IntegerValueAtIndex(product_str, 0);
136  os_minor_version_ = IntegerValueAtIndex(product_str, 1);
137  os_build_number_ = IntegerValueAtIndex(product_str, 2);
138}
139
140string MinidumpGenerator::UniqueNameInDirectory(const string &dir,
141                                                string *unique_name) {
142  CFUUIDRef uuid = CFUUIDCreate(NULL);
143  CFStringRef uuid_cfstr = CFUUIDCreateString(NULL, uuid);
144  CFRelease(uuid);
145  string file_name(ConvertToString(uuid_cfstr));
146  CFRelease(uuid_cfstr);
147  string path(dir);
148
149  // Ensure that the directory (if non-empty) has a trailing slash so that
150  // we can append the file name and have a valid pathname.
151  if (!dir.empty()) {
152    if (dir.at(dir.size() - 1) != '/')
153      path.append(1, '/');
154  }
155
156  path.append(file_name);
157  path.append(".dmp");
158
159  if (unique_name)
160    *unique_name = file_name;
161
162  return path;
163}
164
165bool MinidumpGenerator::Write(const char *path) {
166  WriteStreamFN writers[] = {
167    &MinidumpGenerator::WriteThreadListStream,
168    &MinidumpGenerator::WriteSystemInfoStream,
169    &MinidumpGenerator::WriteModuleListStream,
170    &MinidumpGenerator::WriteMiscInfoStream,
171    &MinidumpGenerator::WriteBreakpadInfoStream,
172    // Exception stream needs to be the last entry in this array as it may
173    // be omitted in the case where the minidump is written without an
174    // exception.
175    &MinidumpGenerator::WriteExceptionStream,
176  };
177  bool result = false;
178
179  // If opening was successful, create the header, directory, and call each
180  // writer.  The destructor for the TypedMDRVAs will cause the data to be
181  // flushed.  The destructor for the MinidumpFileWriter will close the file.
182  if (writer_.Open(path)) {
183    TypedMDRVA<MDRawHeader> header(&writer_);
184    TypedMDRVA<MDRawDirectory> dir(&writer_);
185
186    if (!header.Allocate())
187      return false;
188
189    int writer_count = sizeof(writers) / sizeof(writers[0]);
190
191    // If we don't have exception information, don't write out the
192    // exception stream
193    if (!exception_thread_ && !exception_type_)
194      --writer_count;
195
196    // Add space for all writers
197    if (!dir.AllocateArray(writer_count))
198      return false;
199
200    MDRawHeader *header_ptr = header.get();
201    header_ptr->signature = MD_HEADER_SIGNATURE;
202    header_ptr->version = MD_HEADER_VERSION;
203    time(reinterpret_cast<time_t *>(&(header_ptr->time_date_stamp)));
204    header_ptr->stream_count = writer_count;
205    header_ptr->stream_directory_rva = dir.position();
206
207    MDRawDirectory local_dir;
208    result = true;
209    for (int i = 0; (result) && (i < writer_count); ++i) {
210      result = (this->*writers[i])(&local_dir);
211
212      if (result)
213        dir.CopyIndex(i, &local_dir);
214    }
215  }
216  return result;
217}
218
219size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) {
220  mach_vm_address_t stack_region_base = start_addr;
221  mach_vm_size_t stack_region_size;
222  natural_t nesting_level = 0;
223  vm_region_submap_info_64 submap_info;
224  mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
225
226  vm_region_recurse_info_t region_info;
227  region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info);
228
229  if (start_addr == 0) {
230    return 0;
231  }
232
233  kern_return_t result =
234    mach_vm_region_recurse(crashing_task_, &stack_region_base,
235                           &stack_region_size, &nesting_level,
236                           region_info,
237                           &info_count);
238
239  if (start_addr < stack_region_base) {
240    // probably stack corruption, since mach_vm_region had to go
241    // higher in the process address space to find a valid region.
242    return 0;
243  }
244
245
246  if ((stack_region_base + stack_region_size) == TOP_OF_THREAD0_STACK) {
247    // The stack for thread 0 needs to extend all the way to
248    // 0xc0000000 on 32 bit and 00007fff5fc00000 on 64bit.  HOWEVER,
249    // for many processes, the stack is first created in one page
250    // below this, and is then later extended to a much larger size by
251    // creating a new VM region immediately below the initial page.
252
253    // You can see this for yourself by running vmmap on a "hello,
254    // world" program
255
256    // Because of the above, we'll add 4k to include the original
257    // stack frame page.
258    // This method of finding the stack region needs to be done in
259    // a better way; the breakpad issue 247 is tracking this.
260    stack_region_size += 0x1000;
261  }
262
263  return result == KERN_SUCCESS ?
264    stack_region_base + stack_region_size - start_addr : 0;
265}
266
267bool MinidumpGenerator::WriteStackFromStartAddress(
268    mach_vm_address_t start_addr,
269    MDMemoryDescriptor *stack_location) {
270  UntypedMDRVA memory(&writer_);
271
272  bool result = false;
273  size_t size = CalculateStackSize(start_addr);
274
275  if (size == 0) {
276      // In some situations the stack address for the thread can come back 0.
277      // In these cases we skip over the threads in question and stuff the
278      // stack with a clearly borked value.
279      start_addr = 0xDEADBEEF;
280      size = 16;
281      if (!memory.Allocate(size))
282        return false;
283
284      unsigned long long dummy_stack[2];  // Fill dummy stack with 16 bytes of
285                                          // junk.
286      dummy_stack[0] = 0xDEADBEEF;
287      dummy_stack[1] = 0xDEADBEEF;
288
289      result = memory.Copy(dummy_stack, size);
290  } else {
291
292    if (!memory.Allocate(size))
293      return false;
294
295    if (dynamic_images_) {
296
297      kern_return_t kr;
298
299      void *stack_memory = ReadTaskMemory(crashing_task_,
300                                          (void*)start_addr,
301                                          size,
302                                          &kr);
303
304      if (stack_memory == NULL) {
305        return false;
306      }
307
308      result = memory.Copy(stack_memory, size);
309      free(stack_memory);
310    } else {
311      result = memory.Copy(reinterpret_cast<const void *>(start_addr), size);
312    }
313  }
314
315  stack_location->start_of_memory_range = start_addr;
316  stack_location->memory = memory.location();
317
318  return result;
319}
320
321#if TARGET_CPU_PPC || TARGET_CPU_PPC64
322bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
323                                   MDMemoryDescriptor *stack_location) {
324  breakpad_thread_state_t *machine_state =
325    reinterpret_cast<breakpad_thread_state_t *>(state);
326  mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
327  return WriteStackFromStartAddress(start_addr, stack_location);
328}
329
330u_int64_t
331MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
332  breakpad_thread_state_t *machine_state =
333    reinterpret_cast<breakpad_thread_state_t *>(state);
334
335  return REGISTER_FROM_THREADSTATE(machine_state, srr0);
336}
337
338bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
339                                     MDLocationDescriptor *register_location) {
340  TypedMDRVA<MinidumpContext> context(&writer_);
341  breakpad_thread_state_t *machine_state =
342    reinterpret_cast<breakpad_thread_state_t *>(state);
343
344  if (!context.Allocate())
345    return false;
346
347  *register_location = context.location();
348  MinidumpContext *context_ptr = context.get();
349  context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
350
351#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
352#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a)
353
354  AddReg(srr0);
355  AddReg(cr);
356  AddReg(xer);
357  AddReg(ctr);
358  AddReg(lr);
359  AddReg(vrsave);
360
361  AddGPR(0);
362  AddGPR(1);
363  AddGPR(2);
364  AddGPR(3);
365  AddGPR(4);
366  AddGPR(5);
367  AddGPR(6);
368  AddGPR(7);
369  AddGPR(8);
370  AddGPR(9);
371  AddGPR(10);
372  AddGPR(11);
373  AddGPR(12);
374  AddGPR(13);
375  AddGPR(14);
376  AddGPR(15);
377  AddGPR(16);
378  AddGPR(17);
379  AddGPR(18);
380  AddGPR(19);
381  AddGPR(20);
382  AddGPR(21);
383  AddGPR(22);
384  AddGPR(23);
385  AddGPR(24);
386  AddGPR(25);
387  AddGPR(26);
388  AddGPR(27);
389  AddGPR(28);
390  AddGPR(29);
391  AddGPR(30);
392  AddGPR(31);
393
394#if TARGET_CPU_PPC
395  /* The mq register  is only for PPC */
396  AddReg(mq);
397#endif
398
399
400  return true;
401}
402
403#elif TARGET_CPU_X86 || TARGET_CPU_X86_64
404
405bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
406                                   MDMemoryDescriptor *stack_location) {
407  breakpad_thread_state_t *machine_state =
408    reinterpret_cast<breakpad_thread_state_t *>(state);
409
410#if TARGET_CPU_X86_64
411  mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, rsp);
412#else
413  mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp);
414#endif
415  return WriteStackFromStartAddress(start_addr, stack_location);
416}
417
418u_int64_t
419MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
420  breakpad_thread_state_t *machine_state =
421    reinterpret_cast<breakpad_thread_state_t *>(state);
422
423#if TARGET_CPU_X86_64
424  return REGISTER_FROM_THREADSTATE(machine_state, rip);
425#else
426  return REGISTER_FROM_THREADSTATE(machine_state, eip);
427#endif
428}
429
430bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
431                                     MDLocationDescriptor *register_location) {
432  TypedMDRVA<MinidumpContext> context(&writer_);
433  breakpad_thread_state_t *machine_state =
434    reinterpret_cast<breakpad_thread_state_t *>(state);
435
436  if (!context.Allocate())
437    return false;
438
439  *register_location = context.location();
440  MinidumpContext *context_ptr = context.get();
441
442#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
443#if TARGET_CPU_X86
444  context_ptr->context_flags = MD_CONTEXT_X86;
445  AddReg(eax);
446  AddReg(ebx);
447  AddReg(ecx);
448  AddReg(edx);
449  AddReg(esi);
450  AddReg(edi);
451  AddReg(ebp);
452  AddReg(esp);
453
454  AddReg(cs);
455  AddReg(ds);
456  AddReg(ss);
457  AddReg(es);
458  AddReg(fs);
459  AddReg(gs);
460  AddReg(eflags);
461
462  AddReg(eip);
463#else
464  context_ptr->context_flags = MD_CONTEXT_AMD64;
465  AddReg(rax);
466  AddReg(rbx);
467  AddReg(rcx);
468  AddReg(rdx);
469  AddReg(rdi);
470  AddReg(rsi);
471  AddReg(rbp);
472  AddReg(rsp);
473  AddReg(r8);
474  AddReg(r9);
475  AddReg(r10);
476  AddReg(r11);
477  AddReg(r12);
478  AddReg(r13);
479  AddReg(r14);
480  AddReg(r15);
481  AddReg(rip);
482  // according to AMD's software developer guide, bits above 18 are
483  // not used in the flags register.  Since the minidump format
484  // specifies 32 bits for the flags register, we can truncate safely
485  // with no loss.
486  context_ptr->eflags = machine_state->__rflags;
487  AddReg(cs);
488  AddReg(fs);
489  AddReg(gs);
490#endif
491#undef AddReg(a)
492
493  return true;
494}
495#endif
496
497bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
498                                          MDRawThread *thread) {
499  breakpad_thread_state_data_t state;
500  mach_msg_type_number_t state_count = sizeof(state);
501
502  if (thread_get_state(thread_id, BREAKPAD_MACHINE_THREAD_STATE,
503                       state, &state_count) ==
504      KERN_SUCCESS) {
505    if (!WriteStack(state, &thread->stack))
506      return false;
507
508    if (!WriteContext(state, &thread->thread_context))
509      return false;
510
511    thread->thread_id = thread_id;
512  } else {
513    return false;
514  }
515
516  return true;
517}
518
519bool MinidumpGenerator::WriteThreadListStream(
520    MDRawDirectory *thread_list_stream) {
521  TypedMDRVA<MDRawThreadList> list(&writer_);
522  thread_act_port_array_t threads_for_task;
523  mach_msg_type_number_t thread_count;
524  int non_generator_thread_count;
525
526  if (task_threads(crashing_task_, &threads_for_task, &thread_count))
527    return false;
528
529  // Don't include the generator thread
530  non_generator_thread_count = thread_count - 1;
531  if (!list.AllocateObjectAndArray(non_generator_thread_count,
532                                   sizeof(MDRawThread)))
533    return false;
534
535  thread_list_stream->stream_type = MD_THREAD_LIST_STREAM;
536  thread_list_stream->location = list.location();
537
538  list.get()->number_of_threads = non_generator_thread_count;
539
540  MDRawThread thread;
541  int thread_idx = 0;
542
543  for (unsigned int i = 0; i < thread_count; ++i) {
544    memset(&thread, 0, sizeof(MDRawThread));
545
546    if (threads_for_task[i] != handler_thread_) {
547      if (!WriteThreadStream(threads_for_task[i], &thread))
548        return false;
549
550      list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread));
551    }
552  }
553
554  return true;
555}
556
557bool
558MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) {
559  TypedMDRVA<MDRawExceptionStream> exception(&writer_);
560
561  if (!exception.Allocate())
562    return false;
563
564  exception_stream->stream_type = MD_EXCEPTION_STREAM;
565  exception_stream->location = exception.location();
566  MDRawExceptionStream *exception_ptr = exception.get();
567  exception_ptr->thread_id = exception_thread_;
568
569  // This naming is confusing, but it is the proper translation from
570  // mach naming to minidump naming.
571  exception_ptr->exception_record.exception_code = exception_type_;
572  exception_ptr->exception_record.exception_flags = exception_code_;
573
574  breakpad_thread_state_data_t state;
575  mach_msg_type_number_t stateCount = sizeof(state);
576
577  if (thread_get_state(exception_thread_,
578                       BREAKPAD_MACHINE_THREAD_STATE,
579                       state,
580                       &stateCount) != KERN_SUCCESS)
581    return false;
582
583  if (!WriteContext(state, &exception_ptr->thread_context))
584    return false;
585
586  if (exception_type_ == EXC_BAD_ACCESS)
587    exception_ptr->exception_record.exception_address = exception_subcode_;
588  else
589    exception_ptr->exception_record.exception_address = CurrentPCForStack(state);
590
591  return true;
592}
593
594bool MinidumpGenerator::WriteSystemInfoStream(
595    MDRawDirectory *system_info_stream) {
596  TypedMDRVA<MDRawSystemInfo> info(&writer_);
597
598  if (!info.Allocate())
599    return false;
600
601  system_info_stream->stream_type = MD_SYSTEM_INFO_STREAM;
602  system_info_stream->location = info.location();
603
604  // CPU Information
605  uint32_t cpu_type;
606  size_t len = sizeof(cpu_type);
607  sysctlbyname("hw.cputype", &cpu_type, &len, NULL, 0);
608  uint32_t number_of_processors;
609  len = sizeof(number_of_processors);
610  sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0);
611  MDRawSystemInfo *info_ptr = info.get();
612
613  switch (cpu_type) {
614    case CPU_TYPE_POWERPC:
615      info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
616      break;
617    case CPU_TYPE_I386:
618      info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
619#ifdef __i386__
620      // ebx is used for PIC code, so we need
621      // to preserve it.
622#define cpuid(op,eax,ebx,ecx,edx)      \
623  asm ("pushl %%ebx   \n\t"            \
624       "cpuid         \n\t"            \
625       "movl %%ebx,%1 \n\t"            \
626       "popl %%ebx"                    \
627       : "=a" (eax),                   \
628         "=g" (ebx),                   \
629         "=c" (ecx),                   \
630         "=d" (edx)                    \
631       : "0" (op))
632      int unused, unused2;
633      // get vendor id
634      cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0],
635            info_ptr->cpu.x86_cpu_info.vendor_id[2],
636            info_ptr->cpu.x86_cpu_info.vendor_id[1]);
637      // get version and feature info
638      cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2,
639            info_ptr->cpu.x86_cpu_info.feature_information);
640
641      // family
642      info_ptr->processor_level =
643        (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8;
644      // 0xMMSS (Model, Stepping)
645      info_ptr->processor_revision =
646        (info_ptr->cpu.x86_cpu_info.version_information & 0xF) |
647        ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4);
648
649      // decode extended model info
650      if (info_ptr->processor_level == 0xF ||
651          info_ptr->processor_level == 0x6) {
652        info_ptr->processor_revision |=
653          ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4);
654      }
655
656      // decode extended family info
657      if (info_ptr->processor_level == 0xF) {
658        info_ptr->processor_level +=
659          ((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20);
660      }
661
662#endif // __i386__
663      break;
664    default:
665      info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
666      break;
667  }
668
669  info_ptr->number_of_processors = number_of_processors;
670  info_ptr->platform_id = MD_OS_MAC_OS_X;
671
672  MDLocationDescriptor build_string_loc;
673
674  if (!writer_.WriteString(build_string_, 0,
675                           &build_string_loc))
676    return false;
677
678  info_ptr->csd_version_rva = build_string_loc.rva;
679  info_ptr->major_version = os_major_version_;
680  info_ptr->minor_version = os_minor_version_;
681  info_ptr->build_number = os_build_number_;
682
683  return true;
684}
685
686bool MinidumpGenerator::WriteModuleStream(unsigned int index,
687                                          MDRawModule *module) {
688  if (dynamic_images_) {
689    // we're in a different process than the crashed process
690    DynamicImage *image = dynamic_images_->GetImage(index);
691
692    if (!image)
693      return false;
694
695    const breakpad_mach_header *header = image->GetMachHeader();
696
697    if (!header)
698      return false;
699
700    int cpu_type = header->cputype;
701
702    memset(module, 0, sizeof(MDRawModule));
703
704    MDLocationDescriptor string_location;
705
706    const char* name = image->GetFilePath();
707    if (!writer_.WriteString(name, 0, &string_location))
708      return false;
709
710    module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
711    module->size_of_image = image->GetVMSize();
712    module->module_name_rva = string_location.rva;
713
714    // We'll skip the executable module, because they don't have
715    // LC_ID_DYLIB load commands, and the crash processing server gets
716    // version information from the Plist file, anyway.
717    if (index != (uint32_t)FindExecutableModule()) {
718      module->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE;
719      module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION;
720      // Convert MAC dylib version format, which is a 32 bit number, to the
721      // format used by minidump.  The mac format is <16 bits>.<8 bits>.<8 bits>
722      // so it fits nicely into the windows version with some massaging
723      // The mapping is:
724      //    1) upper 16 bits of MAC version go to lower 16 bits of product HI
725      //    2) Next most significant 8 bits go to upper 16 bits of product LO
726      //    3) Least significant 8 bits go to lower 16 bits of product LO
727      uint32_t modVersion = image->GetVersion();
728      module->version_info.file_version_hi = 0;
729      module->version_info.file_version_hi = modVersion >> 16;
730      module->version_info.file_version_lo |= (modVersion & 0xff00)  << 8;
731      module->version_info.file_version_lo |= (modVersion & 0xff);
732    }
733
734    if (!WriteCVRecord(module, cpu_type, name)) {
735      return false;
736    }
737  } else {
738    // we're getting module info in the crashed process
739
740    const breakpad_mach_header *header;
741    header = (breakpad_mach_header*)_dyld_get_image_header(index);
742    if (!header)
743      return false;
744
745#ifdef __LP64__
746    assert(header->magic == MH_MAGIC_64);
747
748    if(header->magic != MH_MAGIC_64)
749      return false;
750#else
751    assert(header->magic == MH_MAGIC);
752
753    if(header->magic != MH_MAGIC)
754      return false;
755#endif
756
757    int cpu_type = header->cputype;
758    unsigned long slide = _dyld_get_image_vmaddr_slide(index);
759    const char* name = _dyld_get_image_name(index);
760    const struct load_command *cmd =
761      reinterpret_cast<const struct load_command *>(header + 1);
762
763    memset(module, 0, sizeof(MDRawModule));
764
765    for (unsigned int i = 0; cmd && (i < header->ncmds); i++) {
766      if (cmd->cmd == LC_SEGMENT) {
767
768        const breakpad_mach_segment_command *seg =
769          reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
770
771        if (!strcmp(seg->segname, "__TEXT")) {
772          MDLocationDescriptor string_location;
773
774          if (!writer_.WriteString(name, 0, &string_location))
775            return false;
776
777          module->base_of_image = seg->vmaddr + slide;
778          module->size_of_image = seg->vmsize;
779          module->module_name_rva = string_location.rva;
780
781          if (!WriteCVRecord(module, cpu_type, name))
782            return false;
783
784          return true;
785        }
786      }
787
788      cmd = reinterpret_cast<struct load_command*>((char *)cmd + cmd->cmdsize);
789    }
790  }
791
792  return true;
793}
794
795int MinidumpGenerator::FindExecutableModule() {
796  if (dynamic_images_) {
797    int index = dynamic_images_->GetExecutableImageIndex();
798
799    if (index >= 0) {
800      return index;
801    }
802  } else {
803    int image_count = _dyld_image_count();
804    const struct mach_header *header;
805
806    for (int index = 0; index < image_count; ++index) {
807      header = _dyld_get_image_header(index);
808
809      if (header->filetype == MH_EXECUTE)
810        return index;
811    }
812  }
813
814  // failed - just use the first image
815  return 0;
816}
817
818bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type,
819                                      const char *module_path) {
820  TypedMDRVA<MDCVInfoPDB70> cv(&writer_);
821
822  // Only return the last path component of the full module path
823  const char *module_name = strrchr(module_path, '/');
824
825  // Increment past the slash
826  if (module_name)
827    ++module_name;
828  else
829    module_name = "<Unknown>";
830
831  size_t module_name_length = strlen(module_name);
832
833  if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t)))
834    return false;
835
836  if (!cv.CopyIndexAfterObject(0, module_name, module_name_length))
837    return false;
838
839  module->cv_record = cv.location();
840  MDCVInfoPDB70 *cv_ptr = cv.get();
841  cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE;
842  cv_ptr->age = 0;
843
844  // Get the module identifier
845  FileID file_id(module_path);
846  unsigned char identifier[16];
847
848  if (file_id.MachoIdentifier(cpu_type, identifier)) {
849    cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 |
850      (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 |
851      (uint32_t)identifier[3];
852    cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5];
853    cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7];
854    cv_ptr->signature.data4[0] = identifier[8];
855    cv_ptr->signature.data4[1] = identifier[9];
856    cv_ptr->signature.data4[2] = identifier[10];
857    cv_ptr->signature.data4[3] = identifier[11];
858    cv_ptr->signature.data4[4] = identifier[12];
859    cv_ptr->signature.data4[5] = identifier[13];
860    cv_ptr->signature.data4[6] = identifier[14];
861    cv_ptr->signature.data4[7] = identifier[15];
862  }
863
864  return true;
865}
866
867bool MinidumpGenerator::WriteModuleListStream(
868    MDRawDirectory *module_list_stream) {
869  TypedMDRVA<MDRawModuleList> list(&writer_);
870
871  int image_count = dynamic_images_ ?
872    dynamic_images_->GetImageCount() : _dyld_image_count();
873
874  if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE))
875    return false;
876
877  module_list_stream->stream_type = MD_MODULE_LIST_STREAM;
878  module_list_stream->location = list.location();
879  list.get()->number_of_modules = image_count;
880
881  // Write out the executable module as the first one
882  MDRawModule module;
883  int executableIndex = FindExecutableModule();
884
885  if (!WriteModuleStream(executableIndex, &module)) {
886    return false;
887  }
888
889  list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE);
890  int destinationIndex = 1;  // Write all other modules after this one
891
892  for (int i = 0; i < image_count; ++i) {
893    if (i != executableIndex) {
894      if (!WriteModuleStream(i, &module)) {
895        return false;
896      }
897
898      list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE);
899    }
900  }
901
902  return true;
903}
904
905bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
906  TypedMDRVA<MDRawMiscInfo> info(&writer_);
907
908  if (!info.Allocate())
909    return false;
910
911  misc_info_stream->stream_type = MD_MISC_INFO_STREAM;
912  misc_info_stream->location = info.location();
913
914  MDRawMiscInfo *info_ptr = info.get();
915  info_ptr->size_of_info = sizeof(MDRawMiscInfo);
916  info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID |
917    MD_MISCINFO_FLAGS1_PROCESS_TIMES |
918    MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO;
919
920  // Process ID
921  info_ptr->process_id = getpid();
922
923  // Times
924  struct rusage usage;
925  if (getrusage(RUSAGE_SELF, &usage) != -1) {
926    // Omit the fractional time since the MDRawMiscInfo only wants seconds
927    info_ptr->process_user_time = usage.ru_utime.tv_sec;
928    info_ptr->process_kernel_time = usage.ru_stime.tv_sec;
929  }
930  int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, info_ptr->process_id };
931  size_t size;
932  if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &size, NULL, 0)) {
933    mach_vm_address_t addr;
934    if (mach_vm_allocate(mach_task_self(),
935                         &addr,
936                         size,
937                         true) == KERN_SUCCESS) {
938      struct kinfo_proc *proc = (struct kinfo_proc *)addr;
939      if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), proc, &size, NULL, 0))
940        info_ptr->process_create_time = proc->kp_proc.p_starttime.tv_sec;
941      mach_vm_deallocate(mach_task_self(), addr, size);
942    }
943  }
944
945  // Speed
946  uint64_t speed;
947  size = sizeof(speed);
948  sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0);
949  info_ptr->processor_max_mhz = speed / (1000 * 1000);
950  info_ptr->processor_mhz_limit = speed / (1000 * 1000);
951  size = sizeof(speed);
952  sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0);
953  info_ptr->processor_current_mhz = speed / (1000 * 1000);
954
955  return true;
956}
957
958bool MinidumpGenerator::WriteBreakpadInfoStream(
959    MDRawDirectory *breakpad_info_stream) {
960  TypedMDRVA<MDRawBreakpadInfo> info(&writer_);
961
962  if (!info.Allocate())
963    return false;
964
965  breakpad_info_stream->stream_type = MD_BREAKPAD_INFO_STREAM;
966  breakpad_info_stream->location = info.location();
967  MDRawBreakpadInfo *info_ptr = info.get();
968
969  if (exception_thread_ && exception_type_) {
970    info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
971                         MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
972    info_ptr->dump_thread_id = handler_thread_;
973    info_ptr->requesting_thread_id = exception_thread_;
974  } else {
975    info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID;
976    info_ptr->dump_thread_id = handler_thread_;
977    info_ptr->requesting_thread_id = 0;
978  }
979
980  return true;
981}
982
983}  // namespace google_breakpad
984