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
23static 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
35TraceStream::TraceStream(void)
36{
37}
38
39TraceStream::~TraceStream(void)
40{
41 if (mem_file > 0) close(mem_file);
42}
43
44bool 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 */
75int 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
112int TraceStream::write(void* buf, int size)
113{
114 return zstd->write(buf, size);
115}
116
117int 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
133int TraceStream::write(EventHead* head, void* extra_buf)
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
150int 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
205int 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