report-generator: implement top report generator
[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 "stats.h"
28 #include "clock.h"
29 #include "proc-scanner.h"
30
31 struct report_generator_system {
32         /** system cpu usage statistics */
33         struct stats_system previous;
34 };
35
36 struct report_generator_process
37 {
38         /** process pid */
39         int pid;
40         /** process cpu usage statistics */
41         struct stats_process previous;
42 };
43
44 struct report_generator_app
45 {
46         char *app_id;
47         report_generator_process_t *process_gen;
48 };
49
50 struct report_generator_top
51 {
52         struct stats_system sys_stats;
53         proc_scanner_t *scanner;
54         report_generator_top_type_e type;
55         int limit;
56 };
57
58 struct report_generator_top_closure {
59         struct stats_system sys_stats;
60         int current_index;
61         int max_index;
62         struct app_cpu_usage_report *cpu_report;
63         struct app_memory_usage_report *mem_report;
64 };
65
66 int _app_report_generator_setup_process_generator(report_generator_app_t *generator);
67 static int _report_generator_top_report_generator_scan(report_generator_top_t *generator);
68
69 report_generator_system_t *report_generator_new_system_report_generator()
70 {
71         report_generator_system_t *ret = calloc(1, sizeof(struct report_generator_system));
72         if (!ret)
73                 return NULL;
74
75         if (stats_update_system_stats(&ret->previous) != 0) {
76                 ERR("stats_update_system_stats failed");
77                 free(ret);
78                 return NULL;
79         }
80
81         return ret;
82 }
83
84 void report_generator_free_system_generator(report_generator_system_t *generator)
85 {
86         if (!generator)
87                 return;
88
89         free(generator);
90 }
91
92 report_generator_process_t *report_generator_new_process_report_generator(int pid)
93 {
94         ON_TRUE_RETURN_VAL(pid <= 0, NULL);
95
96         report_generator_process_t *ret = calloc(1, sizeof(struct report_generator_process));
97         if (!ret)
98                 return NULL;
99
100         if (stats_update_process_stats(pid, &ret->previous) != 0) {
101                 ERR("stats_update_process_stats failed.");
102                 free(ret);
103                 return NULL;
104         };
105
106         ret->pid = pid;
107
108         return ret;
109 }
110
111 void report_generator_free_process_generator(report_generator_process_t *generator)
112 {
113         if (!generator)
114                 return;
115
116         free(generator);
117 }
118
119 report_generator_app_t *report_generator_new_app_report_generator(const char *app_id)
120 {
121         ON_NULL_RETURN_VAL(app_id, NULL);
122
123         report_generator_app_t *ret = calloc(1, sizeof(struct report_generator_app));
124         if (!ret) {
125                 ERR("malloc failed");
126                 return NULL;
127         }
128
129         ret->app_id = strdup(app_id);
130         _app_report_generator_setup_process_generator(ret);
131         return ret;
132 }
133
134 void report_generator_free_app_generator(report_generator_app_t *generator)
135 {
136         if (!generator) return;
137         report_generator_free_process_generator(generator->process_gen);
138         free(generator->app_id);
139         free(generator);
140 }
141
142 int report_generator_generate_system_cpu_usage_report(
143                 report_generator_system_t *generator,
144                 int interval,
145                 struct system_cpu_usage_report *report)
146 {
147         ON_NULL_RETURN_VAL(generator, -1);
148         ON_TRUE_RETURN_VAL(interval < 0, -1);
149         ON_NULL_RETURN_VAL(report, -1);
150
151         float usage;
152         bool block = interval > 0;
153         struct stats_system current;
154
155         if (block) {
156                 if (stats_update_system_stats(&generator->previous) != 0) {
157                         ERR("stats_update_system_stats failed.");
158                         return -1;
159                 }
160                 sleep(interval);
161         }
162
163         if (stats_update_system_stats(&current) != 0) {
164                 ERR("stats_update_system_stats failed.");
165                 return -1;
166         }
167
168         if (stats_get_system_cpu_usage_average(&generator->previous, &current, &usage))
169         {
170                 ERR("stats_get_system_cpu_usage_average failed");
171                 return -1;
172         }
173
174         report->usage = usage;
175         report->time = clock_realtime_get().tv_sec;
176
177         generator->previous = current;
178
179         return 0;
180 }
181
182 int report_generator_generate_system_memory_usage_report(
183                 report_generator_system_t *generator,
184                 struct system_memory_usage_report *report) {
185         ON_NULL_RETURN_VAL(generator, -1);
186         ON_NULL_RETURN_VAL(report, -1);
187
188         float usage;
189
190         if (stats_update_system_stats(&generator->previous) != 0) {
191                 ERR("stats_update_system_stats failed.");
192                 return -1;
193         }
194
195         if (stats_get_system_memory_usage(&generator->previous, &usage) != 0) {
196                 ERR("stats_get_system_memory_usage failed.");
197                 return -1;
198         }
199
200         report->time = clock_realtime_get().tv_sec;
201         report->usage = usage;
202
203         return 0;
204 }
205
206 int report_generator_generate_process_cpu_usage_report(
207                 report_generator_process_t *generator,
208                 int interval,
209                 struct process_cpu_usage_report *report)
210 {
211         ON_NULL_RETURN_VAL(generator, -1);
212         ON_TRUE_RETURN_VAL(interval < 0, -1);
213         ON_NULL_RETURN_VAL(report, -1);
214
215         struct stats_process current = {0,};
216         bool block = interval > 0;
217         float usage;
218
219         if (block) {
220                 if (stats_update_process_stats(generator->pid, &generator->previous) != 0) {
221                         ERR("stats_update_process_stats failed.");
222                         return -1;
223                 }
224                 sleep(interval);
225         }
226
227         if (stats_update_process_stats(generator->pid, &current) != 0) {
228                 ERR("stats_update_process_stats failed.");
229                 return -1;
230         }
231
232         if (stats_get_process_cpu_usage_average(&generator->previous, &current, &usage) ) {
233                 ERR("stats_update_process_stats failed.");
234                 return -1;
235         }
236
237         report->time = clock_realtime_get().tv_sec;
238         report->pid = generator->pid;
239         report->usage = usage;
240
241         generator->previous = current;
242
243         return 0;
244 }
245
246 int report_generator_generate_process_memory_usage_report(
247                 report_generator_process_t *generator,
248                 struct process_memory_usage_report *report)
249 {
250         ON_NULL_RETURN_VAL(generator, -1);
251         ON_NULL_RETURN_VAL(report, -1);
252
253         float usage;
254
255         if (stats_get_process_memory_usage(generator->pid, &usage) != 0) {
256                 ERR("stats_get_process_memory_usage failed.");
257                 return -1;
258         }
259
260         report->time = clock_realtime_get().tv_sec;
261         report->usage = usage;
262
263         return 0;
264 }
265
266 int _app_report_generator_setup_process_generator(report_generator_app_t *generator)
267 {
268         int pid = app_info_provider_find_main_pid(generator->app_id);
269         if (pid < 0) {
270                 return -1;
271         }
272
273         if (!generator->process_gen || generator->process_gen->pid != pid) {
274                 if (generator->process_gen)
275                         report_generator_free_process_generator(generator->process_gen);
276                 generator->process_gen = report_generator_new_process_report_generator(pid);
277         }
278         return 0;
279 }
280
281 int report_generator_generate_app_cpu_usage_report(
282                 report_generator_app_t *generator,
283                 int interval,
284                 struct app_cpu_usage_report *report)
285 {
286         ON_NULL_RETURN_VAL(generator, -1);
287         ON_TRUE_RETURN_VAL(interval < 0, -1);
288         ON_NULL_RETURN_VAL(report, -1);
289
290         if (_app_report_generator_setup_process_generator(generator) != 0) {
291                 ERR("_app_report_generator_setup_process_generator failed.");
292                 return -1;
293         }
294
295         if (report_generator_generate_process_cpu_usage_report(
296                                 generator->process_gen, interval, &report->process_report) != 0)
297         {
298                 ERR("report_generator_generate_process_cpu_usage_report failed.");
299                 return 0;
300         }
301
302         strncpy(report->app_id, generator->app_id, sizeof(report->app_id));
303         report->process_report.pid = generator->process_gen->pid;
304
305         return 0;
306 }
307
308 int report_generator_generate_app_memory_usage_report(
309                 report_generator_app_t *generator,
310                 struct app_memory_usage_report *report)
311 {
312         ON_NULL_RETURN_VAL(generator, -1);
313         ON_NULL_RETURN_VAL(report, -1);
314
315         if (_app_report_generator_setup_process_generator(generator)) {
316                 ERR("_app_report_generator_setup_process_generator failed.");
317                 return -1;
318         }
319
320         if (report_generator_generate_process_memory_usage_report(
321                                 generator->process_gen, &report->process_report) != 0)
322         {
323                 ERR("report_generator_generate_process_memory_usage_report failed.");
324                 return 0;
325         }
326
327         strncpy(report->app_id, generator->app_id, sizeof(report->app_id));
328         report->process_report.pid = generator->process_gen->pid;
329
330         return 0;
331 }
332
333 int report_generator_generate_load_average_report(struct system_load_average_report *report)
334 {
335         ON_NULL_RETURN_VAL(report, -1);
336
337         float a1, a5, a15;
338
339         if (stats_get_load_averages(&a1, &a5, &a15) != 0) {
340                 ERR("stats_get_load_averages failed.");
341                 return -1;
342         }
343
344         report->time = clock_realtime_get().tv_sec;
345         report->one_min_avg = a1;
346         report->five_min_avg = a5;
347         report->fifteen_min_avg = a15;
348
349         return 0;
350 }
351
352 report_generator_top_t *report_generator_new_top_report_generator(report_generator_top_type_e type, int limit)
353 {
354         report_generator_top_t *gen = calloc(1, sizeof(report_generator_top_t));
355         if (!gen) {
356                 ERR("calloc failed.");
357                 return NULL;
358         }
359
360         gen->type = type;
361         gen->limit = limit;
362         gen->scanner = proc_scanner_new();
363         if (!gen->scanner) {
364                 report_generator_free_top_generator(gen);
365                 return NULL;
366         }
367         // run initial scan, so other next report will have valid data.
368         if (_report_generator_top_report_generator_scan(gen) != 0) {
369                 report_generator_free_top_generator(gen);
370                 return NULL;
371         }
372         if (stats_update_system_stats(&gen->sys_stats) != 0) {
373                 report_generator_free_top_generator(gen);
374                 return NULL;
375         }
376
377         return gen;
378 }
379
380 void report_generator_free_top_generator(report_generator_top_t *generator)
381 {
382         if (!generator) return;
383         proc_scanner_free(generator->scanner);
384         free(generator);
385 }
386
387 static bool _append_to_mem_report(struct process *proc, void *data)
388 {
389         struct report_generator_top_closure *closure = data;
390         struct app_memory_usage_report report = {0,};
391         const char *appid;
392         int mem;
393
394         if (closure->current_index >= closure->max_index)
395                 return false;
396
397         if (process_get_memory_usage(proc, &mem) != 0) {
398                 report.process_report.usage = NAN;
399         } else {
400                 report.process_report.usage = (float)mem / closure->sys_stats.total_memory;
401         }
402
403         appid = process_get_appid(proc);
404         if (appid) strncpy(report.app_id, appid, sizeof(report.app_id));
405         report.process_report.pid = process_get_pid(proc);
406         report.process_report.time = clock_realtime_get().tv_sec;
407         closure->mem_report[closure->current_index++] = report;
408
409         return true;
410 }
411
412 static bool _append_to_cpu_report(struct process *proc, void *data)
413 {
414         struct report_generator_top_closure *closure = data;
415         struct app_cpu_usage_report report = {0,};
416         const char *appid;
417         unsigned long long ticks = 0;
418
419         if (closure->current_index >= closure->max_index)
420                 return false;
421
422         if (process_get_cpu_usage(proc, &ticks) != 0) {
423                 report.process_report.usage = NAN;
424         } else {
425                 report.process_report.usage = stats_get_cpu_usage_percentage(ticks, closure->sys_stats.frame_time);
426         }
427
428         appid = process_get_appid(proc);
429         if (appid) strncpy(report.app_id, appid, sizeof(report.app_id));
430         report.process_report.pid = process_get_pid(proc);
431         report.process_report.time = clock_realtime_get().tv_sec;
432         closure->cpu_report[closure->current_index++] = report;
433
434         return true;
435 }
436
437 static int _sort_by_cpu_usage(struct process *proc1, struct process *proc2)
438 {
439         unsigned long long usage1, usage2;
440         if (process_get_cpu_usage(proc1, &usage1) != 0) {
441                 return 1;
442         }
443         if (process_get_cpu_usage(proc2, &usage2) != 0) {
444                 return -1;
445         }
446         return usage2 - usage1;
447 }
448
449 static int _sort_by_memory_usage(struct process *proc1, struct process *proc2)
450 {
451         int usage1, usage2;
452         if (process_get_memory_usage(proc1, &usage1) != 0) {
453                 return 1;
454         }
455         if (process_get_memory_usage(proc2, &usage2) != 0) {
456                 return -1;
457         }
458         return usage2 - usage1;
459 }
460
461 static int _report_generator_top_report_generator_scan_apps(
462                 report_generator_top_t *generator)
463 {
464         app_info_iterator_t *iter = app_info_provider_get_running_applications();
465         int i = 0;
466
467         if (!iter) {
468                 return -1;
469         }
470
471         int count = app_info_iterator_get_count(iter);
472         if (count < 1) {
473                 app_info_iterator_free(iter);
474                 return -1;
475         }
476
477         int *pids = calloc(count, sizeof(int));
478         if (!pids) {
479                 app_info_iterator_free(iter);
480                 return -1;
481         }
482
483         do {
484                 pids[i++] = app_info_iterator_get_pid(iter);
485         }
486         while (app_info_iterator_next(iter));
487
488         app_info_iterator_free(iter);
489
490         return proc_scanner_scan_pids(generator->scanner, pids, count);
491 }
492
493 static int _report_generator_top_report_generator_scan_all(
494                 report_generator_top_t *generator)
495 {
496         return proc_scanner_scan(generator->scanner);
497 }
498
499 static int _report_generator_top_report_generator_scan(
500                 report_generator_top_t *generator)
501 {
502         switch (generator->type) {
503                 case REPORT_GENERATOR_TOP_TYPE_APPS:
504                         return _report_generator_top_report_generator_scan_apps(generator);
505                 case REPORT_GENERATOR_TOP_TYPE_ALL:
506                         return _report_generator_top_report_generator_scan_all(generator);
507                         break;
508         }
509
510         return -1;
511 }
512
513 int report_generator_generate_top_cpu_report(
514                 report_generator_top_t *generator,
515                 struct app_cpu_usage_report **report,
516                 int *n_reports)
517 {
518         ON_NULL_RETURN_VAL(generator, -1);
519         ON_NULL_RETURN_VAL(report, -1);
520         ON_NULL_RETURN_VAL(n_reports, -1);
521
522         struct report_generator_top_closure closure = {0,};
523
524         if (stats_update_system_stats(&generator->sys_stats) != 0) {
525                 return -1;
526         }
527
528         if (_report_generator_top_report_generator_scan(generator) != 0) {
529                 return -1;
530         }
531
532         if (proc_scanner_sort_processes(generator->scanner, _sort_by_cpu_usage) != 0) {
533                 return -1;
534         }
535
536         closure.max_index = generator->limit;
537         closure.sys_stats = generator->sys_stats;
538         closure.cpu_report = calloc(generator->limit, sizeof(struct app_cpu_usage_report));
539         if (!closure.cpu_report) {
540                 return -1;
541         }
542
543         if (proc_scanner_foreach_process(generator->scanner, _append_to_cpu_report, &closure) != 0) {
544                 free(closure.cpu_report);
545                 return -1;
546         }
547
548         *report = closure.cpu_report;
549         *n_reports = closure.current_index;
550
551         return 0;
552 }
553
554 int report_generator_generate_top_memory_report(
555                 report_generator_top_t *generator,
556                 struct app_memory_usage_report **report,
557                 int *n_reports)
558 {
559         ON_NULL_RETURN_VAL(generator, -1);
560         ON_NULL_RETURN_VAL(report, -1);
561         ON_NULL_RETURN_VAL(n_reports, -1);
562
563         struct report_generator_top_closure closure = {0,};
564
565         if (stats_update_system_stats(&generator->sys_stats) != 0) {
566                 return -1;
567         }
568
569         if (_report_generator_top_report_generator_scan(generator) != 0) {
570                 return -1;
571         }
572
573         if (proc_scanner_sort_processes(generator->scanner, _sort_by_memory_usage) != 0) {
574                 return -1;
575         }
576
577         closure.max_index = generator->limit;
578         closure.sys_stats = generator->sys_stats;
579         closure.mem_report = calloc(generator->limit, sizeof(struct app_memory_usage_report));
580         if (!closure.mem_report) {
581                 return -1;
582         }
583
584         if (proc_scanner_foreach_process(generator->scanner, _append_to_mem_report, &closure) != 0) {
585                 free(closure.mem_report);
586                 return -1;
587         }
588
589         *report = closure.mem_report;
590         *n_reports = closure.current_index;
591
592         return 0;
593 }