Refactor Memory Monitor 60/290060/3
authorHwankyu Jhun <h.jhun@samsung.com>
Sun, 19 Mar 2023 23:58:27 +0000 (23:58 +0000)
committerHwankyu Jhun <h.jhun@samsung.com>
Mon, 20 Mar 2023 00:35:50 +0000 (00:35 +0000)
The launchpad memory monitor is implemented using C++ language.

Change-Id: I13a530d912a43ecff1271e78ede22334da9344c3
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
src/launchpad-process-pool/launchpad.cc
src/launchpad-process-pool/launchpad_memory_monitor.cc [deleted file]
src/launchpad-process-pool/memory_monitor.cc [new file with mode: 0644]
src/launchpad-process-pool/memory_monitor.hh [new file with mode: 0644]
src/lib/launchpad-common/procfs.cc [new file with mode: 0644]
src/lib/launchpad-common/procfs.hh [moved from src/launchpad-process-pool/launchpad_memory_monitor.h with 51% similarity]

index 0980599..cb240cc 100644 (file)
 #include <algorithm>
 #include <string>
 
+#include <procfs.hh>
+
 #include "launchpad-process-pool/debug.hh"
 #include "launchpad-process-pool/launcher_info.hh"
 #include "launchpad-process-pool/launchpad_config.h"
 #include "launchpad-process-pool/launchpad_debug.h"
 #include "launchpad-process-pool/launchpad_inotify.h"
 #include "launchpad-process-pool/launchpad_io_channel.h"
-#include "launchpad-process-pool/launchpad_memory_monitor.h"
 #include "launchpad-process-pool/launchpad_signal.h"
 #include "launchpad-process-pool/loader_info.hh"
 #include "launchpad-process-pool/slot_info.h"
 #include "lib/common/inc/key.h"
 #include "lib/common/inc/launchpad_common.h"
 #include "lib/common/inc/launchpad_plugin.h"
-#include "lib/common/inc/launchpad_proc.h"
 #include "lib/common/inc/launchpad_types.h"
 #include "lib/common/inc/perf.h"
 
 #include "launchpad-process-pool/dbus.hh"
 #include "launchpad-process-pool/log.hh"
+#include "launchpad-process-pool/memory_monitor.hh"
 #include "launchpad-process-pool/worker.hh"
 
 #define AUL_PR_NAME 16
