• 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 "utilities.h"
22 using namespace std::chrono;
23 namespace OHOS {
24 namespace Developtools {
25 namespace HiPerf {
~PerfFileWriter()26 PerfFileWriter::~PerfFileWriter()
27 {
28     // if file was not closed properly, remove it before exit
29     if (fp_ != nullptr) {
30         fclose(fp_);
31         fp_ = nullptr;
32         if (remove(fileName_.c_str()) != 0) {
33             HLOGE("fail to remove file(%s).", fileName_.c_str());
34         }
35     }
36 }
37 
Open(const std::string & fileName,bool compressData)38 bool PerfFileWriter::Open(const std::string &fileName, bool compressData)
39 {
40     // check file existence, if exist, remove it
41     if (access(fileName.c_str(), F_OK) == 0) {
42         // file exists
43         if (remove(fileName.c_str()) != 0) {
44 			char errInfo[ERRINFOLEN] = { 0 };
45             strerror_r(errno, errInfo, ERRINFOLEN);
46             printf("can't remove exist file(%s). %d:%s\n", fileName.c_str(), errno,
47                    errInfo);
48             return false;
49         }
50     }
51     std::string resolvedPath = CanonicalizeSpecPath(fileName.c_str());
52     fp_ = fopen(resolvedPath.c_str(), "web+");
53     if (fp_ == nullptr) {
54         char errInfo[ERRINFOLEN] = { 0 };
55         strerror_r(errno, errInfo, ERRINFOLEN);
56         printf("can't create file(%s). %d:%s\n", fileName.c_str(), errno, errInfo);
57         return false;
58     }
59 
60     fileName_ = fileName;
61     compressData_ = compressData;
62     attrSection_.offset = 0;
63     attrSection_.size = 0;
64     dataSection_ = attrSection_;
65     header_.size = sizeof(header_);
66     fileBuffer_.resize(WRITER_BUFFER_SIZE);
67     if (setvbuf(fp_, fileBuffer_.data(), _IOFBF, WRITER_BUFFER_SIZE) != 0) {
68         HLOGD("setvbuf failed");
69     }
70 
71     return true;
72 }
73 
Close()74 bool PerfFileWriter::Close()
75 {
76     HLOG_ASSERT(fp_ != nullptr);
77     bool rc = true;
78 
79     if (!WriteHeader()) {
80         rc = false;
81     }
82     if (!WriteFeatureData()) {
83         rc = false;
84     }
85 
86     if (fclose(fp_) != 0) {
87         HLOGD("fail to close fp ");
88         rc = false;
89     }
90     fp_ = nullptr;
91 
92     // compress and rename .perf.data to gzip file
93     if (compressData_) {
94         std::string gzName = fileName_ + ".gz";
95         if (CompressFile(fileName_, gzName)) {
96             if (remove(fileName_.c_str()) != 0) {
97                 char errInfo[ERRINFOLEN] = { 0 };
98                 strerror_r(errno, errInfo, ERRINFOLEN);
99                 printf("can't remove file(%s). %d:%s\n",
100                     fileName_.c_str(), errno, errInfo);
101             }
102             if (rename(gzName.c_str(), fileName_.c_str()) != 0) {
103                 char errInfo[ERRINFOLEN] = { 0 };
104                 strerror_r(errno, errInfo, ERRINFOLEN);
105                 printf("can't rename file(%s) to (%s). %d:%s\n", gzName.c_str(), fileName_.c_str(),
106                        errno, errInfo);
107             }
108         } else {
109             char errInfo[ERRINFOLEN] = { 0 };
110             strerror_r(errno, errInfo, ERRINFOLEN);
111             printf("failed to compress file(%s). %d:%s\n", fileName_.c_str(), errno,
112                    errInfo);
113         }
114     }
115 
116     return rc;
117 }
118 
WriteRecord(const PerfEventRecord & record)119 bool PerfFileWriter::WriteRecord(const PerfEventRecord &record)
120 {
121     if (!isWritingRecord) {
122         HLOGV("need write <attrs> first");
123         return false;
124     }
125 
126     HLOGV("write '%s'", record.GetName().c_str());
127 
128     if (record.GetSize() > RECORD_SIZE_LIMIT) {
129         HLOGD("%s record size exceed limit", record.GetName().c_str());
130         return false;
131     }
132     // signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr 0xb64eb195
133     static std::vector<u8> buf(RECORD_SIZE_LIMIT);
134 
135     if (!record.GetBinary(buf)) {
136         return false;
137     }
138 
139     if (!Write(buf.data(), record.GetSize())) {
140         return false;
141     }
142     dataSection_.size += record.GetSize();
143 
144     ++recordCount_;
145 
146     return true;
147 }
148 
ReadDataSection(ProcessRecordCB & callback)149 bool PerfFileWriter::ReadDataSection(ProcessRecordCB &callback)
150 {
151     HLOG_ASSERT(fp_ != nullptr);
152     if (fseek(fp_, dataSection_.offset, SEEK_SET) != 0) {
153         HLOGE("fseek() failed");
154         return false;
155     }
156     HLOGD("dataSection_ at offset %" PRIu64 " + %" PRIu64 "", dataSection_.offset,
157           dataSection_.size);
158 
159     return ReadRecords(callback);
160 }
161 
ReadRecords(ProcessRecordCB & callback)162 bool PerfFileWriter::ReadRecords(ProcessRecordCB &callback)
163 {
164     // record size can not exceed 64K
165     HIPERF_BUF_ALIGN uint8_t buf[RECORD_SIZE_LIMIT];
166     // diff with reader
167     uint64_t remainingSize = dataSection_.size;
168     size_t recordNumber = 0;
169     while (remainingSize > 0) {
170         if (remainingSize < sizeof(perf_event_header)) {
171             HLOGW("not enough sizeof(perf_event_header).");
172             return false;
173         } else if (Read(buf, sizeof(perf_event_header)) <= 0) {
174             HLOGW("read perf_event_header failed.");
175             return false;
176         } else {
177             perf_event_header *header = reinterpret_cast<perf_event_header *>(buf);
178             HLOG_ASSERT(header->size < sizeof(buf));
179             if (remainingSize >= header->size) {
180                 size_t headerSize = sizeof(perf_event_header);
181                 if (Read(buf + headerSize, header->size - headerSize)) {
182                     uint8_t *data = buf;
183                     // the record is allowed from a cache memory, does not free memory after use
184                     auto record = GetPerfSampleFromCacheMain(static_cast<perf_event_type>(header->type),
185                                                              data, defaultEventAttr_);
186                     // skip unknown record
187                     if (record == nullptr) {
188                         return true;
189                     }
190                     remainingSize -= header->size;
191                     // call callback to process, do not destroy the record
192                     callback(std::move(record));
193                     recordNumber++;
194                 }
195             } else {
196                 HLOGW("not enough header->size.");
197             }
198         }
199     }
200     HLOGD("read back %zu records", recordNumber);
201     return true;
202 }
203 
Read(void * buf,size_t len)204 bool PerfFileWriter::Read(void *buf, size_t len)
205 {
206     HLOG_ASSERT(buf != nullptr);
207     HLOG_ASSERT(fp_ != nullptr);
208     HLOG_ASSERT(len > 0);
209 
210     if (fread(buf, len, 1, fp_) != 1) {
211         HLOGD("failed to read file");
212         return false;
213     }
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     if (len != 0u && fwrite(buf, len, 1, fp_) != 1) {
244         HLOGD("PerfFileWriter fwrite fail ");
245         return false;
246     }
247 #ifdef HIPERF_DEBUG_TIME
248     writeTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
249 #endif
250     return true;
251 }
252 
WriteAttrAndId(const std::vector<AttrWithId> & attrIds)253 bool PerfFileWriter::WriteAttrAndId(const std::vector<AttrWithId> &attrIds)
254 {
255     if (attrIds.empty()) {
256         return false;
257     }
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     if (!GetFilePos(idSectionOffset)) {
269         return false;
270     }
271 
272     HLOGD("attrIds %zu", attrIds.size());
273     for (auto &attrId : attrIds) {
274         HLOGD(" attrIds ids %zu", attrId.ids.size());
275         if (!Write(attrId.ids.data(), attrId.ids.size() * sizeof(uint64_t))) {
276             return false;
277         }
278     }
279 
280     // Write attr section.
281     uint64_t attrSectionOffset;
282     if (!GetFilePos(attrSectionOffset)) {
283         return false;
284     }
285     for (auto &attrId : attrIds) {
286         perf_file_attr fileAttr;
287         fileAttr.attr = attrId.attr;
288         fileAttr.ids.offset = idSectionOffset;
289         fileAttr.ids.size = attrId.ids.size() * sizeof(uint64_t);
290         idSectionOffset += fileAttr.ids.size;
291 
292         if (!Write(&fileAttr, sizeof(fileAttr))) {
293             return false;
294         }
295     }
296 
297     uint64_t dataSectionOffset;
298     if (!GetFilePos(dataSectionOffset)) {
299         return false;
300     }
301 
302     attrSection_.offset = attrSectionOffset;
303     attrSection_.size = dataSectionOffset - attrSectionOffset;
304     dataSection_.offset = dataSectionOffset;
305 
306     defaultEventAttr_ = attrIds[0].attr;
307 
308     isWritingRecord = true;
309 
310     return true;
311 }
312 
LeftLessRight(const std::unique_ptr<PerfFileSection> & l,const std::unique_ptr<PerfFileSection> & r)313 static bool LeftLessRight(const std::unique_ptr<PerfFileSection> &l,
314                           const std::unique_ptr<PerfFileSection> &r)
315 {
316     if (l == nullptr || r == nullptr) {
317         return false;
318     }
319     return l->featureId_ < r->featureId_;
320 }
321 // to write perf_file_header to file
322 // can only be called after all attr and records has been written.
WriteHeader()323 bool PerfFileWriter::WriteHeader()
324 {
325     header_.attrSize = sizeof(perf_file_attr);
326     header_.attrs = attrSection_;
327     header_.data = dataSection_;
328 
329     // ignore event_types
330     header_.eventTypes.size = 0;
331     header_.eventTypes.offset = 0;
332 
333     // save header
334     if (fseek(fp_, 0, SEEK_SET) == -1) {
335         HLOGD("fseek return error ");
336         return false;
337     }
338     if (!Write(&header_, sizeof(header_))) {
339         return false;
340     }
341     return true;
342 }
343 
WriteFeatureData()344 bool PerfFileWriter::WriteFeatureData()
345 {
346     long featureOffset = 0;
347     if (fseek(fp_, 0, SEEK_END) != 0) {
348         HLOGD("fseek SEEK_END return error ");
349         return false;
350     }
351     if ((featureOffset = ftell(fp_)) == -1) {
352         HLOGD("ftell return error ");
353         return false;
354     }
355 
356     for (size_t i = 0; i < sizeof(header_.features); i++) {
357         if (header_.features[i] != 0) {
358             HLOGV(" features['%zu'] '0x%x'", i, header_.features[i]);
359         }
360     }
361 
362     unsigned long contentOffset = featureOffset + featureSections_.size() * sizeof(perf_file_section);
363     HLOGV("features start at file '0x%lx' content at '0x%lx'", featureOffset, contentOffset);
364 
365     // reorder
366     std::sort(featureSections_.begin(), featureSections_.end(), LeftLessRight);
367     // save features head
368     int i = 0;
369     for (auto &featureSection : featureSections_) {
370         featureSection->header.offset = contentOffset;
371         featureSection->header.size = featureSection->GetSize();
372         contentOffset += featureSection->header.size;
373         HLOGV("save features[%d] head offset '0x%" PRIx64 "' size '0x%" PRIx64 "'", i,
374               featureSection->header.offset, featureSection->header.size);
375         i++;
376 
377         // write features heads
378         if (!Write(&featureSection->header, sizeof(featureSection->header))) {
379             return false;
380         }
381     }
382     long offset = ftell(fp_);
383     if (offset < 0) {
384         return false;
385     }
386     HLOGV("features data at file '0x%lx'", offset);
387 
388     i = 0;
389     for (auto &featureSection : featureSections_) {
390         std::vector<char> buf(featureSection->header.size);
391         featureSection->GetBinary(&buf[0], featureSection->header.size);
392         HLOGV("save features[%d] content size '0x%" PRIx64 "'", i, featureSection->header.size);
393         i++;
394 
395         // write features content
396         if (!Write(&buf[0], featureSection->header.size)) {
397             HLOGE("write failed %" PRIu64 ".", featureSection->header.size);
398             return false;
399         }
400     }
401 
402     return true;
403 }
404 
AddNrCpusFeature(FEATURE feature,uint32_t nrCpusAvailable,uint32_t nrCpusOnline)405 bool PerfFileWriter::AddNrCpusFeature(FEATURE feature, uint32_t nrCpusAvailable,
406                                       uint32_t nrCpusOnline)
407 {
408     HLOGV("add feature '%s': nrCpusAvailable %u, nrCpusOnline %u",
409           PerfFileSection::GetFeatureName(FEATURE::NRCPUS).c_str(), nrCpusAvailable, nrCpusOnline);
410     featureSections_.emplace_back(
411         std::make_unique<PerfFileSectionNrCpus>(feature, nrCpusAvailable, nrCpusOnline));
412 
413     // update header feature bits
414     header_.features[static_cast<int>(FEATURE::NRCPUS) / BITS_IN_BYTE] |=
415         1 << (static_cast<int>(FEATURE::NRCPUS) % BITS_IN_BYTE); // bit
416     return true;
417 }
418 
AddEventDescFeature(FEATURE feature,const std::vector<AttrWithId> & eventDesces)419 bool PerfFileWriter::AddEventDescFeature(FEATURE feature,
420                                          const std::vector<AttrWithId> &eventDesces)
421 {
422     HLOGV("add feature '%s' %zu", PerfFileSection::GetFeatureName(FEATURE::EVENT_DESC).c_str(),
423           eventDesces.size());
424     featureSections_.emplace_back(std::make_unique<PerfFileSectionEventDesc>(feature, eventDesces));
425 
426     header_.features[static_cast<int>(FEATURE::EVENT_DESC) / BITS_IN_BYTE] |=
427         1 << (static_cast<int>(FEATURE::EVENT_DESC) % BITS_IN_BYTE); // bit
428     return true;
429 }
430 
AddStringFeature(FEATURE feature,std::string string)431 bool PerfFileWriter::AddStringFeature(FEATURE feature, std::string string)
432 {
433     HLOGV("add feature '%s' string '%s'", PerfFileSection::GetFeatureName(feature).c_str(),
434           string.c_str());
435     featureSections_.emplace_back(std::make_unique<PerfFileSectionString>(feature, string));
436 
437     // update header feature bits
438     header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE);
439     return true;
440 }
441 
AddBoolFeature(FEATURE feature)442 bool PerfFileWriter::AddBoolFeature(FEATURE feature)
443 {
444     // same as u64, just use 1 as value
445     return AddU64Feature(feature, 1u);
446 }
447 
AddU64Feature(FEATURE feature,uint64_t v)448 bool PerfFileWriter::AddU64Feature(FEATURE feature, uint64_t v)
449 {
450     HLOGV("add feature '%s' uint64_t '%" PRIu64 "'",
451           PerfFileSection::GetFeatureName(feature).c_str(), v);
452     featureSections_.emplace_back(std::make_unique<PerfFileSectionU64>(feature, v));
453 
454     // update header feature bits
455     header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE);
456     return true;
457 }
458 
AddUniStackTableFeature(const ProcessStackMap * table)459 bool PerfFileWriter::AddUniStackTableFeature(const ProcessStackMap *table)
460 {
461     const FEATURE feature = FEATURE::HIPERF_FILES_UNISTACK_TABLE;
462     featureSections_.emplace_back(
463         std::make_unique<PerfFileSectionUniStackTable>(feature, table));
464     header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE);
465     return true;
466 }
467 
AddSymbolsFeature(const std::vector<std::unique_ptr<SymbolsFile>> & symbolsFiles)468 bool PerfFileWriter::AddSymbolsFeature(
469     const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
470 {
471     const FEATURE feature = FEATURE::HIPERF_FILES_SYMBOL;
472     HLOGV("add feature symbolsFiles %zu", symbolsFiles.size());
473     std::vector<SymbolFileStruct> symbolFileStructs {};
474     for (auto &symbolsFile : symbolsFiles) {
475         if (symbolsFile->SymbolsLoaded()) {
476             auto &symbolsFileStruct = symbolFileStructs.emplace_back();
477             symbolsFile->ExportSymbolToFileFormat(symbolsFileStruct);
478         }
479     }
480     featureSections_.emplace_back(
481         std::make_unique<PerfFileSectionSymbolsFiles>(feature, symbolFileStructs));
482     // update header feature bits
483     header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE);
484     return true;
485 }
486 } // namespace HiPerf
487 } // namespace Developtools
488 } // namespace OHOS
489