resource: Add process group resource driver 24/272424/5
authorDongwoo Lee <dwoo08.lee@samsung.com>
Mon, 14 Mar 2022 01:43:38 +0000 (10:43 +0900)
committerDongwoo Lee <dwoo08.lee@samsung.com>
Mon, 21 Mar 2022 04:10:50 +0000 (13:10 +0900)
This resource is pseudo-device driver which provides information of
related process group.

Controls:
 - PROCESS_GROUP_CTRL_ROOT_PID: pid of top-level process in a group
  what we want to see.
Atrributes:
 - PROCESS_GROUP_ATTR_LIST: array of pid which includes all processes
      related with root pid. if root pid is
    negative, list contains all processes.

Change-Id: I0b7a85dd7d8f2bd4628e17c8db923dd4abd112a8
Signed-off-by: Dongwoo Lee <dwoo08.lee@samsung.com>
CMakeLists.txt
lib/tmonitor/tmonitor.h
src/resource/resource-process-group.c [new file with mode: 0644]

index b100c63..b1bf42e 100644 (file)
@@ -48,6 +48,7 @@ SET(SRCS
        src/resource/resource-system.c
        src/resource/resource-battery.c
        src/resource/resource-process.c
+       src/resource/resource-process-group.c
        src/monitor/monitor.c
        src/monitor/monitor-thread.c
        src/monitor/monitor-command.c
index d64ad51..5cda25d 100644 (file)
@@ -40,6 +40,7 @@ extern "C" {
 #define RESOURCE_TYPE_PROCESS          6
 #define RESOURCE_TYPE_DISPLAY          7
 #define RESOURCE_TYPE_SYSTEM           8
+#define RESOURCE_TYPE_PROCESS_GROUP    9
 #define RESOURCE_TYPE_NONSTANDARD      99
 
 /**
@@ -122,6 +123,11 @@ extern "C" {
 
 #define PROCESS_CTRL_TGID                      BIT(0)
 
+/* Process List Resource */
+#define PROCESS_GROUP_ATTR_LIST                        BIT(0)  /* DATA_TYPE_ARRAY */
+
+#define PROCESS_GROUP_CTRL_ROOT_PID            BIT(0)
+
 /**
  * @brief Initialize the tizen monitor
  * @return @c positive integer as tizen monitor id on success, otherwise a negative error value
diff --git a/src/resource/resource-process-group.c b/src/resource/resource-process-group.c
new file mode 100644 (file)
index 0000000..a7fc7e1
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * PASS (Power Aware System Service) - Process Group Resource Driver
+ *
+ * Copyright (c) 2022 Samsung Electronics 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 <util/log.h>
+#include <util/resource.h>
+#include <util/kernel.h>
+
+#include <tmonitor/tmonitor.h>
+
+#define PROC_DIR_PATH  "/proc/"
+
+struct process_info {
+       pid_t pid;
+       pid_t ppid;
+       struct process_info *parent;
+};
+
+static int process_group_get_list(const struct resource *res,
+                               const struct resource_attribute *attr,
+                               void *data)
+{
+       struct process_info *pi, *target_pi, *parent_pi;
+       struct dirent *task_entry;
+       struct array_value *list = data;
+       char *statfields[PROCESS_STAT_FIELD_MAX];
+       char buf[BUFF_MAX];
+       GHashTableIter iter;
+       gpointer key, value;
+       GHashTable *process_hash;
+       GArray *process_array;
+       DIR *task_dir;
+       int i, ret = 0;
+       int *data_array;
+       pid_t pid;
+
+       if (!res || !res->priv || !attr || !data)
+               return -EINVAL;
+
+       task_dir = opendir(PROC_DIR_PATH);
+       if (!task_dir)
+               return -ESRCH;
+
+       process_hash = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, free);
+       if (!process_hash) {
+               ret = -ENOMEM;
+               goto out_close;
+       }
+
+       /* create list of all processes */
+       while ((task_entry = readdir(task_dir)) != NULL) {
+               const char *name = task_entry->d_name;
+
+               if (task_entry->d_type != DT_DIR && task_entry->d_type != DT_UNKNOWN)
+                       continue;
+
+               if (name[0] < '0' || name[0] > '9')
+                       continue;
+
+               pid = atoi(name);
+
+               ret = kernel_get_process_stat_fields(pid, buf, BUFF_MAX, statfields);
+               if (ret < 0)
+                       continue; /* process might be terminated */
+
+               pi = calloc(1, sizeof(struct process_info));
+               if (!pi) {
+                       ret = -ENOMEM;
+                       goto out_free_hash;
+               }
+
+               pi->pid = pid;
+               pi->ppid = atoi(statfields[PROCESS_STAT_FIELD_PPID]);
+
+               if (g_hash_table_contains(process_hash, (gpointer)&pi->ppid)) {
+                       parent_pi = g_hash_table_lookup(process_hash, (gpointer)&pi->ppid);
+
+                       pi->parent = parent_pi;
+               }
+
+               g_hash_table_insert(process_hash, (gpointer)&pi->pid, (gpointer)pi);
+       }
+
+       target_pi = res->priv;
+
+       process_array = g_array_new(false, false, sizeof(int));
+       if (!process_array) {
+               ret = -ENOMEM;
+               goto out_free_hash;
+       }
+
+       if (target_pi->pid < 0) {
+               /* just add all processes into array if parent pid is negative */
+               g_hash_table_iter_init(&iter, process_hash);
+               while (g_hash_table_iter_next(&iter, &key, &value)) {
+                       pi = (struct process_info *)value;
+                       g_array_append_val(process_array, pi->pid);
+               }
+       } else {
+               g_hash_table_iter_init(&iter, process_hash);
+               while (g_hash_table_iter_next(&iter, &key, &value)) {
+                       pi = (struct process_info *)value;
+
+                       if (target_pi->pid == pi->pid || target_pi->pid == pi->ppid) {
+                               g_array_append_val(process_array, pi->pid);
+                               continue;
+                       }
+
+                       parent_pi = pi->parent;
+                       while (parent_pi != NULL) {
+                               if (parent_pi->ppid == target_pi->pid) {
+                                       g_array_append_val(process_array, pi->pid);
+                                       break;
+                               }
+                               parent_pi = parent_pi->parent;
+                       }
+               }
+       }
+
+       if (list->data)
+               free(list->data);
+
+       data_array = list->data = malloc(sizeof(int) * process_array->len);
+       if (!data_array) {
+               ret = -ENOMEM;
+               goto out_free_array;
+       }
+       list->type = DATA_TYPE_INT;
+       list->length = process_array->len;
+
+       for (i = 0; i < list->length; i++)
+               data_array[i] = g_array_index(process_array, int, i);
+
+out_free_array:
+       g_array_free(process_array, true);
+out_free_hash:
+       g_hash_table_destroy(process_hash);
+out_close:
+       closedir(task_dir);
+
+       return ret;
+}
+
+static const struct resource_attribute process_group_attrs[] = {
+       {
+               .name   = "PROCESS_GROUP_ATTR_LIST",
+               .id     = PROCESS_GROUP_ATTR_LIST,
+               .type   = DATA_TYPE_ARRAY,
+               .ops    = {
+                       .get = process_group_get_list,
+                       .is_supported = resource_attr_supported_always,
+               },
+       },
+};
+
+static int process_group_setup_root_pid(const struct resource *res,
+                               const struct resource_control *ctrl,
+                               const void *data)
+{
+       struct process_info *target;
+       char *statfields[PROCESS_STAT_FIELD_MAX];
+       char buf[BUFF_MAX];
+       int target_pid;
+       int ret;
+
+       if (!res || !res->priv || !ctrl)
+               return -EINVAL;
+
+       target = res->priv;
+
+       target_pid = (int)(intptr_t)data;
+
+       ret = kernel_get_process_stat_fields(target_pid, buf, BUFF_MAX, statfields);
+       if (ret < 0) {
+               _E("target process pid is not valid");
+               target->pid = -1;
+               target->ppid = -1;
+               return ret;
+       }
+
+       target->pid = atoi(statfields[PROCESS_STAT_FIELD_PID]);
+       target->ppid = atoi(statfields[PROCESS_STAT_FIELD_PPID]);
+
+       return 0;
+}
+
+static const struct resource_control process_group_ctrls[] = {
+       {
+               .name = "PROCESS_GROUP_CTRL_ROOT_PID",
+               .id = PROCESS_GROUP_CTRL_ROOT_PID,
+               .ops = {
+                       .set = process_group_setup_root_pid,
+               },
+       },
+};
+
+static int process_group_init(struct resource *res)
+{
+       struct process_info *target;
+
+       target = malloc(sizeof(struct process_info));
+       if (!target)
+               return -ENOMEM;
+
+       target->pid = -1;
+       target->ppid = -1;
+
+       res->priv = target;
+
+       return 0;
+}
+
+static void process_group_exit(struct resource *res)
+{
+       if (res && res->priv)
+               free(res->priv);
+}
+
+static const struct resource_driver process_group_driver = {
+       .name           = "PROCESS_GROUP",
+       .type           = RESOURCE_TYPE_PROCESS_GROUP,
+       .attrs          = process_group_attrs,
+       .num_attrs      = ARRAY_SIZE(process_group_attrs),
+       .ctrls          = process_group_ctrls,
+       .num_ctrls      = ARRAY_SIZE(process_group_ctrls),
+       .ops            = {
+               .init = process_group_init,
+               .exit = process_group_exit,
+       },
+};
+RESOURCE_DRIVER_REGISTER(&process_group_driver)