Add new functions to get cpu usage of a specific process 67/317267/8
authorHwankyu Jhun <h.jhun@samsung.com>
Sat, 28 Dec 2024 10:05:02 +0000 (19:05 +0900)
committerHwanKyu Jhun <h.jhun@samsung.com>
Wed, 1 Jan 2025 22:15:43 +0000 (22:15 +0000)
To get the CPU usage of the specific process, new functions are added.

Change-Id: Ib3eadfe63e4da36464d9c4f52b4a742cad193715
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
src/aul/aul_cpu_monitor.cc [new file with mode: 0644]
src/aul/include/aul_cpu_monitor.h [new file with mode: 0644]
src/tool/aul_test/tests/aul_cpu_monitor_get_cpu_usage_test.cc [new file with mode: 0644]

diff --git a/src/aul/aul_cpu_monitor.cc b/src/aul/aul_cpu_monitor.cc
new file mode 100644 (file)
index 0000000..a5809df
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2024 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 "aul/include/aul_cpu_monitor.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <new>
+#include <string>
+
+#include "aul/aul_api.h"
+#include "aul/common/log_private.hh"
+#include "aul/include/aul.h"
+
+namespace {
+
+class CpuTimes {
+ public:
+  CpuTimes(pid_t pid) : pid_(pid) {}
+
+   ~CpuTimes() {}
+
+  int Update() {
+    if (UpdateTotalCpuTime() != 0)
+      return -1;
+
+    return UpdateProcessCpuTime();
+  }
+
+  double 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;
+  }
+
+  pid_t GetPid() const { return pid_; }
+
+ private:
+  int UpdateTotalCpuTime() {
+    FILE* fp = fopen("/proc/stat", "r");
+    if (fp == nullptr) {
+      _E("Failed to open /proc/stat");
+      return -1;
+    }
+
+    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 -1;
+    }
+
+    previous_total_time_ = current_total_time_;
+    current_total_time_ =
+        user + nice + system + idle_time + iowait + irq + softirq + steal;
+    _D("previous_total_time=%llu, current_total_time=%llu",
+       previous_total_time_, current_total_time_);
+    return 0;
+  }
+
+  int UpdateProcessCpuTime() {
+    std::string path = "/proc/" + std::to_string(pid_) + "/stat";
+    FILE* fp = fopen(path.c_str(), "r");
+    if (fp == nullptr) {
+      _E("Failed to open %s", path.c_str());
+      return -1;
+    }
+
+    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 -1;
+    }
+
+    previous_process_time_ = current_process_time_;
+    current_process_time_ = utime + stime;
+    _W("previous_process_time=%llu, current_process_time=%llu",
+       previous_process_time_, current_process_time_);
+    return 0;
+  }
+
+ private:
+  pid_t pid_;
+  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
+
+extern "C" API int aul_cpu_times_create(pid_t pid, aul_cpu_times_h* cpu_times) {
+  if (pid < 1 || cpu_times == nullptr) {
+    _E("Invalid parameter");
+    return AUL_R_EINVAL;
+  }
+
+  auto* handle = new (std::nothrow) ::CpuTimes(pid);
+  if (handle == nullptr) {
+    _E("Out of memory");
+    return AUL_R_ENOMEM;
+  }
+
+  *cpu_times = static_cast<aul_cpu_times_h>(handle);
+  return AUL_R_OK;
+}
+
+extern "C" API int aul_cpu_times_destroy(aul_cpu_times_h cpu_times) {
+  if (cpu_times == nullptr) {
+    _E("Invalid parameter");
+    return AUL_R_EINVAL;
+  }
+
+  auto* handle = static_cast<::CpuTimes*>(cpu_times);
+  delete handle;
+  return AUL_R_OK;
+}
+
+extern "C" API int aul_cpu_times_update(aul_cpu_times_h cpu_times) {
+  if (cpu_times == nullptr) {
+    _E("Invalid parameter");
+    return AUL_R_EINVAL;
+  }
+
+  auto* handle = static_cast<::CpuTimes*>(cpu_times);
+  if (handle->Update() != 0) {
+    _E("Failed to update cpu times");
+    return AUL_R_ERROR;
+  }
+
+  return AUL_R_OK;
+}
+
+extern "C" API int aul_cpu_monitor_get_cpu_usage(
+    aul_cpu_times_h cpu_times, double* cpu_usage) {
+  if (cpu_times == nullptr || cpu_usage == nullptr) {
+    _E("Invalid parameter");
+    return AUL_R_EINVAL;
+  }
+
+  auto handle = static_cast<::CpuTimes*>(cpu_times);
+  *cpu_usage = handle->CalculateCpuUsage();
+  _D("Process=%d, CPU usage=%.4f%%", handle->GetPid(), *cpu_usage);
+  return AUL_R_OK;
+}
diff --git a/src/aul/include/aul_cpu_monitor.h b/src/aul/include/aul_cpu_monitor.h
new file mode 100644 (file)
index 0000000..bb7953b
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2024 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 __AUL_CPU_MONITOR_H__
+#define __AUL_CPU_MONITOR_H__
+
+#include <sys/types.h>
+
+#include <aul.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief The handle of CPU times.
+ * @since_tizen 9.0
+ */
+typedef void *aul_cpu_times_h;
+
+/**
+ * @brief Creates a handle of CPU times.
+ * @since_tizen 9.0
+ * @param[in]   pid          The process ID
+ * @param[out]  cpu_times    The handle of CPU times
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #AUL_R_OK Successful
+ * @retval #AUL_R_EINVAL Invalid parameter
+ * @retval #AUL_R_ENOMEM Out of memory
+ * @see aul_cpu_times_destroy()
+ */
+int aul_cpu_times_create(pid_t pid, aul_cpu_times_h *cpu_times);
+
+/**
+ * @brief Destroys the handle of CPU times.
+ * @since_tizen 9.0
+ * @param[in]   cpu_times    The handle of CPU times
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #AUL_R_OK Successful
+ * @retval #AUL_R_EINVAL Invalid parameter
+ * @see aul_cpu_times_create()
+ */
+int aul_cpu_times_destroy(aul_cpu_times_h cpu_times);
+
+/**
+ * @brief Updates the handle of CPU times.
+ * @since_tizen 9.0
+ * @param[in]   cpu_times    The handle of CPU times
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #AUL_R_OK Successful
+ * @retval #AUL_R_EINVAL Invalid parameter
+ * @retval #AUL_R_ERROR Internal error occured
+ * @see aul_cpu_times_create()
+ */
+int aul_cpu_times_update(aul_cpu_times_h cpu_times);
+
+/**
+ * @brief Gets the cpu usage from the handle of CPU times.
+ * @since_tizen 9.0
+ * @param[in]   cpu_times    The handle of CPU times
+ * @param[out]  cpu_usage    The cpu usage
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #AUL_R_OK Successful
+ * @retval #AUL_R_EINVAL Invalid parameter
+ * @retval #AUL_R_ERROR Internal error occured
+ * @see aul_cpu_times_create()
+ * @code
+ * #include <stdio.h>
+ * #include <aul_cpu_monitor.h>
+ * int main(void) {
+ *   aul_cpu_times_h cpu_times;
+ *   if (aul_cpu_times_create(getpid(), &cpu_times)!= AUL_R_OK) {
+ *     return -1;
+ *   }
+ *
+ *   if (aul_cpu_times_update(cpu_times)!= AUL_R_OK) {
+ *     aul_cpu_times_destroy(cpu_times);
+ *     return -1;
+ *   }
+ *
+ *   sleep(1);
+ *
+ *   if (aul_cpu_times_update(cpu_times)!= AUL_R_OK) {
+ *     aul_cpu_times_destroy(cpu_times);
+ *     return -1;
+ *   }
+ *
+ *   double cpu_usage;
+ *   if (aul_cpu_monitor_get_cpu_usage(cpu_times, &cpu_usage)!= AUL_R_OK) {
+ *     aul_cpu_times_destroy(cpu_times);
+ *     return -1;
+ *   }
+ *
+ *   printf("cpu usage: %f\n", cpu_usage);
+ *   aul_cpu_times_destroy(cpu_times);
+ *   return 0;
+ * }
+ * @endcode
+ */
+int aul_cpu_monitor_get_cpu_usage(aul_cpu_times_h cpu_times, double *cpu_usage);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __AUL_CPU_MONITOR_H__ */
diff --git a/src/tool/aul_test/tests/aul_cpu_monitor_get_cpu_usage_test.cc b/src/tool/aul_test/tests/aul_cpu_monitor_get_cpu_usage_test.cc
new file mode 100644 (file)
index 0000000..c9971ce
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2024 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 <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "aul_test.hh"
+#include "include/aul_cpu_monitor.h"
+#include "log_private.hh"
+
+namespace aul_test {
+
+class AulCpuMonitorGetCpuUsageTest : public AulTest {
+ public:
+  AulCpuMonitorGetCpuUsageTest()
+      : AulTest("get_cpu_usage", "aul_cpu_monitor_get_cpu_usage",
+                "get_cpu_usage <pid>") {}
+
+  virtual ~AulCpuMonitorGetCpuUsageTest() {}
+
+  void SetUp() override {}
+
+  void TearDown() override {}
+
+  int Test(int argc, char** argv) override {
+    if (argc < 3) {
+      Usage();
+      return -1;
+    }
+
+    if (getuid() != 0) {
+      fprintf(stderr, "Permission denied\n");
+      return -EPERM;
+    }
+
+    unsigned int seconds = 1;
+    if (argc > 3)
+      seconds = atoi(argv[3]);
+
+    _D("[aul_cpu_monitor_get_cpu_usage test] %s", argv[2]);
+    pid_t pid = atoi(argv[2]);
+    aul_cpu_times_h cpu_times = nullptr;
+    int ret = aul_cpu_times_create(pid, &cpu_times);
+    ret |= aul_cpu_times_update(cpu_times);
+
+    sleep(seconds);
+
+    ret |= aul_cpu_times_update(cpu_times);
+
+    double cpu_usage = 0.0f;
+    ret |= aul_cpu_monitor_get_cpu_usage(cpu_times, &cpu_usage);
+    aul_cpu_times_destroy(cpu_times);
+
+    printf("Process=%d, CPU usage=%.4f%%\n", pid, cpu_usage);
+    return ret;
+  }
+};
+
+AUL_TEST_REGISTER(AulCpuMonitorGetCpuUsageTest, get_cpu_usage_test);
+
+}  // namespace aul_test