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