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