* Copyright (c) 2022-2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "watchdog.h"
#include <charconv>
#include <parameter.h>
#include <unistd.h>
#include "app_mgr_client.h"
#include "app_recovery.h"
#include "appfreeze_inner.h"
#include "hisysevent.h"
#include "hilog_tag_wrapper.h"
#include "xcollie/watchdog.h"
#include "parameters.h"
namespace OHOS {
namespace AppExecFwk {
namespace {
constexpr uint32_t CHECK_MAIN_THREAD_IS_ALIVE = 1;
constexpr int RESET_RATIO = 2;
#ifdef ABILITY_RUNTIME_HITRACE_ENABLE
constexpr int32_t CHARACTER_WIDTH = 2;
#endif
constexpr int32_t BACKGROUND_REPORT_COUNT_MAX = 5;
constexpr int32_t WATCHDOG_REPORT_COUNT_MAX = 5;
#ifdef SUPPORT_ASAN
constexpr uint32_t CHECK_INTERVAL_TIME = 45000;
#else
constexpr uint32_t CHECK_INTERVAL_TIME = 3000;
#endif
static uint32_t checkIntervalTime_ = 3000;
constexpr float FLOAT_EPSILON = 0.01f;
constexpr int32_t TIME_CONVERT_RATIO = 1000;
constexpr int32_t MAX_RATIO_SIZE = 4;
#ifdef APP_NO_RESPONSE_DIALOG_WEARABLE
constexpr uint32_t WEARABLE_CHECK_INTERVAL_TIME = 5000;
#endif
constexpr uint32_t BACKGROUND_INTERVAL_TIME = 20000;
}
std::shared_ptr<EventHandler> Watchdog::appMainHandler_ = nullptr;
#ifdef ABILITY_RUNTIME_HITRACE_ENABLE
OHOS::HiviewDFX::HiTraceId* Watchdog::hitraceId_ = nullptr;
#endif
static constexpr const char* const CHECK_BACKGROUND_THREAD[] = {
"com.ohos.sceneboard"
};
Watchdog::Watchdog()
{
checkIntervalTime_ = CHECK_INTERVAL_TIME * getRatioValue();
}
Watchdog::~Watchdog()
{
if (!stopWatchdog_) {
TAG_LOGD(AAFwkTag::APPDFR, "Stop watchdog");
OHOS::HiviewDFX::Watchdog::GetInstance().StopWatchdog();
}
}
float Watchdog::getRatioValue()
{
float defaultRatio = 1.0f;
std::string ratioStr = OHOS::system::GetParameter("const.sys.dfx.appfreeze.timeout_unit_time_ratio", "1000");
if (ratioStr.empty() || !IsNumeric(ratioStr)) {
return defaultRatio;
}
if (ratioStr.size() > MAX_RATIO_SIZE) {
return defaultRatio;
}
uint64_t ratioVal = 0;
auto [ptr, ec] = std::from_chars(ratioStr.data(), ratioStr.data() + ratioStr.size(), ratioVal);
if (ec != std::errc()) {
return defaultRatio;
}
float result = (ratioVal * 1.0) / TIME_CONVERT_RATIO;
return (result < FLOAT_EPSILON) ? defaultRatio : result;
}
bool Watchdog::IsNumeric(const std::string &str)
{
return !str.empty() && std::all_of(str.begin(), str.end(), ::isdigit);
}
void Watchdog::Init(const std::shared_ptr<EventHandler> mainHandler)
{
std::unique_lock<std::mutex> lock(cvMutex_);
Watchdog::appMainHandler_ = mainHandler;
if (appMainHandler_ != nullptr) {
TAG_LOGD(AAFwkTag::APPDFR, "Watchdog init send event");
appMainHandler_->SendEvent(CHECK_MAIN_THREAD_IS_ALIVE, 0, EventQueue::Priority::VIP);
}
lastWatchTime_ = 0;
#ifdef ABILITY_RUNTIME_HITRACE_ENABLE
hitraceId_ = OHOS::HiviewDFX::HiTraceChain::GetIdAddress();
#endif
auto watchdogTask = [this] { this->Timer(); };
#ifdef APP_NO_RESPONSE_DIALOG_WEARABLE
OHOS::HiviewDFX::Watchdog::GetInstance().RunPeriodicalTask("AppkitWatchdog", watchdogTask,
WEARABLE_CHECK_INTERVAL_TIME, INI_TIMER_FIRST_SECOND);
#else
OHOS::HiviewDFX::Watchdog::GetInstance().RunOneShotTask("AppkitOneShotWatchdogTask", watchdogTask,
INI_ONE_SHOE_TASK_FIRST_SECOND);
OHOS::HiviewDFX::Watchdog::GetInstance().RunPeriodicalTask("AppkitWatchdog", watchdogTask,
checkIntervalTime_, INI_TIMER_FIRST_SECOND);
#endif
SetMainThreadSample();
SetReportLifeCycleAsAppfreeze();
}
void Watchdog::SetReportLifeCycleAsAppfreeze()
{
char* env = getenv("DFX_APPFREEZE_LOG_OPTIONS");
if (env == nullptr) {
return;
}
AppExecFwk::AppfreezeInner::GetInstance()->SetReportLifeCycleAsAppfreeze(
strstr(env, "report_lifecycle_as_appfreeze:enable") != nullptr);
}
void Watchdog::SetMainThreadSample()
{
char* env = getenv("DFX_APPFREEZE_LOG_OPTIONS");
if (env == nullptr) {
return;
}
AppExecFwk::AppfreezeInner::GetInstance()->SetMainThreadSample(
strstr(env, "mainthread_sampling:enable") != nullptr);
}
void Watchdog::Stop()
{
TAG_LOGD(AAFwkTag::APPDFR, "called");
std::unique_lock<std::mutex> lock(cvMutex_);
if (stopWatchdog_) {
TAG_LOGD(AAFwkTag::APPDFR, "stoped");
return;
}
stopWatchdog_.store(true);
cvWatchdog_.notify_all();
OHOS::HiviewDFX::Watchdog::GetInstance().StopWatchdog();
if (appMainHandler_) {
appMainHandler_.reset();
appMainHandler_ = nullptr;
}
}
void Watchdog::SetAppMainThreadState(const bool appMainThreadState)
{
std::unique_lock<std::mutex> lock(cvMutex_);
appMainThreadIsAlive_.store(appMainThreadState);
}
#ifdef ABILITY_RUNTIME_HITRACE_ENABLE
void Watchdog::SetHiTraceChainId()
{
OHOS::HiviewDFX::HiTraceId hitraceId = *hitraceId_;
if (hitraceId.IsValid() == 0) {
TAG_LOGW(AAFwkTag::APPDFR, "get hitrace id is invalid.");
return;
}
OHOS::HiviewDFX::HiTraceChain::SetId(hitraceId);
std::stringstream ss;
ss << std::hex << std::setfill('0') << std::setw(CHARACTER_WIDTH) << hitraceId.GetChainId();
TAG_LOGI(AAFwkTag::APPDFR, "Main thread blocked, %{public}s", ss.str().c_str());
}
#endif
#ifdef APP_NO_RESPONSE_DIALOG
bool isDeviceType2in1()
{
const int bufferLen = 128;
char paramOutBuf[bufferLen] = {0};
const char *devicetype2in1 = "2in1";
int ret = GetParameter("const.product.devicetype", "", paramOutBuf, bufferLen);
return ret > 0 && strncmp(paramOutBuf, devicetype2in1, strlen(devicetype2in1)) == 0;
}
#endif
#ifdef APP_NO_RESPONSE_DIALOG
void Watchdog::ChangeTimeOut(const std::string& bundleName)
{
constexpr char SCENEBOARD_SERVICE_ABILITY[] = "com.ohos.sceneboard";
constexpr int TIMEOUT = 5000;
if (bundleName == SCENEBOARD_SERVICE_ABILITY) {
OHOS::HiviewDFX::Watchdog::GetInstance().RemovePeriodicalTask("AppkitWatchdog");
auto watchdogTask = [this] { this->Timer(); };
OHOS::HiviewDFX::Watchdog::GetInstance().RunPeriodicalTask("AppkitWatchdog", watchdogTask, TIMEOUT,
INI_TIMER_FIRST_SECOND);
}
}
#endif
void Watchdog::SetBundleInfo(const std::string& bundleName, const std::string& bundleVersion, bool isSystemApp)
{
OHOS::HiviewDFX::Watchdog::GetInstance().SetSystemApp(isSystemApp);
OHOS::HiviewDFX::Watchdog::GetInstance().SetBundleInfo(bundleName, bundleVersion);
#ifdef APP_NO_RESPONSE_DIALOG
if (isDeviceType2in1()) {
ChangeTimeOut(bundleName);
}
#endif
{
std::unique_lock<std::mutex> lock(cvMutex_);
bundleName_ = bundleName;
}
}
void Watchdog::SetBackgroundStatus(const bool isInBackground)
{
std::unique_lock<std::mutex> lock(cvMutex_);
isInBackground_.store(isInBackground);
OHOS::HiviewDFX::Watchdog::GetInstance().SetForeground(!isInBackground);
AppExecFwk::AppfreezeInner::GetInstance()->SetAppInForeground(!isInBackground);
}
void Watchdog::AllowReportEvent()
{
std::unique_lock<std::mutex> lock(cvMutex_);
needReport_.store(true);
isSixSecondEvent_.store(false);
backgroundReportCount_.store(0);
watchdogReportCount_.store(0);
}
bool Watchdog::IsReportEvent()
{
if (appMainThreadIsAlive_) {
appMainThreadIsAlive_.store(false);
return false;
}
TAG_LOGD(AAFwkTag::APPDFR, "AppMainThread not alive");
return true;
}
bool Watchdog::IsStopWatchdog()
{
std::unique_lock<std::mutex> lock(cvMutex_);
return stopWatchdog_;
}
void Watchdog::SetBgWorkingThreadStatus(const bool isBgWorkingThread)
{
std::unique_lock<std::mutex> lock(cvMutex_);
isBgWorkingThread_.store(isBgWorkingThread);
}
void Watchdog::Timer()
{
std::unique_lock<std::mutex> lock(cvMutex_);
if (stopWatchdog_) {
TAG_LOGD(AAFwkTag::APPDFR, "stoped");
return;
}
if (!needReport_) {
watchdogReportCount_++;
TAG_LOGE(AAFwkTag::APPDFR, "wait count: %{public}d", watchdogReportCount_.load());
if (watchdogReportCount_.load() >= WATCHDOG_REPORT_COUNT_MAX) {
#ifdef ABILITY_RUNTIME_HITRACE_ENABLE
SetHiTraceChainId();
#endif
#ifndef APP_NO_RESPONSE_DIALOG
AppExecFwk::AppfreezeInner::GetInstance()->AppfreezeHandleOverReportCount(true);
#endif
watchdogReportCount_.store(0);
} else if (watchdogReportCount_.load() >= (WATCHDOG_REPORT_COUNT_MAX - 1)) {
#ifndef APP_NO_RESPONSE_DIALOG
AppExecFwk::AppfreezeInner::GetInstance()->AppfreezeHandleOverReportCount(false);
#endif
}
return;
}
if (IsReportEvent()) {
const int bufferLen = 128;
char paramOutBuf[bufferLen] = {0};
const char *hook_mode = "startup:";
int ret = GetParameter("libc.hook_mode", "", paramOutBuf, bufferLen);
if (ret <= 0 || strncmp(paramOutBuf, hook_mode, strlen(hook_mode)) != 0) {
ReportEvent();
}
}
if (appMainHandler_ != nullptr) {
appMainHandler_->SendEvent(CHECK_MAIN_THREAD_IS_ALIVE, 0, EventQueue::Priority::VIP);
}
int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
system_clock::now().time_since_epoch()).count();
#ifdef APP_NO_RESPONSE_DIALOG_WEARABLE
if ((now - lastWatchTime_) < 0 || (now - lastWatchTime_) >= (WEARABLE_CHECK_INTERVAL_TIME / RESET_RATIO)) {
#else
if ((now - lastWatchTime_) < 0 || (now - lastWatchTime_) >= (checkIntervalTime_ / RESET_RATIO)) {
#endif
lastWatchTime_ = now;
}
}
bool Watchdog::CheckReportEvent(int64_t now)
{
if (isInBackground_ && backgroundReportCount_.load() < BACKGROUND_REPORT_COUNT_MAX) {
bool enableCheck = std::find(std::begin(CHECK_BACKGROUND_THREAD), std::end(CHECK_BACKGROUND_THREAD),
bundleName_) != std::end(CHECK_BACKGROUND_THREAD);
if ((now - lastBackGroundWatchTime_) < 0 || (now - lastBackGroundWatchTime_) >= (BACKGROUND_INTERVAL_TIME)) {
TAG_LOGW(AAFwkTag::APPDFR, "In Background, thread may be blocked in, not report time"
"currTime: %{public}" PRIu64 ", lastTime: %{public}" PRIu64 ", enableCheck: %{public}d",
static_cast<uint64_t>(now), static_cast<uint64_t>(lastWatchTime_), enableCheck);
lastBackGroundWatchTime_ = now;
}
if (enableCheck) {
backgroundReportCount_++;
return false;
}
if (halfBckGroundCount_ > 0) {
return false;
}
halfBckGroundCount_++;
}
return true;
}
void Watchdog::ReportEvent()
{
if (isBgWorkingThread_) {
TAG_LOGD(AAFwkTag::APPDFR, "Thread is working in the background, do not report this time");
return;
}
int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
system_clock::now().time_since_epoch()).count();
#ifdef APP_NO_RESPONSE_DIALOG_WEARABLE
if ((now - lastWatchTime_) > (RESET_RATIO * WEARABLE_CHECK_INTERVAL_TIME) ||
(now - lastWatchTime_) < (WEARABLE_CHECK_INTERVAL_TIME / RESET_RATIO)) {
#else
if ((now - lastWatchTime_) > (RESET_RATIO * checkIntervalTime_) ||
(now - lastWatchTime_) < (checkIntervalTime_ / RESET_RATIO)) {
#endif
TAG_LOGI(AAFwkTag::APPDFR,
"Thread may be blocked, not report time. currTime: %{public}llu, lastTime: %{public}llu",
static_cast<unsigned long long>(now), static_cast<unsigned long long>(lastWatchTime_));
return;
}
if (!CheckReportEvent(now)) {
return;
}
backgroundReportCount_++;
if (!needReport_) {
return;
}
#ifndef APP_NO_RESPONSE_DIALOG
if (isSixSecondEvent_) {
needReport_.store(false);
}
#endif
#ifdef ABILITY_RUNTIME_HITRACE_ENABLE
if (isSixSecondEvent_) {
SetHiTraceChainId();
}
#endif
AppExecFwk::AppfreezeInner::GetInstance()->ThreadBlock(isSixSecondEvent_, 0, now, isInBackground_);
}
}
}