• 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         }
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