• 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                     remainingSize -= header->size;
190                     // call callback to process, do not destroy the record
191                     callback(std::move(record));
192                     recordNumber++;
193                 }
194             } else {
195                 HLOGW("not enough header->size.");
196             }
197         }
198     }
199     HLOGD("read back %zu records", recordNumber);
200     return true;
201 }
202 
Read(void * buf,size_t len)203 bool PerfFileWriter::Read(void *buf, size_t len)
204 {
205     HLOG_ASSERT(buf != nullptr);
206     HLOG_ASSERT(fp_ != nullptr);
207     HLOG_ASSERT(len > 0);
208 
209     if (fread(buf, len, 1, fp_) != 1) {
210         HLOGD("failed to read file");
211         return false;
212     }
213     return true;
214 }
215 
GetDataSize() const216 uint64_t PerfFileWriter::GetDataSize() const
217 {
218     return dataSection_.size;
219 }
220 
GetRecordCount() const221 uint PerfFileWriter::GetRecordCount() const
222 {
223     return recordCount_;
224 }
225 
GetFilePos(uint64_t & pos) const226 bool PerfFileWriter::GetFilePos(uint64_t &pos) const
227 {
228     off_t offset = ftello(fp_);
229     if (offset == -1) {
230         HLOGD("RecordFileWriter ftello fail");
231         return false;
232     }
233     pos = static_cast<uint64_t>(offset);
234     return true;
235 }
236 
Write(const void * buf,size_t len)237 bool PerfFileWriter::Write(const void *buf, size_t len)
238 {
239 #ifdef HIPERF_DEBUG_TIME
240     const auto startTime = steady_clock::now();
241 #endif
242     if (len != 0u && fwrite(buf, len, 1, fp_) != 1) {
243         HLOGD("PerfFileWriter fwrite fail ");
244         return false;
245     }
246 #ifdef HIPERF_DEBUG_TIME
247     writeTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
248 #endif
249     return true;
250 }
251 
WriteAttrAndId(const std::vector<AttrWithId> & attrIds)252 bool PerfFileWriter::WriteAttrAndId(const std::vector<AttrWithId> &attrIds)
253 {
254     if (attrIds.empty()) {
255         return false;
256     }
257 
258     // Skip file header part.
259     if (fp_ == nullptr) {
260         return false;
261     } else if (fseek(fp_, header_.size, SEEK_SET) == -1) {
262         return false;
263     }
264 
265     // Write id section.
266     uint64_t idSectionOffset;
267     if (!GetFilePos(idSectionOffset)) {
268         return false;
269     }
270 
271     HLOGD("attrIds %zu", attrIds.size());
272     for (auto &attrId : attrIds) {
273         HLOGD(" attrIds ids %zu", attrId.ids.size());
274         if (!Write(attrId.ids.data(), attrId.ids.size() * sizeof(uint64_t))) {
275             return false;
276         }
277     }
278 
279     // Write attr section.
280     uint64_t attrSectionOffset;
281     if (!GetFilePos(attrSectionOffset)) {
282         return false;
283     }
284     for (auto &attrId : attrIds) {
285         perf_file_attr fileAttr;
286         fileAttr.attr = attrId.attr;
287         fileAttr.ids.offset = idSectionOffset;
288         fileAttr.ids.size = attrId.ids.size() * sizeof(uint64_t);
289         idSectionOffset += fileAttr.ids.size;
290 
291         if (!Write(&fileAttr, sizeof(fileAttr))) {
292             return false;
293         }
294     }
295 
296     uint64_t dataSectionOffset;
297     if (!GetFilePos(dataSectionOffset)) {
298         return false;
299     }
300 
301     attrSection_.offset = attrSectionOffset;
302     attrSection_.size = dataSectionOffset - attrSectionOffset;
303     dataSection_.offset = dataSectionOffset;
304 
305     defaultEventAttr_ = attrIds[0].attr;
306 
307     isWritingRecord = true;
308 
309     return true;
310 }
311 
LeftLessRight(const std::unique_ptr<PerfFileSection> & l,const std::unique_ptr<PerfFileSection> & r)312 static bool LeftLessRight(const std::unique_ptr<PerfFileSection> &l,
313                           const std::unique_ptr<PerfFileSection> &r)
314 {
315     return l->featureId_ < r->featureId_;
316 }
317 // to write perf_file_header to file
318 // can only be called after all attr and records has been written.
WriteHeader()319 bool PerfFileWriter::WriteHeader()
320 {
321     header_.attrSize = sizeof(perf_file_attr);
322     header_.attrs = attrSection_;
323     header_.data = dataSection_;
324 
325     // ignore event_types
326     header_.eventTypes.size = 0;
327     header_.eventTypes.offset = 0;
328 
329     // save header
330     if (fseek(fp_, 0, SEEK_SET) == -1) {
331         HLOGD("fseek return error ");
332         return false;
333     }
334     if (!Write(&header_, sizeof(header_))) {
335         return false;
336     }
337     return true;
338 }
339 
WriteFeatureData()340 bool PerfFileWriter::WriteFeatureData()
341 {
342     long featureOffset = 0;
343     if (fseek(fp_, 0, SEEK_END) != 0) {
344         HLOGD("fseek SEEK_END return error ");
345         return false;
346     }
347     if ((featureOffset = ftell(fp_)) == -1) {
348         HLOGD("ftell return error ");
349         return false;
350     }
351 
352     for (size_t i = 0; i < sizeof(header_.features); i++) {
353         if (header_.features[i] != 0) {
354             HLOGV(" features['%zu'] '0x%x'", i, header_.features[i]);
355         }
356     }
357 
358     unsigned long contentOffset = featureOffset + featureSections_.size() * sizeof(perf_file_section);
359     HLOGV("features start at file '0x%lx' content at '0x%lx'", featureOffset, contentOffset);
360 
361     // reorder
362     std::sort(featureSections_.begin(), featureSections_.end(), LeftLessRight);
363     // save features head
364     int i = 0;
365     for (auto &featureSection : featureSections_) {
366         featureSection->header.offset = contentOffset;
367         featureSection->header.size = featureSection->GetSize();
368         contentOffset += featureSection->header.size;
369         HLOGV("save features[%d] head offset '0x%" PRIx64 "' size '0x%" PRIx64 "'", i,
370               featureSection->header.offset, featureSection->header.size);
371         i++;
372 
373         // write features heads
374         if (!Write(&featureSection->header, sizeof(featureSection->header))) {
375             return false;
376         }
377     }
378     long offset = ftell(fp_);
379     if (offset < 0) {
380         return false;
381     }
382     HLOGV("features data at file '0x%lx'", offset);
383 
384     i = 0;
385     for (auto &featureSection : featureSections_) {
386         std::vector<char> buf(featureSection->header.size);
387         featureSection->GetBinary(&buf[0], featureSection->header.size);
388         HLOGV("save features[%d] content size '0x%" PRIx64 "'", i, featureSection->header.size);
389         i++;
390 
391         // write features content
392         if (!Write(&buf[0], featureSection->header.size)) {
393             HLOGE("write failed %" PRIu64 ".", featureSection->header.size);
394             return false;
395         }
396     }
397 
398     return true;
399 }
400 
AddNrCpusFeature(FEATURE feature,uint32_t nrCpusAvailable,uint32_t nrCpusOnline)401 bool PerfFileWriter::AddNrCpusFeature(FEATURE feature, uint32_t nrCpusAvailable,
402                                       uint32_t nrCpusOnline)
403 {
404     HLOGV("add feature '%s': nrCpusAvailable %u, nrCpusOnline %u",
405           PerfFileSection::GetFeatureName(FEATURE::NRCPUS).c_str(), nrCpusAvailable, nrCpusOnline);
406     featureSections_.emplace_back(
407         std::make_unique<PerfFileSectionNrCpus>(feature, nrCpusAvailable, nrCpusOnline));
408 
409     // update header feature bits
410     header_.features[(int)FEATURE::NRCPUS / BITS_IN_BYTE] |=
411         1 << ((int)FEATURE::NRCPUS % BITS_IN_BYTE); // bit
412     return true;
413 }
414 
AddEventDescFeature(FEATURE feature,const std::vector<AttrWithId> & eventDesces)415 bool PerfFileWriter::AddEventDescFeature(FEATURE feature,
416                                          const std::vector<AttrWithId> &eventDesces)
417 {
418     HLOGV("add feature '%s' %zu", PerfFileSection::GetFeatureName(FEATURE::EVENT_DESC).c_str(),
419           eventDesces.size());
420     featureSections_.emplace_back(std::make_unique<PerfFileSectionEventDesc>(feature, eventDesces));
421 
422     header_.features[(int)FEATURE::EVENT_DESC / BITS_IN_BYTE] |=
423         1 << ((int)FEATURE::EVENT_DESC % BITS_IN_BYTE); // bit
424     return true;
425 }
426 
AddStringFeature(FEATURE feature,std::string string)427 bool PerfFileWriter::AddStringFeature(FEATURE feature, std::string string)
428 {
429     HLOGV("add feature '%s' string '%s'", PerfFileSection::GetFeatureName(feature).c_str(),
430           string.c_str());
431     featureSections_.emplace_back(std::make_unique<PerfFileSectionString>(feature, string));
432 
433     // update header feature bits
434     header_.features[(int)feature / BITS_IN_BYTE] |= 1 << ((int)feature % BITS_IN_BYTE); // bit
435     return true;
436 }
437 
AddBoolFeature(FEATURE feature)438 bool PerfFileWriter::AddBoolFeature(FEATURE feature)
439 {
440     // same as u64, just use 1 as value
441     return AddU64Feature(feature, 1u);
442 }
443 
AddU64Feature(FEATURE feature,uint64_t v)444 bool PerfFileWriter::AddU64Feature(FEATURE feature, uint64_t v)
445 {
446     HLOGV("add feature '%s' uint64_t '%" PRIu64 "'",
447           PerfFileSection::GetFeatureName(feature).c_str(), v);
448     featureSections_.emplace_back(std::make_unique<PerfFileSectionU64>(feature, v));
449 
450     // update header feature bits
451     header_.features[(int)feature / BITS_IN_BYTE] |= 1 << ((int)feature % BITS_IN_BYTE); // bit
452     return true;
453 }
454 
AddUniStackTableFeature(const ProcessStackMap * table)455 bool PerfFileWriter::AddUniStackTableFeature(const ProcessStackMap *table)
456 {
457     const FEATURE feature = FEATURE::HIPERF_FILES_UNISTACK_TABLE;
458     featureSections_.emplace_back(
459         std::make_unique<PerfFileSectionUniStackTable>(feature, table));
460     header_.features[(int)feature / BITS_IN_BYTE] |= 1 << ((int)feature % BITS_IN_BYTE);
461     return true;
462 }
463 
AddSymbolsFeature(const std::vector<std::unique_ptr<SymbolsFile>> & symbolsFiles)464 bool PerfFileWriter::AddSymbolsFeature(
465     const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
466 {
467     const FEATURE feature = FEATURE::HIPERF_FILES_SYMBOL;
468     HLOGV("add feature symbolsFiles %zu", symbolsFiles.size());
469     std::vector<SymbolFileStruct> symbolFileStructs {};
470     for (auto &symbolsFile : symbolsFiles) {
471         if (symbolsFile->SymbolsLoaded()) {
472             auto &symbolsFileStruct = symbolFileStructs.emplace_back();
473             symbolsFile->ExportSymbolToFileFormat(symbolsFileStruct);
474         }
475     }
476     featureSections_.emplace_back(
477         std::make_unique<PerfFileSectionSymbolsFiles>(feature, symbolFileStructs));
478     // update header feature bits
479     header_.features[(int)feature / BITS_IN_BYTE] |= 1 << ((int)feature % BITS_IN_BYTE); // bit
480     return true;
481 }
482 } // namespace HiPerf
483 } // namespace Developtools
484 } // namespace OHOS
485