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