report-generator: reafactor process 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 "sys-stats.h"
28 #include "clock.h"
29 #include "proc-scanner.h"
30 #include "process.h"
31 #include "json-schema-defs.h"
32
33 struct report_generator_system {
34         /** system cpu usage statistics */
35         struct sys_stats stats;
36 };
37
38 struct report_generator_process
39 {
40         proc_scanner_t *scanner;
41         report_generator_filter_cb filter;
42         void *user_data;
43         JsonBuilder *current_builder;
44 };
45
46 struct report_generator_top
47 {
48         struct sys_stats sys_stats;
49         proc_scanner_t *scanner;
50         report_generator_top_type_e type;
51         int limit;
52 };
53
54 struct report_generator_top_closure {
55         struct sys_stats sys_stats;
56         int current_index; int max_index;
57         struct process_usage_report *usage_report;
58 };
59
60 static int _report_generator_top_report_generator_scan(report_generator_top_t *generator);
61
62 report_generator_system_t *report_generator_new_system_report_generator()
63 {
64         report_generator_system_t *ret = calloc(1, sizeof(struct report_generator_system));
65         if (!ret)
66                 return NULL;
67
68         if (sys_stats_update(&ret->stats) != 0) {
69                 ERR("stats_update_system_stats failed");
70                 free(ret);
71                 return NULL;
72         }
73
74         return ret;
75 }
76
77 void report_generator_free_system_generator(report_generator_system_t *generator)
78 {
79         if (!generator)
80                 return;
81
82         free(generator);
83 }
84
85 report_generator_process_t *report_generator_new_process_report_generator()
86 {
87         report_generator_process_t *ret = calloc(1, sizeof(struct report_generator_process));
88         if (!ret)
89                 return NULL;
90
91         ret->scanner = proc_scanner_new();
92         if (!ret->scanner) goto err;
93
94         if (proc_scanner_scan(ret->scanner) != 0) {
95                 ERR("proc_scanner_scan failed");
96                 goto err;
97         }
98
99         return ret;
100
101 err:
102         proc_scanner_free(ret->scanner);
103         free(ret);
104         return NULL;
105 }
106
107 void report_generator_free_process_generator(report_generator_process_t *generator)
108 {
109         if (!generator) return;
110         proc_scanner_free(generator->scanner);
111         free(generator);
112 }
113
114 static bool _match_proc_and_append(struct process *proc, void *data)
115 {
116         report_generator_process_t *gen = data;
117
118         if (gen->filter && gen->filter(proc, gen->user_data)) {
119                 process_serialize(proc, gen->current_builder);
120         }
121
122         return true;
123 }
124
125 int report_generator_generate_process_report(
126                 report_generator_process_t *generator,
127                 JsonBuilder *builder)
128 {
129         ON_NULL_RETURN_VAL(generator, -1);
130         ON_NULL_RETURN_VAL(builder, -1);
131
132         // refresh data
133         if (proc_scanner_scan(generator->scanner) != 0) {
134                 ERR("proc_scanner_scan failed");
135                 return -1;
136         }
137
138         generator->current_builder = builder;
139         json_builder_begin_object(builder);
140
141         json_builder_set_member_name(builder, SCHEMA_TYPE);
142         json_builder_add_string_value(builder, SCHEMA_TYPE_PROCESS);
143
144         json_builder_set_member_name(builder, "time");
145         json_builder_add_int_value(builder, clock_realtime_get());
146
147         json_builder_set_member_name(builder, SCHEMA_RESULT_DATA_PROCESS);
148         json_builder_begin_array(builder);
149
150         proc_scanner_foreach_process(generator->scanner, _match_proc_and_append, generator);
151         json_builder_end_array(builder);
152
153         json_builder_end_object(builder); // end report
154
155         generator->current_builder = NULL;
156         return 0;
157 }
158
159 void report_generator_set_filter(
160                 report_generator_process_t *generator,
161                 report_generator_filter_cb cb, const void *user_data)
162 {
163         ON_NULL_RETURN(generator);
164         ON_NULL_RETURN(cb);
165
166         generator->filter = cb;
167         generator->user_data = (void*)user_data;
168 }
169
170 int report_generator_generate_system_report(
171                 report_generator_system_t *generator,
172                 struct system_report *report)
173 {
174         ON_NULL_RETURN_VAL(generator, -1);
175         ON_NULL_RETURN_VAL(report, -1);
176
177         if (sys_stats_update(&generator->stats) != 0) {
178                 ERR("stats_update_system_stats failed.");
179                 return -1;
180         }
181
182         if (sys_stats_get_cpu_usage_percentage(&generator->stats, &report->cpu))
183         {
184                 ERR("stats_get_system_cpu_usage_average failed");
185                 return -1;
186         }
187
188         if (sys_stats_get_memory_usage_percentage(&generator->stats, &report->memory) != 0) {
189                 ERR("stats_get_system_memory_usage failed.");
190                 return -1;
191         }
192
193         report->time = clock_realtime_get();
194
195         return 0;
196 }
197
198 int report_generator_generate_load_average_report(struct system_load_average_report *report)
199 {
200         ON_NULL_RETURN_VAL(report, -1);
201
202         float a1, a5, a15;
203
204         if (sys_stats_get_load_averages(&a1, &a5, &a15) != 0) {
205                 ERR("stats_get_load_averages failed.");
206                 return -1;
207         }
208
209         report->time = clock_realtime_get();
210         report->one_min_avg = a1;
211         report->five_min_avg = a5;
212         report->fifteen_min_avg = a15;
213
214         return 0;
215 }
216
217 report_generator_top_t *report_generator_new_top_report_generator(report_generator_top_type_e type, int limit)
218 {
219         report_generator_top_t *gen = calloc(1, sizeof(report_generator_top_t));
220         if (!gen) {
221                 ERR("calloc failed.");
222                 return NULL;
223         }
224
225         gen->type = type;
226         gen->limit = limit;
227         gen->scanner = proc_scanner_new();
228         if (!gen->scanner) {
229                 report_generator_free_top_generator(gen);
230                 return NULL;
231         }
232         // run initial scan, so other next report will have valid data.
233         if (_report_generator_top_report_generator_scan(gen) != 0) {
234                 report_generator_free_top_generator(gen);
235                 return NULL;
236         }
237         if (sys_stats_update(&gen->sys_stats) != 0) {
238                 report_generator_free_top_generator(gen);
239                 return NULL;
240         }
241
242         return gen;
243 }
244
245 void report_generator_free_top_generator(report_generator_top_t *generator)
246 {
247         if (!generator) return;
248         proc_scanner_free(generator->scanner);
249         free(generator);
250 }
251
252 static bool _append_to_mem_report(struct process *proc, void *data)
253 {
254         struct report_generator_top_closure *closure = data;
255         struct process_usage_report report = {0,};
256         const char *appid;
257         int mem;
258
259         if (closure->current_index >= closure->max_index)
260                 return false;
261
262         if (process_get_memory_usage(proc, &mem) != 0) {
263                 report.usage = NAN;
264         } else {
265                 report.usage = (float)mem / closure->sys_stats.total_memory;
266         }
267
268         appid = process_get_appid(proc);
269         if (appid) strncpy(report.app_id, appid, sizeof(report.app_id));
270         report.pid = process_get_pid(proc);
271         report.time = clock_realtime_get();
272         closure->usage_report[closure->current_index++] = report;
273
274         return true;
275 }
276
277 static bool _append_to_cpu_report(struct process *proc, void *data)
278 {
279         struct report_generator_top_closure *closure = data;
280         struct process_usage_report report = {0,};
281         const char *appid;
282
283         if (closure->current_index >= closure->max_index)
284                 return false;
285
286         if (process_get_cpu_usage_percentage(proc, &report.usage) != 0) {
287                 report.usage = NAN;
288         }
289
290         appid = process_get_appid(proc);
291         if (appid) strncpy(report.app_id, appid, sizeof(report.app_id));
292         report.pid = process_get_pid(proc);
293         report.time = clock_realtime_get();
294         closure->usage_report[closure->current_index++] = report;
295
296         return true;
297 }
298
299 static int _sort_by_cpu_usage(struct process *proc1, struct process *proc2)
300 {
301         unsigned long long usage1, usage2;
302         if (process_get_cpu_usage(proc1, &usage1) != 0) {
303                 return 1;
304         }
305         if (process_get_cpu_usage(proc2, &usage2) != 0) {
306                 return -1;
307         }
308         return usage2 - usage1;
309 }
310
311 static int _sort_by_memory_usage(struct process *proc1, struct process *proc2)
312 {
313         int usage1, usage2;
314         if (process_get_memory_usage(proc1, &usage1) != 0) {
315                 return 1;
316         }
317         if (process_get_memory_usage(proc2, &usage2) != 0) {
318                 return -1;
319         }
320         return usage2 - usage1;
321 }
322
323 static int _report_generator_top_report_generator_scan_apps(
324                 report_generator_top_t *generator)
325 {
326         app_info_iterator_t *iter = app_info_provider_get_running_applications();
327         int i = 0;
328
329         if (!iter) {
330                 return -1;
331         }
332
333         int count = app_info_iterator_get_count(iter);
334         if (count < 1) {
335                 app_info_iterator_free(iter);
336                 return -1;
337         }
338
339         int *pids = calloc(count, sizeof(int));
340         if (!pids) {
341                 app_info_iterator_free(iter);
342                 return -1;
343         }
344
345         do {
346                 pids[i++] = app_info_iterator_get_pid(iter);
347         }
348         while (app_info_iterator_next(iter));
349
350         app_info_iterator_free(iter);
351
352         int ret = proc_scanner_scan_pids(generator->scanner, pids, i);
353         free(pids);
354         return ret;
355 }
356
357 static int _report_generator_top_report_generator_scan_all(
358                 report_generator_top_t *generator)
359 {
360         return proc_scanner_scan(generator->scanner);
361 }
362
363 static int _report_generator_top_report_generator_scan(
364                 report_generator_top_t *generator)
365 {
366         switch (generator->type) {
367                 case REPORT_GENERATOR_TOP_TYPE_APPS:
368                         return _report_generator_top_report_generator_scan_apps(generator);
369                 case REPORT_GENERATOR_TOP_TYPE_ALL:
370                         return _report_generator_top_report_generator_scan_all(generator);
371                         break;
372         }
373
374         return -1;
375 }
376
377 int report_generator_generate_top_cpu_report(
378                 report_generator_top_t *generator,
379                 struct process_usage_report **report,
380                 int *n_reports)
381 {
382         ON_NULL_RETURN_VAL(generator, -1);
383         ON_NULL_RETURN_VAL(report, -1);
384         ON_NULL_RETURN_VAL(n_reports, -1);
385
386         struct report_generator_top_closure closure = {0,};
387
388         if (sys_stats_update(&generator->sys_stats) != 0) {
389                 return -1;
390         }
391
392         if (_report_generator_top_report_generator_scan(generator) != 0) {
393                 return -1;
394         }
395
396         if (proc_scanner_sort_processes(generator->scanner, _sort_by_cpu_usage) != 0) {
397                 return -1;
398         }
399
400         closure.max_index = generator->limit;
401         closure.sys_stats = generator->sys_stats;
402         closure.usage_report = calloc(generator->limit, sizeof(struct process_usage_report));
403         if (!closure.usage_report) {
404                 return -1;
405         }
406
407         if (proc_scanner_foreach_process(generator->scanner, _append_to_cpu_report, &closure) != 0) {
408                 free(closure.usage_report);
409                 return -1;
410         }
411
412         *report = closure.usage_report;
413         *n_reports = closure.current_index;
414
415         return 0;
416 }
417
418 int report_generator_generate_top_memory_report(
419                 report_generator_top_t *generator,
420                 struct process_usage_report **report,
421                 int *n_reports)
422 {
423         ON_NULL_RETURN_VAL(generator, -1);
424         ON_NULL_RETURN_VAL(report, -1);
425         ON_NULL_RETURN_VAL(n_reports, -1);
426
427         struct report_generator_top_closure closure = {0,};
428
429         if (sys_stats_update(&generator->sys_stats) != 0) {
430                 return -1;
431         }
432
433         if (_report_generator_top_report_generator_scan(generator) != 0) {
434                 return -1;
435         }
436
437         if (proc_scanner_sort_processes(generator->scanner, _sort_by_memory_usage) != 0) {
438                 return -1;
439         }
440
441         closure.max_index = generator->limit;
442         closure.sys_stats = generator->sys_stats;
443         closure.usage_report = calloc(generator->limit, sizeof(struct process_usage_report));
444         if (!closure.usage_report) {
445                 return -1;
446         }
447
448         if (proc_scanner_foreach_process(generator->scanner, _append_to_mem_report, &closure) != 0) {
449                 free(closure.usage_report);
450                 return -1;
451         }
452
453         *report = closure.usage_report;
454         *n_reports = closure.current_index;
455
456         return 0;
457 }