minidump_generator.cc revision 60a883212fbe065f37da2ffdeca2bbe22742c7e8
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 <algorithm>
31#include <cstdio>
32
33#include <mach/host_info.h>
34#include <mach/i386/thread_status.h>
35#include <mach/mach_vm.h>
36#include <mach/vm_statistics.h>
37#include <mach-o/dyld.h>
38#include <mach-o/loader.h>
39#include <sys/sysctl.h>
40#include <sys/resource.h>
41
42#include <CoreFoundation/CoreFoundation.h>
43
44#include "client/mac/handler/minidump_generator.h"
45
46#ifdef HAS_PPC_SUPPORT
47#include <mach/ppc/thread_status.h>
48#endif
49
50#include "client/minidump_file_writer-inl.h"
51#include "common/mac/file_id.h"
52#include "common/mac/string_utilities.h"
53
54using MacStringUtils::ConvertToString;
55using MacStringUtils::IntegerValueAtIndex;
56
57namespace google_breakpad {
58
59#if __LP64__
60#define LC_SEGMENT_ARCH LC_SEGMENT_64
61#else
62#define LC_SEGMENT_ARCH LC_SEGMENT
63#endif
64
65// constructor when generating from within the crashed process
66MinidumpGenerator::MinidumpGenerator()
67    : writer_(),
68      exception_type_(0),
69      exception_code_(0),
70      exception_subcode_(0),
71      exception_thread_(0),
72      crashing_task_(mach_task_self()),
73      handler_thread_(mach_thread_self()),
74      cpu_type_(DynamicImages::GetNativeCPUType()),
75      dynamic_images_(NULL),
76      memory_blocks_(&allocator_) {
77  GatherSystemInformation();
78}
79
80// constructor when generating from a different process than the
81// crashed process
82MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
83                                     mach_port_t handler_thread)
84    : writer_(),
85      exception_type_(0),
86      exception_code_(0),
87      exception_subcode_(0),
88      exception_thread_(0),
89      crashing_task_(crashing_task),
90      handler_thread_(handler_thread),
91      cpu_type_(DynamicImages::GetNativeCPUType()),
92      dynamic_images_(NULL),
93      memory_blocks_(&allocator_) {
94  if (crashing_task != mach_task_self()) {
95    dynamic_images_ = new DynamicImages(crashing_task_);
96    cpu_type_ = dynamic_images_->GetCPUType();
97  } else {
98    dynamic_images_ = NULL;
99    cpu_type_ = DynamicImages::GetNativeCPUType();
100  }
101
102  GatherSystemInformation();
103}
104
105MinidumpGenerator::~MinidumpGenerator() {
106  delete dynamic_images_;
107}
108
109char MinidumpGenerator::build_string_[16];
110int MinidumpGenerator::os_major_version_ = 0;
111int MinidumpGenerator::os_minor_version_ = 0;
112int MinidumpGenerator::os_build_number_ = 0;
113
114// static
115void MinidumpGenerator::GatherSystemInformation() {
116  // If this is non-zero, then we've already gathered the information
117  if (os_major_version_)
118    return;
119
120  // This code extracts the version and build information from the OS
121  CFStringRef vers_path =
122    CFSTR("/System/Library/CoreServices/SystemVersion.plist");
123  CFURLRef sys_vers =
124    CFURLCreateWithFileSystemPath(NULL,
125                                  vers_path,
126                                  kCFURLPOSIXPathStyle,
127                                  false);
128  CFDataRef data;
129  SInt32 error;
130  CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL,
131                                           &error);
132
133  if (!data)
134    return;
135
136  CFDictionaryRef list = static_cast<CFDictionaryRef>
137    (CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable,
138                                     NULL));
139  if (!list)
140    return;
141
142  CFStringRef build_version = static_cast<CFStringRef>
143    (CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
144  CFStringRef product_version = static_cast<CFStringRef>
145    (CFDictionaryGetValue(list, CFSTR("ProductVersion")));
146  string build_str = ConvertToString(build_version);
147  string product_str = ConvertToString(product_version);
148
149  CFRelease(list);
150  CFRelease(sys_vers);
151  CFRelease(data);
152
153  strlcpy(build_string_, build_str.c_str(), sizeof(build_string_));
154
155  // Parse the string that looks like "10.4.8"
156  os_major_version_ = IntegerValueAtIndex(product_str, 0);
157  os_minor_version_ = IntegerValueAtIndex(product_str, 1);
158  os_build_number_ = IntegerValueAtIndex(product_str, 2);
159}
160
161string MinidumpGenerator::UniqueNameInDirectory(const string &dir,
162                                                string *unique_name) {
163  CFUUIDRef uuid = CFUUIDCreate(NULL);
164  CFStringRef uuid_cfstr = CFUUIDCreateString(NULL, uuid);
165  CFRelease(uuid);
166  string file_name(ConvertToString(uuid_cfstr));
167  CFRelease(uuid_cfstr);
168  string path(dir);
169
170  // Ensure that the directory (if non-empty) has a trailing slash so that
171  // we can append the file name and have a valid pathname.
172  if (!dir.empty()) {
173    if (dir.at(dir.size() - 1) != '/')
174      path.append(1, '/');
175  }
176
177  path.append(file_name);
178  path.append(".dmp");
179
180  if (unique_name)
181    *unique_name = file_name;
182
183  return path;
184}
185
186bool MinidumpGenerator::Write(const char *path) {
187  WriteStreamFN writers[] = {
188    &MinidumpGenerator::WriteThreadListStream,
189    &MinidumpGenerator::WriteMemoryListStream,
190    &MinidumpGenerator::WriteSystemInfoStream,
191    &MinidumpGenerator::WriteModuleListStream,
192    &MinidumpGenerator::WriteMiscInfoStream,
193    &MinidumpGenerator::WriteBreakpadInfoStream,
194    // Exception stream needs to be the last entry in this array as it may
195    // be omitted in the case where the minidump is written without an
196    // exception.
197    &MinidumpGenerator::WriteExceptionStream,
198  };
199  bool result = false;
200
201  // If opening was successful, create the header, directory, and call each
202  // writer.  The destructor for the TypedMDRVAs will cause the data to be
203  // flushed.  The destructor for the MinidumpFileWriter will close the file.
204  if (writer_.Open(path)) {
205    TypedMDRVA<MDRawHeader> header(&writer_);
206    TypedMDRVA<MDRawDirectory> dir(&writer_);
207
208    if (!header.Allocate())
209      return false;
210
211    int writer_count = static_cast<int>(sizeof(writers) / sizeof(writers[0]));
212
213    // If we don't have exception information, don't write out the
214    // exception stream
215    if (!exception_thread_ && !exception_type_)
216      --writer_count;
217
218    // Add space for all writers
219    if (!dir.AllocateArray(writer_count))
220      return false;
221
222    MDRawHeader *header_ptr = header.get();
223    header_ptr->signature = MD_HEADER_SIGNATURE;
224    header_ptr->version = MD_HEADER_VERSION;
225    time(reinterpret_cast<time_t *>(&(header_ptr->time_date_stamp)));
226    header_ptr->stream_count = writer_count;
227    header_ptr->stream_directory_rva = dir.position();
228
229    MDRawDirectory local_dir;
230    result = true;
231    for (int i = 0; (result) && (i < writer_count); ++i) {
232      result = (this->*writers[i])(&local_dir);
233
234      if (result)
235        dir.CopyIndex(i, &local_dir);
236    }
237  }
238  return result;
239}
240
241size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) {
242  mach_vm_address_t stack_region_base = start_addr;
243  mach_vm_size_t stack_region_size;
244  natural_t nesting_level = 0;
245  vm_region_submap_info_64 submap_info;
246  mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
247
248  vm_region_recurse_info_t region_info;
249  region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info);
250
251  if (start_addr == 0) {
252    return 0;
253  }
254
255  kern_return_t result =
256    mach_vm_region_recurse(crashing_task_, &stack_region_base,
257                           &stack_region_size, &nesting_level,
258                           region_info, &info_count);
259
260  if (result != KERN_SUCCESS || start_addr < stack_region_base) {
261    // Failure or stack corruption, since mach_vm_region had to go
262    // higher in the process address space to find a valid region.
263    return 0;
264  }
265
266  unsigned int tag = submap_info.user_tag;
267
268  // If the user tag is VM_MEMORY_STACK, look for more readable regions with
269  // the same tag placed immediately above the computed stack region. Under
270  // some circumstances, the stack for thread 0 winds up broken up into
271  // multiple distinct abutting regions. This can happen for several reasons,
272  // including user code that calls setrlimit(RLIMIT_STACK, ...) or changes
273  // the access on stack pages by calling mprotect.
274  if (tag == VM_MEMORY_STACK) {
275    while (true) {
276      mach_vm_address_t next_region_base = stack_region_base +
277                                           stack_region_size;
278      mach_vm_address_t proposed_next_region_base = next_region_base;
279      mach_vm_size_t next_region_size;
280      nesting_level = 0;
281      mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
282      result = mach_vm_region_recurse(crashing_task_, &next_region_base,
283                                      &next_region_size, &nesting_level,
284                                      region_info, &info_count);
285      if (result != KERN_SUCCESS ||
286          next_region_base != proposed_next_region_base ||
287          submap_info.user_tag != tag ||
288          submap_info.protection & VM_PROT_READ == 0) {
289        break;
290      }
291
292      stack_region_size += next_region_size;
293    }
294  }
295
296  return stack_region_base + stack_region_size - start_addr;
297}
298
299bool MinidumpGenerator::WriteStackFromStartAddress(
300    mach_vm_address_t start_addr,
301    MDMemoryDescriptor *stack_location) {
302  UntypedMDRVA memory(&writer_);
303
304  bool result = false;
305  size_t size = CalculateStackSize(start_addr);
306
307  if (size == 0) {
308      // In some situations the stack address for the thread can come back 0.
309      // In these cases we skip over the threads in question and stuff the
310      // stack with a clearly borked value.
311      start_addr = 0xDEADBEEF;
312      size = 16;
313      if (!memory.Allocate(size))
314        return false;
315
316      unsigned long long dummy_stack[2];  // Fill dummy stack with 16 bytes of
317                                          // junk.
318      dummy_stack[0] = 0xDEADBEEF;
319      dummy_stack[1] = 0xDEADBEEF;
320
321      result = memory.Copy(dummy_stack, size);
322  } else {
323
324    if (!memory.Allocate(size))
325      return false;
326
327    if (dynamic_images_) {
328      vector<uint8_t> stack_memory;
329      if (ReadTaskMemory(crashing_task_,
330                         start_addr,
331                         size,
332                         stack_memory) != KERN_SUCCESS) {
333        return false;
334      }
335
336      result = memory.Copy(&stack_memory[0], size);
337    } else {
338      result = memory.Copy(reinterpret_cast<const void *>(start_addr), size);
339    }
340  }
341
342  stack_location->start_of_memory_range = start_addr;
343  stack_location->memory = memory.location();
344
345  return result;
346}
347
348bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
349                                   MDMemoryDescriptor *stack_location) {
350  switch (cpu_type_) {
351#ifdef HAS_PPC_SUPPORT
352    case CPU_TYPE_POWERPC:
353      return WriteStackPPC(state, stack_location);
354    case CPU_TYPE_POWERPC64:
355      return WriteStackPPC64(state, stack_location);
356#endif
357    case CPU_TYPE_I386:
358      return WriteStackX86(state, stack_location);
359    case CPU_TYPE_X86_64:
360      return WriteStackX86_64(state, stack_location);
361    default:
362      return false;
363  }
364}
365
366bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
367                                     MDLocationDescriptor *register_location) {
368  switch (cpu_type_) {
369#ifdef HAS_PPC_SUPPORT
370    case CPU_TYPE_POWERPC:
371      return WriteContextPPC(state, register_location);
372    case CPU_TYPE_POWERPC64:
373      return WriteContextPPC64(state, register_location);
374#endif
375    case CPU_TYPE_I386:
376      return WriteContextX86(state, register_location);
377    case CPU_TYPE_X86_64:
378      return WriteContextX86_64(state, register_location);
379    default:
380      return false;
381  }
382}
383
384u_int64_t MinidumpGenerator::CurrentPCForStack(
385    breakpad_thread_state_data_t state) {
386  switch (cpu_type_) {
387#ifdef HAS_PPC_SUPPORT
388    case CPU_TYPE_POWERPC:
389      return CurrentPCForStackPPC(state);
390    case CPU_TYPE_POWERPC64:
391      return CurrentPCForStackPPC64(state);
392#endif
393    case CPU_TYPE_I386:
394      return CurrentPCForStackX86(state);
395    case CPU_TYPE_X86_64:
396      return CurrentPCForStackX86_64(state);
397    default:
398      assert("Unknown CPU type!");
399      return 0;
400  }
401}
402
403#ifdef HAS_PCC_SUPPORT
404bool MinidumpGenerator::WriteStackPPC(breakpad_thread_state_data_t state,
405                                      MDMemoryDescriptor *stack_location) {
406  ppc_thread_state_t *machine_state =
407      reinterpret_cast<ppc_thread_state_t *>(state);
408  mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
409  return WriteStackFromStartAddress(start_addr, stack_location);
410}
411
412bool MinidumpGenerator::WriteStackPPC64(breakpad_thread_state_data_t state,
413                                        MDMemoryDescriptor *stack_location) {
414  ppc_thread_state64_t *machine_state =
415      reinterpret_cast<ppc_thread_state64_t *>(state);
416  mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
417  return WriteStackFromStartAddress(start_addr, stack_location);
418}
419
420u_int64_t
421MinidumpGenerator::CurrentPCForStackPPC(breakpad_thread_state_data_t state) {
422  ppc_thread_state_t *machine_state =
423      reinterpret_cast<ppc_thread_state_t *>(state);
424
425  return REGISTER_FROM_THREADSTATE(machine_state, srr0);
426}
427
428u_int64_t
429MinidumpGenerator::CurrentPCForStackPPC64(breakpad_thread_state_data_t state) {
430  ppc_thread_state64_t *machine_state =
431      reinterpret_cast<ppc_thread_state64_t *>(state);
432
433  return REGISTER_FROM_THREADSTATE(machine_state, srr0);
434}
435
436bool MinidumpGenerator::WriteContextPPC(breakpad_thread_state_data_t state,
437                                        MDLocationDescriptor *register_location)
438{
439  TypedMDRVA<MDRawContextPPC> context(&writer_);
440  ppc_thread_state_t *machine_state =
441      reinterpret_cast<ppc_thread_state_t *>(state);
442
443  if (!context.Allocate())
444    return false;
445
446  *register_location = context.location();
447  MDRawContextPPC *context_ptr = context.get();
448  context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
449
450#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
451#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a)
452
453  AddReg(srr0);
454  AddReg(cr);
455  AddReg(xer);
456  AddReg(ctr);
457  AddReg(lr);
458  AddReg(vrsave);
459
460  AddGPR(0);
461  AddGPR(1);
462  AddGPR(2);
463  AddGPR(3);
464  AddGPR(4);
465  AddGPR(5);
466  AddGPR(6);
467  AddGPR(7);
468  AddGPR(8);
469  AddGPR(9);
470  AddGPR(10);
471  AddGPR(11);
472  AddGPR(12);
473  AddGPR(13);
474  AddGPR(14);
475  AddGPR(15);
476  AddGPR(16);
477  AddGPR(17);
478  AddGPR(18);
479  AddGPR(19);
480  AddGPR(20);
481  AddGPR(21);
482  AddGPR(22);
483  AddGPR(23);
484  AddGPR(24);
485  AddGPR(25);
486  AddGPR(26);
487  AddGPR(27);
488  AddGPR(28);
489  AddGPR(29);
490  AddGPR(30);
491  AddGPR(31);
492  AddReg(mq);
493#undef AddReg
494#undef AddGPR
495
496  return true;
497}
498
499bool MinidumpGenerator::WriteContextPPC64(
500    breakpad_thread_state_data_t state,
501    MDLocationDescriptor *register_location) {
502  TypedMDRVA<MDRawContextPPC64> context(&writer_);
503  ppc_thread_state64_t *machine_state =
504      reinterpret_cast<ppc_thread_state64_t *>(state);
505
506  if (!context.Allocate())
507    return false;
508
509  *register_location = context.location();
510  MDRawContextPPC64 *context_ptr = context.get();
511  context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
512
513#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
514#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a)
515
516  AddReg(srr0);
517  AddReg(cr);
518  AddReg(xer);
519  AddReg(ctr);
520  AddReg(lr);
521  AddReg(vrsave);
522
523  AddGPR(0);
524  AddGPR(1);
525  AddGPR(2);
526  AddGPR(3);
527  AddGPR(4);
528  AddGPR(5);
529  AddGPR(6);
530  AddGPR(7);
531  AddGPR(8);
532  AddGPR(9);
533  AddGPR(10);
534  AddGPR(11);
535  AddGPR(12);
536  AddGPR(13);
537  AddGPR(14);
538  AddGPR(15);
539  AddGPR(16);
540  AddGPR(17);
541  AddGPR(18);
542  AddGPR(19);
543  AddGPR(20);
544  AddGPR(21);
545  AddGPR(22);
546  AddGPR(23);
547  AddGPR(24);
548  AddGPR(25);
549  AddGPR(26);
550  AddGPR(27);
551  AddGPR(28);
552  AddGPR(29);
553  AddGPR(30);
554  AddGPR(31);
555#undef AddReg
556#undef AddGPR
557
558  return true;
559}
560
561#endif
562
563bool MinidumpGenerator::WriteStackX86(breakpad_thread_state_data_t state,
564                                   MDMemoryDescriptor *stack_location) {
565  i386_thread_state_t *machine_state =
566      reinterpret_cast<i386_thread_state_t *>(state);
567
568  mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp);
569  return WriteStackFromStartAddress(start_addr, stack_location);
570}
571
572bool MinidumpGenerator::WriteStackX86_64(breakpad_thread_state_data_t state,
573                                         MDMemoryDescriptor *stack_location) {
574  x86_thread_state64_t *machine_state =
575      reinterpret_cast<x86_thread_state64_t *>(state);
576
577  mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, rsp);
578  return WriteStackFromStartAddress(start_addr, stack_location);
579}
580
581u_int64_t
582MinidumpGenerator::CurrentPCForStackX86(breakpad_thread_state_data_t state) {
583  i386_thread_state_t *machine_state =
584      reinterpret_cast<i386_thread_state_t *>(state);
585
586  return REGISTER_FROM_THREADSTATE(machine_state, eip);
587}
588
589u_int64_t
590MinidumpGenerator::CurrentPCForStackX86_64(breakpad_thread_state_data_t state) {
591  x86_thread_state64_t *machine_state =
592      reinterpret_cast<x86_thread_state64_t *>(state);
593
594  return REGISTER_FROM_THREADSTATE(machine_state, rip);
595}
596
597bool MinidumpGenerator::WriteContextX86(breakpad_thread_state_data_t state,
598                                        MDLocationDescriptor *register_location)
599{
600  TypedMDRVA<MDRawContextX86> context(&writer_);
601  i386_thread_state_t *machine_state =
602      reinterpret_cast<i386_thread_state_t *>(state);
603
604  if (!context.Allocate())
605    return false;
606
607  *register_location = context.location();
608  MDRawContextX86 *context_ptr = context.get();
609
610#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
611
612  context_ptr->context_flags = MD_CONTEXT_X86;
613  AddReg(eax);
614  AddReg(ebx);
615  AddReg(ecx);
616  AddReg(edx);
617  AddReg(esi);
618  AddReg(edi);
619  AddReg(ebp);
620  AddReg(esp);
621
622  AddReg(cs);
623  AddReg(ds);
624  AddReg(ss);
625  AddReg(es);
626  AddReg(fs);
627  AddReg(gs);
628  AddReg(eflags);
629
630  AddReg(eip);
631#undef AddReg
632
633  return true;
634}
635
636bool MinidumpGenerator::WriteContextX86_64(
637    breakpad_thread_state_data_t state,
638    MDLocationDescriptor *register_location) {
639  TypedMDRVA<MDRawContextAMD64> context(&writer_);
640  x86_thread_state64_t *machine_state =
641      reinterpret_cast<x86_thread_state64_t *>(state);
642
643  if (!context.Allocate())
644    return false;
645
646  *register_location = context.location();
647  MDRawContextAMD64 *context_ptr = context.get();
648
649#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
650
651  context_ptr->context_flags = MD_CONTEXT_AMD64;
652  AddReg(rax);
653  AddReg(rbx);
654  AddReg(rcx);
655  AddReg(rdx);
656  AddReg(rdi);
657  AddReg(rsi);
658  AddReg(rbp);
659  AddReg(rsp);
660  AddReg(r8);
661  AddReg(r9);
662  AddReg(r10);
663  AddReg(r11);
664  AddReg(r12);
665  AddReg(r13);
666  AddReg(r14);
667  AddReg(r15);
668  AddReg(rip);
669  // according to AMD's software developer guide, bits above 18 are
670  // not used in the flags register.  Since the minidump format
671  // specifies 32 bits for the flags register, we can truncate safely
672  // with no loss.
673  context_ptr->eflags = static_cast<u_int32_t>(REGISTER_FROM_THREADSTATE(machine_state, rflags));
674  AddReg(cs);
675  AddReg(fs);
676  AddReg(gs);
677#undef AddReg
678
679  return true;
680}
681
682bool MinidumpGenerator::GetThreadState(thread_act_t target_thread,
683                                       thread_state_t state,
684                                       mach_msg_type_number_t *count) {
685  thread_state_flavor_t flavor;
686  switch (cpu_type_) {
687#ifdef HAS_PPC_SUPPORT
688    case CPU_TYPE_POWERPC:
689      flavor = PPC_THREAD_STATE;
690      break;
691    case CPU_TYPE_POWERPC64:
692      flavor = PPC_THREAD_STATE64;
693      break;
694#endif
695    case CPU_TYPE_I386:
696      flavor = i386_THREAD_STATE;
697      break;
698    case CPU_TYPE_X86_64:
699      flavor = x86_THREAD_STATE64;
700      break;
701    default:
702      return false;
703  }
704  return thread_get_state(target_thread, flavor,
705                          state, count) == KERN_SUCCESS;
706}
707
708bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
709                                          MDRawThread *thread) {
710  breakpad_thread_state_data_t state;
711  mach_msg_type_number_t state_count
712      = static_cast<mach_msg_type_number_t>(sizeof(state));
713
714  if (GetThreadState(thread_id, state, &state_count)) {
715    if (!WriteStack(state, &thread->stack))
716      return false;
717
718    memory_blocks_.push_back(thread->stack);
719
720    if (!WriteContext(state, &thread->thread_context))
721      return false;
722
723    thread->thread_id = thread_id;
724  } else {
725    return false;
726  }
727
728  return true;
729}
730
731bool MinidumpGenerator::WriteThreadListStream(
732    MDRawDirectory *thread_list_stream) {
733  TypedMDRVA<MDRawThreadList> list(&writer_);
734  thread_act_port_array_t threads_for_task;
735  mach_msg_type_number_t thread_count;
736  int non_generator_thread_count;
737
738  if (task_threads(crashing_task_, &threads_for_task, &thread_count))
739    return false;
740
741  // Don't include the generator thread
742  if (handler_thread_ != MACH_PORT_NULL)
743    non_generator_thread_count = thread_count - 1;
744  else
745    non_generator_thread_count = thread_count;
746  if (!list.AllocateObjectAndArray(non_generator_thread_count,
747                                   sizeof(MDRawThread)))
748    return false;
749
750  thread_list_stream->stream_type = MD_THREAD_LIST_STREAM;
751  thread_list_stream->location = list.location();
752
753  list.get()->number_of_threads = non_generator_thread_count;
754
755  MDRawThread thread;
756  int thread_idx = 0;
757
758  for (unsigned int i = 0; i < thread_count; ++i) {
759    memset(&thread, 0, sizeof(MDRawThread));
760
761    if (threads_for_task[i] != handler_thread_) {
762      if (!WriteThreadStream(threads_for_task[i], &thread))
763        return false;
764
765      list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread));
766    }
767  }
768
769  return true;
770}
771
772bool MinidumpGenerator::WriteMemoryListStream(
773    MDRawDirectory *memory_list_stream) {
774  TypedMDRVA<MDRawMemoryList> list(&writer_);
775
776  // If the dump has an exception, include some memory around the
777  // instruction pointer.
778  const size_t kIPMemorySize = 256;  // bytes
779  bool have_ip_memory = false;
780  MDMemoryDescriptor ip_memory_d;
781  if (exception_thread_ && exception_type_) {
782    breakpad_thread_state_data_t state;
783    mach_msg_type_number_t stateCount
784      = static_cast<mach_msg_type_number_t>(sizeof(state));
785
786    if (thread_get_state(exception_thread_,
787                         BREAKPAD_MACHINE_THREAD_STATE,
788                         state,
789                         &stateCount) == KERN_SUCCESS) {
790      u_int64_t ip = CurrentPCForStack(state);
791      // Bound it to the upper and lower bounds of the region
792      // it's contained within. If it's not in a known memory region,
793      // don't bother trying to write it.
794      mach_vm_address_t addr = ip;
795      mach_vm_size_t size;
796      natural_t nesting_level = 0;
797      vm_region_submap_info_64 info;
798      mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
799
800      kern_return_t ret =
801        mach_vm_region_recurse(crashing_task_,
802                               &addr,
803                               &size,
804                               &nesting_level,
805                               (vm_region_recurse_info_t)&info,
806                               &info_count);
807      if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) {
808        // Try to get 128 bytes before and after the IP, but
809        // settle for whatever's available.
810        ip_memory_d.start_of_memory_range =
811          std::max(uintptr_t(addr),
812                   uintptr_t(ip - (kIPMemorySize / 2)));
813        uintptr_t end_of_range =
814          std::min(uintptr_t(ip + (kIPMemorySize / 2)),
815                   uintptr_t(addr + size));
816        ip_memory_d.memory.data_size =
817          end_of_range - ip_memory_d.start_of_memory_range;
818        have_ip_memory = true;
819        // This needs to get appended to the list even though
820        // the memory bytes aren't filled in yet so the entire
821        // list can be written first. The memory bytes will get filled
822        // in after the memory list is written.
823        memory_blocks_.push_back(ip_memory_d);
824      }
825    }
826  }
827
828  // Now fill in the memory list and write it.
829  unsigned memory_count = memory_blocks_.size();
830  if (!list.AllocateObjectAndArray(memory_count,
831                                   sizeof(MDMemoryDescriptor)))
832    return false;
833
834  memory_list_stream->stream_type = MD_MEMORY_LIST_STREAM;
835  memory_list_stream->location = list.location();
836
837  list.get()->number_of_memory_ranges = memory_count;
838
839  unsigned int i;
840  for (i = 0; i < memory_count; ++i) {
841    list.CopyIndexAfterObject(i, &memory_blocks_[i],
842                              sizeof(MDMemoryDescriptor));
843  }
844
845  if (have_ip_memory) {
846    // Now read the memory around the instruction pointer.
847    UntypedMDRVA ip_memory(&writer_);
848    if (!ip_memory.Allocate(ip_memory_d.memory.data_size))
849      return false;
850
851    if (dynamic_images_) {
852      // Out-of-process.
853      vector<uint8_t> memory;
854      if (ReadTaskMemory(crashing_task_,
855                         ip_memory_d.start_of_memory_range,
856                         ip_memory_d.memory.data_size,
857                         memory) != KERN_SUCCESS) {
858        return false;
859      }
860
861      ip_memory.Copy(&memory[0], ip_memory_d.memory.data_size);
862    } else {
863      // In-process, just copy from local memory.
864      ip_memory.Copy(
865        reinterpret_cast<const void *>(ip_memory_d.start_of_memory_range),
866        ip_memory_d.memory.data_size);
867    }
868
869    ip_memory_d.memory = ip_memory.location();
870    // Write this again now that the data location is filled in.
871    list.CopyIndexAfterObject(i - 1, &ip_memory_d,
872                              sizeof(MDMemoryDescriptor));
873  }
874
875  return true;
876}
877
878bool
879MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) {
880  TypedMDRVA<MDRawExceptionStream> exception(&writer_);
881
882  if (!exception.Allocate())
883    return false;
884
885  exception_stream->stream_type = MD_EXCEPTION_STREAM;
886  exception_stream->location = exception.location();
887  MDRawExceptionStream *exception_ptr = exception.get();
888  exception_ptr->thread_id = exception_thread_;
889
890  // This naming is confusing, but it is the proper translation from
891  // mach naming to minidump naming.
892  exception_ptr->exception_record.exception_code = exception_type_;
893  exception_ptr->exception_record.exception_flags = exception_code_;
894
895  breakpad_thread_state_data_t state;
896  mach_msg_type_number_t state_count
897      = static_cast<mach_msg_type_number_t>(sizeof(state));
898
899  if (!GetThreadState(exception_thread_, state, &state_count))
900    return false;
901
902  if (!WriteContext(state, &exception_ptr->thread_context))
903    return false;
904
905  if (exception_type_ == EXC_BAD_ACCESS)
906    exception_ptr->exception_record.exception_address = exception_subcode_;
907  else
908    exception_ptr->exception_record.exception_address = CurrentPCForStack(state);
909
910  return true;
911}
912
913bool MinidumpGenerator::WriteSystemInfoStream(
914    MDRawDirectory *system_info_stream) {
915  TypedMDRVA<MDRawSystemInfo> info(&writer_);
916
917  if (!info.Allocate())
918    return false;
919
920  system_info_stream->stream_type = MD_SYSTEM_INFO_STREAM;
921  system_info_stream->location = info.location();
922
923  // CPU Information
924  uint32_t number_of_processors;
925  size_t len = sizeof(number_of_processors);
926  sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0);
927  MDRawSystemInfo *info_ptr = info.get();
928
929  switch (cpu_type_) {
930    case CPU_TYPE_POWERPC:
931    case CPU_TYPE_POWERPC64:
932      info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
933      break;
934    case CPU_TYPE_I386:
935    case CPU_TYPE_X86_64:
936      if (cpu_type_ == CPU_TYPE_I386)
937        info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
938      else
939        info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_AMD64;
940#ifdef __i386__
941      // ebx is used for PIC code, so we need
942      // to preserve it.
943#define cpuid(op,eax,ebx,ecx,edx)      \
944  asm ("pushl %%ebx   \n\t"            \
945       "cpuid         \n\t"            \
946       "movl %%ebx,%1 \n\t"            \
947       "popl %%ebx"                    \
948       : "=a" (eax),                   \
949         "=g" (ebx),                   \
950         "=c" (ecx),                   \
951         "=d" (edx)                    \
952       : "0" (op))
953#elif defined(__x86_64__)
954
955#define cpuid(op,eax,ebx,ecx,edx)      \
956  asm ("cpuid         \n\t"            \
957       : "=a" (eax),                   \
958         "=b" (ebx),                   \
959         "=c" (ecx),                   \
960         "=d" (edx)                    \
961       : "0" (op))
962#endif
963
964#if defined(__i386__) || defined(__x86_64__)
965      int unused, unused2;
966      // get vendor id
967      cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0],
968            info_ptr->cpu.x86_cpu_info.vendor_id[2],
969            info_ptr->cpu.x86_cpu_info.vendor_id[1]);
970      // get version and feature info
971      cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2,
972            info_ptr->cpu.x86_cpu_info.feature_information);
973
974      // family
975      info_ptr->processor_level =
976        (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8;
977      // 0xMMSS (Model, Stepping)
978      info_ptr->processor_revision =
979        (info_ptr->cpu.x86_cpu_info.version_information & 0xF) |
980        ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4);
981
982      // decode extended model info
983      if (info_ptr->processor_level == 0xF ||
984          info_ptr->processor_level == 0x6) {
985        info_ptr->processor_revision |=
986          ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4);
987      }
988
989      // decode extended family info
990      if (info_ptr->processor_level == 0xF) {
991        info_ptr->processor_level +=
992          ((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20);
993      }
994
995#endif  // __i386__ || __x86_64_
996      break;
997    default:
998      info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
999      break;
1000  }
1001
1002  info_ptr->number_of_processors = number_of_processors;
1003  info_ptr->platform_id = MD_OS_MAC_OS_X;
1004
1005  MDLocationDescriptor build_string_loc;
1006
1007  if (!writer_.WriteString(build_string_, 0,
1008                           &build_string_loc))
1009    return false;
1010
1011  info_ptr->csd_version_rva = build_string_loc.rva;
1012  info_ptr->major_version = os_major_version_;
1013  info_ptr->minor_version = os_minor_version_;
1014  info_ptr->build_number = os_build_number_;
1015
1016  return true;
1017}
1018
1019bool MinidumpGenerator::WriteModuleStream(unsigned int index,
1020                                          MDRawModule *module) {
1021  if (dynamic_images_) {
1022    // we're in a different process than the crashed process
1023    DynamicImage *image = dynamic_images_->GetImage(index);
1024
1025    if (!image)
1026      return false;
1027
1028    memset(module, 0, sizeof(MDRawModule));
1029
1030    MDLocationDescriptor string_location;
1031
1032    string name = image->GetFilePath();
1033    if (!writer_.WriteString(name.c_str(), 0, &string_location))
1034      return false;
1035
1036    module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
1037    module->size_of_image = static_cast<u_int32_t>(image->GetVMSize());
1038    module->module_name_rva = string_location.rva;
1039
1040    // We'll skip the executable module, because they don't have
1041    // LC_ID_DYLIB load commands, and the crash processing server gets
1042    // version information from the Plist file, anyway.
1043    if (index != (uint32_t)FindExecutableModule()) {
1044      module->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE;
1045      module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION;
1046      // Convert MAC dylib version format, which is a 32 bit number, to the
1047      // format used by minidump.  The mac format is <16 bits>.<8 bits>.<8 bits>
1048      // so it fits nicely into the windows version with some massaging
1049      // The mapping is:
1050      //    1) upper 16 bits of MAC version go to lower 16 bits of product HI
1051      //    2) Next most significant 8 bits go to upper 16 bits of product LO
1052      //    3) Least significant 8 bits go to lower 16 bits of product LO
1053      uint32_t modVersion = image->GetVersion();
1054      module->version_info.file_version_hi = 0;
1055      module->version_info.file_version_hi = modVersion >> 16;
1056      module->version_info.file_version_lo |= (modVersion & 0xff00)  << 8;
1057      module->version_info.file_version_lo |= (modVersion & 0xff);
1058    }
1059
1060    if (!WriteCVRecord(module, image->GetCPUType(), name.c_str())) {
1061      return false;
1062    }
1063  } else {
1064    // Getting module info in the crashed process
1065    const breakpad_mach_header *header;
1066    header = (breakpad_mach_header*)_dyld_get_image_header(index);
1067    if (!header)
1068      return false;
1069
1070#ifdef __LP64__
1071    assert(header->magic == MH_MAGIC_64);
1072
1073    if(header->magic != MH_MAGIC_64)
1074      return false;
1075#else
1076    assert(header->magic == MH_MAGIC);
1077
1078    if(header->magic != MH_MAGIC)
1079      return false;
1080#endif
1081
1082    int cpu_type = header->cputype;
1083    unsigned long slide = _dyld_get_image_vmaddr_slide(index);
1084    const char* name = _dyld_get_image_name(index);
1085    const struct load_command *cmd =
1086        reinterpret_cast<const struct load_command *>(header + 1);
1087
1088    memset(module, 0, sizeof(MDRawModule));
1089
1090    for (unsigned int i = 0; cmd && (i < header->ncmds); i++) {
1091      if (cmd->cmd == LC_SEGMENT_ARCH) {
1092
1093        const breakpad_mach_segment_command *seg =
1094            reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
1095
1096        if (!strcmp(seg->segname, "__TEXT")) {
1097          MDLocationDescriptor string_location;
1098
1099          if (!writer_.WriteString(name, 0, &string_location))
1100            return false;
1101
1102          module->base_of_image = seg->vmaddr + slide;
1103          module->size_of_image = static_cast<u_int32_t>(seg->vmsize);
1104          module->module_name_rva = string_location.rva;
1105
1106          if (!WriteCVRecord(module, cpu_type, name))
1107            return false;
1108
1109          return true;
1110        }
1111      }
1112
1113      cmd = reinterpret_cast<struct load_command*>((char *)cmd + cmd->cmdsize);
1114    }
1115  }
1116
1117  return true;
1118}
1119
1120int MinidumpGenerator::FindExecutableModule() {
1121  if (dynamic_images_) {
1122    int index = dynamic_images_->GetExecutableImageIndex();
1123
1124    if (index >= 0) {
1125      return index;
1126    }
1127  } else {
1128    int image_count = _dyld_image_count();
1129    const struct mach_header *header;
1130
1131    for (int index = 0; index < image_count; ++index) {
1132      header = _dyld_get_image_header(index);
1133
1134      if (header->filetype == MH_EXECUTE)
1135        return index;
1136    }
1137  }
1138
1139  // failed - just use the first image
1140  return 0;
1141}
1142
1143bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type,
1144                                      const char *module_path) {
1145  TypedMDRVA<MDCVInfoPDB70> cv(&writer_);
1146
1147  // Only return the last path component of the full module path
1148  const char *module_name = strrchr(module_path, '/');
1149
1150  // Increment past the slash
1151  if (module_name)
1152    ++module_name;
1153  else
1154    module_name = "<Unknown>";
1155
1156  size_t module_name_length = strlen(module_name);
1157
1158  if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t)))
1159    return false;
1160
1161  if (!cv.CopyIndexAfterObject(0, module_name, module_name_length))
1162    return false;
1163
1164  module->cv_record = cv.location();
1165  MDCVInfoPDB70 *cv_ptr = cv.get();
1166  cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE;
1167  cv_ptr->age = 0;
1168
1169  // Get the module identifier
1170  FileID file_id(module_path);
1171  unsigned char identifier[16];
1172
1173  if (file_id.MachoIdentifier(cpu_type, identifier)) {
1174    cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 |
1175      (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 |
1176      (uint32_t)identifier[3];
1177    cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5];
1178    cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7];
1179    cv_ptr->signature.data4[0] = identifier[8];
1180    cv_ptr->signature.data4[1] = identifier[9];
1181    cv_ptr->signature.data4[2] = identifier[10];
1182    cv_ptr->signature.data4[3] = identifier[11];
1183    cv_ptr->signature.data4[4] = identifier[12];
1184    cv_ptr->signature.data4[5] = identifier[13];
1185    cv_ptr->signature.data4[6] = identifier[14];
1186    cv_ptr->signature.data4[7] = identifier[15];
1187  }
1188
1189  return true;
1190}
1191
1192bool MinidumpGenerator::WriteModuleListStream(
1193    MDRawDirectory *module_list_stream) {
1194  TypedMDRVA<MDRawModuleList> list(&writer_);
1195
1196  int image_count = dynamic_images_ ?
1197    dynamic_images_->GetImageCount() : _dyld_image_count();
1198
1199  if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE))
1200    return false;
1201
1202  module_list_stream->stream_type = MD_MODULE_LIST_STREAM;
1203  module_list_stream->location = list.location();
1204  list.get()->number_of_modules = image_count;
1205
1206  // Write out the executable module as the first one
1207  MDRawModule module;
1208  int executableIndex = FindExecutableModule();
1209
1210  if (!WriteModuleStream(executableIndex, &module)) {
1211    return false;
1212  }
1213
1214  list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE);
1215  int destinationIndex = 1;  // Write all other modules after this one
1216
1217  for (int i = 0; i < image_count; ++i) {
1218    if (i != executableIndex) {
1219      if (!WriteModuleStream(i, &module)) {
1220        return false;
1221      }
1222
1223      list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE);
1224    }
1225  }
1226
1227  return true;
1228}
1229
1230bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
1231  TypedMDRVA<MDRawMiscInfo> info(&writer_);
1232
1233  if (!info.Allocate())
1234    return false;
1235
1236  misc_info_stream->stream_type = MD_MISC_INFO_STREAM;
1237  misc_info_stream->location = info.location();
1238
1239  MDRawMiscInfo *info_ptr = info.get();
1240  info_ptr->size_of_info = static_cast<u_int32_t>(sizeof(MDRawMiscInfo));
1241  info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID |
1242    MD_MISCINFO_FLAGS1_PROCESS_TIMES |
1243    MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO;
1244
1245  // Process ID
1246  info_ptr->process_id = getpid();
1247
1248  // Times
1249  struct rusage usage;
1250  if (getrusage(RUSAGE_SELF, &usage) != -1) {
1251    // Omit the fractional time since the MDRawMiscInfo only wants seconds
1252    info_ptr->process_user_time =
1253        static_cast<u_int32_t>(usage.ru_utime.tv_sec);
1254    info_ptr->process_kernel_time =
1255        static_cast<u_int32_t>(usage.ru_stime.tv_sec);
1256  }
1257  int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID,
1258                 static_cast<int>(info_ptr->process_id) };
1259  u_int mibsize = static_cast<u_int>(sizeof(mib) / sizeof(mib[0]));
1260  size_t size;
1261  if (!sysctl(mib, mibsize, NULL, &size, NULL, 0)) {
1262    mach_vm_address_t addr;
1263    if (mach_vm_allocate(mach_task_self(),
1264                         &addr,
1265                         size,
1266                         true) == KERN_SUCCESS) {
1267      struct kinfo_proc *proc = (struct kinfo_proc *)addr;
1268      if (!sysctl(mib, mibsize, proc, &size, NULL, 0))
1269        info_ptr->process_create_time =
1270            static_cast<u_int32_t>(proc->kp_proc.p_starttime.tv_sec);
1271      mach_vm_deallocate(mach_task_self(), addr, size);
1272    }
1273  }
1274
1275  // Speed
1276  uint64_t speed;
1277  const uint64_t kOneMillion = 1000 * 1000;
1278  size = sizeof(speed);
1279  sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0);
1280  info_ptr->processor_max_mhz = static_cast<u_int32_t>(speed / kOneMillion);
1281  info_ptr->processor_mhz_limit = static_cast<u_int32_t>(speed / kOneMillion);
1282  size = sizeof(speed);
1283  sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0);
1284  info_ptr->processor_current_mhz = static_cast<u_int32_t>(speed / kOneMillion);
1285
1286  return true;
1287}
1288
1289bool MinidumpGenerator::WriteBreakpadInfoStream(
1290    MDRawDirectory *breakpad_info_stream) {
1291  TypedMDRVA<MDRawBreakpadInfo> info(&writer_);
1292
1293  if (!info.Allocate())
1294    return false;
1295
1296  breakpad_info_stream->stream_type = MD_BREAKPAD_INFO_STREAM;
1297  breakpad_info_stream->location = info.location();
1298  MDRawBreakpadInfo *info_ptr = info.get();
1299
1300  if (exception_thread_ && exception_type_) {
1301    info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
1302                         MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
1303    info_ptr->dump_thread_id = handler_thread_;
1304    info_ptr->requesting_thread_id = exception_thread_;
1305  } else {
1306    info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID;
1307    info_ptr->dump_thread_id = handler_thread_;
1308    info_ptr->requesting_thread_id = 0;
1309  }
1310
1311  return true;
1312}
1313
1314}  // namespace google_breakpad
1315