Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(551)

Side by Side Diff: breakpad/linux/minidump_writer.cc

Issue 414049: Linux: Use upstream google-breakpad instead of our fork.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « breakpad/linux/minidump_writer.h ('k') | breakpad/linux/minidump_writer_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2009, 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 // This code writes out minidump files:
31 // http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx
32 //
33 // Minidumps are a Microsoft format which Breakpad uses for recording crash
34 // dumps. This code has to run in a compromised environment (the address space
35 // may have received SIGSEGV), thus the following rules apply:
36 // * You may not enter the dynamic linker. This means that we cannot call
37 // any symbols in a shared library (inc libc). Because of this we replace
38 // libc functions in linux_libc_support.h.
39 // * You may not call syscalls via the libc wrappers. This rule is a subset
40 // of the first rule but it bears repeating. We have direct wrappers
41 // around the system calls in linux_syscall_support.h.
42 // * You may not malloc. There's an alternative allocator in memory.h and
43 // a canonical instance in the LinuxDumper object. We use the placement
44 // new form to allocate objects and we don't delete them.
45
46 #include "breakpad/linux/minidump_writer.h"
47 #include "client/minidump_file_writer-inl.h"
48
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <stdio.h>
52 #include <unistd.h>
53 #include <sys/ucontext.h>
54 #include <sys/user.h>
55 #include <sys/utsname.h>
56
57 #include "client/minidump_file_writer.h"
58 #include "google_breakpad/common/minidump_format.h"
59 #include "google_breakpad/common/minidump_cpu_amd64.h"
60 #include "google_breakpad/common/minidump_cpu_x86.h"
61
62 #include "breakpad/linux/exception_handler.h"
63 #include "breakpad/linux/line_reader.h"
64 #include "breakpad/linux/linux_dumper.h"
65 #include "breakpad/linux/linux_libc_support.h"
66 #include "breakpad/linux/linux_syscall_support.h"
67 #include "breakpad/linux/minidump_format_linux.h"
68
69 // Minidump defines register structures which are different from the raw
70 // structures which we get from the kernel. These are platform specific
71 // functions to juggle the ucontext and user structures into minidump format.
72 #if defined(__i386)
73 typedef MDRawContextX86 RawContextCPU;
74
75 // Write a uint16_t to memory
76 // out: memory location to write to
77 // v: value to write.
78 static void U16(void* out, uint16_t v) {
79 memcpy(out, &v, sizeof(v));
80 }
81
82 // Write a uint32_t to memory
83 // out: memory location to write to
84 // v: value to write.
85 static void U32(void* out, uint32_t v) {
86 memcpy(out, &v, sizeof(v));
87 }
88
89 // Juggle an x86 user_(fp|fpx|)regs_struct into minidump format
90 // out: the minidump structure
91 // info: the collection of register structures.
92 static void CPUFillFromThreadInfo(MDRawContextX86 *out,
93 const google_breakpad::ThreadInfo &info) {
94 out->context_flags = MD_CONTEXT_X86_ALL;
95
96 out->dr0 = info.dregs[0];
97 out->dr1 = info.dregs[1];
98 out->dr2 = info.dregs[2];
99 out->dr3 = info.dregs[3];
100 // 4 and 5 deliberatly omitted because they aren't included in the minidump
101 // format.
102 out->dr6 = info.dregs[6];
103 out->dr7 = info.dregs[7];
104
105 out->gs = info.regs.xgs;
106 out->fs = info.regs.xfs;
107 out->es = info.regs.xes;
108 out->ds = info.regs.xds;
109
110 out->edi = info.regs.edi;
111 out->esi = info.regs.esi;
112 out->ebx = info.regs.ebx;
113 out->edx = info.regs.edx;
114 out->ecx = info.regs.ecx;
115 out->eax = info.regs.eax;
116
117 out->ebp = info.regs.ebp;
118 out->eip = info.regs.eip;
119 out->cs = info.regs.xcs;
120 out->eflags = info.regs.eflags;
121 out->esp = info.regs.esp;
122 out->ss = info.regs.xss;
123
124 out->float_save.control_word = info.fpregs.cwd;
125 out->float_save.status_word = info.fpregs.swd;
126 out->float_save.tag_word = info.fpregs.twd;
127 out->float_save.error_offset = info.fpregs.fip;
128 out->float_save.error_selector = info.fpregs.fcs;
129 out->float_save.data_offset = info.fpregs.foo;
130 out->float_save.data_selector = info.fpregs.fos;
131
132 // 8 registers * 10 bytes per register.
133 memcpy(out->float_save.register_area, info.fpregs.st_space, 10 * 8);
134
135 // This matches the Intel fpsave format.
136 U16(out->extended_registers + 0, info.fpregs.cwd);
137 U16(out->extended_registers + 2, info.fpregs.swd);
138 U16(out->extended_registers + 4, info.fpregs.twd);
139 U16(out->extended_registers + 6, info.fpxregs.fop);
140 U32(out->extended_registers + 8, info.fpxregs.fip);
141 U16(out->extended_registers + 12, info.fpxregs.fcs);
142 U32(out->extended_registers + 16, info.fpregs.foo);
143 U16(out->extended_registers + 20, info.fpregs.fos);
144 U32(out->extended_registers + 24, info.fpxregs.mxcsr);
145
146 memcpy(out->extended_registers + 32, &info.fpxregs.st_space, 128);
147 memcpy(out->extended_registers + 160, &info.fpxregs.xmm_space, 128);
148 }
149
150 // Juggle an x86 ucontext into minidump format
151 // out: the minidump structure
152 // info: the collection of register structures.
153 static void CPUFillFromUContext(MDRawContextX86 *out, const ucontext *uc,
154 const struct _libc_fpstate* fp) {
155 const greg_t* regs = uc->uc_mcontext.gregs;
156
157 out->context_flags = MD_CONTEXT_X86_FULL |
158 MD_CONTEXT_X86_FLOATING_POINT;
159
160 out->gs = regs[REG_GS];
161 out->fs = regs[REG_FS];
162 out->es = regs[REG_ES];
163 out->ds = regs[REG_DS];
164
165 out->edi = regs[REG_EDI];
166 out->esi = regs[REG_ESI];
167 out->ebx = regs[REG_EBX];
168 out->edx = regs[REG_EDX];
169 out->ecx = regs[REG_ECX];
170 out->eax = regs[REG_EAX];
171
172 out->ebp = regs[REG_EBP];
173 out->eip = regs[REG_EIP];
174 out->cs = regs[REG_CS];
175 out->eflags = regs[REG_EFL];
176 out->esp = regs[REG_UESP];
177 out->ss = regs[REG_SS];
178
179 out->float_save.control_word = fp->cw;
180 out->float_save.status_word = fp->sw;
181 out->float_save.tag_word = fp->tag;
182 out->float_save.error_offset = fp->ipoff;
183 out->float_save.error_selector = fp->cssel;
184 out->float_save.data_offset = fp->dataoff;
185 out->float_save.data_selector = fp->datasel;
186
187 // 8 registers * 10 bytes per register.
188 memcpy(out->float_save.register_area, fp->_st, 10 * 8);
189 }
190
191 #elif defined(__x86_64)
192 typedef MDRawContextAMD64 RawContextCPU;
193
194 static void CPUFillFromThreadInfo(MDRawContextAMD64 *out,
195 const google_breakpad::ThreadInfo &info) {
196 out->context_flags = MD_CONTEXT_AMD64_FULL |
197 MD_CONTEXT_AMD64_SEGMENTS;
198
199 out->cs = info.regs.cs;
200
201 out->ds = info.regs.ds;
202 out->es = info.regs.es;
203 out->fs = info.regs.fs;
204 out->gs = info.regs.gs;
205
206 out->ss = info.regs.ss;
207 out->eflags = info.regs.eflags;
208
209 out->dr0 = info.dregs[0];
210 out->dr1 = info.dregs[1];
211 out->dr2 = info.dregs[2];
212 out->dr3 = info.dregs[3];
213 // 4 and 5 deliberatly omitted because they aren't included in the minidump
214 // format.
215 out->dr6 = info.dregs[6];
216 out->dr7 = info.dregs[7];
217
218 out->rax = info.regs.rax;
219 out->rcx = info.regs.rcx;
220 out->rdx = info.regs.rdx;
221 out->rbx = info.regs.rbx;
222
223 out->rsp = info.regs.rsp;
224
225 out->rbp = info.regs.rbp;
226 out->rsi = info.regs.rsi;
227 out->rdi = info.regs.rdi;
228 out->r8 = info.regs.r8;
229 out->r9 = info.regs.r9;
230 out->r10 = info.regs.r10;
231 out->r11 = info.regs.r11;
232 out->r12 = info.regs.r12;
233 out->r13 = info.regs.r13;
234 out->r14 = info.regs.r14;
235 out->r15 = info.regs.r15;
236
237 out->rip = info.regs.rip;
238
239 out->flt_save.control_word = info.fpregs.cwd;
240 out->flt_save.status_word = info.fpregs.swd;
241 out->flt_save.tag_word = info.fpregs.ftw;
242 out->flt_save.error_opcode = info.fpregs.fop;
243 out->flt_save.error_offset = info.fpregs.rip;
244 out->flt_save.error_selector = 0; // We don't have this.
245 out->flt_save.data_offset = info.fpregs.rdp;
246 out->flt_save.data_selector = 0; // We don't have this.
247 out->flt_save.mx_csr = info.fpregs.mxcsr;
248 out->flt_save.mx_csr_mask = info.fpregs.mxcr_mask;
249 memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16);
250 memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16);
251 }
252
253 static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc,
254 const struct _libc_fpstate* fpregs) {
255 const greg_t* regs = uc->uc_mcontext.gregs;
256
257 out->context_flags = MD_CONTEXT_AMD64_FULL;
258
259 out->cs = regs[REG_CSGSFS] & 0xffff;
260
261 out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff;
262 out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff;
263
264 out->eflags = regs[REG_EFL];
265
266 out->rax = regs[REG_RAX];
267 out->rcx = regs[REG_RCX];
268 out->rdx = regs[REG_RDX];
269 out->rbx = regs[REG_RBX];
270
271 out->rsp = regs[REG_RSP];
272 out->rbp = regs[REG_RBP];
273 out->rsi = regs[REG_RSI];
274 out->rdi = regs[REG_RDI];
275 out->r8 = regs[REG_R8];
276 out->r9 = regs[REG_R9];
277 out->r10 = regs[REG_R10];
278 out->r11 = regs[REG_R11];
279 out->r12 = regs[REG_R12];
280 out->r13 = regs[REG_R13];
281 out->r14 = regs[REG_R14];
282 out->r15 = regs[REG_R15];
283
284 out->rip = regs[REG_RIP];
285
286 out->flt_save.control_word = fpregs->cwd;
287 out->flt_save.status_word = fpregs->swd;
288 out->flt_save.tag_word = fpregs->ftw;
289 out->flt_save.error_opcode = fpregs->fop;
290 out->flt_save.error_offset = fpregs->rip;
291 out->flt_save.data_offset = fpregs->rdp;
292 out->flt_save.error_selector = 0; // We don't have this.
293 out->flt_save.data_selector = 0; // We don't have this.
294 out->flt_save.mx_csr = fpregs->mxcsr;
295 out->flt_save.mx_csr_mask = fpregs->mxcr_mask;
296 memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16);
297 memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
298 }
299
300 #else
301 #error "This code has not been ported to your platform yet."
302 #endif
303
304 namespace google_breakpad {
305
306 class MinidumpWriter {
307 public:
308 MinidumpWriter(const char* filename,
309 pid_t crashing_pid,
310 const ExceptionHandler::CrashContext* context)
311 : filename_(filename),
312 siginfo_(&context->siginfo),
313 ucontext_(&context->context),
314 float_state_(&context->float_state),
315 crashing_tid_(context->tid),
316 dumper_(crashing_pid) {
317 }
318
319 bool Init() {
320 return dumper_.Init() && minidump_writer_.Open(filename_) &&
321 dumper_.ThreadsSuspend();
322 }
323
324 ~MinidumpWriter() {
325 minidump_writer_.Close();
326 dumper_.ThreadsResume();
327 }
328
329 bool Dump() {
330 // A minidump file contains a number of tagged streams. This is the number
331 // of stream which we write.
332 static const unsigned kNumWriters = 11;
333
334 TypedMDRVA<MDRawHeader> header(&minidump_writer_);
335 TypedMDRVA<MDRawDirectory> dir(&minidump_writer_);
336 if (!header.Allocate())
337 return false;
338 if (!dir.AllocateArray(kNumWriters))
339 return false;
340 memset(header.get(), 0, sizeof(MDRawHeader));
341
342 header.get()->signature = MD_HEADER_SIGNATURE;
343 header.get()->version = MD_HEADER_VERSION;
344 header.get()->time_date_stamp = time(NULL);
345 header.get()->stream_count = kNumWriters;
346 header.get()->stream_directory_rva = dir.position();
347
348 unsigned dir_index = 0;
349 MDRawDirectory dirent;
350
351 if (!WriteThreadListStream(&dirent))
352 return false;
353 dir.CopyIndex(dir_index++, &dirent);
354
355 if (!WriteMappings(&dirent))
356 return false;
357 dir.CopyIndex(dir_index++, &dirent);
358
359 if (!WriteExceptionStream(&dirent))
360 return false;
361 dir.CopyIndex(dir_index++, &dirent);
362
363 if (!WriteSystemInfoStream(&dirent))
364 return false;
365 dir.CopyIndex(dir_index++, &dirent);
366
367 dirent.stream_type = MD_LINUX_CPU_INFO;
368 if (!WriteFile(&dirent.location, "/proc/cpuinfo"))
369 NullifyDirectoryEntry(&dirent);
370 dir.CopyIndex(dir_index++, &dirent);
371
372 dirent.stream_type = MD_LINUX_PROC_STATUS;
373 if (!WriteProcFile(&dirent.location, crashing_tid_, "status"))
374 NullifyDirectoryEntry(&dirent);
375 dir.CopyIndex(dir_index++, &dirent);
376
377 dirent.stream_type = MD_LINUX_LSB_RELEASE;
378 if (!WriteFile(&dirent.location, "/etc/lsb-release"))
379 NullifyDirectoryEntry(&dirent);
380 dir.CopyIndex(dir_index++, &dirent);
381
382 dirent.stream_type = MD_LINUX_CMD_LINE;
383 if (!WriteProcFile(&dirent.location, crashing_tid_, "cmdline"))
384 NullifyDirectoryEntry(&dirent);
385 dir.CopyIndex(dir_index++, &dirent);
386
387 dirent.stream_type = MD_LINUX_ENVIRON;
388 if (!WriteProcFile(&dirent.location, crashing_tid_, "environ"))
389 NullifyDirectoryEntry(&dirent);
390 dir.CopyIndex(dir_index++, &dirent);
391
392 dirent.stream_type = MD_LINUX_AUXV;
393 if (!WriteProcFile(&dirent.location, crashing_tid_, "auxv"))
394 NullifyDirectoryEntry(&dirent);
395 dir.CopyIndex(dir_index++, &dirent);
396
397 dirent.stream_type = MD_LINUX_AUXV;
398 if (!WriteProcFile(&dirent.location, crashing_tid_, "maps"))
399 NullifyDirectoryEntry(&dirent);
400 dir.CopyIndex(dir_index++, &dirent);
401
402 // If you add more directory entries, don't forget to update kNumWriters,
403 // above.
404
405 dumper_.ThreadsResume();
406 return true;
407 }
408
409 // Write information about the threads.
410 bool WriteThreadListStream(MDRawDirectory* dirent) {
411 const unsigned num_threads = dumper_.threads().size();
412
413 TypedMDRVA<uint32_t> list(&minidump_writer_);
414 if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread)))
415 return false;
416
417 dirent->stream_type = MD_THREAD_LIST_STREAM;
418 dirent->location = list.location();
419
420 *list.get() = num_threads;
421
422 for (unsigned i = 0; i < num_threads; ++i) {
423 MDRawThread thread;
424 my_memset(&thread, 0, sizeof(thread));
425 thread.thread_id = dumper_.threads()[i];
426 // We have a different source of information for the crashing thread. If
427 // we used the actual state of the thread we would find it running in the
428 // signal handler with the alternative stack, which would be deeply
429 // unhelpful.
430 if (thread.thread_id == crashing_tid_) {
431 const void* stack;
432 size_t stack_len;
433 if (!dumper_.GetStackInfo(&stack, &stack_len, GetStackPointer()))
434 return false;
435 UntypedMDRVA memory(&minidump_writer_);
436 if (!memory.Allocate(stack_len))
437 return false;
438 uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len);
439 dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len);
440 memory.Copy(stack_copy, stack_len);
441 thread.stack.start_of_memory_range = (uintptr_t) (stack);
442 thread.stack.memory = memory.location();
443 TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
444 if (!cpu.Allocate())
445 return false;
446 my_memset(cpu.get(), 0, sizeof(RawContextCPU));
447 CPUFillFromUContext(cpu.get(), ucontext_, float_state_);
448 thread.thread_context = cpu.location();
449 crashing_thread_context_ = cpu.location();
450 } else {
451 ThreadInfo info;
452 if (!dumper_.ThreadInfoGet(dumper_.threads()[i], &info))
453 return false;
454 UntypedMDRVA memory(&minidump_writer_);
455 if (!memory.Allocate(info.stack_len))
456 return false;
457 uint8_t* stack_copy =
458 (uint8_t*) dumper_.allocator()->Alloc(info.stack_len);
459 dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack,
460 info.stack_len);
461 memory.Copy(stack_copy, info.stack_len);
462 thread.stack.start_of_memory_range = (uintptr_t)(info.stack);
463 thread.stack.memory = memory.location();
464 TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
465 if (!cpu.Allocate())
466 return false;
467 my_memset(cpu.get(), 0, sizeof(RawContextCPU));
468 CPUFillFromThreadInfo(cpu.get(), info);
469 thread.thread_context = cpu.location();
470 }
471
472 list.CopyIndexAfterObject(i, &thread, sizeof(thread));
473 }
474
475 return true;
476 }
477
478 static bool ShouldIncludeMapping(const MappingInfo& mapping) {
479 if (mapping.name[0] == 0 || // we only want modules with filenames.
480 mapping.offset || // we only want to include one mapping per shared lib.
481 mapping.size < 4096) { // too small to get a signature for.
482 return false;
483 }
484
485 return true;
486 }
487
488 // Write information about the mappings in effect. Because we are using the
489 // minidump format, the information about the mappings is pretty limited.
490 // Because of this, we also include the full, unparsed, /proc/$x/maps file in
491 // another stream in the file.
492 bool WriteMappings(MDRawDirectory* dirent) {
493 const unsigned num_mappings = dumper_.mappings().size();
494 unsigned num_output_mappings = 0;
495
496 for (unsigned i = 0; i < dumper_.mappings().size(); ++i) {
497 const MappingInfo& mapping = *dumper_.mappings()[i];
498 if (ShouldIncludeMapping(mapping))
499 num_output_mappings++;
500 }
501
502 TypedMDRVA<uint32_t> list(&minidump_writer_);
503 if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE))
504 return false;
505
506 dirent->stream_type = MD_MODULE_LIST_STREAM;
507 dirent->location = list.location();
508 *list.get() = num_output_mappings;
509
510 for (unsigned i = 0, j = 0; i < num_mappings; ++i) {
511 const MappingInfo& mapping = *dumper_.mappings()[i];
512 if (!ShouldIncludeMapping(mapping))
513 continue;
514
515 MDRawModule mod;
516 my_memset(&mod, 0, MD_MODULE_SIZE);
517 mod.base_of_image = mapping.start_addr;
518 mod.size_of_image = mapping.size;
519 const size_t filepath_len = my_strlen(mapping.name);
520
521 // Figure out file name from path
522 const char* filename_ptr = mapping.name + filepath_len - 1;
523 while (filename_ptr >= mapping.name) {
524 if (*filename_ptr == '/')
525 break;
526 filename_ptr--;
527 }
528 filename_ptr++;
529 const size_t filename_len = mapping.name + filepath_len - filename_ptr;
530
531 uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX];
532 uint8_t* cv_ptr = cv_buf;
533 UntypedMDRVA cv(&minidump_writer_);
534 if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1))
535 return false;
536
537 const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE;
538 memcpy(cv_ptr, &cv_signature, sizeof(cv_signature));
539 cv_ptr += sizeof(cv_signature);
540
541 {
542 // We XOR the first page of the file to get a signature for it.
543 uint8_t xor_buf[sizeof(MDGUID)];
544 size_t done = 0;
545 uint8_t* signature = cv_ptr;
546 cv_ptr += sizeof(xor_buf);
547
548 my_memset(signature, 0, sizeof(xor_buf));
549 while (done < 4096) {
550 dumper_.CopyFromProcess(xor_buf, crashing_tid_,
551 (void *) (mod.base_of_image + done),
552 sizeof(xor_buf));
553 for (unsigned i = 0; i < sizeof(xor_buf); ++i)
554 signature[i] ^= xor_buf[i];
555 done += sizeof(xor_buf);
556 }
557 my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux.
558 cv_ptr += sizeof(uint32_t);
559 }
560
561 // Write pdb_file_name
562 memcpy(cv_ptr, filename_ptr, filename_len + 1);
563 cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1);
564
565 mod.cv_record = cv.location();
566
567 MDLocationDescriptor ld;
568 if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld))
569 return false;
570 mod.module_name_rva = ld.rva;
571
572 list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
573 }
574
575 return true;
576 }
577
578 bool WriteExceptionStream(MDRawDirectory* dirent) {
579 TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_);
580 if (!exc.Allocate())
581 return false;
582 my_memset(exc.get(), 0, sizeof(MDRawExceptionStream));
583
584 dirent->stream_type = MD_EXCEPTION_STREAM;
585 dirent->location = exc.location();
586
587 exc.get()->thread_id = crashing_tid_;
588 exc.get()->exception_record.exception_code = siginfo_->si_signo;
589 exc.get()->exception_record.exception_address =
590 (uintptr_t) siginfo_->si_addr;
591 exc.get()->thread_context = crashing_thread_context_;
592
593 return true;
594 }
595
596 bool WriteSystemInfoStream(MDRawDirectory* dirent) {
597 TypedMDRVA<MDRawSystemInfo> si(&minidump_writer_);
598 if (!si.Allocate())
599 return false;
600 my_memset(si.get(), 0, sizeof(MDRawSystemInfo));
601
602 dirent->stream_type = MD_SYSTEM_INFO_STREAM;
603 dirent->location = si.location();
604
605 WriteCPUInformation(si.get());
606 WriteOSInformation(si.get());
607
608 return true;
609 }
610
611 private:
612 #if defined(__i386)
613 uintptr_t GetStackPointer() {
614 return ucontext_->uc_mcontext.gregs[REG_ESP];
615 }
616 #elif defined(__x86_64)
617 uintptr_t GetStackPointer() {
618 return ucontext_->uc_mcontext.gregs[REG_RSP];
619 }
620 #else
621 #error "This code has not been ported to your platform yet."
622 #endif
623
624 void NullifyDirectoryEntry(MDRawDirectory* dirent) {
625 dirent->stream_type = 0;
626 dirent->location.data_size = 0;
627 dirent->location.rva = 0;
628 }
629
630 bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
631 char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0};
632 static const char vendor_id_name[] = "vendor_id";
633 static const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1;
634
635 struct CpuInfoEntry {
636 const char* info_name;
637 int value;
638 bool found;
639 } cpu_info_table[] = {
640 { "processor", -1, false },
641 { "model", 0, false },
642 { "stepping", 0, false },
643 { "cpu family", 0, false },
644 };
645
646 // processor_architecture should always be set, do this first
647 sys_info->processor_architecture =
648 #if defined(__i386)
649 MD_CPU_ARCHITECTURE_X86;
650 #elif defined(__x86_64)
651 MD_CPU_ARCHITECTURE_AMD64;
652 #else
653 #error "Unknown CPU arch"
654 #endif
655
656 const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0);
657 if (fd < 0)
658 return false;
659
660 {
661 PageAllocator allocator;
662 LineReader* const line_reader = new(allocator) LineReader(fd);
663 const char* line;
664 unsigned line_len;
665 while (line_reader->GetNextLine(&line, &line_len)) {
666 for (size_t i = 0;
667 i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
668 i++) {
669 CpuInfoEntry* entry = &cpu_info_table[i];
670 if (entry->found)
671 continue;
672 if (!strncmp(line, entry->info_name, strlen(entry->info_name))) {
673 const char* value = strchr(line, ':');
674 if (!value)
675 continue;
676
677 // the above strncmp only matches the prefix, it might be the wrong
678 // line. i.e. we matched "model name" instead of "model".
679 // check and make sure there is only spaces between the prefix and
680 // the colon.
681 const char* space_ptr = line + strlen(entry->info_name);
682 for (; space_ptr < value; space_ptr++) {
683 if (!isspace(*space_ptr)) {
684 break;
685 }
686 }
687 if (space_ptr != value)
688 continue;
689
690 sscanf(++value, " %d", &(entry->value));
691 entry->found = true;
692 }
693 }
694
695 // special case for vendor_id
696 if (!strncmp(line, vendor_id_name, vendor_id_name_length)) {
697 const char* value = strchr(line, ':');
698 if (!value)
699 goto popline;
700
701 // skip ':" and all the spaces that follows
702 do {
703 value++;
704 } while (isspace(*value));
705
706 if (*value) {
707 size_t length = strlen(value);
708 if (length == 0)
709 goto popline;
710 // we don't want the trailing newline
711 if (value[length - 1] == '\n')
712 length--;
713 // ensure we have space for the value
714 if (length < sizeof(vendor_id))
715 strncpy(vendor_id, value, length);
716 }
717 }
718
719 popline:
720 line_reader->PopLine(line_len);
721 }
722 sys_close(fd);
723 }
724
725 // make sure we got everything we wanted
726 for (size_t i = 0;
727 i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
728 i++) {
729 if (!cpu_info_table[i].found) {
730 return false;
731 }
732 }
733 // /proc/cpuinfo contains cpu id, change it into number by adding one.
734 cpu_info_table[0].value++;
735
736 sys_info->number_of_processors = cpu_info_table[0].value;
737 sys_info->processor_level = cpu_info_table[3].value;
738 sys_info->processor_revision = cpu_info_table[1].value << 8 |
739 cpu_info_table[2].value;
740
741 if (vendor_id[0] != '\0') {
742 memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id,
743 sizeof(sys_info->cpu.x86_cpu_info.vendor_id));
744 }
745 return true;
746 }
747
748 bool WriteFile(MDLocationDescriptor* result, const char* filename) {
749 const int fd = sys_open(filename, O_RDONLY, 0);
750 if (fd < 0)
751 return false;
752
753 // We can't stat the files because several of the files that we want to
754 // read are kernel seqfiles, which always have a length of zero. So we have
755 // to read as much as we can into a buffer.
756 static const unsigned kMaxFileSize = 1024;
757 uint8_t* data = (uint8_t*) dumper_.allocator()->Alloc(kMaxFileSize);
758
759 size_t done = 0;
760 while (done < kMaxFileSize) {
761 ssize_t r;
762 do {
763 r = sys_read(fd, data + done, kMaxFileSize - done);
764 } while (r == -1 && errno == EINTR);
765
766 if (r < 1)
767 break;
768 done += r;
769 }
770 sys_close(fd);
771
772 if (!done)
773 return false;
774
775 UntypedMDRVA memory(&minidump_writer_);
776 if (!memory.Allocate(done))
777 return false;
778 memory.Copy(data, done);
779 *result = memory.location();
780 return true;
781 }
782
783 bool WriteOSInformation(MDRawSystemInfo* sys_info) {
784 sys_info->platform_id = MD_OS_LINUX;
785
786 struct utsname uts;
787 if (uname(&uts))
788 return false;
789
790 static const size_t buf_len = 512;
791 char buf[buf_len] = {0};
792 size_t space_left = buf_len - 1;
793 const char* info_table[] = {
794 uts.sysname,
795 uts.release,
796 uts.version,
797 uts.machine,
798 NULL
799 };
800 bool first_item = true;
801 for (const char** cur_info = info_table; *cur_info; cur_info++) {
802 static const char* separator = " ";
803 size_t separator_len = strlen(separator);
804 size_t info_len = strlen(*cur_info);
805 if (info_len == 0)
806 continue;
807
808 if (space_left < info_len + (first_item ? 0 : separator_len))
809 break;
810
811 if (!first_item) {
812 strcat(buf, separator);
813 space_left -= separator_len;
814 }
815
816 first_item = false;
817 strcat(buf, *cur_info);
818 space_left -= info_len;
819 }
820
821 MDLocationDescriptor location;
822 if (!minidump_writer_.WriteString(buf, 0, &location))
823 return false;
824 sys_info->csd_version_rva = location.rva;
825
826 return true;
827 }
828
829 bool WriteProcFile(MDLocationDescriptor* result, pid_t pid,
830 const char* filename) {
831 char buf[80];
832 memcpy(buf, "/proc/", 6);
833 const unsigned pid_len = my_int_len(pid);
834 my_itos(buf + 6, pid, pid_len);
835 buf[6 + pid_len] = '/';
836 memcpy(buf + 6 + pid_len + 1, filename, my_strlen(filename) + 1);
837 return WriteFile(result, buf);
838 }
839
840 const char* const filename_; // output filename
841 const siginfo_t* const siginfo_; // from the signal handler (see sigaction)
842 const struct ucontext* const ucontext_; // also from the signal handler
843 const struct _libc_fpstate* const float_state_; // ditto
844 const pid_t crashing_tid_; // the process which actually crashed
845 LinuxDumper dumper_;
846 MinidumpFileWriter minidump_writer_;
847 MDLocationDescriptor crashing_thread_context_;
848 };
849
850 bool WriteMinidump(const char* filename, pid_t crashing_process,
851 const void* blob, size_t blob_size) {
852 if (blob_size != sizeof(ExceptionHandler::CrashContext))
853 return false;
854 const ExceptionHandler::CrashContext* context =
855 reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
856 MinidumpWriter writer(filename, crashing_process, context);
857 if (!writer.Init())
858 return false;
859 return writer.Dump();
860 }
861
862 } // namespace google_breakpad
OLDNEW
« no previous file with comments | « breakpad/linux/minidump_writer.h ('k') | breakpad/linux/minidump_writer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698