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