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