• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-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 "event_publish.h"
17 
18 #include "bundle_mgr_client.h"
19 #include "file_util.h"
20 #include "json/json.h"
21 #include "hiview_logger.h"
22 #include "storage_acl.h"
23 #include "string_util.h"
24 #include "time_util.h"
25 
26 using namespace OHOS::HiviewDFX::HiAppEvent;
27 namespace OHOS {
28 namespace HiviewDFX {
29 namespace {
30 DEFINE_LOG_TAG("HiView-EventPublish");
31 constexpr int VALUE_MOD = 200000;
32 constexpr int DELAY_TIME = 30;
33 const std::string PATH_DIR = "/data/log/hiview/system_event_db/events/temp";
34 const std::string SANDBOX_DIR = "/data/storage/el2/log";
35 const std::string FILE_PREFIX = "/hiappevent_";
36 const std::string FILE_SUFFIX = ".evt";
37 const std::string DOMAIN_PROPERTY = "domain";
38 const std::string NAME_PROPERTY = "name";
39 const std::string EVENT_TYPE_PROPERTY = "eventType";
40 const std::string PARAM_PROPERTY = "params";
41 const std::string LOG_OVER_LIMIT = "log_over_limit";
42 const std::string EXTERNAL_LOG = "external_log";
43 const std::string PID = "pid";
44 const std::string IS_BUSINESS_JANK = "is_business_jank";
45 constexpr uint64_t MAX_FILE_SIZE = 5 * 1024 * 1024; // 5M
46 constexpr uint64_t WATCHDOG_MAX_FILE_SIZE = 10 * 1024 * 1024; // 10M
47 constexpr uint64_t RESOURCE_OVERLIMIT_MAX_FILE_SIZE = 2048ull * 1024 * 1024; // 2G
48 const std::string XATTR_NAME = "user.appevent";
49 constexpr uint64_t BIT_MASK = 1;
50 const std::unordered_map<std::string, uint8_t> OS_EVENT_POS_INFOS = {
51     { EVENT_APP_CRASH, 0 },
52     { EVENT_APP_FREEZE, 1 },
53     { EVENT_APP_LAUNCH, 2 },
54     { EVENT_SCROLL_JANK, 3 },
55     { EVENT_CPU_USAGE_HIGH, 4 },
56     { EVENT_BATTERY_USAGE, 5 },
57     { EVENT_RESOURCE_OVERLIMIT, 6 },
58     { EVENT_ADDRESS_SANITIZER, 7 },
59     { EVENT_MAIN_THREAD_JANK, 8 },
60     { EVENT_APP_START, 9 },
61 };
62 
63 struct ExternalLogInfo {
64     std::string extensionType_;
65     std::string subPath_;
66     uint64_t maxFileSize_;
67 };
68 
GetExternalLogInfo(const std::string & eventName,ExternalLogInfo & externalLogInfo)69 void GetExternalLogInfo(const std::string &eventName, ExternalLogInfo &externalLogInfo)
70 {
71     if (eventName == EVENT_MAIN_THREAD_JANK) {
72         externalLogInfo.extensionType_ = ".trace";
73         externalLogInfo.subPath_ = "watchdog";
74         externalLogInfo.maxFileSize_ = WATCHDOG_MAX_FILE_SIZE;
75     } else if (eventName == EVENT_RESOURCE_OVERLIMIT) {
76         externalLogInfo.extensionType_ = ".log";
77         externalLogInfo.subPath_ = "resourcelimit";
78         externalLogInfo.maxFileSize_ = RESOURCE_OVERLIMIT_MAX_FILE_SIZE;
79     } else {
80         externalLogInfo.extensionType_ = ".log";
81         externalLogInfo.subPath_ = "hiappevent";
82         externalLogInfo.maxFileSize_ = MAX_FILE_SIZE;
83     }
84 }
85 
GetTempFilePath(int32_t uid)86 std::string GetTempFilePath(int32_t uid)
87 {
88     std::string srcPath = PATH_DIR;
89     srcPath.append(FILE_PREFIX).append(std::to_string(uid)).append(FILE_SUFFIX);
90     return srcPath;
91 }
92 
GetBundleNameById(int32_t uid)93 std::string GetBundleNameById(int32_t uid)
94 {
95     std::string bundleName;
96     AppExecFwk::BundleMgrClient client;
97     if (client.GetNameForUid(uid, bundleName) != 0) {
98         HIVIEW_LOGW("Failed to query bundleName from bms, uid=%{public}d.", uid);
99     } else {
100         HIVIEW_LOGD("bundleName of uid=%{public}d", uid);
101     }
102     return bundleName;
103 }
104 
GetSandBoxBasePath(int32_t uid,const std::string & bundleName)105 std::string GetSandBoxBasePath(int32_t uid, const std::string& bundleName)
106 {
107     int userId = uid / VALUE_MOD;
108     std::string path;
109     path.append("/data/app/el2/")
110         .append(std::to_string(userId))
111         .append("/base/")
112         .append(bundleName)
113         .append("/cache/hiappevent");
114     return path;
115 }
116 
GetSandBoxLogPath(int32_t uid,const std::string & bundleName,const ExternalLogInfo & externalLogInfo)117 std::string GetSandBoxLogPath(int32_t uid, const std::string& bundleName, const ExternalLogInfo &externalLogInfo)
118 {
119     int userId = uid / VALUE_MOD;
120     std::string path;
121     path.append("/data/app/el2/")
122         .append(std::to_string(userId))
123         .append("/log/")
124         .append(bundleName);
125     path.append("/").append(externalLogInfo.subPath_);
126     return path;
127 }
128 
CopyExternalLog(int32_t uid,const std::string & externalLog,const std::string & destPath)129 bool CopyExternalLog(int32_t uid, const std::string& externalLog, const std::string& destPath)
130 {
131     if (FileUtil::CopyFileFast(externalLog, destPath) == 0) {
132         std::string entryTxt = "u:" + std::to_string(uid) + ":rwx";
133         if (OHOS::StorageDaemon::AclSetAccess(destPath, entryTxt) != 0) {
134             HIVIEW_LOGE("failed to set acl access dir");
135             FileUtil::RemoveFile(destPath);
136             return false;
137         }
138         return true;
139     }
140     HIVIEW_LOGE("failed to move log file to sandbox.");
141     return false;
142 }
143 
CheckInSandBoxLog(const std::string & externalLog,const std::string & sandBoxLogPath,Json::Value & externalLogJson,bool & logOverLimit)144 bool CheckInSandBoxLog(const std::string& externalLog, const std::string& sandBoxLogPath,
145     Json::Value& externalLogJson, bool& logOverLimit)
146 {
147     if (externalLog.find(SANDBOX_DIR) == 0) {
148         HIVIEW_LOGI("File in sandbox path not copy.");
149         std::string fileName = FileUtil::ExtractFileName(externalLog);
150         if (FileUtil::FileExists(sandBoxLogPath + "/" + fileName)) {
151             externalLogJson.append(externalLog);
152         } else {
153             HIVIEW_LOGE("sand box log does not exist");
154             logOverLimit = true;
155         }
156         return true;
157     }
158     return false;
159 }
160 
GetDesFileName(Json::Value & params,const std::string & eventName,const ExternalLogInfo & externalLogInfo)161 std::string GetDesFileName(Json::Value& params, const std::string& eventName,
162     const ExternalLogInfo& externalLogInfo)
163 {
164     std::string timeStr = std::to_string(TimeUtil::GetMilliseconds());
165     int pid = 0;
166     if (params.isMember(PID) && params[PID].isInt()) {
167         pid = params[PID].asInt();
168     }
169 
170     std::string desFileName;
171     const std::string BUSINESS_JANK_PREFIX = "BUSINESS_THREAD_JANK";
172     if (params.isMember(IS_BUSINESS_JANK) && params[IS_BUSINESS_JANK].isBool() &&
173         params[IS_BUSINESS_JANK].asBool()) {
174         desFileName = BUSINESS_JANK_PREFIX + "_" + timeStr + "_" + std::to_string(pid)
175         + externalLogInfo.extensionType_;
176     } else {
177         desFileName = eventName + "_" + timeStr + "_" + std::to_string(pid)
178         + externalLogInfo.extensionType_;
179     }
180     return desFileName;
181 }
182 
SendLogToSandBox(int32_t uid,const std::string & eventName,std::string & sandBoxLogPath,Json::Value & params,const ExternalLogInfo & externalLogInfo)183 void SendLogToSandBox(int32_t uid, const std::string& eventName, std::string& sandBoxLogPath, Json::Value& params,
184     const ExternalLogInfo &externalLogInfo)
185 {
186     if (!params.isMember(EXTERNAL_LOG) || !params[EXTERNAL_LOG].isArray() || params[EXTERNAL_LOG].empty()) {
187         HIVIEW_LOGE("no external log need to copy.");
188         return;
189     }
190 
191     bool logOverLimit = false;
192     Json::Value externalLogJson(Json::arrayValue);
193     uint64_t dirSize = FileUtil::GetFolderSize(sandBoxLogPath);
194     for (Json::ArrayIndex i = 0; i < params[EXTERNAL_LOG].size(); ++i) {
195         std::string externalLog = "";
196         if (params[EXTERNAL_LOG][i].isString()) {
197             externalLog = params[EXTERNAL_LOG][i].asString();
198         }
199         if (CheckInSandBoxLog(externalLog, sandBoxLogPath, externalLogJson, logOverLimit)) {
200             continue;
201         }
202         if (externalLog.empty() || !FileUtil::FileExists(externalLog)) {
203             HIVIEW_LOGI("externalLog is empty or not exist.");
204             continue;
205         }
206         uint64_t fileSize = FileUtil::GetFileSize(externalLog);
207         if (dirSize + fileSize <= externalLogInfo.maxFileSize_) {
208             std::string desFileName = GetDesFileName(params, eventName, externalLogInfo);
209             std::string destPath;
210             destPath.append(sandBoxLogPath).append("/").append(desFileName);
211             if (CopyExternalLog(uid, externalLog, destPath)) {
212                 dirSize += fileSize;
213                 externalLogJson.append("/data/storage/el2/log/" + externalLogInfo.subPath_ + "/" + desFileName);
214                 HIVIEW_LOGI("move log file to sandBoxLogPath.");
215             }
216         } else {
217             HIVIEW_LOGE("sand box log dir overlimit file, dirSzie=%{public}" PRIu64 ", limitSize=%{public}" PRIu64,
218                 dirSize, externalLogInfo.maxFileSize_);
219             logOverLimit = true;
220             break;
221         }
222     }
223     params[LOG_OVER_LIMIT] = logOverLimit;
224     params[EXTERNAL_LOG] = externalLogJson;
225 }
226 
RemoveEventInternalField(Json::Value & eventJson)227 void RemoveEventInternalField(Json::Value& eventJson)
228 {
229     if (eventJson[PARAM_PROPERTY].isMember(IS_BUSINESS_JANK)) {
230         eventJson[PARAM_PROPERTY].removeMember(IS_BUSINESS_JANK);
231     }
232     return;
233 }
234 
WriteEventJson(Json::Value & eventJson,const std::string & filePath)235 void WriteEventJson(Json::Value& eventJson, const std::string& filePath)
236 {
237     RemoveEventInternalField(eventJson);
238     std::string eventStr = Json::FastWriter().write(eventJson);
239     if (!FileUtil::SaveStringToFile(filePath, eventStr, false)) {
240         HIVIEW_LOGE("failed to save event, eventName=%{public}s", eventJson[NAME_PROPERTY].asString().c_str());
241         return;
242     }
243     HIVIEW_LOGI("save event finish, eventName=%{public}s", eventJson[NAME_PROPERTY].asString().c_str());
244 }
245 
SaveEventAndLogToSandBox(int32_t uid,const std::string & eventName,const std::string & bundleName,Json::Value & eventJson)246 void SaveEventAndLogToSandBox(int32_t uid, const std::string& eventName, const std::string& bundleName,
247     Json::Value& eventJson)
248 {
249     ExternalLogInfo externalLogInfo;
250     GetExternalLogInfo(eventName, externalLogInfo);
251     std::string sandBoxLogPath = GetSandBoxLogPath(uid, bundleName, externalLogInfo);
252     SendLogToSandBox(uid, eventName, sandBoxLogPath, eventJson[PARAM_PROPERTY], externalLogInfo);
253     std::string desPath = GetSandBoxBasePath(uid, bundleName);
254     std::string timeStr = std::to_string(TimeUtil::GetMilliseconds());
255     desPath.append(FILE_PREFIX).append(timeStr).append(".txt");
256     WriteEventJson(eventJson, desPath);
257 }
258 
SaveEventToTempFile(int32_t uid,Json::Value & eventJson)259 void SaveEventToTempFile(int32_t uid, Json::Value& eventJson)
260 {
261     std::string tempPath = GetTempFilePath(uid);
262     WriteEventJson(eventJson, tempPath);
263 }
264 
CheckAppListenedEvents(const std::string & path,const std::string & eventName)265 bool CheckAppListenedEvents(const std::string& path, const std::string& eventName)
266 {
267     if (OS_EVENT_POS_INFOS.find(eventName) == OS_EVENT_POS_INFOS.end()) {
268         HIVIEW_LOGE("undefined event path, eventName=%{public}s.", eventName.c_str());
269         return false;
270     }
271 
272     std::string value;
273     if (!FileUtil::GetDirXattr(path, XATTR_NAME, value)) {
274         HIVIEW_LOGE("failed to get xattr path, eventName=%{public}s.", eventName.c_str());
275         return false;
276     }
277     if (value.empty()) {
278         HIVIEW_LOGE("getxattr value empty path, eventName=%{public}s.", eventName.c_str());
279         return false;
280     }
281     HIVIEW_LOGD("getxattr success path, eventName=%{public}s, value=%{public}s.", eventName.c_str(), value.c_str());
282     uint64_t eventsMask = static_cast<uint64_t>(std::strtoull(value.c_str(), nullptr, 0));
283     if (!(eventsMask & (BIT_MASK << OS_EVENT_POS_INFOS.at(eventName)))) {
284         HIVIEW_LOGI("unlistened event path, eventName=%{public}s, eventsMask=%{public}" PRIu64, eventName.c_str(),
285             eventsMask);
286         return false;
287     }
288     return true;
289 }
290 }
291 
StartOverLimitThread(int32_t uid,const std::string & eventName,const std::string & bundleName,Json::Value & eventJson)292 void EventPublish::StartOverLimitThread(int32_t uid, const std::string& eventName, const std::string& bundleName,
293     Json::Value& eventJson)
294 {
295     if (sendingOverlimitThread_) {
296         return;
297     }
298     HIVIEW_LOGI("start send overlimit thread.");
299     sendingOverlimitThread_ = std::make_unique<std::thread>([this, uid, eventName, bundleName, eventJson] {
300         this->SendOverLimitEventToSandBox(uid, eventName, bundleName, eventJson);
301     });
302     sendingOverlimitThread_->detach();
303 }
304 
SendOverLimitEventToSandBox(int32_t uid,const std::string & eventName,const std::string & bundleName,Json::Value eventJson)305 void EventPublish::SendOverLimitEventToSandBox(int32_t uid, const std::string& eventName,
306                                                const std::string& bundleName, Json::Value eventJson)
307 {
308     ExternalLogInfo externalLogInfo;
309     GetExternalLogInfo(eventName, externalLogInfo);
310     std::string sandBoxLogPath = GetSandBoxLogPath(uid, bundleName, externalLogInfo);
311     SendLogToSandBox(uid, eventName, sandBoxLogPath, eventJson[PARAM_PROPERTY], externalLogInfo);
312     std::string desPath = GetSandBoxBasePath(uid, bundleName);
313     std::string timeStr = std::to_string(TimeUtil::GetMilliseconds());
314     desPath.append(FILE_PREFIX).append(timeStr).append(".txt");
315     WriteEventJson(eventJson, desPath);
316     sendingOverlimitThread_.reset();
317 }
318 
StartSendingThread()319 void EventPublish::StartSendingThread()
320 {
321     if (sendingThread_ == nullptr) {
322         HIVIEW_LOGI("start send thread.");
323         sendingThread_ = std::make_unique<std::thread>([this] { this->SendEventToSandBox(); });
324         sendingThread_->detach();
325     }
326 }
327 
SendEventToSandBox()328 void EventPublish::SendEventToSandBox()
329 {
330     std::this_thread::sleep_for(std::chrono::seconds(DELAY_TIME));
331     std::lock_guard<std::mutex> lock(mutex_);
332     std::string timeStr = std::to_string(TimeUtil::GetMilliseconds());
333     std::vector<std::string> files;
334     FileUtil::GetDirFiles(PATH_DIR, files, false);
335     for (const auto& srcPath : files) {
336         std::string uidStr = StringUtil::GetMidSubstr(srcPath, FILE_PREFIX, FILE_SUFFIX);
337         if (uidStr.empty()) {
338             continue;
339         }
340         int32_t uid = StringUtil::StrToInt(uidStr);
341         std::string bundleName = GetBundleNameById(uid);
342         if (bundleName.empty()) {
343             HIVIEW_LOGW("empty bundleName uid=%{public}d.", uid);
344             (void)FileUtil::RemoveFile(srcPath);
345             continue;
346         }
347         std::string desPath = GetSandBoxBasePath(uid, bundleName);
348         if (!FileUtil::FileExists(desPath)) {
349             HIVIEW_LOGE("SendEventToSandBox not exit.");
350             (void)FileUtil::RemoveFile(srcPath);
351             continue;
352         }
353         desPath.append(FILE_PREFIX).append(timeStr).append(".txt");
354         if (FileUtil::CopyFile(srcPath, desPath) == -1) {
355             HIVIEW_LOGE("failed to move file to desFile.");
356             continue;
357         }
358         HIVIEW_LOGI("copy srcPath to desPath success.");
359         (void)FileUtil::RemoveFile(srcPath);
360     }
361     sendingThread_.reset();
362 }
363 
PushEvent(int32_t uid,const std::string & eventName,HiSysEvent::EventType eventType,const std::string & paramJson)364 void EventPublish::PushEvent(int32_t uid, const std::string& eventName, HiSysEvent::EventType eventType,
365     const std::string& paramJson)
366 {
367     if (eventName.empty() || paramJson.empty() || uid < 0) {
368         HIVIEW_LOGW("empty param.");
369         return;
370     }
371     std::string bundleName = GetBundleNameById(uid);
372     if (bundleName.empty()) {
373         HIVIEW_LOGW("empty bundleName uid=%{public}d.", uid);
374         return;
375     }
376     std::lock_guard<std::mutex> lock(mutex_);
377     if (!FileUtil::FileExists(PATH_DIR) && !FileUtil::ForceCreateDirectory(PATH_DIR)) {
378         HIVIEW_LOGE("failed to create resourceDir.");
379         return;
380     }
381     std::string srcPath = GetTempFilePath(uid);
382     std::string desPath = GetSandBoxBasePath(uid, bundleName);
383     if (!FileUtil::FileExists(desPath)) {
384         HIVIEW_LOGE("desPath not exit.");
385         (void)FileUtil::RemoveFile(srcPath);
386         return;
387     }
388     if (!CheckAppListenedEvents(desPath, eventName)) {
389         return;
390     }
391 
392     Json::Value eventJson;
393     eventJson[DOMAIN_PROPERTY] = DOMAIN_OS;
394     eventJson[NAME_PROPERTY] = eventName;
395     eventJson[EVENT_TYPE_PROPERTY] = eventType;
396     Json::Value params;
397     Json::Reader reader;
398     if (!reader.parse(paramJson, params)) {
399         HIVIEW_LOGE("failed to parse paramJson bundleName, eventName=%{public}s.", eventName.c_str());
400         return;
401     }
402     eventJson[PARAM_PROPERTY] = params;
403     const std::unordered_set<std::string> immediateEvents = {"APP_CRASH", "APP_FREEZE", "ADDRESS_SANITIZER",
404         "APP_LAUNCH", "CPU_USAGE_HIGH", EVENT_MAIN_THREAD_JANK};
405     if (immediateEvents.find(eventName) != immediateEvents.end()) {
406         SaveEventAndLogToSandBox(uid, eventName, bundleName, eventJson);
407     } else if (eventName == EVENT_RESOURCE_OVERLIMIT) {
408         StartOverLimitThread(uid, eventName, bundleName, std::ref(eventJson));
409     } else {
410         SaveEventToTempFile(uid, eventJson);
411         StartSendingThread();
412     }
413 }
414 } // namespace HiviewDFX
415 } // namespace OHOS