report: return memory usage as percent
[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->usage = (float)mem_usage.used / mem_usage.total;
194
195         return 0;
196 }
197
198 /**
199  * Calculates system average cpu usage between previous and current
200  * snapshots.
201  */
202 static void _calculate_process_cpu_usage(struct process_ticks_snapshot *previous,
203                 struct process_ticks_snapshot *current, int ncpus, float *usage)
204 {
205         struct process_ticks_snapshot diff;
206
207         diff.process_ticks = current->process_ticks > previous->process_ticks ? current->process_ticks - previous->process_ticks: 0;
208         diff.system_ticks = current->system_ticks > previous->system_ticks ? current->system_ticks - previous->system_ticks : 0;
209
210         if (diff.system_ticks == 0)
211                 *usage = 0;
212         else
213                 *usage = (float)diff.process_ticks / diff.system_ticks * ncpus;
214 }
215
216 static unsigned long long
217 _system_total_time(const struct procfs_system_cpu_usage_info *info)
218 {
219         return info->user + info->system + info->nice + info->idle;
220 }
221
222 static int
223 _report_generator_read_process_ticks(int pid, struct process_ticks_snapshot *ticks)
224 {
225         struct procfs_system_cpu_usage_info system_ticks;
226         struct procfs_process_cpu_usage_info process_ticks;
227
228         if (procfs_read_process_cpu_usage(pid, &process_ticks) != 0) {
229                 ERR("procfs_read_process_cpu_usage failed.");
230                 return -1;
231         }
232
233         if (procfs_read_system_cpu_usage(&system_ticks) != 0) {
234                 ERR("procfs_read_system_cpu_usage failed.");
235                 return -1;
236         }
237
238         ticks->process_ticks = process_ticks.stime + process_ticks.utime;
239         ticks->system_ticks = _system_total_time(&system_ticks);
240
241         return 0;
242 }
243
244 int report_generator_generate_process_cpu_usage_report(
245                 report_generator_process_t *generator,
246                 int interval,
247                 struct process_cpu_usage_report *report)
248 {
249         ON_NULL_RETURN_VAL(generator, -1);
250         ON_TRUE_RETURN_VAL(interval < 0, -1);
251         ON_NULL_RETURN_VAL(report, -1);
252
253         struct process_ticks_snapshot current;
254         bool block = interval > 0;
255         float usage;
256         int ncpus;
257
258         if (block) {
259                 if (_report_generator_read_process_ticks(generator->pid, &generator->previous) != 0) {
260                         ERR("Unable to update process cpu ticks.");
261                         return -1;
262                 }
263                 sleep(interval);
264         }
265
266         if (_report_generator_read_process_ticks(generator->pid, &current) != 0) {
267                 ERR("Unable to update process cpu ticks.");
268                 return -1;
269         }
270
271         if (procfs_read_cpu_count(&ncpus) != 0) {
272                 ERR("procfs_read_cpu_count failed.");
273                 return -1;
274         }
275
276         _calculate_process_cpu_usage(&generator->previous, &current, ncpus, &usage);
277
278         report->time = clock_get_monotonic().tv_sec;
279         report->pid = generator->pid;
280         report->usage = usage;
281
282         generator->previous = current;
283
284         return 0;
285 }
286
287 int report_generator_generate_proccess_memory_usage_report(
288                 report_generator_process_t *generator,
289                 struct process_memory_usage_report *report)
290 {
291         ON_NULL_RETURN_VAL(generator, -1);
292         ON_NULL_RETURN_VAL(report, -1);
293
294         struct procfs_process_memory_usage_info mem_info;
295         struct procfs_system_memory_usage_info sys_info;
296
297         if (procfs_read_process_memory_usage(generator->pid, &mem_info) != 0) {
298                 ERR("procfs_read_process_memory_usage failed.");
299                 return -1;
300         }
301
302         if (procfs_read_system_memory_usage(&sys_info) != 0) {
303                 ERR("procfs_read_system_memory_usage failed.");
304                 return -1;
305         }
306
307         report->time = clock_get_monotonic().tv_sec;
308         report->usage = sys_info.total > 0 ? (float)mem_info.rss / sys_info.total : 0;
309
310         return 0;
311 }
312
313 int report_generator_generate_load_average_report(struct system_load_average_report *report)
314 {
315         ON_NULL_RETURN_VAL(report, -1);
316
317         struct procfs_load_average_info info = {0,};
318
319         if (procfs_read_system_load_average(&info) != 0) {
320                 ERR("procfs_read_system_load_average failed.");
321                 return -1;
322         }
323
324         report->time = clock_get_monotonic().tv_sec;
325         report->one_min_avg = info.one_min_avg;
326         report->five_min_avg = info.five_min_avg;
327         report->fifteen_min_avg = info.fifteen_min_avg;
328
329         return 0;
330 }
331