• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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