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 }
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 HLOGE("not enough header->size.");
273 return false;
274 }
275 size_t headerSize = sizeof(perf_event_header);
276 if (!Read(buf + headerSize, header->size - headerSize)) {
277 HLOGE("read record data size failed %zu", header->size - headerSize);
278 return false;
279 }
280 size_t speSize = 0;
281 if (header->type == PERF_RECORD_AUXTRACE) {
282 struct PerfRecordAuxtraceData *auxtrace =
283 reinterpret_cast<struct PerfRecordAuxtraceData *>(header + 1);
284 speSize = auxtrace->size;
285 if (speSize > 0) {
286 Read(buf + header->size, auxtrace->size);
287 }
288 }
289 uint8_t *data = buf;
290 PerfEventRecord& record = PerfEventRecordFactory::GetPerfEventRecord(
291 static_cast<perf_event_type>(header->type), data, *attr);
292 // unknown record , break the process
293 if (record.GetName() == nullptr) {
294 return false;
295 }
296 HLOGV("record type %u", record.GetType());
297 remainingSize = remainingSize - header->size - speSize;
298 #ifdef HIPERF_DEBUG_TIME
299 const auto startCallbackTime = steady_clock::now();
300 #endif
301 // call callback to process, then destroy record
302 callback(record);
303 recordNumber++;
304 #ifdef HIPERF_DEBUG_TIME
305 readCallbackTime_ +=
306 duration_cast<microseconds>(steady_clock::now() - startCallbackTime);
307 #endif
308 }
309 HLOGD("read back %zu records", recordNumber);
310 #ifdef HIPERF_DEBUG_TIME
311 readRecordTime_ += duration_cast<microseconds>(steady_clock::now() - startReadTime);
312 #endif
313 return true;
314 }
315
Read(void * buf,const size_t len)316 bool PerfFileReader::Read(void *buf, const size_t len)
317 {
318 if (buf == nullptr || len == 0) {
319 HLOG_ASSERT(buf != nullptr);
320 HLOG_ASSERT(len > 0);
321 return false;
322 }
323
324 if (fread(buf, len, 1, fp_) != 1) {
325 printf("failed to read file: %d", errno);
326 return false;
327 }
328 return true;
329 }
330
GetHeader() const331 const perf_file_header &PerfFileReader::GetHeader() const
332 {
333 return header_;
334 }
335
Read(char * buf,const uint64_t offset,const size_t len)336 bool PerfFileReader::Read(char *buf, const uint64_t offset, const size_t len)
337 {
338 if (buf == nullptr || len == 0) {
339 HLOG_ASSERT(buf != nullptr);
340 HLOG_ASSERT(len > 0);
341 return false;
342 }
343 if (fseek(fp_, offset, SEEK_SET) != 0) {
344 HLOGE("fseek() failed");
345 return false;
346 }
347
348 CHECK_TRUE(fread(buf, len, 1, fp_) == 1, false, LOG_TYPE_PRINTF, "failed to read file: %d", errno);
349 HLOGM("offset %" PRIx64 " len %zu buf %x %x %x %x", offset, len, buf[0], buf[1], buf[2],
350 buf[3]);
351 return true;
352 }
GetFeatures() const353 const std::vector<FEATURE> &PerfFileReader::GetFeatures() const
354 {
355 return features_;
356 }
357
GetFeatureSections() const358 const std::vector<std::unique_ptr<PerfFileSection>> &PerfFileReader::GetFeatureSections() const
359 {
360 return perfFileSections_;
361 }
362
GetFeatureString(const FEATURE feature) const363 const std::string PerfFileReader::GetFeatureString(const FEATURE feature) const
364 {
365 std::string featureName = PerfFileSection::GetFeatureName(feature);
366 HLOGV("GetFeatureSection %s", featureName.c_str());
367 if (!IsFeatrureStringSection(feature)) {
368 HLOGV("not a string feature: %s", featureName.c_str());
369 } else {
370 const PerfFileSection *featureSection = GetFeatureSection(feature);
371 if (featureSection != nullptr) {
372 const PerfFileSectionString *sectionString =
373 static_cast<const PerfFileSectionString *>(featureSection);
374 return sectionString->ToString();
375 } else {
376 HLOGV("have not found: %s", featureName.c_str());
377 }
378 }
379 return EMPTY_STRING;
380 }
381
GetFeatureSection(const FEATURE feature) const382 const PerfFileSection *PerfFileReader::GetFeatureSection(const FEATURE feature) const
383 {
384 for (auto const &it : perfFileSections_) {
385 HLOGV("perfFileSections");
386 if (it->featureId_ == feature) {
387 return it.get();
388 }
389 }
390 return nullptr;
391 }
392
ReadFeatureSection()393 bool PerfFileReader::ReadFeatureSection()
394 {
395 uint64_t featureSectionOffsetRead = featureSectionOffset_;
396 HLOGV(" ReadDataSection data offset '0x%" PRIx64 " ", featureSectionOffset_);
397
398 for (FEATURE feature : features_) {
399 perf_file_section sectionHeader;
400 // read failed ??
401 CHECK_TRUE(Read((char *)§ionHeader, featureSectionOffsetRead, sizeof(sectionHeader)),
402 false, LOG_TYPE_PRINTF,
403 "file format not correct. featureSectionOffsetRead '0x%" PRIx64 "\n", featureSectionOffsetRead);
404
405 HLOGV("process feature %d:%s", feature, PerfFileSection::GetFeatureName(feature).c_str());
406 HLOGV(" sectionHeader -> read offset '0x%" PRIx64 " size '0x%" PRIx64 "'",
407 sectionHeader.offset, sectionHeader.size);
408 CHECK_TRUE(sectionHeader.size != 0 && sectionHeader.size <= fileSize_, false, 1,
409 "sectionHeader.size %" PRIu64 " is not correct", sectionHeader.size);
410
411 std::vector<char> buf(sectionHeader.size);
412 // read failed ??
413 CHECK_TRUE(Read(&buf[0], sectionHeader.offset, buf.size()), false, LOG_TYPE_PRINTF,
414 "file format not correct. featureSectionDataOffset '0x%" PRIx64 "\n", sectionHeader.offset);
415 if (IsFeatrureStringSection(feature)) {
416 perfFileSections_.emplace_back(
417 std::make_unique<PerfFileSectionString>(feature, (char *)&buf[0], buf.size()));
418 } else if (feature == FEATURE::HIPERF_FILES_SYMBOL) {
419 perfFileSections_.emplace_back(std::make_unique<PerfFileSectionSymbolsFiles>(
420 feature, (char *)&buf[0], buf.size()));
421 } else if (feature == FEATURE::EVENT_DESC) {
422 perfFileSections_.emplace_back(
423 std::make_unique<PerfFileSectionEventDesc>(feature, (char *)&buf[0], buf.size()));
424 } else if (feature == FEATURE::HIPERF_FILES_UNISTACK_TABLE) {
425 perfFileSections_.emplace_back(
426 std::make_unique<PerfFileSectionUniStackTable>(feature, (char *)&buf[0], buf.size()));
427 PerfRecordSample::SetDumpRemoveStack(true);
428 } else {
429 HLOGW("still not imp how to process with feature %d", feature);
430 }
431
432 featureSectionOffsetRead += sizeof(sectionHeader); // next feaure
433 }
434 return true;
435 }
436 } // namespace HiPerf
437 } // namespace Developtools
438 } // namespace OHOS
439