1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
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 * Description: FtraceParser class implements
16 */
17#include "ftrace_parser.h"
18
19#include <algorithm>
20#include <cerrno>
21#include <cstring>
22#include <fcntl.h>
23#include <fstream>
24#include <regex>
25#include <sstream>
26#include <unistd.h>
27
28#include "common.h"
29#include "file_utils.h"
30#include "securec.h"
31#include "string_utils.h"
32
33#ifdef HILOG_DEBUG
34#undef HILOG_DEBUG
35#endif
36
37#define HILOG_DEBUG(LOG_CORE, fmt, ...) \
38    if (debugOn_) { \
39        PROFILER_LOG_INFO(LOG_CORE, ":DEBUG: " fmt, ##__VA_ARGS__); \
40    }
41
42namespace {
43using namespace OHOS::Developtools::Profiler;
44constexpr unsigned RB_MISSED_EVENTS = (1uL << 31); // Flag when events were overwritten
45constexpr unsigned RB_MISSED_STORED = (1 << 30);   // Missed count stored at end
46constexpr unsigned RB_MISSED_FLAGS = (RB_MISSED_EVENTS | RB_MISSED_STORED);
47
48constexpr unsigned COL_IDX_NAME = 0;
49constexpr unsigned COL_IDX_VALUE = 1;
50
51constexpr unsigned TS_EXT_SHIFT = 27;
52constexpr uint32_t INT_MAX_LEN = 10;
53
54inline uint64_t GetTimestampIncrements(uint64_t ext)
55{
56    return ext << TS_EXT_SHIFT;
57}
58} // namespace
59
60FTRACE_NS_BEGIN
61FtraceParser::FtraceParser()
62{
63    PROFILER_LOG_INFO(LOG_CORE, "FtraceParser create!");
64}
65
66bool FtraceParser::Init()
67{
68    fixedCharArrayRegex_ = std::regex(R"(char \w+\[\d+\])");
69    flexDataLocArrayRegex_ = std::regex(R"(__data_loc [a-zA-Z_0-9 ]+\[\] \w+)");
70    if (FtraceFsOps::GetInstance().IsHmKernel()) {
71        return true;
72    }
73    std::string printkFormats = FtraceFsOps::GetInstance().GetPrintkFormats();
74    CHECK_TRUE(printkFormats.size() > 0, false, "read printk_formats failed!");
75    CHECK_TRUE(PrintkFormatsParser::GetInstance().Parse(printkFormats), false, "parse printk_formats failed");
76
77    std::string formatDesc = FtraceFsOps::GetInstance().GetPageHeaderFormat();
78    CHECK_TRUE(formatDesc.size() > 0, false, "read header_page failed!");
79    return ParseHeaderPageFormat(formatDesc);
80}
81
82FtraceParser::~FtraceParser()
83{
84    PROFILER_LOG_INFO(LOG_CORE, "FtraceParser destroy!");
85}
86
87bool FtraceParser::SetupEvent(const std::string& type, const std::string& name)
88{
89    if (!SubEventParser<FtraceEvent>::GetInstance().IsSupport(name)) {
90        // no sub event parser found for event, so no need to parse format file
91        return false;
92    }
93
94    EventFormat format;
95    format.eventType = type;
96    format.eventName = name;
97    std::string desc = FtraceFsOps::GetInstance().GetEventDataFormat(type, name);
98    if (desc != "") {
99        CHECK_TRUE(ParseEventFormat(desc.data(), format), false, "parse %s/%s/format failed!",
100            type.c_str(), name.c_str());
101        CHECK_TRUE(SubEventParser<FtraceEvent>::GetInstance().SetupEvent(format),
102            false, "setup %s/%s failed!", type.c_str(), name.c_str());
103        CHECK_TRUE(SubEventParser<ProtoEncoder::FtraceEvent>::GetInstance().SetupEvent(format),
104            false, "setup pbzero %s/%s failed!", type.c_str(), name.c_str());
105    }
106    return true;
107}
108
109bool FtraceParser::ParseHeaderPageFormat(const std::string& formatDesc)
110{
111    EventFormat format = {};
112    CHECK_TRUE(ParseEventFormat(formatDesc, format), false, "parse events/header_page failed!");
113
114    bool commitFound = false;
115    for (auto& field : format.fields) {
116        if (field.name == "timestamp") {
117            pageHeaderFormat_.timestamp = field;
118        } else if (field.name == "commit") {
119            pageHeaderFormat_.commit = field;
120            commitFound = true;
121        } else if (field.name == "overwrite") {
122            pageHeaderFormat_.overwrite = field;
123        }
124    }
125
126    CHECK_TRUE(commitFound, false, "commit field not found!");
127    return true;
128}
129
130int FtraceParser::GetHeaderPageCommitSize(void)
131{
132    // return the size value of commit field read from events/header_page
133    return pageHeaderFormat_.commit.size;
134}
135
136bool FtraceParser::ParseEventFormat(const std::string& formatDesc, EventFormat& format)
137{
138    std::string idLinePrefix = "ID:";
139    std::string fieldLinePrefix = "field:";
140    std::string printFmtLinePrefix = "print fmt:";
141
142    std::string line;
143    std::stringstream sin(formatDesc);
144    while (getline(sin, line)) {
145        line = StringUtils::Strip(line);
146        if (line.empty()) {
147            continue;
148        } else if (StringUtils::StartsWith(line, fieldLinePrefix)) {
149            ParseFieldFormat(line, format);
150        } else if (StringUtils::StartsWith(line, idLinePrefix)) {
151            auto idStr = line.substr(idLinePrefix.size() + 1);
152            format.eventId = static_cast<uint32_t>(atoi(idStr.c_str()));
153        }
154    }
155    CHECK_TRUE(format.fields.size() > 0, false, "ParseEventFormat failed!");
156    size_t lastFiledIndex = format.fields.size() > 1 ? format.fields.size() - 1 : 0;
157    format.eventSize = format.fields[lastFiledIndex].offset + format.fields[lastFiledIndex].size;
158    return true;
159}
160
161static std::string SplitNameFromTypeName(const std::string& typeName)
162{
163    std::string name;
164    if (typeName.size() > 0) { // split type and name
165        auto posT0 = typeName.rfind(" ");
166        std::string rightHalf = typeName.substr(posT0 + 1);
167        size_t dataIndex = rightHalf.size() > 1 ? rightHalf.size() - 1 : 0;
168        if (rightHalf[dataIndex] != ']') {
169            name = rightHalf;
170        } else {
171            std::string::size_type postT1 = rightHalf.rfind('[');
172            if (postT1 == std::string::npos) {
173                return "";
174            }
175            name = rightHalf.substr(0, postT1);
176        }
177    }
178    return name;
179}
180
181static std::string EraseNameFromTypeName(const std::string& typeName, const std::string& name)
182{
183    std::string type;
184    if (name.size() > 0) { // erase name part from typeName
185        type = typeName;
186        auto pos = type.find(name);
187        type.replace(pos, name.size(), "");
188        type = StringUtils::Strip(type);
189    }
190    return type;
191}
192
193static void ParseCommonFiledIndex(CommonFiledIndex& commonIndex, const std::string& name, int index)
194{
195    if (name == "common_type") {
196        commonIndex.type = index;
197    } else if (name == "common_flags") {
198        commonIndex.flags = index;
199    } else if (name == "common_preempt_count") {
200        commonIndex.preemt = index;
201    } else if (name == "common_pid") {
202        commonIndex.pid = index;
203    }
204}
205
206bool FtraceParser::ParseFieldFormat(const std::string& fieldLine, EventFormat& format)
207{
208    FieldFormat fieldInfo;
209    std::string typeName;
210    std::string offsetStr;
211    std::string sizeStr;
212    std::string signedStr;
213
214    for (auto& part : StringUtils::Split(fieldLine, ";")) {
215        auto cols = StringUtils::Split(StringUtils::Strip(part), ":");
216        if (cols.size() < COL_IDX_VALUE) {
217            continue;
218        }
219        const auto& key = cols[COL_IDX_NAME];
220        if (key == "field") {
221            typeName = cols[COL_IDX_VALUE];
222        } else if (key == "offset") {
223            offsetStr = cols[COL_IDX_VALUE];
224        } else if (key == "size") {
225            sizeStr = cols[COL_IDX_VALUE];
226        } else if (key == "signed") {
227            signedStr = cols[COL_IDX_VALUE];
228        }
229    }
230
231    std::string name = SplitNameFromTypeName(typeName);
232    std::string type = EraseNameFromTypeName(typeName, name); // for field type
233    fieldInfo.name = name;
234    fieldInfo.typeName = typeName;
235    fieldInfo.offset = atoi(offsetStr.c_str());
236    fieldInfo.size = atoi(sizeStr.c_str());
237    fieldInfo.isSigned = atoi(signedStr.c_str());
238
239    ParseFieldType(type, fieldInfo);
240    ParseProtoType(fieldInfo);
241
242    if (StringUtils::StartsWith(name, "common_")) {
243        ParseCommonFiledIndex(format.commonIndex, name, static_cast<int>(format.commonFields.size()));
244        format.commonFields.push_back(fieldInfo);
245    } else {
246        format.fields.push_back(fieldInfo);
247    }
248    return true;
249}
250
251static bool ParseSepcialIntType(FieldFormat& field, const std::string& type, const std::string& typeName)
252{
253    if (type == "bool") {
254        field.filedType = FIELD_TYPE_BOOL;
255        return true;
256    }
257
258    if (type == "ino_t" || type == "i_ino") {
259        if (field.size == sizeof(uint32_t)) {
260            field.filedType = FIELD_TYPE_INODE32;
261            return true;
262        } else if (field.size == sizeof(uint64_t)) {
263            field.filedType = FIELD_TYPE_INODE64;
264            return true;
265        }
266    }
267
268    if (type == "dev_t") {
269        if (field.size == sizeof(uint32_t)) {
270            field.filedType = FIELD_TYPE_DEVID32;
271            return true;
272        } else if (field.size == sizeof(uint64_t)) {
273            field.filedType = FIELD_TYPE_DEVID64;
274            return true;
275        }
276    }
277
278    // Pids (as in 'sched_switch').
279    if (type == "pid_t") {
280        field.filedType = FIELD_TYPE_PID32;
281        return true;
282    }
283
284    if ((typeName.find("common_pid") != std::string::npos)) {
285        field.filedType = FIELD_TYPE_COMMONPID32;
286        return true;
287    }
288    return false;
289}
290
291static bool ParseCommonIntType(FieldFormat& field, bool sign)
292{
293    switch (field.size) {
294        case sizeof(int8_t):
295            field.filedType = sign ? FIELD_TYPE_INT8 : FIELD_TYPE_UINT8;
296            return true;
297        case sizeof(int16_t):
298            field.filedType = sign ? FIELD_TYPE_INT16 : FIELD_TYPE_UINT16;
299            return true;
300        case sizeof(int32_t):
301            field.filedType = sign ? FIELD_TYPE_INT32 : FIELD_TYPE_UINT32;
302            return true;
303        case sizeof(int64_t):
304            field.filedType = sign ? FIELD_TYPE_INT64 : FIELD_TYPE_UINT64;
305            return true;
306        default:
307            break;
308    }
309    return false;
310}
311
312static bool ParseKernelAddrField(FieldFormat& field, const std::string& type)
313{
314    if (type == "void*" || type == "void *") {
315        if (field.size == sizeof(uint64_t)) { // 64-bit kernel addresses
316            field.filedType = FIELD_TYPE_SYMADDR64;
317            return true;
318        } else if (field.size == sizeof(uint32_t)) { // 32-bit kernel addresses
319            field.filedType = FIELD_TYPE_SYMADDR32;
320            return true;
321        }
322    }
323    return false;
324}
325
326bool FtraceParser::ParseFieldType(const std::string& type, FieldFormat& field)
327{
328    const std::string& typeName = field.typeName;
329    // Fixed size C char arrary, likes "char a[LEN]"
330    if (std::regex_match(typeName, fixedCharArrayRegex_)) {
331        field.filedType = FIELD_TYPE_FIXEDCSTRING;
332        return true;
333    }
334
335    // for flex array with __data_loc mark, likes: __data_loc char[] name; __data_loc __u8[] buf;
336    if (std::regex_match(typeName, flexDataLocArrayRegex_)) {
337        CHECK_TRUE(field.size == sizeof(uint32_t), false, "__data_loc %s, size: %hu", typeName.c_str(), field.size);
338        field.filedType = FIELD_TYPE_DATALOC;
339        return true;
340    }
341
342    if ((typeName.find("char[]") != std::string::npos) || (typeName.find("char *") != std::string::npos)) {
343        field.filedType = FIELD_TYPE_STRINGPTR;
344        return true;
345    }
346
347    // Variable length strings: "char foo" + size: 0 (as in 'print').
348    if ((type == "char" || type == "char []") && field.size == 0) {
349        field.filedType = FIELD_TYPE_CSTRING;
350        return true;
351    }
352
353    // 64-bit kernel addresses
354    if (ParseKernelAddrField(field, type)) {
355        return true;
356    }
357
358    if (ParseSepcialIntType(field, type, typeName)) {
359        return true;
360    }
361
362    // int uint:
363    if (ParseCommonIntType(field, field.isSigned)) {
364        return true;
365    }
366    return false;
367}
368
369void FtraceParser::ParseProtoType(FieldFormat& field)
370{
371    switch (field.filedType) {
372        case FIELD_TYPE_CSTRING:
373        case FIELD_TYPE_FIXEDCSTRING:
374        case FIELD_TYPE_STRINGPTR:
375        case FIELD_TYPE_DATALOC:
376            field.protoType = PROTO_TYPE_STRING;
377            break;
378        case FIELD_TYPE_INT8:
379        case FIELD_TYPE_INT16:
380        case FIELD_TYPE_INT32:
381        case FIELD_TYPE_PID32:
382        case FIELD_TYPE_COMMONPID32:
383            field.protoType = PROTO_TYPE_INT32;
384            break;
385        case FIELD_TYPE_INT64:
386            field.protoType = PROTO_TYPE_INT64;
387            break;
388        case FIELD_TYPE_UINT8:
389        case FIELD_TYPE_UINT16:
390        case FIELD_TYPE_UINT32:
391        case FIELD_TYPE_BOOL:
392        case FIELD_TYPE_DEVID32:
393        case FIELD_TYPE_SYMADDR32:
394            field.protoType = PROTO_TYPE_UINT32;
395            break;
396        case FIELD_TYPE_DEVID64:
397        case FIELD_TYPE_UINT64:
398        case FIELD_TYPE_INODE32:
399        case FIELD_TYPE_INODE64:
400        case FIELD_TYPE_SYMADDR64:
401            field.protoType = PROTO_TYPE_UINT64;
402            break;
403        case FIELD_TYPE_INVALID:
404            field.protoType = PROTO_TYPE_UNKNOWN;
405            break;
406        default:
407            break;
408    }
409}
410
411bool FtraceParser::ParsePerCpuStatus(PerCpuStats& stats, const std::string& perCpuStats)
412{
413    std::string line;
414    std::stringstream input(perCpuStats);
415
416    int count = 0;
417    while (getline(input, line, '\n')) {
418        std::string sep = ": ";
419        size_t pos = line.rfind(sep);
420        if (pos == std::string::npos) {
421            continue;
422        }
423        std::stringstream ss(line.substr(pos + sep.size()));
424        std::string name = line.substr(0, pos);
425        if (name == "entries") {
426            ss >> stats.entries;
427            count++;
428        } else if (name == "overrun") {
429            ss >> stats.overrun;
430            count++;
431        } else if (name == "commit overrun") {
432            ss >> stats.commitOverrun;
433            count++;
434        } else if (name == "bytes") {
435            ss >> stats.bytes;
436            count++;
437        } else if (name == "oldest event ts") {
438            ss >> stats.oldestEventTs;
439            count++;
440        } else if (name == "now ts") {
441            ss >> stats.nowTs;
442            count++;
443        } else if (name == "dropped events") {
444            ss >> stats.droppedEvents;
445            count++;
446        } else if (name == "read events") {
447            ss >> stats.readEvents;
448            count++;
449        }
450    }
451    return count > 0;
452}
453
454// parse kernel ring buffer page header data
455bool FtraceParser::ParsePageHeader()
456{
457    // read time stamp
458    uint64_t timestamp = 0;
459    CHECK_TRUE(ReadInc(&cur_, endOfPage_, &timestamp, sizeof(timestamp)), false, "read timestamp from page failed!");
460    pageHeader_.timestamp = timestamp;
461
462    // read data size and overwriten flags
463    uint64_t commit = 0;
464    const int commitSize = GetHeaderPageCommitSize(); // 8B on 64bit device, 4B on 32bit device
465    CHECK_TRUE(ReadInc(&cur_, endOfPage_, &commit, commitSize), false, "read commit to page header failed!");
466
467    // refers kernel function ring_buffer_page_len:
468    pageHeader_.size = (commit & ~RB_MISSED_FLAGS);
469    pageHeader_.overwrite = (commit & RB_MISSED_EVENTS);
470
471    pageHeader_.startpos = cur_;
472    pageHeader_.endpos = cur_ + pageHeader_.size;
473    return true;
474}
475
476// parse /sys/kernel/debug/tracing/saved_tgids
477// refers kernel function saved_tgids_show
478bool FtraceParser::ParseSavedTgid(const std::string& savedTgid)
479{
480    int32_t pid = 0;
481    int32_t tgid = 0;
482    std::stringstream sin(savedTgid);
483    // kernel format code with: "%d %d\n"
484    while (sin >> pid >> tgid) {
485        tgidDict_[pid] = tgid;
486    }
487
488    if (tgidDict_.size() == 0) {
489        PROFILER_LOG_WARN(LOG_CORE, "ParseSavedTgid: parsed tigds: %zu", tgidDict_.size());
490    }
491    return true;
492}
493
494// parse /sys/kernel/debug/tracing/saved_cmdlines
495// refers kernel function saved_cmdlines_show
496bool FtraceParser::ParseSavedCmdlines(const std::string& savedCmdlines)
497{
498    bool retval = false;
499    int32_t pid;
500    std::string comm;
501    std::string line;
502    std::stringstream sin(savedCmdlines);
503    while (std::getline(sin, line)) {
504        // kernel format with: "%d %s\n"
505        auto pos = line.find(' ');
506        if (pos != std::string::npos && pos < INT_MAX_LEN) {
507            auto pidStr = line.substr(0, pos);
508            pid = COMMON::IsNumeric(pidStr) ? std::stoi(pidStr) : 0;
509            comm = line.substr(pos + 1);
510            commDict_[pid] = comm;
511            retval = true;
512        }
513    }
514
515    if (commDict_.size() == 0) {
516        PROFILER_LOG_WARN(LOG_CORE, "ParseSavedCmdlines: parsed cmdlines: %zu", commDict_.size());
517    }
518    return retval;
519}
520
521bool FtraceParser::ParsePaddingData(const FtraceEventHeader& eventHeader)
522{
523    if (eventHeader.timeDelta == 0) {
524        return false;
525    }
526    uint32_t paddingLength;
527    CHECK_TRUE(ReadInc(&cur_, endOfData_, &paddingLength, sizeof(paddingLength)), false, "read padding len failed!");
528
529    // skip padding data
530    cur_ += paddingLength;
531    return true;
532}
533
534bool FtraceParser::ParseTimeExtend(const FtraceEventHeader& eventHeader)
535{
536    uint32_t deltaExt = 0;
537    CHECK_TRUE(ReadInc(&cur_, endOfData_, &deltaExt, sizeof(deltaExt)), false, "read time delta failed!");
538
539    timestamp_ += GetTimestampIncrements(deltaExt);
540    PROFILER_LOG_INFO(LOG_CORE, "ParseTimeExtend: update ts with %u to %" PRIu64, deltaExt, timestamp_);
541    return true;
542}
543
544bool FtraceParser::ParseTimeStamp(const FtraceEventHeader& eventHeader)
545{
546    uint32_t deltaExt = 0;
547    CHECK_TRUE(ReadInc(&cur_, endOfData_, &deltaExt, sizeof(deltaExt)), false, "read time delta failed!");
548
549    // refers kernel function rb_update_write_stamp in ring_buffer.c
550    timestamp_ = eventHeader.timeDelta + GetTimestampIncrements(deltaExt);
551    PROFILER_LOG_INFO(LOG_CORE, "ParseTimeStamp: update ts with %u to %" PRIu64, deltaExt, timestamp_);
552    return true;
553}
554
555bool FtraceParser::ReadInc(uint8_t* start[], uint8_t end[], void* outData, size_t outSize)
556{
557    if ((end - *start) < static_cast<ptrdiff_t>(outSize)) {
558        return false;
559    }
560    CHECK_TRUE(memcpy_s(outData, outSize, *start, outSize) == EOK, false,
561               "read %zu bytes from memory region FAILED", outSize);
562    *start += outSize;
563    return true;
564}
565
566bool FtraceParser::IsValidIndex(int idx)
567{
568    return idx != CommonFiledIndex::INVALID_IDX;
569}
570
571void FtraceParser::SetDebugOn(bool value)
572{
573    debugOn_ = value;
574    PROFILER_LOG_INFO(LOG_CORE, "debugOption: %s", debugOn_ ? "true" : "false");
575}
576FTRACE_NS_END
577