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