1/* 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 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#include "perf_file_reader.h" 16 17#include <bitset> 18#include <cinttypes> 19#include <cstdlib> 20#include <memory> 21 22#include <sys/stat.h> 23#include <unistd.h> 24 25#include "hiperf_hilog.h" 26#include "utilities.h" 27 28using namespace std::chrono; 29namespace OHOS { 30namespace Developtools { 31namespace HiPerf { 32const int FETURE_MAX = 256; 33const int SIZE_FETURE_COUNT = 8; 34 35std::unique_ptr<PerfFileReader> PerfFileReader::Instance(const std::string &fileName) 36{ 37 std::string resolvedPath = CanonicalizeSpecPath(fileName.c_str()); 38 FILE *fp = fopen(resolvedPath.c_str(), "rb"); 39 if (fp == nullptr) { 40 HLOGE("fail to open file %s", fileName.c_str()); 41 return nullptr; 42 } 43 44 std::unique_ptr<PerfFileReader> reader = std::make_unique<PerfFileReader>(fileName, fp); 45 if (!reader->ReadFileHeader()) { 46 // Fail to read header, maybe its compressed 47 if (reader->IsGzipFile()) { 48 fclose(fp); 49 reader->fp_ = nullptr; 50 51 CHECK_TRUE(!UncompressFile(fileName, ".perf.data"), nullptr, 1, 52 "Fail to UncompressFile(%s)", fileName.c_str()); 53 54 // open the uncompressed hidden file .perf.data 55 FILE *fp2 = fopen(".perf.data", "rb"); 56 if (fp2 == nullptr) { 57 HLOGE("fail to open uncompressed file .perf.data"); 58 return nullptr; 59 } 60 61 reader->fp_ = fp2; 62 reader->compressData_ = true; 63 64 if (!reader->ReadFileHeader()) { 65 HLOGE("fail to read header of file .perf.data"); 66 return nullptr; 67 } 68 goto end; 69 } 70 return nullptr; 71 } 72end: 73 CHECK_TRUE(!reader->ReadAttrSection(), nullptr, 0, ""); 74 return reader; 75} 76 77PerfFileReader::PerfFileReader(const std::string &fileName, FILE *fp) : fp_(fp), fileName_(fileName) 78{ 79 featureSectionOffset_ = 0; 80 struct stat fileStat; 81 if (fp != nullptr) { 82 if (fstat(fileno(fp), &fileStat) != -1 and fileStat.st_size > 0) { 83 fileSize_ = fileStat.st_size; 84 } 85 } 86} 87 88PerfFileReader::~PerfFileReader() 89{ 90 // if file was not closed properly 91 if (fp_ != nullptr) { 92 fclose(fp_); 93 fp_ = nullptr; 94 } 95 96 // remove the uncompressed .perf.data 97 if (compressData_) { 98 if (remove(".perf.data") != 0) { 99 HLOGE("Fail to remove uncompressed file .perf.data"); 100 perror("Fail to remove temp file"); 101 } 102 } 103} 104 105bool PerfFileReader::IsValidDataFile() 106{ 107 return (memcmp(header_.magic, PERF_MAGIC, sizeof(header_.magic)) == 0); 108} 109 110bool PerfFileReader::IsGzipFile() 111{ 112 return header_.magic[0] == '\x1f' and header_.magic[1] == '\x8b'; 113} 114 115bool PerfFileReader::ReadFileHeader() 116{ 117 if (Read(&header_, sizeof(header_))) { 118 dataSectionSize_ = header_.data.size; 119 if (IsValidDataFile()) { 120 featureSectionOffset_ = header_.data.offset + header_.data.size; 121 for (int i = 0; i < FETURE_MAX / SIZE_FETURE_COUNT; i++) { 122 std::bitset<SIZE_FETURE_COUNT> features(header_.features[i]); 123 for (int j = 0; j < SIZE_FETURE_COUNT; j++) { 124 if (features.test(j)) { 125 features_.emplace_back((FEATURE)(((uint64_t)i) * SIZE_FETURE_COUNT + j)); 126 } 127 } 128 } 129 return true; 130 } 131 } 132 return false; 133} 134 135bool PerfFileReader::ReadAttrSection() 136{ 137 if (header_.attrSize != sizeof(perf_file_attr)) { 138 // 4.19 and 5.1 use diff size , 128 vs 136 139 HLOGW("attr size %" PRId64 " doesn't match expected size %zu", header_.attrSize, 140 sizeof(perf_file_attr)); 141 } 142 CHECK_TRUE(header_.attrSize == 0, false, 0, ""); 143 int attrCount = header_.attrs.size / header_.attrSize; 144 CHECK_TRUE(attrCount == 0, false, 1, "no attr in file"); 145 if (fseek(fp_, header_.attrs.offset, SEEK_SET) != 0) { 146 HLOGE("fseek() failed"); 147 return false; 148 } 149 for (int i = 0; i < attrCount; ++i) { 150 std::vector<char> buf(header_.attrSize); 151 CHECK_TRUE(!Read(buf.data(), buf.size()), false, 0, ""); 152 // size of perf_event_attr change between different linux kernel versions. 153 // can not memcpy to perf_file_attr as a whole 154 perf_file_attr attr {}; 155 size_t attrSize = header_.attrSize - sizeof(attr.ids); 156 157 // If the size is smaller, you can use a pointer to point directly. 158 // Our UAPI is 4.19. is less than 5.1 159 if (header_.attrSize < sizeof(perf_event_attr)) { 160 HLOGE("size not match, ptr of perf_event_attr maybe overfollow %zu vs %zu", 161 sizeof(perf_event_attr), attrSize); 162 } 163 164 attr.attr = *(reinterpret_cast<perf_event_attr *>(&buf[0])); 165 attr.ids = *(reinterpret_cast<perf_file_section *>(&buf[attrSize])); 166 vecAttr_.push_back(attr); 167 } 168 169 // read ids for attr 170 for (size_t i = 0; i < vecAttr_.size(); ++i) { 171 std::vector<uint64_t> ids; 172 CHECK_TRUE(!ReadIdsForAttr(vecAttr_[i], &ids), false, 0, ""); 173 vecAttrIds_.push_back(ids); 174 175 // map ids to attr index 176 for (auto id : ids) { 177 mapId2Attr_[id] = i; 178 } 179 } 180 181 return true; 182} 183 184bool PerfFileReader::ReadIdsForAttr(const perf_file_attr &attr, std::vector<uint64_t> *ids) 185{ 186 if (attr.ids.size > 0) { 187 size_t count = attr.ids.size / sizeof(uint64_t); 188 if (fseek(fp_, attr.ids.offset, SEEK_SET) != 0) { 189 HLOGE("fseek() failed"); 190 return false; 191 } 192 CHECK_TRUE(ids == nullptr, false, 0, ""); 193 ids->resize(count); 194 CHECK_TRUE(!Read(ids->data(), attr.ids.size), false, 0, ""); 195 } 196 return true; 197} 198 199std::vector<AttrWithId> PerfFileReader::GetAttrSection() const 200{ 201 std::vector<AttrWithId> result(vecAttr_.size()); 202 203 for (size_t i = 0; i < vecAttr_.size(); ++i) { 204 result[i].attr = vecAttr_[i].attr; 205 result[i].ids = vecAttrIds_[i]; 206 } 207 return result; 208} 209 210bool PerfFileReader::ReadDataSection(ProcessRecordCB &callback) 211{ 212 if (fseek(fp_, header_.data.offset, SEEK_SET) != 0) { 213 HLOGE("fseek() failed"); 214 return false; 215 } 216 217 HLOGD("dataSection_ at offset %" PRId64 " + %" PRId64 "", header_.data.offset, 218 header_.data.size); 219 220 CHECK_TRUE(!ReadRecord(callback), false, LOG_TYPE_PRINTF, "some record format is error!\n"); 221 222#ifdef HIPERF_DEBUG_TIME 223 printf("readRecordTime: %" PRId64 " ms\n", 224 duration_cast<milliseconds>(readRecordTime_).count()); 225 printf("readCallbackTime: %" PRId64 " ms\n", 226 duration_cast<milliseconds>(readCallbackTime_).count()); 227#endif 228 return dataSectionSize_ == 0; 229} 230 231const perf_event_attr *PerfFileReader::GetDefaultAttr() 232{ 233 CHECK_TRUE(vecAttr_.empty(), nullptr, 0, ""); 234 return &(vecAttr_[0].attr); 235} 236 237bool PerfFileReader::ReadRecord(ProcessRecordCB &callback) 238{ 239#ifdef HIPERF_DEBUG_TIME 240 const auto startReadTime = steady_clock::now(); 241#endif 242 // record size can not exceed 64K 243 HIPERF_BUF_ALIGN static uint8_t buf[RECORD_SIZE_LIMIT_SPE]; 244 // diff with reader 245 uint64_t remainingSize = header_.data.size; 246 size_t recordNumber = 0; 247 const perf_event_attr *attr = GetDefaultAttr(); 248 CHECK_TRUE(attr == nullptr, false, 1, "attr is null"); 249 while (remainingSize > 0) { 250 if (remainingSize < sizeof(perf_event_header)) { 251 HLOGW("not enough sizeof perf_event_header"); 252 return false; 253 } else if (!Read(buf, sizeof(perf_event_header))) { 254 HLOGW("read perf_event_header failed."); 255 return false; 256 } else { 257 perf_event_header *header = reinterpret_cast<perf_event_header *>(buf); 258 if (header == nullptr) { 259 HLOGE("read record header is null"); 260 return false; 261 } else if (header->size > RECORD_SIZE_LIMIT) { 262 HLOGE("read record header size error %hu", header->size); 263 return false; 264 } 265 if (remainingSize >= header->size) { 266 size_t headerSize = sizeof(perf_event_header); 267 if (Read(buf + headerSize, header->size - headerSize)) { 268 size_t speSize = 0; 269 if (header->type == PERF_RECORD_AUXTRACE) { 270 struct PerfRecordAuxtraceData *auxtrace = reinterpret_cast<struct PerfRecordAuxtraceData *> 271 (header + 1); 272 speSize = auxtrace->size; 273 if (speSize > 0) { 274 Read(buf + header->size, auxtrace->size); 275 } 276 } 277 uint8_t *data = buf; 278 std::unique_ptr<PerfEventRecord> record = GetPerfEventRecord( 279 static_cast<perf_event_type>(header->type), data, *attr); 280 // unknown record , break the process 281 if (!record) { 282 return false; 283 } else { 284 HLOGV("record type %u", record->GetType()); 285 } 286 remainingSize = remainingSize - header->size - speSize; 287#ifdef HIPERF_DEBUG_TIME 288 const auto startCallbackTime = steady_clock::now(); 289#endif 290 // call callback to process, then destroy record 291 callback(std::move(record)); 292 recordNumber++; 293#ifdef HIPERF_DEBUG_TIME 294 readCallbackTime_ += 295 duration_cast<microseconds>(steady_clock::now() - startCallbackTime); 296#endif 297 } else { 298 HLOGE("read record data size failed %zu", header->size - headerSize); 299 return false; 300 } 301 } else { 302 HLOGE("not enough header->size."); 303 return false; 304 } 305 } 306 } 307 HLOGD("read back %zu records", recordNumber); 308#ifdef HIPERF_DEBUG_TIME 309 readRecordTime_ += duration_cast<microseconds>(steady_clock::now() - startReadTime); 310#endif 311 return true; 312} 313 314bool PerfFileReader::Read(void *buf, size_t len) 315{ 316 if (buf == nullptr || len == 0) { 317 HLOG_ASSERT(buf != nullptr); 318 HLOG_ASSERT(len > 0); 319 return false; 320 } 321 322 if (fread(buf, len, 1, fp_) != 1) { 323 printf("failed to read file: %d", errno); 324 return false; 325 } 326 return true; 327} 328 329const perf_file_header &PerfFileReader::GetHeader() const 330{ 331 return header_; 332} 333 334bool PerfFileReader::Read(char *buf, uint64_t offset, size_t len) 335{ 336 if (buf == nullptr || len == 0) { 337 HLOG_ASSERT(buf != nullptr); 338 HLOG_ASSERT(len > 0); 339 return false; 340 } 341 if (fseek(fp_, offset, SEEK_SET) != 0) { 342 HLOGE("fseek() failed"); 343 return false; 344 } 345 346 CHECK_TRUE(fread(buf, len, 1, fp_) != 1, false, LOG_TYPE_PRINTF, "failed to read file: %d", errno); 347 HLOGM("offset %" PRIx64 " len %zu buf %x %x %x %x", offset, len, buf[0], buf[1], buf[2], 348 buf[3]); 349 return true; 350} 351const std::vector<FEATURE> &PerfFileReader::GetFeatures() const 352{ 353 return features_; 354} 355 356const std::vector<std::unique_ptr<PerfFileSection>> &PerfFileReader::GetFeatureSections() const 357{ 358 return perfFileSections_; 359} 360 361const std::string PerfFileReader::GetFeatureString(const FEATURE feature) const 362{ 363 std::string featureName = PerfFileSection::GetFeatureName(feature); 364 HLOGV("GetFeatureSection %s", featureName.c_str()); 365 if (!IsFeatrureStringSection(feature)) { 366 HLOGV("not a string feature: %s", featureName.c_str()); 367 } else { 368 const PerfFileSection *featureSection = GetFeatureSection(feature); 369 if (featureSection != nullptr) { 370 const PerfFileSectionString *sectionString = 371 static_cast<const PerfFileSectionString *>(featureSection); 372 return sectionString->ToString(); 373 } else { 374 HLOGV("have not found: %s", featureName.c_str()); 375 } 376 } 377 return EMPTY_STRING; 378} 379 380const PerfFileSection *PerfFileReader::GetFeatureSection(FEATURE feature) const 381{ 382 for (auto const &it : perfFileSections_) { 383 HLOGV("perfFileSections"); 384 if (it->featureId_ == feature) { 385 return it.get(); 386 } 387 } 388 return nullptr; 389} 390 391bool PerfFileReader::ReadFeatureSection() 392{ 393 uint64_t featureSectionOffsetRead = featureSectionOffset_; 394 HLOGV(" ReadDataSection data offset '0x%" PRIx64 " ", featureSectionOffset_); 395 396 for (FEATURE feature : features_) { 397 perf_file_section sectionHeader; 398 // read failed ?? 399 CHECK_TRUE(!Read((char *)§ionHeader, featureSectionOffsetRead, sizeof(sectionHeader)), 400 false, LOG_TYPE_PRINTF, 401 "file format not correct. featureSectionOffsetRead '0x%" PRIx64 "\n", featureSectionOffsetRead); 402 403 HLOGV("process feature %d:%s", feature, PerfFileSection::GetFeatureName(feature).c_str()); 404 HLOGV(" sectionHeader -> read offset '0x%" PRIx64 " size '0x%" PRIx64 "'", 405 sectionHeader.offset, sectionHeader.size); 406 CHECK_TRUE(sectionHeader.size == 0 or sectionHeader.size > fileSize_, false, 1, 407 "sectionHeader.size %" PRIu64 " is not correct", sectionHeader.size); 408 409 std::vector<char> buf(sectionHeader.size); 410 // read failed ?? 411 CHECK_TRUE(!Read(&buf[0], sectionHeader.offset, buf.size()), false, LOG_TYPE_PRINTF, 412 "file format not correct. featureSectionDataOffset '0x%" PRIx64 "\n", sectionHeader.offset); 413 if (IsFeatrureStringSection(feature)) { 414 perfFileSections_.emplace_back( 415 std::make_unique<PerfFileSectionString>(feature, (char *)&buf[0], buf.size())); 416 } else if (feature == FEATURE::HIPERF_FILES_SYMBOL) { 417 perfFileSections_.emplace_back(std::make_unique<PerfFileSectionSymbolsFiles>( 418 feature, (char *)&buf[0], buf.size())); 419 } else if (feature == FEATURE::EVENT_DESC) { 420 perfFileSections_.emplace_back( 421 std::make_unique<PerfFileSectionEventDesc>(feature, (char *)&buf[0], buf.size())); 422 } else if (feature == FEATURE::HIPERF_FILES_UNISTACK_TABLE) { 423 perfFileSections_.emplace_back( 424 std::make_unique<PerfFileSectionUniStackTable>(feature, (char *)&buf[0], buf.size())); 425 PerfRecordSample::dumpRemoveStack_ = true; 426 } else { 427 HLOGW("still not imp how to process with feature %d", feature); 428 } 429 430 featureSectionOffsetRead += sizeof(sectionHeader); // next feaure 431 } 432 return true; 433} 434} // namespace HiPerf 435} // namespace Developtools 436} // namespace OHOS 437