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.
26 #include "err-check.h"
28 #define LOADAVG_FILEPATH "/proc/loadavg"
29 #define UPTIME_FILEPATH "/proc/uptime"
30 #define POSSIBLE_CPUS_FILEPATH "/sys/devices/system/cpu/possible"
31 #define MEMINFO_FILEPATH "/proc/meminfo"
32 #define STAT_FILEPATH "/proc/stat"
33 #define PIDSTAT_FILEPATH "/proc/%d/stat"
34 #define SMAPS_FILEPATH "/proc/%d/smaps"
35 #define PROC_DIR_PATH "/proc/"
36 #define PROC_PID_EXE_PATH "/proc/%d/exe"
37 #define PROC_PID_CMDLINE_PATH "/proc/%d/cmdline"
39 int procfs_read_loadavg(struct procfs_loadavg *info)
43 ON_NULL_RETURN_VAL(info, -1);
45 FILE *loadavg_fp = fopen(LOADAVG_FILEPATH, "r");
47 ERR("failed to open " LOADAVG_FILEPATH);
51 if (fscanf(loadavg_fp, "%f %f %f", &a1, &a5, &a15) != 3) {
52 ERR("failed to read " LOADAVG_FILEPATH);
57 info->one_min_avg = a1;
58 info->five_min_avg = a5;
59 info->fifteen_min_avg = a15;
66 int procfs_read_stat(struct procfs_stat *usage)
68 ON_NULL_RETURN_VAL(usage, -1);
71 struct procfs_stat tmp;
72 const char *prefix = "cpu ";
74 FILE *stat_fp = fopen(STAT_FILEPATH, "r");
76 ERR("failed to open " STAT_FILEPATH);
80 while (fgets(line, sizeof(line), stat_fp)) {
81 if (!strncmp(line, prefix, strlen(prefix)) &&
82 sscanf(line + 4, "%llu %llu %llu %llu %llu %llu %llu",
83 &tmp.user, &tmp.nice, &tmp.system, &tmp.idle,
84 &tmp.iowait, &tmp.irq, &tmp.softirq) == 7) {
96 int procfs_read_meminfo(struct procfs_meminfo *usage)
98 ON_NULL_RETURN_VAL(usage, -1);
102 unsigned long mem_total = 0;
103 unsigned long mem_free = 0;
104 unsigned long cached = 0;
105 unsigned long mem_available = 0;
106 unsigned long swap_total = 0;
107 unsigned long swap_free = 0;
109 FILE *meminfo_fp = fopen(MEMINFO_FILEPATH, "r");
111 ERR("failed to open " MEMINFO_FILEPATH);
115 while (fgets(line, sizeof(line), meminfo_fp)) {
116 if (sscanf(line, "MemTotal: %lu", &mem_total) == 1)
117 usage->total = mem_total;
118 else if (sscanf(line, "MemFree: %lu", &mem_free) == 1)
119 usage->free = mem_free;
120 else if (sscanf(line, "Cached: %lu", &cached) == 1)
121 usage->cache = cached;
122 else if (sscanf(line, "MemAvailable: %lu", &value) == 1)
123 mem_available = value;
124 else if (sscanf(line, "SwapTotal: %lu", &value) == 1)
126 else if (sscanf(line, "SwapFree: %lu", &value) == 1)
130 // Calculate used memory
131 // MemAvailable is exposed since Linux 3.14, so handle also previous
134 if (mem_available > 0) {
135 if (mem_total > mem_available)
136 usage->used = mem_total - mem_available;
138 if ((mem_total > mem_free) && ((mem_total - mem_free) > cached))
139 usage->used = mem_total - mem_free - cached;
142 usage->swap = (swap_total > swap_free) ? (swap_total - swap_free) : 0;
149 int procfs_read_process_smaps(int pid, struct procfs_process_smaps *usage)
151 ON_NULL_RETURN_VAL(usage, -1);
152 ON_TRUE_RETURN_VAL(pid <= 0, -1);
154 char smapspath[64], line[256];
157 snprintf(smapspath, sizeof(smapspath), SMAPS_FILEPATH, pid);
159 FILE *smaps_fp = fopen(smapspath, "r");
161 ERR("failed to open path: %s", smapspath);
165 memset(usage, 0x0, sizeof(struct procfs_process_smaps));
167 while (fgets(line, sizeof(line), smaps_fp)) {
168 if (sscanf(line, "Size: %lu", &value) == 1)
170 else if (sscanf(line, "Rss: %lu", &value) == 1)
172 else if (sscanf(line, "Pss: %lu", &value) == 1)
174 else if (sscanf(line, "Shared_Clean: %lu", &value) == 1)
175 usage->shared_clean += value;
176 else if (sscanf(line, "Shared_Dirty: %lu", &value) == 1)
177 usage->shared_dirty += value;
178 else if (sscanf(line, "Private_Clean: %lu", &value) == 1)
179 usage->private_clean += value;
180 else if (sscanf(line, "Private_Dirty: %lu", &value) == 1)
181 usage->private_dirty += value;
189 static int read_file(const char *filename, char *buf, size_t buf_size)
191 FILE *fp = fopen(filename, "r");
194 size_t read = fread(buf, 1, buf_size, fp);
204 int procfs_read_process_stat(int pid, struct procfs_process_stat *usage)
206 ON_NULL_RETURN_VAL(usage, -1);
207 ON_TRUE_RETURN_VAL(pid <= 0, -1);
211 unsigned long long utime, stime;
215 snprintf(statpath, sizeof(statpath), PIDSTAT_FILEPATH, pid);
217 if ((read = read_file(statpath, buf, sizeof(buf))) <= 0) {
221 if (read == sizeof(buf)) {
225 char *name_end = strrchr(buf, ')');
226 if (!name_end) return -1;
254 , &utime, &stime, &rss) != 3) {
255 ERR("reading pid stat file failed");
259 usage->utime = utime;
260 usage->stime = stime;
266 int procfs_read_uptime(unsigned long *uptime)
268 ON_NULL_RETURN_VAL(uptime, -1);
272 FILE *uptime_fp = fopen(UPTIME_FILEPATH, "r");
274 ERR("failed to open " UPTIME_FILEPATH);
278 if (fscanf(uptime_fp, "%lf ", &duptime) != 1) {
279 ERR("failed to read " UPTIME_FILEPATH);
285 *uptime = (unsigned long)duptime;
290 int procfs_read_cpu_possible(int *cpu_count)
292 ON_NULL_RETURN_VAL(cpu_count, -1);
296 FILE *possible_fp = fopen(POSSIBLE_CPUS_FILEPATH, "r");
298 ERR("failed to open " POSSIBLE_CPUS_FILEPATH);
302 if (fscanf(possible_fp, "%d-%d", &dummy, &cpus) != 2) {
303 ERR("failed to read " POSSIBLE_CPUS_FILEPATH);
308 *cpu_count = cpus + 1;
315 bool _procfs_dirname_parse_pid(const char *dirname, int *pid)
319 if (!isdigit(*dirname))
322 if (sscanf(dirname, "%d", &parsed_pid) != 1) {
329 int procfs_iterate_pids(procfs_pid_iterator_cb iterator, void *user_data)
331 ON_NULL_RETURN_VAL(iterator, -1);
333 struct dirent *entry;
336 DIR *dir = opendir(PROC_DIR_PATH);
338 ERR("opendir failed.");
342 // According to POSIX docs readdir is not-thread safe.
343 // however in glib recent implementations readdir
344 // is thread safe, so we can avoid using locks here.
345 while ((entry = readdir(dir)) != NULL) {
346 if (!_procfs_dirname_parse_pid(entry->d_name, &pid))
349 if (iterator(pid, user_data))
359 int procfs_read_process_exe(int pid, char **exe)
361 ON_TRUE_RETURN_VAL(pid < 0, -1);
362 ON_NULL_RETURN_VAL(exe, -1);
369 snprintf(buf, sizeof(buf), PROC_PID_EXE_PATH, pid);
374 char *tmp = realloc(buffer, size);
380 ret = readlink(buf, buffer, size);
386 } while (ret >= size);
392 int procfs_read_process_cmdline(int pid, char **cmdline)
394 ON_TRUE_RETURN_VAL(pid < 0, -1);
395 ON_NULL_RETURN_VAL(cmdline, -1);
398 int read, read_total = 0;
401 snprintf(buf, sizeof(buf), PROC_PID_CMDLINE_PATH, pid);
403 FILE *cmdline_fp = fopen(buf, "r");
405 ERR("failed to open %s", buf);
409 while ((read = fread(buf, 1, sizeof(buf), cmdline_fp)) != 0) {
410 char *tmp = realloc(cmd, read_total + read + 1);
417 memcpy(cmd + read_total, buf, read);
421 if (read_total == 0) {
427 // clear string from end-of-string characters
428 for (int i = 0; i < read_total; ++i) {
432 cmd[read_total] = '\0';