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