report-generator: implement app_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 <assert.h>
19 #include <string.h>
20 #include <stdbool.h>
21 #include <unistd.h>
22
23 #include "report-generator.h"
24 #include "procfs.h"
25 #include "log.h"
26 #include "err-check.h"
27 #include "appinfo-provider.h"
28
29 struct report_generator_system {
30         /** previous read from procfs */
31         struct procfs_system_cpu_usage_info previous;
32 };
33
34 struct process_ticks_snapshot
35 {
36         unsigned long long system_ticks;
37         unsigned long long process_ticks;
38 };
39
40 struct report_generator_process
41 {
42         /** process pid */
43         int pid;
44         /** previous process stats */
45         struct process_ticks_snapshot previous;
46 };
47
48 struct report_generator_app
49 {
50         char *app_id;
51         report_generator_process_t *process_gen;
52 };
53
54 static int _report_generator_read_process_ticks(int pid, struct process_ticks_snapshot *ticks);
55 int _app_report_generator_setup_process_generator(report_generator_app_t *generator);
56
57 static struct timespec clock_get_monotonic()
58 {
59         struct timespec ret = {0,};
60
61         if (clock_gettime(CLOCK_MONOTONIC, &ret) != 0) {
62                 ERR("Platform do not support monotonic clock type");
63                 //TODO consider adding first init function to evaluate
64                 //if clock_gettime can be used, so calling module could
65                 //handle cases without monotonic clock gracefully.
66                 abort();
67         }
68
69         return ret;
70 }
71
72 report_generator_system_t *report_generator_new_system_report_generator()
73 {
74         report_generator_system_t *ret = calloc(1, sizeof(struct report_generator_system));
75         if (!ret)
76                 return NULL;
77
78         if (procfs_read_system_cpu_usage(&ret->previous) != 0) {
79                 ERR("procfs_read_system_cpu_usage failed");
80                 free(ret);
81                 return NULL;
82         }
83
84         return ret;
85 }
86
87 void report_generator_free_system_generator(report_generator_system_t *generator)
88 {
89         if (!generator)
90                 return;
91
92         free(generator);
93 }
94
95 report_generator_process_t *report_generator_new_process_report_generator(int pid)
96 {
97         ON_TRUE_RETURN_VAL(pid <= 0, NULL);
98
99         report_generator_process_t *ret = calloc(1, sizeof(struct report_generator_process));
100         if (!ret)
101                 return NULL;
102
103         if (_report_generator_read_process_ticks(pid, &ret->previous) != 0) {
104                 ERR("_report_generator_read_process_ticks failed.");
105                 free(ret);
106                 return NULL;
107         };
108
109         ret->pid = pid;
110
111         return ret;
112 }
113
114 void report_generator_free_process_generator(report_generator_process_t *generator)
115 {
116         if (!generator)
117                 return;
118
119         free(generator);
120 }
121
122 report_generator_app_t *report_generator_new_app_report_generator(const char *app_id)
123 {
124         ON_NULL_RETURN_VAL(app_id, NULL);
125
126         report_generator_app_t *ret = malloc(sizeof(struct report_generator_app));
127         if (!ret) {
128                 ERR("malloc failed");
129                 return NULL;
130         }
131
132         ret->app_id = strdup(app_id);
133         _app_report_generator_setup_process_generator(ret);
134         return ret;
135 }
136
137 void report_generator_free_app_generator(report_generator_app_t *generator)
138 {
139         if (!generator) return;
140         report_generator_free_process_generator(generator->process_gen);
141         free(generator->app_id);
142         free(generator);
143 }
144
145 static void _calculate_system_cpu_usage(
146                 struct procfs_system_cpu_usage_info *previous,
147                 struct procfs_system_cpu_usage_info *current,
148                 float *usage)
149 {
150         struct procfs_system_cpu_usage_info diff_ticks = { 0,};
151
152         diff_ticks.user = current->user > previous->user ? current->user - previous->user : 0;
153         diff_ticks.system = current->system > previous->system ? current->system - previous->system : 0;
154         diff_ticks.nice = current->nice > previous->nice ? current->nice - previous->nice : 0;
155         diff_ticks.idle = current->idle > previous->idle ? current->idle - previous->idle : 0;
156         diff_ticks.iowait = current->iowait > previous->iowait ? current->iowait - previous->iowait : 0;
157         diff_ticks.irq = current->irq > previous->irq ? current->irq - previous->irq : 0;
158         diff_ticks.softirq = current->softirq > previous->softirq ? current->softirq - previous->softirq : 0;
159
160         unsigned long long total = diff_ticks.user + diff_ticks.system + diff_ticks.nice +
161                 diff_ticks.idle + diff_ticks.iowait + diff_ticks.irq + diff_ticks.softirq;
162
163         unsigned long long busy = diff_ticks.user + diff_ticks.system + diff_ticks.nice +
164                 diff_ticks.irq + diff_ticks.softirq;
165
166         if (total == 0) {
167                 *usage = 0;
168         } else {
169                 *usage = (float)busy / total;
170         }
171 }
172
173 int report_generator_generate_system_cpu_usage_report(
174                 report_generator_system_t *generator,
175                 int interval,
176                 struct system_cpu_usage_report *report)
177 {
178         ON_NULL_RETURN_VAL(generator, -1);
179         ON_TRUE_RETURN_VAL(interval < 0, -1);
180         ON_NULL_RETURN_VAL(report, -1);
181
182         struct procfs_system_cpu_usage_info current = {0,};
183         float usage;
184         bool block = interval > 0;
185
186         if (block) {
187                 if (procfs_read_system_cpu_usage(&generator->previous) != 0) {
188                         ERR("procfs_read_system_cpu_usage failed.");
189                         return -1;
190                 }
191                 sleep(interval);
192         }
193
194         if (procfs_read_system_cpu_usage(&current) != 0) {
195                 ERR("procfs_read_system_cpu_usage failed.");
196                 return -1;
197         }
198
199         _calculate_system_cpu_usage(&generator->previous, &current, &usage);
200
201         report->usage = usage;
202         report->time = clock_get_monotonic().tv_sec;
203
204         generator->previous = current;
205
206         return 0;
207 }
208
209 int report_generator_generate_system_memory_usage_report(
210                 report_generator_system_t *generator,
211                 struct system_memory_usage_report *report) {
212         ON_NULL_RETURN_VAL(generator, -1);
213         ON_NULL_RETURN_VAL(report, -1);
214
215         struct procfs_system_memory_usage_info mem_usage;
216
217         if (procfs_read_system_memory_usage(&mem_usage) != 0) {
218                 ERR("procfs_read_system_memory_usage failed.");
219                 return -1;
220         }
221
222         report->time = clock_get_monotonic().tv_sec;
223         report->usage = (float)mem_usage.used / mem_usage.total;
224
225         return 0;
226 }
227
228 /**
229  * Calculates system average cpu usage between previous and current
230  * snapshots.
231  */
232 static void _calculate_process_cpu_usage(struct process_ticks_snapshot *previous,
233                 struct process_ticks_snapshot *current, int ncpus, float *usage)
234 {
235         struct process_ticks_snapshot diff;
236
237         diff.process_ticks = current->process_ticks > previous->process_ticks ? current->process_ticks - previous->process_ticks: 0;
238         diff.system_ticks = current->system_ticks > previous->system_ticks ? current->system_ticks - previous->system_ticks : 0;
239
240         if (diff.system_ticks == 0)
241                 *usage = 0;
242         else
243                 *usage = (float)diff.process_ticks / diff.system_ticks * ncpus;
244 }
245
246 static unsigned long long
247 _system_total_time(const struct procfs_system_cpu_usage_info *info)
248 {
249         return info->user + info->system + info->nice + info->idle;
250 }
251
252 static int
253 _report_generator_read_process_ticks(int pid, struct process_ticks_snapshot *ticks)
254 {
255         struct procfs_system_cpu_usage_info system_ticks;
256         struct procfs_process_cpu_usage_info process_ticks;
257
258         if (procfs_read_process_cpu_usage(pid, &process_ticks) != 0) {
259                 ERR("procfs_read_process_cpu_usage failed.");
260                 return -1;
261         }
262
263         if (procfs_read_system_cpu_usage(&system_ticks) != 0) {
264                 ERR("procfs_read_system_cpu_usage failed.");
265                 return -1;
266         }
267
268         ticks->process_ticks = process_ticks.stime + process_ticks.utime;
269         ticks->system_ticks = _system_total_time(&system_ticks);
270
271         return 0;
272 }
273
274 int report_generator_generate_process_cpu_usage_report(
275                 report_generator_process_t *generator,
276                 int interval,
277                 struct process_cpu_usage_report *report)
278 {
279         ON_NULL_RETURN_VAL(generator, -1);
280         ON_TRUE_RETURN_VAL(interval < 0, -1);
281         ON_NULL_RETURN_VAL(report, -1);
282
283         struct process_ticks_snapshot current;
284         bool block = interval > 0;
285         float usage;
286         int ncpus;
287
288         if (block) {
289                 if (_report_generator_read_process_ticks(generator->pid, &generator->previous) != 0) {
290                         ERR("Unable to update process cpu ticks.");
291                         return -1;
292                 }
293                 sleep(interval);
294         }
295
296         if (_report_generator_read_process_ticks(generator->pid, &current) != 0) {
297                 ERR("Unable to update process cpu ticks.");
298                 return -1;
299         }
300
301         if (procfs_read_cpu_count(&ncpus) != 0) {
302                 ERR("procfs_read_cpu_count failed.");
303                 return -1;
304         }
305
306         _calculate_process_cpu_usage(&generator->previous, &current, ncpus, &usage);
307
308         report->time = clock_get_monotonic().tv_sec;
309         report->pid = generator->pid;
310         report->usage = usage;
311
312         generator->previous = current;
313
314         return 0;
315 }
316
317 int report_generator_generate_process_memory_usage_report(
318                 report_generator_process_t *generator,
319                 struct process_memory_usage_report *report)
320 {
321         ON_NULL_RETURN_VAL(generator, -1);
322         ON_NULL_RETURN_VAL(report, -1);
323
324         struct procfs_process_memory_usage_info mem_info;
325         struct procfs_system_memory_usage_info sys_info;
326
327         if (procfs_read_process_memory_usage(generator->pid, &mem_info) != 0) {
328                 ERR("procfs_read_process_memory_usage failed.");
329                 return -1;
330         }
331
332         if (procfs_read_system_memory_usage(&sys_info) != 0) {
333                 ERR("procfs_read_system_memory_usage failed.");
334                 return -1;
335         }
336
337         report->time = clock_get_monotonic().tv_sec;
338         report->usage = sys_info.total > 0 ? (float)mem_info.rss / sys_info.total : 0;
339
340         return 0;
341 }
342
343 int _app_report_generator_setup_process_generator(report_generator_app_t *generator)
344 {
345         int pid = app_info_provider_find_main_pid(generator->app_id);
346         if (pid < 0) {
347                 return -1;
348         }
349
350         if (!generator->process_gen || generator->process_gen->pid != pid) {
351                 if (generator->process_gen)
352                         report_generator_free_process_generator(generator->process_gen);
353                 generator->process_gen = report_generator_new_process_report_generator(pid);
354         }
355         return 0;
356 }
357
358 int report_generator_generate_app_cpu_usage_report(
359                 report_generator_app_t *generator,
360                 int interval,
361                 struct app_cpu_usage_report *report)
362 {
363         ON_NULL_RETURN_VAL(generator, -1);
364         ON_TRUE_RETURN_VAL(interval < 0, -1);
365         ON_NULL_RETURN_VAL(report, -1);
366
367         if (_app_report_generator_setup_process_generator(generator) != 0) {
368                 ERR("_app_report_generator_setup_process_generator failed.");
369                 return -1;
370         }
371
372         if (report_generator_generate_process_cpu_usage_report(
373                                 generator->process_gen, interval, &report->process_report) != 0)
374         {
375                 ERR("report_generator_generate_process_cpu_usage_report failed.");
376                 return 0;
377         }
378
379         strncpy(report->app_id, generator->app_id, sizeof(report->app_id));
380
381         return 0;
382 }
383
384 int report_generator_generate_app_memory_usage_report(
385                 report_generator_app_t *generator,
386                 struct app_memory_usage_report *report)
387 {
388         ON_NULL_RETURN_VAL(generator, -1);
389         ON_NULL_RETURN_VAL(report, -1);
390
391         if (_app_report_generator_setup_process_generator(generator)) {
392                 ERR("_app_report_generator_setup_process_generator failed.");
393                 return -1;
394         }
395
396         if (report_generator_generate_process_memory_usage_report(
397                                 generator->process_gen, &report->process_report) != 0)
398         {
399                 ERR("report_generator_generate_process_memory_usage_report failed.");
400                 return 0;
401         }
402
403         strncpy(report->app_id, generator->app_id, sizeof(report->app_id));
404
405         return 0;
406 }
407
408 int report_generator_generate_load_average_report(struct system_load_average_report *report)
409 {
410         ON_NULL_RETURN_VAL(report, -1);
411
412         struct procfs_load_average_info info = {0,};
413
414         if (procfs_read_system_load_average(&info) != 0) {
415                 ERR("procfs_read_system_load_average failed.");
416                 return -1;
417         }
418
419         report->time = clock_get_monotonic().tv_sec;
420         report->one_min_avg = info.one_min_avg;
421         report->five_min_avg = info.five_min_avg;
422         report->fifteen_min_avg = info.fifteen_min_avg;
423
424         return 0;
425 }
426