1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. 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 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 *
15 * Description: FtraceParser class implements
16 */
17 #include "ftrace_parser.h"
18
19 #include <algorithm>
20 #include <cerrno>
21 #include <cstring>
22 #include <fcntl.h>
23 #include <fstream>
24 #include <regex>
25 #include <sstream>
26 #include <unistd.h>
27
28 #include "common.h"
29 #include "file_utils.h"
30 #include "securec.h"
31 #include "string_utils.h"
32
33 #ifdef HILOG_DEBUG
34 #undef HILOG_DEBUG
35 #endif
36
37 #define HILOG_DEBUG(LOG_CORE, fmt, ...) \
38 if (debugOn_) { \
39 PROFILER_LOG_INFO(LOG_CORE, ":DEBUG: " fmt, ##__VA_ARGS__); \
40 }
41
42 namespace {
43 using namespace OHOS::Developtools::Profiler;
44 constexpr unsigned RB_MISSED_EVENTS = (1uL << 31); // Flag when events were overwritten
45 constexpr unsigned RB_MISSED_STORED = (1 << 30); // Missed count stored at end
46 constexpr unsigned RB_MISSED_FLAGS = (RB_MISSED_EVENTS | RB_MISSED_STORED);
47
48 constexpr unsigned COL_IDX_NAME = 0;
49 constexpr unsigned COL_IDX_VALUE = 1;
50
51 constexpr unsigned TS_EXT_SHIFT = 27;
52 constexpr uint32_t INT_MAX_LEN = 10;
53
GetTimestampIncrements(uint64_t ext)54 inline uint64_t GetTimestampIncrements(uint64_t ext)
55 {
56 return ext << TS_EXT_SHIFT;
57 }
58 } // namespace
59
60 FTRACE_NS_BEGIN
FtraceParser()61 FtraceParser::FtraceParser()
62 {
63 PROFILER_LOG_INFO(LOG_CORE, "FtraceParser create!");
64 }
65
Init()66 bool FtraceParser::Init()
67 {
68 fixedCharArrayRegex_ = std::regex(R"(char \w+\[\d+\])");
69 flexDataLocArrayRegex_ = std::regex(R"(__data_loc [a-zA-Z_0-9 ]+\[\] \w+)");
70 if (FtraceFsOps::GetInstance().IsHmKernel()) {
71 return true;
72 }
73 std::string printkFormats = FtraceFsOps::GetInstance().GetPrintkFormats();
74 CHECK_TRUE(printkFormats.size() > 0, false, "read printk_formats failed!");
75 CHECK_TRUE(PrintkFormatsParser::GetInstance().Parse(printkFormats), false, "parse printk_formats failed");
76
77 std::string formatDesc = FtraceFsOps::GetInstance().GetPageHeaderFormat();
78 CHECK_TRUE(formatDesc.size() > 0, false, "read header_page failed!");
79 return ParseHeaderPageFormat(formatDesc);
80 }
81
~FtraceParser()82 FtraceParser::~FtraceParser()
83 {
84 PROFILER_LOG_INFO(LOG_CORE, "FtraceParser destroy!");
85 }
86
SetupEvent(const std::string& type, const std::string& name)87 bool FtraceParser::SetupEvent(const std::string& type, const std::string& name)
88 {
89 if (!SubEventParser<FtraceEvent>::GetInstance().IsSupport(name)) {
90 // no sub event parser found for event, so no need to parse format file
91 return false;
92 }
93
94 EventFormat format;
95 format.eventType = type;
96 format.eventName = name;
97 std::string desc = FtraceFsOps::GetInstance().GetEventDataFormat(type, name);
98 if (desc != "") {
99 CHECK_TRUE(ParseEventFormat(desc.data(), format), false, "parse %s/%s/format failed!",
100 type.c_str(), name.c_str());
101 CHECK_TRUE(SubEventParser<FtraceEvent>::GetInstance().SetupEvent(format),
102 false, "setup %s/%s failed!", type.c_str(), name.c_str());
103 CHECK_TRUE(SubEventParser<ProtoEncoder::FtraceEvent>::GetInstance().SetupEvent(format),
104 false, "setup pbzero %s/%s failed!", type.c_str(), name.c_str());
105 }
106 return true;
107 }
108
ParseHeaderPageFormat(const std::string& formatDesc)109 bool FtraceParser::ParseHeaderPageFormat(const std::string& formatDesc)
110 {
111 EventFormat format = {};
112 CHECK_TRUE(ParseEventFormat(formatDesc, format), false, "parse events/header_page failed!");
113
114 bool commitFound = false;
115 for (auto& field : format.fields) {
116 if (field.name == "timestamp") {
117 pageHeaderFormat_.timestamp = field;
118 } else if (field.name == "commit") {
119 pageHeaderFormat_.commit = field;
120 commitFound = true;
121 } else if (field.name == "overwrite") {
122 pageHeaderFormat_.overwrite = field;
123 }
124 }
125
126 CHECK_TRUE(commitFound, false, "commit field not found!");
127 return true;
128 }
129
GetHeaderPageCommitSize(void)130 int FtraceParser::GetHeaderPageCommitSize(void)
131 {
132 // return the size value of commit field read from events/header_page
133 return pageHeaderFormat_.commit.size;
134 }
135
ParseEventFormat(const std::string& formatDesc, EventFormat& format)136 bool FtraceParser::ParseEventFormat(const std::string& formatDesc, EventFormat& format)
137 {
138 std::string idLinePrefix = "ID:";
139 std::string fieldLinePrefix = "field:";
140 std::string printFmtLinePrefix = "print fmt:";
141
142 std::string line;
143 std::stringstream sin(formatDesc);
144 while (getline(sin, line)) {
145 line = StringUtils::Strip(line);
146 if (line.empty()) {
147 continue;
148 } else if (StringUtils::StartsWith(line, fieldLinePrefix)) {
149 ParseFieldFormat(line, format);
150 } else if (StringUtils::StartsWith(line, idLinePrefix)) {
151 auto idStr = line.substr(idLinePrefix.size() + 1);
152 format.eventId = static_cast<uint32_t>(atoi(idStr.c_str()));
153 }
154 }
155 CHECK_TRUE(format.fields.size() > 0, false, "ParseEventFormat failed!");
156 size_t lastFiledIndex = format.fields.size() > 1 ? format.fields.size() - 1 : 0;
157 format.eventSize = format.fields[lastFiledIndex].offset + format.fields[lastFiledIndex].size;
158 return true;
159 }
160
SplitNameFromTypeName(const std::string& typeName)161 static std::string SplitNameFromTypeName(const std::string& typeName)
162 {
163 std::string name;
164 if (typeName.size() > 0) { // split type and name
165 auto posT0 = typeName.rfind(" ");
166 std::string rightHalf = typeName.substr(posT0 + 1);
167 size_t dataIndex = rightHalf.size() > 1 ? rightHalf.size() - 1 : 0;
168 if (rightHalf[dataIndex] != ']') {
169 name = rightHalf;
170 } else {
171 std::string::size_type postT1 = rightHalf.rfind('[');
172 if (postT1 == std::string::npos) {
173 return "";
174 }
175 name = rightHalf.substr(0, postT1);
176 }
177 }
178 return name;
179 }
180
EraseNameFromTypeName(const std::string& typeName, const std::string& name)181 static std::string EraseNameFromTypeName(const std::string& typeName, const std::string& name)
182 {
183 std::string type;
184 if (name.size() > 0) { // erase name part from typeName
185 type = typeName;
186 auto pos = type.find(name);
187 type.replace(pos, name.size(), "");
188 type = StringUtils::Strip(type);
189 }
190 return type;
191 }
192
ParseCommonFiledIndex(CommonFiledIndex& commonIndex, const std::string& name, int index)193 static void ParseCommonFiledIndex(CommonFiledIndex& commonIndex, const std::string& name, int index)
194 {
195 if (name == "common_type") {
196 commonIndex.type = index;
197 } else if (name == "common_flags") {
198 commonIndex.flags = index;
199 } else if (name == "common_preempt_count") {
200 commonIndex.preemt = index;
201 } else if (name == "common_pid") {
202 commonIndex.pid = index;
203 }
204 }
205
ParseFieldFormat(const std::string& fieldLine, EventFormat& format)206 bool FtraceParser::ParseFieldFormat(const std::string& fieldLine, EventFormat& format)
207 {
208 FieldFormat fieldInfo;
209 std::string typeName;
210 std::string offsetStr;
211 std::string sizeStr;
212 std::string signedStr;
213
214 for (auto& part : StringUtils::Split(fieldLine, ";")) {
215 auto cols = StringUtils::Split(StringUtils::Strip(part), ":");
216 if (cols.size() < COL_IDX_VALUE) {
217 continue;
218 }
219 const auto& key = cols[COL_IDX_NAME];
220 if (key == "field") {
221 typeName = cols[COL_IDX_VALUE];
222 } else if (key == "offset") {
223 offsetStr = cols[COL_IDX_VALUE];
224 } else if (key == "size") {
225 sizeStr = cols[COL_IDX_VALUE];
226 } else if (key == "signed") {
227 signedStr = cols[COL_IDX_VALUE];
228 }
229 }
230
231 std::string name = SplitNameFromTypeName(typeName);
232 std::string type = EraseNameFromTypeName(typeName, name); // for field type
233 fieldInfo.name = name;
234 fieldInfo.typeName = typeName;
235 fieldInfo.offset = atoi(offsetStr.c_str());
236 fieldInfo.size = atoi(sizeStr.c_str());
237 fieldInfo.isSigned = atoi(signedStr.c_str());
238
239 ParseFieldType(type, fieldInfo);
240 ParseProtoType(fieldInfo);
241
242 if (StringUtils::StartsWith(name, "common_")) {
243 ParseCommonFiledIndex(format.commonIndex, name, static_cast<int>(format.commonFields.size()));
244 format.commonFields.push_back(fieldInfo);
245 } else {
246 format.fields.push_back(fieldInfo);
247 }
248 return true;
249 }
250
ParseSepcialIntType(FieldFormat& field, const std::string& type, const std::string& typeName)251 static bool ParseSepcialIntType(FieldFormat& field, const std::string& type, const std::string& typeName)
252 {
253 if (type == "bool") {
254 field.filedType = FIELD_TYPE_BOOL;
255 return true;
256 }
257
258 if (type == "ino_t" || type == "i_ino") {
259 if (field.size == sizeof(uint32_t)) {
260 field.filedType = FIELD_TYPE_INODE32;
261 return true;
262 } else if (field.size == sizeof(uint64_t)) {
263 field.filedType = FIELD_TYPE_INODE64;
264 return true;
265 }
266 }
267
268 if (type == "dev_t") {
269 if (field.size == sizeof(uint32_t)) {
270 field.filedType = FIELD_TYPE_DEVID32;
271 return true;
272 } else if (field.size == sizeof(uint64_t)) {
273 field.filedType = FIELD_TYPE_DEVID64;
274 return true;
275 }
276 }
277
278 // Pids (as in 'sched_switch').
279 if (type == "pid_t") {
280 field.filedType = FIELD_TYPE_PID32;
281 return true;
282 }
283
284 if ((typeName.find("common_pid") != std::string::npos)) {
285 field.filedType = FIELD_TYPE_COMMONPID32;
286 return true;
287 }
288 return false;
289 }
290
ParseCommonIntType(FieldFormat& field, bool sign)291 static bool ParseCommonIntType(FieldFormat& field, bool sign)
292 {
293 switch (field.size) {
294 case sizeof(int8_t):
295 field.filedType = sign ? FIELD_TYPE_INT8 : FIELD_TYPE_UINT8;
296 return true;
297 case sizeof(int16_t):
298 field.filedType = sign ? FIELD_TYPE_INT16 : FIELD_TYPE_UINT16;
299 return true;
300 case sizeof(int32_t):
301 field.filedType = sign ? FIELD_TYPE_INT32 : FIELD_TYPE_UINT32;
302 return true;
303 case sizeof(int64_t):
304 field.filedType = sign ? FIELD_TYPE_INT64 : FIELD_TYPE_UINT64;
305 return true;
306 default:
307 break;
308 }
309 return false;
310 }
311
ParseKernelAddrField(FieldFormat& field, const std::string& type)312 static bool ParseKernelAddrField(FieldFormat& field, const std::string& type)
313 {
314 if (type == "void*" || type == "void *") {
315 if (field.size == sizeof(uint64_t)) { // 64-bit kernel addresses
316 field.filedType = FIELD_TYPE_SYMADDR64;
317 return true;
318 } else if (field.size == sizeof(uint32_t)) { // 32-bit kernel addresses
319 field.filedType = FIELD_TYPE_SYMADDR32;
320 return true;
321 }
322 }
323 return false;
324 }
325
ParseFieldType(const std::string& type, FieldFormat& field)326 bool FtraceParser::ParseFieldType(const std::string& type, FieldFormat& field)
327 {
328 const std::string& typeName = field.typeName;
329 // Fixed size C char arrary, likes "char a[LEN]"
330 if (std::regex_match(typeName, fixedCharArrayRegex_)) {
331 field.filedType = FIELD_TYPE_FIXEDCSTRING;
332 return true;
333 }
334
335 // for flex array with __data_loc mark, likes: __data_loc char[] name; __data_loc __u8[] buf;
336 if (std::regex_match(typeName, flexDataLocArrayRegex_)) {
337 CHECK_TRUE(field.size == sizeof(uint32_t), false, "__data_loc %s, size: %hu", typeName.c_str(), field.size);
338 field.filedType = FIELD_TYPE_DATALOC;
339 return true;
340 }
341
342 if ((typeName.find("char[]") != std::string::npos) || (typeName.find("char *") != std::string::npos)) {
343 field.filedType = FIELD_TYPE_STRINGPTR;
344 return true;
345 }
346
347 // Variable length strings: "char foo" + size: 0 (as in 'print').
348 if ((type == "char" || type == "char []") && field.size == 0) {
349 field.filedType = FIELD_TYPE_CSTRING;
350 return true;
351 }
352
353 // 64-bit kernel addresses
354 if (ParseKernelAddrField(field, type)) {
355 return true;
356 }
357
358 if (ParseSepcialIntType(field, type, typeName)) {
359 return true;
360 }
361
362 // int uint:
363 if (ParseCommonIntType(field, field.isSigned)) {
364 return true;
365 }
366 return false;
367 }
368
ParseProtoType(FieldFormat& field)369 void FtraceParser::ParseProtoType(FieldFormat& field)
370 {
371 switch (field.filedType) {
372 case FIELD_TYPE_CSTRING:
373 case FIELD_TYPE_FIXEDCSTRING:
374 case FIELD_TYPE_STRINGPTR:
375 case FIELD_TYPE_DATALOC:
376 field.protoType = PROTO_TYPE_STRING;
377 break;
378 case FIELD_TYPE_INT8:
379 case FIELD_TYPE_INT16:
380 case FIELD_TYPE_INT32:
381 case FIELD_TYPE_PID32:
382 case FIELD_TYPE_COMMONPID32:
383 field.protoType = PROTO_TYPE_INT32;
384 break;
385 case FIELD_TYPE_INT64:
386 field.protoType = PROTO_TYPE_INT64;
387 break;
388 case FIELD_TYPE_UINT8:
389 case FIELD_TYPE_UINT16:
390 case FIELD_TYPE_UINT32:
391 case FIELD_TYPE_BOOL:
392 case FIELD_TYPE_DEVID32:
393 case FIELD_TYPE_SYMADDR32:
394 field.protoType = PROTO_TYPE_UINT32;
395 break;
396 case FIELD_TYPE_DEVID64:
397 case FIELD_TYPE_UINT64:
398 case FIELD_TYPE_INODE32:
399 case FIELD_TYPE_INODE64:
400 case FIELD_TYPE_SYMADDR64:
401 field.protoType = PROTO_TYPE_UINT64;
402 break;
403 case FIELD_TYPE_INVALID:
404 field.protoType = PROTO_TYPE_UNKNOWN;
405 break;
406 default:
407 break;
408 }
409 }
410
ParsePerCpuStatus(PerCpuStats& stats, const std::string& perCpuStats)411 bool FtraceParser::ParsePerCpuStatus(PerCpuStats& stats, const std::string& perCpuStats)
412 {
413 std::string line;
414 std::stringstream input(perCpuStats);
415
416 int count = 0;
417 while (getline(input, line, '\n')) {
418 std::string sep = ": ";
419 size_t pos = line.rfind(sep);
420 if (pos == std::string::npos) {
421 continue;
422 }
423 std::stringstream ss(line.substr(pos + sep.size()));
424 std::string name = line.substr(0, pos);
425 if (name == "entries") {
426 ss >> stats.entries;
427 count++;
428 } else if (name == "overrun") {
429 ss >> stats.overrun;
430 count++;
431 } else if (name == "commit overrun") {
432 ss >> stats.commitOverrun;
433 count++;
434 } else if (name == "bytes") {
435 ss >> stats.bytes;
436 count++;
437 } else if (name == "oldest event ts") {
438 ss >> stats.oldestEventTs;
439 count++;
440 } else if (name == "now ts") {
441 ss >> stats.nowTs;
442 count++;
443 } else if (name == "dropped events") {
444 ss >> stats.droppedEvents;
445 count++;
446 } else if (name == "read events") {
447 ss >> stats.readEvents;
448 count++;
449 }
450 }
451 return count > 0;
452 }
453
454 // parse kernel ring buffer page header data
ParsePageHeader()455 bool FtraceParser::ParsePageHeader()
456 {
457 // read time stamp
458 uint64_t timestamp = 0;
459 CHECK_TRUE(ReadInc(&cur_, endOfPage_, ×tamp, sizeof(timestamp)), false, "read timestamp from page failed!");
460 pageHeader_.timestamp = timestamp;
461
462 // read data size and overwriten flags
463 uint64_t commit = 0;
464 const int commitSize = GetHeaderPageCommitSize(); // 8B on 64bit device, 4B on 32bit device
465 CHECK_TRUE(ReadInc(&cur_, endOfPage_, &commit, commitSize), false, "read commit to page header failed!");
466
467 // refers kernel function ring_buffer_page_len:
468 pageHeader_.size = (commit & ~RB_MISSED_FLAGS);
469 pageHeader_.overwrite = (commit & RB_MISSED_EVENTS);
470
471 pageHeader_.startpos = cur_;
472 pageHeader_.endpos = cur_ + pageHeader_.size;
473 return true;
474 }
475
476 // parse /sys/kernel/debug/tracing/saved_tgids
477 // refers kernel function saved_tgids_show
ParseSavedTgid(const std::string& savedTgid)478 bool FtraceParser::ParseSavedTgid(const std::string& savedTgid)
479 {
480 int32_t pid = 0;
481 int32_t tgid = 0;
482 std::stringstream sin(savedTgid);
483 // kernel format code with: "%d %d\n"
484 while (sin >> pid >> tgid) {
485 tgidDict_[pid] = tgid;
486 }
487
488 if (tgidDict_.size() == 0) {
489 PROFILER_LOG_WARN(LOG_CORE, "ParseSavedTgid: parsed tigds: %zu", tgidDict_.size());
490 }
491 return true;
492 }
493
494 // parse /sys/kernel/debug/tracing/saved_cmdlines
495 // refers kernel function saved_cmdlines_show
ParseSavedCmdlines(const std::string& savedCmdlines)496 bool FtraceParser::ParseSavedCmdlines(const std::string& savedCmdlines)
497 {
498 bool retval = false;
499 int32_t pid;
500 std::string comm;
501 std::string line;
502 std::stringstream sin(savedCmdlines);
503 while (std::getline(sin, line)) {
504 // kernel format with: "%d %s\n"
505 auto pos = line.find(' ');
506 if (pos != std::string::npos && pos < INT_MAX_LEN) {
507 auto pidStr = line.substr(0, pos);
508 pid = COMMON::IsNumeric(pidStr) ? std::stoi(pidStr) : 0;
509 comm = line.substr(pos + 1);
510 commDict_[pid] = comm;
511 retval = true;
512 }
513 }
514
515 if (commDict_.size() == 0) {
516 PROFILER_LOG_WARN(LOG_CORE, "ParseSavedCmdlines: parsed cmdlines: %zu", commDict_.size());
517 }
518 return retval;
519 }
520
ParsePaddingData(const FtraceEventHeader& eventHeader)521 bool FtraceParser::ParsePaddingData(const FtraceEventHeader& eventHeader)
522 {
523 if (eventHeader.timeDelta == 0) {
524 return false;
525 }
526 uint32_t paddingLength;
527 CHECK_TRUE(ReadInc(&cur_, endOfData_, &paddingLength, sizeof(paddingLength)), false, "read padding len failed!");
528
529 // skip padding data
530 cur_ += paddingLength;
531 return true;
532 }
533
ParseTimeExtend(const FtraceEventHeader& eventHeader)534 bool FtraceParser::ParseTimeExtend(const FtraceEventHeader& eventHeader)
535 {
536 uint32_t deltaExt = 0;
537 CHECK_TRUE(ReadInc(&cur_, endOfData_, &deltaExt, sizeof(deltaExt)), false, "read time delta failed!");
538
539 timestamp_ += GetTimestampIncrements(deltaExt);
540 PROFILER_LOG_INFO(LOG_CORE, "ParseTimeExtend: update ts with %u to %" PRIu64, deltaExt, timestamp_);
541 return true;
542 }
543
ParseTimeStamp(const FtraceEventHeader& eventHeader)544 bool FtraceParser::ParseTimeStamp(const FtraceEventHeader& eventHeader)
545 {
546 uint32_t deltaExt = 0;
547 CHECK_TRUE(ReadInc(&cur_, endOfData_, &deltaExt, sizeof(deltaExt)), false, "read time delta failed!");
548
549 // refers kernel function rb_update_write_stamp in ring_buffer.c
550 timestamp_ = eventHeader.timeDelta + GetTimestampIncrements(deltaExt);
551 PROFILER_LOG_INFO(LOG_CORE, "ParseTimeStamp: update ts with %u to %" PRIu64, deltaExt, timestamp_);
552 return true;
553 }
554
ReadInc(uint8_t* start[], uint8_t end[], void* outData, size_t outSize)555 bool FtraceParser::ReadInc(uint8_t* start[], uint8_t end[], void* outData, size_t outSize)
556 {
557 if ((end - *start) < static_cast<ptrdiff_t>(outSize)) {
558 return false;
559 }
560 CHECK_TRUE(memcpy_s(outData, outSize, *start, outSize) == EOK, false,
561 "read %zu bytes from memory region FAILED", outSize);
562 *start += outSize;
563 return true;
564 }
565
IsValidIndex(int idx)566 bool FtraceParser::IsValidIndex(int idx)
567 {
568 return idx != CommonFiledIndex::INVALID_IDX;
569 }
570
SetDebugOn(bool value)571 void FtraceParser::SetDebugOn(bool value)
572 {
573 debugOn_ = value;
574 PROFILER_LOG_INFO(LOG_CORE, "debugOption: %s", debugOn_ ? "true" : "false");
575 }
576 FTRACE_NS_END
577