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