report-generator: implement generator functions
[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 static void _calculate_system_cpu_usage(
116                 struct procfs_system_cpu_usage_info *previous,
117                 struct procfs_system_cpu_usage_info *current,
118                 float *usage)
119 {
120         struct procfs_system_cpu_usage_info diff_ticks = { 0,};
121
122         diff_ticks.user = current->user > previous->user ? current->user - previous->user : 0;
123         diff_ticks.system = current->system > previous->system ? current->system - previous->system : 0;
124         diff_ticks.nice = current->nice > previous->nice ? current->nice - previous->nice : 0;
125         diff_ticks.idle = current->idle > previous->idle ? current->idle - previous->idle : 0;
126         diff_ticks.iowait = current->iowait > previous->iowait ? current->iowait - previous->iowait : 0;
127         diff_ticks.irq = current->irq > previous->irq ? current->irq - previous->irq : 0;
128         diff_ticks.softirq = current->softirq > previous->softirq ? current->softirq - previous->softirq : 0;
129
130         unsigned long long total = diff_ticks.user + diff_ticks.system + diff_ticks.nice +
131                 diff_ticks.idle + diff_ticks.iowait + diff_ticks.irq + diff_ticks.softirq;
132
133         unsigned long long busy = diff_ticks.user + diff_ticks.system + diff_ticks.nice +
134                 diff_ticks.irq + diff_ticks.softirq;
135
136         if (total == 0) {
137                 *usage = 0;
138         } else {
139                 *usage = (float)busy / total;
140         }
141 }
142
143 int report_generator_generate_system_cpu_usage_report(
144                 report_generator_system_t *generator,
145                 int interval,
146                 struct system_cpu_usage_report *report)
147 {
148         ON_NULL_RETURN_VAL(generator, -1);
149         ON_TRUE_RETURN_VAL(interval < 0, -1);
150         ON_NULL_RETURN_VAL(report, -1);
151
152         struct procfs_system_cpu_usage_info current = {0,};
153         float usage;
154         bool block = interval > 0;
155
156         if (block) {
157                 if (procfs_read_system_cpu_usage(&generator->previous) != 0) {
158                         ERR("procfs_read_system_cpu_usage failed.");
159                         return -1;
160                 }
161                 sleep(interval);
162         }
163
164         if (procfs_read_system_cpu_usage(&current) != 0) {
165                 ERR("procfs_read_system_cpu_usage failed.");
166                 return -1;
167         }
168
169         _calculate_system_cpu_usage(&generator->previous, &current, &usage);
170
171         report->usage = usage;
172         report->time = clock_get_monotonic().tv_sec;
173
174         generator->previous = current;
175
176         return 0;
177 }
178
179 int report_generator_generate_system_memory_usage_report(
180                 report_generator_system_t *generator,
181                 struct system_memory_usage_report *report) {
182         ON_NULL_RETURN_VAL(generator, -1);
183         ON_NULL_RETURN_VAL(report, -1);
184
185         struct procfs_system_memory_usage_info mem_usage;
186
187         if (procfs_read_system_memory_usage(&mem_usage) != 0) {
188                 ERR("procfs_read_system_memory_usage failed.");
189                 return -1;
190         }
191
192         report->time = clock_get_monotonic().tv_sec;
193         report->total = mem_usage.total;
194         report->used = mem_usage.used;
195         report->free = mem_usage.free;
196
197         return 0;
198 }
199
200 /**
201  * Calculates system average cpu usage between previous and current
202  * snapshots.
203  */
204 static void _calculate_process_cpu_usage(struct process_ticks_snapshot *previous,
205                 struct process_ticks_snapshot *current, int ncpus, float *usage)
206 {
207         struct process_ticks_snapshot diff;
208
209         diff.process_ticks = current->process_ticks > previous->process_ticks ? current->process_ticks - previous->process_ticks: 0;
210         diff.system_ticks = current->system_ticks > previous->system_ticks ? current->system_ticks - previous->system_ticks : 0;
211
212         if (diff.system_ticks == 0)
213                 *usage = 0;
214         else
215                 *usage = (float)diff.process_ticks / diff.system_ticks * ncpus;
216 }
217
218 static unsigned long long
219 _system_total_time(const struct procfs_system_cpu_usage_info *info)
220 {
221         return info->user + info->system + info->nice + info->idle;
222 }
223
224 static int
225 _report_generator_read_process_ticks(int pid, struct process_ticks_snapshot *ticks)
226 {
227         struct procfs_system_cpu_usage_info system_ticks;
228         struct procfs_process_cpu_usage_info process_ticks;
229
230         if (procfs_read_process_cpu_usage(pid, &process_ticks) != 0) {
231                 ERR("procfs_read_process_cpu_usage failed.");
232                 return -1;
233         }
234
235         if (procfs_read_system_cpu_usage(&system_ticks) != 0) {
236                 ERR("procfs_read_system_cpu_usage failed.");
237                 return -1;
238         }
239
240         ticks->process_ticks = process_ticks.stime + process_ticks.utime;
241         ticks->system_ticks = _system_total_time(&system_ticks);
242
243         return 0;
244 }
245
246 int report_generator_generate_process_cpu_usage_report(
247                 report_generator_process_t *generator,
248                 int interval,
249                 struct process_cpu_usage_report *report)
250 {
251         ON_NULL_RETURN_VAL(generator, -1);
252         ON_TRUE_RETURN_VAL(interval < 0, -1);
253         ON_NULL_RETURN_VAL(report, -1);
254
255         struct process_ticks_snapshot current;
256         bool block = interval > 0;
257         float usage;
258         int ncpus;
259
260         if (block) {
261                 if (_report_generator_read_process_ticks(generator->pid, &generator->previous) != 0) {
262                         ERR("Unable to update process cpu ticks.");
263                         return -1;
264                 }
265                 sleep(interval);
266         }
267
268         if (_report_generator_read_process_ticks(generator->pid, &current) != 0) {
269                 ERR("Unable to update process cpu ticks.");
270                 return -1;
271         }
272
273         if (procfs_read_cpu_count(&ncpus) != 0) {
274                 ERR("procfs_read_cpu_count failed.");
275                 return -1;
276         }
277
278         _calculate_process_cpu_usage(&generator->previous, &current, ncpus, &usage);
279
280         report->time = clock_get_monotonic().tv_sec;
281         report->pid = generator->pid;
282         report->usage = usage;
283
284         generator->previous = current;
285
286         return 0;
287 }
288
289 int report_generator_generate_proccess_memory_usage_report(
290                 report_generator_process_t *generator,
291                 struct process_memory_usage_report *report)
292 {
293         ON_NULL_RETURN_VAL(generator, -1);
294         ON_NULL_RETURN_VAL(report, -1);
295
296         struct procfs_process_memory_usage_info mem_info;
297         struct procfs_system_memory_usage_info sys_info;
298
299         if (procfs_read_process_memory_usage(generator->pid, &mem_info) != 0) {
300                 ERR("procfs_read_process_memory_usage failed.");
301                 return -1;
302         }
303
304         if (procfs_read_system_memory_usage(&sys_info) != 0) {
305                 ERR("procfs_read_system_memory_usage failed.");
306                 return -1;
307         }
308
309         report->time = clock_get_monotonic().tv_sec;
310         report->usage = sys_info.total > 0 ? (float)mem_info.rss / sys_info.total : 0;
311
312         return 0;
313 }
314
315 int report_generator_generate_load_average_report(struct system_load_average_report *report)
316 {
317         ON_NULL_RETURN_VAL(report, -1);
318
319         struct procfs_load_average_info info = {0,};
320
321         if (procfs_read_system_load_average(&info) != 0) {
322                 ERR("procfs_read_system_load_average failed.");
323                 return -1;
324         }
325
326         report->time = clock_get_monotonic().tv_sec;
327         report->one_min_avg = info.one_min_avg;
328         report->five_min_avg = info.five_min_avg;
329         report->fifteen_min_avg = info.fifteen_min_avg;
330
331         return 0;
332 }
333