• 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(), "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 
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().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 
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             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 
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 
GetDataSize() const217 uint64_t PerfFileWriter::GetDataSize() const
218 {
219     return dataSection_.size;
220 }
221 
GetRecordCount() const222 uint PerfFileWriter::GetRecordCount() const
223 {
224     return recordCount_;
225 }
226 
GetFilePos(uint64_t & pos) const227 bool 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 
Write(const void * buf,size_t len)238 bool 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 
WriteAttrAndId(const std::vector<AttrWithId> & attrIds)250 bool 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 
LeftLessRight(const std::unique_ptr<PerfFileSection> & l,const std::unique_ptr<PerfFileSection> & r)302 static 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.
WriteHeader()310 bool 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 
WriteFeatureData()329 bool 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 
AddNrCpusFeature(FEATURE feature,uint32_t nrCpusAvailable,uint32_t nrCpusOnline)383 bool 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 
AddEventDescFeature(FEATURE feature,const std::vector<AttrWithId> & eventDesces)397 bool 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 
AddStringFeature(FEATURE feature,std::string string)409 bool 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 
AddBoolFeature(FEATURE feature)420 bool PerfFileWriter::AddBoolFeature(FEATURE feature)
421 {
422     // same as u64, just use 1 as value
423     return AddU64Feature(feature, 1u);
424 }
425 
AddU64Feature(FEATURE feature,uint64_t v)426 bool 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 
AddUniStackTableFeature(const ProcessStackMap * table)437 bool 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 
AddSymbolsFeature(const std::vector<std::unique_ptr<SymbolsFile>> & symbolsFiles)446 bool 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