1 /*
2  * Copyright (c) 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 "panic_report_recovery.h"
17 
18 #include <regex>
19 #include <cstdlib>
20 
21 #include "file_util.h"
22 #include "hiview_logger.h"
23 #include "hiview_zip_util.h"
24 #include "hisysevent.h"
25 #include "hisysevent_util.h"
26 #include "parameters.h"
27 #include "string_util.h"
28 #include "tbox.h"
29 
30 namespace OHOS {
31 namespace HiviewDFX {
32 namespace PanicReport {
33 DEFINE_LOG_LABEL(0xD002D11, "PanicReport");
34 
35 #ifdef UNITTEST
36 constexpr const char* BBOX_PARAM_PATH = "/data/test/bbox/bbox.save.log.flags";
37 constexpr const char* PANIC_LOG_PATH = "/data/test/bbox/panic_log/";
38 #else
39 constexpr const char* BBOX_PARAM_PATH = "/log/reliability/bbox/bbox.save.log.flags";
40 constexpr const char* PANIC_LOG_PATH = "/log/reliability/bbox/panic_log/";
41 #endif
42 constexpr const char* FACTORY_RECOVERY_TIME_PATH = "/log/reliability/bbox/factory.recovery.time";
43 constexpr const char* CMD_LINE = "/proc/cmdline";
44 
45 constexpr const char* LAST_FASTBOOT_LOG = "/ap_log/last_fastboot_log";
46 constexpr const char* HM_KLOG = "/ap_log/hm_klog.txt";
47 constexpr const char* HM_SNAPSHOT = "/ap_log/hm_snapshot.txt";
48 
49 constexpr const char* BOOTEVENT_BOOT_COMPLETED = "bootevent.boot.completed";
50 constexpr const char* LAST_BOOTUP_KEYPOINT = "last_bootup_keypoint";
51 
52 constexpr const char* HAPPEN_TIME = "happentime";
53 constexpr const char* FACTORY_RECOVERY_TIME = "factoryRecoveryTime";
54 constexpr const char* IS_PANIC_UPLOADED = "isPanicUploaded";
55 constexpr const char* IS_STARTUP_SHORT = "isStartUpShort";
56 constexpr const char* SOFTWARE_VERSION = "softwareVersion";
57 
58 constexpr const char* REGEX_FORMAT = R"(=((".*?")|(\S*)))";
59 
60 constexpr int COMPRESSION_RATION = 9;
61 constexpr int ZIP_FILE_SIZE_LIMIT = 5 * 1024 * 1024; // 5MB
62 
63 static bool g_isLastStartUpShort = false;
64 
InitPanicConfigFile()65 bool InitPanicConfigFile()
66 {
67     if (FileUtil::CreateFile(BBOX_PARAM_PATH, FileUtil::FILE_PERM_640) != 0) {
68         return false;
69     }
70     BboxSaveLogFlags bboxSaveLogFlags = LoadBboxSaveFlagFromFile();
71     g_isLastStartUpShort = bboxSaveLogFlags.isStartUpShort;
72     bboxSaveLogFlags.isStartUpShort = true;
73     return SaveBboxLogFlagsToFile(bboxSaveLogFlags);
74 }
75 
LoadBboxSaveFlagFromFile()76 BboxSaveLogFlags LoadBboxSaveFlagFromFile()
77 {
78     std::string content;
79     if (!FileUtil::LoadStringFromFile(BBOX_PARAM_PATH, content)) {
80         HIVIEW_LOGE("Failed to load file: %{public}s.", BBOX_PARAM_PATH);
81         return {};
82     }
83     return {
84         .happenTime = GetParamValueFromString(content, HAPPEN_TIME),
85         .factoryRecoveryTime = GetParamValueFromString(content, FACTORY_RECOVERY_TIME),
86         .softwareVersion = GetParamValueFromString(content, SOFTWARE_VERSION),
87         .isPanicUploaded = GetParamValueFromString(content, IS_PANIC_UPLOADED) != "false",
88         .isStartUpShort = GetParamValueFromString(content, IS_STARTUP_SHORT) == "true"
89     };
90 }
91 
SaveBboxLogFlagsToFile(const BboxSaveLogFlags & bboxSaveLogFlags)92 bool SaveBboxLogFlagsToFile(const BboxSaveLogFlags& bboxSaveLogFlags)
93 {
94     std::stringstream ss;
95     ss << HAPPEN_TIME << "=" << bboxSaveLogFlags.happenTime <<
96         " " << FACTORY_RECOVERY_TIME << "=" << bboxSaveLogFlags.factoryRecoveryTime <<
97         " " << SOFTWARE_VERSION << "=" << bboxSaveLogFlags.softwareVersion <<
98         " " << IS_PANIC_UPLOADED << "=" << (bboxSaveLogFlags.isPanicUploaded ? "true" : "false") <<
99         " " << IS_STARTUP_SHORT << "=" << (bboxSaveLogFlags.isStartUpShort ? "true" : "false");
100     if (!FileUtil::SaveStringToFile(BBOX_PARAM_PATH, ss.str())) {
101         HIVIEW_LOGE("failed to save the msg to file: %{public}s", BBOX_PARAM_PATH);
102         return false;
103     }
104     return true;
105 }
106 
InitPanicReport()107 bool InitPanicReport()
108 {
109     auto fileSize = FileUtil::GetFolderSize(PANIC_LOG_PATH);
110     if (fileSize > ZIP_FILE_SIZE_LIMIT) {
111         HIVIEW_LOGW("zip file size: %{public}" PRIu64 " is over limit", fileSize);
112         ClearFilesInDir(PANIC_LOG_PATH);
113     }
114     return InitPanicConfigFile() ;
115 }
116 
ClearFilesInDir(const std::filesystem::path & dirPath)117 bool ClearFilesInDir(const std::filesystem::path& dirPath)
118 {
119     if (!std::filesystem::is_directory(dirPath)) {
120         HIVIEW_LOGE("The file %{public}s is not a directory.", dirPath.c_str());
121         return false;
122     }
123     bool ret = true;
124     for (const auto& entry : std::filesystem::directory_iterator(dirPath)) {
125         std::error_code errorCode;
126         if (!std::filesystem::remove_all(entry.path(), errorCode)) {
127             HIVIEW_LOGE("Failed to deleted %{public}s errorCode: %{public}d",
128                         entry.path().c_str(), errorCode.value());
129             ret = false;
130         }
131     }
132     return ret;
133 }
134 
IsBootCompleted()135 bool IsBootCompleted()
136 {
137     return OHOS::system::GetParameter(BOOTEVENT_BOOT_COMPLETED, "false") == "true";
138 }
139 
IsLastShortStartUp()140 bool IsLastShortStartUp()
141 {
142     if (g_isLastStartUpShort) {
143         return true;
144     }
145     std::string lastBootUpKeyPoint = GetParamValueFromFile(CMD_LINE, LAST_BOOTUP_KEYPOINT);
146     if (lastBootUpKeyPoint.empty()) {
147         return false;
148     }
149     constexpr int normalNum = 250;
150     return atoi(lastBootUpKeyPoint.c_str()) < normalNum;
151 }
152 
IsRecoveryPanicEvent(const std::shared_ptr<SysEvent> & sysEvent)153 bool IsRecoveryPanicEvent(const std::shared_ptr<SysEvent>& sysEvent)
154 {
155     std::string logPath = sysEvent->GetEventValue("LOG_PATH");
156     return logPath.find(PANIC_LOG_PATH) == 0;
157 }
158 
GetLastRecoveryTime()159 std::string GetLastRecoveryTime()
160 {
161     std::string factoryRecoveryTime;
162     FileUtil::LoadStringFromFile(FACTORY_RECOVERY_TIME_PATH, factoryRecoveryTime);
163     return std::string("\"") + factoryRecoveryTime + "\"";
164 }
165 
GetCurrentVersion()166 std::string GetCurrentVersion()
167 {
168     std::string currentVersion = OHOS::system::GetParameter("const.product.software.version", "unknown");
169     return std::string("\"") + currentVersion + "\"";
170 }
171 
GetBackupFilePath(const std::string & timeStr)172 std::string GetBackupFilePath(const std::string& timeStr)
173 {
174     return std::string(PANIC_LOG_PATH) + "panic_log_" + timeStr + ".zip";
175 }
176 
GetParamValueFromString(const std::string & content,const std::string & param)177 std::string GetParamValueFromString(const std::string& content, const std::string& param)
178 {
179     std::smatch matchStr;
180     if (!std::regex_search(content, matchStr, std::regex(param + REGEX_FORMAT))) {
181         return "";
182     }
183     return matchStr[1].str();
184 }
185 
GetParamValueFromFile(const std::string & filePath,const std::string & param)186 std::string GetParamValueFromFile(const std::string& filePath, const std::string& param)
187 {
188     std::string content;
189     if (!FileUtil::LoadStringFromFile(filePath, content)) {
190         HIVIEW_LOGE("Failed to load file: %{public}s.", filePath.c_str());
191         return "";
192     }
193     return GetParamValueFromString(content, param);
194 }
195 
AddFileToZip(HiviewZipUnit & hiviewZipUnit,const std::string & path)196 void AddFileToZip(HiviewZipUnit& hiviewZipUnit, const std::string& path)
197 {
198     if (hiviewZipUnit.AddFileInZip(path, ZipFileLevel::KEEP_NONE_PARENT_PATH) != 0) {
199         HIVIEW_LOGW("Failed to add file: %{public}s to zip", path.c_str());
200     }
201 }
202 
CompressAndCopyLogFiles(const std::string & srcPath,const std::string & timeStr)203 void CompressAndCopyLogFiles(const std::string& srcPath, const std::string& timeStr)
204 {
205     if (!FileUtil::FileExists(PANIC_LOG_PATH)) {
206         HIVIEW_LOGE("The path of target file: %{public}s is not existed", PANIC_LOG_PATH);
207         return;
208     }
209     ClearFilesInDir(PANIC_LOG_PATH);
210     HiviewZipUnit hiviewZipUnit(GetBackupFilePath(timeStr));
211     std::string lastFastBootLog = srcPath + LAST_FASTBOOT_LOG;
212     AddFileToZip(hiviewZipUnit, lastFastBootLog);
213     std::string hmKLog = srcPath + HM_KLOG;
214     AddFileToZip(hiviewZipUnit, hmKLog);
215     std::string hmSnapShot = srcPath + HM_SNAPSHOT;
216     uint64_t sourceFileSize = FileUtil::GetFileSize(lastFastBootLog) + FileUtil::GetFileSize(hmKLog) +
217         FileUtil::GetFileSize(hmSnapShot);
218     if (sourceFileSize < COMPRESSION_RATION * ZIP_FILE_SIZE_LIMIT) {
219         AddFileToZip(hiviewZipUnit, hmSnapShot);
220     } else {
221         HIVIEW_LOGW("sourceFileSize size: %{public}" PRIu64 " is over limit, dropping hmSnapShot.", sourceFileSize);
222     }
223     uint64_t zipFileSize = FileUtil::GetFolderSize(PANIC_LOG_PATH);
224     if (zipFileSize > ZIP_FILE_SIZE_LIMIT) {
225         HIVIEW_LOGW("zip file size: %{public}" PRIu64 " is over limit", zipFileSize);
226         ClearFilesInDir(PANIC_LOG_PATH);
227         return;
228     }
229     BboxSaveLogFlags bboxSaveLogFlags = LoadBboxSaveFlagFromFile();
230     bboxSaveLogFlags.happenTime = timeStr;
231     bboxSaveLogFlags.factoryRecoveryTime = GetLastRecoveryTime();
232     bboxSaveLogFlags.isPanicUploaded = false;
233     bboxSaveLogFlags.softwareVersion = GetCurrentVersion();
234     SaveBboxLogFlagsToFile(bboxSaveLogFlags);
235 }
236 
ReportPanicEventAfterRecovery(const BboxSaveLogFlags & bboxSaveLogFlags)237 void ReportPanicEventAfterRecovery(const BboxSaveLogFlags& bboxSaveLogFlags)
238 {
239     const std::string& timeStr = bboxSaveLogFlags.happenTime;
240     int64_t happenTime = Tbox::GetHappenTime(StringUtil::GetRleftSubstr(timeStr, "-"),
241                                              "(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})");
242     HiSysEventWrite(
243         HisysEventUtil::KERNEL_VENDOR,
244         "PANIC",
245         HiSysEvent::EventType::FAULT,
246         "MODULE", "AP",
247         "REASON", "HM_PANIC:HM_PANIC_SYSMGR",
248         "LOG_PATH", GetBackupFilePath(timeStr),
249         "SUB_LOG_PATH", timeStr,
250         "HAPPEN_TIME", happenTime,
251         "FIRST_FRAME", "RECOVERY_PANIC",
252         "LAST_FRAME", bboxSaveLogFlags.softwareVersion,
253         "FINGERPRINT", Tbox::CalcFingerPrint(timeStr + bboxSaveLogFlags.softwareVersion, 0, FP_BUFFER)
254     );
255 }
256 
TryToReportRecoveryPanicEvent()257 bool TryToReportRecoveryPanicEvent()
258 {
259     BboxSaveLogFlags bboxSaveLogFlags = LoadBboxSaveFlagFromFile();
260     bool ret = false;
261     if (!bboxSaveLogFlags.isPanicUploaded && bboxSaveLogFlags.factoryRecoveryTime != GetLastRecoveryTime()) {
262         if (FileUtil::FileExists(GetBackupFilePath(bboxSaveLogFlags.happenTime))) {
263             ReportPanicEventAfterRecovery(bboxSaveLogFlags);
264             ret = true;
265         }
266     }
267     bboxSaveLogFlags.isStartUpShort = false;
268     SaveBboxLogFlagsToFile(bboxSaveLogFlags);
269     return ret;
270 }
271 
ConfirmReportResult()272 void ConfirmReportResult()
273 {
274     BboxSaveLogFlags bboxSaveLogFlags = LoadBboxSaveFlagFromFile();
275     const std::string& timeStr = bboxSaveLogFlags.happenTime;
276     if (HisysEventUtil::IsEventProcessed("PANIC", "LOG_PATH", GetBackupFilePath(timeStr))) {
277         bboxSaveLogFlags.isPanicUploaded = true;
278         SaveBboxLogFlagsToFile(bboxSaveLogFlags);
279     }
280 }
281 }
282 }
283 }