PKG_CHECK_MODULES(TIZEN_CORE_DEPS REQUIRED tizen-core)
PKG_CHECK_MODULES(TIZEN_LIBOPENER_DEPS REQUIRED tizen-libopener)
PKG_CHECK_MODULES(INIPARSER_DEPS REQUIRED iniparser)
+PKG_CHECK_MODULES(AUL_DEPS REQUIRED aul)
ADD_SUBDIRECTORY(src)
BuildRequires: pkgconfig(tizen-libopener)
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(iniparser)
+BuildRequires: pkgconfig(aul)
Requires(post): /sbin/ldconfig
Requires(postun): /sbin/ldconfig
AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} SRCS)
+AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/watchdog WATCHDOG_SRCS)
-ADD_LIBRARY(${TARGET_UNITED_SERVICE} SHARED ${SRCS})
+ADD_LIBRARY(${TARGET_UNITED_SERVICE} SHARED ${SRCS} ${WATCHDOG_SRCS})
SET_TARGET_PROPERTIES(${TARGET_UNITED_SERVICE} PROPERTIES SOVERSION ${MAJORVER})
SET_TARGET_PROPERTIES(${TARGET_UNITED_SERVICE} PROPERTIES VERSION ${FULLVER})
TIZEN_CORE_DEPS
TIZEN_LIBOPENER_DEPS
INIPARSER_DEPS
+ AUL_DEPS
)
TARGET_LINK_LIBRARIES(${TARGET_UNITED_SERVICE} PUBLIC "-lpthread -ldl")
#include "exception.hh"
#include "log_private.hh"
#include "service_manager.hh"
+#include "watchdog/watchdog_manager.hh"
namespace tizen_base {
namespace {
const std::string& Service::GetName() const { return info_->GetName(); }
+pid_t Service::GetTid() const { return tid_; }
+
Service::State Service::GetState() const { return state_; }
tizen_core_h Service::GetCore() const { return core_; }
service->tid_ = gettid();
service->OnBaseCreate();
service->state_ = Service::State::Running;
+
service->NotifyStateChanged();
+ WatchdogManager::GetInst().RegisterService(service->GetTid(),
+ service->GetName(), service->GetCore());
+
return false;
}, this, &source);
}
void Service::QuitSelf() {
+ WatchdogManager::GetInst().UnregisterService(tid_);
auto self = shared_from_this();
tizen_core_task_quit(task_);
OnBaseDestroy();
bool IsRunning() const;
const std::shared_ptr<ServiceInfo>& GetServiceInfo() const;
const std::string& GetName() const;
+ pid_t GetTid() const;
State GetState() const;
tizen_core_h GetCore() const;
tizen_core_channel_sender_h GetChannelSender() const;
constexpr const char kPathUnitedService[] = "/usr/share/united-service/";
constexpr const char kConf[] = "/conf/";
+static const std::string kTagServiceLoader = "service-loader";
+static const std::string kWatchdogSec = kTagServiceLoader + ":watchdogsec";
+static const std::string kCpuTimeSec = kTagServiceLoader + ":cputimesec";
+static const std::string kMemoryMonitor = kTagServiceLoader + ":memorymonitor";
+static const std::string kCpuMonitor = kTagServiceLoader + ":cpumonitor";
+static const std::string kBacktrace = kTagServiceLoader + ":backtrace";
+
} // namespace
namespace tizen_base {
Shutdown();
THROW(SERVICE_ERROR_INVALID_CONTEXT);
}
+
+ if (!WatchdogInit()) {
+ Shutdown();
+ throw new std::runtime_error("Failed to initialize watchdog manager");
+ }
}
ServiceLoader::~ServiceLoader() {
return true;
}
+bool ServiceLoader::WatchdogInit() {
+ const fs::path loader_conf(kPathUnitedService + name_ + "/" +
+ name_ + ".loader");
+ if (!fs::is_regular_file(loader_conf)) [[unlikely]] {
+ LOGE("Cannot find config file (%s)", loader_conf.c_str());
+ return false;
+ }
+
+ auto dictionary = IniParser::Parse(loader_conf.string());
+ auto builder = WatchdogConf::Builder();
+
+ builder.SetLoaderName(name_);
+ builder.SetWatchdogSec(std::stoi(dictionary->Get(kWatchdogSec)));
+ builder.SetMemoryMonitorFlag(dictionary->Get(kMemoryMonitor) == "yes");
+ builder.SetCpuMonitorFlag(dictionary->Get(kCpuMonitor) == "yes");
+ builder.SetBacktraceFlag(dictionary->Get(kBacktrace) == "yes");
+
+ auto config = builder.Build();
+ if (!config) [[unlikely]]
+ return false;
+
+ if (!WatchdogManager::GetInst().Init(std::move(config))) {
+ return false;
+ }
+
+ return true;
+}
+
void ServiceLoader::Shutdown() {
if (source_) {
tizen_core_source_destroy(source_);
#include "service.hh"
#include "service_info.hh"
+#include "watchdog/watchdog_manager.hh"
+#include "watchdog/watchdog_conf.hh"
+#include "watchdog/heartbeat.hh"
+
namespace tizen_base {
class ServiceLoader {
void ServiceStateChangedCb(const Service* service, Service::State state);
bool Init();
+ bool WatchdogInit();
void Shutdown();
void LoadServices();
static void ChannelReceiveCb(tizen_core_channel_object_h object,
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "cpu_time.hh"
+
+namespace tizen_base {
+
+pid_t CpuTime::GetTid() const {
+ return tid_;
+}
+
+bool CpuTime::Update() {
+ LOGD("CPUtime Update [TID %d]", tid_);
+ bool ret = UpdateTotalCpuTime();
+ if (ret)
+ return UpdateProcessCpuTime();
+ return false;
+}
+
+bool CpuTime::UpdateTotalCpuTime() {
+ FILE* fp = fopen("/proc/stat", "r");
+ if (fp == nullptr) {
+ _E("Failed to open /proc/stat");
+ return false;
+ }
+
+ unsigned long long user = 0;
+ unsigned long long nice = 0;
+ unsigned long long system = 0;
+ unsigned long long idle_time = 0;
+ unsigned long long iowait = 0;
+ unsigned long long irq = 0;
+ unsigned long long softirq = 0;
+ unsigned long long steal = 0;
+ int ret =
+ fscanf(fp, "cpu %llu %llu %llu %llu %llu %llu %llu %llu", &user, &nice,
+ &system, &idle_time, &iowait, &irq, &softirq, &steal);
+ fclose(fp);
+ if (ret != 8) {
+ _E("Failed to scan /proc/stat: %d", ret);
+ return false;
+ }
+
+ previous_total_time_ = current_total_time_;
+ current_total_time_ =
+ user + nice + system + idle_time + iowait + irq + softirq + steal;
+ return true;
+}
+
+bool CpuTime::UpdateProcessCpuTime() {
+ std::string path = "/proc/" + std::to_string(getpid()) + "/task/"
+ + std::to_string(tid_) + "/stat";
+
+ FILE* fp = fopen(path.c_str(), "r");
+ if (fp == nullptr) {
+ _E("Failed to open %s", path.c_str());
+ return false;
+ }
+
+ unsigned long long utime = 0;
+ unsigned long long stime = 0;
+ int ret = fscanf(
+ fp, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %llu %llu",
+ &utime, &stime);
+ fclose(fp);
+ if (ret != 2) {
+ _E("Failed to scan %s: %d", path.c_str(), ret);
+ return false;
+ }
+
+ previous_process_time_ = current_process_time_;
+ current_process_time_ = utime + stime;
+ return true;
+}
+
+
+double CpuTime::CalculateCpuUsage() {
+ auto total_diff = current_total_time_ - previous_total_time_;
+ if (total_diff == 0) return 0.0f;
+ auto process_diff = current_process_time_ - previous_process_time_;
+ return (static_cast<double>(process_diff) / total_diff) * 100.0;
+}
+
+} // namespace tizen_base
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef CPU_TIME_HH_
+#define CPU_TIME_HH_
+
+#include <tizen_core.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "log_private.hh"
+
+namespace tizen_base {
+
+class CpuTime {
+ public:
+ CpuTime(pid_t tid) : tid_(tid) {}
+ pid_t GetTid() const;
+ bool Update();
+ double CalculateCpuUsage();
+
+ private:
+ bool UpdateTotalCpuTime();
+ bool UpdateProcessCpuTime();
+ pid_t tid_;
+
+ unsigned long long current_total_time_ = 0;
+ unsigned long long current_process_time_ = 0;
+ unsigned long long previous_total_time_ = 0;
+ unsigned long long previous_process_time_ = 0;
+};
+
+} // namespace tizen_base
+
+#endif // CPU_TIME_HH_
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "heartbeat.hh"
+
+namespace tizen_base {
+
+pid_t Heartbeat::GetTid() const {
+ return tid_;
+}
+
+void Heartbeat::SendPing() {
+ tizen_core_source_h source = nullptr;
+ tizen_core_add_idle_job(
+ core_, [](void* user_data) -> bool {
+ auto* heartbeat = static_cast<Heartbeat*>(user_data);
+ heartbeat->SetPingResult();
+ return false;
+ }, this, &source);
+ return;
+}
+
+void Heartbeat::SetPingResult() {
+ LOGD("Heartbeat Update [TID %d]", gettid());
+ ping_ = true;
+}
+
+bool Heartbeat::CheckPingResult() {
+ bool result = ping_;
+ ping_ = false;
+ return result;
+}
+
+} // namespace tizen_base
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef HEARTBEAT_HH_
+#define HEARTBEAT_HH_
+
+#include <tizen_core.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "log_private.hh"
+
+namespace tizen_base {
+
+class Heartbeat {
+ public:
+ Heartbeat(pid_t tid, tizen_core_h core) :
+ tid_(tid), core_(core), ping_(true) {};
+ pid_t GetTid() const;
+ void SendPing();
+ void SetPingResult();
+ bool CheckPingResult();
+
+ private:
+ pid_t tid_;
+ tizen_core_h core_;
+ bool ping_;
+};
+
+} // namespace tizen_base
+
+#endif // HEARTBEAT_HH_
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "inspector.hh"
+#include "cpu_time.hh"
+
+#include <filesystem>
+#include <string>
+#include <iostream>
+#include <fstream>
+
+#include <aul_backtrace.h>
+
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+
+// build amd and aul to remove below
+namespace {
+ int aul_send_backtrace_request_mock(int tid) {
+ _I("MOCK: Send BT request to amd");
+ return AUL_R_OK;
+ }
+}
+
+namespace tizen_base::Inspector {
+ void PrintBacktrace(pid_t tid, std::string name) {
+ int ret = aul_send_backtrace_request_mock(tid);
+ if (ret != AUL_R_OK) {
+ _E("Fail to print bt for %d (%d)", tid, ret);
+ }
+ }
+
+ void MemoryMonitor(std::string loader_name) {
+ if (loader_name.empty()) {
+ _E("Invalid parameter");
+ return;
+ }
+
+ std::filesystem::path smaps_path = "/proc/" + std::to_string(getpid()) +
+ "/smaps";
+ if (!std::filesystem::exists(smaps_path)) {
+ _E("%s does not exist", smaps_path.c_str());
+ return;
+ }
+
+ std::ifstream file(smaps_path);
+ if (!file) {
+ _E("Failed to open %s", smaps_path.c_str());
+ return;
+ }
+
+ LOGW("[Loader %d(%s)] Memory monitor", getpid(), loader_name.c_str());
+
+ uint64_t total_pss = 0;
+ std::string line;
+ std::string libinfo;
+ while (std::getline(file, line)) {
+ std::stringstream stream(line);
+ std::string tag;
+
+ if (stream >> tag) {
+ if (!tag.ends_with(":")) {
+ std::string area = std::move(tag);
+ std::string perm;
+ std::string dummy;
+ std::string filename;
+ stream >> perm;
+ stream >> dummy >> dummy >> dummy;
+ stream >> filename;
+
+ libinfo = area + " " + perm + " " + filename;
+
+ } else if (tag == "Pss:") {
+ uint64_t pss = 0;
+ stream >> pss;
+ total_pss += pss;
+ if (pss > 0) {
+ LOGW("(PSS %-4ld kb) %s\n", pss, libinfo.c_str());
+ }
+ }
+ }
+ }
+
+ LOGW("[%d] Total PSS %-4ld kB", getpid(), total_pss);
+ return;
+ }
+
+ void CpuMonitor(std::shared_ptr<CpuTime> cpu_time) {
+ double cpu_usage = cpu_time->CalculateCpuUsage();
+ LOGW("[%d:%d] CPU usage = %.4f%%", getpid(), cpu_time->GetTid(), cpu_usage);
+ return;
+ }
+} // namespace tizen_base
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef INSPECTOR_HH_
+#define INSPECTOR_HH_
+
+#include <tizen_core.h>
+#include "cpu_time.hh"
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "log_private.hh"
+
+namespace tizen_base::Inspector {
+
+ void PrintBacktrace(pid_t tid, std::string name);
+ void MemoryMonitor(std::string loader_name);
+ void CpuMonitor(std::shared_ptr<CpuTime> cpu_time);
+
+} // namespace tizen_base::Inspector
+
+#endif // INSPECTOR_HH_
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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_conf.hh"
+#include "log_private.hh"
+
+namespace tizen_base {
+
+WatchdogConf::Builder& WatchdogConf::Builder::SetLoaderName(std::string name) {
+ loader_name_ = name;
+ return *this;
+}
+
+WatchdogConf::Builder& WatchdogConf::Builder::SetWatchdogSec(uint32_t sec) {
+ watchdog_sec_ = sec;
+ return *this;
+}
+
+WatchdogConf::Builder& WatchdogConf::Builder::SetMemoryMonitorFlag(bool flag) {
+ memory_monitor_ = flag;
+ return *this;
+}
+
+WatchdogConf::Builder& WatchdogConf::Builder::SetCpuMonitorFlag(bool flag) {
+ cpu_monitor_ = flag;
+ return *this;
+}
+
+WatchdogConf::Builder& WatchdogConf::Builder::SetBacktraceFlag(bool flag) {
+ backtrace_ = flag;
+ return *this;
+}
+
+std::shared_ptr<WatchdogConf> WatchdogConf::Builder::Build() {
+ auto conf = std::shared_ptr<WatchdogConf>(
+ new (std::nothrow) WatchdogConf(std::move(loader_name_), watchdog_sec_,
+ memory_monitor_, cpu_monitor_, backtrace_));
+
+ if (conf == nullptr) [[unlikely]] {
+ LOGE("Out of memory");
+ return nullptr;
+ }
+ return conf;
+}
+
+WatchdogConf::WatchdogConf(std::string loader_name, uint32_t watchdog_sec,
+ bool memory_monitor, bool cpu_monitor, bool backtrace)
+ : loader_name_(loader_name), watchdog_sec_(watchdog_sec),
+ memory_monitor_(memory_monitor), cpu_monitor_(cpu_monitor),
+ backtrace_(backtrace) {}
+
+std::string WatchdogConf::GetLoaderName() const {
+ return loader_name_;
+}
+
+uint32_t WatchdogConf::GetWatchdogSec() const {
+ return watchdog_sec_;
+}
+
+bool WatchdogConf::IsMemoryMonitor() const {
+ return memory_monitor_;
+}
+
+bool WatchdogConf::IsCpuMonitor() const {
+ return cpu_monitor_;
+}
+
+bool WatchdogConf::IsBacktrace() const {
+ return cpu_monitor_;
+}
+
+} // namespace tizen_base
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef WATCHDOG_CONF_HH_
+#define WATCHDOG_CONF_HH_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "watchdog_conf.hh"
+
+namespace tizen_base {
+
+class WatchdogConf {
+ public :
+ class Builder {
+ public:
+ Builder(){};
+ Builder& SetLoaderName(std::string name);
+ Builder& SetWatchdogSec(uint32_t sec);
+ Builder& SetMemoryMonitorFlag(bool flag);
+ Builder& SetCpuMonitorFlag(bool flag);
+ Builder& SetBacktraceFlag(bool flag);
+ std::shared_ptr<WatchdogConf> Build();
+
+ private:
+ std::string loader_name_;
+ uint32_t watchdog_sec_;
+ bool memory_monitor_;
+ bool cpu_monitor_;
+ bool backtrace_;
+ };
+
+ std::string GetLoaderName() const;
+ uint32_t GetWatchdogSec() const;
+ bool IsCpuMonitor() const;
+ bool IsMemoryMonitor() const;
+ bool IsBacktrace() const;
+
+ private :
+ WatchdogConf(std::string loader_name, uint32_t watchdog_sec,
+ bool memory_monitor, bool cpu_monitor, bool backtrace);
+
+ std::string loader_name_;
+ uint32_t watchdog_sec_;
+ bool memory_monitor_;
+ bool cpu_monitor_;
+ bool backtrace_;
+};
+
+} // namespace tizen_base
+
+#endif // WATCHDOG_CONF_HH_
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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_context.hh"
+#include <mutex>
+
+namespace tizen_base {
+
+WatchdogContext::Builder& WatchdogContext::Builder::SetTid(pid_t tid) {
+ tid_ = tid;
+ return *this;
+}
+
+WatchdogContext::Builder& WatchdogContext::Builder::SetLoaderName(std::string name) {
+ loader_name_ = std::move(name);
+ return *this;
+}
+
+WatchdogContext::Builder& WatchdogContext::Builder::SetServiceName(std::string name) {
+ service_name_ = std::move(name);
+ return *this;
+}
+
+WatchdogContext::Builder& WatchdogContext::Builder::SetMemoryMonitorFlag(bool flag) {
+ memory_monitor_ = flag;
+ return *this;
+}
+
+WatchdogContext::Builder& WatchdogContext::Builder::SetCpuMonitorFlag(bool flag) {
+ cpu_monitor_ = flag;
+ return *this;
+}
+
+WatchdogContext::Builder& WatchdogContext::Builder::SetBacktraceFlag(bool flag) {
+ backtrace_ = flag;
+ return *this;
+}
+
+WatchdogContext::Builder& WatchdogContext::Builder::SetHeartbeat(
+ std::shared_ptr<Heartbeat> heartbeat) {
+ heartbeat_ = std::move(heartbeat);
+ return *this;
+}
+
+WatchdogContext::Builder& WatchdogContext::Builder::SetCpuTime(
+ std::shared_ptr<CpuTime> cpu_time) {
+ cpu_time_ = std::move(cpu_time);
+ return *this;
+}
+
+std::shared_ptr<WatchdogContext> WatchdogContext::Builder::Build() {
+ auto context = std::shared_ptr<WatchdogContext>(
+ new (std::nothrow) WatchdogContext(tid_, std::move(loader_name_),
+ std::move(service_name_), memory_monitor_, cpu_monitor_,
+ backtrace_, heartbeat_, cpu_time_));
+
+ if (context == nullptr) [[unlikely]] {
+ LOGE("Out of memory");
+ return nullptr;
+ }
+ return context;
+}
+
+pid_t WatchdogContext::GetTid() const {
+ return tid_;
+}
+
+std::string WatchdogContext::GetLoaderName() const {
+ return loader_name_;
+}
+
+std::string WatchdogContext::GetServiceName() const {
+ return service_name_;
+}
+
+std::shared_ptr<Heartbeat> WatchdogContext::GetHeartbeat() {
+ return heartbeat_;
+}
+
+std::shared_ptr<CpuTime> WatchdogContext::GetCpuTime() {
+ return cpu_time_;
+}
+
+bool WatchdogContext::CpuTimeInterval() {
+ return cpu_time_->Update();
+}
+
+bool WatchdogContext::HeartbeatInterval() {
+ bool ret = heartbeat_->CheckPingResult();
+ heartbeat_->SendPing();
+ return ret;
+}
+
+void WatchdogContext::OnWatchdogTimeout() {
+ auto self_ = shared_from_this();
+ LOGE("Loader(%s) catch tid %d(%s) timeout", GetLoaderName().c_str(),
+ GetTid(), GetServiceName().c_str());
+
+ if (IsBacktrace()) {
+ Inspector::PrintBacktrace(GetTid(), GetServiceName());
+ }
+
+ std::thread inspect(WatchdogContext::Inspect, std::move(self_));
+ inspect.detach();
+ return;
+}
+
+bool WatchdogContext::IsMemoryMonitor() {
+ return memory_monitor_;
+}
+
+bool WatchdogContext::IsCpuMonitor() {
+ return cpu_monitor_;
+}
+
+bool WatchdogContext::IsBacktrace() {
+ return backtrace_;
+}
+
+void WatchdogContext::Inspect(std::shared_ptr<WatchdogContext> context) {
+ static std::mutex inspect_print_mutex_;
+ std::unique_lock<std::mutex> lock(inspect_print_mutex_);
+
+ LOGD("Inspect Start, tid %d(%s)",
+ context->GetTid(), context->GetServiceName().c_str());
+
+ if (context->IsMemoryMonitor()) {
+ Inspector::MemoryMonitor(context->GetLoaderName());
+ }
+ if (context->IsCpuMonitor()) {
+ Inspector::CpuMonitor(context->GetCpuTime());
+ }
+
+ // ad-hoc, print bt as last step of inspect.
+
+}
+
+} // namespace tizen_base
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef WATCHDOG_CONTEXT_HH_
+#define WATCHDOG_CONTEXT_HH_
+
+#include "heartbeat.hh"
+#include "cpu_time.hh"
+#include "inspector.hh"
+
+#include <tizen_core.h>
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <unordered_map>
+
+#include "log_private.hh"
+
+namespace tizen_base {
+
+class WatchdogContext : public std::enable_shared_from_this<WatchdogContext> {
+ public:
+ class Builder {
+ public:
+ Builder(){};
+ Builder& SetTid(pid_t tid);
+ Builder& SetLoaderName(std::string name);
+ Builder& SetServiceName(std::string name);
+ Builder& SetMemoryMonitorFlag(bool flag);
+ Builder& SetCpuMonitorFlag(bool flag);
+ Builder& SetBacktraceFlag(bool flag);
+ Builder& SetHeartbeat(std::shared_ptr<Heartbeat> heartbeat);
+ Builder& SetCpuTime(std::shared_ptr<CpuTime> cpu_time);
+ std::shared_ptr<WatchdogContext> Build();
+
+ private:
+ pid_t tid_;
+ std::string loader_name_;
+ std::string service_name_;
+ bool memory_monitor_ = false;
+ bool cpu_monitor_ = false;
+ bool backtrace_ = false;
+ std::shared_ptr<Heartbeat> heartbeat_ = nullptr;
+ std::shared_ptr<CpuTime> cpu_time_ = nullptr;
+ };
+
+ pid_t GetTid() const;
+ std::string GetLoaderName() const;
+ std::string GetServiceName() const;
+
+ bool HeartbeatInterval();
+ bool CpuTimeInterval();
+ void OnWatchdogTimeout();
+
+ bool IsMemoryMonitor();
+ bool IsCpuMonitor();
+ bool IsBacktrace();
+
+ private:
+ WatchdogContext(pid_t tid, std::string loader_name, std::string service_name,
+ bool memory_monitor, bool cpu_monitor, bool backtrace,
+ std::shared_ptr<Heartbeat> heartbeat, std::shared_ptr<CpuTime> cpu_time)
+ : tid_(tid),
+ loader_name_(std::move(loader_name)),
+ service_name_(std::move(service_name)),
+ memory_monitor_(memory_monitor),
+ cpu_monitor_(cpu_monitor),
+ backtrace_(backtrace),
+ heartbeat_(std::move(heartbeat)),
+ cpu_time_(std::move(cpu_time)) {}
+
+ static void Inspect(std::shared_ptr<WatchdogContext> context);
+ std::shared_ptr<Heartbeat> GetHeartbeat();
+ std::shared_ptr<CpuTime> GetCpuTime();
+
+ pid_t tid_;
+ std::string loader_name_;
+ std::string service_name_;
+ bool memory_monitor_;
+ bool cpu_monitor_;
+ bool backtrace_;
+ std::shared_ptr<Heartbeat> heartbeat_;
+ std::shared_ptr<CpuTime> cpu_time_;
+};
+
+} // namespace tizen_base
+
+#endif // WATCHDOG_CONTEXT_HH_
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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_manager.hh"
+#include "log_private.hh"
+
+namespace tizen_base {
+
+WatchdogManager& WatchdogManager::GetInst() {
+ static WatchdogManager inst;
+ return inst;
+}
+
+WatchdogManager::~WatchdogManager() {
+ int ret = tizen_core_task_destroy(task_);
+ if (ret != TIZEN_CORE_ERROR_NONE) {
+ LOGE("Cannot destroy watchdog task");
+ }
+}
+
+bool WatchdogManager::Init(std::shared_ptr<WatchdogConf> conf) {
+ if (init_) {
+ LOGW("Reinitialize manager instance");
+ }
+
+ conf_ = std::move(conf);
+ int ret = tizen_core_task_create("watchdog", true, &task_);
+ if (ret != TIZEN_CORE_ERROR_NONE) {
+ LOGE("Cannot create watchdog task(%d)", ret);
+ return false;
+ }
+
+ ret = tizen_core_task_get_tizen_core(task_, &core_);
+ if (ret != TIZEN_CORE_ERROR_NONE) {
+ LOGE("Cannot find core from watchdog task(%d)", ret);
+ return false;
+ }
+
+ if (!StartWatchdog()) {
+ return false;
+ }
+
+ LOGD("Manager instance initialied done");
+ init_ = true;
+ return true;
+}
+
+void WatchdogManager::RegisterService(pid_t tid, std::string name,
+ tizen_core_h core) {
+ LOGI("Loader(%s) register tid %d(%s)", conf_->GetLoaderName().c_str(), tid, name.c_str());
+
+ auto builder = WatchdogContext::Builder();
+ builder.SetLoaderName(conf_->GetLoaderName());
+ builder.SetServiceName(std::move(name));
+ builder.SetTid(std::move(tid));
+ builder.SetMemoryMonitorFlag(conf_->IsMemoryMonitor());
+ builder.SetBacktraceFlag(conf_->IsBacktrace());
+
+ if (conf_->IsCpuMonitor()) {
+ builder.SetCpuMonitorFlag(true);
+ auto cpu_time = std::make_shared<CpuTime>(tid);
+ builder.SetCpuTime(cpu_time);
+ }
+
+ if (conf_->GetWatchdogSec() > 0) {
+ auto heartbeat = std::make_shared<Heartbeat>(tid, core);
+ builder.SetHeartbeat(heartbeat);
+ }
+
+ std::unique_lock<std::mutex> lock(context_list_mutex_);
+ contexts_.emplace_back(std::move(builder.Build()));
+}
+
+void WatchdogManager::UnregisterService(pid_t tid) {
+ std::unique_lock<std::mutex> lock(context_list_mutex_);
+ int ret = contexts_.remove_if([tid](auto context) {
+ return context->GetTid() == tid;
+ });
+
+ if (ret == 1) [[likely]]
+ LOGI("Loader(%s) unregister tid %d", conf_->GetLoaderName().c_str(), tid);
+ else
+ LOGE("Loader(%s) unregister tid %d return %d", conf_->GetLoaderName().c_str(), tid, ret);
+}
+
+void WatchdogManager::Interval() {
+ std::unique_lock<std::mutex> lock(context_list_mutex_);
+ int count = 1;
+ for (auto& context : contexts_) {
+ LOGI("[%d/%ld] %s Interval", count++, contexts_.size(), context->GetServiceName().c_str());
+ if (!context->CpuTimeInterval()) {
+ LOGW("Failed to update CpuTime of tid %d", context->GetTid());
+ }
+ if (!context->HeartbeatInterval()) {
+ LOGE("Failed to verify Heartbeat of tid %d", context->GetTid());
+ context->OnWatchdogTimeout();
+ }
+ }
+}
+
+bool WatchdogManager::StartWatchdog() {
+ int ret = 0;
+
+ if (conf_->GetWatchdogSec() > 0) {
+ tizen_core_source_h oneshot_source;
+
+ // Logging Purpose
+ ret = tizen_core_add_idle_job(core_, [](void* user_data) -> bool {
+ LOGI("Watchdog Manager Job Thread %d", gettid());
+ return false;
+ }, nullptr, &oneshot_source);
+ if (ret != TIZEN_CORE_ERROR_NONE) {
+ LOGE("Cannot add idle job to watchdog %d", ret);
+ return false;
+ }
+
+ // Interval
+ ret = tizen_core_add_timer(core_, conf_->GetWatchdogSec() * 1000,
+ [](void* user_data) -> bool {
+ WatchdogManager::GetInst().Interval();
+ return true;
+ }, nullptr, &source_);
+ if (ret != TIZEN_CORE_ERROR_NONE) {
+ LOGE("Cannot add watchdog interval %d", ret);
+ return false;
+ }
+
+ // Run
+ ret = tizen_core_task_run(task_);
+ if (ret != TIZEN_CORE_ERROR_NONE) {
+ LOGE("Cannot run watchdog task %d", ret);
+ return false;
+ }
+ } else {
+ LOGW("Watchdog is disabled (WatchdogSec 0)");
+ }
+
+ return true;
+}
+
+bool WatchdogManager::StopWatchdog() {
+ int ret = 0;
+ int result = true;
+
+ ret = tizen_core_remove_source(core_, source_);
+ if (ret != TIZEN_CORE_ERROR_NONE) {
+ LOGE("Cannot Stop Watchdog Monitor %d", ret);
+ result = false;
+ }
+
+ return result;
+}
+
+} // namespace tizen_base
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef WATCHDOG_MANAGER_HH_
+#define WATCHDOG_MANAGER_HH_
+
+#include "watchdog_conf.hh"
+#include "watchdog_context.hh"
+
+#include <tizen_core.h>
+
+#include <memory>
+#include <algorithm>
+#include <string>
+#include <thread>
+#include <mutex>
+#include <list>
+
+namespace tizen_base {
+
+class WatchdogManager {
+ public:
+ static WatchdogManager& GetInst();
+ bool Init(std::shared_ptr<WatchdogConf> conf);
+ void RegisterService(pid_t tid, std::string name, tizen_core_h core);
+ void UnregisterService(pid_t tid);
+
+ private:
+ bool StartWatchdog();
+ bool StopWatchdog();
+ void Interval();
+
+ WatchdogManager() : init_(false), conf_(nullptr) {};
+ ~WatchdogManager();
+
+ private:
+ bool init_;
+ tizen_core_h core_;
+ tizen_core_task_h task_;
+ tizen_core_source_h source_;
+
+ std::shared_ptr<WatchdogConf> conf_;
+ std::mutex context_list_mutex_;
+ std::list<std::shared_ptr<WatchdogContext>> contexts_;
+};
+
+} // namespace tizen_base
+
+#endif // WATCHDOG_MANAGER_HH_