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
Instance(const std::string & fileName)36 std::unique_ptr<PerfFileReader> PerfFileReader::Instance(const std::string &fileName)
37 {
38 std::string resolvedPath = CanonicalizeSpecPath(fileName.c_str());
39 FILE *fp = fopen(resolvedPath.c_str(), "rb");
40 if (fp == nullptr) {
41 HLOGE("fail to open file %s", fileName.c_str());
42 return nullptr;
43 }
44
45 std::unique_ptr<PerfFileReader> reader = std::make_unique<PerfFileReader>(fileName, fp);
46 if (!reader->ReadFileHeader()) {
47 // Fail to read header, maybe its compressed
48 if (reader->IsGzipFile()) {
49 fclose(fp);
50 reader->fp_ = nullptr;
51
52 CHECK_TRUE(!UncompressFile(fileName, ".perf.data"), nullptr, 1,
53 "Fail to UncompressFile(%s)", fileName.c_str());
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 CHECK_TRUE(!reader->ReadAttrSection(), nullptr, 0, "");
75 return reader;
76 }
77
PerfFileReader(const std::string & fileName,FILE * fp)78 PerfFileReader::PerfFileReader(const std::string &fileName, FILE *fp) : fp_(fp), fileName_(fileName)
79 {
80 featureSectionOffset_ = 0;
81 struct stat fileStat;
82 if (fp != nullptr) {
83 if (fstat(fileno(fp), &fileStat) != -1 and fileStat.st_size > 0) {
84 fileSize_ = fileStat.st_size;
85 }
86 }
87 }
88
~PerfFileReader()89 PerfFileReader::~PerfFileReader()
90 {
91 // if file was not closed properly
92 if (fp_ != nullptr) {
93 fclose(fp_);
94 fp_ = nullptr;
95 }
96
97 // remove the uncompressed .perf.data
98 if (compressData_) {
99 if (remove(".perf.data") != 0) {
100 HLOGE("Fail to remove uncompressed file .perf.data");
101 perror("Fail to remove temp file");
102 }
103 }
104 }
105
IsValidDataFile()106 bool PerfFileReader::IsValidDataFile()
107 {
108 return (memcmp(header_.magic, PERF_MAGIC, sizeof(header_.magic)) == 0);
109 }
110
IsGzipFile()111 bool PerfFileReader::IsGzipFile()
112 {
113 return header_.magic[0] == '\x1f' and header_.magic[1] == '\x8b';
114 }
115
ReadFileHeader()116 bool PerfFileReader::ReadFileHeader()
117 {
118 if (Read(&header_, sizeof(header_))) {
119 dataSectionSize_ = header_.data.size;
120 if (IsValidDataFile()) {
121 featureSectionOffset_ = header_.data.offset + header_.data.size;
122 for (int i = 0; i < FETURE_MAX / SIZE_FETURE_COUNT; i++) {
123 std::bitset<SIZE_FETURE_COUNT> features(header_.features[i]);
124 for (int j = 0; j < SIZE_FETURE_COUNT; j++) {
125 if (features.test(j)) {
126 features_.emplace_back((FEATURE)(((uint64_t)i) * SIZE_FETURE_COUNT + j));
127 }
128 }
129 }
130 return true;
131 }
132 }
133 return false;
134 }
135
ReadAttrSection()136 bool PerfFileReader::ReadAttrSection()
137 {
138 if (header_.attrSize != sizeof(perf_file_attr)) {
139 // 4.19 and 5.1 use diff size , 128 vs 136
140 HLOGW("attr size %" PRId64 " doesn't match expected size %zu", header_.attrSize,
141 sizeof(perf_file_attr));
142 }
143 CHECK_TRUE(header_.attrSize == 0, false, 0, "");
144 CHECK_TRUE(header_.attrSize > THOUSANDS, false, 1, "attr size exceeds 1000");
145 int attrCount = header_.attrs.size / header_.attrSize;
146 CHECK_TRUE(attrCount == 0, false, 1, "no attr in file");
147 if (fseek(fp_, header_.attrs.offset, SEEK_SET) != 0) {
148 HLOGE("fseek() failed");
149 return false;
150 }
151 for (int i = 0; i < attrCount; ++i) {
152 std::vector<char> buf(header_.attrSize);
153 CHECK_TRUE(!Read(buf.data(), buf.size()), false, 0, "");
154 // size of perf_event_attr change between different linux kernel versions.
155 // can not memcpy to perf_file_attr as a whole
156 perf_file_attr attr {};
157 size_t attrSize = header_.attrSize - sizeof(attr.ids);
158
159 // If the size is smaller, you can use a pointer to point directly.
160 // Our UAPI is 4.19. is less than 5.1
161 if (header_.attrSize < sizeof(perf_event_attr)) {
162 HLOGE("size not match, ptr of perf_event_attr maybe overfollow %zu vs %zu",
163 sizeof(perf_event_attr), attrSize);
164 }
165
166 attr.attr = *(reinterpret_cast<perf_event_attr *>(&buf[0]));
167 attr.ids = *(reinterpret_cast<perf_file_section *>(&buf[attrSize]));
168 vecAttr_.push_back(attr);
169 }
170
171 // read ids for attr
172 for (size_t i = 0; i < vecAttr_.size(); ++i) {
173 std::vector<uint64_t> ids;
174 CHECK_TRUE(!ReadIdsForAttr(vecAttr_[i], &ids), false, 0, "");
175 vecAttrIds_.push_back(ids);
176
177 // map ids to attr index
178 for (auto id : ids) {
179 mapId2Attr_[id] = i;
180 }
181 }
182
183 return true;
184 }
185
ReadIdsForAttr(const perf_file_attr & attr,std::vector<uint64_t> * ids)186 bool PerfFileReader::ReadIdsForAttr(const perf_file_attr &attr, std::vector<uint64_t> *ids)
187 {
188 if (attr.ids.size > 0) {
189 size_t count = attr.ids.size / sizeof(uint64_t) + 1;
190 if (count > MAX_VECTOR_RESIZE_COUNT) {
191 HLOGE("count(%zu) out of range", count);
192 return false;
193 }
194 if (fseek(fp_, attr.ids.offset, SEEK_SET) != 0) {
195 HLOGE("fseek() failed");
196 return false;
197 }
198 CHECK_TRUE(ids == nullptr, false, 0, "");
199 ids->resize(count);
200 CHECK_TRUE(!Read(ids->data(), attr.ids.size), false, 0, "");
201 }
202 return true;
203 }
204
GetAttrSection() const205 std::vector<AttrWithId> PerfFileReader::GetAttrSection() const
206 {
207 std::vector<AttrWithId> result(vecAttr_.size());
208
209 for (size_t i = 0; i < vecAttr_.size(); ++i) {
210 result[i].attr = vecAttr_[i].attr;
211 result[i].ids = vecAttrIds_[i];
212 }
213 return result;
214 }
215
ReadDataSection(ProcessRecordCB & callback)216 bool PerfFileReader::ReadDataSection(ProcessRecordCB &callback)
217 {
218 if (fseek(fp_, header_.data.offset, SEEK_SET) != 0) {
219 HLOGE("fseek() failed");
220 return false;
221 }
222
223 HLOGD("dataSection_ at offset %" PRId64 " + %" PRId64 "", header_.data.offset,
224 header_.data.size);
225
226 CHECK_TRUE(!ReadRecord(callback), false, LOG_TYPE_PRINTF, "some record format is error!\n");
227
228 #ifdef HIPERF_DEBUG_TIME
229 printf("readRecordTime: %" PRId64 " ms\n",
230 duration_cast<milliseconds>(readRecordTime_).count());
231 printf("readCallbackTime: %" PRId64 " ms\n",
232 duration_cast<milliseconds>(readCallbackTime_).count());
233 #endif
234 return dataSectionSize_ == 0;
235 }
236
GetDefaultAttr()237 const perf_event_attr *PerfFileReader::GetDefaultAttr()
238 {
239 CHECK_TRUE(vecAttr_.empty(), nullptr, 0, "");
240 return &(vecAttr_[0].attr);
241 }
242
ReadRecord(ProcessRecordCB & callback)243 bool PerfFileReader::ReadRecord(ProcessRecordCB &callback)
244 {
245 #ifdef HIPERF_DEBUG_TIME
246 const auto startReadTime = steady_clock::now();
247 #endif
248 // record size can not exceed 64K
249 HIPERF_BUF_ALIGN static uint8_t buf[RECORD_SIZE_LIMIT_SPE];
250 // diff with reader
251 uint64_t remainingSize = header_.data.size;
252 size_t recordNumber = 0;
253 const perf_event_attr *attr = GetDefaultAttr();
254 CHECK_TRUE(attr == nullptr, false, 1, "attr is null");
255 while (remainingSize > 0) {
256 if (remainingSize < sizeof(perf_event_header)) {
257 HLOGW("not enough sizeof perf_event_header");
258 return false;
259 } else if (!Read(buf, sizeof(perf_event_header))) {
260 HLOGW("read perf_event_header failed.");
261 return false;
262 } else {
263 perf_event_header *header = reinterpret_cast<perf_event_header *>(buf);
264 if (header == nullptr) {
265 HLOGE("read record header is null");
266 return false;
267 } else if (header->size > RECORD_SIZE_LIMIT) {
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 std::unique_ptr<PerfEventRecord> record = GetPerfEventRecord(
285 static_cast<perf_event_type>(header->type), data, *attr);
286 // unknown record , break the process
287 if (!record) {
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(std::move(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 *)§ionHeader, 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 or 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::dumpRemoveStack_ = 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