1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14
15 #include "ebpf_converter.h"
16
17 #include <algorithm>
18 #include <cstring>
19 #include <cxxabi.h>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <iostream>
23 #include <unistd.h>
24 #include <memory>
25 #include <securec.h>
26
27 #define CHK(expr) \
28 do { \
29 if (!(expr)) { \
30 std::cout << __FUNCTION__ << ": "; \
31 std::cout << __LINE__ << ", "; \
32 std::cout << "read data failed" << std::endl; \
33 return; \
34 } \
35 } while (0)
36
37 namespace OHOS {
38 namespace EBPF_TOOLS {
39 namespace {
40 constexpr uint8_t HEADER_MAGIC = 8;
41 constexpr uint8_t ARGS_NUM = 4;
42 constexpr uint8_t SYM_ENT_LEN_64 = 24;
43 constexpr uint8_t SYM_ENT_LEN_32 = 16;
44 constexpr uint8_t STT_FUNC = 2;
45 constexpr uint8_t EVENT_FS_FIXED_SIZE = 64;
46 constexpr uint8_t EVENT_MAPS_FIXED_SIZE = 36;
47 constexpr uint8_t EVENT_SYM_FIXED_SIZE = 36;
48 constexpr uint8_t EVENT_MEM_FIXED_SIZE = 88;
49 constexpr uint8_t EVENT_STR_FIXED_SIZE = 40;
50 constexpr uint8_t EVENT_BIO_FIXED_SIZE = 88;
51 } // namespace
EbpfConverter(const std::string & inputPath,const std::string & outPath)52 EbpfConverter::EbpfConverter(const std::string& inputPath, const std::string& outPath)
53 : inputPath_(inputPath), outputPath_(outPath) {}
54
StartParsing()55 void EbpfConverter::StartParsing()
56 {
57 if (access(inputPath_.c_str(), R_OK) != 0) {
58 std::cout << "the input file path is invalid" << std::endl;
59 return;
60 }
61 fd_ = open(inputPath_.c_str(), O_RDONLY);
62 if (fd_ == -1) {
63 std::cout << "open " << inputPath_ << " failed" << std::endl;
64 return;
65 }
66 char magic[HEADER_MAGIC + 1];
67 (void)memset_s(magic, sizeof(magic), 0, sizeof(magic));
68 CHK(Read(reinterpret_cast<void*>(&magic), sizeof(magic) - 1));
69 outData_ << "magic: " << magic << '\n';
70 HeaderDataItem header = {};
71 CHK(Read(reinterpret_cast<void*>(&header), sizeof(header)));
72 outData_ << "headSize: " << header.headSize << '\n' << "version: " << header.version << '\n'
73 << "clock: " << header.clock << '\n' << "cmdLineLen: " << header.cmdLineLen;
74
75 char cmdline[header.cmdLineLen + 1];
76 if (header.cmdLineLen > 0) {
77 CHK(Read(reinterpret_cast<void*>(cmdline), sizeof(cmdline) - 1));
78 outData_ << "\ncmdline: " << cmdline << '\n';
79 }
80 fileSize_ = lseek(fd_, header.headSize, SEEK_SET);
81
82 FILE *file = fopen(outputPath_.c_str(), "w");
83 if (file == nullptr) {
84 std::cout << "create " << outputPath_ << " failed" << std::endl;
85 return;
86 }
87
88 while (true) {
89 uint32_t type = 0;
90 if (read(fd_, reinterpret_cast<void*>(&type), sizeof(type)) <= 0) {
91 break;
92 }
93 switch (type) {
94 case MAPSTRACE: {
95 EventMapsParsing();
96 break;
97 }
98 case SYMBOLTRACE: {
99 SymbolInfoParsing();
100 break;
101 }
102 case FSTRACE: {
103 EventFsParsing();
104 break;
105 }
106 case PFTRACE: {
107 EventMemParsing();
108 break;
109 }
110 case BIOTRACE: {
111 EventBIOParsing();
112 break;
113 }
114 case STRTRACE: {
115 EventStrParsing();
116 break;
117 }
118 default: {
119 std::cout << "Invalid type: " << type << std::endl;
120 close(fd_);
121 fd_ = -1;
122 fileSize_ = 0;
123 fclose(file);
124 return;
125 }
126 }
127 }
128 close(fd_);
129 fd_ = -1;
130 fileSize_ = 0;
131 std::cout << "Data read successfully, output..." << std::endl;
132 size_t len = outData_.str().size();
133 if (fwrite(outData_.str().c_str(), 1, len, file) != len) {
134 std::cout << "write data failed" << std::endl;
135 fclose(file);
136 return;
137 }
138 fclose(file);
139 std::cout << "Data parsing success..." << std::endl;
140 }
141
Read(void * buffer,size_t size)142 bool EbpfConverter::Read(void* buffer, size_t size)
143 {
144 ssize_t ret = read(fd_, buffer, size);
145 if (ret <= 0) {
146 close(fd_);
147 fd_ = -1;
148 fileSize_ = 0;
149 return false;
150 }
151 return true;
152 }
153
EventFsParsing()154 void EbpfConverter::EventFsParsing()
155 {
156 lseek(fd_, fileSize_, SEEK_SET);
157 EventFs efs = {};
158 CHK(Read(reinterpret_cast<void*>(&efs), EVENT_FS_FIXED_SIZE));
159 uint64_t args[ARGS_NUM] = { 0 };
160 CHK(Read(reinterpret_cast<void*>(args), sizeof(args)));
161 efs.args = std::vector<uint64_t>(args, args + ARGS_NUM);
162 CHK(Read(reinterpret_cast<void*>(&efs.comm), sizeof(efs.comm)));
163 uint64_t userIPs[efs.nrUserIPs];
164 if (efs.nrUserIPs > 0) {
165 CHK(Read(reinterpret_cast<void*>(userIPs), sizeof(userIPs)));
166 efs.userIPs = std::vector<uint64_t>(userIPs, userIPs + efs.nrUserIPs);
167 }
168 fileSize_ = lseek(fd_, 0, SEEK_CUR);
169
170 outData_ << "\nEventFs:\n"
171 << " type: " << efs.tag << '\n'
172 << " len: " << efs.len << '\n'
173 << " pid: " << efs.pid << '\n'
174 << " tid: " << efs.tid << '\n'
175 << " tracerName: " << efs.tracerName << '\n'
176 << " start: " << efs.start << '\n'
177 << " end: " << efs.end << '\n'
178 << " typeName: " << efs.typeName << '\n'
179 << " ret: " << efs.ret << '\n'
180 << " nrUserIPs: " << efs.nrUserIPs << '\n'
181 << " type: " << efs.type << '\n'
182 << " args: ";
183 for (const auto &arg : efs.args) {
184 outData_ << arg << ", ";
185 }
186
187 outData_ << "\n userIPs:\n";
188 for (const auto &ip : efs.userIPs) {
189 if (ip == 0) {
190 continue;
191 }
192 outData_ << " 0x" << std::hex << ip << std::dec << ":";
193 auto itm = GetSymbolInfo(efs.pid, ip);
194 outData_ << "\n vAddr_: 0x" << std::hex << vAddr_[ip] << std::dec << '\n';
195 for (const auto &sym : itm.second) {
196 outData_ << " symbol: " << sym << '\n';
197 }
198 outData_ << " fileName: " << itm.first << '\n';
199 }
200 outData_ << " comm: " << std::string(efs.comm) << '\n';
201 }
202
EventMapsParsing()203 void EbpfConverter::EventMapsParsing()
204 {
205 lseek(fd_, fileSize_, SEEK_SET);
206 EventMaps maps = {};
207 CHK(Read(reinterpret_cast<void*>(&maps), EVENT_MAPS_FIXED_SIZE));
208 char fileName[maps.fileNameLen + 1];
209 (void)memset_s(fileName, sizeof(fileName), 0, sizeof(fileName));
210 if (maps.fileNameLen > 0) {
211 CHK(Read(reinterpret_cast<void*>(fileName), sizeof(fileName) - 1));
212 maps.fileName = fileName;
213 }
214 auto iter = maps_.find(maps.pid);
215 if (iter == maps_.end()) {
216 maps_[maps.pid] = std::vector<EventMaps>({ maps });
217 } else {
218 iter->second.push_back(maps);
219 }
220 fileSize_ = lseek(fd_, 0, SEEK_CUR);
221
222 // put output
223 outData_ << "\nEventMaps:\n"
224 << " type: " << maps.type << '\n'
225 << " len: " << maps.len << '\n'
226 << " start: 0x" << std::hex << maps.start << std::dec << '\n'
227 << " end: 0x" << std::hex << maps.end << std::dec << '\n'
228 << " offset: 0x" << std::hex << maps.offset << std::dec << '\n'
229 << " pid: " << maps.pid << '\n'
230 << " fileNameLen: " << maps.fileNameLen << '\n'
231 << " fileName: " << maps.fileName << '\n';
232 }
233
SymbolInfoParsing()234 void EbpfConverter::SymbolInfoParsing()
235 {
236 lseek(fd_, fileSize_, SEEK_SET);
237 uint64_t strAddr = 0;
238 uint64_t symAddr = 0;
239 SymbolInfo info = {};
240 CHK(Read(reinterpret_cast<void*>(&info), EVENT_SYM_FIXED_SIZE));
241
242 info.strTab = new char[info.strTabLen + 1];
243 if (memset_s(info.strTab, info.strTabLen + 1, 0, info.strTabLen + 1) != EOK) {
244 std::cout << "memset string table failed" << std::endl;
245 return;
246 }
247 if (info.strTabLen > 0) {
248 auto ret = lseek(fd_, 0, SEEK_CUR);
249 if (ret >= 0) {
250 strAddr = (uint64_t)ret;
251 }
252 CHK(Read(reinterpret_cast<void*>(info.strTab), info.strTabLen));
253 }
254 info.symTab = new char[info.symTabLen + 1];
255 if (memset_s(info.symTab, info.symTabLen + 1, 0, info.symTabLen + 1) != EOK) {
256 std::cout << "memset symbol table failed" << std::endl;
257 return;
258 }
259 if (info.symTabLen > 0) {
260 auto ret = lseek(fd_, 0, SEEK_CUR);
261 if (ret >= 0) {
262 symAddr = (uint64_t)ret;
263 }
264 CHK(Read(reinterpret_cast<void*>(info.symTab), info.symTabLen));
265 }
266 char fileName[info.fileNameSize + 1];
267 (void)memset_s(fileName, sizeof(fileName), 0, sizeof(fileName));
268 if (info.fileNameSize > 0) {
269 CHK(Read(reinterpret_cast<void*>(&fileName), sizeof(fileName) - 1));
270 info.fileName = fileName;
271 }
272 record_[std::string(fileName)] = Record(fileName, symAddr, strAddr);
273 symbolInfo_[fileName] = info;
274 fileSize_ = lseek(fd_, 0, SEEK_CUR);
275
276 // put output
277 outData_ << "\nSymbolInfo:\n"
278 << " tag: " << info.type << '\n'
279 << " len: " << info.len << '\n'
280 << " textVaddr: 0x" << std::hex << info.textVaddr << std::dec << '\n'
281 << " textOffset: 0x" << std::hex << info.textOffset << std::dec << '\n'
282 << " strTabLen: 0x" << std::hex << info.strTabLen << std::dec <<'\n'
283 << " symTabLen: 0x" << std::hex << info.symTabLen << std::dec << '\n'
284 << " fileNameSize: " << info.fileNameSize << '\n'
285 << " fileName: " << info.fileName << '\n'
286 << " strTabAddr: 0x" << std::hex << record_[info.fileName].strTabAddr << std::dec << '\n'
287 << " symTabAddr: 0x" << std::hex << record_[info.fileName].symTabAddr << std::dec << '\n';
288 }
289
EventMemParsing()290 void EbpfConverter::EventMemParsing()
291 {
292 lseek(fd_, fileSize_, SEEK_SET);
293 EventMem mem = {};
294 CHK(Read(reinterpret_cast<void*>(&mem), EVENT_MEM_FIXED_SIZE));
295 uint64_t userIPs[mem.nips];
296 if (mem.nips > 0) {
297 CHK(Read(reinterpret_cast<void*>(&userIPs), sizeof(userIPs)));
298 mem.userIPs = std::vector<uint64_t>(userIPs, userIPs + mem.nips);
299 }
300 fileSize_ = lseek(fd_, 0, SEEK_CUR);
301
302 outData_ << "\nEventMem:\n"
303 << " tag: " << mem.tag << '\n'
304 << " len: " << mem.len << '\n'
305 << " pid: " << mem.pid << '\n'
306 << " tid: " << mem.tid << '\n'
307 << " tagName: " << mem.tagName << '\n'
308 << " start: " << mem.start << '\n'
309 << " end: " << mem.end << '\n'
310 << " typeName: " << mem.typeName << '\n'
311 << " addr: " << mem.addr << '\n'
312 << " size: " << mem.size << '\n'
313 << " nips: " << mem.nips << '\n'
314 << " type: " << mem.type << '\n'
315 << " comm: " << mem.comm << '\n'
316 << " userIPs:\n";
317 for (const auto &ip : mem.userIPs) {
318 if (ip == 0) {
319 continue;
320 }
321 outData_ << " 0x" << std::hex << ip << std::dec << ":";
322 auto itm = GetSymbolInfo(mem.pid, ip);
323 outData_ << "\n vAddr_: 0x" << std::hex << vAddr_[ip] << std::dec << '\n';
324 for (const auto &sym : itm.second) {
325 outData_ << " symbol: " << sym << '\n';
326 }
327 outData_ << " fileName: " << itm.first << '\n';
328 }
329 }
330
EventStrParsing()331 void EbpfConverter::EventStrParsing()
332 {
333 lseek(fd_, fileSize_, SEEK_SET);
334 EventStr str = {};
335 CHK(Read(reinterpret_cast<void*>(&str), EVENT_STR_FIXED_SIZE));
336 char fileName[str.strLen + 1];
337 if (str.strLen > 0) {
338 CHK(Read(reinterpret_cast<void*>(&fileName), sizeof(fileName) - 1));
339 str.fileName = fileName;
340 }
341 str_.push_back(str);
342 int32_t newPos = static_cast<int32_t>(str.len - 32 - str.strLen);
343 fileSize_ = lseek(fd_, lseek(fd_, 0, SEEK_CUR) + newPos, SEEK_SET);
344
345 outData_ << "\nEventStr:\n"
346 << " tag: " << str.tag << '\n'
347 << " len: " << str.len << '\n'
348 << " pid: " << str.pid << '\n'
349 << " tid: " << str.tid << '\n'
350 << " start: " << str.start << '\n'
351 << " srcTracer: " << str.srcTracer << '\n'
352 << " srcType: " << str.srcType << '\n'
353 << " strLen: " << str.strLen << '\n'
354 << " fileName: " << str.fileName << '\n';
355 }
356
EventBIOParsing()357 void EbpfConverter::EventBIOParsing()
358 {
359 lseek(fd_, fileSize_, SEEK_SET);
360 EventBIO bio = {};
361 CHK(Read(reinterpret_cast<void*>(&bio), EVENT_BIO_FIXED_SIZE));
362 uint64_t userIPs[bio.nips];
363 if (bio.nips > 0) {
364 CHK(Read(reinterpret_cast<void*>(userIPs), sizeof(userIPs)));
365 bio.userIPs = std::vector<uint64_t>(userIPs, userIPs + bio.nips);
366 }
367 fileSize_ = lseek(fd_, 0, SEEK_CUR);
368
369 outData_ << "\nEventBIO:\n"
370 << " tag: " << bio.tag << '\n'
371 << " len: " << bio.len << '\n'
372 << " pid: " << bio.pid << '\n'
373 << " tid: " << bio.tid << '\n'
374 << " comm: " << bio.comm << '\n'
375 << " start: " << bio.start << '\n'
376 << " end: " << bio.end << '\n'
377 << " prio: " << bio.prio << '\n'
378 << " size: " << bio.size << '\n'
379 << " type: " << bio.type << '\n'
380 << " typeName: " << bio.typeName << '\n';
381
382 for (const auto &ip : bio.userIPs) {
383 if (ip == 0) {
384 continue;
385 }
386 outData_ << " 0x" << std::hex << ip << std::dec << ":";
387 auto itm = GetSymbolInfo(bio.pid, ip);
388 outData_ << "\n vAddr_: 0x" << std::hex << vAddr_[ip] << std::dec << '\n';
389 for (const auto &sym : itm.second) {
390 outData_ << " symbol: " << sym << '\n';
391 }
392 outData_ << " fileName: " << itm.first << '\n';
393 }
394 }
395
GetSymbolInfo(uint64_t pid,uint64_t ip)396 std::pair<std::string, std::vector<std::string>> EbpfConverter::GetSymbolInfo(uint64_t pid, uint64_t ip)
397 {
398 auto mapIter = maps_.find(pid);
399 if (mapIter == maps_.end()) {
400 return std::pair<std::string, std::vector<std::string>>();
401 }
402
403 std::string fileName;
404 uint64_t start = 0;
405 uint32_t offset = 0;
406 std::any_of(mapIter->second.begin(), mapIter->second.end(), [&](const EventMaps &maps) {
407 if (ip >= maps.start && ip <= maps.end) {
408 fileName = maps.fileName;
409 start = maps.start;
410 offset = maps.offset;
411 return true;
412 }
413 return false;
414 });
415 if (fileName.empty()) {
416 return std::pair<std::string, std::vector<std::string>>();
417 }
418
419 auto symItem = symbolInfo_.find(fileName);
420 if (symItem == symbolInfo_.end()) {
421 return std::pair<std::string, std::vector<std::string>>();
422 }
423 uint64_t vaddr = ip - start + offset - symItem->second.textOffset + symItem->second.textVaddr;
424 vAddr_[ip] = vaddr;
425 std::pair<std::string, std::vector<std::string>> symbolInfos;
426 symbolInfos.first = fileName;
427 if (symItem->second.symEntlen == SYM_ENT_LEN_64) {
428 uint32_t count = 0;
429 while (count < symItem->second.symTabLen) {
430 Elf64_Sym sym;
431 if (memcpy_s(&sym, sizeof(sym), symItem->second.symTab + count, SYM_ENT_LEN_64) != EOK) {
432 std::cout << "copy symTab failed" << std::endl;
433 return std::pair<std::string, std::vector<std::string>>();
434 }
435 if (vaddr >= sym.st_value &&
436 vaddr <= sym.st_value + sym.st_size &&
437 (sym.st_info & STT_FUNC) &&
438 sym.st_value != 0) {
439 char *ret = abi::__cxa_demangle(static_cast<char *>(symItem->second.strTab + sym.st_name),
440 nullptr, nullptr, nullptr);
441 ret == nullptr ? symbolInfos.second.push_back(std::string(symItem->second.strTab + sym.st_name))
442 : symbolInfos.second.push_back(std::string(ret));
443 }
444 count += SYM_ENT_LEN_64;
445 }
446 } else if (symItem->second.symEntlen == SYM_ENT_LEN_32) {
447 uint32_t count = 0;
448 while (count < symItem->second.symTabLen) {
449 Elf32_Sym sym;
450 if (memcpy_s(&sym, sizeof(sym), symItem->second.strTab + count, SYM_ENT_LEN_32) != EOK) {
451 std::cout << "copy symTab failed" << std::endl;
452 return std::pair<std::string, std::vector<std::string>>();
453 }
454 if (vaddr >= sym.st_value &&
455 vaddr <= sym.st_value + sym.st_size &&
456 (sym.st_info & STT_FUNC) &&
457 sym.st_value != 0) {
458 char *ret = abi::__cxa_demangle(symItem->second.strTab + sym.st_name, nullptr, nullptr, nullptr);
459 ret == nullptr ? symbolInfos.second.push_back(std::string(symItem->second.strTab + sym.st_name))
460 : symbolInfos.second.push_back(std::string(ret));
461 }
462 count += SYM_ENT_LEN_32;
463 }
464 } else {
465 std::cout << "Invalid symEntlen" << std::endl;
466 }
467 return symbolInfos;
468 }
469 } // EBPF_TOOLS
470 } // OHOS
471
main(int32_t argc,char * argv[])472 int32_t main(int32_t argc, char* argv[])
473 {
474 std::ios::sync_with_stdio(false);
475 std::cin.tie(nullptr);
476 struct option long_options[] = {
477 {"input", required_argument, nullptr, 'i'},
478 {"out", required_argument, nullptr, 'o'},
479 {"help", no_argument, nullptr, 'h'},
480 {0, 0, 0, 0}
481 };
482 int32_t opt;
483 std::string inputPath, outputPath;
484 while ((opt = getopt_long(argc, argv, "i:o:h", long_options, nullptr)) != -1) {
485 switch (opt) {
486 case 'i': {
487 inputPath = optarg;
488 continue;
489 }
490 case 'o': {
491 outputPath = optarg;
492 continue;
493 }
494 case 'h': {
495 std::cout << " --input" << " " << "-i" << " :" << "input file name" << std::endl;
496 std::cout << " --out" << " " << "-o" << " :" << "out file name" << std::endl;
497 break;
498 }
499 default: {
500 std::cout << "Parameter error" << std::endl;
501 break;
502 }
503 }
504 }
505 OHOS::EBPF_TOOLS::EbpfConverter ec(inputPath, outputPath);
506 ec.StartParsing();
507 return 0;
508 }