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