1/*
2 * Copyright (c) 2021-2023 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 <cstdint>
17#include <ctime>
18#include <mutex>
19#include <string>
20#include <sys/stat.h>
21#include <vector>
22
23#include "constants.h"
24#include "faultlog_info.h"
25#include "string_util.h"
26#include "time_util.h"
27
28namespace OHOS {
29namespace HiviewDFX {
30namespace {
31constexpr int DEFAULT_BUFFER_SIZE = 64;
32constexpr const char* const DEFAULT_FAULTLOG_TEMP_FOLDER = "/data/log/faultlog/temp/";
33} // namespace
34
35std::string GetFormatedTime(uint64_t target)
36{
37    time_t now = time(nullptr);
38    if (target > static_cast<uint64_t>(now)) {
39        target = target / 1000; // 1000 : convert millisecond to seconds
40    }
41
42    time_t out = static_cast<time_t>(target);
43    struct tm tmStruct {0};
44    struct tm* timeInfo = localtime_r(&out, &tmStruct);
45    if (timeInfo == nullptr) {
46        return "00000000000000";
47    }
48
49    char buf[DEFAULT_BUFFER_SIZE] = {0};
50    strftime(buf, DEFAULT_BUFFER_SIZE - 1, "%Y%m%d%H%M%S", timeInfo);
51    return std::string(buf, strlen(buf));
52}
53
54std::string GetFaultNameByType(int32_t faultType, bool asFileName)
55{
56    switch (faultType) {
57        case FaultLogType::JS_CRASH:
58            return asFileName ? "jscrash" : "JS_ERROR";
59        case FaultLogType::CPP_CRASH:
60            return asFileName ? "cppcrash" : "CPP_CRASH";
61        case FaultLogType::APP_FREEZE:
62            return asFileName ? "appfreeze" : "APP_FREEZE";
63        case FaultLogType::SYS_FREEZE:
64            return asFileName ? "sysfreeze" : "SYS_FREEZE";
65        case FaultLogType::SYS_WARNING:
66            return asFileName ? "syswarning" : "SYS_WARNING";
67        case FaultLogType::RUST_PANIC:
68            return asFileName ? "rustpanic" : "RUST_PANIC";
69        case FaultLogType::ADDR_SANITIZER:
70            return asFileName ? "sanitizer" : "ADDR_SANITIZER";
71        default:
72            break;
73    }
74    return "Unknown";
75}
76
77std::string GetFaultLogName(const FaultLogInfo& info)
78{
79    std::string name = info.module;
80    if (name.find("/") != std::string::npos) {
81        name = info.module.substr(info.module.find_last_of("/") + 1);
82    }
83
84    std::string ret = "";
85    if (info.faultLogType == FaultLogType::ADDR_SANITIZER) {
86        if (info.reason.compare("TSAN") == 0) {
87            ret.append("tsan");
88        } else if (info.reason.compare("UBSAN") == 0) {
89            ret.append("ubsan");
90        } else if (info.reason.compare("GWP-ASAN") == 0) {
91            ret.append("gwpasan");
92        } else if (info.reason.compare("HWASAN") == 0) {
93            ret.append("hwasan");
94        } else if (info.reason.compare("ASAN") == 0) {
95            ret.append("asan");
96        } else {
97            ret.append("sanitizer");
98        }
99    } else {
100        ret.append(GetFaultNameByType(info.faultLogType, true));
101    }
102    ret.append("-");
103    ret.append(name);
104    ret.append("-");
105    ret.append(std::to_string(info.id));
106    ret.append("-");
107    ret.append(GetFormatedTime(info.time));
108    return ret;
109}
110
111int32_t GetLogTypeByName(const std::string& type)
112{
113    if (type == "jscrash") {
114        return FaultLogType::JS_CRASH;
115    } else if (type == "cppcrash") {
116        return FaultLogType::CPP_CRASH;
117    } else if (type == "appfreeze") {
118        return FaultLogType::APP_FREEZE;
119    } else if (type == "sysfreeze") {
120        return FaultLogType::SYS_FREEZE;
121    } else if (type == "syswarning") {
122        return FaultLogType::SYS_WARNING;
123    } else if (type == "sanitizer") {
124        return FaultLogType::ADDR_SANITIZER;
125    } else if (type == "all" || type == "ALL") {
126        return FaultLogType::ALL;
127    } else {
128        return -1;
129    }
130}
131
132FaultLogInfo ExtractInfoFromFileName(const std::string& fileName)
133{
134    // FileName LogType-PackageName-Uid-YYYYMMDDHHMMSS
135    FaultLogInfo info;
136    std::vector<std::string> splitStr;
137    const int32_t expectedVecSize = 4;
138    StringUtil::SplitStr(fileName, "-", splitStr);
139    if (splitStr.size() == expectedVecSize) {
140        info.faultLogType = GetLogTypeByName(splitStr[0]);                 // 0 : index of log type
141        info.module = splitStr[1];                                         // 1 : index of module name
142        StringUtil::ConvertStringTo<int32_t>(splitStr[2], info.id);        // 2 : index of uid
143        info.time = TimeUtil::StrToTimeStamp(splitStr[3], "%Y%m%d%H%M%S"); // 3 : index of timestamp
144    }
145    info.pid = 0;
146    return info;
147}
148
149FaultLogInfo ExtractInfoFromTempFile(const std::string& fileName)
150{
151    // FileName LogType-pid-time
152    FaultLogInfo info;
153    std::vector<std::string> splitStr;
154    const int32_t expectedVecSize = 3;
155    StringUtil::SplitStr(fileName, "-", splitStr);
156    if (splitStr.size() == expectedVecSize) {
157        info.faultLogType = GetLogTypeByName(splitStr[0]);                 // 0 : index of log type
158        StringUtil::ConvertStringTo<int32_t>(splitStr[1], info.pid);       // 1 : index of pid
159        StringUtil::ConvertStringTo<int64_t>(splitStr[2], info.time);      // 2 : index of timestamp
160    }
161    return info;
162}
163
164std::string RegulateModuleNameIfNeed(const std::string& name)
165{
166    std::vector<std::string> splitStr;
167    StringUtil::SplitStr(name, "/", splitStr);
168    auto size = splitStr.size();
169    if (size > 0) {
170        return splitStr[size - 1];
171    }
172    return name;
173}
174
175time_t GetFileLastAccessTimeStamp(const std::string& fileName)
176{
177    struct stat fileInfo;
178    if (stat(fileName.c_str(), &fileInfo) != 0) {
179        return 0;
180    }
181    return fileInfo.st_atime;
182}
183
184std::string GetCppCrashTempLogName(const FaultLogInfo& info)
185{
186    return std::string(DEFAULT_FAULTLOG_TEMP_FOLDER) +
187        "cppcrash-" +
188        std::to_string(info.pid) +
189        "-" +
190        std::to_string(info.time);
191}
192
193std::string GetThreadStack(const std::string& path, int32_t threadId)
194{
195    std::string stack;
196    if (path.empty()) {
197        return stack;
198    }
199    char realPath[PATH_MAX] = {0};
200    if (realpath(path.c_str(), realPath) == nullptr) {
201        return stack;
202    }
203    if (strncmp(realPath, FaultLogger::FAULTLOG_BASE_FOLDER, strlen(FaultLogger::FAULTLOG_BASE_FOLDER)) != 0) {
204        return stack;
205    }
206
207    std::ifstream logFile(realPath);
208    if (!logFile.is_open()) {
209        return stack;
210    }
211    std::string regTidString = "^Tid:" + std::to_string(threadId) + ", Name:(.{0,32})$";
212    std::regex regTid(regTidString);
213    std::regex regStack(R"(^#\d{2,3} (pc|at) .{0,1024}$)");
214    std::string line;
215    while (std::getline(logFile, line)) {
216        if (!logFile.good()) {
217            break;
218        }
219
220        if (!std::regex_match(line, regTid)) {
221            continue;
222        }
223
224        do {
225            stack.append(line + "\n");
226            if (!logFile.good()) {
227                break;
228            }
229        } while (std::getline(logFile, line) && std::regex_match(line, regStack));
230        break;
231    }
232
233    return stack;
234}
235} // namespace HiviewDFX
236} // namespace OHOS
237