report-generator: refactor system report
[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_report(
148                 report_generator_system_t *generator,
149                 struct system_report *report)
150 {
151         ON_NULL_RETURN_VAL(generator, -1);
152         ON_NULL_RETURN_VAL(report, -1);
153
154         if (sys_stats_update(&generator->stats) != 0) {
155                 ERR("stats_update_system_stats failed.");
156                 return -1;
157         }
158
159         if (sys_stats_get_cpu_usage_percentage(&generator->stats, &report->cpu))
160         {
161                 ERR("stats_get_system_cpu_usage_average failed");
162                 return -1;
163         }
164
165         if (sys_stats_get_memory_usage_percentage(&generator->stats, &report->memory) != 0) {
166                 ERR("stats_get_system_memory_usage failed.");
167                 return -1;
168         }
169
170
171         report->time = clock_realtime_get();
172
173         return 0;
174 }
175
176 int report_generator_generate_process_cpu_usage_report(
177                 report_generator_process_t *generator,
178                 struct process_usage_report *report)
179 {
180         ON_NULL_RETURN_VAL(generator, -1);
181         ON_NULL_RETURN_VAL(report, -1);
182
183         float usage;
184
185         if (process_update(&generator->proc) != 0) {
186                 ERR("process_update failed.");
187                 return -1;
188         }
189
190         if (process_get_cpu_usage_percentage(&generator->proc, &usage) != 0) {
191                 ERR("process_get_cpu_usage_percentage failed.");
192                 return -1;
193         }
194
195         report->time = clock_realtime_get();
196         report->pid = generator->pid;
197         report->usage = usage;
198
199         return 0;
200 }
201
202 int report_generator_generate_process_memory_usage_report(
203                 report_generator_process_t *generator,
204                 struct process_usage_report *report)
205 {
206         ON_NULL_RETURN_VAL(generator, -1);
207         ON_NULL_RETURN_VAL(report, -1);
208
209         float usage;
210
211         if (process_get_memory_usage_percentage(&generator->proc, &usage) != 0) {
212                 ERR("process_get_memory_usage_percentage failed.");
213                 return -1;
214         }
215
216         report->time = clock_realtime_get();
217         report->usage = usage;
218
219         return 0;
220 }
221
222 int _app_report_generator_setup_process_generator(report_generator_app_t *generator)
223 {
224         int pid = app_info_provider_find_main_pid(generator->app_id);
225         if (pid < 0) {
226                 return -1;
227         }
228
229         if (!generator->process_gen || generator->process_gen->pid != pid) {
230                 if (generator->process_gen)
231                         report_generator_free_process_generator(generator->process_gen);
232                 generator->process_gen = report_generator_new_process_report_generator(pid);
233         }
234         return 0;
235 }
236
237 int report_generator_generate_app_cpu_usage_report(
238                 report_generator_app_t *generator,
239                 struct process_usage_report *report)
240 {
241         ON_NULL_RETURN_VAL(generator, -1);
242         ON_NULL_RETURN_VAL(report, -1);
243
244         if (_app_report_generator_setup_process_generator(generator) != 0) {
245                 ERR("_app_report_generator_setup_process_generator failed.");
246                 return -1;
247         }
248
249         if (report_generator_generate_process_cpu_usage_report(
250                                 generator->process_gen, report) != 0)
251         {
252                 ERR("report_generator_generate_process_cpu_usage_report failed.");
253                 return 0;
254         }
255
256         strncpy(report->app_id, generator->app_id, sizeof(report->app_id));
257         report->pid = generator->process_gen->pid;
258
259         return 0;
260 }
261
262 int report_generator_generate_app_memory_usage_report(
263                 report_generator_app_t *generator,
264                 struct process_usage_report *report)
265 {
266         ON_NULL_RETURN_VAL(generator, -1);
267         ON_NULL_RETURN_VAL(report, -1);
268
269         if (_app_report_generator_setup_process_generator(generator)) {
270                 ERR("_app_report_generator_setup_process_generator failed.");
271                 return -1;
272         }
273
274         if (report_generator_generate_process_memory_usage_report(
275                                 generator->process_gen, report) != 0)
276         {
277                 ERR("report_generator_generate_process_memory_usage_report failed.");
278                 return 0;
279         }
280
281         strncpy(report->app_id, generator->app_id, sizeof(report->app_id));
282         report->pid = generator->process_gen->pid;
283
284         return 0;
285 }
286
287 int report_generator_generate_load_average_report(struct system_load_average_report *report)
288 {
289         ON_NULL_RETURN_VAL(report, -1);
290
291         float a1, a5, a15;
292
293         if (sys_stats_get_load_averages(&a1, &a5, &a15) != 0) {
294                 ERR("stats_get_load_averages failed.");
295                 return -1;
296         }
297
298         report->time = clock_realtime_get();
299         report->one_min_avg = a1;
300         report->five_min_avg = a5;
301         report->fifteen_min_avg = a15;
302
303         return 0;
304 }
305
306 report_generator_top_t *report_generator_new_top_report_generator(report_generator_top_type_e type, int limit)
307 {
308         report_generator_top_t *gen = calloc(1, sizeof(report_generator_top_t));
309         if (!gen) {
310                 ERR("calloc failed.");
311                 return NULL;
312         }
313
314         gen->type = type;
315         gen->limit = limit;
316         gen->scanner = proc_scanner_new();
317         if (!gen->scanner) {
318                 report_generator_free_top_generator(gen);
319                 return NULL;
320         }
321         // run initial scan, so other next report will have valid data.
322         if (_report_generator_top_report_generator_scan(gen) != 0) {
323                 report_generator_free_top_generator(gen);
324                 return NULL;
325         }
326         if (sys_stats_update(&gen->sys_stats) != 0) {
327                 report_generator_free_top_generator(gen);
328                 return NULL;
329         }
330
331         return gen;
332 }
333
334 void report_generator_free_top_generator(report_generator_top_t *generator)
335 {
336         if (!generator) return;
337         proc_scanner_free(generator->scanner);
338         free(generator);
339 }
340
341 static bool _append_to_mem_report(struct process *proc, void *data)
342 {
343         struct report_generator_top_closure *closure = data;
344         struct process_usage_report report = {0,};
345         const char *appid;
346         int mem;
347
348         if (closure->current_index >= closure->max_index)
349                 return false;
350
351         if (process_get_memory_usage(proc, &mem) != 0) {
352                 report.usage = NAN;
353         } else {
354                 report.usage = (float)mem / closure->sys_stats.total_memory;
355         }
356
357         appid = process_get_appid(proc);
358         if (appid) strncpy(report.app_id, appid, sizeof(report.app_id));
359         report.pid = process_get_pid(proc);
360         report.time = clock_realtime_get();
361         closure->usage_report[closure->current_index++] = report;
362
363         return true;
364 }
365
366 static bool _append_to_cpu_report(struct process *proc, void *data)
367 {
368         struct report_generator_top_closure *closure = data;
369         struct process_usage_report report = {0,};
370         const char *appid;
371
372         if (closure->current_index >= closure->max_index)
373                 return false;
374
375         if (process_get_cpu_usage_percentage(proc, &report.usage) != 0) {
376                 report.usage = NAN;
377         }
378
379         appid = process_get_appid(proc);
380         if (appid) strncpy(report.app_id, appid, sizeof(report.app_id));
381         report.pid = process_get_pid(proc);
382         report.time = clock_realtime_get();
383         closure->usage_report[closure->current_index++] = report;
384
385         return true;
386 }
387
388 static int _sort_by_cpu_usage(struct process *proc1, struct process *proc2)
389 {
390         unsigned long long usage1, usage2;
391         if (process_get_cpu_usage(proc1, &usage1) != 0) {
392                 return 1;
393         }
394         if (process_get_cpu_usage(proc2, &usage2) != 0) {
395                 return -1;
396         }
397         return usage2 - usage1;
398 }
399
400 static int _sort_by_memory_usage(struct process *proc1, struct process *proc2)
401 {
402         int usage1, usage2;
403         if (process_get_memory_usage(proc1, &usage1) != 0) {
404                 return 1;
405         }
406         if (process_get_memory_usage(proc2, &usage2) != 0) {
407                 return -1;
408         }
409         return usage2 - usage1;
410 }
411
412 static int _report_generator_top_report_generator_scan_apps(
413                 report_generator_top_t *generator)
414 {
415         app_info_iterator_t *iter = app_info_provider_get_running_applications();
416         int i = 0;
417
418         if (!iter) {
419                 return -1;
420         }
421
422         int count = app_info_iterator_get_count(iter);
423         if (count < 1) {
424                 app_info_iterator_free(iter);
425                 return -1;
426         }
427
428         int *pids = calloc(count, sizeof(int));
429         if (!pids) {
430                 app_info_iterator_free(iter);
431                 return -1;
432         }
433
434         do {
435                 pids[i++] = app_info_iterator_get_pid(iter);
436         }
437         while (app_info_iterator_next(iter));
438
439         app_info_iterator_free(iter);
440
441         int ret = proc_scanner_scan_pids(generator->scanner, pids, i);
442         free(pids);
443         return ret;
444 }
445
446 static int _report_generator_top_report_generator_scan_all(
447                 report_generator_top_t *generator)
448 {
449         return proc_scanner_scan(generator->scanner);
450 }
451
452 static int _report_generator_top_report_generator_scan(
453                 report_generator_top_t *generator)
454 {
455         switch (generator->type) {
456                 case REPORT_GENERATOR_TOP_TYPE_APPS:
457                         return _report_generator_top_report_generator_scan_apps(generator);
458                 case REPORT_GENERATOR_TOP_TYPE_ALL:
459                         return _report_generator_top_report_generator_scan_all(generator);
460                         break;
461         }
462
463         return -1;
464 }
465
466 int report_generator_generate_top_cpu_report(
467                 report_generator_top_t *generator,
468                 struct process_usage_report **report,
469                 int *n_reports)
470 {
471         ON_NULL_RETURN_VAL(generator, -1);
472         ON_NULL_RETURN_VAL(report, -1);
473         ON_NULL_RETURN_VAL(n_reports, -1);
474
475         struct report_generator_top_closure closure = {0,};
476
477         if (sys_stats_update(&generator->sys_stats) != 0) {
478                 return -1;
479         }
480
481         if (_report_generator_top_report_generator_scan(generator) != 0) {
482                 return -1;
483         }
484
485         if (proc_scanner_sort_processes(generator->scanner, _sort_by_cpu_usage) != 0) {
486                 return -1;
487         }
488
489         closure.max_index = generator->limit;
490         closure.sys_stats = generator->sys_stats;
491         closure.usage_report = calloc(generator->limit, sizeof(struct process_usage_report));
492         if (!closure.usage_report) {
493                 return -1;
494         }
495
496         if (proc_scanner_foreach_process(generator->scanner, _append_to_cpu_report, &closure) != 0) {
497                 free(closure.usage_report);
498                 return -1;
499         }
500
501         *report = closure.usage_report;
502         *n_reports = closure.current_index;
503
504         return 0;
505 }
506
507 int report_generator_generate_top_memory_report(
508                 report_generator_top_t *generator,
509                 struct process_usage_report **report,
510                 int *n_reports)
511 {
512         ON_NULL_RETURN_VAL(generator, -1);
513         ON_NULL_RETURN_VAL(report, -1);
514         ON_NULL_RETURN_VAL(n_reports, -1);
515
516         struct report_generator_top_closure closure = {0,};
517
518         if (sys_stats_update(&generator->sys_stats) != 0) {
519                 return -1;
520         }
521
522         if (_report_generator_top_report_generator_scan(generator) != 0) {
523                 return -1;
524         }
525
526         if (proc_scanner_sort_processes(generator->scanner, _sort_by_memory_usage) != 0) {
527                 return -1;
528         }
529
530         closure.max_index = generator->limit;
531         closure.sys_stats = generator->sys_stats;
532         closure.usage_report = calloc(generator->limit, sizeof(struct process_usage_report));
533         if (!closure.usage_report) {
534                 return -1;
535         }
536
537         if (proc_scanner_foreach_process(generator->scanner, _append_to_mem_report, &closure) != 0) {
538                 free(closure.usage_report);
539                 return -1;
540         }
541
542         *report = closure.usage_report;
543         *n_reports = closure.current_index;
544
545         return 0;
546 }