• 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_reader.h"
16 
17 #include <bitset>
18 #include <cinttypes>
19 #include <cstdlib>
20 #include <memory>
21 
22 #include <sys/stat.h>
23 #include <unistd.h>
24 
25 #include "utilities.h"
26 
27 using namespace std::chrono;
28 namespace OHOS {
29 namespace Developtools {
30 namespace HiPerf {
31 const int FETURE_MAX = 256;
32 const int SIZE_FETURE_COUNT = 8;
33 
Instance(const std::string & fileName)34 std::unique_ptr<PerfFileReader> PerfFileReader::Instance(const std::string &fileName)
35 {
36     std::string resolvedPath = CanonicalizeSpecPath(fileName.c_str());
37     FILE *fp = fopen(resolvedPath.c_str(), "rb");
38     if (fp == nullptr) {
39         HLOGE("fail to open file %s", fileName.c_str());
40         return nullptr;
41     }
42 
43     std::unique_ptr<PerfFileReader> reader = std::make_unique<PerfFileReader>(fileName, fp);
44     if (!reader->ReadFileHeader()) {
45         // Fail to read header, maybe its compressed
46         if (reader->IsGzipFile()) {
47             fclose(fp);
48             reader->fp_ = nullptr;
49 
50             if (!UncompressFile(fileName, ".perf.data")) {
51                 HLOGE("Fail to UncompressFile(%s)", fileName.c_str());
52                 return nullptr;
53             }
54 
55             // open the uncompressed hidden file .perf.data
56             FILE *fp2 = fopen(".perf.data", "rb");
57             if (fp2 == nullptr) {
58                 HLOGE("fail to open uncompressed file .perf.data");
59                 return nullptr;
60             }
61 
62             reader->fp_ = fp2;
63             reader->compressData_ = true;
64 
65             if (!reader->ReadFileHeader()) {
66                 HLOGE("fail to read header of file .perf.data");
67                 return nullptr;
68             }
69             goto end;
70         }
71         return nullptr;
72     }
73 end:
74     if (!reader->ReadAttrSection()) {
75         return nullptr;
76     }
77     return reader;
78 }
79 
PerfFileReader(const std::string & fileName,FILE * fp)80 PerfFileReader::PerfFileReader(const std::string &fileName, FILE *fp) : fp_(fp), fileName_(fileName)
81 {
82     featureSectionOffset_ = 0;
83     struct stat fileStat;
84     if (fp != nullptr) {
85         if (fstat(fileno(fp), &fileStat) != -1 and fileStat.st_size > 0) {
86             fileSize_ = fileStat.st_size;
87         }
88     }
89 }
90 
~PerfFileReader()91 PerfFileReader::~PerfFileReader()
92 {
93     // if file was not closed properly
94     if (fp_ != nullptr) {
95         fclose(fp_);
96         fp_ = nullptr;
97     }
98 
99     // remove the uncompressed .perf.data
100     if (compressData_) {
101         if (remove(".perf.data") != 0) {
102             HLOGE("Fail to remove uncompressed file .perf.data");
103             perror("Fail to remove temp file");
104         }
105     }
106 }
107 
IsValidDataFile()108 bool PerfFileReader::IsValidDataFile()
109 {
110     return (memcmp(header_.magic, PERF_MAGIC, sizeof(header_.magic)) == 0);
111 }
112 
IsGzipFile()113 bool PerfFileReader::IsGzipFile()
114 {
115     return header_.magic[0] == '\x1f' and header_.magic[1] == '\x8b';
116 }
117 
ReadFileHeader()118 bool PerfFileReader::ReadFileHeader()
119 {
120     if (Read(&header_, sizeof(header_))) {
121         dataSectionSize_ = header_.data.size;
122         if (IsValidDataFile()) {
123             featureSectionOffset_ = header_.data.offset + header_.data.size;
124             for (int i = 0; i < FETURE_MAX / SIZE_FETURE_COUNT; i++) {
125                 std::bitset<SIZE_FETURE_COUNT> features(header_.features[i]);
126                 for (int j = 0; j < SIZE_FETURE_COUNT; j++) {
127                     if (features.test(j)) {
128                         features_.emplace_back((FEATURE)(i * SIZE_FETURE_COUNT + j));
129                     }
130                 }
131             }
132             return true;
133         }
134     }
135     return false;
136 }
137 
ReadAttrSection()138 bool PerfFileReader::ReadAttrSection()
139 {
140     if (header_.attrSize != sizeof(perf_file_attr)) {
141         // 4.19 and 5.1 use diff size , 128 vs 136
142         HLOGW("attr size %" PRId64 " doesn't match expected size %zu", header_.attrSize,
143               sizeof(perf_file_attr));
144     }
145 
146     int attrCount = header_.attrs.size / header_.attrSize;
147     if (attrCount == 0) {
148         HLOGE("no attr in file");
149         return false;
150     }
151     if (fseek(fp_, header_.attrs.offset, SEEK_SET) != 0) {
152         HLOGE("fseek() failed");
153         return false;
154     }
155     for (int i = 0; i < attrCount; ++i) {
156         std::vector<char> buf(header_.attrSize);
157         if (!Read(buf.data(), buf.size())) {
158             return false;
159         }
160         // size of perf_event_attr change between different linux kernel versions.
161         // can not memcpy to perf_file_attr as a whole
162         perf_file_attr attr {};
163         size_t attr_size = header_.attrSize - sizeof(attr.ids);
164 
165         // If the size is smaller, you can use a pointer to point directly.
166         // Our UAPI is 4.19. is less than 5.1
167         if (sizeof(perf_event_attr) > header_.attrSize) {
168             HLOGE("size not match, ptr of perf_event_attr maybe overfollow %zu vs %zu",
169                   sizeof(perf_event_attr), attr_size);
170         }
171 
172         attr.attr = *(reinterpret_cast<perf_event_attr *>(&buf[0]));
173         attr.ids = *(reinterpret_cast<perf_file_section *>(&buf[attr_size]));
174         vecAttr_.push_back(attr);
175     }
176 
177     // read ids for attr
178     for (size_t i = 0; i < vecAttr_.size(); ++i) {
179         std::vector<uint64_t> ids;
180         if (!ReadIdsForAttr(vecAttr_[i], &ids)) {
181             return false;
182         }
183         vecAttrIds_.push_back(ids);
184 
185         // map ids to attr index
186         for (auto id : ids) {
187             mapId2Attr_[id] = i;
188         }
189     }
190 
191     return true;
192 }
193 
ReadIdsForAttr(const perf_file_attr & attr,std::vector<uint64_t> * ids)194 bool PerfFileReader::ReadIdsForAttr(const perf_file_attr &attr, std::vector<uint64_t> *ids)
195 {
196     if (attr.ids.size > 0) {
197         size_t count = attr.ids.size / sizeof(uint64_t);
198         if (fseek(fp_, attr.ids.offset, SEEK_SET) != 0) {
199             HLOGE("fseek() failed");
200             return false;
201         }
202 
203         ids->resize(count);
204         if (!Read(ids->data(), attr.ids.size)) {
205             return false;
206         }
207     }
208     return true;
209 }
210 
GetAttrSection() const211 std::vector<AttrWithId> PerfFileReader::GetAttrSection() const
212 {
213     std::vector<AttrWithId> result(vecAttr_.size());
214 
215     for (size_t i = 0; i < vecAttr_.size(); ++i) {
216         result[i].attr = vecAttr_[i].attr;
217         result[i].ids = vecAttrIds_[i];
218     }
219     return result;
220 }
221 
ReadDataSection(ProcessRecordCB & callback)222 bool PerfFileReader::ReadDataSection(ProcessRecordCB &callback)
223 {
224     if (fseek(fp_, header_.data.offset, SEEK_SET) != 0) {
225         HLOGE("fseek() failed");
226         return false;
227     }
228 
229     HLOGD("dataSection_ at offset %" PRId64 " + %" PRId64 "", header_.data.offset,
230           header_.data.size);
231 
232     if (!ReadRecord(callback)) {
233         printf("some record format is error!\n");
234         return false;
235     };
236 
237 #ifdef HIPERF_DEBUG_TIME
238     printf("readRecordTime: %" PRId64 " ms\n",
239            duration_cast<milliseconds>(readRecordTime_).count());
240     printf("readCallbackTime: %" PRId64 " ms\n",
241            duration_cast<milliseconds>(readCallbackTime_).count());
242 #endif
243     return dataSectionSize_ == 0;
244 }
245 
GetDefaultAttr()246 const perf_event_attr *PerfFileReader::GetDefaultAttr()
247 {
248     if (vecAttr_.empty())
249         return nullptr;
250 
251     return &(vecAttr_[0].attr);
252 }
253 
ReadRecord(ProcessRecordCB & callback)254 bool PerfFileReader::ReadRecord(ProcessRecordCB &callback)
255 {
256 #ifdef HIPERF_DEBUG_TIME
257     const auto startReadTime = steady_clock::now();
258 #endif
259     // record size can not exceed 64K
260     HIPERF_BUF_ALIGN uint8_t buf[RECORD_SIZE_LIMIT];
261     // diff with reader
262     uint64_t remainingSize = header_.data.size;
263     size_t recordNumber = 0;
264     while (remainingSize > 0) {
265         if (remainingSize < sizeof(perf_event_header)) {
266             HLOGW("not enough sizeof perf_event_header");
267             return false;
268         } else if (!Read(buf, sizeof(perf_event_header))) {
269             HLOGW("read perf_event_header failed.");
270             return false;
271         } else {
272             perf_event_header *header = reinterpret_cast<perf_event_header *>(buf);
273             if (header->size > sizeof(buf)) {
274                 HLOGE("read record header size error %hu", header->size);
275                 return false;
276             }
277             if (remainingSize >= header->size) {
278                 size_t headerSize = sizeof(perf_event_header);
279                 if (Read(buf + headerSize, header->size - headerSize)) {
280                     uint8_t *data = buf;
281                     std::unique_ptr<PerfEventRecord> record = GetPerfEventRecord(
282                         static_cast<perf_event_type>(header->type), data, *GetDefaultAttr());
283                     // unknown record , break the process
284                     if (!record) {
285                         return false;
286                     } else {
287                         HLOGV("record type %u", record->GetType());
288                     }
289                     remainingSize -= header->size;
290 #ifdef HIPERF_DEBUG_TIME
291                     const auto startCallbackTime = steady_clock::now();
292 #endif
293                     // call callback to process, then destroy record
294                     callback(std::move(record));
295                     recordNumber++;
296 #ifdef HIPERF_DEBUG_TIME
297                     readCallbackTime_ +=
298                         duration_cast<microseconds>(steady_clock::now() - startCallbackTime);
299 #endif
300                 } else {
301                     HLOGE("read record data size failed %zu", header->size - headerSize);
302                     return false;
303                 }
304             } else {
305                 HLOGE("not enough header->size.");
306                 return false;
307             }
308         }
309     }
310     HLOGD("read back %zu records", recordNumber);
311 #ifdef HIPERF_DEBUG_TIME
312     readRecordTime_ += duration_cast<microseconds>(steady_clock::now() - startReadTime);
313 #endif
314     return true;
315 }
316 
Read(void * buf,size_t len)317 bool PerfFileReader::Read(void *buf, size_t len)
318 {
319     if (buf == nullptr || len == 0) {
320         HLOG_ASSERT(buf != nullptr);
321         HLOG_ASSERT(len > 0);
322         return false;
323     }
324 
325     if (fread(buf, len, 1, fp_) != 1) {
326         printf("failed to read file: %d", errno);
327         return false;
328     }
329     return true;
330 }
331 
GetHeader() const332 const perf_file_header &PerfFileReader::GetHeader() const
333 {
334     return header_;
335 }
336 
Read(char * buf,uint64_t offset,size_t len)337 bool PerfFileReader::Read(char *buf, uint64_t offset, size_t len)
338 {
339     if (buf == nullptr || len == 0) {
340         HLOG_ASSERT(buf != nullptr);
341         HLOG_ASSERT(len > 0);
342         return false;
343     }
344     if (fseek(fp_, offset, SEEK_SET) != 0) {
345         HLOGE("fseek() failed");
346         return false;
347     }
348 
349     if (fread(buf, len, 1, fp_) != 1) {
350         printf("failed to read file: %d", errno);
351         return false;
352     }
353     HLOGM("offset %" PRIx64 " len %zu buf %x %x %x %x", offset, len, buf[0], buf[1], buf[2],
354           buf[3]);
355     return true;
356 }
GetFeatures() const357 const std::vector<FEATURE> &PerfFileReader::GetFeatures() const
358 {
359     return features_;
360 }
361 
GetFeatureSections() const362 const std::vector<std::unique_ptr<PerfFileSection>> &PerfFileReader::GetFeatureSections() const
363 {
364     return perfFileSections_;
365 }
366 
GetFeatureString(const FEATURE feature) const367 const std::string PerfFileReader::GetFeatureString(const FEATURE feature) const
368 {
369     std::string featureName = PerfFileSection::GetFeatureName(feature);
370     HLOGV("GetFeatureSection %s", featureName.c_str());
371     if (!IsFeatrureStringSection(feature)) {
372         HLOGV("not a string feature: %s", featureName.c_str());
373     } else {
374         const PerfFileSection *featureSection = GetFeatureSection(feature);
375         if (featureSection != nullptr) {
376             const PerfFileSectionString *sectionString =
377                 static_cast<const PerfFileSectionString *>(featureSection);
378             return sectionString->toString();
379         } else {
380             HLOGV("have not found: %s", featureName.c_str());
381         }
382     }
383     return EMPTY_STRING;
384 }
385 
GetFeatureSection(FEATURE feature) const386 const PerfFileSection *PerfFileReader::GetFeatureSection(FEATURE feature) const
387 {
388     HLOGV("enter");
389     for (auto const &it : perfFileSections_) {
390         HLOGV("perfFileSections %p", it.get());
391         if (it->featureId_ == feature) {
392             return it.get();
393         }
394     }
395     return nullptr;
396 }
397 
ReadFeatureSection()398 bool PerfFileReader::ReadFeatureSection()
399 {
400     uint64_t featureSectionOffsetRead = featureSectionOffset_;
401     HLOGV(" ReadDataSection data offset '0x%" PRIx64 " ", featureSectionOffset_);
402 
403     for (FEATURE feature : features_) {
404         perf_file_section sectionHeader;
405         if (!Read((char *)&sectionHeader, featureSectionOffsetRead, sizeof(sectionHeader))) {
406             // read failed ??
407             printf("file format not correct. featureSectionOffsetRead '0x%" PRIx64 "\n",
408                    featureSectionOffsetRead);
409             return false;
410         }
411 
412         HLOGV("process feature %d:%s", feature, PerfFileSection::GetFeatureName(feature).c_str());
413         HLOGV(" sectionHeader -> read offset '0x%" PRIx64 " size '0x%" PRIx64 "'",
414               sectionHeader.offset, sectionHeader.size);
415         if (sectionHeader.size == 0 or sectionHeader.size > fileSize_) {
416             HLOGE("sectionHeader.size %" PRIu64 " is not correct", sectionHeader.size);
417             return false;
418         }
419 
420         std::vector<char> buf(sectionHeader.size);
421         if (!Read(&buf[0], sectionHeader.offset, buf.size())) {
422             // read failed ??
423             printf("file format not correct. featureSectionDataOffset '0x%" PRIx64 "\n",
424                    sectionHeader.offset);
425             return false;
426         }
427         if (IsFeatrureStringSection(feature)) {
428             perfFileSections_.emplace_back(
429                 std::make_unique<PerfFileSectionString>(feature, (char *)&buf[0], buf.size()));
430         } else if (feature == FEATURE::HIPERF_FILES_SYMBOL) {
431             perfFileSections_.emplace_back(std::make_unique<PerfFileSectionSymbolsFiles>(
432                 feature, (char *)&buf[0], buf.size()));
433         } else if (feature == FEATURE::EVENT_DESC) {
434             perfFileSections_.emplace_back(
435                 std::make_unique<PerfFileSectionEventDesc>(feature, (char *)&buf[0], buf.size()));
436         } else {
437             HLOGW("still not imp how to process with feature %d", feature);
438         }
439 
440         featureSectionOffsetRead += sizeof(sectionHeader); // next feaure
441     }
442     return true;
443 }
444 } // namespace HiPerf
445 } // namespace Developtools
446 } // namespace OHOS