-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathprocdata.lua
More file actions
330 lines (302 loc) · 8.16 KB
/
procdata.lua
File metadata and controls
330 lines (302 loc) · 8.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
--
-- Access to system information provided by the Linux /proc pseudo-file system
--
local procdata = {}
local unistd = require("posix.unistd")
local lfs = require("lfs")
--
-- Platform Information
--
-- OS, release, gcc version
local plat_file = "/proc/version"
local plat_patt = "([^%s]+)%s+version%s+([^%s]+)"
local gcc_patt = ".-gcc version ([%.%d]+)"
function procdata.get_platform()
local fd = io.open(plat_file)
if not fd then
return nil, "Error opening "..plat_file
end
local info = fd:read("*a")
io.close(fd)
local os, release = info:match(plat_patt)
if not os then
os = "unknown"
release = "unknown"
end
local gcc = info:match(gcc_patt)
if not gcc then
gcc = "unknown"
end
return {os = os, release = release, gcc = gcc}
end
--
-- Memory Information
--
local mem_fact = {k = 1024, m = 1024^2, g = 1024^3, t = 1024^4}
local mem_patt = ":%s*(%d+)"
local unit_patt = ":%s*%d+%s*(%a+)"
local mem_file = "/proc/meminfo"
local ram_patt = "MemTotal"
local swap_patt = "SwapTotal"
local ramf_patt = "MemFree"
local swapf_patt = "SwapFree"
-- Convert memory info from provided unit to bytes
local function getm(info, type)
local m = info:match(type..mem_patt)
if not m then return nil end
m = tonumber(m)
local um = info:match(type..unit_patt)
if not um then return m end
local f = mem_fact[(um:sub(1,1)):lower()]
if f then return m * f else return m end
end
-- Total memory (ram and swap) in bytes
function procdata.get_total_memory()
local fd = io.open(mem_file)
if not fd then
return nil, "Error opening "..mem_file
end
local info = fd:read("*a")
io.close(fd)
local ram = getm(info,ram_patt)
if not ram then return nil, "Error getting ram" end
local swap = getm(info,swap_patt)
if not swap then return nil, "Error getting swap" end
return {ram = ram, swap = swap}
end
-- Free memory (ram and swap) in bytes
function procdata.get_free_memory()
local fd = io.open(mem_file)
if not fd then
return nil, "Error opening "..mem_file
end
local info = fd:read("*a")
io.close(fd)
local ram = getm(info,ramf_patt)
if not ram then return nil, "Error getting free ram" end
local swap = getm(info,swapf_patt)
if not swap then return nil, "Error getting free swap" end
return {ram = ram, swap = swap}
end
-- Used memory (ram and swap) in bytes
function procdata.get_used_memory()
local tmem, fmem, err
tmem,err = procdata.get_total_memory()
if not tmem then return nil, err end
fmem, err = procdata.get_free_memory()
if not fmem then return nil, err end
return {ram = tmem.ram - fmem.ram, swap = tmem.swap - fmem.swap}
end
--
-- CPU information
--
-- Number of cpus
local ncpus_file = "/proc/stat"
local ncpus_patt = "cpu%d+"
function procdata.get_num_cpus()
local fd = io.open(ncpus_file)
if not fd then
return nil, "Error opening "..ncpus_file
end
local info = fd:read("*a")
io.close(fd)
local ncpus = 0
for _ in info:gmatch(ncpus_patt) do
ncpus = ncpus + 1
end
if ncpus == 0 then
return 1
else
return ncpus
end
end
-- Clock speed (in MHz)
local speed_alt1_file = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"
local speed_alt2_file = "/proc/cpuinfo"
local speed_alt2_patt = "cpu MHz%s*:%s*(%d+)"
function procdata.get_clock_speed()
local fd, info, cspeed
-- first we try /sys
fd = io.open(speed_alt1_file)
if fd then
cspeed = tonumber(fd:read("*a"))/1000
io.close(fd)
else
-- alternatively we try /proc/cpuinfo to get highest speed
fd = io.open(speed_alt2_file)
if not fd then
return nil,
"Cannot find either ..speed_alt1_file".. "or "..speed_alt2_file
end
info = fd:read("*a")
io.close(fd)
cspeed = 0
for sp in info:gmatch(speed_alt2_patt) do
sp = tonumber(sp)
if sp > cspeed then
cspeed = sp
end
end
if cspeed == 0 then
return nil, "Error getting cpu clock speed"
end
end
return cspeed
end
-- Load average (loadavg, executing/total scheduling entities)
local lavg_file = "/proc/loadavg"
local lavg_patt = "(%d+%.%d+)%s+(%d+%.%d+)%s+(%d+%.%d+)"
local ent_patt = "%d+%.%d+%s+%d+%.%d+%s+%d+%.%d+%s+(%d+)/(%d+)"
function procdata.get_cpu_load()
local fd = io.open(lavg_file)
if not fd then
return nil, "Error opening "..lavg_file
end
local info = fd:read("*a")
io.close(fd)
local l1,l5,l15 = info:match(lavg_patt)
if not l1 then
return nil, "Error getting load average"
end
local t = {l1 = tonumber(l1), l5 = tonumber(l5), l15 = tonumber(l15)}
local re,te = info:match(ent_patt)
if re then
t.executing = tonumber(re)
t.total = tonumber(te)
end
return t
end
-- CPU times (user, nice, system, idle) in seconds
local ctimes_file = "/proc/stat"
local ctimes_patt = "cpu%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)"
function procdata.get_cpu_times()
local fd = io.open(ctimes_file)
if not fd then
return nil, "Error opening "..ctimes_file
end
local info = fd:read("*a")
io.close(fd)
local usr,nice,sys,idle = info:match(ctimes_patt)
if not usr then
return nil, "Error getting cpu times"
end
local cticks = unistd.sysconf(unistd._SC_CLK_TCK)
return {user = tonumber(usr)/cticks,
nice = tonumber(nice)/cticks,
system = tonumber(sys)/cticks,
idle = tonumber(idle)/cticks}
end
-- System uptime in seconds
local uptime_file = "/proc/uptime"
function procdata.get_uptime()
local fd = io.open(uptime_file)
if not fd then
return nil, "Error opening "..uptime_file
end
local info = fd:read("*a")
io.close(fd)
local upt = info:match("(%d+%.%d+)")
if not upt then
return nil, "Error getting system uptime"
end
return upt
end
--
-- Processes information
--
-- pid, ppid, command string, state
-- user and system time (in seconds)
-- starttime (in seconds since standard epoch)
-- memory use: virtual, resident and shared (in bytes)
local skip = string.rep("[^%s]*%s+",9)
local stat_patt = "%s*(%d+)%s+%(([^)]+)%)%s+(%a)%s+(%d+)%s+"..skip..
"(%d+)%s+(%d+)"
local statm_patt = "%s*(%d+)%s+(%d+)%s+(%d+)"
function procdata.get_process_info(pid)
if not pid then
return nil, "Process ID (pid) not provided"
end
local fproc = "/proc/"..pid
local fstat = fproc.."/stat"
local fstatm = fproc.."/statm"
local fd = io.open(fstat)
if not fd then
return nil, "Process "..pid.." not found"
end
local info = fd:read("*a")
io.close(fd)
local pid,comm,state,ppid,utime,stime =
info:match(stat_patt)
if not pid then
return nil, "Error getting process "..pid.." info"
end
local cticks = unistd.sysconf(unistd._SC_CLK_TCK)
utime = tonumber(utime)/cticks
stime = tonumber(stime)/cticks
local starttime
local attr,err = lfs.attributes(fproc)
if not attr then
starttime = 0
else
starttime = attr.change
end
fd = io.open(fstatm)
local vmsize, rss, shared
if fd then
info = fd:read("*a")
io.close(fd)
vmsize,rss,shared = info:match(statm_patt)
local psize = 4096
if not vmsize then
vmsize = 0
rss = 0
shared = 0
else
vmsize = tonumber(vmsize) * psize
rss = tonumber(rss) * psize
shared = tonumber(shared) * psize
end
end
local t = {
pid = tonumber(pid), ppid = tonumber(ppid),
comm = comm, state = state,
utime = utime, stime = stime, starttime = starttime,
vmsize = vmsize, rss = rss, shared = shared,
}
t.walltime = os.time() - t.starttime
return t
end
-- get all processes
function procdata.get_processes()
local pids = {}
for dir in lfs.dir("/proc") do
if tonumber(dir) then
table.insert(pids,tonumber(dir))
end
end
return pids
end
-- get process children
local ppid_patt = "%s*%d+%s+%([^)]+%)%s+%a%s+(%d+).*"
function procdata.get_children(pid)
if not pid then
return nil, "Process ID (pid) not provided"
end
local children = {}
local pids = procdata.get_processes()
for i = 1, #pids do
local child = pids[i]
local file_pid = "/proc/"..child.."/stat"
local fd = io.open(file_pid)
if fd then
local info = fd:read("*a")
io.close(fd)
local ppid = info:match(ppid_patt)
if tonumber(ppid) == pid then
table.insert(children, child)
end
end
end
return children
end
return procdata