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
20namespace OHOS {
21namespace Developtools {
22namespace HiPerf {
23static 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};
31static 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
40static constexpr size_t MAX_SYMBOLS_FILE_NUMBER = 300;
41static constexpr size_t MAX_SYMBOLS_NUMBER = 10000;
42#endif
43std::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
61void PerfFileSection::Init(const char *buffer, size_t maxSize)
62{
63    rBuffer_ = buffer;
64    maxSize_ = maxSize;
65    offset_ = 0;
66}
67
68// for write
69void PerfFileSection::Init(char *buffer, size_t maxSize)
70{
71    wBuffer_ = buffer;
72    maxSize_ = maxSize;
73    offset_ = 0;
74}
75
76bool PerfFileSection::Write(uint32_t u32)
77{
78    uint32_t value = u32;
79    return Write((char *)&value, sizeof(uint32_t));
80}
81
82bool PerfFileSection::Write(uint64_t u64)
83{
84    uint64_t value = u64;
85    return Write((char *)&value, sizeof(uint64_t));
86}
87
88bool 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
97bool PerfFileSection::Write(const char *buf, size_t size)
98{
99    return Write(buf, size, size);
100}
101
102bool 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
118bool PerfFileSection::Read(uint32_t &value)
119{
120    static_assert(sizeof(uint32_t) == 4);
121    return Read((char *)&value, sizeof(uint32_t));
122}
123
124bool PerfFileSection::Read(uint64_t &value)
125{
126    static_assert(sizeof(uint64_t) == 8);
127
128    return Read((char *)&value, sizeof(uint64_t));
129}
130
131bool 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}
145void PerfFileSection::Skip(size_t size)
146{
147    offset_ += size;
148}
149
150bool 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
171uint32_t PerfFileSection::SizeOf(std::string &string)
172{
173    return sizeof(uint32_t) + string.size() + 1; /* '\0' */
174}
175
176PerfFileSectionString::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
183PerfFileSectionString::PerfFileSectionString(FEATURE id, const std::string &charString)
184    : PerfFileSection(id)
185{
186    stdString_ = charString;
187}
188
189bool 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
198size_t PerfFileSectionString::GetSize()
199{
200    return SizeOf(stdString_);
201}
202
203const std::string PerfFileSectionString::ToString() const
204{
205    return stdString_;
206}
207
208size_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
230void 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
278PerfFileSectionSymbolsFiles::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
286bool 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
317PerfFileSectionUniStackTable::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
344bool 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
372size_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
384PerfFileSectionNrCpus::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
391PerfFileSectionNrCpus::PerfFileSectionNrCpus(FEATURE id, uint32_t nrCpusAvailable,
392                                             uint32_t nrCpusOnline)
393    : PerfFileSection(id), nrCpusAvailable_(nrCpusAvailable), nrCpusOnline_(nrCpusOnline)
394{
395}
396
397bool 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
407size_t PerfFileSectionNrCpus::GetSize()
408{
409    return (sizeof(nrCpusAvailable_) + sizeof(nrCpusOnline_));
410}
411
412void PerfFileSectionNrCpus::GetValue(uint32_t &nrCpusAvailable, uint32_t &nrCpusOnline) const
413{
414    nrCpusAvailable = nrCpusAvailable_;
415    nrCpusOnline = nrCpusOnline_;
416}
417
418PerfFileSectionU64::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
425PerfFileSectionU64::PerfFileSectionU64(FEATURE id, uint64_t v) : PerfFileSection(id)
426{
427    value_ = v;
428}
429
430bool 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
439size_t PerfFileSectionU64::GetSize()
440{
441    return sizeof(value_);
442}
443
444void PerfFileSectionU64::GetValue(uint64_t &v) const
445{
446    v = value_;
447}
448
449PerfFileSectionEventDesc::PerfFileSectionEventDesc(FEATURE id,
450                                                   const std::vector<AttrWithId> &eventDesces)
451    : PerfFileSection(id)
452{
453    eventDesces_ = eventDesces;
454}
455
456PerfFileSectionEventDesc::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'
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
501bool 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
519size_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
533void PerfFileSectionEventDesc::GetValue(std::vector<AttrWithId> &eventDesces) const
534{
535    eventDesces = eventDesces_;
536}
537} // namespace HiPerf
538} // namespace Developtools
539} // namespace OHOS
540