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 42namespace { 43using namespace OHOS::Developtools::Profiler; 44constexpr unsigned RB_MISSED_EVENTS = (1uL << 31); // Flag when events were overwritten 45constexpr unsigned RB_MISSED_STORED = (1 << 30); // Missed count stored at end 46constexpr unsigned RB_MISSED_FLAGS = (RB_MISSED_EVENTS | RB_MISSED_STORED); 47 48constexpr unsigned COL_IDX_NAME = 0; 49constexpr unsigned COL_IDX_VALUE = 1; 50 51constexpr unsigned TS_EXT_SHIFT = 27; 52constexpr uint32_t INT_MAX_LEN = 10; 53 54inline uint64_t GetTimestampIncrements(uint64_t ext) 55{ 56 return ext << TS_EXT_SHIFT; 57} 58} // namespace 59 60FTRACE_NS_BEGIN 61FtraceParser::FtraceParser() 62{ 63 PROFILER_LOG_INFO(LOG_CORE, "FtraceParser create!"); 64} 65 66bool 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 82FtraceParser::~FtraceParser() 83{ 84 PROFILER_LOG_INFO(LOG_CORE, "FtraceParser destroy!"); 85} 86 87bool 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 109bool 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 130int FtraceParser::GetHeaderPageCommitSize(void) 131{ 132 // return the size value of commit field read from events/header_page 133 return pageHeaderFormat_.commit.size; 134} 135 136bool 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 161static 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 181static 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 193static 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 206bool 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 251static 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 291static 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 312static 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 326bool 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 369void 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 411bool 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 455bool 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 478bool 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 496bool 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 521bool 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 534bool 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 544bool 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 555bool 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 566bool FtraceParser::IsValidIndex(int idx) 567{ 568 return idx != CommonFiledIndex::INVALID_IDX; 569} 570 571void FtraceParser::SetDebugOn(bool value) 572{ 573 debugOn_ = value; 574 PROFILER_LOG_INFO(LOG_CORE, "debugOption: %s", debugOn_ ? "true" : "false"); 575} 576FTRACE_NS_END 577