1 /*
2   Purpose : Parse /proc/meminfo
3   Author  : Ky-Anh Huynh
4   Date    : 2017 Sep 10th
5   Licens  : MIT
6 */
7 
8 module dusybox.meminfo;
9 
10 import std.stdio;
11 import std.format;
12 
13 /*
14   $ cat /proc/meminfo
15   MemTotal:       16337684 kB
16   MemFree:         2612196 kB
17   MemAvailable:   12543580 kB
18   Buffers:         1127916 kB
19   Cached:          7467756 kB
20   ...
21 */
22 size_t[string] meminfo(in string[] lines) {
23   size_t[string] results;
24 
25   foreach(string line; lines) {
26     string key;
27     size_t value;
28     auto line_st = line;
29     try {
30       line_st.formattedRead!"%s: %s"(key, value);
31       results[key] = value;
32     }
33     catch (Exception exc) {
34       debug(2) stderr.write("meminfo: Unable to parse (key, value) from line '%s'", line);
35     }
36   }
37 
38   if ("Cached" !in results
39     || "MemAvailable" !in results
40     || "MemTotal" !in results
41     || "MemFree" !in results
42     || "Buffers" !in results
43     || "Shmem" !in results
44     || "SReclaimable" !in results) {
45 
46     stderr.writeln("meminfo: Missing some memory information.");
47     return results;
48   }
49 
50   results["PageCached"] = results["Cached"];
51   results["Cached"] = results["PageCached"] + results["SReclaimable"];
52 
53   /*
54   See https://gitlab.com/procps-ng/procps/blob/master/proc/sysinfo.c#L792
55 
56   Quoted:
57     if kb_main_available is greater than kb_main_total or our calculation of
58     mem_used overflows, that's symptomatic of running within a lxc container
59     where such values will be dramatically distorted over those of the host.
60   */
61   if (results["MemAvailable"] > results["MemTotal"]) {
62     results["MemAvailable"] = results["MemFree"];
63   }
64   auto mem_used = results["MemTotal"] - results["MemFree"];
65   if (mem_used >= results["Buffers"] + results["Cached"]) {
66     mem_used -= results["Buffers"] + results["Cached"];
67   }
68 
69   results["MemUsed"] = mem_used;
70 
71   return results;
72 }
73 
74 size_t[string] meminfo(in string filename = "/proc/meminfo") {
75   string[] lines;
76 
77   foreach(string line; std.stdio.lines(filename.File("r"))) {
78     lines ~= line;
79   }
80   return meminfo(lines);
81 }
82 
83 unittest {
84   string[] empty_arr;
85   size_t[string] empty_result;
86 
87   assert(empty_result == meminfo(empty_arr));
88 
89   const string[] lines = [
90     "MemTotal:       10000 kB\n",
91     "MemFree:         1000 kB\n"];
92 
93   auto results = meminfo(lines);
94 
95   assert(results["MemTotal"] == 10000, "Unable to read MemTotal.");
96   assert(results["MemFree"]  == 1000, "Unable to read MemFree.");
97 
98   results = meminfo();
99   assert(results["Active"] > 0);
100 }