2 * PASS (Power Aware System Service) - Process Resource Driver
4 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
20 #include <linux/taskstats.h>
23 #include <util/resource.h>
24 #include <util/kernel.h>
26 #include <resource-monitor/resource-monitor.h>
28 struct process_context {
32 char comm[TS_COMM_LEN];
33 struct taskstats prev;
34 struct taskstats curr;
35 struct proc_map_info map_info;
37 u_int64_t prev_total_time;
40 u_int64_t total_memory;
44 static int taskstat_support = -1;
46 static int process_get_cpu_util(struct resource *res,
47 const struct resource_attribute *attr,
50 struct process_context *ctx;
51 struct taskstats *prev, *curr;
52 double *util = (double *)data;
54 if (!res || !attr || !data)
57 ctx = get_resource_privdata(res);
62 _E("resource %s is not yet initialized\n",
63 get_resource_name(res));
70 if (ctx->cpu_period >= 1E-6) {
71 *util = (double)(curr->ac_utime + curr->ac_stime);
72 *util -= (double)(prev->ac_utime + prev->ac_stime);
73 *util *= (jiffy / (ctx->cpu_period * 10000.0));
74 *util = min(*util, 100.0);
80 static int process_get_mem_attrs(struct resource *res,
81 const struct resource_attribute *attr,
84 struct process_context *ctx;
85 struct taskstats *curr;
86 u_int64_t *mem = (u_int64_t *)data;
88 if (!res || !attr || !data)
91 ctx = get_resource_privdata(res);
96 _E("resource %s is not yet initialized\n",
97 get_resource_name(res));
102 case PROCESS_ATTR_MEM_VIRT:
104 *mem = (curr->virtmem * 1024) / curr->ac_stime;
106 case PROCESS_ATTR_MEM_RSS:
108 *mem = (curr->coremem * 1024) / curr->ac_stime;
110 case PROCESS_ATTR_MEM_RSS_PERCENT:
112 double *percent = (double *)data;
115 *percent = (curr->coremem * 1024) / curr->ac_stime;
116 *percent /= ctx->total_memory * 100.0;
119 case PROCESS_ATTR_MEM_PSS:
120 *mem = (u_int64_t)ctx->map_info.pss;
122 case PROCESS_ATTR_MEM_SWAP:
123 *mem = (u_int64_t)ctx->map_info.swap;
125 case PROCESS_ATTR_MEM_SWAP_PSS:
126 *mem = (u_int64_t)ctx->map_info.swap_pss;
128 case PROCESS_ATTR_MEM_GPU:
129 *mem = (u_int64_t)ctx->map_info.gpu_mem;
138 static int process_get_disk_attrs(struct resource *res,
139 const struct resource_attribute *attr,
142 struct process_context *ctx;
143 struct taskstats *prev, *curr;
146 if (!res || !attr || !data)
149 ctx = get_resource_privdata(res);
154 _E("resource %s is not yet initialized\n",
155 get_resource_name(res));
162 period = curr->ac_etime - prev->ac_etime;
165 case PROCESS_ATTR_DISK_READ_PER_SEC:
166 *(double *)data = (double)((curr->read_bytes - prev->read_bytes) * 1024) / period;
168 case PROCESS_ATTR_DISK_WRITE_PER_SEC:
169 *(double *)data = (double)((curr->write_bytes - prev->write_bytes) * 1024) / period;
177 static int process_get_context_data(struct resource *res,
178 const struct resource_attribute *attr,
181 struct process_context *ctx;
183 if (!res || !attr || !data)
186 ctx = get_resource_privdata(res);
191 _E("resource %s is not yet initialized\n",
192 get_resource_name(res));
197 case PROCESS_ATTR_NAME:
198 if (!taskstat_support && (strlen(ctx->comm) == 0)) {
199 char *stat_fields[PROCESS_STAT_FIELD_MAX];
200 char buffer[BUFF_MAX];
203 ret = kernel_get_process_stat_fields(ctx->tgid, buffer,
204 BUFF_MAX, stat_fields);
208 len = strlen(stat_fields[PROCESS_STAT_FIELD_COMM]) - 2/* '(', ')' */;
209 len = (len < TS_COMM_LEN - 1) ? len : TS_COMM_LEN - 1;
210 strncpy(ctx->comm, stat_fields[PROCESS_STAT_FIELD_COMM] + 1, len);
213 strncpy((char *)data, ctx->comm, TS_COMM_LEN);
215 case PROCESS_ATTR_PGID:
217 char *stat_fields[PROCESS_STAT_FIELD_MAX];
218 char buffer[BUFF_MAX];
221 ret = kernel_get_process_stat_fields(ctx->tgid, buffer,
222 BUFF_MAX, stat_fields);
226 ctx->pgid = atoi(stat_fields[PROCESS_STAT_FIELD_PGID]);
229 *((int *)data) = ctx->pgid;
231 case PROCESS_ATTR_PPID:
233 char *stat_fields[PROCESS_STAT_FIELD_MAX];
234 char buffer[BUFF_MAX];
237 ret = kernel_get_process_stat_fields(ctx->tgid, buffer,
238 BUFF_MAX, stat_fields);
242 ctx->ppid = atoi(stat_fields[PROCESS_STAT_FIELD_PPID]);
245 *((int *)data) = ctx->ppid;
252 static bool process_check_taskstat_support(struct resource *resource,
253 const struct resource_attribute *attr)
255 return !!taskstat_support;
258 static bool process_check_gpu_support(struct resource *resource,
259 const struct resource_attribute *attr)
261 return kernel_check_gpu_support();
264 static const struct resource_attribute process_attrs[] = {
266 .name = "PROCESS_ATTR_CPU_UTIL",
267 .id = PROCESS_ATTR_CPU_UTIL,
268 .type = DATA_TYPE_DOUBLE,
270 .get = process_get_cpu_util,
271 .is_supported = process_check_taskstat_support,
274 .name = "PROCESS_ATTR_MEM_VIRT",
275 .id = PROCESS_ATTR_MEM_VIRT,
276 .type = DATA_TYPE_UINT64,
278 .get = process_get_mem_attrs,
279 .is_supported = process_check_taskstat_support,
282 .name = "PROCESS_ATTR_MEM_RSS",
283 .id = PROCESS_ATTR_MEM_RSS,
284 .type = DATA_TYPE_UINT64,
286 .get = process_get_mem_attrs,
287 .is_supported = process_check_taskstat_support,
290 .name = "PROCESS_ATTR_MEM_RSS_PERCENT",
291 .id = PROCESS_ATTR_MEM_RSS_PERCENT,
292 .type = DATA_TYPE_DOUBLE,
294 .get = process_get_mem_attrs,
295 .is_supported = process_check_taskstat_support,
298 .name = "PROCESS_ATTR_DISK_READ_PER_SEC",
299 .id = PROCESS_ATTR_DISK_READ_PER_SEC,
300 .type = DATA_TYPE_DOUBLE,
302 .get = process_get_disk_attrs,
303 .is_supported = process_check_taskstat_support,
306 .name = "PROCESS_ATTR_DISK_WRITE_PER_SEC",
307 .id = PROCESS_ATTR_DISK_WRITE_PER_SEC,
308 .type = DATA_TYPE_DOUBLE,
310 .get = process_get_disk_attrs,
311 .is_supported = process_check_taskstat_support,
314 .name = "PROCESS_ATTR_NAME",
315 .id = PROCESS_ATTR_NAME,
316 .type = DATA_TYPE_STRING,
318 .get = process_get_context_data,
319 .is_supported = resource_attr_supported_always,
322 .name = "PROCESS_ATTR_PGID",
323 .id = PROCESS_ATTR_PGID,
324 .type = DATA_TYPE_INT,
326 .get = process_get_context_data,
327 .is_supported = resource_attr_supported_always,
330 .name = "PROCESS_ATTR_PPID",
331 .id = PROCESS_ATTR_PPID,
332 .type = DATA_TYPE_INT,
334 .get = process_get_context_data,
335 .is_supported = resource_attr_supported_always,
338 .name = "PROCESS_ATTR_MEM_PSS",
339 .id = PROCESS_ATTR_MEM_PSS,
340 .type = DATA_TYPE_UINT64,
342 .get = process_get_mem_attrs,
343 .is_supported = resource_attr_supported_always,
346 .name = "PROCESS_ATTR_MEM_SWAP",
347 .id = PROCESS_ATTR_MEM_SWAP,
348 .type = DATA_TYPE_UINT64,
350 .get = process_get_mem_attrs,
351 .is_supported = resource_attr_supported_always,
354 .name = "PROCESS_ATTR_MEM_SWAP_PSS",
355 .id = PROCESS_ATTR_MEM_SWAP_PSS,
356 .type = DATA_TYPE_UINT64,
358 .get = process_get_mem_attrs,
359 .is_supported = resource_attr_supported_always,
362 .name = "PROCESS_ATTR_MEM_GPU",
363 .id = PROCESS_ATTR_MEM_GPU,
364 .type = DATA_TYPE_UINT64,
366 .get = process_get_mem_attrs,
367 .is_supported = process_check_gpu_support,
372 #define saturatingSub(a, b) (a > b ? a - b : 0)
374 static u_int64_t get_total_cpu_time(void)
376 struct cpu_stat cpu_stat;
377 u_int64_t total_time;
380 ret = kernel_get_total_cpu_stat(&cpu_stat);
382 _E("failed to get cpu stat");
386 total_time = cpu_stat.user + cpu_stat.system + cpu_stat.nice + cpu_stat.idle;
387 total_time += cpu_stat.wait + cpu_stat.hard_irq + cpu_stat.soft_irq;
392 static int process_setup_tgid(struct resource *res,
393 const struct resource_control *ctrl,
396 struct process_context *ctx;
397 u_int64_t total_memory;
399 bool include_gpu_mem;
404 ctx = get_resource_privdata(res);
408 include_gpu_mem = is_resource_attr_interested(res, PROCESS_GROUP_ATTR_MEM_GPU);
409 total_memory = ctx->total_memory;
410 memset(ctx, 0, sizeof(*ctx));
412 ctx->total_memory = total_memory;
413 ctx->tgid = (pid_t)(intptr_t)data;
414 ctx->prev_total_time = get_total_cpu_time();
416 /* update initial status */
417 if (taskstat_support) {
418 ret = kernel_get_thread_group_taskstats(&ctx->curr, ctx->tgid, true);
422 memcpy(ctx->comm, ctx->curr.ac_comm, strlen(ctx->curr.ac_comm) + 1);
423 ctx->ppid = ctx->curr.ac_ppid;
426 * if taskstat is not supported, comm, pgid and ppid is retrieved
427 * from procfs status lazily.
429 * ctx->comm is now "\0" so it is treated as not initialized.
436 ret = kernel_get_thread_group_map_info(&ctx->map_info, ctx->tgid, include_gpu_mem);
443 static const struct resource_control process_ctrls[] = {
445 .name = "PROCESS_CTRL_TGID",
446 .id = PROCESS_CTRL_TGID,
448 .set = process_setup_tgid,
453 static int process_prepare_update(struct resource *res)
455 struct process_context *ctx;
456 u_int64_t total_time;
458 bool include_gpu_mem;
463 ctx = get_resource_privdata(res);
467 include_gpu_mem = is_resource_attr_interested(res, PROCESS_GROUP_ATTR_MEM_GPU);
469 if (taskstat_support) {
470 memcpy(&ctx->prev, &ctx->curr, sizeof(struct taskstats));
471 ret = kernel_get_thread_group_taskstats(&ctx->curr, ctx->tgid, false);
476 ret = kernel_get_thread_group_map_info(&ctx->map_info, ctx->tgid, include_gpu_mem);
480 online = kernel_get_online_cpu_num();
481 total_time = get_total_cpu_time();
483 ctx->online_cpu = online;
484 ctx->cpu_period = (double)saturatingSub(total_time, ctx->prev_total_time) / online;
485 ctx->prev_total_time = total_time;
490 static int process_init(struct resource *res)
492 struct process_context *ctx;
496 /* get system USER_HZ at once */
497 jiffy = sysconf(_SC_CLK_TCK);
502 if (taskstat_support < 0)
503 taskstat_support = (int)kernel_check_taskstat_support();
505 ctx = calloc(1, sizeof(struct process_context));
509 ret = kernel_get_memory_total(&ctx->total_memory);
515 set_resource_privdata(res, ctx);
520 static void process_exit(struct resource *res)
522 struct process_context *ctx;
527 ctx = get_resource_privdata(res);
532 set_resource_privdata(res, NULL);
535 static const struct resource_driver process_resource_driver = {
537 .type = RESOURCE_TYPE_PROCESS,
538 .flag = RESOURCE_FLAG_PROCESS,
539 .attrs = process_attrs,
540 .num_attrs = ARRAY_SIZE(process_attrs),
541 .ctrls = process_ctrls,
542 .num_ctrls = ARRAY_SIZE(process_ctrls),
544 .init = process_init,
545 .exit = process_exit,
546 .prepare_update = process_prepare_update,
549 RESOURCE_DRIVER_REGISTER(&process_resource_driver)