task-factory: app-task implementation
[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
28 struct report_generator_system
29 {
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 static int _report_generator_read_process_ticks(int pid, struct process_ticks_snapshot *ticks);
49
50 static struct timespec clock_get_monotonic()
51 {
52         struct timespec ret = {0,};
53
54         if (clock_gettime(CLOCK_MONOTONIC, &ret) != 0) {
55                 ERR("Platform do not support monotonic clock type");
56                 //TODO consider adding first init function to evaluate
57                 //if clock_gettime can be used, so calling module could
58                 //handle cases without monotonic clock gracefully.
59                 abort();
60         }
61
62         return ret;
63 }
64
65 report_generator_system_t *report_generator_new_system_report_generator()
66 {
67         report_generator_system_t *ret = calloc(1, sizeof(struct report_generator_system));
68         if (!ret)
69                 return NULL;
70
71         if (procfs_read_system_cpu_usage(&ret->previous) != 0) {
72                 ERR("procfs_read_system_cpu_usage failed");
73                 free(ret);
74                 return NULL;
75         }
76
77         return ret;
78 }
79
80 void report_generator_free_system_generator(report_generator_system_t *generator)
81 {
82         if (!generator)
83                 return;
84
85         free(generator);
86 }
87
88 report_generator_process_t *report_generator_new_process_report_generator(int pid)
89 {
90         ON_TRUE_RETURN_VAL(pid <= 0, NULL);
91
92         report_generator_process_t *ret = calloc(1, sizeof(struct report_generator_process));
93         if (!ret)
94                 return NULL;
95
96         if (_report_generator_read_process_ticks(pid, &ret->previous) != 0) {
97                 ERR("_report_generator_read_process_ticks failed.");
98                 free(ret);
99                 return NULL;
100         };
101
102         ret->pid = pid;
103
104         return ret;
105 }
106
107 void report_generator_free_process_generator(report_generator_process_t *generator)
108 {
109         if (!generator)
110                 return;
111
112         free(generator);
113 }
114
115 report_generator_apps_t *report_generator_new_apps_report_generator(const char *app_id)
116 {
117         //TODO implement
118         return NULL;
119 }
120
121 void report_generator_free_apps_generator(report_generator_apps_t *generator)
122 {
123         //TODO implement
124 }
125
126 static void _calculate_system_cpu_usage(
127                 struct procfs_system_cpu_usage_info *previous,
128                 struct procfs_system_cpu_usage_info *current,
129                 float *usage)
130 {
131         struct procfs_system_cpu_usage_info diff_ticks = { 0,};
132
133         diff_ticks.user = current->user > previous->user ? current->user - previous->user : 0;
134         diff_ticks.system = current->system > previous->system ? current->system - previous->system : 0;
135         diff_ticks.nice = current->nice > previous->nice ? current->nice - previous->nice : 0;
136         diff_ticks.idle = current->idle > previous->idle ? current->idle - previous->idle : 0;
137         diff_ticks.iowait = current->iowait > previous->iowait ? current->iowait - previous->iowait : 0;
138         diff_ticks.irq = current->irq > previous->irq ? current->irq - previous->irq : 0;
139         diff_ticks.softirq = current->softirq > previous->softirq ? current->softirq - previous->softirq : 0;
140
141         unsigned long long total = diff_ticks.user + diff_ticks.system + diff_ticks.nice +
142                 diff_ticks.idle + diff_ticks.iowait + diff_ticks.irq + diff_ticks.softirq;
143
144         unsigned long long busy = diff_ticks.user + diff_ticks.system + diff_ticks.nice +
145                 diff_ticks.irq + diff_ticks.softirq;
146
147         if (total == 0) {
148                 *usage = 0;
149         } else {
150                 *usage = (float)busy / total;
151         }
152 }
153
154 int report_generator_generate_system_cpu_usage_report(
155                 report_generator_system_t *generator,
156                 int interval,
157                 struct system_cpu_usage_report *report)
158 {
159         ON_NULL_RETURN_VAL(generator, -1);
160         ON_TRUE_RETURN_VAL(interval < 0, -1);
161         ON_NULL_RETURN_VAL(report, -1);
162
163         struct procfs_system_cpu_usage_info current = {0,};
164         float usage;
165         bool block = interval > 0;
166
167         if (block) {
168                 if (procfs_read_system_cpu_usage(&generator->previous) != 0) {
169                         ERR("procfs_read_system_cpu_usage failed.");
170                         return -1;
171                 }
172                 sleep(interval);
173         }
174
175         if (procfs_read_system_cpu_usage(&current) != 0) {
176                 ERR("procfs_read_system_cpu_usage failed.");
177                 return -1;
178         }
179
180         _calculate_system_cpu_usage(&generator->previous, &current, &usage);
181
182         report->usage = usage;
183         report->time = clock_get_monotonic().tv_sec;
184
185         generator->previous = current;
186
187         return 0;
188 }
189
190 int report_generator_generate_system_memory_usage_report(
191                 report_generator_system_t *generator,
192                 struct system_memory_usage_report *report) {
193         ON_NULL_RETURN_VAL(generator, -1);
194         ON_NULL_RETURN_VAL(report, -1);
195
196         struct procfs_system_memory_usage_info mem_usage;
197
198         if (procfs_read_system_memory_usage(&mem_usage) != 0) {
199                 ERR("procfs_read_system_memory_usage failed.");
200                 return -1;
201         }
202
203         report->time = clock_get_monotonic().tv_sec;
204         report->usage = (float)mem_usage.used / mem_usage.total;
205
206         return 0;
207 }
208
209 /**
210  * Calculates system average cpu usage between previous and current
211  * snapshots.
212  */
213 static void _calculate_process_cpu_usage(struct process_ticks_snapshot *previous,
214                 struct process_ticks_snapshot *current, int ncpus, float *usage)
215 {
216         struct process_ticks_snapshot diff;
217
218         diff.process_ticks = current->process_ticks > previous->process_ticks ? current->process_ticks - previous->process_ticks: 0;
219         diff.system_ticks = current->system_ticks > previous->system_ticks ? current->system_ticks - previous->system_ticks : 0;
220
221         if (diff.system_ticks == 0)
222                 *usage = 0;
223         else
224                 *usage = (float)diff.process_ticks / diff.system_ticks * ncpus;
225 }
226
227 static unsigned long long
228 _system_total_time(const struct procfs_system_cpu_usage_info *info)
229 {
230         return info->user + info->system + info->nice + info->idle;
231 }
232
233 static int
234 _report_generator_read_process_ticks(int pid, struct process_ticks_snapshot *ticks)
235 {
236         struct procfs_system_cpu_usage_info system_ticks;
237         struct procfs_process_cpu_usage_info process_ticks;
238
239         if (procfs_read_process_cpu_usage(pid, &process_ticks) != 0) {
240                 ERR("procfs_read_process_cpu_usage failed.");
241                 return -1;
242         }
243
244         if (procfs_read_system_cpu_usage(&system_ticks) != 0) {
245                 ERR("procfs_read_system_cpu_usage failed.");
246                 return -1;
247         }
248
249         ticks->process_ticks = process_ticks.stime + process_ticks.utime;
250         ticks->system_ticks = _system_total_time(&system_ticks);
251
252         return 0;
253 }
254
255 int report_generator_generate_process_cpu_usage_report(
256                 report_generator_process_t *generator,
257                 int interval,
258                 struct process_cpu_usage_report *report)
259 {
260         ON_NULL_RETURN_VAL(generator, -1);
261         ON_TRUE_RETURN_VAL(interval < 0, -1);
262         ON_NULL_RETURN_VAL(report, -1);
263
264         struct process_ticks_snapshot current;
265         bool block = interval > 0;
266         float usage;
267         int ncpus;
268
269         if (block) {
270                 if (_report_generator_read_process_ticks(generator->pid, &generator->previous) != 0) {
271                         ERR("Unable to update process cpu ticks.");
272                         return -1;
273                 }
274                 sleep(interval);
275         }
276
277         if (_report_generator_read_process_ticks(generator->pid, &current) != 0) {
278                 ERR("Unable to update process cpu ticks.");
279                 return -1;
280         }
281
282         if (procfs_read_cpu_count(&ncpus) != 0) {
283                 ERR("procfs_read_cpu_count failed.");
284                 return -1;
285         }
286
287         _calculate_process_cpu_usage(&generator->previous, &current, ncpus, &usage);
288
289         report->time = clock_get_monotonic().tv_sec;
290         report->pid = generator->pid;
291         report->usage = usage;
292
293         generator->previous = current;
294
295         return 0;
296 }
297
298 int report_generator_generate_proccess_memory_usage_report(
299                 report_generator_process_t *generator,
300                 struct process_memory_usage_report *report)
301 {
302         ON_NULL_RETURN_VAL(generator, -1);
303         ON_NULL_RETURN_VAL(report, -1);
304
305         struct procfs_process_memory_usage_info mem_info;
306         struct procfs_system_memory_usage_info sys_info;
307
308         if (procfs_read_process_memory_usage(generator->pid, &mem_info) != 0) {
309                 ERR("procfs_read_process_memory_usage failed.");
310                 return -1;
311         }
312
313         if (procfs_read_system_memory_usage(&sys_info) != 0) {
314                 ERR("procfs_read_system_memory_usage failed.");
315                 return -1;
316         }
317
318         report->time = clock_get_monotonic().tv_sec;
319         report->usage = sys_info.total > 0 ? (float)mem_info.rss / sys_info.total : 0;
320
321         return 0;
322 }
323
324 int report_generator_generate_apps_cpu_usage_report(
325                 report_generator_apps_t *generator,
326                 int interval,
327                 struct app_cpu_usage_report **reports)
328 {
329         //TODO implement
330         return -1;
331 }
332
333
334 int report_generator_generate_apps_memory_usage_report(
335                 report_generator_apps_t *generator,
336                 struct app_memory_usage_report **report)
337 {
338         //TODO implement
339         return -1;
340 }
341
342 int report_generator_generate_load_average_report(struct system_load_average_report *report)
343 {
344         ON_NULL_RETURN_VAL(report, -1);
345
346         struct procfs_load_average_info info = {0,};
347
348         if (procfs_read_system_load_average(&info) != 0) {
349                 ERR("procfs_read_system_load_average failed.");
350                 return -1;
351         }
352
353         report->time = clock_get_monotonic().tv_sec;
354         report->one_min_avg = info.one_min_avg;
355         report->five_min_avg = info.five_min_avg;
356         report->fifteen_min_avg = info.fifteen_min_avg;
357
358         return 0;
359 }
360