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
24 using namespace std::chrono;
25 namespace OHOS {
26 namespace Developtools {
27 namespace HiPerf {
~PerfFileWriter()28 PerfFileWriter::~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
Open(const std::string & fileName,bool compressData)40 bool 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(), "wb+");
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
Close()76 bool 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
WriteRecord(const PerfEventRecord & record)121 bool 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(), record.GetSize());
129
130 CHECK_TRUE(record.GetSize() > RECORD_SIZE_LIMIT_SPE, false, 1,
131 "%s record size exceed limit", record.GetName());
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
ReadDataSection(ProcessRecordCB & callback)145 bool 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
ReadRecords(ProcessRecordCB & callback)158 bool 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 if (header->size > RECORD_SIZE_LIMIT || header->size < sizeof(perf_event_header)) {
175 HLOGE("read record header size error %hu", header->size);
176 return false;
177 }
178 if (remainingSize >= header->size) {
179 size_t headerSize = sizeof(perf_event_header);
180 if (Read(buf + headerSize, header->size - headerSize)) {
181 size_t speSize = 0;
182 if (header->type == PERF_RECORD_AUXTRACE) {
183 struct PerfRecordAuxtraceData *auxtrace = reinterpret_cast<struct PerfRecordAuxtraceData *>
184 (header + 1);
185 speSize = auxtrace->size;
186 if (speSize > 0) {
187 Read(buf + header->size, auxtrace->size);
188 }
189 }
190 uint8_t *data = buf;
191 // the record is allowed from a cache memory, does not free memory after use
192 PerfEventRecord& record = PerfEventRecordFactory::GetPerfEventRecord(
193 static_cast<perf_event_type>(header->type), data, defaultEventAttr_);
194 // skip unknown record
195 CHECK_TRUE(record.GetName() == nullptr, true, 0, "");
196 remainingSize = remainingSize - header->size - speSize;
197 // call callback to process, do not destroy the record
198 callback(record);
199 recordNumber++;
200 }
201 } else {
202 HLOGW("not enough header->size.");
203 }
204 }
205 }
206 HLOGD("read back %zu records", recordNumber);
207 return true;
208 }
209
Read(void * buf,size_t len)210 bool PerfFileWriter::Read(void *buf, size_t len)
211 {
212 HLOG_ASSERT(buf != nullptr);
213 HLOG_ASSERT(fp_ != nullptr);
214 HLOG_ASSERT(len > 0);
215
216 CHECK_TRUE(fread(buf, len, 1, fp_) != 1, false, 1, "failed to read file");
217 return true;
218 }
219
SetWriteRecordStat(bool isWrite)220 void PerfFileWriter::SetWriteRecordStat(bool isWrite)
221 {
222 isWritingRecord = isWrite;
223 }
224
GetDataSize() const225 uint64_t PerfFileWriter::GetDataSize() const
226 {
227 return dataSection_.size;
228 }
229
GetRecordCount() const230 uint PerfFileWriter::GetRecordCount() const
231 {
232 return recordCount_;
233 }
234
GetFilePos(uint64_t & pos) const235 bool PerfFileWriter::GetFilePos(uint64_t &pos) const
236 {
237 off_t offset = ftello(fp_);
238 if (offset == -1) {
239 HLOGD("RecordFileWriter ftello fail");
240 return false;
241 }
242 pos = static_cast<uint64_t>(offset);
243 return true;
244 }
245
Write(const void * buf,size_t len)246 bool PerfFileWriter::Write(const void *buf, size_t len)
247 {
248 #ifdef HIPERF_DEBUG_TIME
249 const auto startTime = steady_clock::now();
250 #endif
251 CHECK_TRUE(len != 0u && fwrite(buf, len, 1, fp_) != 1, false, 1, "PerfFileWriter fwrite fail ");
252 #ifdef HIPERF_DEBUG_TIME
253 writeTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
254 #endif
255 return true;
256 }
257
WriteAttrAndId(const std::vector<AttrWithId> & attrIds,bool isSpe)258 bool PerfFileWriter::WriteAttrAndId(const std::vector<AttrWithId> &attrIds, bool isSpe)
259 {
260 CHECK_TRUE(attrIds.empty(), false, 0, "");
261
262 // Skip file header part.
263 if (fp_ == nullptr) {
264 return false;
265 } else if (fseek(fp_, header_.size, SEEK_SET) == -1) {
266 return false;
267 }
268
269 // Write id section.
270 uint64_t idSectionOffset;
271 CHECK_TRUE(!GetFilePos(idSectionOffset), false, 0, "");
272
273 HLOGD("attrIds %zu", attrIds.size());
274 for (auto &attrId : attrIds) {
275 HLOGD(" attrIds ids %zu", attrId.ids.size());
276 CHECK_TRUE(!Write(attrId.ids.data(), attrId.ids.size() * sizeof(uint64_t)), false, 0, "");
277 }
278
279 // Write attr section.
280 uint64_t attrSectionOffset;
281 CHECK_TRUE(!GetFilePos(attrSectionOffset), false, 0, "");
282 for (auto &attrId : attrIds) {
283 perf_file_attr fileAttr;
284 fileAttr.attr = attrId.attr;
285 fileAttr.ids.offset = idSectionOffset;
286 fileAttr.ids.size = attrId.ids.size() * sizeof(uint64_t);
287 idSectionOffset += fileAttr.ids.size;
288
289 if (!Write(&fileAttr, sizeof(fileAttr))) {
290 return false;
291 }
292 }
293
294 uint64_t dataSectionOffset;
295 if (!GetFilePos(dataSectionOffset)) {
296 return false;
297 }
298
299 attrSection_.offset = attrSectionOffset;
300 attrSection_.size = dataSectionOffset - attrSectionOffset;
301 dataSection_.offset = dataSectionOffset;
302
303 defaultEventAttr_ = attrIds[0].attr;
304 if (!WriteAuxTraceEvent(isSpe)) {
305 HLOGE("WriteAuxTraceEvent failed");
306 return false;
307 }
308 isWritingRecord = true;
309 return true;
310 }
311
WriteTimeConvEvent()312 bool PerfFileWriter::WriteTimeConvEvent()
313 {
314 perf_event_header header;
315 PerfRecordTtimeConvData auxTimeConv;
316 header.type = PERF_RECORD_TIME_CONV;
317 header.misc = PERF_RECORD_MISC_KERNEL;
318 header.size = static_cast<uint16_t>(sizeof(perf_event_header) + sizeof(PerfRecordTtimeConvData));
319 constexpr uint32_t timeShift = 21;
320 auxTimeConv.time_shift = timeShift;
321 auxTimeConv.time_mult = 1;
322 auxTimeConv.time_zero = 1;
323 auxTimeConv.time_cycles = 1;
324 auxTimeConv.cap_user_time_zero = 1;
325 auxTimeConv.cap_user_time_short = 1;
326 if (!Write(&header, sizeof(header))) {
327 return false;
328 }
329 if (!Write(&auxTimeConv, sizeof(auxTimeConv))) {
330 return false;
331 }
332 dataSection_.size = dataSection_.size + sizeof(header) + sizeof(auxTimeConv);
333 return true;
334 }
335
WriteAuxTraceInfoEvent()336 bool PerfFileWriter::WriteAuxTraceInfoEvent()
337 {
338 perf_event_header header;
339 PerfRecordAuxtraceInfoData auxTraceEvent;
340 header.type = PERF_RECORD_AUXTRACE_INFO;
341 header.misc = PERF_RECORD_MISC_KERNEL;
342 header.size = static_cast<uint16_t>(sizeof(perf_event_header) + sizeof(PerfRecordAuxtraceInfoData));
343 constexpr uint32_t auxTraceType = 4;
344 constexpr uint64_t armSpe = 7;
345 constexpr uint64_t cpuMmaps = 2;
346 auxTraceEvent.type = auxTraceType;
347 auxTraceEvent.priv[0] = armSpe;
348 auxTraceEvent.priv[1] = cpuMmaps;
349 if (!Write(&header, sizeof(header))) {
350 return false;
351 }
352 if (!Write(&auxTraceEvent, sizeof(auxTraceEvent))) {
353 return false;
354 }
355 dataSection_.size = dataSection_.size + sizeof(header) + sizeof(auxTraceEvent);
356 return true;
357 }
358
WriteCpuMapEvent()359 bool PerfFileWriter::WriteCpuMapEvent()
360 {
361 perf_event_header header;
362 PerfRecordCpuMapData cpuMap;
363 header.type = PERF_RECORD_CPU_MAP;
364 header.misc = PERF_RECORD_MISC_KERNEL;
365 header.size = static_cast<uint16_t>(sizeof(perf_event_header) + sizeof(PerfRecordCpuMapData));
366 constexpr uint32_t nrNum = 16;
367 cpuMap.nr = nrNum;
368 for (uint i = 0; i < cpuMap.nr; i++) {
369 cpuMap.cpu[i] = i;
370 }
371 if (!Write(&header, sizeof(header))) {
372 return false;
373 }
374 if (!Write(&cpuMap, sizeof(cpuMap))) {
375 return false;
376 }
377 dataSection_.size = dataSection_.size + sizeof(header) + sizeof(cpuMap);
378 return true;
379 }
380
WriteAuxTraceEvent(bool isSpe)381 bool PerfFileWriter::WriteAuxTraceEvent(bool isSpe)
382 {
383 if (!isSpe) {
384 return true;
385 }
386 // the following events just for perf parse hiperf spe data
387 if (!WriteTimeConvEvent()) {
388 HLOGE("WriteTimeConvEvent failed");
389 return false;
390 }
391 if (!WriteAuxTraceInfoEvent()) {
392 HLOGE("WriteAuxTraceInfoEvent failed");
393 return false;
394 }
395 if (!WriteCpuMapEvent()) {
396 HLOGE("WriteCpuMapEvent failed");
397 return false;
398 }
399 return true;
400 }
401
LeftLessRight(const std::unique_ptr<PerfFileSection> & l,const std::unique_ptr<PerfFileSection> & r)402 static bool LeftLessRight(const std::unique_ptr<PerfFileSection> &l,
403 const std::unique_ptr<PerfFileSection> &r)
404 {
405 CHECK_TRUE(l == nullptr || r == nullptr, false, 0, "");
406 return l->featureId_ < r->featureId_;
407 }
408 // to write perf_file_header to file
409 // can only be called after all attr and records has been written.
WriteHeader()410 bool PerfFileWriter::WriteHeader()
411 {
412 header_.attrSize = sizeof(perf_file_attr);
413 header_.attrs = attrSection_;
414 header_.data = dataSection_;
415
416 // ignore event_types
417 header_.eventTypes.size = 0;
418 header_.eventTypes.offset = 0;
419
420 // save header
421 if (fseek(fp_, 0, SEEK_SET) == -1) {
422 HLOGD("fseek return error ");
423 return false;
424 }
425 CHECK_TRUE(!Write(&header_, sizeof(header_)), false, 0, "");
426 return true;
427 }
428
WriteFeatureData()429 bool PerfFileWriter::WriteFeatureData()
430 {
431 long featureOffset = 0;
432 if (fseek(fp_, 0, SEEK_END) != 0) {
433 HLOGD("fseek SEEK_END return error ");
434 return false;
435 }
436 CHECK_TRUE((featureOffset = ftell(fp_)) == -1, false, 1, "ftell return error ");
437
438 for (size_t i = 0; i < sizeof(header_.features); i++) {
439 if (header_.features[i] != 0) {
440 HLOGV(" features['%zu'] '0x%x'", i, header_.features[i]);
441 }
442 }
443
444 unsigned long contentOffset = featureOffset + featureSections_.size() * sizeof(perf_file_section);
445 HLOGV("features start at file '0x%lx' content at '0x%lx'", featureOffset, contentOffset);
446
447 // reorder
448 std::sort(featureSections_.begin(), featureSections_.end(), LeftLessRight);
449 // save features head
450 int i = 0;
451 for (auto &featureSection : featureSections_) {
452 featureSection->header.offset = contentOffset;
453 featureSection->header.size = featureSection->GetSize();
454 contentOffset += featureSection->header.size;
455 HLOGV("save features[%d] head offset '0x%" PRIx64 "' size '0x%" PRIx64 "'", i,
456 featureSection->header.offset, featureSection->header.size);
457 i++;
458
459 // write features heads
460 CHECK_TRUE(!Write(&featureSection->header, sizeof(featureSection->header)), false, 0, "");
461 }
462 long offset = ftell(fp_);
463 CHECK_TRUE(offset < 0, false, 0, "");
464 HLOGV("features data at file '0x%lx'", offset);
465
466 i = 0;
467 for (auto &featureSection : featureSections_) {
468 std::vector<char> buf(featureSection->header.size);
469 featureSection->GetBinary(&buf[0], featureSection->header.size);
470 HLOGV("save features[%d] content size '0x%" PRIx64 "'", i, featureSection->header.size);
471 i++;
472
473 // write features content
474 if (!Write(&buf[0], featureSection->header.size)) {
475 HLOGE("write failed %" PRIu64 ".", featureSection->header.size);
476 return false;
477 }
478 }
479
480 return true;
481 }
482
AddNrCpusFeature(FEATURE feature,uint32_t nrCpusAvailable,uint32_t nrCpusOnline)483 bool PerfFileWriter::AddNrCpusFeature(FEATURE feature, uint32_t nrCpusAvailable,
484 uint32_t nrCpusOnline)
485 {
486 HLOGV("add feature '%s': nrCpusAvailable %u, nrCpusOnline %u",
487 PerfFileSection::GetFeatureName(FEATURE::NRCPUS).c_str(), nrCpusAvailable, nrCpusOnline);
488 featureSections_.emplace_back(
489 std::make_unique<PerfFileSectionNrCpus>(feature, nrCpusAvailable, nrCpusOnline));
490
491 // update header feature bits
492 header_.features[static_cast<int>(FEATURE::NRCPUS) / BITS_IN_BYTE] |=
493 1 << (static_cast<int>(FEATURE::NRCPUS) % BITS_IN_BYTE); // bit
494 return true;
495 }
496
AddEventDescFeature(FEATURE feature,const std::vector<AttrWithId> & eventDesces)497 bool PerfFileWriter::AddEventDescFeature(FEATURE feature,
498 const std::vector<AttrWithId> &eventDesces)
499 {
500 HLOGV("add feature '%s' %zu", PerfFileSection::GetFeatureName(FEATURE::EVENT_DESC).c_str(),
501 eventDesces.size());
502 featureSections_.emplace_back(std::make_unique<PerfFileSectionEventDesc>(feature, eventDesces));
503
504 header_.features[static_cast<int>(FEATURE::EVENT_DESC) / BITS_IN_BYTE] |=
505 1 << (static_cast<int>(FEATURE::EVENT_DESC) % BITS_IN_BYTE); // bit
506 return true;
507 }
508
AddStringFeature(FEATURE feature,std::string string)509 bool PerfFileWriter::AddStringFeature(FEATURE feature, std::string string)
510 {
511 HLOGV("add feature '%s' string '%s'", PerfFileSection::GetFeatureName(feature).c_str(),
512 string.c_str());
513 featureSections_.emplace_back(std::make_unique<PerfFileSectionString>(feature, string));
514
515 // update header feature bits
516 header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE);
517 return true;
518 }
519
AddBoolFeature(FEATURE feature)520 bool PerfFileWriter::AddBoolFeature(FEATURE feature)
521 {
522 // same as u64, just use 1 as value
523 return AddU64Feature(feature, 1u);
524 }
525
AddU64Feature(FEATURE feature,uint64_t v)526 bool PerfFileWriter::AddU64Feature(FEATURE feature, uint64_t v)
527 {
528 HLOGV("add feature '%s' uint64_t '%" PRIu64 "'",
529 PerfFileSection::GetFeatureName(feature).c_str(), v);
530 featureSections_.emplace_back(std::make_unique<PerfFileSectionU64>(feature, v));
531
532 // update header feature bits
533 header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE);
534 return true;
535 }
536
AddUniStackTableFeature(const ProcessStackMap * table)537 bool PerfFileWriter::AddUniStackTableFeature(const ProcessStackMap *table)
538 {
539 const FEATURE feature = FEATURE::HIPERF_FILES_UNISTACK_TABLE;
540 featureSections_.emplace_back(
541 std::make_unique<PerfFileSectionUniStackTable>(feature, table));
542 header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE);
543 return true;
544 }
545
AddSymbolsFeature(const std::vector<std::unique_ptr<SymbolsFile>> & symbolsFiles)546 bool PerfFileWriter::AddSymbolsFeature(
547 const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
548 {
549 const FEATURE feature = FEATURE::HIPERF_FILES_SYMBOL;
550 HLOGV("add feature symbolsFiles %zu", symbolsFiles.size());
551 std::vector<SymbolFileStruct> symbolFileStructs {};
552 for (auto &symbolsFile : symbolsFiles) {
553 HLOGV("add feature symbolsFile %s", symbolsFile->filePath_.c_str());
554 if (symbolsFile->SymbolsLoaded()) {
555 auto &symbolsFileStruct = symbolFileStructs.emplace_back();
556 symbolsFile->ExportSymbolToFileFormat(symbolsFileStruct);
557 }
558 }
559 featureSections_.emplace_back(
560 std::make_unique<PerfFileSectionSymbolsFiles>(feature, symbolFileStructs));
561 // update header feature bits
562 header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE);
563 return true;
564 }
565 } // namespace HiPerf
566 } // namespace Developtools
567 } // namespace OHOS
568