minidump_generator.cc revision 32d40647362a014f2184a6f4d2c8e0453eb8f655
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/vm_statistics.h>
34#include <mach-o/dyld.h>
35#include <mach-o/loader.h>
36#include <sys/sysctl.h>
37#include <sys/resource.h>
38
39#include <CoreFoundation/CoreFoundation.h>
40
41#include "client/mac/handler/minidump_generator.h"
42#include "client/minidump_file_writer-inl.h"
43#include "common/mac/file_id.h"
44#include "common/mac/string_utilities.h"
45
46using MacStringUtils::ConvertToString;
47using MacStringUtils::IntegerValueAtIndex;
48
49namespace google_breakpad {
50
51MinidumpGenerator::MinidumpGenerator()
52    : exception_type_(0),
53      exception_code_(0),
54      exception_thread_(0),
55      crashing_task_(mach_task_self()),
56      handler_thread_(mach_thread_self()) {
57  dynamic_images_ = new DynamicImages(mach_task_self());
58  GatherSystemInformation();
59}
60
61MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread)
62    : exception_type_(0),
63      exception_code_(0),
64      exception_thread_(0),
65      crashing_task_(crashing_task),
66      handler_thread_(handler_thread) {
67  dynamic_images_ = new DynamicImages(crashing_task_);
68  GatherSystemInformation();
69}
70
71MinidumpGenerator::~MinidumpGenerator() {
72}
73
74char MinidumpGenerator::build_string_[16];
75int MinidumpGenerator::os_major_version_ = 0;
76int MinidumpGenerator::os_minor_version_ = 0;
77int MinidumpGenerator::os_build_number_ = 0;
78
79// static
80void MinidumpGenerator::GatherSystemInformation() {
81  // If this is non-zero, then we've already gathered the information
82  if (os_major_version_)
83    return;
84
85  // This code extracts the version and build information from the OS
86  CFStringRef vers_path =
87    CFSTR("/System/Library/CoreServices/SystemVersion.plist");
88  CFURLRef sys_vers =
89    CFURLCreateWithFileSystemPath(NULL, vers_path, kCFURLPOSIXPathStyle, false);
90  CFDataRef data;
91  SInt32 error;
92  CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL,
93                                           &error);
94
95  if (!data)
96    return;
97
98  CFDictionaryRef list = static_cast<CFDictionaryRef>
99    (CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable,
100                                     NULL));
101  if (!list)
102    return;
103
104  CFStringRef build_version = static_cast<CFStringRef>
105    (CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
106  CFStringRef product_version = static_cast<CFStringRef>
107    (CFDictionaryGetValue(list, CFSTR("ProductVersion")));
108  string build_str = ConvertToString(build_version);
109  string product_str = ConvertToString(product_version);
110
111  CFRelease(list);
112  CFRelease(sys_vers);
113  CFRelease(data);
114
115  strlcpy(build_string_, build_str.c_str(), sizeof(build_string_));
116
117  // Parse the string that looks like "10.4.8"
118  os_major_version_ = IntegerValueAtIndex(product_str, 0);
119  os_minor_version_ = IntegerValueAtIndex(product_str, 1);
120  os_build_number_ = IntegerValueAtIndex(product_str, 2);
121}
122
123string MinidumpGenerator::UniqueNameInDirectory(const string &dir,
124                                                string *unique_name) {
125  CFUUIDRef uuid = CFUUIDCreate(NULL);
126  CFStringRef uuid_cfstr = CFUUIDCreateString(NULL, uuid);
127  CFRelease(uuid);
128  string file_name(ConvertToString(uuid_cfstr));
129  CFRelease(uuid_cfstr);
130  string path(dir);
131
132  // Ensure that the directory (if non-empty) has a trailing slash so that
133  // we can append the file name and have a valid pathname.
134  if (!dir.empty()) {
135    if (dir.at(dir.size() - 1) != '/')
136      path.append(1, '/');
137  }
138
139  path.append(file_name);
140  path.append(".dmp");
141
142  if (unique_name)
143    *unique_name = file_name;
144
145  return path;
146}
147
148bool MinidumpGenerator::Write(const char *path) {
149  WriteStreamFN writers[] = {
150    &MinidumpGenerator::WriteThreadListStream,
151    &MinidumpGenerator::WriteSystemInfoStream,
152    &MinidumpGenerator::WriteModuleListStream,
153    &MinidumpGenerator::WriteMiscInfoStream,
154    &MinidumpGenerator::WriteBreakpadInfoStream,
155    // Exception stream needs to be the last entry in this array as it may
156    // be omitted in the case where the minidump is written without an
157    // exception.
158    &MinidumpGenerator::WriteExceptionStream,
159  };
160  bool result = true;
161
162  // If opening was successful, create the header, directory, and call each
163  // writer.  The destructor for the TypedMDRVAs will cause the data to be
164  // flushed.  The destructor for the MinidumpFileWriter will close the file.
165  if (writer_.Open(path)) {
166    TypedMDRVA<MDRawHeader> header(&writer_);
167    TypedMDRVA<MDRawDirectory> dir(&writer_);
168
169    if (!header.Allocate())
170      return false;
171
172    int writer_count = sizeof(writers) / sizeof(writers[0]);
173
174    // If we don't have exception information, don't write out the
175    // exception stream
176    if (!exception_thread_ && !exception_type_)
177      --writer_count;
178
179    // Add space for all writers
180    if (!dir.AllocateArray(writer_count))
181      return false;
182
183    MDRawHeader *header_ptr = header.get();
184    header_ptr->signature = MD_HEADER_SIGNATURE;
185    header_ptr->version = MD_HEADER_VERSION;
186    time(reinterpret_cast<time_t *>(&(header_ptr->time_date_stamp)));
187    header_ptr->stream_count = writer_count;
188    header_ptr->stream_directory_rva = dir.position();
189
190    MDRawDirectory local_dir;
191    for (int i = 0; (result) && (i < writer_count); ++i) {
192      result = (this->*writers[i])(&local_dir);
193
194      if (result)
195        dir.CopyIndex(i, &local_dir);
196    }
197  }
198
199  return result;
200}
201
202size_t MinidumpGenerator::CalculateStackSize(vm_address_t start_addr) {
203  vm_address_t stack_region_base = start_addr;
204  vm_size_t stack_region_size;
205  natural_t nesting_level = 0;
206  vm_region_submap_info submap_info;
207  mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT;
208  kern_return_t result =
209    vm_region_recurse(crashing_task_, &stack_region_base, &stack_region_size,
210                      &nesting_level,
211                      reinterpret_cast<vm_region_recurse_info_t>(&submap_info),
212                      &info_count);
213
214  if ((stack_region_base + stack_region_size) == 0xbffff000) {
215    // The stack for thread 0 needs to extend all the way to 0xc0000000
216    // For many processes the stack is first created in one page
217    // from 0xbffff000 - 0xc0000000 and is then later extended to
218    // a much larger size by creating a new VM region immediately below
219    // the initial page
220
221    // include the original stack frame page (0xbffff000 - 0xc0000000)
222    stack_region_size += 0x1000;
223  }
224
225  return result == KERN_SUCCESS ?
226    stack_region_base + stack_region_size - start_addr : 0;
227}
228
229bool MinidumpGenerator::WriteStackFromStartAddress(
230    vm_address_t start_addr,
231    MDMemoryDescriptor *stack_location) {
232  UntypedMDRVA memory(&writer_);
233  size_t size = CalculateStackSize(start_addr);
234
235  // If there's an error in the calculation, return at least the current
236  // stack information
237  if (size == 0)
238    size = 16;
239
240  if (!memory.Allocate(size))
241    return false;
242
243  void *stack_memory = ReadTaskMemory(crashing_task_, (void*)start_addr, size);
244
245  bool result = memory.Copy(stack_memory, size);
246
247  free(stack_memory);
248
249
250  stack_location->start_of_memory_range = start_addr;
251  stack_location->memory = memory.location();
252
253  return result;
254}
255
256#if TARGET_CPU_PPC
257bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
258                                   MDMemoryDescriptor *stack_location) {
259  ppc_thread_state_t *machine_state =
260    reinterpret_cast<ppc_thread_state_t *>(state);
261  vm_address_t start_addr = machine_state->r1;
262  return WriteStackFromStartAddress(start_addr, stack_location);
263}
264
265u_int64_t MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
266  ppc_thread_state_t *machine_state =
267    reinterpret_cast<ppc_thread_state_t *>(state);
268
269  return machine_state->srr0;
270}
271
272bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
273                                     MDLocationDescriptor *register_location) {
274  TypedMDRVA<MDRawContextPPC> context(&writer_);
275  ppc_thread_state_t *machine_state =
276    reinterpret_cast<ppc_thread_state_t *>(state);
277
278  if (!context.Allocate())
279    return false;
280
281  *register_location = context.location();
282  MDRawContextPPC *context_ptr = context.get();
283  context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
284
285#define AddReg(a) context_ptr->a = machine_state->a
286#define AddGPR(a) context_ptr->gpr[a] = machine_state->r ## a
287  AddReg(srr0);
288  AddReg(cr);
289  AddReg(xer);
290  AddReg(ctr);
291  AddReg(mq);
292  AddReg(lr);
293  AddReg(vrsave);
294
295  AddGPR(0);
296  AddGPR(1);
297  AddGPR(2);
298  AddGPR(3);
299  AddGPR(4);
300  AddGPR(5);
301  AddGPR(6);
302  AddGPR(7);
303  AddGPR(8);
304  AddGPR(9);
305  AddGPR(10);
306  AddGPR(11);
307  AddGPR(12);
308  AddGPR(13);
309  AddGPR(14);
310  AddGPR(15);
311  AddGPR(16);
312  AddGPR(17);
313  AddGPR(18);
314  AddGPR(19);
315  AddGPR(20);
316  AddGPR(21);
317  AddGPR(22);
318  AddGPR(23);
319  AddGPR(24);
320  AddGPR(25);
321  AddGPR(26);
322  AddGPR(27);
323  AddGPR(28);
324  AddGPR(29);
325  AddGPR(30);
326  AddGPR(31);
327  return true;
328}
329
330#elif TARGET_CPU_X86
331bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
332                                   MDMemoryDescriptor *stack_location) {
333  i386_thread_state_t *machine_state =
334    reinterpret_cast<i386_thread_state_t *>(state);
335  vm_address_t start_addr = machine_state->esp;
336  return WriteStackFromStartAddress(start_addr, stack_location);
337}
338
339u_int64_t MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
340  i386_thread_state_t *machine_state =
341    reinterpret_cast<i386_thread_state_t *>(state);
342
343  return machine_state->eip;
344}
345
346bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
347                                     MDLocationDescriptor *register_location) {
348  TypedMDRVA<MDRawContextX86> context(&writer_);
349  i386_thread_state_t *machine_state =
350    reinterpret_cast<i386_thread_state_t *>(state);
351
352  if (!context.Allocate())
353    return false;
354
355  *register_location = context.location();
356  MDRawContextX86 *context_ptr = context.get();
357  context_ptr->context_flags = MD_CONTEXT_X86;
358#define AddReg(a) context_ptr->a = machine_state->a
359  AddReg(cs);
360  AddReg(ds);
361  AddReg(ss);
362  AddReg(es);
363  AddReg(fs);
364  AddReg(gs);
365  AddReg(eflags);
366
367  AddReg(eip);
368  AddReg(eax);
369  AddReg(ebx);
370  AddReg(ecx);
371  AddReg(edx);
372  AddReg(esi);
373  AddReg(edi);
374  AddReg(ebp);
375  AddReg(esp);
376  return true;
377}
378#endif
379
380bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
381                                          MDRawThread *thread) {
382  breakpad_thread_state_data_t state;
383  mach_msg_type_number_t state_count = sizeof(state);
384
385  if (thread_get_state(thread_id, BREAKPAD_MACHINE_THREAD_STATE,
386                       state, &state_count) ==
387      KERN_SUCCESS) {
388    if (!WriteStack(state, &thread->stack))
389      return false;
390
391    if (!WriteContext(state, &thread->thread_context))
392      return false;
393
394    thread->thread_id = thread_id;
395  } else {
396    return false;
397  }
398
399  return true;
400}
401
402bool MinidumpGenerator::WriteThreadListStream(
403    MDRawDirectory *thread_list_stream) {
404  TypedMDRVA<MDRawThreadList> list(&writer_);
405  thread_act_port_array_t threads_for_task;
406  mach_msg_type_number_t thread_count;
407  int non_generator_thread_count;
408
409  if (task_threads(crashing_task_, &threads_for_task, &thread_count))
410    return false;
411
412  // Don't include the generator thread
413  non_generator_thread_count = thread_count - 1;
414  if (!list.AllocateObjectAndArray(non_generator_thread_count,
415                                   sizeof(MDRawThread)))
416    return false;
417
418  thread_list_stream->stream_type = MD_THREAD_LIST_STREAM;
419  thread_list_stream->location = list.location();
420
421  list.get()->number_of_threads = non_generator_thread_count;
422
423  MDRawThread thread;
424  int thread_idx = 0;
425
426  for (unsigned int i = 0; i < thread_count; ++i) {
427    memset(&thread, 0, sizeof(MDRawThread));
428
429    if (threads_for_task[i] != handler_thread_) {
430      if (!WriteThreadStream(threads_for_task[i], &thread))
431        return false;
432
433      list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread));
434    }
435  }
436
437  return true;
438}
439
440bool MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) {
441  TypedMDRVA<MDRawExceptionStream> exception(&writer_);
442
443  if (!exception.Allocate())
444    return false;
445
446  exception_stream->stream_type = MD_EXCEPTION_STREAM;
447  exception_stream->location = exception.location();
448  MDRawExceptionStream *exception_ptr = exception.get();
449  exception_ptr->thread_id = exception_thread_;
450
451  // This naming is confusing, but it is the proper translation from
452  // mach naming to minidump naming.
453  exception_ptr->exception_record.exception_code = exception_type_;
454  exception_ptr->exception_record.exception_flags = exception_code_;
455
456  breakpad_thread_state_data_t state;
457  mach_msg_type_number_t stateCount = sizeof(state);
458
459  if (thread_get_state(exception_thread_, BREAKPAD_MACHINE_THREAD_STATE, state,
460                       &stateCount) != KERN_SUCCESS)
461    return false;
462
463  if (!WriteContext(state, &exception_ptr->thread_context))
464    return false;
465
466  exception_ptr->exception_record.exception_address = CurrentPCForStack(state);
467
468  return true;
469}
470
471bool MinidumpGenerator::WriteSystemInfoStream(
472    MDRawDirectory *system_info_stream) {
473  TypedMDRVA<MDRawSystemInfo> info(&writer_);
474
475  if (!info.Allocate())
476    return false;
477
478  system_info_stream->stream_type = MD_SYSTEM_INFO_STREAM;
479  system_info_stream->location = info.location();
480
481  // CPU Information
482  uint32_t cpu_type;
483  size_t len = sizeof(cpu_type);
484  sysctlbyname("hw.cputype", &cpu_type, &len, NULL, 0);
485  uint32_t number_of_processors;
486  len = sizeof(number_of_processors);
487  sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0);
488  MDRawSystemInfo *info_ptr = info.get();
489
490  switch (cpu_type) {
491    case CPU_TYPE_POWERPC:
492      info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
493      break;
494    case CPU_TYPE_I386:
495      info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
496#ifdef __i386__
497      // ebx is used for PIC code, so we need
498      // to preserve it.
499#define cpuid(op,eax,ebx,ecx,edx)      \
500  asm ("pushl %%ebx   \n\t"            \
501       "cpuid         \n\t"	       \
502       "movl %%ebx,%1 \n\t"	       \
503       "popl %%ebx"		       \
504       : "=a" (eax),		       \
505         "=g" (ebx),		       \
506         "=c" (ecx),		       \
507         "=d" (edx)		       \
508       : "0" (op))
509      int unused, unused2;
510      // get vendor id
511      cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0],
512            info_ptr->cpu.x86_cpu_info.vendor_id[2],
513            info_ptr->cpu.x86_cpu_info.vendor_id[1]);
514      // get version and feature info
515      cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2,
516            info_ptr->cpu.x86_cpu_info.feature_information);
517      // family
518      info_ptr->processor_level =
519        (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8;
520      // 0xMMSS (Model, Stepping)
521      info_ptr->processor_revision =
522        (info_ptr->cpu.x86_cpu_info.version_information & 0xF) |
523        ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4);
524#endif // __i386__
525      break;
526    default:
527      info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
528      break;
529  }
530
531  info_ptr->number_of_processors = number_of_processors;
532  info_ptr->platform_id = MD_OS_MAC_OS_X;
533
534  MDLocationDescriptor build_string_loc;
535
536  if (!writer_.WriteString(build_string_, 0,
537                           &build_string_loc))
538    return false;
539
540  info_ptr->csd_version_rva = build_string_loc.rva;
541  info_ptr->major_version = os_major_version_;
542  info_ptr->minor_version = os_minor_version_;
543  info_ptr->build_number = os_build_number_;
544
545  return true;
546}
547
548bool MinidumpGenerator::WriteModuleStream(unsigned int index,
549                                          MDRawModule *module) {
550  DynamicImage *image = dynamic_images_->GetImage(index);
551
552  if (!image)
553    return false;
554
555  const mach_header *header = image->GetMachHeader();
556
557  if (!header)
558    return false;
559
560  int cpu_type = header->cputype;
561
562  memset(module, 0, sizeof(MDRawModule));
563
564  MDLocationDescriptor string_location;
565
566  const char* name = image->GetFilePath();
567  if (!writer_.WriteString(name, 0, &string_location))
568    return false;
569
570  module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
571  module->size_of_image = image->GetVMSize();
572  module->module_name_rva = string_location.rva;
573
574  if (!WriteCVRecord(module, cpu_type, name)) {
575    return false;
576  }
577
578  return true;
579}
580
581int MinidumpGenerator::FindExecutableModule() {
582  int index = dynamic_images_->GetExecutableImageIndex();
583
584  if (index >= 0) {
585    return index;
586  }
587
588  // failed - just use the first image
589  return 0;
590}
591
592bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type,
593                                      const char *module_path) {
594  TypedMDRVA<MDCVInfoPDB70> cv(&writer_);
595
596  // Only return the last path component of the full module path
597  const char *module_name = strrchr(module_path, '/');
598
599  // Increment past the slash
600  if (module_name)
601    ++module_name;
602  else
603    module_name = "<Unknown>";
604
605  size_t module_name_length = strlen(module_name);
606
607  if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t)))
608    return false;
609
610  if (!cv.CopyIndexAfterObject(0, module_name, module_name_length))
611    return false;
612
613  module->cv_record = cv.location();
614  MDCVInfoPDB70 *cv_ptr = cv.get();
615  cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE;
616  cv_ptr->age = 0;
617
618  // Get the module identifier
619  FileID file_id(module_path);
620  unsigned char identifier[16];
621
622  if (file_id.MachoIdentifier(cpu_type, identifier)) {
623    cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 |
624      (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 |
625      (uint32_t)identifier[3];
626    cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5];
627    cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7];
628    cv_ptr->signature.data4[0] = identifier[8];
629    cv_ptr->signature.data4[1] = identifier[9];
630    cv_ptr->signature.data4[2] = identifier[10];
631    cv_ptr->signature.data4[3] = identifier[11];
632    cv_ptr->signature.data4[4] = identifier[12];
633    cv_ptr->signature.data4[5] = identifier[13];
634    cv_ptr->signature.data4[6] = identifier[14];
635    cv_ptr->signature.data4[7] = identifier[15];
636  }
637
638  return true;
639}
640
641bool MinidumpGenerator::WriteModuleListStream(
642    MDRawDirectory *module_list_stream) {
643  TypedMDRVA<MDRawModuleList> list(&writer_);
644
645  if (!_dyld_present())
646    return false;
647
648  int image_count = dynamic_images_->GetImageCount();
649
650  if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE))
651    return false;
652
653  module_list_stream->stream_type = MD_MODULE_LIST_STREAM;
654  module_list_stream->location = list.location();
655  list.get()->number_of_modules = image_count;
656
657  // Write out the executable module as the first one
658  MDRawModule module;
659  int executableIndex = FindExecutableModule();
660
661  if (!WriteModuleStream(executableIndex, &module)) {
662    return false;
663  }
664
665  list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE);
666  int destinationIndex = 1;  // Write all other modules after this one
667
668  for (int i = 0; i < image_count; ++i) {
669    if (i != executableIndex) {
670      if (!WriteModuleStream(i, &module)) {
671        return false;
672      }
673
674      list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE);
675    }
676  }
677
678  return true;
679}
680
681bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
682  TypedMDRVA<MDRawMiscInfo> info(&writer_);
683
684  if (!info.Allocate())
685    return false;
686
687  misc_info_stream->stream_type = MD_MISC_INFO_STREAM;
688  misc_info_stream->location = info.location();
689
690  MDRawMiscInfo *info_ptr = info.get();
691  info_ptr->size_of_info = sizeof(MDRawMiscInfo);
692  info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID |
693    MD_MISCINFO_FLAGS1_PROCESS_TIMES |
694    MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO;
695
696  // Process ID
697  info_ptr->process_id = getpid();
698
699  // Times
700  struct rusage usage;
701  if (getrusage(RUSAGE_SELF, &usage) != -1) {
702    // Omit the fractional time since the MDRawMiscInfo only wants seconds
703    info_ptr->process_user_time = usage.ru_utime.tv_sec;
704    info_ptr->process_kernel_time = usage.ru_stime.tv_sec;
705  }
706  int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, info_ptr->process_id };
707  size_t size;
708  if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &size, NULL, 0)) {
709    vm_address_t addr;
710    if (vm_allocate(mach_task_self(), &addr, size, true) == KERN_SUCCESS) {
711      struct kinfo_proc *proc = (struct kinfo_proc *)addr;
712      if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), proc, &size, NULL, 0))
713        info_ptr->process_create_time = proc->kp_proc.p_starttime.tv_sec;
714      vm_deallocate(mach_task_self(), addr, size);
715    }
716  }
717
718  // Speed
719  uint64_t speed;
720  size = sizeof(speed);
721  sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0);
722  info_ptr->processor_max_mhz = speed / (1000 * 1000);
723  info_ptr->processor_mhz_limit = speed / (1000 * 1000);
724  size = sizeof(speed);
725  sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0);
726  info_ptr->processor_current_mhz = speed / (1000 * 1000);
727
728  return true;
729}
730
731bool MinidumpGenerator::WriteBreakpadInfoStream(
732    MDRawDirectory *breakpad_info_stream) {
733  TypedMDRVA<MDRawBreakpadInfo> info(&writer_);
734
735  if (!info.Allocate())
736    return false;
737
738  breakpad_info_stream->stream_type = MD_BREAKPAD_INFO_STREAM;
739  breakpad_info_stream->location = info.location();
740  MDRawBreakpadInfo *info_ptr = info.get();
741
742  if (exception_thread_ && exception_type_) {
743    info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
744                         MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
745    info_ptr->dump_thread_id = handler_thread_;
746    info_ptr->requesting_thread_id = exception_thread_;
747  } else {
748    info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID;
749    info_ptr->dump_thread_id = handler_thread_;
750    info_ptr->requesting_thread_id = 0;
751  }
752
753  return true;
754}
755
756}  // namespace google_breakpad
757