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}