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