1/*
2 * Copyright (c) 2023-2024 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
16#include "common_utils.h"
17
18#include <cinttypes>
19#include <unistd.h>
20#include <cstdio>
21#include <fstream>
22#include <fcntl.h>
23#include <sstream>
24#include "cJSON.h"
25#include "securec.h"
26
27namespace OHOS {
28namespace HiviewDFX {
29namespace Hitrace {
30namespace {
31const std::string CPUFREQ_PREFIX = "/sys/devices/system/cpu/cpu";
32const std::string CPUFREQ_AFTERFIX = "/cpufreq/scaling_cur_freq";
33}
34
35std::string CanonicalizeSpecPath(const char* src)
36{
37    if (src == nullptr || strlen(src) >= PATH_MAX) {
38        HILOG_ERROR(LOG_CORE, "CanonicalizeSpecPath: %{pubilc}s failed.", src);
39        return "";
40    }
41    char resolvedPath[PATH_MAX] = { 0 };
42
43    if (access(src, F_OK) == 0) {
44        if (realpath(src, resolvedPath) == nullptr) {
45            HILOG_ERROR(LOG_CORE, "CanonicalizeSpecPath: realpath %{pubilc}s failed.", src);
46            return "";
47        }
48    } else {
49        std::string fileName(src);
50        if (fileName.find("..") == std::string::npos) {
51            if (sprintf_s(resolvedPath, PATH_MAX, "%s", src) == -1) {
52                HILOG_ERROR(LOG_CORE, "CanonicalizeSpecPath: sprintf_s %{pubilc}s failed.", src);
53                return "";
54            }
55        } else {
56            HILOG_ERROR(LOG_CORE, "CanonicalizeSpecPath: find .. src failed.");
57            return "";
58        }
59    }
60
61    std::string res(resolvedPath);
62    return res;
63}
64
65bool MarkClockSync(const std::string& traceRootPath)
66{
67    constexpr unsigned int bufferSize = 128;
68    char buffer[bufferSize] = { 0 };
69    std::string traceMarker = "trace_marker";
70    std::string resolvedPath = CanonicalizeSpecPath((traceRootPath + traceMarker).c_str());
71    int fd = open(resolvedPath.c_str(), O_WRONLY);
72    if (fd == -1) {
73        HILOG_ERROR(LOG_CORE, "MarkClockSync: oepn %{public}s fail, errno(%{public}d)", resolvedPath.c_str(), errno);
74        return false;
75    }
76
77    // write realtime_ts
78    struct timespec rts = {0, 0};
79    if (clock_gettime(CLOCK_REALTIME, &rts) == -1) {
80        HILOG_ERROR(LOG_CORE, "MarkClockSync: get realtime error, errno(%{public}d)", errno);
81        close(fd);
82        return false;
83    }
84    constexpr unsigned int nanoSeconds = 1000000000; // seconds converted to nanoseconds
85    constexpr unsigned int nanoToMill = 1000000; // millisecond converted to nanoseconds
86    int len = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1,
87        "trace_event_clock_sync: realtime_ts=%" PRId64 "\n",
88        static_cast<int64_t>((rts.tv_sec * nanoSeconds + rts.tv_nsec) / nanoToMill));
89    if (len < 0) {
90        HILOG_ERROR(LOG_CORE, "MarkClockSync: entering realtime_ts into buffer error, errno(%{public}d)", errno);
91        close(fd);
92        return false;
93    }
94
95    if (write(fd, buffer, len) < 0) {
96        HILOG_ERROR(LOG_CORE, "MarkClockSync: writing realtime error, errno(%{public}d)", errno);
97    }
98
99    // write parent_ts
100    struct timespec mts = {0, 0};
101    if (clock_gettime(CLOCK_MONOTONIC, &mts) == -1) {
102        HILOG_ERROR(LOG_CORE, "MarkClockSync: get parent_ts error, errno(%{public}d)", errno);
103        close(fd);
104        return false;
105    }
106    constexpr float nanoToSecond = 1000000000.0f; // consistent with the ftrace timestamp format
107    len = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "trace_event_clock_sync: parent_ts=%f\n",
108        static_cast<float>(((static_cast<float>(mts.tv_sec)) * nanoSeconds + mts.tv_nsec) / nanoToSecond));
109    if (len < 0) {
110        HILOG_ERROR(LOG_CORE, "MarkClockSync: entering parent_ts into buffer error, errno(%{public}d)", errno);
111        close(fd);
112        return false;
113    }
114    if (write(fd, buffer, len) < 0) {
115        HILOG_ERROR(LOG_CORE, "MarkClockSync: writing parent_ts error, errno(%{public}d)", errno);
116    }
117    close(fd);
118    return true;
119}
120
121static cJSON* ParseJsonFromFile(const std::string& filePath)
122{
123    std::ifstream inFile(filePath, std::ios::in);
124    if (!inFile.is_open()) {
125        HILOG_ERROR(LOG_CORE, "ParseJsonFromFile: %{public}s is not existed.", filePath.c_str());
126        return nullptr;
127    }
128    std::string fileContent((std::istreambuf_iterator<char>(inFile)), std::istreambuf_iterator<char>());
129    cJSON* root = cJSON_Parse(fileContent.c_str());
130    if (root == nullptr) {
131        HILOG_ERROR(LOG_CORE, "ParseJsonFromFile: %{public}s is not in JSON format.", filePath.c_str());
132    }
133    inFile.close();
134    return root;
135}
136
137static void ParseSysFiles(cJSON *tags, TagCategory& tagCategory)
138{
139    cJSON *sysFiles = cJSON_GetObjectItem(tags, "sysFiles");
140    if (sysFiles != nullptr && cJSON_IsArray(sysFiles)) {
141        cJSON *sysFile = nullptr;
142        cJSON_ArrayForEach(sysFile, sysFiles) {
143            if (cJSON_IsString(sysFile)) {
144                tagCategory.sysFiles.push_back(sysFile->valuestring);
145            }
146        }
147    }
148}
149
150static bool ParseTagCategory(cJSON* tagCategoryNode, std::map<std::string, TagCategory>& allTags)
151{
152    cJSON* tags = nullptr;
153    cJSON_ArrayForEach(tags, tagCategoryNode) {
154        if (tags == nullptr || tags->string == nullptr) {
155            continue;
156        }
157        TagCategory tagCategory;
158        cJSON* description = cJSON_GetObjectItem(tags, "description");
159        if (description != nullptr && cJSON_IsString(description)) {
160            tagCategory.description = description->valuestring;
161        }
162        cJSON* tagOffset = cJSON_GetObjectItem(tags, "tag_offset");
163        if (tagOffset != nullptr && cJSON_IsNumber(tagOffset)) {
164            tagCategory.tag = 1ULL << tagOffset->valueint;
165        }
166        cJSON* type = cJSON_GetObjectItem(tags, "type");
167        if (type != nullptr && cJSON_IsNumber(type)) {
168            tagCategory.type = type->valueint;
169        }
170        ParseSysFiles(tags, tagCategory);
171        allTags.insert(std::pair<std::string, TagCategory>(tags->string, tagCategory));
172    }
173    return true;
174}
175
176static bool ParseTagGroups(cJSON* tagGroupsNode, std::map<std::string, std::vector<std::string>> &tagGroupTable)
177{
178    cJSON* tagGroup = nullptr;
179    cJSON_ArrayForEach(tagGroup, tagGroupsNode) {
180        if (tagGroup == nullptr || tagGroup->string == nullptr) {
181            continue;
182        }
183        std::string tagGroupName = tagGroup->string;
184        std::vector<std::string> tagList;
185        cJSON* tag = nullptr;
186        cJSON_ArrayForEach(tag, tagGroup) {
187            if (cJSON_IsString(tag)) {
188                tagList.push_back(tag->valuestring);
189            }
190        }
191        tagGroupTable.insert(std::pair<std::string, std::vector<std::string>>(tagGroupName, tagList));
192    }
193    return true;
194}
195
196bool ParseTagInfo(std::map<std::string, TagCategory> &allTags,
197                  std::map<std::string, std::vector<std::string>> &tagGroupTable)
198{
199    std::string traceUtilsPath = "/system/etc/hiview/hitrace_utils.json";
200    cJSON* root = ParseJsonFromFile(traceUtilsPath);
201    if (root == nullptr) {
202        return false;
203    }
204    cJSON* tagCategory = cJSON_GetObjectItem(root, "tag_category");
205    if (tagCategory == nullptr) {
206        HILOG_ERROR(LOG_CORE, "ParseTagInfo: %{public}s is not contain tag_category node.", traceUtilsPath.c_str());
207        cJSON_Delete(root);
208        return false;
209    }
210    if (!ParseTagCategory(tagCategory, allTags)) {
211        cJSON_Delete(root);
212        return false;
213    }
214    cJSON* tagGroups = cJSON_GetObjectItem(root, "tag_groups");
215    if (tagGroups == nullptr) {
216        HILOG_ERROR(LOG_CORE, "ParseTagInfo: %{public}s is not contain tag_groups node.", traceUtilsPath.c_str());
217        cJSON_Delete(root);
218        return false;
219    }
220    if (!ParseTagGroups(tagGroups, tagGroupTable)) {
221        cJSON_Delete(root);
222        return false;
223    }
224    cJSON_Delete(root);
225    HILOG_INFO(LOG_CORE, "ParseTagInfo: parse done.");
226    return true;
227}
228
229bool IsNumber(const std::string &str)
230{
231    if (str.empty()) {
232        return false;
233    }
234    for (auto c : str) {
235        if (!isdigit(c)) {
236            return false;
237        }
238    }
239    return true;
240}
241
242int GetCpuProcessors()
243{
244    int processors = 0;
245    processors = sysconf(_SC_NPROCESSORS_CONF);
246    return (processors == 0) ? 1 : processors;
247}
248
249void ReadCurrentCpuFrequencies(std::string& freqs)
250{
251    int cpuNum = GetCpuProcessors();
252    std::ifstream file;
253    std::string line;
254    for (int i = 0; i < cpuNum; ++i) {
255        std::string freq = "0";
256        std::string cpuFreqPath = CPUFREQ_PREFIX + std::to_string(i) + CPUFREQ_AFTERFIX;
257        file.open(cpuFreqPath);
258        if (file.is_open()) {
259            if (std::getline(file, line)) {
260                std::istringstream iss(line);
261                iss >> freq;
262            }
263            file.close();
264        }
265        freqs += "cpu_id=" + std::to_string(i) + " state=" + freq;
266        if (i != cpuNum - 1) {
267            freqs += ",";
268        }
269    }
270}
271} // namespace Hitrace
272} // namespace HiviewDFX
273} // namespace OHOS
274