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_writer.h" 16 17#include <cinttypes> 18#include <cstdlib> 19#include <unistd.h> 20 21#include "hiperf_hilog.h" 22#include "utilities.h" 23 24using namespace std::chrono; 25namespace OHOS { 26namespace Developtools { 27namespace HiPerf { 28PerfFileWriter::~PerfFileWriter() 29{ 30 // if file was not closed properly, remove it before exit 31 if (fp_ != nullptr) { 32 fclose(fp_); 33 fp_ = nullptr; 34 if (remove(fileName_.c_str()) != 0) { 35 HLOGE("fail to remove file(%s).", fileName_.c_str()); 36 } 37 } 38} 39 40bool PerfFileWriter::Open(const std::string &fileName, bool compressData) 41{ 42 // check file existence, if exist, remove it 43 if (access(fileName.c_str(), F_OK) == 0) { 44 // file exists 45 if (remove(fileName.c_str()) != 0) { 46 char errInfo[ERRINFOLEN] = { 0 }; 47 strerror_r(errno, errInfo, ERRINFOLEN); 48 printf("can't remove exist file(%s). %d:%s\n", fileName.c_str(), errno, 49 errInfo); 50 return false; 51 } 52 } 53 std::string resolvedPath = CanonicalizeSpecPath(fileName.c_str()); 54 fp_ = fopen(resolvedPath.c_str(), "web+"); 55 if (fp_ == nullptr) { 56 char errInfo[ERRINFOLEN] = { 0 }; 57 strerror_r(errno, errInfo, ERRINFOLEN); 58 printf("can't create file(%s). %d:%s\n", fileName.c_str(), errno, errInfo); 59 return false; 60 } 61 62 fileName_ = fileName; 63 compressData_ = compressData; 64 attrSection_.offset = 0; 65 attrSection_.size = 0; 66 dataSection_ = attrSection_; 67 header_.size = sizeof(header_); 68 fileBuffer_.resize(WRITER_BUFFER_SIZE); 69 if (setvbuf(fp_, fileBuffer_.data(), _IOFBF, WRITER_BUFFER_SIZE) != 0) { 70 HLOGD("setvbuf failed"); 71 } 72 73 return true; 74} 75 76bool PerfFileWriter::Close() 77{ 78 HLOG_ASSERT(fp_ != nullptr); 79 bool rc = true; 80 81 if (!WriteHeader()) { 82 rc = false; 83 } 84 if (!WriteFeatureData()) { 85 rc = false; 86 } 87 88 if (fclose(fp_) != 0) { 89 HLOGD("fail to close fp "); 90 rc = false; 91 } 92 fp_ = nullptr; 93 94 // compress and rename .perf.data to gzip file 95 if (compressData_) { 96 std::string gzName = fileName_ + ".gz"; 97 if (CompressFile(fileName_, gzName)) { 98 if (remove(fileName_.c_str()) != 0) { 99 char errInfo[ERRINFOLEN] = { 0 }; 100 strerror_r(errno, errInfo, ERRINFOLEN); 101 printf("can't remove file(%s). %d:%s\n", 102 fileName_.c_str(), errno, errInfo); 103 } 104 if (rename(gzName.c_str(), fileName_.c_str()) != 0) { 105 char errInfo[ERRINFOLEN] = { 0 }; 106 strerror_r(errno, errInfo, ERRINFOLEN); 107 printf("can't rename file(%s) to (%s). %d:%s\n", gzName.c_str(), fileName_.c_str(), 108 errno, errInfo); 109 } 110 } else { 111 char errInfo[ERRINFOLEN] = { 0 }; 112 strerror_r(errno, errInfo, ERRINFOLEN); 113 printf("failed to compress file(%s). %d:%s\n", fileName_.c_str(), errno, 114 errInfo); 115 } 116 } 117 118 return rc; 119} 120 121bool PerfFileWriter::WriteRecord(const PerfEventRecord &record) 122{ 123 if (!isWritingRecord) { 124 HLOGV("need write <attrs> first"); 125 return false; 126 } 127 128 HLOGV("write '%s', size %zu", record.GetName().c_str(), record.GetSize()); 129 130 CHECK_TRUE(record.GetSize() > RECORD_SIZE_LIMIT_SPE, false, 1, 131 "%s record size exceed limit", record.GetName().c_str()); 132 // signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr 0xb64eb195 133 static std::vector<u8> buf(RECORD_SIZE_LIMIT_SPE); 134 135 CHECK_TRUE(!record.GetBinary(buf), false, 0, ""); 136 137 CHECK_TRUE(!Write(buf.data(), record.GetSize()), false, 0, ""); 138 dataSection_.size += record.GetSize(); 139 140 ++recordCount_; 141 142 return true; 143} 144 145bool PerfFileWriter::ReadDataSection(ProcessRecordCB &callback) 146{ 147 HLOG_ASSERT(fp_ != nullptr); 148 if (fseek(fp_, dataSection_.offset, SEEK_SET) != 0) { 149 HLOGE("fseek() failed"); 150 return false; 151 } 152 HLOGD("dataSection_ at offset %" PRIu64 " + %" PRIu64 "", dataSection_.offset, 153 dataSection_.size); 154 155 return ReadRecords(callback); 156} 157 158bool PerfFileWriter::ReadRecords(ProcessRecordCB &callback) 159{ 160 // record size can not exceed 64K 161 HIPERF_BUF_ALIGN static uint8_t buf[RECORD_SIZE_LIMIT_SPE]; 162 // diff with reader 163 uint64_t remainingSize = dataSection_.size; 164 size_t recordNumber = 0; 165 while (remainingSize > 0) { 166 if (remainingSize < sizeof(perf_event_header)) { 167 HLOGW("not enough sizeof(perf_event_header)."); 168 return false; 169 } else if (!Read(buf, sizeof(perf_event_header))) { 170 HLOGW("read perf_event_header failed."); 171 return false; 172 } else { 173 perf_event_header *header = reinterpret_cast<perf_event_header *>(buf); 174 HLOG_ASSERT(header->size < RECORD_SIZE_LIMIT); 175 if (remainingSize >= header->size) { 176 size_t headerSize = sizeof(perf_event_header); 177 if (Read(buf + headerSize, header->size - headerSize)) { 178 size_t speSize = 0; 179 if (header->type == PERF_RECORD_AUXTRACE) { 180 struct PerfRecordAuxtraceData *auxtrace = reinterpret_cast<struct PerfRecordAuxtraceData *> 181 (header + 1); 182 speSize = auxtrace->size; 183 if (speSize > 0) { 184 Read(buf + header->size, auxtrace->size); 185 } 186 } 187 uint8_t *data = buf; 188 // the record is allowed from a cache memory, does not free memory after use 189 auto record = GetPerfSampleFromCacheMain(static_cast<perf_event_type>(header->type), 190 data, defaultEventAttr_); 191 // skip unknown record 192 CHECK_TRUE(record == nullptr, true, 0, ""); 193 remainingSize = remainingSize - header->size - speSize; 194 // call callback to process, do not destroy the record 195 callback(std::move(record)); 196 recordNumber++; 197 } 198 } else { 199 HLOGW("not enough header->size."); 200 } 201 } 202 } 203 HLOGD("read back %zu records", recordNumber); 204 return true; 205} 206 207bool PerfFileWriter::Read(void *buf, size_t len) 208{ 209 HLOG_ASSERT(buf != nullptr); 210 HLOG_ASSERT(fp_ != nullptr); 211 HLOG_ASSERT(len > 0); 212 213 CHECK_TRUE(fread(buf, len, 1, fp_) != 1, false, 1, "failed to read file"); 214 return true; 215} 216 217uint64_t PerfFileWriter::GetDataSize() const 218{ 219 return dataSection_.size; 220} 221 222uint PerfFileWriter::GetRecordCount() const 223{ 224 return recordCount_; 225} 226 227bool PerfFileWriter::GetFilePos(uint64_t &pos) const 228{ 229 off_t offset = ftello(fp_); 230 if (offset == -1) { 231 HLOGD("RecordFileWriter ftello fail"); 232 return false; 233 } 234 pos = static_cast<uint64_t>(offset); 235 return true; 236} 237 238bool PerfFileWriter::Write(const void *buf, size_t len) 239{ 240#ifdef HIPERF_DEBUG_TIME 241 const auto startTime = steady_clock::now(); 242#endif 243 CHECK_TRUE(len != 0u && fwrite(buf, len, 1, fp_) != 1, false, 1, "PerfFileWriter fwrite fail "); 244#ifdef HIPERF_DEBUG_TIME 245 writeTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 246#endif 247 return true; 248} 249 250bool PerfFileWriter::WriteAttrAndId(const std::vector<AttrWithId> &attrIds) 251{ 252 CHECK_TRUE(attrIds.empty(), false, 0, ""); 253 254 // Skip file header part. 255 if (fp_ == nullptr) { 256 return false; 257 } else if (fseek(fp_, header_.size, SEEK_SET) == -1) { 258 return false; 259 } 260 261 // Write id section. 262 uint64_t idSectionOffset; 263 CHECK_TRUE(!GetFilePos(idSectionOffset), false, 0, ""); 264 265 HLOGD("attrIds %zu", attrIds.size()); 266 for (auto &attrId : attrIds) { 267 HLOGD(" attrIds ids %zu", attrId.ids.size()); 268 CHECK_TRUE(!Write(attrId.ids.data(), attrId.ids.size() * sizeof(uint64_t)), false, 0, ""); 269 } 270 271 // Write attr section. 272 uint64_t attrSectionOffset; 273 CHECK_TRUE(!GetFilePos(attrSectionOffset), false, 0, ""); 274 for (auto &attrId : attrIds) { 275 perf_file_attr fileAttr; 276 fileAttr.attr = attrId.attr; 277 fileAttr.ids.offset = idSectionOffset; 278 fileAttr.ids.size = attrId.ids.size() * sizeof(uint64_t); 279 idSectionOffset += fileAttr.ids.size; 280 281 if (!Write(&fileAttr, sizeof(fileAttr))) { 282 return false; 283 } 284 } 285 286 uint64_t dataSectionOffset; 287 if (!GetFilePos(dataSectionOffset)) { 288 return false; 289 } 290 291 attrSection_.offset = attrSectionOffset; 292 attrSection_.size = dataSectionOffset - attrSectionOffset; 293 dataSection_.offset = dataSectionOffset; 294 295 defaultEventAttr_ = attrIds[0].attr; 296 297 isWritingRecord = true; 298 299 return true; 300} 301 302static bool LeftLessRight(const std::unique_ptr<PerfFileSection> &l, 303 const std::unique_ptr<PerfFileSection> &r) 304{ 305 CHECK_TRUE(l == nullptr || r == nullptr, false, 0, ""); 306 return l->featureId_ < r->featureId_; 307} 308// to write perf_file_header to file 309// can only be called after all attr and records has been written. 310bool PerfFileWriter::WriteHeader() 311{ 312 header_.attrSize = sizeof(perf_file_attr); 313 header_.attrs = attrSection_; 314 header_.data = dataSection_; 315 316 // ignore event_types 317 header_.eventTypes.size = 0; 318 header_.eventTypes.offset = 0; 319 320 // save header 321 if (fseek(fp_, 0, SEEK_SET) == -1) { 322 HLOGD("fseek return error "); 323 return false; 324 } 325 CHECK_TRUE(!Write(&header_, sizeof(header_)), false, 0, ""); 326 return true; 327} 328 329bool PerfFileWriter::WriteFeatureData() 330{ 331 long featureOffset = 0; 332 if (fseek(fp_, 0, SEEK_END) != 0) { 333 HLOGD("fseek SEEK_END return error "); 334 return false; 335 } 336 CHECK_TRUE((featureOffset = ftell(fp_)) == -1, false, 1, "ftell return error "); 337 338 for (size_t i = 0; i < sizeof(header_.features); i++) { 339 if (header_.features[i] != 0) { 340 HLOGV(" features['%zu'] '0x%x'", i, header_.features[i]); 341 } 342 } 343 344 unsigned long contentOffset = featureOffset + featureSections_.size() * sizeof(perf_file_section); 345 HLOGV("features start at file '0x%lx' content at '0x%lx'", featureOffset, contentOffset); 346 347 // reorder 348 std::sort(featureSections_.begin(), featureSections_.end(), LeftLessRight); 349 // save features head 350 int i = 0; 351 for (auto &featureSection : featureSections_) { 352 featureSection->header.offset = contentOffset; 353 featureSection->header.size = featureSection->GetSize(); 354 contentOffset += featureSection->header.size; 355 HLOGV("save features[%d] head offset '0x%" PRIx64 "' size '0x%" PRIx64 "'", i, 356 featureSection->header.offset, featureSection->header.size); 357 i++; 358 359 // write features heads 360 CHECK_TRUE(!Write(&featureSection->header, sizeof(featureSection->header)), false, 0, ""); 361 } 362 long offset = ftell(fp_); 363 CHECK_TRUE(offset < 0, false, 0, ""); 364 HLOGV("features data at file '0x%lx'", offset); 365 366 i = 0; 367 for (auto &featureSection : featureSections_) { 368 std::vector<char> buf(featureSection->header.size); 369 featureSection->GetBinary(&buf[0], featureSection->header.size); 370 HLOGV("save features[%d] content size '0x%" PRIx64 "'", i, featureSection->header.size); 371 i++; 372 373 // write features content 374 if (!Write(&buf[0], featureSection->header.size)) { 375 HLOGE("write failed %" PRIu64 ".", featureSection->header.size); 376 return false; 377 } 378 } 379 380 return true; 381} 382 383bool PerfFileWriter::AddNrCpusFeature(FEATURE feature, uint32_t nrCpusAvailable, 384 uint32_t nrCpusOnline) 385{ 386 HLOGV("add feature '%s': nrCpusAvailable %u, nrCpusOnline %u", 387 PerfFileSection::GetFeatureName(FEATURE::NRCPUS).c_str(), nrCpusAvailable, nrCpusOnline); 388 featureSections_.emplace_back( 389 std::make_unique<PerfFileSectionNrCpus>(feature, nrCpusAvailable, nrCpusOnline)); 390 391 // update header feature bits 392 header_.features[static_cast<int>(FEATURE::NRCPUS) / BITS_IN_BYTE] |= 393 1 << (static_cast<int>(FEATURE::NRCPUS) % BITS_IN_BYTE); // bit 394 return true; 395} 396 397bool PerfFileWriter::AddEventDescFeature(FEATURE feature, 398 const std::vector<AttrWithId> &eventDesces) 399{ 400 HLOGV("add feature '%s' %zu", PerfFileSection::GetFeatureName(FEATURE::EVENT_DESC).c_str(), 401 eventDesces.size()); 402 featureSections_.emplace_back(std::make_unique<PerfFileSectionEventDesc>(feature, eventDesces)); 403 404 header_.features[static_cast<int>(FEATURE::EVENT_DESC) / BITS_IN_BYTE] |= 405 1 << (static_cast<int>(FEATURE::EVENT_DESC) % BITS_IN_BYTE); // bit 406 return true; 407} 408 409bool PerfFileWriter::AddStringFeature(FEATURE feature, std::string string) 410{ 411 HLOGV("add feature '%s' string '%s'", PerfFileSection::GetFeatureName(feature).c_str(), 412 string.c_str()); 413 featureSections_.emplace_back(std::make_unique<PerfFileSectionString>(feature, string)); 414 415 // update header feature bits 416 header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE); 417 return true; 418} 419 420bool PerfFileWriter::AddBoolFeature(FEATURE feature) 421{ 422 // same as u64, just use 1 as value 423 return AddU64Feature(feature, 1u); 424} 425 426bool PerfFileWriter::AddU64Feature(FEATURE feature, uint64_t v) 427{ 428 HLOGV("add feature '%s' uint64_t '%" PRIu64 "'", 429 PerfFileSection::GetFeatureName(feature).c_str(), v); 430 featureSections_.emplace_back(std::make_unique<PerfFileSectionU64>(feature, v)); 431 432 // update header feature bits 433 header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE); 434 return true; 435} 436 437bool PerfFileWriter::AddUniStackTableFeature(const ProcessStackMap *table) 438{ 439 const FEATURE feature = FEATURE::HIPERF_FILES_UNISTACK_TABLE; 440 featureSections_.emplace_back( 441 std::make_unique<PerfFileSectionUniStackTable>(feature, table)); 442 header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE); 443 return true; 444} 445 446bool PerfFileWriter::AddSymbolsFeature( 447 const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles) 448{ 449 const FEATURE feature = FEATURE::HIPERF_FILES_SYMBOL; 450 HLOGV("add feature symbolsFiles %zu", symbolsFiles.size()); 451 std::vector<SymbolFileStruct> symbolFileStructs {}; 452 for (auto &symbolsFile : symbolsFiles) { 453 HLOGV("add feature symbolsFile %s", symbolsFile->filePath_.c_str()); 454 if (symbolsFile->SymbolsLoaded()) { 455 auto &symbolsFileStruct = symbolFileStructs.emplace_back(); 456 symbolsFile->ExportSymbolToFileFormat(symbolsFileStruct); 457 } 458 } 459 featureSections_.emplace_back( 460 std::make_unique<PerfFileSectionSymbolsFiles>(feature, symbolFileStructs)); 461 // update header feature bits 462 header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE); 463 return true; 464} 465} // namespace HiPerf 466} // namespace Developtools 467} // namespace OHOS 468