1 /*
2 * Copyright (c) 2022 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 #include "log_parse.h"
16
17 #include "string_util.h"
18 #include "tbox.h"
19
20 using namespace std;
21 namespace OHOS {
22 namespace HiviewDFX {
23 const std::string LogParse::UNMATCHED_EXCEPTION = "UnMatchedException";
24
25 // some stack function is invalid, so it should be ignored
26 const std::map<std::string, std::set<std::string>> LogParse::ignoreList_ = {
27 {"Level1", {
28 "libc.so",
29 "libc++.so",
30 "ld-musl-aarch64.so",
31 "libc_fdleak_debug.so",
32 "unknown",
33 "watchdog",
34 "kthread",
35 "rdr_system_error"}
36 },
37 {"Level2", {
38 "libart.so",
39 "__switch_to",
40 "dump_backtrace",
41 "show_stack",
42 "dump_stack"}
43 },
44 {"Level3", {
45 "panic"}
46 }
47 };
48
49 // it is key word in app crash log, it can replace complexed info which may have safe and privacy info
50 const std::set<std::string> LogParse::exceptionList_ = {
51 "ArithmeticException",
52 "ArrayIndexOutOfBoundsException",
53 "ArrayStoreException",
54 "ClassCastException",
55 "ClassNotFoundException",
56 "CloneNotSupportedException",
57 "EnumConstantNotPresentException",
58 "IllegalAccessException",
59 "IllegalArgumentException",
60 "IllegalMonitorStateException",
61 "IllegalStateException",
62 "IllegalThreadStateException",
63 "IndexOutOfBoundsException",
64 "InstantiationException",
65 "InterruptedException",
66 "NegativeArraySizeException",
67 "NoSuchFieldException",
68 "NoSuchMethodException",
69 "NullPointerException",
70 "NumberFormatException",
71 "ReflectiveOperationException",
72 "RuntimeException",
73 "SecurityException",
74 "StringIndexOutOfBoundsException"
75 };
76
IsIgnoreLibrary(const string & val) const77 bool LogParse::IsIgnoreLibrary(const string& val) const
78 {
79 for (auto list : ignoreList_) {
80 for (auto str : list.second) {
81 if (val.find(str, 0) != string::npos) {
82 return true;
83 }
84 }
85 }
86 return false;
87 }
88
89 /*
90 * Remove ignored backtrace
91 * inStack : inverted sequence with fault log
92 * outStack : filter stack
93 */
GetValidStack(int num,stack<string> & inStack,stack<string> & outStack) const94 bool LogParse::GetValidStack(int num, stack<string>& inStack, stack<string>& outStack) const
95 {
96 vector<string> validStack;
97 size_t count = static_cast<size_t>(num);
98 // count < 1: indicate stack is empty
99 if (count < 1 || inStack.empty()) {
100 return false;
101 }
102
103 // Automatically checks if it is a stack
104 bool iStack = Tbox::IsCallStack(inStack.top());
105 if (iStack) {
106 validStack = GetValidStack(count, inStack);
107 }
108 outStack = GetStackTop(validStack, count);
109 return true;
110 }
111
GetStackTop(const vector<string> & validStack,const size_t num) const112 stack<string> LogParse::GetStackTop(const vector<string>& validStack, const size_t num) const
113 {
114 size_t len = validStack.size();
115 stack<string> stackTop;
116 for (size_t i = 0; i < len; i++) {
117 if (i == 0 || len - i < num) {
118 stackTop.push(validStack.at(i));
119 }
120 }
121 return stackTop;
122 }
123
StackToMultipart(stack<string> & inStack,size_t num) const124 list<vector<string>> LogParse::StackToMultipart(stack<string>& inStack, size_t num) const
125 {
126 stack<string> partStack;
127 vector<string> validPart;
128 list<vector<string>> multiPart;
129 while (!inStack.empty()) {
130 string topStr = inStack.top();
131 StringUtil::EraseString(topStr, "\t");
132 if (Tbox::HasCausedBy(topStr)) {
133 topStr = MatchExceptionLibrary(topStr);
134 if (!partStack.empty()) {
135 validPart = GetValidStack(num, partStack);
136 }
137 validPart.insert(validPart.begin(), topStr);
138 multiPart.push_back(validPart);
139 partStack = stack<string>();
140 validPart.clear();
141 inStack.pop();
142 continue;
143 }
144 partStack.push(topStr);
145 inStack.pop();
146 }
147 if (!partStack.empty()) {
148 validPart = GetValidStack(num, partStack);
149 multiPart.push_back(validPart);
150 }
151 return multiPart;
152 }
153
GetValidBlock(stack<string> inStack,vector<string> & lastPart) const154 string LogParse::GetValidBlock(stack<string> inStack, vector<string>& lastPart) const
155 {
156 vector<string> validStack;
157
158 list<vector<string>> multiPart = StackToMultipart(inStack, 3); // 3 : first/second/last frame
159 size_t size = multiPart.size();
160 if (size == 0) {
161 return "";
162 }
163 if (size == 1) {
164 // only one part
165 validStack = multiPart.front();
166 if (validStack.size() > STACK_LEN_MAX) {
167 // keep the begin 28 lines and the end 2 lines
168 validStack.erase(validStack.begin() + (STACK_LEN_MAX - 2), validStack.end() - 2); // 2 : end 2 lines
169 }
170 } else if (size >= 2) { // at least 2 parts
171 for (auto part : multiPart) {
172 if (validStack.size() >= STACK_LEN_MAX) {
173 break;
174 }
175 validStack.insert(validStack.begin(), part.begin(), part.end());
176 }
177 if (multiPart.front().size() > STACK_LEN_MAX) {
178 // keep the begin 28 lines and the end 2 lines
179 validStack.erase(validStack.begin() + (STACK_LEN_MAX - 2), validStack.end() - 2); // 2 : end 2 lines
180 } else if (validStack.size() > STACK_LEN_MAX) {
181 // keep the begin 2 lines and the end 28 lines
182 validStack.erase(validStack.begin() + 2, validStack.end() - (STACK_LEN_MAX - 2)); // 2 : begin 2 lines
183 }
184 }
185
186 for (auto part : multiPart) {
187 // multiPart has at least 2 parts
188 if (size > 1 && !part.empty() && HasExceptionList(part.front())) {
189 part.erase(part.begin());
190 }
191 // lastPart should has at least 3 lines
192 if (!part.empty()) {
193 reverse(part.begin(), part.end());
194 lastPart = part;
195 break;
196 }
197 }
198 return Tbox::ARRAY_STR + StringUtil::VectorToString(validStack, false);
199 }
200
GetValidStack(size_t num,stack<string> & inStack) const201 vector<string> LogParse::GetValidStack(size_t num, stack<string>& inStack) const
202 {
203 stack<string> src = inStack;
204 vector<string> validStack;
205 stack<string> outStatck;
206 string stackName;
207 size_t len = src.size();
208 for (size_t i = 0; i < len; i++) {
209 stackName = Tbox::GetStackName(src.top()); // extract function name from the stack
210 if (!IsIgnoreLibrary(stackName)) {
211 validStack.push_back(stackName);
212 }
213 src.pop();
214 }
215 if (validStack.empty()) {
216 MatchIgnoreLibrary(inStack, outStatck, num);
217 len = outStatck.size();
218 for (size_t i = 0; i < len; i++) {
219 stackName = Tbox::GetStackName(outStatck.top());
220 validStack.push_back(stackName);
221 outStatck.pop();
222 }
223 }
224 return validStack;
225 }
226
MatchExceptionLibrary(const string & val)227 string LogParse::MatchExceptionLibrary(const string& val)
228 {
229 for (auto& str : LogParse::exceptionList_) {
230 if (val.find(str, 0) != string::npos) {
231 return str;
232 }
233 }
234 return UNMATCHED_EXCEPTION;
235 }
236
MatchIgnoreLibrary(stack<string> inStack,stack<string> & outStack,size_t num) const237 void LogParse::MatchIgnoreLibrary(stack<string> inStack, stack<string>& outStack, size_t num) const
238 {
239 if (inStack.size() <= num) {
240 outStack = inStack;
241 return;
242 }
243 size_t count = 0;
244 for (auto it = ignoreList_.rbegin(); it != ignoreList_.rend(); ++it) {
245 if (count == ignoreList_.size() - 1) {
246 outStack = inStack;
247 return;
248 }
249
250 stack<string> src = inStack;
251 while (src.size() > num) {
252 string name = src.top();
253 for (auto str : it->second) {
254 if (name.find(str, 0) != string::npos) {
255 outStack = src;
256 return;
257 }
258 }
259 src.pop();
260 }
261 count++;
262 }
263 }
264
265 /*
266 * INPUT :
267 * info : trace spliting by "\n"
268 * OUTPUT :
269 * trace : last part trace to get Frame
270 * return string : valid trace spliting by "\n"
271 */
GetFilterTrace(const std::string & info,std::vector<std::string> & trace,std::string eventType) const272 std::string LogParse::GetFilterTrace(const std::string& info, std::vector<std::string>& trace,
273 std::string eventType) const
274 {
275 std::string newInfo = info;
276 if (eventType == "JS_ERROR" && newInfo.find("libace_napi.z.so") != std::string::npos) {
277 newInfo = StringUtil::GetRightSubstr(info, "libace_napi.z.so");
278 }
279 StringUtil::SplitStr(newInfo, "\n", trace, false, false);
280 std::stack<std::string> traceStack;
281 for (const auto& str : trace) {
282 traceStack.push(str);
283 }
284 trace.clear();
285 return GetValidBlock(traceStack, trace);
286 }
287
SetFrame(std::stack<std::string> & stack,std::map<std::string,std::string> & eventInfo) const288 void LogParse::SetFrame(std::stack<std::string>& stack, std::map<std::string, std::string>& eventInfo) const
289 {
290 std::vector<std::string> name = {"FIRST_FRAME", "SECOND_FRAME", "LAST_FRAME"};
291 size_t len = stack.size();
292 for (size_t i = 0; i < len; i++) {
293 if (eventInfo.find(name[i]) == eventInfo.end()) {
294 eventInfo[name[i]] = stack.top();
295 }
296 stack.pop();
297 }
298 }
299
HasExceptionList(const string & line) const300 bool LogParse::HasExceptionList(const string& line) const
301 {
302 auto iter = exceptionList_.find(line);
303 if (line == UNMATCHED_EXCEPTION || iter != exceptionList_.end()) {
304 return true;
305 }
306 return false;
307 }
308 }
309 }