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_format.h"
16
17 #include "debug_logger.h"
18 #include "hiperf_hilog.h"
19
20 namespace OHOS {
21 namespace Developtools {
22 namespace HiPerf {
23 static const std::vector<std::string> EXT_FEATURE_NAMES = {
24 "hiperf_files_symbol",
25 "hiperf_workloader_cmd",
26 "hiperf_record_time",
27 "hiperf_cpu_off",
28 "hiperf_hm_devhost",
29 "hiperf_stack_table",
30 };
31 static const std::vector<std::string> FEATURE_NAMES = {
32 "unknown_feature", "tracing_data", "build_id", "hostname", "osrelease",
33 "version", "arch", "nrcpus", "cpudesc", "cpuid",
34 "total_mem", "cmdline", "event_desc", "cpu_topology", "numa_topology",
35 "branch_stack", "pmu_mappings", "group_desc", "auxtrace", "stat",
36 "cache", "sample_time", "mem_topology", "last_feature",
37 };
38 #ifdef FUZZER_TEST
39 // issue from fuzz test and also will lead to PerfFileSectionSymbolsFiles uncompletely construct
40 static constexpr size_t MAX_SYMBOLS_FILE_NUMBER = 300;
41 static constexpr size_t MAX_SYMBOLS_NUMBER = 10000;
42 #endif
GetFeatureName(FEATURE featureId)43 std::string PerfFileSection::GetFeatureName(FEATURE featureId)
44 {
45 unsigned int index = static_cast<unsigned int>(featureId);
46 if (featureId >= FEATURE::HIPERF_FIRST_FEATURE) {
47 index -= static_cast<unsigned int>(FEATURE::HIPERF_FIRST_FEATURE);
48 if (index >= EXT_FEATURE_NAMES.size()) {
49 return FEATURE_NAMES[0];
50 }
51 return EXT_FEATURE_NAMES[index];
52 } else {
53 if (index >= FEATURE_NAMES.size()) {
54 return FEATURE_NAMES[0];
55 }
56 return FEATURE_NAMES[index];
57 }
58 }
59
60 // for read
Init(const char *buffer, size_t maxSize)61 void PerfFileSection::Init(const char *buffer, size_t maxSize)
62 {
63 rBuffer_ = buffer;
64 maxSize_ = maxSize;
65 offset_ = 0;
66 }
67
68 // for write
Init(char *buffer, size_t maxSize)69 void PerfFileSection::Init(char *buffer, size_t maxSize)
70 {
71 wBuffer_ = buffer;
72 maxSize_ = maxSize;
73 offset_ = 0;
74 }
75
Write(uint32_t u32)76 bool PerfFileSection::Write(uint32_t u32)
77 {
78 uint32_t value = u32;
79 return Write((char *)&value, sizeof(uint32_t));
80 }
81
Write(uint64_t u64)82 bool PerfFileSection::Write(uint64_t u64)
83 {
84 uint64_t value = u64;
85 return Write((char *)&value, sizeof(uint64_t));
86 }
87
Write(const std::string &str)88 bool PerfFileSection::Write(const std::string &str)
89 {
90 if (Write((uint32_t)str.size() + 1)) { // include the ending \0
91 return Write(str.c_str(), str.size(), str.size() + 1);
92 } else {
93 return false;
94 }
95 }
96
Write(const char *buf, size_t size)97 bool PerfFileSection::Write(const char *buf, size_t size)
98 {
99 return Write(buf, size, size);
100 }
101
Write(const char *buf, size_t size, size_t max)102 bool PerfFileSection::Write(const char *buf, size_t size, size_t max)
103 {
104 CHECK_TRUE(offset_ + size > maxSize_, false, 1,
105 "write out of size!!! offset_ %zu size %zu max %zu", offset_, size, maxSize_);
106 CHECK_TRUE(offset_ + max > maxSize_, false, 1,
107 "write out of size!!! offset_ %zu size %zu max %zu", offset_, size, maxSize_);
108 CHECK_TRUE(wBuffer_ == nullptr, false, 0, "");
109 std::copy(buf, buf + size, wBuffer_ + offset_);
110 if (size >= max) {
111 offset_ += size;
112 } else {
113 offset_ += max;
114 }
115 return true;
116 }
117
Read(uint32_t &value)118 bool PerfFileSection::Read(uint32_t &value)
119 {
120 static_assert(sizeof(uint32_t) == 4);
121 return Read((char *)&value, sizeof(uint32_t));
122 }
123
Read(uint64_t &value)124 bool PerfFileSection::Read(uint64_t &value)
125 {
126 static_assert(sizeof(uint64_t) == 8);
127
128 return Read((char *)&value, sizeof(uint64_t));
129 }
130
Read(std::string &value)131 bool PerfFileSection::Read(std::string &value)
132 {
133 uint32_t size = 0;
134 CHECK_TRUE(!Read(size), false, 0, "");
135 // if size large than buf size or 0 size ?
136 // don't assert for fuzz test
137 CHECK_TRUE(size == 0 or size > maxSize_, false, 0, "");
138 char buf[size];
139 CHECK_TRUE(!Read(buf, size), false, 0, "");
140 CHECK_TRUE(buf[size - 1] != 0, false, 0, "");
141 value = buf;
142 HLOGDUMMY("Read String size %u buf : %s", size, value.c_str());
143 return true;
144 }
Skip(size_t size)145 void PerfFileSection::Skip(size_t size)
146 {
147 offset_ += size;
148 }
149
Read(char *buf, size_t size)150 bool PerfFileSection::Read(char *buf, size_t size)
151 {
152 HLOG_ASSERT(buf != nullptr);
153 if (size == 0) {
154 HLOGE("read zero size!!! offset_ %zu size %zu max %zu", offset_, size, maxSize_);
155 return false;
156 } else if (offset_ + size > maxSize_) {
157 HLOGE("read out of size!!! offset_ %zu size %zu max %zu", offset_, size, maxSize_);
158 if (memset_s(buf, size, 0, size) != EOK) { // make sure the content return is 0 when failed
159 HLOGE("memset_s failed in PerfFileSection::Read");
160 return false;
161 }
162 return false;
163 }
164 HLOGD("PerfFileSection::Read offset_ %zu size %zu maxSize_ %zu", offset_, size, maxSize_);
165 std::copy((rBuffer_ + offset_), (rBuffer_ + offset_ + size), buf);
166 offset_ += size;
167 HLOGDUMMY("after read offset_ %zx size %zu buf %x", offset_, size, buf[0]);
168 return true;
169 }
170
SizeOf(std::string &string)171 uint32_t PerfFileSection::SizeOf(std::string &string)
172 {
173 return sizeof(uint32_t) + string.size() + 1; /* '\0' */
174 }
175
PerfFileSectionString(FEATURE id, const char *buf, size_t size)176 PerfFileSectionString::PerfFileSectionString(FEATURE id, const char *buf, size_t size)
177 : PerfFileSection(id)
178 {
179 Init(buf, size);
180 CHECK_TRUE(!Read(stdString_), NO_RETVAL, 0, ""); // or throw ...
181 }
182
PerfFileSectionString(FEATURE id, const std::string &charString)183 PerfFileSectionString::PerfFileSectionString(FEATURE id, const std::string &charString)
184 : PerfFileSection(id)
185 {
186 stdString_ = charString;
187 }
188
GetBinary(char *buf, size_t size)189 bool PerfFileSectionString::GetBinary(char *buf, size_t size)
190 {
191 CHECK_TRUE(size < GetSize(), false, 0, "");
192
193 Init(buf, size);
194 Write(stdString_);
195 return true;
196 }
197
198 size_t PerfFileSectionString::GetSize()
199 {
200 return SizeOf(stdString_);
201 }
202
203 const std::string PerfFileSectionString::ToString() const
204 {
205 return stdString_;
206 }
207
208 size_t PerfFileSectionSymbolsFiles::GetSize()
209 {
210 size_t size = 0;
211
212 size += sizeof(uint32_t); // how many SymbolFileStruct
213 for (auto &symbolFileStruct : symbolFileStructs_) {
214 size += SizeOf(symbolFileStruct.filePath_);
215 size += sizeof(symbolFileStruct.symbolType_);
216 size += sizeof(symbolFileStruct.textExecVaddr_);
217 size += sizeof(symbolFileStruct.textExecVaddrFileOffset_);
218 size += SizeOf(symbolFileStruct.buildId_);
219
220 size += sizeof(uint32_t); // how many SymbolStruct
221 for (auto &symbolStruct : symbolFileStruct.symbolStructs_) {
222 size += sizeof(symbolStruct.vaddr_);
223 size += sizeof(symbolStruct.len_);
224 size += SizeOf(symbolStruct.symbolName_);
225 }
226 }
227 return size;
228 }
229
230 void PerfFileSectionSymbolsFiles::ReadSymbolFileStructs()
231 {
232 uint32_t symbolFileNumber = 0;
233 if (!Read(symbolFileNumber)) {
234 HLOGE(" symbolFileNumber read failed");
235 return;
236 #ifdef FUZZER_TEST
237 } else if (symbolFileNumber > MAX_SYMBOLS_FILE_NUMBER) {
238 HLOGE(" symbolFileNumber %u too large", symbolFileNumber);
239 return;
240 #endif
241 } else {
242 HLOGV(" symbolFileNumber %u", symbolFileNumber);
243 }
244
245 for (uint32_t i = symbolFileNumber; i > 0; i--) {
246 auto &symbolFileStruct = symbolFileStructs_.emplace_back();
247
248 Read(symbolFileStruct.filePath_);
249 HLOGV(" symbolFileStruct.filePath_ %s", symbolFileStruct.filePath_.c_str());
250
251 Read(symbolFileStruct.symbolType_);
252 Read(symbolFileStruct.textExecVaddr_);
253 Read(symbolFileStruct.textExecVaddrFileOffset_);
254 Read(symbolFileStruct.buildId_);
255
256 uint32_t symbolsNumber = 0;
257 if (!Read(symbolsNumber)) {
258 HLOGE(" symbols read failed");
259 return;
260 #ifdef FUZZER_TEST
261 } else if (symbolsNumber > MAX_SYMBOLS_NUMBER) {
262 HLOGE(" symbols %u too large", symbolsNumber);
263 return;
264 #endif
265 } else {
266 HLOGV(" symbols %u", symbolsNumber);
267 }
268 for (; symbolsNumber > 0; symbolsNumber--) {
269 auto &symbolStruct = symbolFileStruct.symbolStructs_.emplace_back();
270 Read(symbolStruct.vaddr_);
271 Read(symbolStruct.len_);
272 Read(symbolStruct.symbolName_);
273 }
274 HLOGV(" %zu SymbolStruct read.", symbolFileStruct.symbolStructs_.size());
275 }
276 }
277
PerfFileSectionSymbolsFiles(FEATURE id, const char *buf, size_t size)278 PerfFileSectionSymbolsFiles::PerfFileSectionSymbolsFiles(FEATURE id, const char *buf, size_t size)
279 : PerfFileSection(id)
280 {
281 Init(buf, size);
282 ReadSymbolFileStructs();
283 HLOGV(" %zu SymbolFileStruct read.", symbolFileStructs_.size());
284 }
285
GetBinary(char *buf, size_t size)286 bool PerfFileSectionSymbolsFiles::GetBinary(char *buf, size_t size)
287 {
288 HLOGV("PerfFileSectionSymbolsFiles get buffer size %zu.", size);
289 HLOG_ASSERT(size >= GetSize());
290
291 Init(buf, size);
292 CHECK_TRUE(!Write((uint32_t)symbolFileStructs_.size()), false, 1,
293 "PerfFileSectionSymbolsFiles write failed with %zu.", symbolFileStructs_.size());
294 for (auto &symbolFileStruct : symbolFileStructs_) {
295 Write(symbolFileStruct.filePath_);
296 Write(symbolFileStruct.symbolType_);
297 Write(symbolFileStruct.textExecVaddr_);
298 Write(symbolFileStruct.textExecVaddrFileOffset_);
299 Write(symbolFileStruct.buildId_);
300
301 Write((uint32_t)symbolFileStruct.symbolStructs_.size());
302 for (auto &symbolStruct : symbolFileStruct.symbolStructs_) {
303 Write(symbolStruct.vaddr_);
304 Write(symbolStruct.len_);
305 Write(symbolStruct.symbolName_);
306 }
307 HLOGV(" %zu SymbolStruct writed. for %s at 0x%016" PRIx64 "@0x%08" PRIx64 ": %s",
308 symbolFileStruct.symbolStructs_.size(), symbolFileStruct.filePath_.c_str(),
309 symbolFileStruct.textExecVaddr_, symbolFileStruct.textExecVaddrFileOffset_,
310 symbolFileStruct.buildId_.c_str());
311 }
312 HLOGV("%zu SymbolFileStruct writed.", symbolFileStructs_.size());
313
314 return true;
315 }
316
PerfFileSectionUniStackTable(FEATURE id, const char *buf, size_t size)317 PerfFileSectionUniStackTable::PerfFileSectionUniStackTable(FEATURE id, const char *buf, size_t size)
318 : PerfFileSection(id)
319 {
320 uint32_t processTableCount;
321 Init(buf, size);
322 if (!Read(processTableCount)) {
323 HLOGV("processTableCount read failed\n");
324 return;
325 } else {
326 HLOGV("processTableCount %" PRIu32 "\n", processTableCount);
327 }
328 for (uint32_t i = 0; i < processTableCount; ++i) {
329 UniStackTableInfo& stackTable = uniStackTableInfos_.emplace_back();
330 Read(stackTable.pid);
331 HLOGV("pid %" PRIu32 " ", stackTable.pid);
332 Read(stackTable.tableSize);
333 HLOGV("tableSize %" PRIu32 " ", stackTable.tableSize);
334 Read(stackTable.numNodes);
335 HLOGV("numNodes %" PRIu32 " ", stackTable.numNodes);
336 for (size_t j = 0; j < stackTable.numNodes; j++) {
337 UniStackNode& node = stackTable.nodes.emplace_back();
338 Read(node.index);
339 Read(node.node.value);
340 }
341 }
342 }
343
GetBinary(char *buf, size_t size)344 bool PerfFileSectionUniStackTable::GetBinary(char *buf, size_t size)
345 {
346 HLOG_ASSERT(size >= GetSize());
347 Init(buf, size);
348 Write(uint32_t(processStackTable_->size()));
349 for (auto it = processStackTable_->begin(); it != processStackTable_->end(); ++it) {
350 const auto &table = it->second;
351 if (table == nullptr) {
352 continue;
353 }
354 Write(table->GetPid());
355 Write(table->GetTabelSize());
356 const auto &idxs = table->GetUsedIndexes();
357 Write(uint32_t(idxs.size()));
358 Node *head = table->GetHeadNode();
359 Node *node = nullptr;
360 for (const auto idx : idxs) {
361 node = head + idx;
362 if (node == nullptr) {
363 continue;
364 }
365 Write(idx);
366 Write(node->value);
367 }
368 }
369 return true;
370 }
371
GetSize()372 size_t PerfFileSectionUniStackTable::GetSize()
373 {
374 CHECK_TRUE(processStackTable_ == nullptr, 0, 0, "");
375 size_t size = 0;
376 // section header info size
377 size += sizeof(uint32_t); // how many tables/process
378 for (auto it = processStackTable_->begin(); it != processStackTable_->end(); ++it) {
379 size += it->second->GetWriteSize();
380 }
381 return size;
382 }
383
PerfFileSectionNrCpus(FEATURE id, const char *buf, size_t size)384 PerfFileSectionNrCpus::PerfFileSectionNrCpus(FEATURE id, const char *buf, size_t size)
385 : PerfFileSection(id)
386 {
387 Init(buf, size);
388 CHECK_TRUE(!Read(nrCpusAvailable_) || !Read(nrCpusOnline_), NO_RETVAL, 0, "");
389 }
390
PerfFileSectionNrCpus(FEATURE id, uint32_t nrCpusAvailable, uint32_t nrCpusOnline)391 PerfFileSectionNrCpus::PerfFileSectionNrCpus(FEATURE id, uint32_t nrCpusAvailable,
392 uint32_t nrCpusOnline)
393 : PerfFileSection(id), nrCpusAvailable_(nrCpusAvailable), nrCpusOnline_(nrCpusOnline)
394 {
395 }
396
GetBinary(char *buf, size_t size)397 bool PerfFileSectionNrCpus::GetBinary(char *buf, size_t size)
398 {
399 CHECK_TRUE(size < GetSize(), false, 0, "");
400
401 Init(buf, size);
402 Write(nrCpusAvailable_);
403 Write(nrCpusOnline_);
404 return true;
405 }
406
407 size_t PerfFileSectionNrCpus::GetSize()
408 {
409 return (sizeof(nrCpusAvailable_) + sizeof(nrCpusOnline_));
410 }
411
412 void PerfFileSectionNrCpus::GetValue(uint32_t &nrCpusAvailable, uint32_t &nrCpusOnline) const
413 {
414 nrCpusAvailable = nrCpusAvailable_;
415 nrCpusOnline = nrCpusOnline_;
416 }
417
418 PerfFileSectionU64::PerfFileSectionU64(FEATURE id, const char *buf, size_t size)
419 : PerfFileSection(id)
420 {
421 Init(buf, size);
422 CHECK_TRUE(!Read(value_), NO_RETVAL, 0, "");
423 }
424
425 PerfFileSectionU64::PerfFileSectionU64(FEATURE id, uint64_t v) : PerfFileSection(id)
426 {
427 value_ = v;
428 }
429
430 bool PerfFileSectionU64::GetBinary(char *buf, size_t size)
431 {
432 CHECK_TRUE(size < GetSize(), false, 0, "");
433
434 Init(buf, size);
435 Write(value_);
436 return true;
437 }
438
439 size_t PerfFileSectionU64::GetSize()
440 {
441 return sizeof(value_);
442 }
443
444 void PerfFileSectionU64::GetValue(uint64_t &v) const
445 {
446 v = value_;
447 }
448
449 PerfFileSectionEventDesc::PerfFileSectionEventDesc(FEATURE id,
450 const std::vector<AttrWithId> &eventDesces)
451 : PerfFileSection(id)
452 {
453 eventDesces_ = eventDesces;
454 }
455
456 PerfFileSectionEventDesc::PerfFileSectionEventDesc(FEATURE id, const char *buf, size_t size)
457 : PerfFileSection(id)
458 {
459 constexpr uint32_t maxIds = 600;
460 Init(buf, size);
461 uint32_t nr = 0;
462 CHECK_TRUE(!Read(nr), NO_RETVAL, 0, "");
463 uint32_t attrSize = 0;
464 CHECK_TRUE(!Read(attrSize), NO_RETVAL, 0, "");
465 if (attrSize != sizeof(perf_event_attr)) { // only for log or debug
466 HLOGW("perf_event_attr version is different, attrSize %d vs %zu", attrSize,
467 sizeof(perf_event_attr));
468 }
469
470 for (; nr > 0; nr--) {
471 AttrWithId eventDesc;
472 // compatible with the different version of 'perf_event_attr'
sizeof(perf_event_attr)473 if (attrSize > sizeof(perf_event_attr)) {
474 if (!Read(reinterpret_cast<char*>(&(eventDesc.attr)), sizeof(perf_event_attr))) {
475 return;
476 }
477 // skip tail bytes
478 HLOGW("skip %zu byte for diff attr size", attrSize - sizeof(perf_event_attr));
479 Skip(attrSize - sizeof(perf_event_attr));
480 } else if (!Read(reinterpret_cast<char*>(&(eventDesc.attr)), attrSize)) {
481 return;
482 }
483
484 uint32_t nrIds = 0;
485 if (!Read(nrIds)) {
486 return;
487 } else if (nrIds == 0) {
488 HLOGW("nrIds is not correct ! %u", nrIds);
489 return;
490 } else if (nrIds > maxIds) {
491 HLOGW("nrIds is too large ! %u", nrIds);
492 }
493 CHECK_TRUE(!Read(eventDesc.name), NO_RETVAL, 0, "");
494 eventDesc.ids.resize(nrIds, 0);
495 CHECK_TRUE(!Read(reinterpret_cast<char*>(eventDesc.ids.data()), sizeof(uint64_t) * nrIds), NO_RETVAL, 0, "");
496 eventDesces_.emplace_back(std::move(eventDesc));
497 }
498 HLOGV("read complete. %zu events", eventDesces_.size());
499 }
500
GetBinary(char *buf, size_t size)501 bool PerfFileSectionEventDesc::GetBinary(char *buf, size_t size)
502 {
503 CHECK_TRUE(size < GetSize(), false, 0, "");
504 Init(buf, size);
505
506 CHECK_TRUE(!Write(static_cast<uint32_t>(eventDesces_.size())), false, 0, "");
507 CHECK_TRUE(!Write(static_cast<uint32_t>(sizeof(perf_event_attr))), false, 0, "");
508 for (auto &eventDesc : eventDesces_) {
509 CHECK_TRUE(!Write(reinterpret_cast<char*>(&(eventDesc.attr)), sizeof(perf_event_attr)), false, 0, "");
510 CHECK_TRUE(!Write(static_cast<uint32_t>(eventDesc.ids.size())), false, 0, "");
511 CHECK_TRUE(!Write(eventDesc.name), false, 0, "");
512 // clang-format off
513 CHECK_TRUE(!Write(reinterpret_cast<char*>(eventDesc.ids.data()), sizeof(uint64_t) * eventDesc.ids.size()),
514 false, 0, ""); // clang-format on
515 }
516 return true;
517 }
518
519 size_t PerfFileSectionEventDesc::GetSize()
520 {
521 size_t size = sizeof(uint32_t); // nr
522 size += sizeof(uint32_t); // attr_size
523
524 size += (eventDesces_.size() * sizeof(perf_event_attr));
525 size += (eventDesces_.size() * sizeof(uint32_t)); // nr_ids
526 for (auto &eventDesc : eventDesces_) {
527 size += SizeOf(eventDesc.name);
528 size += (sizeof(uint64_t) * eventDesc.ids.size());
529 }
530 return size;
531 }
532
533 void PerfFileSectionEventDesc::GetValue(std::vector<AttrWithId> &eventDesces) const
534 {
535 eventDesces = eventDesces_;
536 }
537 } // namespace HiPerf
538 } // namespace Developtools
539 } // namespace OHOS
540