1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include <assert.h>
6#include <elf.h>
7#include <link.h>
8#include <unistd.h>
9#include <fcntl.h>
10#include <inttypes.h>
11#include <string.h>
12#include <errno.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <stddef.h>
16#include <limits.h>
17#include <dlfcn.h>
18#include <sys/auxv.h>
19#include <sys/mman.h>
20#include <sys/prctl.h>
21#include <sys/ptrace.h>
22#include <sys/stat.h>
23#include <sys/stat.h>
24#include <sys/syscall.h>
25#include <sys/types.h>
26#include <sys/user.h>
27#include <sys/wait.h>
28
29#include <iostream>
30#include <memory>
31#include <map>
32#include <string>
33#include <algorithm>
34#include <type_traits>
35
36#include "event_man.h"
37#include "easylogging++.h"
38#include "trace_reader.h"
39#include "trace_writer.h"
40#include "md_types.h"
41#include "config.h"
42
43using namespace std;
44
45INITIALIZE_EASYLOGGINGPP
46
47#ifdef __mips__
48#define PAGE_SIZE_SUB_ONE (4*1024-1)
49#elif defined(__sw_64)
50#define PAGE_SIZE_SUB_ONE (4*1024-1)
51#else
52#define PAGE_SIZE_SUB_ONE (4095)
53#endif
54
55#define Ehdr ElfW(Ehdr)
56#define Phdr ElfW(Phdr)
57#define Shdr ElfW(Shdr)
58#define Nhdr ElfW(Nhdr)
59
60static void rolloutHandler(const char* filename, std::size_t size)
61{
62 static unsigned int log_idx = 0;
63 (void)size;
64
65#ifdef _DEBUG
66 // SHOULD NOT LOG ANYTHING HERE BECAUSE LOG FILE IS CLOSED!
67 std::cout << "************** Rolling out [" << filename
68 << "] because it reached [" << size << " bytes]" << std::endl;
69#endif
70
71 // BACK IT UP
72 std::stringstream ss;
73 ss << "mv " << filename << " " << filename << "-backup." << ++log_idx;
74 (void)system(ss.str().c_str());
75}
76
77
78static void init_log(DumpConfig& cfg, const char* filename)
79{
80 el::Configurations defaultConf;
81
82 defaultConf.setToDefault();
83
84 if (cfg.log_debug) {
85 defaultConf.set(el::Level::Debug, el::ConfigurationType::Enabled, "true");
86 }
87 else {
88 defaultConf.set(el::Level::Debug, el::ConfigurationType::Enabled, "false");
89 }
90
91 /*To hide %user,%host,%func,%file,%line*/
92 defaultConf.set(el::Level::Debug, el::ConfigurationType::Format, "%level %msg");
93 defaultConf.set(el::Level::Warning, el::ConfigurationType::Format, "%level %msg");
94 defaultConf.set(el::Level::Error, el::ConfigurationType::Format, "%level %msg");
95 defaultConf.set(el::Level::Info, el::ConfigurationType::Format, "%level %msg");
96
97 defaultConf.setGlobally(el::ConfigurationType::ToStandardOutput,
98 cfg.log_to_stdout?"true":"false");
99 defaultConf.setGlobally(el::ConfigurationType::ToFile,
100 cfg.log_to_file?"true":"false");
101 if (cfg.log_to_file) {
102 string logfilename = cfg.dump_dir + filename;
103 defaultConf.setGlobally(el::ConfigurationType::Filename,
104 logfilename.data());
105 }
106
107 if (cfg.log_flush_threshold >= 0) {
108 auto && oss = std::ostringstream();
109 oss << cfg.log_flush_threshold;
110
111 auto log_flush_threshold = oss.str();
112 defaultConf.setGlobally(el::ConfigurationType::LogFlushThreshold,
113 log_flush_threshold.data());
114 }
115
116 if (cfg.log_file_max_size > 0) {
117 auto && oss = std::ostringstream();
118 oss << cfg.log_file_max_size;
119
120 auto log_file_max_size = oss.str();
121
122 defaultConf.setGlobally(el::ConfigurationType::MaxLogFileSize,
123 log_file_max_size.data());
124
125 el::Helpers::installPreRollOutCallback(rolloutHandler);
126 //el::Helpers::uninstallPreRollOutCallback();
127 }
128 el::Loggers::reconfigureLogger("default", defaultConf);
129 el::Loggers::setDefaultConfigurations(defaultConf, true);
130}
131
132class Timeline{
133public:
134 Timeline(void) { current = 0;};
135 ~Timeline(void) {};
136 int open_trace(const char* maps_file, const char* context_file);
137 int generate_coredump(int index, const char* core_file, bool verbose);
138
139 uint32_t current;
140 char mode[5];
141 vector<EventEntry> event_table;
142 vector<MapsEntry> maps_table;
143 TraceReader ctx_reader;
144 TraceReader maps_reader;
145};
146
147///////////////////////////////////////////////////////////////////////
148// coredump functions
149
150class Options {
151public:
152 Options() : verbose(true), out_fd(0), total(0) {
153 }
154 ~Options(){
155 if (out_fd != STDOUT_FILENO) {
156 close(out_fd);
157 }
158 LOG(INFO) << "coredump file size " << total;
159 }
160
161 bool verbose;
162 int out_fd;
163 int total;
164};
165
166// Write all of the given buffer, handling short writes and EINTR. Return true
167// ff successful.
168static bool write_file(Options& option, const void* idata, size_t length)
169{
170 const uint8_t* data = (const uint8_t*) idata;
171
172 size_t done = 0;
173 while (done < length) {
174 ssize_t r = 0;
175 do {
176 r = write(option.out_fd, data + done, length - done);
177 } while (r == -1 && errno == EINTR);
178
179 if (r < 1) {
180 LOG(ERROR) << "failed to write coredump file, current=" << option.total;
181 return false;
182 }
183 done += r;
184 }
185
186 option.total += length;
187 return true;
188}
189
190/* Dynamically determines the byte sex of the system. Returns non-zero
191 * for big-endian machines.
192 */
193static inline int sex() {
194 int probe = 1;
195 return !*(char *)&probe;
196}
197
198typedef struct elf_timeval { /* Time value with microsecond resolution */
199 long tv_sec; /* Seconds */
200 long tv_usec; /* Microseconds */
201} elf_timeval;
202
203typedef struct _elf_siginfo { /* Information about signal (unused) */
204 int32_t si_signo; /* Signal number */
205 int32_t si_code; /* Extra code */
206 int32_t si_errno; /* Errno */
207} _elf_siginfo;
208
209
210#if defined(__aarch64__)
211#elif defined (__mips64)
212// see linux kernel source: arch/mips/include/uapi/asm/ptrace.h
213typedef struct tag_user_regs_struct {
214 gregset_t regs;
215 uint64_t lo;
216 uint64_t hi;
217 uint64_t pc;
218 uint64_t bad;
219 uint64_t sr;
220 uint64_t cause;
221 uint64_t unkn[7];
222}user_regs_struct;
223#elif defined(__sw_64)
224// see linux kernel source: arch/sw_64/include/uapi/asm/ptrace.h
225typedef struct tag_user_regs_struct {
226 unsigned long v0;
227 unsigned long t0;
228 unsigned long t1;
229 unsigned long t2;
230 unsigned long t3;
231 unsigned long t4;
232 unsigned long t5;
233 unsigned long t6;
234 unsigned long t7;
235 unsigned long s0;
236 unsigned long s1;
237 unsigned long s2;
238 unsigned long s3;
239 unsigned long s4;
240 unsigned long s5;
241 unsigned long fp;
242 unsigned long a0;
243 unsigned long a1;
244 unsigned long a2;
245 unsigned long a3;
246 unsigned long a4;
247 unsigned long a5;
248 unsigned long t8;
249 unsigned long t9;
250 unsigned long t10;
251 unsigned long t11;
252 unsigned long ra;
253 unsigned long t12;
254 unsigned long at;
255 unsigned long gp;
256 unsigned long sp;
257 unsigned long pc;
258 unsigned long unkn;
259}user_regs_struct;
260#else
261#endif
262
263typedef struct prstatus { /* Information about thread; includes CPU reg*/
264 _elf_siginfo pr_info; /* Info associated with signal */
265 uint16_t pr_cursig; /* Current signal */
266 unsigned long pr_sigpend; /* Set of pending signals */
267 unsigned long pr_sighold; /* Set of held signals */
268 pid_t pr_pid; /* Process ID */
269 pid_t pr_ppid; /* Parent's process ID */
270 pid_t pr_pgrp; /* Group ID */
271 pid_t pr_sid; /* Session ID */
272 elf_timeval pr_utime; /* User time */
273 elf_timeval pr_stime; /* System time */
274 elf_timeval pr_cutime; /* Cumulative user time */
275 elf_timeval pr_cstime; /* Cumulative system time */
276 user_regs_struct pr_reg; /* CPU registers */
277 uint32_t pr_fpvalid; /* True if math co-processor being used */
278} prstatus;
279
280typedef struct prpsinfo { /* Information about process */
281 unsigned char pr_state; /* Numeric process state */
282 char pr_sname; /* Char for pr_state */
283 unsigned char pr_zomb; /* Zombie */
284 signed char pr_nice; /* Nice val */
285 unsigned long pr_flag; /* Flags */
286#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__)
287 uint32_t pr_uid; /* User ID */
288 uint32_t pr_gid; /* Group ID */
289#else
290 uint16_t pr_uid; /* User ID */
291 uint16_t pr_gid; /* Group ID */
292#endif
293 pid_t pr_pid; /* Process ID */
294 pid_t pr_ppid; /* Parent's process ID */
295 pid_t pr_pgrp; /* Group ID */
296 pid_t pr_sid; /* Session ID */
297 char pr_fname[16]; /* Filename of executable */
298 char pr_psargs[80]; /* Initial part of arg list */
299} prpsinfo;
300
301// We parse the minidump file and keep the parsed information in this structure
302struct DumpedProcess {
303 DumpedProcess() : crashing_tid(-1) {
304 memset(&prps, 0, sizeof(prps));
305 prps.pr_sname = 'R';
306 memset(&debug, 0, sizeof(debug));
307 }
308
309 struct Mapping {
310 Mapping() : permissions(0xFFFFFFFF),
311 start_address(0),
312 end_address(0),
313 offset(0) {
314 }
315
316 uint32_t permissions;
317 uintptr_t start_address, end_address, offset;
318 // The name we write out to the core.
319 string filename;
320 string data;
321 };
322 std::map<uintptr_t, Mapping> mappings;
323
324 pid_t crashing_tid;
325 int fatal_signal;
326
327 class Heap{
328 public:
329 Heap(uintptr_t address, string&& astr):
330 addr(address), data(std::move(astr)){
331 LOG(DEBUG) << "Heap move constructor:" << HEX(addr);
332 }
333 Heap(uintptr_t address, string& astr):
334 addr(address), data(astr){
335 LOG(DEBUG) << "Heap constructor:" << HEX(addr);
336 }
337 uintptr_t addr;
338 string data;
339 };
340 std::vector<Heap> heaps;
341
342 struct Thread {
343 pid_t tid;
344
345 USER_REGS regs;
346 USER_FPREGS fpregs;
347
348#if defined(__i386__)
349 user_fpxregs_struct fpxregs;
350#endif
351
352 uintptr_t stack_addr;
353 string stack;
354 };
355 std::vector<Thread> threads;
356
357
358 uintptr_t at_phdr;
359 string auxv;
360 string vdso;
361 uintptr_t vdso_addr;
362
363 prpsinfo prps;
364
365 // The GUID/filename from MD_MODULE_LIST_STREAM entries.
366 // We gather them for merging later on into the list of maps.
367 struct Signature {
368 string filename;
369 char guid[48];
370 };
371 std::map<uintptr_t, Signature> signatures;
372
373 string dynamic_data;
374 MDRawDebug debug;
375 std::vector<MDRawLinkMap> link_map;
376};
377
378static uint16_t ParseThreadList(const Options& options,
379 DumpedProcess* process, TraceReader* reader)
380{
381 EventHead head;
382
383 static_assert(sizeof(head.cur_time) +
384 sizeof(head.reason) +
385 sizeof(head.thread_num) +
386 sizeof(head.current_tid) +
387 sizeof(head.extra_size) == sizeof(head),
388 "struct EventHead aligned error!");
389 reader->ReadBlock(&head, sizeof(head));
390
391 if (head.extra_size > 0) {
392 // delay load them in get_event_extra_info
393 reader->Seek(reader->Current() + head.extra_size);
394 }
395
396 if (options.verbose) {
397 LOG(INFO) << "EMD_THREAD_LIST_STREAM:\nEvent(" << get_event_name(head.reason)
398 << "), threads=" << head.thread_num;
399 }
400
401 for (int i = 0; i<head.thread_num; ++i) {
402 DumpedProcess::Thread thread;
403
404 uint32_t tid = 0;
405 reader->ReadValue<uint32_t>(&tid);
406 thread.tid = tid & 0xffff;
407 reader->ReadBlock(&thread.regs, sizeof(thread.regs));
408 reader->ReadBlock(&thread.fpregs, sizeof(thread.fpregs));
409
410 string tls; reader->ReadBlock(tls); //skip tls data
411
412 reader->ReadValue<uintptr_t>(&thread.stack_addr);
413 reader->ReadBlock(thread.stack);
414
415 process->threads.push_back(thread);
416 if (options.verbose) {
417 LOG(INFO) << "thread " << thread.tid << " stack range"
418 << ":" << HEX(thread.stack_addr)
419 << "," << HEX(thread.stack_addr + thread.stack.size());
420 }
421 }
422
423 uint32_t heap_count = 0;
424 reader->ReadValue<uint32_t>(&heap_count);
425 for (uint32_t i = 0; i<heap_count; ++i) {
426 uintptr_t addr = 0;
427 reader->ReadValue<uintptr_t>(&addr);
428 string data; reader->ReadBlock(data);
429 if (!data.empty()) {
430 if (options.verbose) {
431 LOG(INFO) << "heap segment " << i
432 << ":" << HEX(addr)
433 << "," << HEX(addr + data.size());
434 }
435 process->heaps.push_back(DumpedProcess::Heap(addr, std::move(data)));
436 }
437 }
438
439 if (head.reason >= DUMP_REASON_signal && head.reason < DUMP_REASON_dbus) {
440 process->crashing_tid = head.current_tid;
441 process->fatal_signal = head.reason - DUMP_REASON_signal;
442 }
443 return head.reason;
444}
445
446static bool ParseSystemInfo(TraceReader* reader, bool verbose, char* mode)
447{
448 string blk; reader->ReadBlock(blk);
449 MemoryRange range(blk);
450
451 const MDRawSystemInfo* sysinfo = range.GetData<MDRawSystemInfo>(0);
452 if (!sysinfo) {
453 LOG(ERROR) << "Failed to access EMD_SYSTEM_INFO_STREAM";
454 return false;
455 }
456#if defined(__i386__)
457 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_X86) {
458 LOG(ERROR) << "This version of emd only supports x86 (32bit)";
459 return false;
460 }
461#elif defined(__x86_64__)
462 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_AMD64) {
463 LOG(ERROR) << "This version of emd only supports x86 (64bit).";
464 return false;
465 }
466#elif defined(__arm__)
467 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM) {
468 LOG(ERROR) << "This version of emd only supports ARM (32bit).";
469 return false;
470 }
471#elif defined(__aarch64__)
472 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM64) {
473 LOG(ERROR) << "This version of emd only supports ARM (64bit).";
474 return false;
475 }
476#elif defined(__mips__)
477# if _MIPS_SIM == _ABIO32
478 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_MIPS) {
479 LOG(ERROR) << "This version of emd only supports mips o32 (32bit).";
480 return false;
481 }
482# elif _MIPS_SIM == _ABI64
483 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_MIPS64) {
484 LOG(ERROR) << "This version of emd only supports mips n64 (64bit).";
485 return false;
486 }
487# else
488# error "This mips ABI is currently not supported (n32)"
489# endif
490#elif defined(__sw_64)
491 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_SW64) {
492 LOG(ERROR) << "This version of emd only supports sw n64 (64bit).";
493 return false;
494 }
495#else
496#error "This code has not been ported to your platform yet"
497#endif
498
499 memcpy(mode, sysinfo->mode, 4);
500 mode[4] = 0;
501
502 if (verbose) {
503 const char* arch = sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86
504 ? "i386"
505 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64
506 ? "x86-64"
507 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_ARM
508 ? "ARM"
509 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_ARM64
510 ? "ARM64"
511 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_MIPS
512 ? "MIPS"
513 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_MIPS64
514 ? "MIPS64"
515 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_SW64
516 ? "SW64"
517 : "???";
518 LOG(INFO) << "EMD_SYSTEM_INFO_STREAM:\nArchitecture: \n" << arch
519 << "\nRecord Mode: " << mode
520 << "\nKernel:\n" << sysinfo->uname
521 << "\nDistribution:\n" << sysinfo->lsb_release;;
522 }
523
524 return true;
525}
526
527static void ParseMaps(const Options& options, DumpedProcess* process,
528 const MemoryRange& range)
529{
530 if (options.verbose) {
531 LOG(INFO) << "EMD_LINUX_MAPS:\n"
532 << string((char *)range.data(), range.length())
533 << "------------------------------------------------------------";
534 }
535 for (const uint8_t* ptr = range.data();
536 ptr < range.data() + range.length();) {
537 const uint8_t* eol = (uint8_t*)memchr(ptr, '\n',
538 range.data() + range.length() - ptr);
539 string line((const char*)ptr,
540 eol ? eol - ptr : range.data() + range.length() - ptr);
541 ptr = eol ? eol + 1 : range.data() + range.length();
542 unsigned long long start, stop, offset;
543 char* permissions = nullptr;
544 char* filename = nullptr;
545 sscanf(line.c_str(), "%llx-%llx %m[-rwxp] %llx %*[:0-9a-f] %*d %ms",
546 &start, &stop, &permissions, &offset, &filename);
547 if (filename && *filename == '/') {
548 DumpedProcess::Mapping mapping;
549 mapping.permissions = 0;
550 if (strchr(permissions, 'r')) {
551 mapping.permissions |= PF_R;
552 }
553 if (strchr(permissions, 'w')) {
554 mapping.permissions |= PF_W;
555 }
556 if (strchr(permissions, 'x')) {
557 mapping.permissions |= PF_X;
558 }
559 mapping.start_address = start;
560 mapping.end_address = stop;
561 mapping.offset = offset;
562 if (filename) {
563 mapping.filename = filename;
564 }
565 process->mappings[mapping.start_address] = mapping;
566 }
567 free(permissions);
568 free(filename);
569 }
570}
571
572static void ParseAuxVector(const Options& options,
573 DumpedProcess* process, TraceReader* reader)
574{
575 string blk; reader->ReadBlock(blk);
576 MemoryRange range(blk);
577
578 // Some versions of Chrome erroneously used the MD_LINUX_AUXV stream value
579 // when dumping /proc/$x/maps
580 if (range.length() > 17) {
581 // The AUXV vector contains binary data, whereas the maps always begin
582 // with an 8+ digit hex address followed by a hyphen and another 8+ digit
583 // address.
584 char addresses[18];
585 memcpy(addresses, range.data(), 17);
586 addresses[17] = '\000';
587 if (strspn(addresses, "0123456789abcdef-") == 17) {
588 ParseMaps(options, process, range);
589 return;
590 }
591 }
592
593 process->auxv = blk;
594 const elf_aux_entry* aux_entry = (const elf_aux_entry*)blk.data();
595 for (uint32_t i=0; i < blk.size()/sizeof(elf_aux_entry); ++i) {
596 if (aux_entry->a_type == AT_PHDR) {
597 process->at_phdr = aux_entry->a_un.a_val;
598 }
599 ++aux_entry;
600 }
601
602 if (options.verbose) {
603 LOG(DEBUG) << "EMD_LINUX_AUXV:\nFound auxv, bytes=" << blk.size()
604 << ", AT_PHDR=" << HEX(process->at_phdr);
605 }
606}
607
608static void ParseCmdLine(const Options& options,
609 DumpedProcess* process, TraceReader* reader)
610{
611 string blk; reader->ReadBlock(blk);
612 MemoryRange range(blk);
613
614 // The command line is supposed to use NUL bytes to separate arguments.
615 // As Chrome rewrites its own command line and (incorrectly) substitutes
616 // spaces, this is often not the case in our minidump files.
617 const char* cmdline = (const char*) range.data();
618 unsigned i = 0;
619 for (; i < range.length() && cmdline[i] && cmdline[i] != ' '; ++i) { }
620
621 if (options.verbose) {
622 LOG(INFO) << "EMD_LINUX_CMD_LINE:\nargv[0]=" << string(cmdline, i);
623 for (unsigned j = ++i, argc = 1; j < range.length(); ++j) {
624 if (!cmdline[j] || cmdline[j] == ' ') {
625 LOG(INFO) << "\nargv["<< argc << "]=" << string(cmdline + i, j - i);
626 ++argc;
627 i = j + 1;
628 }
629 }
630 }
631
632 const char *binary_name = cmdline;
633 for (size_t i = 0; i < range.length(); ++i) {
634 if (cmdline[i] == '/') {
635 binary_name = cmdline + i + 1;
636 } else if (cmdline[i] == 0 || cmdline[i] == ' ') {
637 static const size_t fname_len = sizeof(process->prps.pr_fname) - 1;
638 static const size_t args_len = sizeof(process->prps.pr_psargs) - 1;
639 memset(process->prps.pr_fname, 0, fname_len + 1);
640 memset(process->prps.pr_psargs, 0, args_len + 1);
641 unsigned len = cmdline + i - binary_name;
642 memcpy(process->prps.pr_fname, binary_name,
643 len > fname_len ? fname_len : len);
644
645 len = range.length() > args_len ? args_len : range.length();
646 memcpy(process->prps.pr_psargs, cmdline, len);
647 for (unsigned j = 0; j < len; ++j) {
648 if (process->prps.pr_psargs[j] == 0)
649 process->prps.pr_psargs[j] = ' ';
650 }
651 break;
652 }
653 }
654}
655
656static void ParseDSODebugInfo(const Options& options,
657 DumpedProcess* process, TraceReader* reader)
658{
659 int size = 0;
660 MDRawDebug debug;
661 reader->ReadValue<int>(&size);
662 if (0 == size) {
663 return;
664 }
665 if (sizeof(debug) != size) {
666 LOG(WARNING) << "Invalid EMD_LINUX_DSO_DEBUG";
667 return;
668 }
669 reader->ReadBlock(&debug, sizeof(debug));
670
671 if (options.verbose) {
672 LOG(INFO) << "EMD_LINUX_DSO_DEBUG:\nVersion: " << debug.version
673 << "\nNumber of DSOs: " << debug.dso_count
674 << "\nBrk handler: " << HEX(debug.brk)
675 << "\nLoader address: " << HEX(debug.ldbase)
676#if defined(__mips64)
677 << "\nrld_map: " << HEX(debug.rld_map)
678#endif
679 << "\n_DYNAMIC address: " << HEX(debug.dynamic);
680 }
681
682 process->debug = debug;
683
684 if (debug.dso_count) {
685 unique_ptr<MDRawLinkMap[]> buffer = make_unique<MDRawLinkMap[]>(debug.dso_count);
686 MDRawLinkMap* link_map = buffer.get();
687
688 reader->ReadBlock(buffer.get(), debug.dso_count*sizeof(MDRawLinkMap));
689
690 for (unsigned int i = 0; i < debug.dso_count; ++i) {
691 // NOTE: MD_LINUX_DSO_DEBUG info is gotten from DT_DEBUG field in the dynamic
692 // section.
693 if (options.verbose) {
694 LOG(DEBUG) << i
695 << ", " << HEX(link_map->addr)
696 << ", " << HEX(link_map->ld)
697 << ", " << link_map->name;
698 }
699 process->link_map.push_back(*link_map);
700 ++link_map;
701 }
702 }
703
704 string blk; reader->ReadBlock(blk); // load dynamic data
705 if (blk.length() > 0) {
706 process->dynamic_data.assign(blk.data(), blk.length());
707 }
708}
709
710static void ParseModuleList(const Options& options, DumpedProcess* process,
711 TraceReader* reader)
712{
713 if (options.verbose) {
714 LOG(INFO) << "EMD_MODULE_LIST_STREAM:";
715 }
716
717 uint32_t num_mappings = 0;
718 if (!reader->ReadValue(&num_mappings))
719 return;
720
721 unique_ptr<MDRawModule[]> buffer = make_unique<MDRawModule[]>(num_mappings);
722 reader->ReadBlock(buffer.get(), num_mappings*sizeof(MDRawModule));
723
724 for (unsigned i = 0; i < num_mappings; ++i) {
725 DumpedProcess::Mapping mapping;
726 MDRawModule* rawmodule = buffer.get() + i;
727 mapping.start_address = rawmodule->base_of_image;
728 mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image;
729
730 if (process->mappings.find(mapping.start_address) ==
731 process->mappings.end()) {
732 // We prefer data from MD_LINUX_MAPS over MD_MODULE_LIST_STREAM, as
733 // the former is a strict superset of the latter.
734 process->mappings[mapping.start_address] = mapping;
735 }
736
737 DumpedProcess::Signature signature;
738 signature.filename = rawmodule->file_path;
739
740 if (options.verbose) {
741 for (int j = 0; j<kMDGUIDSize; ++j){
742 sprintf(&signature.guid[j*2], "%02x", rawmodule->guid[j]);
743 }
744 signature.guid[kMDGUIDSize*2] = 0;
745 LOG(INFO) << HEX(rawmodule->base_of_image)
746 << "-" << HEX(rawmodule->base_of_image + rawmodule->size_of_image)
747 << ", " << signature.guid
748 << ", " << rawmodule->file_path;
749 }
750
751 process->signatures[rawmodule->base_of_image] = signature;
752 }
753}
754
755#if defined(__sw_64)
756void copy_regs(user_regs_struct* dst, const USER_REGS* src)
757{
758 // NOTE: the layout of dst and src are different!
759 dst->v0 = src->v0;
760 dst->t0 = src->t0;
761 dst->t1 = src->t1;
762 dst->t2 = src->t2;
763 dst->t3 = src->t3;
764 dst->t4 = src->t4;
765 dst->t5 = src->t5;
766 dst->t6 = src->t6;
767 dst->t7 = src->t7;
768
769 dst->s0 = src->s0;
770 dst->s1 = src->s1;
771 dst->s2 = src->s2;
772 dst->s3 = src->s3;
773 dst->s4 = src->s4;
774 dst->s5 = src->s5;
775 dst->fp = src->s6;
776 dst->a0 = src->a0;
777 dst->a1 = src->a1;
778 dst->a2 = src->a2;
779 dst->a3 = src->a3;
780 dst->a4 = src->a4;
781 dst->a5 = src->a5;
782
783 dst->t8 = src->t8;
784 dst->t9 = src->t9;
785 dst->t10 = src->t10;
786 dst->t11 = src->t11;
787 dst->ra = src->ra;
788 dst->t12 = src->t12;
789 dst->at = src->r28;
790
791 dst->gp = src->gp;
792 dst->sp = src->sp;
793 dst->pc = src->pc;
794}
795#endif
796
797static bool WriteThread(struct Options& options,
798 const DumpedProcess::Thread& thread, int fatal_signal)
799{
800 struct prstatus pr;
801 memset(&pr, 0, sizeof(pr));
802
803 pr.pr_info.si_signo = fatal_signal;
804 pr.pr_cursig = fatal_signal;
805 pr.pr_pid = thread.tid;
806
807#if defined(__sw_64)
808 copy_regs(&pr.pr_reg, &thread.regs);
809#else
810 memcpy(&pr.pr_reg, &thread.regs, sizeof(thread.regs));
811#endif
812
813 Nhdr nhdr;
814 memset(&nhdr, 0, sizeof(nhdr));
815 nhdr.n_namesz = 5;
816 nhdr.n_descsz = sizeof(struct prstatus);
817 nhdr.n_type = NT_PRSTATUS;
818 if (!write_file(options, &nhdr, sizeof(nhdr)) ||
819 !write_file(options, "CORE\0\0\0\0", 8) ||
820 !write_file(options, &pr, sizeof(struct prstatus))) {
821 return false;
822 }
823
824 nhdr.n_descsz = sizeof(USER_FPREGS);
825 nhdr.n_type = NT_FPREGSET;
826 if (!write_file(options, &nhdr, sizeof(nhdr)) ||
827 !write_file(options, "CORE\0\0\0\0", 8) ||
828 !write_file(options, &thread.fpregs, sizeof(USER_FPREGS))) {
829 return false;
830 }
831
832#if defined(__i386__)
833 nhdr.n_descsz = sizeof(user_fpxregs_struct);
834 nhdr.n_type = NT_PRXFPREG;
835 if (!write_file(options, &nhdr, sizeof(nhdr)) ||
836 !write_file(options, "LINUX\0\0\0", 8) ||
837 !write_file(options, &thread.fpxregs, sizeof(user_fpxregs_struct))) {
838 return false;
839 }
840#endif
841
842 return true;
843}
844
845// gdb's svr4_exec_displacement will failed if we not exclude AT_PHDR region.
846// Then the call stack backtrace is corrupt!
847static void ExcludePhdrRegion(DumpedProcess::Mapping& mapping, uintptr_t phdr_addr)
848{
849 if (mapping.start_address < phdr_addr && phdr_addr < mapping.end_address) {
850 LOG(WARNING) << "exclude phdr region in " << HEX(mapping.start_address)
851 << "-" << HEX(mapping.end_address);
852 mapping.end_address = phdr_addr;
853 mapping.data.resize(mapping.end_address - mapping.start_address);
854 }
855}
856
857static void AddDataToMapping(DumpedProcess* process,
858 const string& data, uintptr_t addr)
859{
860 for (std::map<uintptr_t, DumpedProcess::Mapping>::iterator
861 iter = process->mappings.begin();
862 iter != process->mappings.end();
863 ++iter) {
864 if (addr >= iter->second.start_address &&
865 addr < iter->second.end_address) {
866 uintptr_t base = (addr & ~PAGE_SIZE_SUB_ONE);
867 DumpedProcess::Mapping mapping = iter->second;
868
869 if (base != iter->second.start_address) {
870 // If there are memory pages in the mapping prior to where the
871 // data starts, truncate the existing mapping so that it ends with
872 // the page immediately preceding the data region.
873 iter->second.end_address = base;
874 if (!mapping.filename.empty()) {
875 // "mapping" is a copy of "iter->second". We are splitting the
876 // existing mapping into two separate ones when we write the data
877 // to the core file. The first one does not have any associated
878 // data in the core file, the second one is backed by data that is
879 // included with the core file.
880 // If this mapping wasn't supposed to be anonymous, then we also
881 // have to update the file offset upon splitting the mapping.
882 mapping.offset += iter->second.end_address -
883 iter->second.start_address;
884 }
885 }
886 // Create a new mapping that contains the data contents. We often
887 // limit the amount of data that is actually written to the core
888 // file. But it is OK if the mapping itself extends past the end of
889 // the data.
890 mapping.start_address = base;
891 mapping.data.assign(addr & PAGE_SIZE_SUB_ONE, 0).append(data);
892 mapping.data.append(-mapping.data.size() & PAGE_SIZE_SUB_ONE, 0);
893 ExcludePhdrRegion(mapping, process->at_phdr);
894 process->mappings[mapping.start_address] = mapping;
895 if (base + mapping.data.size() < mapping.end_address) {
896 LOG(WARNING) << "data mapping is not enough:" << HEX(mapping.start_address)
897 << "," << mapping.data.size() << "/" << (mapping.end_address - base);
898 }
899 return;
900 }
901 }
902 // Didn't find a suitable existing mapping for the data. Create a new one.
903 DumpedProcess::Mapping mapping;
904 mapping.permissions = PF_R | PF_W;
905 mapping.start_address = addr & ~PAGE_SIZE_SUB_ONE;
906 mapping.end_address = (addr + data.size() + PAGE_SIZE_SUB_ONE) & ~PAGE_SIZE_SUB_ONE;
907 mapping.data.assign(addr & PAGE_SIZE_SUB_ONE, 0).append(data);
908 mapping.data.append(-mapping.data.size() & PAGE_SIZE_SUB_ONE, 0);
909 ExcludePhdrRegion(mapping, process->at_phdr);
910 process->mappings[mapping.start_address] = mapping;
911}
912
913static string BaseName(const string& path) {
914 char* path_tmp = strdup(path.c_str());
915 assert(path_tmp);
916 string result(basename(path_tmp));
917 free(path_tmp);
918 return result;
919}
920
921#if 0
922static void Add_rodata(DumpedProcess* process, const char* filename, uintptr_t base)
923{
924 // skip kMappedFileUnsafePrefix and kDeletedSuffix file
925 if ( strstr(filename, kMappedFileUnsafePrefix) ||
926 strstr(filename, kDeletedSuffix)) {
927 return ;
928 }
929
930 MemoryMappedFile mapped_file(filename, 0);
931 if (!mapped_file.data() ||
932 mapped_file.size() < SELFMAG) {
933 return;
934 }
935 if (!IsValidElf(mapped_file.data())) {
936 return;
937 }
938
939 void* data = nullptr;
940 size_t data_size = 0;
941 if (!FindElfSection(mapped_file.data(), ".rodata", SHT_PROGBITS,
942 (const void**)&data, &data_size)) {
943 return;
944 }
945
946 string heap((char*)data, data_size);
947 uintptr_t addr = base + int((char*)data - (char*)mapped_file.data());
948 AddDataToMapping(process, heap, addr);
949 LOG(DEBUG) << ".rodata at " << HEX(addr)
950 << ", size=" << data_size <<", for " << filename;
951}
952#endif
953
954static void AugmentMappings(const Options& options, DumpedProcess* process)
955{
956 // For each thread, find the memory mapping that matches the thread's stack.
957 // Then adjust the mapping to include the stack dump.
958 for (unsigned i = 0; i < process->threads.size(); ++i) {
959 const DumpedProcess::Thread& thread = process->threads[i];
960 AddDataToMapping(process, thread.stack, thread.stack_addr);
961 }
962
963 for (auto& heap: process->heaps) {
964 AddDataToMapping(process, heap.data, heap.addr);
965 }
966
967 // NOTE: vdso is not implemented in SunWay!
968 if (process->vdso.size() > 1024) {
969 AddDataToMapping(process, process->vdso, process->vdso_addr);
970 }
971
972#if 0
973 // load .rodata section of each module
974 for (auto& m: process->signatures) {
975 Add_rodata(process, m.second.filename.data(), m.first);
976 }
977#endif
978
979 // Create a new link map with information about DSOs. We move this map to
980 // the beginning of the address space, as this area should always be
981 // available.
982 static const uintptr_t start_addr = 4096;
983 string data;
984 struct r_debug debug;
985 debug.r_version = process->debug.version;
986 debug.r_brk = (ElfW(Addr))process->debug.brk;
987 debug.r_state = r_debug::RT_CONSISTENT;
988 debug.r_ldbase = (ElfW(Addr))process->debug.ldbase;
989 debug.r_map = process->debug.dso_count > 0 ?
990 (struct link_map*)(start_addr + sizeof(debug)) : 0;
991 data.append((char*)&debug, sizeof(debug));
992
993 struct link_map* prev = 0;
994 for (std::vector<MDRawLinkMap>::iterator iter = process->link_map.begin();
995 iter != process->link_map.end();
996 ++iter) {
997 struct link_map link_map;
998 link_map.l_addr = (ElfW(Addr))iter->addr;
999 link_map.l_name = (char*)(start_addr + data.size() + sizeof(link_map));
1000 link_map.l_ld = (ElfW(Dyn)*)iter->ld;
1001 link_map.l_prev = prev;
1002 prev = (struct link_map*)(start_addr + data.size());
1003 string filename = iter->name;
1004
1005 // Look up signature for this filename. If available, change filename
1006 // to point to GUID, instead.
1007 std::map<uintptr_t, DumpedProcess::Signature>::const_iterator sig =
1008 process->signatures.find((uintptr_t)iter->addr);
1009 if (sig != process->signatures.end()) {
1010 // At this point, we have:
1011 // old_filename: The path as found via SONAME (e.g. /lib/libpthread.so.0).
1012 // sig_filename: The path on disk (e.g. /lib/libpthread-2.19.so).
1013 string sig_filename = sig->second.filename;
1014 string old_filename = filename.empty() ? sig_filename : filename;
1015 string new_filename;
1016
1017 // First set up the leading path. We assume dirname always ends with a
1018 // trailing slash (as needed), so we won't be appending one manually.
1019 if (1) {
1020 string dirname = old_filename;
1021 size_t slash = dirname.find_last_of('/');
1022 if (slash != string::npos) {
1023 new_filename = dirname.substr(0, slash + 1);
1024 }
1025 }
1026
1027 // Decide whether we use the filename or the SONAME (where the SONAME tends
1028 // to be a symlink to the actual file).
1029 new_filename += BaseName(old_filename);
1030
1031 if (filename != new_filename) {
1032 if (options.verbose) {
1033 LOG(DEBUG) << HEX(link_map.l_addr)
1034 << " rewriting mapping " << filename.c_str()
1035 << " to " << new_filename.c_str();
1036 }
1037 filename = new_filename;
1038 }
1039 }
1040
1041 if (std::distance(iter, process->link_map.end()) == 1) {
1042 link_map.l_next = 0;
1043 } else {
1044 link_map.l_next = (struct link_map*)(start_addr + data.size() +
1045 sizeof(link_map) +
1046 ((filename.size() + 8) & ~7));
1047 }
1048 data.append((char*)&link_map, sizeof(link_map));
1049 data.append(filename);
1050 data.append(8 - (filename.size() & 7), 0);
1051 }
1052 AddDataToMapping(process, data, start_addr);
1053
1054 // Map the page containing the _DYNAMIC array
1055 if (!process->dynamic_data.empty()) {
1056 // Make _DYNAMIC DT_DEBUG entry point to our link map
1057 for (int i = 0;; ++i) {
1058 ElfW(Dyn) dyn;
1059 if ((i+1)*sizeof(dyn) > process->dynamic_data.length()) {
1060 if (options.verbose) {
1061 LOG(WARNING) << "DT_DEBUG entry not found";
1062 }
1063 return;
1064 }
1065 memcpy(&dyn, process->dynamic_data.c_str() + i*sizeof(dyn),
1066 sizeof(dyn));
1067
1068#if defined(__mips__)
1069 if (dyn.d_tag == DT_MIPS_RLD_MAP) {
1070 // NOTE: fix DT_MIPS_RLD_MAP
1071 // or call stack back trace will miss call frame!
1072 string ptr;
1073 ptr.assign((char*)&start_addr, sizeof(start_addr));
1074 AddDataToMapping(process, ptr, dyn.d_un.d_ptr);
1075 break;
1076 }
1077 else if (dyn.d_tag == DT_MIPS_RLD_MAP_REL) {
1078 // NOTE: set value of .rld_map to start_addr,
1079 // or call stack back trace will miss call frame!
1080 string ptr;
1081 ptr.assign((char*)&start_addr, sizeof(start_addr));
1082 AddDataToMapping(process, ptr, process->debug.rld_map);
1083 break;
1084#else
1085 if (dyn.d_tag == DT_DEBUG) {
1086 process->dynamic_data.replace(i*sizeof(dyn) +
1087 offsetof(ElfW(Dyn), d_un.d_ptr),
1088 sizeof(start_addr),
1089 (char*)&start_addr, sizeof(start_addr));
1090 break;
1091#endif
1092 } else if (dyn.d_tag == DT_NULL) {
1093 if (options.verbose) {
1094 LOG(WARNING) << "DT_DEBUG entry not found 2";
1095 }
1096 return;
1097 }
1098 }
1099 AddDataToMapping(process, process->dynamic_data,
1100 (uintptr_t)process->debug.dynamic);
1101 }
1102}
1103
1104int Timeline::generate_coredump(int index, const char* core_file, bool verbose)
1105{
1106 struct Options options;
1107 DumpedProcess process;
1108 TraceReader* cf = &ctx_reader;
1109 TraceReader* mf = &maps_reader;
1110
1111 options.verbose = verbose;
1112
1113 if (index >= 0 && index < (int)event_table.size()) {
1114 current = index;
1115 }
1116 else {
1117 return -1;
1118 }
1119
1120#if 0
1121 if (!strcmp(mode, "fast")) {
1122 if (event_table[current].type >= DUMP_REASON_dbus &&
1123 event_table[current].type < DUMP_REASON_ptrace) {
1124 LOG(INFO) << "No stack data for x11 and dbus with fast mode!";
1125 return -1;
1126 }
1127 }
1128#endif
1129
1130 if (core_file == nullptr || !strcmp(core_file, "-")) {
1131 options.out_fd = STDOUT_FILENO;
1132 } else {
1133 options.out_fd = open(core_file, O_WRONLY|O_CREAT|O_TRUNC, 0664);
1134 if (options.out_fd == -1) {
1135 return -2;
1136 }
1137 }
1138
1139 cf->Seek(event_table.at(current).offset);
1140
1141 uint16_t event = ParseThreadList(options, &process, cf);
1142 if (!strcmp(mode, "fast") &&
1143 (event >= DUMP_REASON_signal && event < DUMP_REASON_dbus)) {
1144 // FIX pc,ra,sp , so backtrace can work right?
1145
1146 for (auto& i:process.threads) {
1147 if (i.tid != process.crashing_tid) {
1148 continue;
1149 }
1150
1151#if defined(__mips64) || defined(__sw_64)
1152 int pos = i.regs.s5 - i.stack_addr;
1153 if (pos >= 0 && pos < i.stack.size()) {
1154 memcpy(&i.regs, i.stack.data() + pos, sizeof(i.regs));
1155 i.regs.sp += 512; //see syscall_wrapper in mips64/sunway64
1156 i.regs.ra = i.regs.t2; //see syscall_wrapper in mips64/sunway64
1157 i.regs.pc = i.regs.t3; // see build_trampoline in mips64/sunway64
1158 }
1159#elif defined(__aarch64__)
1160 int pos = i.regs.x28 - i.stack_addr;
1161 if (pos >= 0 && pos < i.stack.size()) {
1162 memcpy(&i.regs, i.stack.data() + pos, sizeof(i.regs));
1163 i.regs.sp += 512; //see syscall_wrapper in arm64
1164 i.regs.ra = i.regs.x11; //see syscall_wrapper in arm64
1165 i.regs.pc = i.regs.x10; // see build_trampoline in arm64
1166 }
1167#endif
1168 }
1169 }
1170
1171 MapsEntry target;
1172 target.offset = 0;
1173 target.time = event_table.at(current).time;
1174 auto upper = upper_bound(maps_table.begin(),
1175 maps_table.end(), target,
1176 [](auto& a, auto& b){ return (a.time < b.time); });
1177 if (upper == maps_table.end()) upper = maps_table.end() - 1;
1178
1179 // These parse order can't be changed,
1180 // see TraceSession::dump_maps, dump_auxv, dump_proc_file!
1181 mf->Seek(maps_table.at(0).offset);
1182 ParseAuxVector(options, &process, mf);
1183
1184 ParseCmdLine(options, &process, mf);
1185
1186 // load environ
1187 string env; mf->ReadBlock(env);
1188 if (options.verbose) {
1189 const char* walk = env.data();
1190 LOG(INFO) << "EMD_LINUX_ENVIRON:";
1191 for (int i = 0; *walk > 0; ++i) {
1192 if (memcmp(walk, "LS_COLORS=", 8)) {
1193 LOG(INFO) << i << ":" << walk;
1194 }
1195 else {
1196 LOG(INFO) << i << ":LS_COLORS=...";
1197 }
1198 walk += strlen(walk) + 1;
1199 }
1200 }
1201
1202 // Load vdso
1203 mf->ReadBlock(process.vdso);
1204 // NOTE: vdso is not implemented in SunWay!
1205 if (process.vdso.size() > 1024) {
1206 int position = process.vdso.size() - sizeof(uintptr_t);
1207 assert(0 == (position & 1023));
1208 memcpy(&process.vdso_addr,
1209 process.vdso.data() + position, sizeof(uintptr_t));
1210 process.vdso.erase(position);
1211 }
1212
1213 if (upper > maps_table.begin()) {
1214 mf->Seek(upper->offset);
1215 }
1216 string blk; mf->ReadBlock(blk);
1217 MemoryRange maps(blk);
1218 ParseMaps(options, &process, maps);
1219
1220 ParseModuleList(options, &process, mf);
1221
1222 ParseDSODebugInfo(options, &process, mf);
1223 if (process.debug.dso_count < 1) {
1224 return -3;
1225 }
1226
1227 AugmentMappings(options, &process);
1228
1229 // Write the ELF header. The file will look like:
1230 // ELF header
1231 // Phdr for the PT_NOTE
1232 // Phdr for each of the thread stacks
1233 // PT_NOTE
1234 // each of the thread stacks
1235 Ehdr ehdr;
1236 memset(&ehdr, 0, sizeof(Ehdr));
1237 ehdr.e_ident[0] = ELFMAG0;
1238 ehdr.e_ident[1] = ELFMAG1;
1239 ehdr.e_ident[2] = ELFMAG2;
1240 ehdr.e_ident[3] = ELFMAG3;
1241 ehdr.e_ident[4] = ELF_CLASS;
1242 ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB;
1243 ehdr.e_ident[6] = EV_CURRENT;
1244 ehdr.e_type = ET_CORE;
1245 ehdr.e_machine = ELF_ARCH;
1246 ehdr.e_version = EV_CURRENT;
1247 ehdr.e_phoff = sizeof(Ehdr);
1248 ehdr.e_ehsize = sizeof(Ehdr);
1249 ehdr.e_phentsize= sizeof(Phdr);
1250 ehdr.e_phnum = 1 + // PT_NOTE
1251 process.mappings.size(); // memory mappings
1252 ehdr.e_shentsize= sizeof(Shdr);
1253 if (!write_file(options, &ehdr, sizeof(Ehdr))) {
1254 return -4;
1255 }
1256
1257#if defined(GENERATE_NT_FILE)
1258 long filemapinfo[5];
1259 int filenamelen = strlen("/home/deepin/chliu/event_recorder/debug/testclone") + 1;
1260#endif
1261
1262 size_t offset = sizeof(Ehdr) + ehdr.e_phnum * sizeof(Phdr);
1263 size_t filesz = sizeof(Nhdr) + 8 + sizeof(prpsinfo) +
1264 sizeof(Nhdr) + 8 + process.auxv.length() +
1265#if defined(GENERATE_NT_FILE)
1266 sizeof(Nhdr) + 8 + sizeof(filemapinfo) + (filenamelen+15)/16*16 +
1267#endif
1268 process.threads.size() * (
1269 (sizeof(Nhdr) + 8 + sizeof(prstatus))
1270 + sizeof(Nhdr) + 8 + sizeof(USER_FPREGS)
1271#if defined(__i386__)
1272 + sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct)
1273#endif
1274 );
1275
1276 Phdr phdr;
1277 memset(&phdr, 0, sizeof(Phdr));
1278 phdr.p_type = PT_NOTE;
1279 phdr.p_offset = offset;
1280 phdr.p_filesz = filesz;
1281 if (!write_file(options, &phdr, sizeof(phdr))) {
1282 return -5;
1283 }
1284
1285 phdr.p_type = PT_LOAD;
1286 phdr.p_align = 4096;
1287 size_t note_align = phdr.p_align - ((offset+filesz) % phdr.p_align);
1288 if (note_align == phdr.p_align) {
1289 note_align = 0;
1290 }
1291 offset += note_align;
1292
1293 for (std::map<uintptr_t, DumpedProcess::Mapping>::const_iterator iter =
1294 process.mappings.begin();
1295 iter != process.mappings.end(); ++iter) {
1296 const DumpedProcess::Mapping& mapping = iter->second;
1297 if (mapping.permissions == 0xFFFFFFFF) {
1298 // This is a map that we found in MD_MODULE_LIST_STREAM (as opposed to
1299 // MD_LINUX_MAPS). It lacks some of the information that we would like
1300 // to include.
1301 phdr.p_flags = PF_R;
1302 } else {
1303 phdr.p_flags = mapping.permissions;
1304 }
1305 phdr.p_vaddr = mapping.start_address;
1306 phdr.p_memsz = mapping.end_address - mapping.start_address;
1307 if (mapping.data.size()) {
1308 offset += filesz;
1309 filesz = mapping.data.size();
1310 phdr.p_filesz = mapping.data.size();
1311 phdr.p_offset = offset;
1312 } else {
1313 phdr.p_filesz = 0;
1314 phdr.p_offset = 0;
1315 }
1316 if (!write_file(options, &phdr, sizeof(phdr))) {
1317 return -6;
1318 }
1319 }
1320
1321 Nhdr nhdr;
1322 memset(&nhdr, 0, sizeof(nhdr));
1323 nhdr.n_namesz = 5;
1324 nhdr.n_descsz = sizeof(prpsinfo);
1325 nhdr.n_type = NT_PRPSINFO;
1326 if (!write_file(options, &nhdr, sizeof(nhdr)) ||
1327 !write_file(options, "CORE\0\0\0\0", 8) ||
1328 !write_file(options, &process.prps, sizeof(prpsinfo))) {
1329 return -7;
1330 }
1331
1332 nhdr.n_descsz = process.auxv.length();
1333 nhdr.n_type = NT_AUXV;
1334 if (!write_file(options, &nhdr, sizeof(nhdr)) ||
1335 !write_file(options, "CORE\0\0\0\0", 8) ||
1336 !write_file(options, process.auxv.data(), process.auxv.length())) {
1337 return -8;
1338 }
1339
1340#if defined(GENERATE_NT_FILE)
1341 nhdr.n_descsz = sizeof(filemapinfo) + (filenamelen+15)/16*16;
1342 nhdr.n_type = NT_FILE;
1343 if (!write_file(options, &nhdr, sizeof(nhdr)) ||
1344 !write_file(options, "CORE\0\0\0\0", 8)) {
1345 }
1346 // write file map count
1347 // write file map entry(long start, long end, long offset)
1348 filemapinfo[0] = 1;
1349 filemapinfo[1] = 1;
1350 filemapinfo[2] = 0xaaaaab8000LL;
1351 filemapinfo[3] = 0xaaaaabc000LL;
1352 filemapinfo[4] = 0;
1353 write_file(options, filemapinfo, sizeof(filemapinfo));
1354 // write file name array
1355 write_file(options,
1356 "/home/deepin/chliu/event_recorder/debug/testclone",
1357 filenamelen);
1358 int align = 16 - (filenamelen & 15);
1359 if (align > 0) {
1360 memset(filemapinfo, 0, align);
1361 write_file(options, filemapinfo, align);
1362 }
1363#endif
1364
1365 for (unsigned i = 0; i < process.threads.size(); ++i) {
1366 if (process.threads[i].tid == process.crashing_tid) {
1367 WriteThread(options, process.threads[i], process.fatal_signal);
1368 break;
1369 }
1370 }
1371
1372 for (unsigned i = 0; i < process.threads.size(); ++i) {
1373 if (process.threads[i].tid != process.crashing_tid)
1374 WriteThread(options, process.threads[i], 0);
1375 }
1376
1377 if (note_align) {
1378 unique_ptr<char[]> scratch = make_unique<char[]>(note_align);
1379 memset(scratch.get(), 0, note_align);
1380 if (!write_file(options, scratch.get(), note_align)) {
1381 return -9;
1382 }
1383 }
1384
1385 for (std::map<uintptr_t, DumpedProcess::Mapping>::const_iterator iter =
1386 process.mappings.begin();
1387 iter != process.mappings.end(); ++iter) {
1388 const DumpedProcess::Mapping& mapping = iter->second;
1389 if (mapping.data.size()) {
1390 if (options.verbose) {
1391 LOG(INFO) << "write data mapping:" << HEX(mapping.start_address)
1392 << ", " << mapping.data.size();
1393 }
1394 if (!write_file(options, mapping.data.c_str(), mapping.data.size())) {
1395 return -10;
1396 }
1397 }
1398 }
1399
1400 if (options.verbose) {
1401 LOG(DEBUG) << "Create coredump file:" << core_file;
1402 }
1403
1404 return 0;
1405}
1406
1407int Timeline::open_trace(const char* maps_file, const char* context_file)
1408{
1409 if (!maps_reader.Open(maps_file, true)) {
1410 return -1;
1411 }
1412
1413 if (!ctx_reader.Open(context_file, true)) {
1414 return -2;
1415 }
1416
1417 // Always check the system info first, as that allows us to tell whether
1418 // this is a minidump file that is compatible with our converter.
1419 bool ok = ParseSystemInfo(&ctx_reader, true, mode);
1420 if (!ok) {
1421 LOG(ERROR) << "Invalid system info head in context file";
1422 return -3;
1423 }
1424 ok = ParseSystemInfo(&maps_reader, true, mode);
1425 if (!ok) {
1426 LOG(ERROR) << "Invalid system info head in maps file";
1427 return -4;
1428 }
1429
1430 maps_reader.BuildMapsTable(&maps_table);
1431 if (maps_table.size() < 1) {
1432 LOG(ERROR) << "Invalid maps file.";
1433 return -5;
1434 }
1435 LOG(DEBUG) << "maps count:" << maps_table.size();
1436
1437 ctx_reader.BuildEventTable(&event_table);
1438 if (event_table.size() < 1) {
1439 LOG(ERROR) << "Invalid context file.";
1440 return -6;
1441 }
1442 if (!strcmp(mode, "fast")) {
1443 sort(event_table.begin(), event_table.end(),
1444 [](auto&a, auto& b){return a.time < b.time;});
1445 }
1446 LOG(DEBUG) << "event count:" << event_table.size();
1447
1448 current = 0;
1449
1450 return (int)(event_table.size());
1451}
1452
1453int create_timeline(const char* maps_file, const char* context_file, void** pp_timeline)
1454{
1455 static char g_init = 0;
1456
1457 if (0 == g_init) {
1458 DumpConfig cfg;
1459 load_config(cfg);
1460 init_log(cfg, "emdv.log");
1461 }
1462 ++g_init;
1463
1464 Timeline* timeline = new Timeline();
1465 int ret = timeline->open_trace(maps_file, context_file);
1466
1467 if (ret > 0) *pp_timeline = timeline;
1468
1469 return ret;
1470}
1471
1472int destroy_timeline(void* timeline)
1473{
1474 Timeline* t = (reinterpret_cast<Timeline*>(timeline));
1475 if (t) {
1476 delete t;
1477 }
1478
1479 return 0;
1480}
1481
1482int dump_raw_event(const char* context_file)
1483{
1484 char mode[5];
1485 TraceReader ctx_reader;
1486 vector<EventEntry> event_table;
1487
1488 if (!ctx_reader.Open(context_file, true)) {
1489 return -1;
1490 }
1491
1492 // Always check the system info first, as that allows us to tell whether
1493 // this is a minidump file that is compatible with our converter.
1494 bool ok = ParseSystemInfo(&ctx_reader, true, mode);
1495 if (!ok) {
1496 LOG(ERROR) << "Invalid system info head in context file";
1497 return -2;
1498 }
1499
1500 ctx_reader.BuildEventTable(&event_table);
1501 if (event_table.size() < 1) {
1502 LOG(ERROR) << "Invalid context file.";
1503 return -3;
1504 }
1505
1506 for (auto& e : event_table) {
1507 printf("%s\n", get_event_name(e.type));
1508 }
1509
1510 return 0;
1511}
1512
1513const EventEntry* get_event_pointer(void* timeline)
1514{
1515 Timeline* t = (reinterpret_cast<Timeline*>(timeline));
1516 if (t) {
1517 return &t->event_table[0];
1518 }
1519
1520 return nullptr;
1521}
1522
1523int get_event(void* timeline, int index, EventEntry* out)
1524{
1525 Timeline* t = (reinterpret_cast<Timeline*>(timeline));
1526 if (t) {
1527 const EventEntry& entry = t->event_table.at(index);
1528 out->time = entry.time;
1529 out->duration = entry.duration;
1530 out->type = entry.type;
1531 out->thread_num = entry.thread_num;
1532 out->tid = entry.tid;
1533 out->extra_size = entry.extra_size;
1534 out->offset = entry.offset;
1535 out->syscall_result = entry.syscall_result;
1536 }
1537
1538 return 0;
1539}
1540
1541
1542#if defined(__x86_64__)
1543#include "./x86_64/syscall_param.h"
1544#elif defined(__mips64)
1545#include "./mips64/syscall_param.h"
1546#elif defined(__sw_64)
1547#include "./sw64/syscall_param.h"
1548#elif defined(__aarch64__)
1549#include "./aarch64/syscall_param.h"
1550#else
1551#error need define new arch implement
1552#endif
1553
1554static int get_syscall_ptr_args(USER_REGS* regs,
1555 unsigned char flags, unsigned char args_no, uintptr_t* out)
1556{
1557 uintptr_t args[6];
1558 int count = 0;
1559 // parameter is void*, size indicate in next parameter
1560 bool size_indicate_by_next = flags & 0x80;
1561
1562#if defined(__x86_64__)
1563 // The kernel interface uses: %rdi, %rsi, %rdx, %r10, %r8 and %r9."
1564 args[0] = regs->rdi;
1565 args[1] = regs->rsi;
1566 args[2] = regs->rdx;
1567 args[3] = regs->r10;
1568 args[4] = regs->r8;
1569 args[5] = regs->r9;
1570#elif defined(__mips__) || defined(__mips64)
1571 //FIXME: The mips/o32 system call convention passes arguments 5 through 8 on the user
1572 //stack.
1573 args[0] = regs->a0;
1574 args[1] = regs->a1;
1575 args[2] = regs->a2;
1576 args[3] = regs->a3;
1577 args[4] = regs->a4;
1578 args[5] = regs->a5;
1579#elif defined(__sw_64)
1580 args[0] = regs->a0;
1581 args[1] = regs->a1;
1582 args[2] = regs->a2;
1583 args[3] = regs->a3;
1584 args[4] = regs->a4;
1585 args[5] = regs->a5;
1586#elif defined(__aarch64__)
1587 args[0] = regs->x0;
1588 args[1] = regs->x1;
1589 args[2] = regs->x2;
1590 args[3] = regs->x3;
1591 args[4] = regs->x4;
1592 args[5] = regs->x5;
1593#else
1594#error Not implment
1595#endif
1596
1597 for (unsigned char i = 0; i < args_no; ++i) {
1598 if (flags & 1) {
1599 uintptr_t addr = args[i];
1600 if (addr > 0) {
1601 if (size_indicate_by_next) {
1602 if (args[i+1] > 0) {
1603 out[count++] = addr;
1604 }
1605 break;
1606 }
1607 else {
1608 out[count++] = addr; // Null terminate string
1609 }
1610 }
1611 else {
1612 out[count++] = 1; //indicate null ptr
1613 }
1614 }
1615 flags >>= 1;
1616 }
1617
1618 return count;
1619}
1620
1621static char* load_memory(string& stack, uintptr_t stack_start,
1622 uintptr_t* str_ptr, int* count, char* walk, char* walk_end)
1623{
1624 int done = 0;
1625 int todo = *count;
1626 uintptr_t stack_end = stack_start + stack.size();
1627
1628 if (todo >= 6) return walk; // not init
1629
1630 for (int i = 0; i < 6; ++i) {
1631 if (0 == str_ptr[i]) continue; // has done
1632
1633 if (1 == str_ptr[i]) {
1634 strcpy(walk, "null");
1635 walk += 4;
1636 *walk++ = '\n';
1637 *walk = 0;
1638 ++done;
1639 str_ptr[i] = 0; // has loaded.
1640 continue;
1641 }
1642
1643 if (stack_start <= str_ptr[i] && stack_end > str_ptr[i]) {
1644 const char* ptr = stack.data() + str_ptr[i] - stack_start;
1645 const char* ptr_end = stack.data() + stack.size();
1646 while (*ptr > 0 &&
1647 *ptr != '\n' && // limited one line
1648 ptr < ptr_end &&
1649 walk < walk_end) {
1650 *walk++ = *ptr++;
1651 }
1652 *walk++ = '\n';
1653 *walk = 0;
1654 ++done;
1655 str_ptr[i] = 0; // has loaded.
1656 }
1657 }
1658
1659 *count = todo - done;
1660
1661 return walk;
1662}
1663
1664int get_event_extra_info(void* timeline, int index, char* buf, int buf_size)
1665{
1666 Timeline* t = (reinterpret_cast<Timeline*>(timeline));
1667 if (!t) {
1668 return 0;
1669 }
1670
1671 const EventEntry& entry = t->event_table.at(index);
1672 if (buf && buf_size > entry.extra_size) {
1673 t->ctx_reader.Seek(entry.offset + sizeof(EventHead));
1674 t->ctx_reader.ReadBlock(buf, entry.extra_size);
1675 buf[entry.extra_size] = 0;
1676 }
1677
1678 if (entry.type >= DUMP_REASON_signal) {
1679 return entry.extra_size;
1680 }
1681
1682 // syscall evnet, load context of char* parameter
1683 int syscall_no = entry.type - __NR_Linux;
1684 int flags = syscall_param_flags[2*syscall_no];
1685 if (!flags) {
1686 return 0;
1687 }
1688
1689 // load register
1690 t->ctx_reader.Seek(entry.offset + sizeof(EventHead));
1691 if (entry.extra_size > 0) {
1692 // delay load them in get_event_extra_info
1693 t->ctx_reader.Seek(t->ctx_reader.Current() + entry.extra_size);
1694 }
1695
1696 USER_REGS regs;
1697 USER_FPREGS fpregs;
1698 string stack;
1699 uintptr_t stack_start;
1700 uintptr_t str_ptr[6];
1701 int str_ptr_count = 6;
1702 char* walk = buf;
1703 char* walk_end = buf + buf_size - 2;
1704
1705 for (int i = 0; i < entry.thread_num && str_ptr_count > 0; ++i) {
1706 uint32_t tid = 0;
1707 t->ctx_reader.ReadValue<uint32_t>(&tid);
1708 t->ctx_reader.ReadBlock(&regs, sizeof(regs));
1709 t->ctx_reader.ReadBlock(&fpregs, sizeof(fpregs));
1710
1711 string tls; t->ctx_reader.ReadBlock(tls); //skip tls data
1712
1713 t->ctx_reader.ReadValue<uintptr_t>(&stack_start);
1714 t->ctx_reader.ReadBlock(stack);
1715
1716 if ((tid & 0xffff) == entry.tid) {
1717 memset(str_ptr, 0, sizeof(str_ptr));
1718 str_ptr_count = get_syscall_ptr_args(&regs, flags,
1719 syscall_param_flags[2*syscall_no + 1], &str_ptr[0]);
1720 }
1721
1722 walk = load_memory(stack, stack_start,
1723 str_ptr, &str_ptr_count, walk, walk_end);
1724 }
1725
1726 // search heap block
1727 uint32_t heap_count = 0;
1728 t->ctx_reader.ReadValue<uint32_t>(&heap_count);
1729 for (uint32_t i = 0; i < heap_count && str_ptr_count > 0; ++i) {
1730 t->ctx_reader.ReadValue<uintptr_t>(&stack_start);
1731 t->ctx_reader.ReadBlock(stack);
1732 if (!stack.empty()) {
1733 walk = load_memory(stack, stack_start,
1734 str_ptr, &str_ptr_count, walk, walk_end);
1735 }
1736 }
1737
1738 return (walk - buf);
1739}
1740
1741int generate_coredump(void* timeline, int index, const char* corefile, int verbose)
1742{
1743 Timeline* t = (reinterpret_cast<Timeline*>(timeline));
1744 if (t) {
1745 return t->generate_coredump(index, corefile, verbose);
1746 }
1747
1748 return 0;
1749}
1750
1751static void __attribute__((constructor)) init_process(void) {
1752}
1753