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/kernel.h>
25 #include <libsyscommon/resource-manager.h>
26 #include <libsyscommon/resource-type.h>
28 #include <resource-monitor/resource-monitor.h>
30 struct process_context {
34 char comm[TS_COMM_LEN];
35 struct taskstats prev;
36 struct taskstats curr;
37 struct proc_map_info map_info;
39 u_int64_t prev_total_time;
44 static u_int64_t total_memory;
46 static bool taskstat_support;
48 static int process_get_cpu_util(struct syscommon_resman_resource *res,
49 const struct syscommon_resman_resource_attribute *attr,
52 struct process_context *ctx;
53 struct taskstats *prev, *curr;
54 double *util = (double *)data;
56 if (!res || !attr || !data)
59 ctx = syscommon_resman_get_resource_privdata(res);
64 _E("resource %s is not yet initialized\n",
65 syscommon_resman_get_resource_name(res));
72 if (ctx->cpu_period >= 1E-6) {
73 *util = (double)(curr->ac_utime + curr->ac_stime);
74 *util -= (double)(prev->ac_utime + prev->ac_stime);
75 *util *= (jiffy / (ctx->cpu_period * 10000.0));
76 *util = min(*util, 100.0);
82 static int process_get_mem_attrs(struct syscommon_resman_resource *res,
83 const struct syscommon_resman_resource_attribute *attr,
86 struct process_context *ctx;
87 struct taskstats *curr;
88 u_int64_t *mem = (u_int64_t *)data;
90 if (!res || !attr || !data)
93 ctx = syscommon_resman_get_resource_privdata(res);
98 _E("resource %s is not yet initialized\n",
99 syscommon_resman_get_resource_name(res));
104 case PROCESS_ATTR_MEM_VIRT:
106 *mem = (curr->virtmem * 1024) / curr->ac_stime;
108 case PROCESS_ATTR_MEM_RSS:
110 *mem = (curr->coremem * 1024) / curr->ac_stime;
112 case PROCESS_ATTR_MEM_RSS_PERCENT:
114 double *percent = (double *)data;
117 *percent = (double)(curr->coremem * 1024) / curr->ac_stime;
118 *percent /= total_memory * 100.0;
121 case PROCESS_ATTR_MEM_PSS:
122 *mem = (u_int64_t)ctx->map_info.pss;
124 case PROCESS_ATTR_MEM_SWAP:
125 *mem = (u_int64_t)ctx->map_info.swap;
127 case PROCESS_ATTR_MEM_SWAP_PSS:
128 *mem = (u_int64_t)ctx->map_info.swap_pss;
130 case PROCESS_ATTR_MEM_GPU:
131 *mem = (u_int64_t)ctx->map_info.gpu_mem;
140 static int process_get_disk_attrs(struct syscommon_resman_resource *res,
141 const struct syscommon_resman_resource_attribute *attr,
144 struct process_context *ctx;
145 struct taskstats *prev, *curr;
148 if (!res || !attr || !data)
151 ctx = syscommon_resman_get_resource_privdata(res);
156 _E("resource %s is not yet initialized\n",
157 syscommon_resman_get_resource_name(res));
164 period = curr->ac_etime - prev->ac_etime;
167 case PROCESS_ATTR_DISK_READ_PER_SEC:
168 *(double *)data = (double)((curr->read_bytes - prev->read_bytes) * 1024) / period;
170 case PROCESS_ATTR_DISK_WRITE_PER_SEC:
171 *(double *)data = (double)((curr->write_bytes - prev->write_bytes) * 1024) / period;
179 static int process_get_context_data(struct syscommon_resman_resource *res,
180 const struct syscommon_resman_resource_attribute *attr,
183 struct process_context *ctx;
185 if (!res || !attr || !data)
188 ctx = syscommon_resman_get_resource_privdata(res);
193 _E("resource %s is not yet initialized\n",
194 syscommon_resman_get_resource_name(res));
199 case PROCESS_ATTR_NAME:
200 if (!taskstat_support && (strlen(ctx->comm) == 0)) {
201 char *stat_fields[PROCESS_STAT_FIELD_MAX];
202 char buffer[BUFF_MAX];
205 ret = kernel_get_process_stat_fields(ctx->tgid, buffer,
206 BUFF_MAX, stat_fields);
210 len = strlen(stat_fields[PROCESS_STAT_FIELD_COMM]) - 2/* '(', ')' */;
211 len = (len < TS_COMM_LEN - 1) ? len : TS_COMM_LEN - 1;
212 strncpy(ctx->comm, stat_fields[PROCESS_STAT_FIELD_COMM] + 1, len);
215 strncpy((char *)data, ctx->comm, TS_COMM_LEN);
217 case PROCESS_ATTR_PGID:
219 char *stat_fields[PROCESS_STAT_FIELD_MAX];
220 char buffer[BUFF_MAX];
223 ret = kernel_get_process_stat_fields(ctx->tgid, buffer,
224 BUFF_MAX, stat_fields);
228 ctx->pgid = atoi(stat_fields[PROCESS_STAT_FIELD_PGID]);
231 *((int *)data) = ctx->pgid;
233 case PROCESS_ATTR_PPID:
235 char *stat_fields[PROCESS_STAT_FIELD_MAX];
236 char buffer[BUFF_MAX];
239 ret = kernel_get_process_stat_fields(ctx->tgid, buffer,
240 BUFF_MAX, stat_fields);
244 ctx->ppid = atoi(stat_fields[PROCESS_STAT_FIELD_PPID]);
247 *((int *)data) = ctx->ppid;
254 static bool process_check_taskstat_support(struct syscommon_resman_resource *resource,
255 const struct syscommon_resman_resource_attribute *attr)
257 return taskstat_support;
260 static bool process_check_gpu_support(struct syscommon_resman_resource *resource,
261 const struct syscommon_resman_resource_attribute *attr)
263 return kernel_check_gpu_support();
266 static const struct syscommon_resman_resource_attribute process_attrs[] = {
268 .name = "PROCESS_ATTR_CPU_UTIL",
269 .id = PROCESS_ATTR_CPU_UTIL,
270 .type = SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE,
271 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE,
273 .get = process_get_cpu_util,
274 .is_supported = process_check_taskstat_support,
277 .name = "PROCESS_ATTR_MEM_VIRT",
278 .id = PROCESS_ATTR_MEM_VIRT,
279 .type = SYSCOMMON_RESMAN_DATA_TYPE_UINT64,
280 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE,
282 .get = process_get_mem_attrs,
283 .is_supported = process_check_taskstat_support,
286 .name = "PROCESS_ATTR_MEM_RSS",
287 .id = PROCESS_ATTR_MEM_RSS,
288 .type = SYSCOMMON_RESMAN_DATA_TYPE_UINT64,
289 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE,
291 .get = process_get_mem_attrs,
292 .is_supported = process_check_taskstat_support,
295 .name = "PROCESS_ATTR_MEM_RSS_PERCENT",
296 .id = PROCESS_ATTR_MEM_RSS_PERCENT,
297 .type = SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE,
298 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE,
300 .get = process_get_mem_attrs,
301 .is_supported = process_check_taskstat_support,
304 .name = "PROCESS_ATTR_DISK_READ_PER_SEC",
305 .id = PROCESS_ATTR_DISK_READ_PER_SEC,
306 .type = SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE,
307 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE,
309 .get = process_get_disk_attrs,
310 .is_supported = process_check_taskstat_support,
313 .name = "PROCESS_ATTR_DISK_WRITE_PER_SEC",
314 .id = PROCESS_ATTR_DISK_WRITE_PER_SEC,
315 .type = SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE,
316 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE,
318 .get = process_get_disk_attrs,
319 .is_supported = process_check_taskstat_support,
322 .name = "PROCESS_ATTR_NAME",
323 .id = PROCESS_ATTR_NAME,
324 .type = SYSCOMMON_RESMAN_DATA_TYPE_STRING,
325 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE,
327 .get = process_get_context_data,
328 .is_supported = syscommon_resman_resource_attr_supported_always,
331 .name = "PROCESS_ATTR_PGID",
332 .id = PROCESS_ATTR_PGID,
333 .type = SYSCOMMON_RESMAN_DATA_TYPE_INT,
334 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE,
336 .get = process_get_context_data,
337 .is_supported = syscommon_resman_resource_attr_supported_always,
340 .name = "PROCESS_ATTR_PPID",
341 .id = PROCESS_ATTR_PPID,
342 .type = SYSCOMMON_RESMAN_DATA_TYPE_INT,
343 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE,
345 .get = process_get_context_data,
346 .is_supported = syscommon_resman_resource_attr_supported_always,
349 .name = "PROCESS_ATTR_MEM_PSS",
350 .id = PROCESS_ATTR_MEM_PSS,
351 .type = SYSCOMMON_RESMAN_DATA_TYPE_UINT64,
352 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE,
354 .get = process_get_mem_attrs,
355 .is_supported = syscommon_resman_resource_attr_supported_always,
358 .name = "PROCESS_ATTR_MEM_SWAP",
359 .id = PROCESS_ATTR_MEM_SWAP,
360 .type = SYSCOMMON_RESMAN_DATA_TYPE_UINT64,
361 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE,
363 .get = process_get_mem_attrs,
364 .is_supported = syscommon_resman_resource_attr_supported_always,
367 .name = "PROCESS_ATTR_MEM_SWAP_PSS",
368 .id = PROCESS_ATTR_MEM_SWAP_PSS,
369 .type = SYSCOMMON_RESMAN_DATA_TYPE_UINT64,
370 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE,
372 .get = process_get_mem_attrs,
373 .is_supported = syscommon_resman_resource_attr_supported_always,
376 .name = "PROCESS_ATTR_MEM_GPU",
377 .id = PROCESS_ATTR_MEM_GPU,
378 .type = SYSCOMMON_RESMAN_DATA_TYPE_UINT64,
379 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE,
381 .get = process_get_mem_attrs,
382 .is_supported = process_check_gpu_support,
387 #define saturatingSub(a, b) (a > b ? a - b : 0)
389 static u_int64_t get_total_cpu_time(void)
391 struct cpu_stat cpu_stat;
392 u_int64_t total_time;
395 ret = kernel_get_total_cpu_stat(&cpu_stat);
397 _E("failed to get cpu stat");
401 total_time = cpu_stat.user + cpu_stat.system + cpu_stat.nice + cpu_stat.idle;
402 total_time += cpu_stat.wait + cpu_stat.hard_irq + cpu_stat.soft_irq;
407 static int process_setup_tgid(struct syscommon_resman_resource *res,
408 const struct syscommon_resman_resource_control *ctrl,
411 struct process_context *ctx;
413 bool include_gpu_mem;
418 ctx = syscommon_resman_get_resource_privdata(res);
422 include_gpu_mem = syscommon_resman_is_resource_attr_interested(res, PROCESS_ATTR_MEM_GPU);
423 memset(ctx, 0, sizeof(*ctx));
425 ctx->tgid = (pid_t)(intptr_t)data;
426 ctx->prev_total_time = get_total_cpu_time();
428 /* update initial status */
429 if (taskstat_support) {
430 ret = kernel_get_thread_group_taskstats(&ctx->curr, ctx->tgid, true);
434 memcpy(ctx->comm, ctx->curr.ac_comm, strlen(ctx->curr.ac_comm) + 1);
435 ctx->ppid = ctx->curr.ac_ppid;
438 * if taskstat is not supported, comm, pgid and ppid is retrieved
439 * from procfs status lazily.
441 * ctx->comm is now "\0" so it is treated as not initialized.
448 ret = kernel_get_thread_group_map_info(&ctx->map_info, ctx->tgid, include_gpu_mem);
455 static const struct syscommon_resman_resource_control process_ctrls[] = {
457 .name = "PROCESS_CTRL_TGID",
458 .id = PROCESS_CTRL_TGID,
460 .set = process_setup_tgid,
465 static int process_prepare_update(struct syscommon_resman_resource *res)
467 struct process_context *ctx;
468 u_int64_t total_time;
470 bool include_gpu_mem;
475 ctx = syscommon_resman_get_resource_privdata(res);
479 include_gpu_mem = syscommon_resman_is_resource_attr_interested(res, PROCESS_ATTR_MEM_GPU);
481 if (taskstat_support) {
482 memcpy(&ctx->prev, &ctx->curr, sizeof(struct taskstats));
483 ret = kernel_get_thread_group_taskstats(&ctx->curr, ctx->tgid, false);
488 ret = kernel_get_thread_group_map_info(&ctx->map_info, ctx->tgid, include_gpu_mem);
492 online = kernel_get_online_cpu_num();
493 total_time = get_total_cpu_time();
495 ctx->online_cpu = online;
496 ctx->cpu_period = (double)saturatingSub(total_time, ctx->prev_total_time) / online;
497 ctx->prev_total_time = total_time;
502 static int process_create(struct syscommon_resman_resource *res)
504 struct process_context *ctx;
506 ctx = calloc(1, sizeof(struct process_context));
510 syscommon_resman_set_resource_privdata(res, ctx);
515 static void process_delete(struct syscommon_resman_resource *res)
517 struct process_context *ctx;
522 ctx = syscommon_resman_get_resource_privdata(res);
527 syscommon_resman_set_resource_privdata(res, NULL);
530 static int process_init(void)
534 /* get system USER_HZ at once */
535 jiffy = sysconf(_SC_CLK_TCK);
539 ret = kernel_get_memory_total(&total_memory);
543 taskstat_support = kernel_check_taskstat_support();
548 static const struct syscommon_resman_resource_driver process_resource_driver = {
550 .type = RESOURCE_TYPE_PROCESS,
551 .flag = SYSCOMMON_RESMAN_RESOURCE_DRIVER_FLAG_UNCOUNTABLE,
552 .attrs = process_attrs,
553 .num_attrs = ARRAY_SIZE(process_attrs),
554 .ctrls = process_ctrls,
555 .num_ctrls = ARRAY_SIZE(process_ctrls),
557 .create = process_create,
558 .delete = process_delete,
559 .init = process_init,
560 .prepare_update = process_prepare_update,
563 SYSCOMMON_RESMAN_RESOURCE_DRIVER_REGISTER(&process_resource_driver)