f386638b619c5de068bf0e1aab546178261d5a10
[platform/core/system/pass.git] / src / resource / resource-process.c
1 /*
2  * PASS (Power Aware System Service) - Process Resource Driver
3  *
4  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
5  *
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
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  */
18
19 #include <unistd.h>
20 #include <linux/taskstats.h>
21
22 #include <util/log.h>
23 #include <util/kernel.h>
24
25 #include <libsyscommon/resource-manager.h>
26 #include <libsyscommon/resource-type.h>
27
28 #include <resource-monitor/resource-monitor.h>
29
30 struct process_context {
31         pid_t tgid;
32         pid_t ppid;
33         pid_t pgid;
34         char comm[TS_COMM_LEN];
35         struct taskstats prev;
36         struct taskstats curr;
37         struct proc_map_info map_info;
38
39         u_int64_t prev_total_time;
40         double cpu_period;
41         int online_cpu;
42 };
43
44 static u_int64_t total_memory;
45 static long jiffy;
46 static bool taskstat_support;
47
48 static int process_get_cpu_util(struct syscommon_resman_resource *res,
49                                 const struct syscommon_resman_resource_attribute *attr,
50                                 void *data)
51 {
52         struct process_context *ctx;
53         struct taskstats *prev, *curr;
54         double *util = (double *)data;
55
56         if (!res || !attr || !data)
57                 return -EINVAL;
58
59         ctx = syscommon_resman_get_resource_privdata(res);
60         if (!ctx)
61                 return -EINVAL;
62
63         if (!ctx->tgid) {
64                 _E("resource %s is not yet initialized\n",
65                                 syscommon_resman_get_resource_name(res));
66                 return -EINVAL;
67         }
68
69         prev = &ctx->prev;
70         curr = &ctx->curr;
71
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);
77         }
78
79         return 0;
80 }
81
82 static int process_get_mem_attrs(struct syscommon_resman_resource *res,
83                                  const struct syscommon_resman_resource_attribute *attr,
84                                  void *data)
85 {
86         struct process_context *ctx;
87         struct taskstats *curr;
88         u_int64_t *mem = (u_int64_t *)data;
89
90         if (!res || !attr || !data)
91                 return -EINVAL;
92
93         ctx = syscommon_resman_get_resource_privdata(res);
94         if (!ctx)
95                 return -EINVAL;
96
97         if (!ctx->tgid) {
98                 _E("resource %s is not yet initialized\n",
99                                 syscommon_resman_get_resource_name(res));
100                 return -EINVAL;
101         }
102
103         switch (attr->id) {
104         case PROCESS_ATTR_MEM_VIRT:
105                 curr = &ctx->curr;
106                 *mem = (curr->virtmem * 1024) / curr->ac_stime;
107                 break;
108         case PROCESS_ATTR_MEM_RSS:
109                 curr = &ctx->curr;
110                 *mem = (curr->coremem * 1024) / curr->ac_stime;
111                 break;
112         case PROCESS_ATTR_MEM_RSS_PERCENT:
113         {
114                 double *percent = (double *)data;
115
116                 curr = &ctx->curr;
117                 *percent = (double)(curr->coremem * 1024) / curr->ac_stime;
118                 *percent /= total_memory * 100.0;
119                 break;
120         }
121         case PROCESS_ATTR_MEM_PSS:
122                 *mem = (u_int64_t)ctx->map_info.pss;
123                 break;
124         case PROCESS_ATTR_MEM_SWAP:
125                 *mem = (u_int64_t)ctx->map_info.swap;
126                 break;
127         case PROCESS_ATTR_MEM_SWAP_PSS:
128                 *mem = (u_int64_t)ctx->map_info.swap_pss;
129                 break;
130         case PROCESS_ATTR_MEM_GPU:
131                 *mem = (u_int64_t)ctx->map_info.gpu_mem;
132                 break;
133         default:
134                 return -EINVAL;
135         }
136
137         return 0;
138 }
139
140 static int process_get_disk_attrs(struct syscommon_resman_resource *res,
141                                 const struct syscommon_resman_resource_attribute *attr,
142                                 void *data)
143 {
144         struct process_context *ctx;
145         struct taskstats *prev, *curr;
146         u_int64_t period;
147
148         if (!res || !attr || !data)
149                 return -EINVAL;
150
151         ctx = syscommon_resman_get_resource_privdata(res);
152         if (!ctx)
153                 return -EINVAL;
154
155         if (!ctx->tgid) {
156                 _E("resource %s is not yet initialized\n",
157                                 syscommon_resman_get_resource_name(res));
158                 return -EINVAL;
159         }
160
161         prev = &ctx->prev;
162         curr = &ctx->curr;
163
164         period = curr->ac_etime - prev->ac_etime;
165
166         switch (attr->id) {
167         case PROCESS_ATTR_DISK_READ_PER_SEC:
168                 *(double *)data = (double)((curr->read_bytes - prev->read_bytes) * 1024) / period;
169                 break;
170         case PROCESS_ATTR_DISK_WRITE_PER_SEC:
171                 *(double *)data = (double)((curr->write_bytes - prev->write_bytes) * 1024) / period;
172                 break;
173         default:
174                 return -EINVAL;
175         }
176         return 0;
177 }
178
179 static int process_get_context_data(struct syscommon_resman_resource *res,
180                                 const struct syscommon_resman_resource_attribute *attr,
181                                 void *data)
182 {
183         struct process_context *ctx;
184
185         if (!res || !attr || !data)
186                 return -EINVAL;
187
188         ctx = syscommon_resman_get_resource_privdata(res);
189         if (!ctx)
190                 return -EINVAL;
191
192         if (!ctx->tgid) {
193                 _E("resource %s is not yet initialized\n",
194                                 syscommon_resman_get_resource_name(res));
195                 return -EINVAL;
196         }
197
198         switch (attr->id) {
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];
203                         int ret, len;
204
205                         ret = kernel_get_process_stat_fields(ctx->tgid, buffer,
206                                                              BUFF_MAX, stat_fields);
207                         if (ret < 0)
208                                 break;
209
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);
213                 }
214
215                 strncpy((char *)data, ctx->comm, TS_COMM_LEN);
216                 break;
217         case PROCESS_ATTR_PGID:
218                 if (ctx->pgid < 0) {
219                         char *stat_fields[PROCESS_STAT_FIELD_MAX];
220                         char buffer[BUFF_MAX];
221                         int ret;
222
223                         ret = kernel_get_process_stat_fields(ctx->tgid, buffer,
224                                                              BUFF_MAX, stat_fields);
225                         if (ret < 0)
226                                 break;
227
228                         ctx->pgid = atoi(stat_fields[PROCESS_STAT_FIELD_PGID]);
229                 }
230
231                 *((int *)data) = ctx->pgid;
232                 break;
233         case PROCESS_ATTR_PPID:
234                 if (ctx->ppid < 0) {
235                         char *stat_fields[PROCESS_STAT_FIELD_MAX];
236                         char buffer[BUFF_MAX];
237                         int ret;
238
239                         ret = kernel_get_process_stat_fields(ctx->tgid, buffer,
240                                                              BUFF_MAX, stat_fields);
241                         if (ret < 0)
242                                 break;
243
244                         ctx->ppid = atoi(stat_fields[PROCESS_STAT_FIELD_PPID]);
245                 }
246
247                 *((int *)data) = ctx->ppid;
248                 break;
249         }
250
251         return 0;
252 }
253
254 static bool process_check_taskstat_support(struct syscommon_resman_resource *resource,
255                                             const struct syscommon_resman_resource_attribute *attr)
256 {
257         return taskstat_support;
258 }
259
260 static bool process_check_gpu_support(struct syscommon_resman_resource *resource,
261                                             const struct syscommon_resman_resource_attribute *attr)
262 {
263         return kernel_check_gpu_support();
264 }
265
266 static const struct syscommon_resman_resource_attribute process_attrs[] = {
267         {
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,
272                 .ops    = {
273                         .get = process_get_cpu_util,
274                         .is_supported = process_check_taskstat_support,
275                 },
276         }, {
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,
281                 .ops    = {
282                         .get = process_get_mem_attrs,
283                         .is_supported = process_check_taskstat_support,
284                 },
285         }, {
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,
290                 .ops    = {
291                         .get = process_get_mem_attrs,
292                         .is_supported = process_check_taskstat_support,
293                 },
294         }, {
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,
299                 .ops    = {
300                         .get = process_get_mem_attrs,
301                         .is_supported = process_check_taskstat_support,
302                 },
303         }, {
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,
308                 .ops    = {
309                         .get = process_get_disk_attrs,
310                         .is_supported = process_check_taskstat_support,
311                 },
312         }, {
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,
317                 .ops    = {
318                         .get = process_get_disk_attrs,
319                         .is_supported = process_check_taskstat_support,
320                 },
321         }, {
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,
326                 .ops    = {
327                         .get = process_get_context_data,
328                         .is_supported = syscommon_resman_resource_attr_supported_always,
329                 },
330         }, {
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,
335                 .ops    = {
336                         .get = process_get_context_data,
337                         .is_supported = syscommon_resman_resource_attr_supported_always,
338                 },
339         }, {
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,
344                 .ops    = {
345                         .get = process_get_context_data,
346                         .is_supported = syscommon_resman_resource_attr_supported_always,
347                 },
348         }, {
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,
353                 .ops    = {
354                         .get = process_get_mem_attrs,
355                         .is_supported = syscommon_resman_resource_attr_supported_always,
356                 },
357         }, {
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,
362                 .ops    = {
363                         .get = process_get_mem_attrs,
364                         .is_supported = syscommon_resman_resource_attr_supported_always,
365                 },
366         }, {
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,
371                 .ops    = {
372                         .get = process_get_mem_attrs,
373                         .is_supported = syscommon_resman_resource_attr_supported_always,
374                 },
375         }, {
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,
380                 .ops    = {
381                         .get = process_get_mem_attrs,
382                         .is_supported = process_check_gpu_support,
383                 },
384         },
385 };
386
387 #define saturatingSub(a, b) (a > b ? a - b : 0)
388
389 static u_int64_t get_total_cpu_time(void)
390 {
391         struct cpu_stat cpu_stat;
392         u_int64_t total_time;
393         int ret;
394
395         ret = kernel_get_total_cpu_stat(&cpu_stat);
396         if (ret < 0) {
397                 _E("failed to get cpu stat");
398                 return 0;
399         };
400
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;
403
404         return total_time;
405 }
406
407 static int process_setup_tgid(struct syscommon_resman_resource *res,
408                                 const struct syscommon_resman_resource_control *ctrl,
409                                 const void *data)
410 {
411         struct process_context *ctx;
412         int ret;
413         bool include_gpu_mem;
414
415         if (!res || !ctrl)
416                 return -EINVAL;
417
418         ctx = syscommon_resman_get_resource_privdata(res);
419         if (!ctx)
420                 return -EINVAL;
421
422         include_gpu_mem = syscommon_resman_is_resource_attr_interested(res, PROCESS_ATTR_MEM_GPU);
423         memset(ctx, 0, sizeof(*ctx));
424
425         ctx->tgid = (pid_t)(intptr_t)data;
426         ctx->prev_total_time = get_total_cpu_time();
427
428         /* update initial status */
429         if (taskstat_support) {
430                 ret = kernel_get_thread_group_taskstats(&ctx->curr, ctx->tgid, true);
431                 if (ret < 0)
432                         return ret;
433
434                 memcpy(ctx->comm, ctx->curr.ac_comm, strlen(ctx->curr.ac_comm) + 1);
435                 ctx->ppid = ctx->curr.ac_ppid;
436         } else {
437                 /*
438                  * if taskstat is not supported, comm, pgid and ppid is retrieved
439                  * from procfs status lazily.
440                  *
441                  * ctx->comm is now "\0" so it is treated as not initialized.
442                  */
443                 ctx->ppid = -1;
444         }
445
446         ctx->pgid = -1;
447
448         ret = kernel_get_thread_group_map_info(&ctx->map_info, ctx->tgid, include_gpu_mem);
449         if (ret < 0)
450                 return ret;
451
452         return 0;
453 }
454
455 static const struct syscommon_resman_resource_control process_ctrls[] = {
456         {
457                 .name = "PROCESS_CTRL_TGID",
458                 .id = PROCESS_CTRL_TGID,
459                 .ops = {
460                         .set = process_setup_tgid,
461                 },
462         },
463 };
464
465 static int process_prepare_update(struct syscommon_resman_resource *res)
466 {
467         struct process_context *ctx;
468         u_int64_t total_time;
469         int ret, online;
470         bool include_gpu_mem;
471
472         if (!res)
473                 return -EINVAL;
474
475         ctx = syscommon_resman_get_resource_privdata(res);
476         if (!ctx)
477                 return -EINVAL;
478
479         include_gpu_mem = syscommon_resman_is_resource_attr_interested(res, PROCESS_ATTR_MEM_GPU);
480
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);
484                 if (ret < 0)
485                         return ret;
486         }
487
488         ret = kernel_get_thread_group_map_info(&ctx->map_info, ctx->tgid, include_gpu_mem);
489         if (ret < 0)
490                 return ret;
491
492         online = kernel_get_online_cpu_num();
493         total_time = get_total_cpu_time();
494
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;
498
499         return 0;
500 }
501
502 static int process_create(struct syscommon_resman_resource *res)
503 {
504         struct process_context *ctx;
505
506         ctx = calloc(1, sizeof(struct process_context));
507         if (!ctx)
508                 return -ENOMEM;
509
510         syscommon_resman_set_resource_privdata(res, ctx);
511
512         return 0;
513 }
514
515 static void process_delete(struct syscommon_resman_resource *res)
516 {
517         struct process_context *ctx;
518
519         if (!res)
520                 return;
521
522         ctx = syscommon_resman_get_resource_privdata(res);
523         if (!ctx)
524                 return;
525
526         free(ctx);
527         syscommon_resman_set_resource_privdata(res, NULL);
528 }
529
530 static int process_init(void)
531 {
532         int ret;
533
534         /* get system USER_HZ at once */
535         jiffy = sysconf(_SC_CLK_TCK);
536         if (jiffy < 0)
537                 return -EINVAL;
538
539         ret = kernel_get_memory_total(&total_memory);
540         if (ret < 0)
541                 return -EINVAL;
542
543         taskstat_support = kernel_check_taskstat_support();
544
545         return 0;
546 }
547
548 static const struct syscommon_resman_resource_driver process_resource_driver = {
549         .name           = "PROCESS",
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),
556         .ops = {
557                 .create = process_create,
558                 .delete = process_delete,
559                 .init = process_init,
560                 .prepare_update = process_prepare_update,
561         },
562 };
563 SYSCOMMON_RESMAN_RESOURCE_DRIVER_REGISTER(&process_resource_driver)