report-json-serializer: app and top serializers
[apps/native/ttsd-worker-task.git] / src / procfs.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 <stdio.h>
18 #include <string.h>
19 #include <dirent.h>
20 #include <stdlib.h>
21
22 #include "procfs.h"
23 #include "log.h"
24 #include "err-check.h"
25
26 #define LOADAVG_FILEPATH "/proc/loadavg"
27 #define UPTIME_FILEPATH  "/proc/uptime"
28 #define POSSIBLE_CPUS_FILEPATH  "/sys/devices/system/cpu/possible"
29 #define MEMINFO_FILEPATH "/proc/meminfo"
30 #define STAT_FILEPATH "/proc/stat"
31 #define PIDSTAT_FILEPATH "/proc/%d/stat"
32 #define SMAPS_FILEPATH "/proc/%d/smaps"
33 #define PROC_DIR_PATH "/proc/"
34
35 struct procfs_pid_iterator
36 {
37         DIR *dir;
38         int current_pid;
39 };
40
41 int procfs_read_system_load_average(struct procfs_load_average_info *info)
42 {
43         float a1, a5, a15;
44
45         ON_NULL_RETURN_VAL(info, -1);
46
47         FILE *loadavg_fp = fopen(LOADAVG_FILEPATH, "r");
48         if (!loadavg_fp) {
49                 ERR("failed to open " LOADAVG_FILEPATH);
50                 return -1;
51         }
52
53         if (fscanf(loadavg_fp, "%f %f %f", &a1, &a5, &a15) != 3) {
54                 ERR("failed to read " LOADAVG_FILEPATH);
55                 fclose(loadavg_fp);
56                 return -1;
57         }
58
59         info->one_min_avg = a1;
60         info->five_min_avg = a5;
61         info->fifteen_min_avg = a15;
62
63         fclose(loadavg_fp);
64
65         return 0;
66 }
67
68 int procfs_read_system_cpu_usage(struct procfs_system_cpu_usage_info *usage)
69 {
70         ON_NULL_RETURN_VAL(usage, -1);
71
72         char line[256];
73         struct procfs_system_cpu_usage_info tmp;
74         const char *prefix = "cpu ";
75
76         FILE *stat_fp = fopen(STAT_FILEPATH, "r");
77         if (!stat_fp) {
78                 ERR("failed to open " STAT_FILEPATH);
79                 return -1;
80         }
81
82         while (fgets(line, sizeof(line), stat_fp)) {
83                 if (!strncmp(line, prefix, strlen(prefix)) &&
84                     sscanf(line + 4, "%llu %llu %llu %llu %llu %llu %llu",
85                                 &tmp.user, &tmp.nice, &tmp.system, &tmp.idle,
86                                 &tmp.iowait, &tmp.irq, &tmp.softirq) == 7) {
87                         *usage = tmp;
88                         fclose(stat_fp);
89                         return 0;
90                 }
91         }
92
93         fclose(stat_fp);
94
95         return -1;
96 }
97
98 int procfs_read_system_percpu_usage(int max_cpus, struct procfs_system_cpu_usage_info usage[])
99 {
100         return -1;
101 }
102
103 int procfs_read_system_memory_usage(struct procfs_system_memory_usage_info *usage)
104 {
105         ON_NULL_RETURN_VAL(usage, -1);
106
107         char line[128];
108         unsigned long value;
109         unsigned long mem_total = 0;
110         unsigned long mem_free = 0;
111         unsigned long cached = 0;
112         unsigned long mem_available = 0;
113         unsigned long swap_total = 0;
114         unsigned long swap_free = 0;
115
116         FILE *meminfo_fp = fopen(MEMINFO_FILEPATH, "r");
117         if (!meminfo_fp) {
118                 ERR("failed to open " MEMINFO_FILEPATH);
119                 return -1;
120         }
121
122         while (fgets(line, sizeof(line), meminfo_fp)) {
123                 if (sscanf(line, "MemTotal: %lu", &mem_total) == 1)
124                         usage->total = mem_total;
125                 else if (sscanf(line, "MemFree: %lu", &mem_free) == 1)
126                         usage->free = mem_free;
127                 else if (sscanf(line, "Cached: %lu", &cached) == 1)
128                         usage->cache = cached;
129                 else if (sscanf(line, "MemAvailable: %lu", &value) == 1)
130                         mem_available = value;
131                 else if (sscanf(line, "SwapTotal: %lu", &value) == 1)
132                         swap_total = value;
133                 else if (sscanf(line, "SwapFree: %lu", &value) == 1)
134                         swap_free = value;
135         }
136
137         // Calculate used memory
138         // MemAvailable is exposed since Linux 3.14, so handle also previous
139         // kernel versions
140         usage->used = 0;
141         if (mem_available > 0) {
142                 if (mem_total > mem_available)
143                         usage->used = mem_total - mem_available;
144         } else {
145                 if ((mem_total > mem_free) && ((mem_total - mem_free) > cached))
146                         usage->used = mem_total - mem_free - cached;
147         }
148
149         usage->swap = (swap_total > swap_free) ? (swap_total - swap_free) : 0;
150
151         fclose(meminfo_fp);
152
153         return 0;
154 }
155
156 int procfs_read_process_memory_usage(int pid, struct procfs_process_memory_usage_info *usage)
157 {
158         ON_NULL_RETURN_VAL(usage, -1);
159         ON_TRUE_RETURN_VAL(pid <= 0, -1);
160
161         char smapspath[64], line[256];
162         unsigned long value;
163
164         snprintf(smapspath, sizeof(smapspath), SMAPS_FILEPATH, pid);
165
166         FILE *smaps_fp = fopen(smapspath, "r");
167         if (!smaps_fp) {
168                  ERR("failed to open path: %s", smapspath);
169                  return -1;
170         }
171
172         memset(usage, 0x0, sizeof(struct procfs_process_memory_usage_info));
173
174         while (fgets(line, sizeof(line), smaps_fp)) {
175                 if (sscanf(line, "Size: %lu", &value) == 1)
176                         usage->vsz += value;
177                 else if (sscanf(line, "Rss: %lu", &value) == 1)
178                         usage->rss += value;
179                 else if (sscanf(line, "Pss: %lu", &value) == 1)
180                         usage->pss += value;
181                 else if (sscanf(line, "Shared_Clean: %lu", &value) == 1)
182                         usage->shared_clean += value;
183                 else if (sscanf(line, "Shared_Dirty: %lu", &value) == 1)
184                         usage->shared_dirty += value;
185                 else if (sscanf(line, "Private_Clean: %lu", &value) == 1)
186                         usage->private_clean += value;
187                 else if (sscanf(line, "Private_Dirty: %lu", &value) == 1)
188                         usage->private_dirty += value;
189         }
190
191         fclose(smaps_fp);
192
193         return 0;
194 }
195
196 int procfs_read_process_cpu_usage(int pid, struct procfs_process_cpu_usage_info *usage)
197 {
198         ON_NULL_RETURN_VAL(usage, -1);
199         ON_TRUE_RETURN_VAL(pid <= 0, -1);
200
201         char statpath[64];
202         unsigned long long utime, stime;
203
204         snprintf(statpath, sizeof(statpath), PIDSTAT_FILEPATH, pid);
205
206         FILE *stat_fp = fopen(statpath, "r");
207         if (!stat_fp) {
208                 ERR("failed to open %s", statpath);
209                 return -1;
210         }
211
212         if (fscanf(stat_fp, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s") < 0) {
213                 ERR("reading pid stat file failed");
214                 fclose(stat_fp);
215                 return -1;
216         }
217
218         if (fscanf(stat_fp, "%llu %llu", &utime, &stime) != 2) {
219                 ERR("reading pid stat file failed");
220                 fclose(stat_fp);
221                 return -1;
222         }
223
224         usage->utime = utime;
225         usage->stime = stime;
226
227         return 0;
228 }
229
230 int procfs_read_uptime(unsigned long *uptime)
231 {
232         ON_NULL_RETURN_VAL(uptime, -1);
233
234         double duptime;
235
236         FILE *uptime_fp = fopen(UPTIME_FILEPATH, "r");
237         if (!uptime_fp) {
238                 ERR("failed to open " UPTIME_FILEPATH);
239                 return -1;
240         }
241
242         if (fscanf(uptime_fp, "%lf ", &duptime) != 1) {
243                 ERR("failed to read " UPTIME_FILEPATH);
244                 fclose(uptime_fp);
245                 return -1;
246         }
247
248         fclose(uptime_fp);
249         *uptime = (unsigned long)duptime;
250
251         return 0;
252 }
253
254 int procfs_read_cpu_count(int *cpu_count)
255 {
256         ON_NULL_RETURN_VAL(cpu_count, -1);
257
258         int cpus, dummy;
259
260         FILE *possible_fp = fopen(POSSIBLE_CPUS_FILEPATH, "r");
261         if (!possible_fp) {
262                 ERR("failed to open " POSSIBLE_CPUS_FILEPATH);
263                 return -1;
264         }
265
266         if (fscanf(possible_fp, "%d-%d", &dummy, &cpus) != 2) {
267                 ERR("failed to read " POSSIBLE_CPUS_FILEPATH);
268                 fclose(possible_fp);
269                 return -1;
270         }
271
272         *cpu_count = cpus + 1;
273
274         fclose(possible_fp);
275
276         return 0;
277 }
278
279 bool _procfs_dirname_parse_pid(const char *dirname, int *pid)
280 {
281         int parsed_pid;
282
283         if (sscanf(dirname, "%d", &parsed_pid) != 1) {
284                 return false;
285         }
286         *pid = parsed_pid;
287         return true;
288 }
289
290 /**
291  * @brief returns true if pid_iterator could successfully read
292  * next pid from /proc directory, false otherwise
293  */
294 bool _procfs_pid_iterator_next_internal(procfs_pid_iterator_t *iter)
295 {
296         struct dirent *entry;
297         int pid;
298         bool ret = false;
299
300         // According to POSIX docs readdir is not-thread safe.
301         // however in glib recent implementations readdir
302         // is thread safe, so we can avoid using locks here.
303         while ((entry = readdir(iter->dir)) != NULL) {
304                 if (_procfs_dirname_parse_pid(entry->d_name, &pid)) {
305                         iter->current_pid = pid;
306                         ret = true;
307                         break;
308                 } else {
309                         continue;
310                 }
311         }
312
313         return ret;
314 }
315
316 procfs_pid_iterator_t *procfs_get_pid_iterator()
317 {
318         procfs_pid_iterator_t *ret = calloc(1, sizeof(struct procfs_pid_iterator));
319         if (!ret) {
320                 ERR("calloc failed.");
321                 return NULL;
322         }
323         ret->dir = opendir(PROC_DIR_PATH);
324         if (!ret->dir) {
325                 ERR("opendir failed.");
326                 procfs_pid_iterator_free(ret);
327                 return NULL;
328         }
329         if (!_procfs_pid_iterator_next_internal(ret)) {
330                 ERR("_procfs_pid_iterator_next_internal failed");
331                 procfs_pid_iterator_free(ret);
332                 return NULL;
333         }
334         return ret;
335 }
336
337 bool procfs_pid_iterator_next(procfs_pid_iterator_t *iterator)
338 {
339         return _procfs_pid_iterator_next_internal(iterator);
340 }
341
342 int procfs_pid_iterator_get_pid(procfs_pid_iterator_t *iterator)
343 {
344         return iterator->current_pid;
345 }
346
347 void procfs_pid_iterator_free(procfs_pid_iterator_t *iterator)
348 {
349         if (!iterator) return;
350         if (iterator->dir) closedir(iterator->dir);
351         free(iterator);
352 }