2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
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
8 * http://floralicense.org/license/
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.
24 #include "err-check.h"
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/"
35 struct procfs_pid_iterator
41 int procfs_read_system_load_average(struct procfs_load_average_info *info)
45 ON_NULL_RETURN_VAL(info, -1);
47 FILE *loadavg_fp = fopen(LOADAVG_FILEPATH, "r");
49 ERR("failed to open " LOADAVG_FILEPATH);
53 if (fscanf(loadavg_fp, "%f %f %f", &a1, &a5, &a15) != 3) {
54 ERR("failed to read " LOADAVG_FILEPATH);
59 info->one_min_avg = a1;
60 info->five_min_avg = a5;
61 info->fifteen_min_avg = a15;
68 int procfs_read_system_cpu_usage(struct procfs_system_cpu_usage_info *usage)
70 ON_NULL_RETURN_VAL(usage, -1);
73 struct procfs_system_cpu_usage_info tmp;
74 const char *prefix = "cpu ";
76 FILE *stat_fp = fopen(STAT_FILEPATH, "r");
78 ERR("failed to open " STAT_FILEPATH);
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) {
98 int procfs_read_system_percpu_usage(int max_cpus, struct procfs_system_cpu_usage_info usage[])
103 int procfs_read_system_memory_usage(struct procfs_system_memory_usage_info *usage)
105 ON_NULL_RETURN_VAL(usage, -1);
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;
116 FILE *meminfo_fp = fopen(MEMINFO_FILEPATH, "r");
118 ERR("failed to open " MEMINFO_FILEPATH);
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)
133 else if (sscanf(line, "SwapFree: %lu", &value) == 1)
137 // Calculate used memory
138 // MemAvailable is exposed since Linux 3.14, so handle also previous
141 if (mem_available > 0) {
142 if (mem_total > mem_available)
143 usage->used = mem_total - mem_available;
145 if ((mem_total > mem_free) && ((mem_total - mem_free) > cached))
146 usage->used = mem_total - mem_free - cached;
149 usage->swap = (swap_total > swap_free) ? (swap_total - swap_free) : 0;
156 int procfs_read_process_memory_usage(int pid, struct procfs_process_memory_usage_info *usage)
158 ON_NULL_RETURN_VAL(usage, -1);
159 ON_TRUE_RETURN_VAL(pid <= 0, -1);
161 char smapspath[64], line[256];
164 snprintf(smapspath, sizeof(smapspath), SMAPS_FILEPATH, pid);
166 FILE *smaps_fp = fopen(smapspath, "r");
168 ERR("failed to open path: %s", smapspath);
172 memset(usage, 0x0, sizeof(struct procfs_process_memory_usage_info));
174 while (fgets(line, sizeof(line), smaps_fp)) {
175 if (sscanf(line, "Size: %lu", &value) == 1)
177 else if (sscanf(line, "Rss: %lu", &value) == 1)
179 else if (sscanf(line, "Pss: %lu", &value) == 1)
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;
196 int procfs_read_process_cpu_usage(int pid, struct procfs_process_cpu_usage_info *usage)
198 ON_NULL_RETURN_VAL(usage, -1);
199 ON_TRUE_RETURN_VAL(pid <= 0, -1);
202 unsigned long long utime, stime;
204 snprintf(statpath, sizeof(statpath), PIDSTAT_FILEPATH, pid);
206 FILE *stat_fp = fopen(statpath, "r");
208 ERR("failed to open %s", statpath);
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");
218 if (fscanf(stat_fp, "%llu %llu", &utime, &stime) != 2) {
219 ERR("reading pid stat file failed");
224 usage->utime = utime;
225 usage->stime = stime;
230 int procfs_read_uptime(unsigned long *uptime)
232 ON_NULL_RETURN_VAL(uptime, -1);
236 FILE *uptime_fp = fopen(UPTIME_FILEPATH, "r");
238 ERR("failed to open " UPTIME_FILEPATH);
242 if (fscanf(uptime_fp, "%lf ", &duptime) != 1) {
243 ERR("failed to read " UPTIME_FILEPATH);
249 *uptime = (unsigned long)duptime;
254 int procfs_read_cpu_count(int *cpu_count)
256 ON_NULL_RETURN_VAL(cpu_count, -1);
260 FILE *possible_fp = fopen(POSSIBLE_CPUS_FILEPATH, "r");
262 ERR("failed to open " POSSIBLE_CPUS_FILEPATH);
266 if (fscanf(possible_fp, "%d-%d", &dummy, &cpus) != 2) {
267 ERR("failed to read " POSSIBLE_CPUS_FILEPATH);
272 *cpu_count = cpus + 1;
279 bool _procfs_dirname_parse_pid(const char *dirname, int *pid)
283 if (sscanf(dirname, "%d", &parsed_pid) != 1) {
291 * @brief returns true if pid_iterator could successfully read
292 * next pid from /proc directory, false otherwise
294 bool _procfs_pid_iterator_next_internal(procfs_pid_iterator_t *iter)
296 struct dirent *entry;
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;
316 procfs_pid_iterator_t *procfs_get_pid_iterator()
318 procfs_pid_iterator_t *ret = calloc(1, sizeof(struct procfs_pid_iterator));
320 ERR("calloc failed.");
323 ret->dir = opendir(PROC_DIR_PATH);
325 ERR("opendir failed.");
326 procfs_pid_iterator_free(ret);
329 if (!_procfs_pid_iterator_next_internal(ret)) {
330 ERR("_procfs_pid_iterator_next_internal failed");
331 procfs_pid_iterator_free(ret);
337 bool procfs_pid_iterator_next(procfs_pid_iterator_t *iterator)
339 return _procfs_pid_iterator_next_internal(iterator);
342 int procfs_pid_iterator_get_pid(procfs_pid_iterator_t *iterator)
344 return iterator->current_pid;
347 void procfs_pid_iterator_free(procfs_pid_iterator_t *iterator)
349 if (!iterator) return;
350 if (iterator->dir) closedir(iterator->dir);