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 20using namespace std; 21namespace OHOS { 22namespace HiviewDFX { 23const std::string LogParse::UNMATCHED_EXCEPTION = "UnMatchedException"; 24 25// some stack function is invalid, so it should be ignored 26const 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 50const 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 77bool 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 */ 94bool 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 112stack<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 124list<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 154string 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 201vector<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 227string 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 237void 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 */ 272std::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 288void 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 300bool 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}