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 
27 namespace OHOS {
28 namespace HiviewDFX {
29 namespace Hitrace {
30 namespace {
31 const std::string CPUFREQ_PREFIX = "/sys/devices/system/cpu/cpu";
32 const std::string CPUFREQ_AFTERFIX = "/cpufreq/scaling_cur_freq";
33 }
34 
CanonicalizeSpecPath(const char* src)35 std::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 
MarkClockSync(const std::string& traceRootPath)65 bool 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 
ParseJsonFromFile(const std::string& filePath)121 static 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 
ParseSysFiles(cJSON *tags, TagCategory& tagCategory)137 static 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 
ParseTagCategory(cJSON* tagCategoryNode, std::map<std::string, TagCategory>& allTags)150 static 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 
ParseTagGroups(cJSON* tagGroupsNode, std::map<std::string, std::vector<std::string>> &tagGroupTable)176 static 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 
ParseTagInfo(std::map<std::string, TagCategory> &allTags, std::map<std::string, std::vector<std::string>> &tagGroupTable)196 bool 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 
IsNumber(const std::string &str)229 bool 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 
GetCpuProcessors()242 int GetCpuProcessors()
243 {
244     int processors = 0;
245     processors = sysconf(_SC_NPROCESSORS_CONF);
246     return (processors == 0) ? 1 : processors;
247 }
248 
ReadCurrentCpuFrequencies(std::string& freqs)249 void 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