Skip to content

Commit 20879e5

Browse files
committed
Dump /proc/[pid]/statm for all processes in print_mem_stats()
It can be usefull to see statm info about all processes in the system during earlyoom killing. In some cases this may allow tracking of "heavy" processes that haven't been killed but are still present in system.
1 parent d38e29b commit 20879e5

File tree

4 files changed

+128
-19
lines changed

4 files changed

+128
-19
lines changed

kill.c

+1-19
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "kill.h"
1919
#include "meminfo.h"
2020
#include "msg.h"
21+
#include "utils.h"
2122

2223
// Processes matching "--prefer REGEX" get OOM_SCORE_PREFER added to their oom_score
2324
#define OOM_SCORE_PREFER 300
@@ -34,25 +35,6 @@
3435
// At most 1 notification per second when --dryrun is active
3536
#define NOTIFY_RATELIMIT 1
3637

37-
static bool isnumeric(char* str)
38-
{
39-
int i = 0;
40-
41-
// Empty string is not numeric
42-
if (str[0] == 0)
43-
return false;
44-
45-
while (1) {
46-
if (str[i] == 0) // End of string
47-
return true;
48-
49-
if (isdigit(str[i]) == 0)
50-
return false;
51-
52-
i++;
53-
}
54-
}
55-
5638
static void notify_dbus(const char* summary, const char* body)
5739
{
5840
int pid = fork();

meminfo.c

+99
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
#include <string.h>
1212
#include <sys/stat.h>
1313
#include <unistd.h>
14+
#include <dirent.h>
1415

1516
#include "globals.h"
1617
#include "meminfo.h"
1718
#include "msg.h"
1819
#include "proc_pid.h"
20+
#include "utils.h"
1921

2022
/* Parse the contents of /proc/meminfo (in buf), return value of "name"
2123
* (example: "MemTotal:")
@@ -269,6 +271,101 @@ int get_uid(int pid)
269271
return (int)st.st_uid;
270272
}
271273

274+
static int get_process_statm(char *buf, size_t buf_size, int pid)
275+
{
276+
char path[PATH_LEN] = { 0 };
277+
snprintf(path, sizeof(path), "%s/%d/statm", procdir_path, pid);
278+
279+
FILE *f = fopen(path, "r");
280+
if (!f) {
281+
debug("%s: failed to open file '%s': %s\n", __func__, path, strerror(errno));
282+
return -EINVAL;
283+
}
284+
285+
char *res = fgets(buf, (int)buf_size, f);
286+
fclose(f);
287+
if (!res) {
288+
debug("%s: failed to read statm info for pid=%d\n", __func__, pid);
289+
return -EINVAL;
290+
}
291+
292+
return 0;
293+
}
294+
295+
static void dump_all_process_meminfo(int __attribute__((format(printf, 1, 2))) (*out_func)(const char* fmt, ...))
296+
{
297+
DIR *procdir = opendir(procdir_path);
298+
if (procdir == NULL) {
299+
fatal(5, "%s: could not open /proc: %s", __func__, strerror(errno));
300+
}
301+
302+
static size_t page_size;
303+
if (page_size == 0) {
304+
page_size = (size_t)sysconf(_SC_PAGESIZE);
305+
if (page_size <= 0) {
306+
fatal(1, "could not read page size\n");
307+
}
308+
}
309+
310+
char *statm = calloc(1, page_size);
311+
if (!statm) {
312+
debug("%s: failed to allocate memory\n", __func__);
313+
return;
314+
}
315+
316+
out_func("===========================\n");
317+
out_func("Dump all processes statm info. More details: Documentation/filesystems/proc.txt\n");
318+
out_func("%%proc (%%pid): %%size %%resident %%shared %%trs %%trs %%drs %%dt\n");
319+
320+
while (1) {
321+
errno = 0;
322+
struct dirent *d = readdir(procdir);
323+
if (d == NULL) {
324+
if (errno != 0)
325+
warn("%s: readdir error: %s\n", __func__, strerror(errno));
326+
break;
327+
}
328+
329+
// proc contains lots of directories not related to processes,
330+
// skip them
331+
if (!isnumeric(d->d_name))
332+
continue;
333+
334+
errno = 0;
335+
int pid = (int)strtol(d->d_name, NULL, 10);
336+
if (errno != 0) {
337+
warn("%s: strtol() failed: %s\n", __func__, strerror(errno));
338+
continue;
339+
}
340+
341+
// Skip kthreads
342+
pid_stat_t stat;
343+
if (!parse_proc_pid_stat(&stat, pid))
344+
continue;
345+
346+
if (stat.rss <= 0)
347+
continue;
348+
349+
int ret = get_process_statm(statm, page_size, pid);
350+
if (ret < 0)
351+
continue;
352+
353+
char name[PATH_LEN] = { 0 };
354+
ret = get_comm(pid, name, sizeof(name));
355+
if (ret < 0) {
356+
debug("pid %d: error reading process name: %s\n", pid, strerror(-ret));
357+
continue;
358+
}
359+
360+
out_func("%s (%d): %s", name, pid, statm);
361+
}
362+
363+
free(statm);
364+
closedir(procdir);
365+
366+
out_func("===========================\n");
367+
}
368+
272369
/* Print a status line like
273370
* mem avail: 5259 MiB (67 %), swap free: 0 MiB (0 %)"
274371
* as an informational message to stdout (default), or
@@ -283,4 +380,6 @@ void print_mem_stats(int __attribute__((format(printf, 1, 2))) (*out_func)(const
283380
m.SwapFreeKiB / 1024,
284381
m.SwapTotalKiB / 1024,
285382
m.SwapFreePercent);
383+
384+
dump_all_process_meminfo(out_func);
286385
}

utils.c

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include <ctype.h>
2+
3+
#include "utils.h"
4+
5+
int isnumeric(char* str)
6+
{
7+
int i = 0;
8+
9+
// Empty string is not numeric
10+
if (str[0] == 0)
11+
return 0;
12+
13+
while (1) {
14+
if (str[i] == 0) // End of string
15+
return 1;
16+
17+
if (isdigit(str[i]) == 0)
18+
return 0;
19+
20+
i++;
21+
}
22+
}

utils.h

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef __UTILS_H__
2+
#define __UTILS_H__
3+
4+
int isnumeric(char* str);
5+
6+
#endif

0 commit comments

Comments
 (0)