stats: rename to sys-stats + refactor
[apps/native/ttsd-worker-task.git] / src / report-generator.c
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Flora License, Version 1.1 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://floralicense.org/license/
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdlib.h>
18 #include <string.h>
19 #include <stdbool.h>
20 #include <unistd.h>
21 #include <math.h>
22
23 #include "report-generator.h"
24 #include "log.h"
25 #include "err-check.h"
26 #include "appinfo-provider.h"
27 #include "sys-stats.h"
28 #include "clock.h"
29 #include "proc-scanner.h"
30 #include "process.h"
31
32 struct report_generator_system {
33         /** system cpu usage statistics */
34         struct sys_stats stats;
35 };
36
37 struct report_generator_process
38 {
39         /** process pid */
40         int pid;
41         /** process statistics */
42         struct process proc;
43         /** update time */
44         float proc_update_time;
45 };
46
47 struct report_generator_app
48 {
49         char *app_id;
50         report_generator_process_t *process_gen;
51 };
52
53 struct report_generator_top
54 {
55         struct sys_stats sys_stats;
56         proc_scanner_t *scanner;
57         report_generator_top_type_e type;
58         int limit;
59 };
60
61 struct report_generator_top_closure {
62         struct sys_stats sys_stats;
63         int current_index;
64         int max_index;
65         struct process_usage_report *usage_report;
66 };
67
68 int _app_report_generator_setup_process_generator(report_generator_app_t *generator);
69 static int _report_generator_top_report_generator_scan(report_generator_top_t *generator);
70
71 report_generator_system_t *report_generator_new_system_report_generator()
72 {
73         report_generator_system_t *ret = calloc(1, sizeof(struct report_generator_system));
74         if (!ret)
75                 return NULL;
76
77         if (sys_stats_update(&ret->stats) != 0) {
78                 ERR("stats_update_system_stats failed");
79                 free(ret);
80                 return NULL;
81         }
82
83         return ret;
84 }
85
86 void report_generator_free_system_generator(report_generator_system_t *generator)
87 {
88         if (!generator)
89                 return;
90
91         free(generator);
92 }
93
94 report_generator_process_t *report_generator_new_process_report_generator(int pid)
95 {
96         ON_TRUE_RETURN_VAL(pid <= 0, NULL);
97
98         report_generator_process_t *ret = calloc(1, sizeof(struct report_generator_process));
99         if (!ret)
100                 return NULL;
101
102         process_init(pid, &ret->proc);
103
104         if (process_update(&ret->proc) != 0) {
105                 ERR("process_update failed.");
106                 free(ret);
107                 return NULL;
108         };
109
110         ret->proc_update_time = clock_monotonic_get();
111         ret->pid = pid;
112
113         return ret;
114 }
115
116 void report_generator_free_process_generator(report_generator_process_t *generator)
117 {
118         if (!generator)
119                 return;
120
121         free(generator);
122 }
123
124 report_generator_app_t *report_generator_new_app_report_generator(const char *app_id)
125 {
126         ON_NULL_RETURN_VAL(app_id, NULL);
127
128         report_generator_app_t *ret = calloc(1, sizeof(struct report_generator_app));
129         if (!ret) {
130                 ERR("malloc failed");
131                 return NULL;
132         }
133
134         ret->app_id = strdup(app_id);
135         _app_report_generator_setup_process_generator(ret);
136         return ret;
137 }
138
139 void report_generator_free_app_generator(report_generator_app_t *generator)
140 {
141         if (!generator) return;
142         report_generator_free_process_generator(generator->process_gen);
143         free(generator->app_id);
144         free(generator);
145 }
146
147 int report_generator_generate_system_cpu_usage_report(
148                 report_generator_system_t *generator,
149                 struct system_usage_report *report)
150 {
151         ON_NULL_RETURN_VAL(generator, -1);
152         ON_NULL_RETURN_VAL(report, -1);
153
154         float usage;
155
156         if (sys_stats_update(&generator->stats) != 0) {
157                 ERR("stats_update_system_stats failed.");
158                 return -1;
159         }
160
161         if (sys_stats_get_cpu_usage_percentage(&generator->stats, &usage))
162         {
163                 ERR("stats_get_system_cpu_usage_average failed");
164                 return -1;
165         }
166
167         report->usage = usage;
168         report->time = clock_realtime_get();
169
170         return 0;
171 }
172
173 int report_generator_generate_system_memory_usage_report(
174                 report_generator_system_t *generator,
175                 struct system_usage_report *report) {
176         ON_NULL_RETURN_VAL(generator, -1);
177         ON_NULL_RETURN_VAL(report, -1);
178
179         float usage;
180
181         if (sys_stats_update(&generator->stats) != 0) {
182                 ERR("stats_update_system_stats failed.");
183                 return -1;
184         }
185
186         if (sys_stats_get_memory_usage_percentage(&generator->stats, &usage) != 0) {
187                 ERR("stats_get_system_memory_usage failed.");
188                 return -1;
189         }
190
191         report->time = clock_realtime_get();
192         report->usage = usage;
193
194         return 0;
195 }
196
197 int report_generator_generate_process_cpu_usage_report(
198                 report_generator_process_t *generator,
199                 struct process_usage_report *report)
200 {
201         ON_NULL_RETURN_VAL(generator, -1);
202         ON_NULL_RETURN_VAL(report, -1);
203
204         float usage;
205
206         if (process_update(&generator->proc) != 0) {
207                 ERR("process_update failed.");
208                 return -1;
209         }
210
211         if (process_get_cpu_usage_percentage(&generator->proc, &usage) != 0) {
212                 ERR("process_get_cpu_usage_percentage failed.");
213                 return -1;
214         }
215
216         report->time = clock_realtime_get();
217         report->pid = generator->pid;
218         report->usage = usage;
219
220         return 0;
221 }
222
223 int report_generator_generate_process_memory_usage_report(
224                 report_generator_process_t *generator,
225                 struct process_usage_report *report)
226 {
227         ON_NULL_RETURN_VAL(generator, -1);
228         ON_NULL_RETURN_VAL(report, -1);
229
230         float usage;
231
232         if (process_get_memory_usage_percentage(&generator->proc, &usage) != 0) {
233                 ERR("process_get_memory_usage_percentage failed.");
234                 return -1;
235         }
236
237         report->time = clock_realtime_get();
238         report->usage = usage;
239
240         return 0;
241 }
242
243 int _app_report_generator_setup_process_generator(report_generator_app_t *generator)
244 {
245         int pid = app_info_provider_find_main_pid(generator->app_id);
246         if (pid < 0) {
247                 return -1;
248         }
249
250         if (!generator->process_gen || generator->process_gen->pid != pid) {
251                 if (generator->process_gen)
252                         report_generator_free_process_generator(generator->process_gen);
253                 generator->process_gen = report_generator_new_process_report_generator(pid);
254         }
255         return 0;
256 }
257
258 int report_generator_generate_app_cpu_usage_report(
259                 report_generator_app_t *generator,
260                 struct process_usage_report *report)
261 {
262         ON_NULL_RETURN_VAL(generator, -1);
263         ON_NULL_RETURN_VAL(report, -1);
264
265         if (_app_report_generator_setup_process_generator(generator) != 0) {
266                 ERR("_app_report_generator_setup_process_generator failed.");
267                 return -1;
268         }
269
270         if (report_generator_generate_process_cpu_usage_report(
271                                 generator->process_gen, report) != 0)
272         {
273                 ERR("report_generator_generate_process_cpu_usage_report failed.");
274                 return 0;
275         }
276
277         strncpy(report->app_id, generator->app_id, sizeof(report->app_id));
278         report->pid = generator->process_gen->pid;
279
280         return 0;
281 }
282
283 int report_generator_generate_app_memory_usage_report(
284                 report_generator_app_t *generator,
285                 struct process_usage_report *report)
286 {
287         ON_NULL_RETURN_VAL(generator, -1);
288         ON_NULL_RETURN_VAL(report, -1);
289
290         if (_app_report_generator_setup_process_generator(generator)) {
291                 ERR("_app_report_generator_setup_process_generator failed.");
292                 return -1;
293         }
294
295         if (report_generator_generate_process_memory_usage_report(
296                                 generator->process_gen, report) != 0)
297         {
298                 ERR("report_generator_generate_process_memory_usage_report failed.");
299                 return 0;
300         }
301
302         strncpy(report->app_id, generator->app_id, sizeof(report->app_id));
303         report->pid = generator->process_gen->pid;
304
305         return 0;
306 }
307
308 int report_generator_generate_load_average_report(struct system_load_average_report *report)
309 {
310         ON_NULL_RETURN_VAL(report, -1);
311
312         float a1, a5, a15;
313
314         if (sys_stats_get_load_averages(&a1, &a5, &a15) != 0) {
315                 ERR("stats_get_load_averages failed.");
316                 return -1;
317         }
318
319         report->time = clock_realtime_get();
320         report->one_min_avg = a1;
321         report->five_min_avg = a5;
322         report->fifteen_min_avg = a15;
323
324         return 0;
325 }
326
327 report_generator_top_t *report_generator_new_top_report_generator(report_generator_top_type_e type, int limit)
328 {
329         report_generator_top_t *gen = calloc(1, sizeof(report_generator_top_t));
330         if (!gen) {
331                 ERR("calloc failed.");
332                 return NULL;
333         }
334
335         gen->type = type;
336         gen->limit = limit;
337         gen->scanner = proc_scanner_new();
338         if (!gen->scanner) {
339                 report_generator_free_top_generator(gen);
340                 return NULL;
341         }
342         // run initial scan, so other next report will have valid data.
343         if (_report_generator_top_report_generator_scan(gen) != 0) {
344                 report_generator_free_top_generator(gen);
345                 return NULL;
346         }
347         if (sys_stats_update(&gen->sys_stats) != 0) {
348                 report_generator_free_top_generator(gen);
349                 return NULL;
350         }
351
352         return gen;
353 }
354
355 void report_generator_free_top_generator(report_generator_top_t *generator)
356 {
357         if (!generator) return;
358         proc_scanner_free(generator->scanner);
359         free(generator);
360 }
361
362 static bool _append_to_mem_report(struct process *proc, void *data)
363 {
364         struct report_generator_top_closure *closure = data;
365         struct process_usage_report report = {0,};
366         const char *appid;
367         int mem;
368
369         if (closure->current_index >= closure->max_index)
370                 return false;
371
372         if (process_get_memory_usage(proc, &mem) != 0) {
373                 report.usage = NAN;
374         } else {
375                 report.usage = (float)mem / closure->sys_stats.total_memory;
376         }
377
378         appid = process_get_appid(proc);
379         if (appid) strncpy(report.app_id, appid, sizeof(report.app_id));
380         report.pid = process_get_pid(proc);
381         report.time = clock_realtime_get();
382         closure->usage_report[closure->current_index++] = report;
383
384         return true;
385 }
386
387 static bool _append_to_cpu_report(struct process *proc, void *data)
388 {
389         struct report_generator_top_closure *closure = data;
390         struct process_usage_report report = {0,};
391         const char *appid;
392
393         if (closure->current_index >= closure->max_index)
394                 return false;
395
396         if (process_get_cpu_usage_percentage(proc, &report.usage) != 0) {
397                 report.usage = NAN;
398         }
399
400         appid = process_get_appid(proc);
401         if (appid) strncpy(report.app_id, appid, sizeof(report.app_id));
402         report.pid = process_get_pid(proc);
403         report.time = clock_realtime_get();
404         closure->usage_report[closure->current_index++] = report;
405
406         return true;
407 }
408
409 static int _sort_by_cpu_usage(struct process *proc1, struct process *proc2)
410 {
411         unsigned long long usage1, usage2;
412         if (process_get_cpu_usage(proc1, &usage1) != 0) {
413                 return 1;
414         }
415         if (process_get_cpu_usage(proc2, &usage2) != 0) {
416                 return -1;
417         }
418         return usage2 - usage1;
419 }
420
421 static int _sort_by_memory_usage(struct process *proc1, struct process *proc2)
422 {
423         int usage1, usage2;
424         if (process_get_memory_usage(proc1, &usage1) != 0) {
425                 return 1;
426         }
427         if (process_get_memory_usage(proc2, &usage2) != 0) {
428                 return -1;
429         }
430         return usage2 - usage1;
431 }
432
433 static int _report_generator_top_report_generator_scan_apps(
434                 report_generator_top_t *generator)
435 {
436         app_info_iterator_t *iter = app_info_provider_get_running_applications();
437         int i = 0;
438
439         if (!iter) {
440                 return -1;
441         }
442
443         int count = app_info_iterator_get_count(iter);
444         if (count < 1) {
445                 app_info_iterator_free(iter);
446                 return -1;
447         }
448
449         int *pids = calloc(count, sizeof(int));
450         if (!pids) {
451                 app_info_iterator_free(iter);
452                 return -1;
453         }
454
455         do {
456                 pids[i++] = app_info_iterator_get_pid(iter);
457         }
458         while (app_info_iterator_next(iter));
459
460         app_info_iterator_free(iter);
461
462         int ret = proc_scanner_scan_pids(generator->scanner, pids, i);
463         free(pids);
464         return ret;
465 }
466
467 static int _report_generator_top_report_generator_scan_all(
468                 report_generator_top_t *generator)
469 {
470         return proc_scanner_scan(generator->scanner);
471 }
472
473 static int _report_generator_top_report_generator_scan(
474                 report_generator_top_t *generator)
475 {
476         switch (generator->type) {
477                 case REPORT_GENERATOR_TOP_TYPE_APPS:
478                         return _report_generator_top_report_generator_scan_apps(generator);
479                 case REPORT_GENERATOR_TOP_TYPE_ALL:
480                         return _report_generator_top_report_generator_scan_all(generator);
481                         break;
482         }
483
484         return -1;
485 }
486
487 int report_generator_generate_top_cpu_report(
488                 report_generator_top_t *generator,
489                 struct process_usage_report **report,
490                 int *n_reports)
491 {
492         ON_NULL_RETURN_VAL(generator, -1);
493         ON_NULL_RETURN_VAL(report, -1);
494         ON_NULL_RETURN_VAL(n_reports, -1);
495
496         struct report_generator_top_closure closure = {0,};
497
498         if (sys_stats_update(&generator->sys_stats) != 0) {
499                 return -1;
500         }
501
502         if (_report_generator_top_report_generator_scan(generator) != 0) {
503                 return -1;
504         }
505
506         if (proc_scanner_sort_processes(generator->scanner, _sort_by_cpu_usage) != 0) {
507                 return -1;
508         }
509
510         closure.max_index = generator->limit;
511         closure.sys_stats = generator->sys_stats;
512         closure.usage_report = calloc(generator->limit, sizeof(struct process_usage_report));
513         if (!closure.usage_report) {
514                 return -1;
515         }
516
517         if (proc_scanner_foreach_process(generator->scanner, _append_to_cpu_report, &closure) != 0) {
518                 free(closure.usage_report);
519                 return -1;
520         }
521
522         *report = closure.usage_report;
523         *n_reports = closure.current_index;
524
525         return 0;
526 }
527
528 int report_generator_generate_top_memory_report(
529                 report_generator_top_t *generator,
530                 struct process_usage_report **report,
531                 int *n_reports)
532 {
533         ON_NULL_RETURN_VAL(generator, -1);
534         ON_NULL_RETURN_VAL(report, -1);
535         ON_NULL_RETURN_VAL(n_reports, -1);
536
537         struct report_generator_top_closure closure = {0,};
538
539         if (sys_stats_update(&generator->sys_stats) != 0) {
540                 return -1;
541         }
542
543         if (_report_generator_top_report_generator_scan(generator) != 0) {
544                 return -1;
545         }
546
547         if (proc_scanner_sort_processes(generator->scanner, _sort_by_memory_usage) != 0) {
548                 return -1;
549         }
550
551         closure.max_index = generator->limit;
552         closure.sys_stats = generator->sys_stats;
553         closure.usage_report = calloc(generator->limit, sizeof(struct process_usage_report));
554         if (!closure.usage_report) {
555                 return -1;
556         }
557
558         if (proc_scanner_foreach_process(generator->scanner, _append_to_mem_report, &closure) != 0) {
559                 free(closure.usage_report);
560                 return -1;
561         }
562
563         *report = closure.usage_report;
564         *n_reports = closure.current_index;
565
566         return 0;
567 }