@@ -130,7 +131,7 @@ typedef struct {
   io_channel_h channel = nullptr;
   io_channel_h hydra_channel = nullptr;
   unsigned int score = 0;
-  unsigned int pss = 0;
+  uint64_t pss = 0;
   int cpu_check_count = 0;
   int on_boot_timeout = 0;
   guint on_boot_timer = 0;
@@ -200,6 +201,8 @@ typedef request_t* request_h;
 
 typedef int (*request_handler)(request_h request);
 
+static void HandleMemoryStatusChangedEvent(bool low_memory);
+
 namespace {
 
 class CleanupInfo : public launchpad::Worker::Job {
@@ -218,6 +221,36 @@ class CleanupInfo : public launchpad::Worker::Job {
   pid_t pid_;
 };
 
+class MemoryManager : public launchpad::MemoryMonitor,
+                      public launchpad::MemoryMonitor::IEvent {
+ public:
+  MemoryManager(const MemoryManager&) = delete;
+  MemoryManager& operator = (const MemoryManager&) = delete;
+  MemoryManager(MemoryManager&&) = delete;
+  MemoryManager& operator = (MemoryManager&&) = delete;
+
+  static MemoryManager& GetInst() {
+    static MemoryManager inst;
+    return inst;
+  }
+
+  void Init() {
+    Start();
+  }
+
+  void Finish() {
+    Stop();
+  }
+
+ private:
+  MemoryManager() : MemoryMonitor(this) {}
+  ~MemoryManager() {}
+
+  void OnMemoryStatusChanged(bool low_memory) override {
+    HandleMemoryStatusChangedEvent(low_memory);
+  }
+};
+
 int __sys_hwacc;
 std::unique_ptr<launchpad::LoaderInfoManager> loader_info_manager;
 std::unique_ptr<launchpad::LoaderInfoManager> app_defined_loader_info_manager;
@@ -520,7 +553,7 @@ static void __update_slots_pss(void) {
     if (cpc->pid == CANDIDATE_NONE)
       continue;
 
-    _proc_get_mem_pss(cpc->pid, &cpc->pss);
+    launchpad::Procfs::GetPssMemory(cpc->pid, &cpc->pss);
   }
 }
 
@@ -571,7 +604,7 @@ static void __pause_all_running_slots(bool is_hydra) {
     cpc = reinterpret_cast<candidate_process_context_t*>(iter->data);
     if (cpc->is_hydra == is_hydra && cpc->pid != CANDIDATE_NONE) {
       __update_slot_state(cpc, launchpad::LoaderMethod::OutOfMemory, true);
-      if (!_memory_monitor_is_low_memory())
+      if (!MemoryManager::GetInst().IsLowMemory())
         return;
     }
 
@@ -972,7 +1005,7 @@ static int __prepare_candidate_process(int type, int loader_id) {
 
   launchpad::Log::Print("[CANDIDATE]", "pid(%7d) | type(%d) | loader(%s)",
       info->pid, cpt->loader_id, cpt->loader_name);
-  _memory_monitor_reset_timer();
+  MemoryManager::GetInst().Reset();
   __candidate_info_free(info);
   return 0;
 }
@@ -2009,17 +2042,13 @@ static int __dispatch_cmd_remove_loader(bundle* kb) {
 }
 
 static int __check_caller_by_pid(int pid) {
-  int ret;
-  char buf[PATH_MAX] = {
-      0,
-  };
-
-  ret = _proc_get_attr(pid, buf, sizeof(buf));
-  if (ret < 0)
+  std::string attr = launchpad::Procfs::GetAttrCurrent(pid);
+  if (attr.empty())
     return -1;
 
-  if (strcmp(buf, "User") == 0 || strcmp(buf, "System") == 0 ||
-      strcmp(buf, "System::Privileged") == 0)
+  if (attr.compare("User") == 0 ||
+      attr.compare("System") == 0 ||
+      attr.compare("System::Privileged") == 0)
     return 0;
 
   return -1;
@@ -2425,7 +2454,7 @@ static int __launch_request_prepare(request_h request) {
 }
 
 static void __launch_request_complete(request_h request) {
-  _memory_monitor_reset_timer();
+  MemoryManager::GetInst().Reset();
   __request_send_result(request, request->pid);
 
   if (request->pid > 0) {
@@ -3011,7 +3040,7 @@ static void __add_app_defined_loaders(void) {
 }
 
 static bool __is_low_memory(void) {
-  if (_memory_monitor_is_low_memory())
+  if (MemoryManager::GetInst().IsLowMemory())
     return true;
 
   if (__memory_status_low >= MEMORY_STATUS_LOW)
@@ -3226,14 +3255,12 @@ static int __init_logger_fd(void) {
   return 0;
 }
 
-static int __memory_monitor_cb(bool low_memory, void* user_data) {
-  candidate_process_context_t* cpc;
-
-  cpc = __get_running_slot(false);
-  if (!cpc && low_memory)
-    return -1;
-
+static void HandleMemoryStatusChangedEvent(bool low_memory) {
   if (low_memory) {
+    candidate_process_context_t* cpc = __get_running_slot(false);
+    if (cpc == nullptr)
+      return;
+
     _W("Low memory");
     __update_slots_pss();
 
@@ -3242,8 +3269,6 @@ static int __memory_monitor_cb(bool low_memory, void* user_data) {
   } else {
     __resume_all_slots();
   }
-
-  return 0;
 }
 
 static gboolean __logger_recovery_cb(gpointer data) {
@@ -3332,8 +3357,7 @@ static int __before_loop(int argc, char** argv) {
     _W("Failed to initialize config");
 
   _inotify_init();
-  _memory_monitor_init();
-  _memory_monitor_set_event_cb(__memory_monitor_cb, nullptr);
+  MemoryManager::GetInst().Init();
 
   MAX_CPU_CHECK_COUNT =
       _config_get_int_value(CONFIG_TYPE_CPU_CHECKER_MAX_COUNT);
@@ -3366,7 +3390,7 @@ static int __before_loop(int argc, char** argv) {
 static void __after_loop(void) {
   launchpad::Log::Finish();
   cleaner.reset();
-  _memory_monitor_fini();
+  MemoryManager::GetInst().Finish();
   __unregister_vconf_events();
   if (__pid_table)
     g_hash_table_destroy(__pid_table);
diff --git a/src/launchpad-process-pool/launchpad_memory_monitor.cc b/src/launchpad-process-pool/launchpad_memory_monitor.cc
deleted file mode 100644 (file)
index c69fe73..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (c) 2020 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 "launchpad-process-pool/launchpad_memory_monitor.h"
-
-#include <errno.h>
-#include <glib.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "launchpad-process-pool/launchpad_config.h"
-#include "lib/common/inc/launchpad_proc.h"
-#include "lib/common/inc/log_private.h"
-
-#define INTERVAL_BASE_RATE 0.15f
-
-struct memory_monitor_s {
-  unsigned int threshold;
-  unsigned int prev_used_ratio;
-  unsigned int base_interval;
-  unsigned int interval;
-  bool low_memory;
-  guint tag;
-  memory_monitor_cb callback;
-  void* user_data;
-};
-
-static struct memory_monitor_s __monitor;
-
-static void __memory_monitor_start(void);
-
-static gboolean __memory_check_cb(gpointer data) {
-  bool low_memory;
-
-  __monitor.tag = 0;
-  low_memory = _memory_monitor_is_low_memory();
-  if (__monitor.low_memory != low_memory && __monitor.callback)
-    __monitor.callback(low_memory, __monitor.user_data);
-
-  __monitor.low_memory = low_memory;
-  __memory_monitor_start();
-
-  return G_SOURCE_REMOVE;
-}
-
-static void __memory_monitor_stop(void) {
-  if (!__monitor.tag)
-    return;
-
-  g_source_remove(__monitor.tag);
-  __monitor.tag = 0;
-}
-
-static void __memory_monitor_start(void) {
-  if (__monitor.threshold == 100)
-    return;
-
-  if (__monitor.tag)
-    return;
-
-  __monitor.tag = g_timeout_add(__monitor.interval, __memory_check_cb, NULL);
-
-  __monitor.interval += __monitor.interval * INTERVAL_BASE_RATE;
-}
-
-int _memory_monitor_reset_timer(void) {
-  _W("Reset");
-  __monitor.interval = __monitor.base_interval;
-
-  __memory_monitor_stop();
-  __memory_monitor_start();
-
-  return 0;
-}
-
-bool _memory_monitor_is_low_memory(void) {
-  unsigned int mem_used_ratio = 0;
-
-  if (__monitor.threshold == 100)
-    return false;
-
-  _proc_get_mem_used_ratio(&mem_used_ratio);
-
-  _W("previous used ratio(%u), current used ratio(%u)",
-     __monitor.prev_used_ratio, mem_used_ratio);
-
-  __monitor.prev_used_ratio = mem_used_ratio;
-
-  if (mem_used_ratio > __monitor.threshold)
-    return true;
-
-  return false;
-}
-
-int _memory_monitor_set_event_cb(memory_monitor_cb callback, void* user_data) {
-  __monitor.callback = callback;
-  __monitor.user_data = user_data;
-
-  return 0;
-}
-
-int _memory_monitor_init(void) {
-  int ret;
-
-  _W("MEMORY_MONITOR_INIT");
-
-  __monitor.threshold =
-      _config_get_int_value(CONFIG_TYPE_MEMORY_MONITOR_THRESHOLD);
-  __monitor.base_interval =
-      _config_get_int_value(CONFIG_TYPE_MEMORY_MONITOR_INTERVAL);
-  __monitor.interval = __monitor.base_interval;
-
-  ret = _proc_get_mem_used_ratio(&__monitor.prev_used_ratio);
-  if (ret != 0) {
-    _E("Failed to get mem used ratio. error(%d)", ret);
-    return ret;
-  }
-
-  __memory_monitor_start();
-
-  return 0;
-}
-
-void _memory_monitor_fini(void) {
-  _W("MEMORY_MONITOR_FINI");
-
-  __memory_monitor_stop();
-}
diff --git a/src/launchpad-process-pool/memory_monitor.cc b/src/launchpad-process-pool/memory_monitor.cc
new file mode 100644 (file)
index 0000000..54acde4
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2023 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 "launchpad-process-pool/memory_monitor.hh"
+
+#include <procfs.hh>
+
+#include "launchpad-process-pool/launchpad_config.h"
+#include "launchpad-process-pool/log_private.hh"
+
+namespace launchpad {
+namespace {
+
+constexpr const float INTERVAL_BASE_RATE = 0.15f;
+
+}  // namespace
+
+MemoryMonitor::MemoryMonitor(IEvent* listener) : listener_(listener) {
+  threshold_ = _config_get_int_value(CONFIG_TYPE_MEMORY_MONITOR_THRESHOLD);
+  base_interval_ = _config_get_int_value(CONFIG_TYPE_MEMORY_MONITOR_INTERVAL);
+  interval_ = base_interval_;
+}
+
+MemoryMonitor::~MemoryMonitor() {
+  Stop();
+}
+
+bool MemoryMonitor::IsLowMemory() {
+  if (threshold_ == 100)
+    return false;
+
+  uint32_t usage = 0;
+  Procfs::GetMemoryUsage(&usage);
+  _W("Previous used ratio: %u, current used ratio: %u",
+      previous_usage_, usage);
+  previous_usage_ = usage;
+
+  if (usage > threshold_)
+    return true;
+
+  return false;
+}
+
+void MemoryMonitor::Reset() {
+  _W("Reset");
+  interval_ = base_interval_;
+
+  Stop();
+  Start();
+}
+
+void MemoryMonitor::Start() {
+  if (threshold_ == 100)
+    return;
+
+  if (timer_ != 0)
+    return;
+
+  timer_ = g_timeout_add(interval_, TimeoutCb, this);
+  interval_ += interval_ * INTERVAL_BASE_RATE;
+}
+
+void MemoryMonitor::Stop() {
+  if (timer_ != 0) {
+    g_source_remove(timer_);
+    timer_ = 0;
+  }
+}
+
+gboolean MemoryMonitor::TimeoutCb(gpointer user_data) {
+  auto* handle = static_cast<MemoryMonitor*>(user_data);
+  handle->timer_ = 0;
+  bool low_memory = handle->IsLowMemory();
+  if (low_memory != handle->low_memory_ && handle->listener_)
+    handle->listener_->OnMemoryStatusChanged(low_memory);
+
+  handle->low_memory_ = low_memory;
+  handle->Start();
+  return G_SOURCE_REMOVE;
+}
+
+}  // namespace launchpad
diff --git a/src/launchpad-process-pool/memory_monitor.hh b/src/launchpad-process-pool/memory_monitor.hh
new file mode 100644 (file)
index 0000000..34f9a2c
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023 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 LAUNCHPAD_PROCESS_POOL_MEMORY_MONITOR_HH_
+#define LAUNCHPAD_PROCESS_POOL_MEMORY_MONITOR_HH_
+
+#include <glib.h>
+
+#include <cstdint>
+
+namespace launchpad {
+
+class MemoryMonitor {
+ public:
+  class IEvent {
+   public:
+    virtual ~IEvent() = default;
+    virtual void OnMemoryStatusChanged(bool low_memory) = 0;
+  };
+
+  explicit MemoryMonitor(IEvent* listener);
+  virtual ~MemoryMonitor();
+
+  bool IsLowMemory();
+  void Reset();
+  void Stop();
+  void Start();
+
+ private:
+  static gboolean TimeoutCb(gpointer user_data);
+
+ private:
+  IEvent* listener_;
+  uint32_t threshold_ = 0;
+  uint32_t previous_usage_ = 0;
+  guint base_interval_ = 0;
+  guint interval_ = 0;
+  guint timer_ = 0;
+  bool low_memory_ = false;
+};
+
+}  // namespace launchpad
+
+#endif  // LAUNCHPAD_PROCESS_POOL_MEMORY_MONITOR_HH_
diff --git a/src/lib/launchpad-common/procfs.cc b/src/lib/launchpad-common/procfs.cc
new file mode 100644 (file)
index 0000000..ac9596b
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2023 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 "launchpad-common/procfs.hh"
+
+#include <unistd.h>
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "launchpad-common/log_private.hh"
+
+namespace launchpad {
+
+void Procfs::GetMemoryUsage(uint32_t* usage) {
+  if (usage == nullptr) {
+    _E("Invalid argument");
+    return;
+  }
+
+  uint64_t mem_total = 0;
+  uint64_t mem_free = 0;
+  uint64_t mem_available = 0;
+  uint64_t mem_cached = 0;
+
+  std::ifstream stream("/proc/meminfo");
+  std::string line;
+  while (getline(stream, line)) {
+    std::istringstream iss(line);
+    std::string key;
+    uint64_t value;
+
+    if (iss >> key >> value) {
+      if (key == "MemTotal:")
+        mem_total = value;
+      else if (key == "MemAvailable:")
+        mem_available = value;
+      else if (key == "MemFree:")
+        mem_free = value;
+      else if (key == "Cached:")
+        mem_cached = value;
+    }
+  }
+
+  if (mem_total == 0) {
+    _E("Failed to get total memory size");
+    return;
+  }
+
+  if (mem_available == 0)
+    mem_available = mem_free + mem_cached;
+
+  *usage = (mem_total - mem_available) * 100 / mem_total;
+  _W("usage: %u %%", *usage);
+}
+
+void Procfs::GetPssMemory(pid_t pid, uint64_t* mem_pss) {
+  if (pid < 1 || mem_pss == nullptr) {
+    _E("Invalid parameter");
+    return;
+  }
+
+  std::ifstream file("/proc/" + std::to_string(pid) + "/smaps");
+  std::string line;
+  uint64_t total_pss = 0;
+
+  while (std::getline(file, line)) {
+    uint64_t pss = 0;
+    if (std::sscanf(line.c_str(), "Pss: %llu kB", &pss) == 1)
+      total_pss += pss;
+  }
+
+  *mem_pss = total_pss;
+}
+
+std::string Procfs::GetAttrCurrent(pid_t pid) {
+  const std::string path = "/proc/" + std::to_string(pid) + "/attr/current";
+  std::ifstream file(path);
+  if (!file.is_open()) {
+    _E("%s is not opened", path.c_str());
+    return {};
+  }
+
+  std::stringstream buffer;
+  buffer << file.rdbuf();
+  file.close();
+
+  const std::string result = buffer.str();
+  if (result.empty()) {
+    _E("file is empty");
+    return {};
+  }
+
+  return result;
+}
+
+}  // namespace launchpad
similarity index 51%
rename from src/launchpad-process-pool/launchpad_memory_monitor.h
rename to src/lib/launchpad-common/procfs.hh
index a8486be..4c45e7a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2023 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.
  * limitations under the License.
  */
 
-#ifndef __LAUNCHPAD_MEMORY_MONITOR_H__
-#define __LAUNCHPAD_MEMORY_MONITOR_H__
+#ifndef LIB_LAUNCHPAD_COMMON_PROCFS_HH_
+#define LIB_LAUNCHPAD_COMMON_PROCFS_HH_
 
-#include <stdbool.h>
+#include <sys/types.h>
 
-typedef int (*memory_monitor_cb)(bool low_memory, void *user_data);
+#include <string>
 
-int _memory_monitor_init(void);
+#undef EXPORT_API
+#define EXPORT_API __attribute__((visibility("default")))
 
-void _memory_monitor_fini(void);
+namespace launchpad {
 
-int _memory_monitor_set_event_cb(memory_monitor_cb callback, void *user_data);
+class EXPORT_API Procfs {
+ public:
+  static void GetMemoryUsage(uint32_t* usage);
+  static void GetPssMemory(pid_t pid, uint64_t* mem_pss);
+  static std::string GetAttrCurrent(pid_t pid);
+};
 
-int _memory_monitor_reset_timer(void);
+}  // namespace launchpad
 
-bool _memory_monitor_is_low_memory(void);
-
-#endif /* __LAUNCHPAD_MEMORY_MONITOR_H__ */
+#endif  // LIB_LAUNCHPAD_COMMON_PROCFS_HH_