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 "dfx_json_formatter.h"
17
18 #include <cstdlib>
19 #include <securec.h>
20 #include "dfx_kernel_stack.h"
21 #ifndef is_ohos_lite
22 #include "json/json.h"
23 #endif
24
25 namespace OHOS {
26 namespace HiviewDFX {
27 #ifndef is_ohos_lite
28 namespace {
29 const int FRAME_BUF_LEN = 1024;
JsonAsString(const Json::Value & val)30 static std::string JsonAsString(const Json::Value& val)
31 {
32 if (val.isConvertibleTo(Json::stringValue)) {
33 return val.asString();
34 }
35 return "";
36 }
37
FormatJsFrame(const Json::Value & frames,const uint32_t & frameIdx,std::string & outStr)38 static bool FormatJsFrame(const Json::Value& frames, const uint32_t& frameIdx, std::string& outStr)
39 {
40 const int jsIdxLen = 10;
41 char buf[jsIdxLen] = { 0 };
42 char idxFmt[] = "#%02u at";
43 if (snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, idxFmt, frameIdx) <= 0) {
44 return false;
45 }
46 outStr = std::string(buf);
47 std::string symbol = JsonAsString(frames[frameIdx]["symbol"]);
48 if (!symbol.empty()) {
49 outStr.append(" " + symbol);
50 }
51 std::string packageName = JsonAsString(frames[frameIdx]["packageName"]);
52 if (!packageName.empty()) {
53 outStr.append(" " + packageName);
54 }
55 std::string file = JsonAsString(frames[frameIdx]["file"]);
56 if (!file.empty()) {
57 std::string line = JsonAsString(frames[frameIdx]["line"]);
58 std::string column = JsonAsString(frames[frameIdx]["column"]);
59 outStr.append(" (" + file + ":" + line + ":" + column + ")");
60 }
61 return true;
62 }
63
FormatNativeFrame(const Json::Value & frames,const uint32_t & frameIdx,std::string & outStr)64 static bool FormatNativeFrame(const Json::Value& frames, const uint32_t& frameIdx, std::string& outStr)
65 {
66 char buf[FRAME_BUF_LEN] = {0};
67 char format[] = "#%02u pc %s %s";
68 std::string buildId = JsonAsString(frames[frameIdx]["buildId"]);
69 std::string file = JsonAsString(frames[frameIdx]["file"]);
70 std::string offset = JsonAsString(frames[frameIdx]["offset"]);
71 std::string pc = JsonAsString(frames[frameIdx]["pc"]);
72 std::string symbol = JsonAsString(frames[frameIdx]["symbol"]);
73 if (snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, format, frameIdx, pc.c_str(),
74 file.empty() ? "Unknown" : file.c_str()) <= 0) {
75 return false;
76 }
77 outStr = std::string(buf);
78 if (!symbol.empty()) {
79 outStr.append("(" + symbol + "+" + offset + ")");
80 }
81 if (!buildId.empty()) {
82 outStr.append("(" + buildId + ")");
83 }
84 return true;
85 }
86 }
87
FormatJsonStack(const std::string & jsonStack,std::string & outStackStr)88 bool DfxJsonFormatter::FormatJsonStack(const std::string& jsonStack, std::string& outStackStr)
89 {
90 Json::Reader reader;
91 Json::Value threads;
92 if (!(reader.parse(jsonStack, threads))) {
93 outStackStr.append("Failed to parse json stack info.");
94 return false;
95 }
96 constexpr int maxThreadCount = 10000;
97 if (threads.size() > maxThreadCount) {
98 outStackStr.append("Thread count exceeds limit(10000).");
99 return false;
100 }
101 for (uint32_t i = 0; i < threads.size(); ++i) {
102 std::string ss;
103 Json::Value thread = threads[i];
104 if (thread["tid"].isConvertibleTo(Json::stringValue) &&
105 thread["thread_name"].isConvertibleTo(Json::stringValue)) {
106 ss += "Tid:" + JsonAsString(thread["tid"]) + ", Name:" + JsonAsString(thread["thread_name"]) + "\n";
107 }
108 if (!thread.isMember("frames") || !thread["frames"].isArray()) {
109 continue;
110 }
111 const Json::Value frames = thread["frames"];
112 constexpr int maxFrameNum = 1000;
113 if (frames.size() > maxFrameNum) {
114 continue;
115 }
116 for (uint32_t j = 0; j < frames.size(); ++j) {
117 std::string frameStr = "";
118 bool formatStatus = false;
119 if (JsonAsString(frames[j]["line"]).empty()) {
120 formatStatus = FormatNativeFrame(frames, j, frameStr);
121 } else {
122 formatStatus = FormatJsFrame(frames, j, frameStr);
123 }
124 if (formatStatus) {
125 ss += frameStr + "\n";
126 } else {
127 // Shall we try to print more information?
128 outStackStr.append("Frame info is illegal.");
129 return false;
130 }
131 }
132
133 outStackStr.append(ss);
134 }
135 return true;
136 }
137
138 #ifdef __aarch64__
FormatKernelStackStr(const std::vector<DfxThreadStack> & processStack,std::string & formattedStack)139 static bool FormatKernelStackStr(const std::vector<DfxThreadStack>& processStack, std::string& formattedStack)
140 {
141 if (processStack.empty()) {
142 return false;
143 }
144 formattedStack = "";
145 for (const auto &threadStack : processStack) {
146 std::string ss = "Tid:" + std::to_string(threadStack.tid) + ", Name:" + threadStack.threadName + "\n";
147 formattedStack.append(ss);
148 for (size_t frameIdx = 0; frameIdx < threadStack.frames.size(); ++frameIdx) {
149 std::string file = threadStack.frames[frameIdx].mapName;
150 char buf[FRAME_BUF_LEN] = {0};
151 char format[] = "#%02zu pc %016" PRIx64 " %s";
152 if (snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, format, frameIdx, threadStack.frames[frameIdx].relPc,
153 file.empty() ? "Unknown" : file.c_str()) <= 0) {
154 continue;
155 }
156 formattedStack.append(std::string(buf, strlen(buf)) + "\n");
157 }
158 }
159 return true;
160 }
161
FormatKernelStackJson(std::vector<DfxThreadStack> processStack,std::string & formattedStack)162 static bool FormatKernelStackJson(std::vector<DfxThreadStack> processStack, std::string& formattedStack)
163 {
164 if (processStack.empty()) {
165 return false;
166 }
167 Json::Value jsonInfo;
168 for (const auto &threadStack : processStack) {
169 Json::Value threadInfo;
170 threadInfo["thread_name"] = threadStack.threadName;
171 threadInfo["tid"] = threadStack.tid;
172 Json::Value frames(Json::arrayValue);
173 for (const auto& frame : threadStack.frames) {
174 Json::Value frameJson;
175 char buf[FRAME_BUF_LEN] = {0};
176 char format[] = "%016" PRIx64;
177 if (snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, format, frame.relPc) <= 0) {
178 continue;
179 }
180 frameJson["pc"] = std::string(buf);
181 frameJson["symbol"] = "";
182 frameJson["offset"] = 0;
183 frameJson["file"] = frame.mapName.empty() ? "Unknown" : frame.mapName;
184 frameJson["buildId"] = "";
185 frames.append(frameJson);
186 }
187 threadInfo["frames"] = frames;
188 jsonInfo.append(threadInfo);
189 }
190 formattedStack = Json::FastWriter().write(jsonInfo);
191 return true;
192 }
193 #endif
194
FormatKernelStack(const std::string & kernelStack,std::string & formattedStack,bool jsonFormat)195 bool DfxJsonFormatter::FormatKernelStack(const std::string& kernelStack, std::string& formattedStack, bool jsonFormat)
196 {
197 #ifdef __aarch64__
198 std::vector<DfxThreadStack> processStack;
199 if (!FormatProcessKernelStack(kernelStack, processStack)) {
200 return false;
201 }
202 if (jsonFormat) {
203 return FormatKernelStackJson(processStack, formattedStack);
204 } else {
205 return FormatKernelStackStr(processStack, formattedStack);
206 }
207 #else
208 return false;
209 #endif
210 }
211 #endif
212 } // namespace HiviewDFX
213 } // namespace OHOS
214