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 <sys/stat.h> |
7 | #include <unistd.h> |
8 | #include <fcntl.h> |
9 | #include <string.h> |
10 | #include <errno.h> |
11 | #include <stdio.h> |
12 | #include <stdlib.h> |
13 | |
14 | #include <iostream> |
15 | #include <memory> |
16 | #include <map> |
17 | #include <string> |
18 | |
19 | #include "event_man.h" |
20 | #include "trace_writer.h" |
21 | #include "easylogging++.h" |
22 | |
23 | static int open_mem_file(pid_t pid) |
24 | { |
25 | int fd = 0; |
26 | char* tmp_path = NULL; |
27 | if (asprintf(&tmp_path, "/proc/%i/mem" , pid) != -1) { |
28 | fd = open(tmp_path, O_RDONLY); |
29 | free(tmp_path); |
30 | } |
31 | |
32 | return fd; |
33 | } |
34 | |
35 | TraceStream::TraceStream(void) |
36 | { |
37 | } |
38 | |
39 | TraceStream::~TraceStream(void) |
40 | { |
41 | if (mem_file > 0) close(mem_file); |
42 | } |
43 | |
44 | bool TraceStream::init(pid_t pid2, int buffer_size, ZstdWriter* writer) |
45 | { |
46 | pid = pid2; |
47 | pread_error = 0; |
48 | |
49 | mem_file = -1; |
50 | zstd = writer; |
51 | |
52 | buffer.resize(buffer_size); |
53 | |
54 | return true; |
55 | } |
56 | |
57 | /* binary layout for one event, always 4bytes align: |
58 | time(sizeof(struct timespec)), event_type(2byte), thread_num(2byte), |
59 | current_tid(2byte), event_extra_size(2byte)[,event_extra_data] |
60 | { |
61 | tid(2byte), cpu_context_size(2byte)[, user_regs_struct+user_fpregs_struct], |
62 | tls_size(4byte)[, tls_data] |
63 | stack_addr(uintptr_t), stack_length(4byte)[,stack_data] |
64 | } |
65 | |
66 | ... |
67 | |
68 | { |
69 | tid(2byte), cpu_context_size(2byte)[, user_regs_struct+user_fpregs_struct], |
70 | tls_size(4byte)[, tls_data] |
71 | stack_addr(uintptr_t), stack_length(4byte)[,stack_data] |
72 | } |
73 | heap_count(4byte)[, [addr(uintptr_t)+size(4byte)+data], ...] |
74 | */ |
75 | int TraceStream::write(ThreadContext* block) |
76 | { |
77 | int total = 0; |
78 | uint16_t cpu_size = 0; |
79 | uint32_t tls_size = 0; |
80 | |
81 | total += zstd->write(&block->tid, sizeof(block->tid)); |
82 | |
83 | cpu_size = sizeof(block->regs) + sizeof(block->fpregs); |
84 | total += zstd->write(&cpu_size, sizeof(cpu_size)); |
85 | total += zstd->write(&block->regs, sizeof(block->regs)); |
86 | total += zstd->write(&block->fpregs, sizeof(block->fpregs)); |
87 | |
88 | tls_size = 0; // block->tls.size()*sizeof(struct user_desc); |
89 | total += zstd->write(&tls_size, sizeof(tls_size)); |
90 | if (tls_size > 0) { |
91 | total += zstd->write(&block->tls[0], tls_size); |
92 | } |
93 | |
94 | // stack |
95 | if (mem_file <= 0) { |
96 | mem_file = open_mem_file(pid); |
97 | if (mem_file <= 0) { |
98 | // failed to read stack |
99 | int size = 0; |
100 | total += zstd->write(&block->stack.start, |
101 | sizeof(block->stack.start)); |
102 | total += zstd->write(&size, sizeof(size)); |
103 | return total; |
104 | } |
105 | } |
106 | |
107 | total += write(&block->stack); |
108 | |
109 | return total; |
110 | } |
111 | |
112 | int TraceStream::write(void* buf, int size) |
113 | { |
114 | return zstd->write(buf, size); |
115 | } |
116 | |
117 | int TraceStream::read(uintptr_t addr, void* buf, int size) |
118 | { |
119 | if (mem_file <= 0) { |
120 | mem_file = open_mem_file(pid); |
121 | if (mem_file <= 0) { |
122 | return -1; |
123 | } |
124 | } |
125 | |
126 | int got = pread64(mem_file, buf, size, addr); |
127 | if (got < size) { |
128 | log_pread_error(errno, addr, got, size); |
129 | } |
130 | return got; |
131 | } |
132 | |
133 | int TraceStream::write(EventHead* head, void* ) |
134 | { |
135 | static_assert(sizeof(head->cur_time) + |
136 | sizeof(head->reason) + |
137 | sizeof(head->thread_num) + |
138 | sizeof(head->current_tid) + |
139 | sizeof(head->extra_size) == sizeof(*head), |
140 | "struct EventHead aligned error!" ); |
141 | |
142 | int ret = zstd->write(head, sizeof(*head)); |
143 | if (head->extra_size > 0 && extra_buf) { |
144 | ret += zstd->write(extra_buf, head->extra_size); |
145 | } |
146 | |
147 | return ret; |
148 | } |
149 | |
150 | int TraceStream::write(VmSegment* segment) |
151 | { |
152 | int ret = 0; |
153 | // save address |
154 | assert((segment->start & 3 ) == 0); |
155 | zstd->write(&segment->start, sizeof(segment->start)); |
156 | |
157 | // save size |
158 | int size = segment->end - segment->start; |
159 | assert((size & 3 ) == 0); |
160 | |
161 | zstd->write(&size, sizeof(size)); |
162 | ret = size + sizeof(size) + sizeof(segment->start); |
163 | |
164 | // save data |
165 | int chunk_bytes = buffer.size(); |
166 | uintptr_t offset = segment->start; |
167 | |
168 | while (size > 0) { |
169 | if (size < chunk_bytes) chunk_bytes = size; |
170 | |
171 | /*FIXME: is need lock current segment memory page*/ |
172 | ssize_t got = pread64(mem_file, &buffer[0], chunk_bytes, offset); |
173 | if (got < chunk_bytes) { |
174 | log_pread_error(errno, offset, got, chunk_bytes); |
175 | if (got > 0) { |
176 | memset(&buffer[got], 0xcc, chunk_bytes - got); |
177 | zstd->write(&buffer[0], chunk_bytes); |
178 | memset(&buffer[0], 0xcc, got); |
179 | offset += chunk_bytes; |
180 | size -= chunk_bytes; |
181 | } |
182 | else { |
183 | memset(&buffer[0], 0xcc, chunk_bytes); |
184 | } |
185 | break; |
186 | } |
187 | |
188 | zstd->write(&buffer[0], chunk_bytes); |
189 | offset += chunk_bytes; |
190 | size -= chunk_bytes; |
191 | } |
192 | |
193 | while (size > 0) { |
194 | if (size < chunk_bytes) chunk_bytes = size; |
195 | |
196 | // fill 0xccc if pread failed |
197 | zstd->write(&buffer[0], chunk_bytes); |
198 | offset += chunk_bytes; |
199 | size -= chunk_bytes; |
200 | } |
201 | |
202 | return ret; |
203 | } |
204 | |
205 | int TraceStream::log_pread_error(int error, uintptr_t offset, int got, int req) |
206 | { |
207 | /* If an address is unmapped in the process, reading from the corresponding |
208 | * offset in the file returns EIO (Input/output error). |
209 | */ |
210 | if (error != 0 && got < 1) { |
211 | ++pread_error; |
212 | LOG(DEBUG) << "pread from " << pid |
213 | << " at " << HEX(offset) |
214 | << " failed, error=" << error; |
215 | } |
216 | else { |
217 | LOG(DEBUG) << "pread from " << pid |
218 | << " at " << HEX(offset) |
219 | << " got " << got |
220 | << " less required " << req; |
221 | } |
222 | |
223 | return 0; |
224 | } |
225 | |