• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "dump_runtime_helper.h"
17 
18 #include <dfx_signal_handler.h>
19 #include <sys/statvfs.h>
20 #include <sys/stat.h>
21 #include <sys/xattr.h>
22 #include "app_mgr_client.h"
23 #include "faultloggerd_client.h"
24 #include "hilog_tag_wrapper.h"
25 #include "js_runtime.h"
26 #include "js_runtime_utils.h"
27 #include "singleton.h"
28 #include "dfx_jsnapi.h"
29 #include "parameters.h"
30 #include "ffrt.h"
31 #include "directory_ex.h"
32 #include "storage_acl.h"
33 
34 namespace OHOS {
35 namespace AppExecFwk {
36 const char *MODULE_NAME = "hiviewdfx.jsLeakWatcher";
37 const char *CHECK = "check";
38 const char *REQUIRE_NAPI = "requireNapi";
39 
40 static bool g_betaVersion = OHOS::system::GetParameter("const.logsystem.versiontype", "unknown") == "beta";
41 static bool g_developMode = (OHOS::system::GetParameter("persist.hiview.leak_detector", "unknown") == "enable") ||
42                             (OHOS::system::GetParameter("persist.hiview.leak_detector", "unknown") == "true");
43 static int g_oomDumpProcessQuota = 0;
44 static uint64_t g_lastOOMDumpTime = 0;
45 static constexpr const char* const EVENT_XATTR_NAME = "user.appevent";
46 static constexpr const char* const OOM_QUOTA_XATTR_NAME = "user.oomdump.quota";
47 static constexpr const char* const PROPERTY2C = "user.oomdumptelemetry.quota";
48 static constexpr const char* const HIAPPEVENT_PATH = "/data/storage/el2/base/cache/hiappevent";
49 static constexpr const char* const OOM_QUOTA_PATH = "/data/storage/el2/base/cache/rawheap";
50 static constexpr const char* const JS_HEAP_LOGTYPE = "user.event_config.js_heap_logtype";
51 static constexpr const char* const EVENT_RAWHEAP = "event_rawheap";
52 static constexpr uint64_t OOM_DUMP_INTERVAL = 7 * 24 * 60 * 60;
53 static constexpr uint64_t OOM_DUMP_SPACE_LIMIT = 30ull * 1024 * 1024 * 1024;
54 static constexpr uint32_t EVENT_RESOURCE_OVERLIMIT_MASK = 6;
55 static constexpr uint64_t BIT_MASK = 1;
56 static constexpr uint32_t BUF_SIZE_256 = 256;
57 static constexpr int DECIMAL_BASE = 10;
58 static constexpr int KB_PER_MB = 1024;
59 
60 enum {
61     INDEX_DELIVERY_TS = 0,
62     INDEX_COMPRESSED_TS_0,
63     INDEX_COMPRESSED_TS_1,
64     INDEX_COMPRESSED_TS_2,
65     INDEX_COMPRESSED_TS_3,
66     INDEX_COMPRESSED_TS_4,
67     INDEX_APP_QUOTA,
68     INDEX_HAS_SENT,
69     INDEX_ROM_RSV_SIZE,
70     PROPERTY2C_SIZE
71 };
72 
DumpRuntimeHelper(const std::shared_ptr<OHOSApplication> & application)73 DumpRuntimeHelper::DumpRuntimeHelper(const std::shared_ptr<OHOSApplication> &application)
74     : application_(application)
75 {}
76 
Check2DOOMDumpOpt()77 bool DumpRuntimeHelper::Check2DOOMDumpOpt()
78 {
79     char* env = getenv("DFX_RESOURCE_OVERLIMIT_OPTIONS");
80     if (env == nullptr) {
81         return false;
82     }
83     return strstr(env, "oomdump:enable") != nullptr;
84 }
85 
SetAppFreezeFilterCallback()86 void DumpRuntimeHelper::SetAppFreezeFilterCallback()
87 {
88     CreateDirDelay(OOM_QUOTA_PATH);
89     if (application_ == nullptr) {
90         TAG_LOGE(AAFwkTag::APPKIT, "null application");
91         return;
92     }
93     auto &runtime = application_->GetRuntime();
94     if (runtime == nullptr) {
95         TAG_LOGE(AAFwkTag::APPKIT, "null runtime");
96         return;
97     }
98     auto appfreezeFilterCallback =
99         [this] (const int32_t pid, const bool needDecreaseQuota, std::string &eventConfig) -> bool {
100         eventConfig = GetEventConfig(JS_HEAP_LOGTYPE);
101         auto client = DelayedSingleton<AppExecFwk::AppMgrClient>::GetInstance();
102         if (client == nullptr) {
103             TAG_LOGE(AAFwkTag::APPKIT, "null client");
104             return false;
105         }
106         if (!CheckOomdumpSwitch()) {
107             TAG_LOGE(AAFwkTag::APPKIT, "failed to check oomdump switch");
108             return false;
109         }
110         if (g_betaVersion || g_developMode || !needDecreaseQuota) {
111             TAG_LOGI(AAFwkTag::APPKIT, "no need to check quota, just dump."
112                 " beta: %{public}d, develop: %{public}d, hidumper: %{public}d",
113                 g_betaVersion, g_developMode, !needDecreaseQuota);
114             client->SetAppFreezeFilter(pid);
115             return true;
116         }
117         bool ret2custom = (eventConfig == EVENT_RAWHEAP);
118         bool ret2D = Check2DQuota(needDecreaseQuota);
119         bool ret2C = Check2CQuota();
120         if (!ret2custom && !ret2D && !ret2C) {
121             TAG_LOGI(AAFwkTag::APPKIT, "check custom|2C|2D quota both failed, no dump.");
122             return false;
123         }
124         TAG_LOGI(AAFwkTag::APPKIT, "check success, will dump. custom: %{public}d 2C: %{public}d, 2D: %{public}d",
125                  ret2custom, ret2C, ret2D);
126         client->SetAppFreezeFilter(pid);
127         return true;
128     };
129     auto vm = (static_cast<AbilityRuntime::JsRuntime&>(*runtime)).GetEcmaVm();
130     panda::DFXJSNApi::SetAppFreezeFilterCallback(vm, appfreezeFilterCallback);
131 }
132 
CheckOomdumpSwitch()133 bool DumpRuntimeHelper::CheckOomdumpSwitch()
134 {
135     // if value is disable, then no dump whatever
136     std::string switchValue = OHOS::system::GetParameter("hiview.oomdump.switch", "unknown");
137     TAG_LOGI(AAFwkTag::APPKIT, "oomdump switch: %{public}s", switchValue.c_str());
138     return switchValue != "disable";
139 }
140 
Check2CQuota()141 bool DumpRuntimeHelper::Check2CQuota()
142 {
143     std::vector<int64_t> quota2C;
144     if (!GetQuota(OOM_QUOTA_PATH, PROPERTY2C, quota2C, PROPERTY2C_SIZE)) {
145         TAG_LOGE(AAFwkTag::APPKIT, "failed to GetQuota for PROPERTY2C");
146         return false;
147     }
148 
149     int compressQuota = GetCompressQuota(quota2C);
150     int appQuota = static_cast<int>(quota2C[INDEX_APP_QUOTA]);
151     int leftQuota = MIN(compressQuota, appQuota) - static_cast<int>(quota2C[INDEX_HAS_SENT]);
152     if (leftQuota <= 0) {
153         TAG_LOGE(AAFwkTag::APPKIT, "invalid quota, compress: %{public}d, app: %{public}d, sent: %{public}" PRId64,
154             compressQuota, appQuota, quota2C[INDEX_HAS_SENT]);
155         return false;
156     }
157 
158     uint64_t now = GetCurrentTimestamp();
159     if (quota2C[INDEX_DELIVERY_TS] < 0) {
160         TAG_LOGE(AAFwkTag::APPKIT, "invalid quota2C[%{public}d]: %{public}" PRId64,
161             INDEX_DELIVERY_TS, quota2C[INDEX_DELIVERY_TS]);
162         return false;
163     }
164     uint64_t deliveryTs = static_cast<uint64_t>(quota2C[INDEX_DELIVERY_TS]);
165     if (now < deliveryTs || now - deliveryTs > OOM_DUMP_INTERVAL) {
166         TAG_LOGE(AAFwkTag::APPKIT, "invalid deliveryTs: %{public}" PRIu64, deliveryTs);
167         return false;
168     }
169 
170     uint64_t spaceQuota = static_cast<uint64_t>(quota2C[INDEX_ROM_RSV_SIZE]) * KB_PER_MB;
171     if (!CheckOOMFreeSpace(spaceQuota)) {
172         TAG_LOGE(AAFwkTag::APPKIT, "rom free space is less than spaceQuota: %{public}" PRIu64, spaceQuota);
173         return false;
174     }
175 
176     TAG_LOGE(AAFwkTag::APPKIT, "success check 2C Quota");
177     return true;
178 }
179 
Check2DQuota(bool needDecreaseQuota)180 bool DumpRuntimeHelper::Check2DQuota(bool needDecreaseQuota)
181 {
182     if (!Check2DOOMDumpOpt()) {
183         TAG_LOGE(AAFwkTag::APPKIT, "Check2DOOMDumpOpt failed");
184         return false;
185     }
186     uint64_t time = 0ull;
187     int quota = 0;
188     int value = OHOS::system::GetIntParameter("persist.hiview.oomdump.process.maxcount", 0);
189     if (value < 0) {
190         TAG_LOGE(AAFwkTag::APPKIT, "invalid persist.hiview.oomdump.process.maxcount: %{public}d", value);
191         return false;
192     }
193     uint32_t oomDumpProcessMaxQuota = static_cast<uint32_t>(value);
194     if (!Get2DQuota(OOM_QUOTA_PATH, OOM_QUOTA_XATTR_NAME, time, quota)) {
195         TAG_LOGI(AAFwkTag::APPKIT, "failed to Get2DQuota, need to Init2DOOMDumpQuota");
196         if (!Init2DOOMDumpQuota(OOM_QUOTA_PATH, oomDumpProcessMaxQuota)) {
197             TAG_LOGE(AAFwkTag::APPKIT, "failed Init2DOOMDumpQuota");
198             return false;
199         }
200     }
201     value = OHOS::system::GetIntParameter("persist.hiview.oomdump.maxcount", 0);
202     if (value < 0) {
203         TAG_LOGE(AAFwkTag::APPKIT, "invalid persist.hiview.oomdump.maxcount: %{public}d", value);
204         return false;
205     }
206     uint32_t oomDumpMaxQuota = static_cast<uint32_t>(value);
207     if (!Check2DOOMDumpQuota(oomDumpMaxQuota, oomDumpProcessMaxQuota)) {
208         TAG_LOGE(AAFwkTag::APPKIT, "Check2DOOMDumpQuota failed");
209         return false;
210     }
211     Set2DQuota(OOM_QUOTA_PATH, GetCurrentTimestamp(), g_oomDumpProcessQuota - 1);
212 
213     TAG_LOGI(AAFwkTag::APPKIT, "succeed to Check2DQuota");
214     return true;
215 }
216 
DumpJsHeap(const OHOS::AppExecFwk::JsHeapDumpInfo & info)217 void DumpRuntimeHelper::DumpJsHeap(const OHOS::AppExecFwk::JsHeapDumpInfo &info)
218 {
219     if (application_ == nullptr) {
220         TAG_LOGE(AAFwkTag::APPKIT, "null application");
221         return;
222     }
223     auto &runtime = application_->GetRuntime();
224     if (runtime == nullptr) {
225         TAG_LOGE(AAFwkTag::APPKIT, "null runtime");
226         return;
227     }
228     if (runtime->GetLanguage() != OHOS::AbilityRuntime::Runtime::Language::JS) {
229         TAG_LOGE(AAFwkTag::APPKIT, "runtime language is not JS");
230         return;
231     }
232     if (info.needLeakobj) {
233         std::string checkList = "";
234         GetCheckList(runtime, checkList);
235         WriteCheckList(checkList);
236     }
237 
238     if (info.needSnapshot == true) {
239         runtime->DumpHeapSnapshot(info.tid, info.needGc, info.needBinary);
240     } else {
241         if (info.needGc == true) {
242             runtime->ForceFullGC(info.tid);
243         }
244     }
245 }
246 
DumpCjHeap(const OHOS::AppExecFwk::CjHeapDumpInfo & info)247 void DumpRuntimeHelper::DumpCjHeap(const OHOS::AppExecFwk::CjHeapDumpInfo &info)
248 {
249     if (application_ == nullptr) {
250         TAG_LOGE(AAFwkTag::APPKIT, "null application");
251         return;
252     }
253     auto &runtime = application_->GetRuntime();
254     if (runtime == nullptr) {
255         TAG_LOGE(AAFwkTag::APPKIT, "null runtime");
256         return;
257     }
258     if (runtime->GetLanguage() != OHOS::AbilityRuntime::Runtime::Language::CJ) {
259         TAG_LOGE(AAFwkTag::APPKIT, "runtime language is not CJ");
260         return;
261     }
262     if (info.needSnapshot == true) {
263         runtime->DumpHeapSnapshot(info.pid, info.needGc);
264     } else {
265         if (info.needGc == true) {
266             runtime->ForceFullGC(info.pid);
267         }
268     }
269 }
270 
GetCheckList(const std::unique_ptr<AbilityRuntime::Runtime> & runtime,std::string & checkList)271 void DumpRuntimeHelper::GetCheckList(const std::unique_ptr<AbilityRuntime::Runtime> &runtime, std::string &checkList)
272 {
273     if (runtime->GetLanguage() != AbilityRuntime::Runtime::Language::JS) {
274         TAG_LOGE(AAFwkTag::APPKIT, "current language not js");
275         return;
276     }
277     AbilityRuntime::JsRuntime &jsruntime = static_cast<AbilityRuntime::JsRuntime&>(*runtime);
278     AbilityRuntime::HandleScope handleScope(jsruntime);
279     auto env = jsruntime.GetNapiEnv();
280 
281     napi_value global = nullptr;
282     napi_get_global(env, &global);
283     napi_value requireValue = GetJsLeakModule(env, global);
284     if (requireValue == nullptr) {
285         TAG_LOGE(AAFwkTag::APPKIT, "null requireValue");
286         return;
287     }
288     napi_value result = GetMethodCheck(env, requireValue, global);
289     if (result == nullptr) {
290         TAG_LOGE(AAFwkTag::APPKIT, "null result");
291         return;
292     }
293 
294     size_t checkListSize = 0;
295     napi_get_value_string_utf8(env, result, nullptr, 0, &checkListSize);
296     checkList.resize(checkListSize + 1);
297     napi_get_value_string_utf8(env, result, &checkList[0], checkListSize + 1, &checkListSize);
298 }
299 
GetJsLeakModule(napi_env env,napi_value global)300 napi_value DumpRuntimeHelper::GetJsLeakModule(napi_env env, napi_value global)
301 {
302     napi_value napiFunc = nullptr;
303     napi_status status = napi_get_named_property(env, global, REQUIRE_NAPI, &napiFunc);
304     if (status != napi_ok) {
305         TAG_LOGE(AAFwkTag::APPKIT, "fail, %{public}d", status);
306         return nullptr;
307     }
308     napi_value moduleName = nullptr;
309     napi_create_string_utf8(env, MODULE_NAME, strlen(MODULE_NAME), &moduleName);
310     napi_value param[1] = {moduleName};
311     napi_value requireValue = nullptr;
312     status = napi_call_function(env, global, napiFunc, 1, &param[0], &requireValue);
313     if (status != napi_ok) {
314         TAG_LOGE(AAFwkTag::APPKIT, "fail, %{public}d", status);
315         return nullptr;
316     }
317     return requireValue;
318 }
319 
GetMethodCheck(napi_env env,napi_value requireValue,napi_value global)320 napi_value DumpRuntimeHelper::GetMethodCheck(napi_env env, napi_value requireValue, napi_value global)
321 {
322     napi_value methodCheck = nullptr;
323     napi_status status = napi_get_named_property(env, requireValue, CHECK, &methodCheck);
324     if (status != napi_ok) {
325         TAG_LOGE(AAFwkTag::APPKIT, "fail, %{public}d", status);
326         return nullptr;
327     }
328     napi_valuetype valuetype = napi_undefined;
329     status = napi_typeof(env, methodCheck, &valuetype);
330     if (status != napi_ok) {
331         TAG_LOGE(AAFwkTag::APPKIT, "failed, %{public}d", status);
332         return nullptr;
333     }
334     napi_value result = nullptr;
335     status = napi_call_function(env, global, methodCheck, 0, nullptr, &result);
336     if (status != napi_ok) {
337         TAG_LOGE(AAFwkTag::APPKIT, "fail, %{public}d", status);
338         return nullptr;
339     }
340     return result;
341 }
342 
WriteCheckList(const std::string & checkList)343 void DumpRuntimeHelper::WriteCheckList(const std::string &checkList)
344 {
345     TAG_LOGD(AAFwkTag::APPKIT, "called");
346     int32_t fd = RequestFileDescriptor(static_cast<int32_t>(FaultLoggerType::JS_HEAP_LEAK_LIST));
347     if (fd < 0) {
348         TAG_LOGE(AAFwkTag::APPKIT, "fd:%{public}d.\n", fd);
349         return;
350     }
351     if (write(fd, checkList.c_str(), strlen(checkList.c_str())) == -1) {
352         TAG_LOGE(AAFwkTag::APPKIT, "fd:%{public}d, errno:%{public}d.\n", fd, errno);
353         close(fd);
354         return;
355     }
356     close(fd);
357 }
358 
CreateDirDelay(const std::string & path)359 void DumpRuntimeHelper::CreateDirDelay(const std::string &path)
360 {
361     ffrt::submit([=] {
362         if (!CreateDir(path)) {
363             TAG_LOGE(AAFwkTag::APPKIT, "failed to create %{public}s", path.c_str());
364             return;
365         }
366         constexpr mode_t defaultLogDirMode = 0770;
367         if (!OHOS::ChangeModeDirectory(path.c_str(), defaultLogDirMode)) {
368             TAG_LOGE(AAFwkTag::APPKIT, "failed to changeMode %{public}s", path.c_str());
369             return;
370         }
371         if (OHOS::StorageDaemon::AclSetAccess(path, "g:1201:rwx") != 0) {
372             TAG_LOGE(AAFwkTag::APPKIT, "failed to AclSetAccess, path: %{public}s", path.c_str());
373             return;
374         }
375         TAG_LOGI(AAFwkTag::APPKIT, "success to AclSetAccess, path: %{public}s", path.c_str());
376         }, {}, {}, {ffrt::task_attr().name("ffrt_dfr_CreateDir")});
377 }
378 
Init2DOOMDumpQuota(const std::string & path,uint32_t oomDumpProcessMaxQuota)379 bool DumpRuntimeHelper::Init2DOOMDumpQuota(const std::string &path, uint32_t oomDumpProcessMaxQuota)
380 {
381     uint64_t currentTime = GetCurrentTimestamp();
382     if (!Set2DQuota(path, currentTime, oomDumpProcessMaxQuota)) {
383         TAG_LOGE(AAFwkTag::APPKIT, "Set2DQuota failed, current: %{public}" PRIu64 ", quota: %{public}d",
384             currentTime, oomDumpProcessMaxQuota);
385         return false;
386     }
387     TAG_LOGI(AAFwkTag::APPKIT, "Init dumpTime: %{public}" PRIu64 ", quota: %{public}d.",
388         currentTime, oomDumpProcessMaxQuota);
389     return true;
390 }
391 
CreateDir(const std::string & path)392 bool DumpRuntimeHelper::CreateDir(const std::string &path)
393 {
394     if (IsFileExists(path)) {
395         TAG_LOGI(AAFwkTag::APPKIT, "File existed. dir: %{public}s", path.c_str());
396         return true;
397     }
398     if (!ForceCreateDirectory(path)) {
399         TAG_LOGE(AAFwkTag::APPKIT, "Failed to create dir: %{public}s", path.c_str());
400         return false;
401     }
402     return true;
403 }
404 
Check2DOOMDumpQuota(uint32_t oomDumpMaxQuota,uint32_t oomDumpProcessMaxQuota)405 bool DumpRuntimeHelper::Check2DOOMDumpQuota(uint32_t oomDumpMaxQuota, uint32_t oomDumpProcessMaxQuota)
406 {
407     if (!CheckAppListenedEvents(HIAPPEVENT_PATH)) {
408         TAG_LOGI(AAFwkTag::APPKIT, "Not subscribe oom event.");
409         return false;
410     }
411     if (oomDumpMaxQuota <= 0) {
412         TAG_LOGI(AAFwkTag::APPKIT, "The whole machine quota has been exhausted, quota=%{public}u", oomDumpMaxQuota);
413         return false;
414     }
415     if (!CheckOOMFreeSpace(OOM_DUMP_SPACE_LIMIT)) {
416         TAG_LOGI(AAFwkTag::APPKIT, "Device space is not enough.");
417         return false;
418     }
419     if (!Get2DQuota(OOM_QUOTA_PATH, OOM_QUOTA_XATTR_NAME, g_lastOOMDumpTime, g_oomDumpProcessQuota)) {
420         TAG_LOGE(AAFwkTag::APPKIT, "Check2DOOMDumpQuota failed to get quota.");
421         return false;
422     }
423     uint64_t currentTime = GetCurrentTimestamp();
424     if (currentTime - g_lastOOMDumpTime <= OOM_DUMP_INTERVAL) {
425         if (g_oomDumpProcessQuota <= 0) {
426             TAG_LOGI(AAFwkTag::APPKIT, "Weekly quota has been exhausted, quota=%{public}d", g_oomDumpProcessQuota);
427             return false;
428         }
429     } else {
430         TAG_LOGI(AAFwkTag::APPKIT, "Over one week, reset process quota=%{public}u", oomDumpProcessMaxQuota);
431         g_oomDumpProcessQuota = static_cast<int>(oomDumpProcessMaxQuota);
432         if (!Set2DQuota(OOM_QUOTA_PATH, currentTime, oomDumpProcessMaxQuota)) {
433             TAG_LOGE(AAFwkTag::APPKIT, "Failed to reset quota.");
434             return false;
435         }
436     }
437     TAG_LOGI(AAFwkTag::APPKIT, "Last oom time=%{public}" PRIu64 ", currentTime=%{public}" PRIu64 ", quota=%{public}d",
438         g_lastOOMDumpTime, currentTime, g_oomDumpProcessQuota);
439     return true;
440 }
441 
GetQuota(const std::string & path,const std::string & property,std::vector<int64_t> & output,size_t size)442 bool DumpRuntimeHelper::GetQuota(const std::string &path, const std::string &property,
443                                  std::vector<int64_t> &output, size_t size)
444 {
445     std::string value;
446     if (!GetDirXattr(path, property, value)) {
447         TAG_LOGE(AAFwkTag::APPKIT, "Failed to get xattr. preperty: %{public}s", property.c_str());
448         return false;
449     }
450     size_t commaCount = static_cast<size_t>(std::count(value.begin(), value.end(), ','));
451     if (commaCount != size - 1) {
452         TAG_LOGE(AAFwkTag::APPKIT, "Invalid number of commas: %{public}zu", commaCount);
453         return false;
454     }
455 
456     std::vector<int64_t> rtn;
457     std::stringstream ss(value);
458     std::string segment;
459     while (std::getline(ss, segment, ',')) {
460         if (segment.empty()) {
461             TAG_LOGE(AAFwkTag::APPKIT, "Invalid segment: empty value found, index: %{public}zu", rtn.size());
462             return false;
463         }
464         long long tmp;
465         if (!SafeStoll(segment, tmp)) {
466             TAG_LOGE(AAFwkTag::APPKIT, "failed to SafeStoll, segment: %{public}s", segment.c_str());
467             return false;
468         }
469         rtn.push_back(static_cast<int64_t>(tmp));
470     }
471 
472     if (rtn.size() != size) {
473         TAG_LOGE(AAFwkTag::APPKIT, "Invalid number of entries: %{public}zu", rtn.size());
474         return false;
475     }
476 
477     output = rtn;
478     return true;
479 }
480 
Get2DQuota(const std::string & path,const std::string & property,uint64_t & timestamp,int & quota)481 bool DumpRuntimeHelper::Get2DQuota(const std::string &path, const std::string &property,
482                                    uint64_t &timestamp, int &quota)
483 {
484     std::string value;
485     if (!GetDirXattr(path, property, value)) {
486         TAG_LOGE(AAFwkTag::APPKIT, "Failed to get xattr. path: %{public}s, preperty: %{public}s",
487             path.c_str(), property.c_str());
488         return false;
489     }
490     std::istringstream ss(value);
491     char delimiter;
492     if (!(ss >> timestamp >> delimiter >> quota) || delimiter != ',') {
493         TAG_LOGE(AAFwkTag::APPKIT, "Failed to decode value: %{public}s", value.c_str());
494         return false;
495     }
496     return true;
497 }
498 
Set2DQuota(const std::string & path,uint64_t timestamp,int newQuota)499 bool DumpRuntimeHelper::Set2DQuota(const std::string &path, uint64_t timestamp, int newQuota)
500 {
501     std::ostringstream value;
502     value << timestamp << "," << newQuota;
503     if (newQuota < 0) {
504         TAG_LOGE(AAFwkTag::APPKIT, "Invalid quota.");
505         return false;
506     }
507     TAG_LOGI(AAFwkTag::APPKIT, "Set quota=%{public}d", newQuota);
508     return SetDirXattr(path, OOM_QUOTA_XATTR_NAME, value.str());
509 }
510 
GetCurrentTimestamp()511 uint64_t DumpRuntimeHelper::GetCurrentTimestamp()
512 {
513     return static_cast<uint64_t>(time(nullptr));
514 }
515 
CheckOOMFreeSpace(uint64_t maxSpace)516 bool DumpRuntimeHelper::CheckOOMFreeSpace(uint64_t maxSpace)
517 {
518     struct statvfs st;
519     if (statvfs("/data/storage/el2/base/", &st) != 0) {
520         return false;
521     }
522 
523     unsigned long freeSize = st.f_bsize * st.f_bfree;
524     if (freeSize <= maxSpace) {
525         return false;
526     }
527     return true;
528 }
529 
GetMaskFromDirXattr(const std::string & path)530 uint64_t DumpRuntimeHelper::GetMaskFromDirXattr(const std::string &path)
531 {
532     std::string value;
533     if (!GetDirXattr(path, EVENT_XATTR_NAME, value)) {
534         TAG_LOGE(AAFwkTag::APPKIT, "Failed to get subscirbe xattr.");
535         return 0;
536     }
537     return static_cast<uint64_t>(std::strtoull(value.c_str(), nullptr, 0));
538 }
539 
CheckAppListenedEvents(const std::string & path)540 bool DumpRuntimeHelper::CheckAppListenedEvents(const std::string &path)
541 {
542     uint64_t eventsMask = GetMaskFromDirXattr(path);
543     if (eventsMask == 0) {
544         TAG_LOGE(AAFwkTag::APPKIT, "Failed to get events mask for path:%{public}s", path.c_str());
545         return false;
546     }
547     if (!(eventsMask & (BIT_MASK << EVENT_RESOURCE_OVERLIMIT_MASK))) {
548         TAG_LOGI(AAFwkTag::APPKIT, "Unlistened event for path:%{public}s", path.c_str());
549         return false;
550     }
551     return true;
552 }
553 
IsFileExists(const std::string & file)554 bool DumpRuntimeHelper::IsFileExists(const std::string &file)
555 {
556     return access(file.c_str(), F_OK) == 0;
557 }
558 
ForceCreateDirectory(const std::string & path)559 bool DumpRuntimeHelper::ForceCreateDirectory(const std::string &path)
560 {
561     std::string::size_type index = 0;
562     do {
563         std::string subPath;
564         index = path.find('/', index + 1); // (index + 1) means the next char traversed
565         if (index == std::string::npos) {
566             subPath = path;
567         } else {
568             subPath = path.substr(0, index);
569         }
570 
571         if (!IsFileExists(subPath) && mkdir(subPath.c_str(), S_IRWXU) != 0) {
572             return false;
573         }
574     } while (index != std::string::npos);
575     return IsFileExists(path);
576 }
577 
SetDirXattr(const std::string & path,const std::string & name,const std::string & value)578 bool DumpRuntimeHelper::SetDirXattr(const std::string &path, const std::string &name, const std::string &value)
579 {
580     return setxattr(path.c_str(), name.c_str(), value.c_str(), strlen(value.c_str()), 0) == 0;
581 }
582 
GetDirXattr(const std::string & path,const std::string & name,std::string & value)583 bool DumpRuntimeHelper::GetDirXattr(const std::string &path, const std::string &name, std::string &value)
584 {
585     char buf[BUF_SIZE_256] = {0};
586     if (getxattr(path.c_str(), name.c_str(), buf, sizeof(buf) - 1) == -1) {
587         TAG_LOGE(AAFwkTag::APPKIT, "failed getxattr, name: %{public}s, err: %{public}d:%{public}s",
588                  name.c_str(), errno, strerror(errno));
589         return false;
590     }
591     value = buf;
592     return true;
593 }
594 
SafeStoll(const std::string & str,long long & value)595 bool DumpRuntimeHelper::SafeStoll(const std::string &str, long long &value)
596 {
597     value = 0;
598     size_t start = 0;
599     bool isNegative = false;
600 
601     if (str.empty()) {
602         TAG_LOGE(AAFwkTag::APPKIT, "str is empty");
603         return false;
604     }
605 
606     if (str[0] == '-') {
607         isNegative = true;
608         start = 1;
609     } else if (str[0] == '+') {
610         start = 1;
611     }
612 
613     for (size_t i = start; i < str.size(); ++i) {
614         if (!isdigit(str[i])) {
615             TAG_LOGE(AAFwkTag::APPKIT, "digit check failed. str: %{public}s, index: %{public}zu", str.c_str(), i);
616             return false;
617         }
618         if (value > (LLONG_MAX - (str[i] - '0')) / DECIMAL_BASE) {
619             TAG_LOGE(AAFwkTag::APPKIT, "out of range, str: %{public}s", str.c_str());
620             return false;
621         }
622         value = value * DECIMAL_BASE + (str[i] - '0');
623     }
624 
625     if (isNegative) {
626         value = -value;
627     }
628 
629     TAG_LOGD(AAFwkTag::APPKIT, "success, str: %{public}s, result: %{public}lld", str.c_str(), value);
630     return true;
631 }
632 
GetCompressQuota(const std::vector<int64_t> & quotas)633 int DumpRuntimeHelper::GetCompressQuota(const std::vector<int64_t> &quotas)
634 {
635     int ret = 0;
636     uint64_t now = static_cast<uint64_t>(time(nullptr));
637     for (int i = INDEX_COMPRESSED_TS_0; i <= INDEX_COMPRESSED_TS_4; i++) {
638         uint64_t compressTs = static_cast<uint64_t>(quotas[i]);
639         if (now > compressTs && now - compressTs > OOM_DUMP_INTERVAL) {
640             ret++;
641         }
642     }
643 
644     return ret;
645 }
646 
SplitPropertyByComma(const std::string & property,std::string & runningId,std::string & value)647 bool DumpRuntimeHelper::SplitPropertyByComma(const std::string &property, std::string &runningId, std::string &value)
648 {
649     size_t commaPos = property.find(',');
650     if (commaPos == std::string::npos) {
651         TAG_LOGE(AAFwkTag::APPKIT, "no comma in property");
652         return false;
653     }
654 
655     runningId = property.substr(0, commaPos);
656     value = property.substr(commaPos + 1);
657     return true;
658 }
659 
GetEventConfig(const std::string & key)660 std::string DumpRuntimeHelper::GetEventConfig(const std::string &key)
661 {
662     std::string property;
663     if (!GetDirXattr(OOM_QUOTA_PATH, key, property)) {
664         TAG_LOGE(AAFwkTag::APPKIT, "failed to GetDirXattr, key: %{public}s", key.c_str());
665         return "";
666     }
667     std::string customRunningId;
668     std::string value;
669     if (!SplitPropertyByComma(property, customRunningId, value)) {
670         TAG_LOGW(AAFwkTag::APPKIT, "failed to SplitPropertyByComma, property: %{public}s", property.c_str());
671         return "";
672     }
673     std::string curRunningId = DFX_GetAppRunningUniqueId();
674     if (curRunningId.empty()) {
675         TAG_LOGE(AAFwkTag::APPKIT, "curRunningId is empty");
676         return "";
677     }
678     if (customRunningId != curRunningId) {
679         TAG_LOGW(AAFwkTag::APPKIT, "runningId not match, customRunningId: %{public}s, curRunningId: %{public}s",
680             customRunningId.c_str(), curRunningId.c_str());
681         return "";
682     }
683 
684     TAG_LOGI(AAFwkTag::APPKIT, "succeed to getEventConfig, value: %{public}s", value.c_str());
685     return value;
686 }
687 } // namespace AppExecFwk
688 } // namespace OHOS
689