Module: LinuxStat::ProcessInfo

Defined in:
lib/linux_stat/process_info.rb

Class Method Summary collapse

Class Method Details

.cmdline(pid = $$) ⇒ Object

cmdline(pid = $$)

Where pid is the process ID.

By default it is the id of the current process ($$)

It retuns the total command of the process.

The output is String. For example:

LinuxStat::ProcessInfo.cmdline

"ruby -r linux_stat -e p LinuxStat::ProcessInfo.cmdline"

If the info isn’t available it will return an empty frozen String.



54
55
56
57
58
59
60
61
# File 'lib/linux_stat/process_info.rb', line 54

def cmdline(pid = $$)
  file = "/proc/#{pid}/cmdline".freeze
  return ''.freeze unless File.readable?(file)

  _cmdline = IO.read(file)
  _cmdline.gsub!(?\u0000, ?\s)
  _cmdline.tap(&:strip!)
end

.command_name(pid = $$) ⇒ Object

command_name(pid = $$)

Where pid is the process ID.

By default it is the id of the current process ($$)

It retuns the total command name of the process.

The output is String. For example:

LinuxStat::ProcessInfo.command_name

"ruby"

If the info isn’t available it will return an empty frozen String.



78
79
80
81
82
83
84
85
86
# File 'lib/linux_stat/process_info.rb', line 78

def command_name(pid = $$)
  # Do note that the /proc/ppid/comm may not contain the full name
  file = "/proc/#{pid}/cmdline".freeze
  return ''.freeze unless File.readable?(file)

  _cmdline = IO.read(file)
  _cmdline.gsub!(?\u0000, ?\s)
  File.split(_cmdline.tap(&:strip!).split[0])[-1]
end

.cpu_stat(pid: $$, sleep: ticks_to_ms) ⇒ Object

cpu_stat(pid: $$, sleep: 1.0 / LinuxStat::Sysconf.sc_clk_tck)

Where pid is the process ID and sleep time is the interval between measurements.

By default it is the id of the current process ($$), and sleep is LinuxStat::Sysconf.sc_clk_tck

The smallest amount of available sleep time is 1.0 / LinuxStat::Sysconf.sc_clk_tck.

  • Note 1:

  1. Do note that the sleep time can slow down your application.

  2. And it’s only needed for the cpu usage calculation.

It retuns the CPU usage, threads, and the last executed CPU in Hash.

For example:

LinuxStat::ProcessInfo.cpu_stat

=> {:cpu_usage=>0.0, :threads=>1, :last_executed_cpu=>1}

But if the info isn’t available, it will return an empty Hash.

The :cpu_usage is in percentage. It’s also divided with the number of CPU.

:cpu_usage for example, will return 25.0 if the CPU count is 4, and the process is using 100% of a thread / core.

A value of 100.0 indicates it is using 100% processing power available to the system.

The :threads returns the number of threads for the process. The value is a Integer.

  • Note 2:

  1. If you just need the CPU usage run LinuxStat::ProcessInfo.cpu_usage(pid = $$)

  2. If you just need the threads run LinuxStat::ProcessInfo.threads(pid = $$)

  3. If you just need the last executed CPU run LinuxStat::ProcessInfo.last_executed_cpu(pid = $$)

  4. Running this method is slower and it opens multiple files at once

Only use this method if you need all of the data at once, in such case, it’s more efficient to use this method.

The :last_executed_cpu also returns an Integer indicating the last executed cpu of the process.



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
# File 'lib/linux_stat/process_info.rb', line 247

def cpu_stat(pid: $$, sleep: ticks_to_ms)
  file = "/proc/#{pid}/stat"
  return {} unless File.readable?(file)

  ticks = get_ticks

  utime, stime, starttime = IO.read(file)
    .split.values_at(13, 14, 21).map(&:to_f)
  uptime = IO.read('/proc/uptime'.freeze).to_f * ticks

  total_time = utime + stime
  idle1 = uptime - starttime - total_time

  sleep(sleep)
  stat = IO.read(file).split

  utime2, stime2, starttime2 = stat.values_at(13, 14, 21).map(&:to_f)
  uptime = IO.read('/proc/uptime'.freeze).to_f * ticks

  total_time2 = utime2 + stime2
  idle2 = uptime - starttime2 - total_time2

  totald = idle2.+(total_time2).-(idle1 + total_time)
  cpu = totald.-(idle2 - idle1).fdiv(totald).*(100).round(2).abs./(LinuxStat::CPU.count)

  {
    cpu_usage: cpu,
    threads: stat[19].to_i,
    last_executed_cpu: stat[38].to_i
  }
end

.cpu_usage(pid: $$, sleep: ticks_to_ms) ⇒ Object

cpu_usage(pid: $$, sleep: 1.0 / LinuxStat::Sysconf.sc_clk_tck)

Where pid is the process ID and sleep time is the interval between measurements.

By default it is the id of the current process ($$), and sleep is 1.0 / LinuxStat::Sysconf.sc_clk_tck

The smallest amount of available sleep time is LinuxStat::Sysconf.sc_clk_tck.

It retuns the CPU usage in Float.

For example:

LinuxStat::ProcessInfo.cpu_usage

=> 10.0

A value of 100.0 indicates it is using 100% processing power available to the system.

But if the info isn’t available, it will return nil.

This method is more efficient than running LinuxStat::ProcessInfo.cpu_stat()



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
# File 'lib/linux_stat/process_info.rb', line 301

def cpu_usage(pid: $$, sleep: ticks_to_ms)
  file = "/proc/#{pid}/stat"
  return nil unless File.readable?(file)

  ticks = get_ticks

  utime, stime, starttime = IO.read(file)
    .split.values_at(13, 14, 21).map(&:to_f)
  uptime = IO.read('/proc/uptime'.freeze).to_f * ticks

  total_time = utime + stime
  idle1 = uptime - starttime - total_time

  sleep(sleep)

  utime2, stime2, starttime2 = IO.read(file)
    .split.values_at(13, 14, 21).map(&:to_f)
  uptime = IO.read('/proc/uptime'.freeze).to_f * ticks

  total_time2 = utime2 + stime2
  idle2 = uptime - starttime2 - total_time2

  totald = idle2.+(total_time2).-(idle1 + total_time)
  totald.-(idle2 - idle1).fdiv(totald).*(100).round(2).abs./(LinuxStat::CPU.count)
end

.gid(pid = $$) ⇒ Object

gid(pid = $$)

returns the GIDs of the process as an Hash containing the following data:

:real, :effective, :saved_set, :filesystem_uid

If the info isn’t available or the argument passed doesn’t exist as a process ID, it will return an empty Hash.



406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/linux_stat/process_info.rb', line 406

def gid(pid = $$)
  file = "/proc/#{pid}/status".freeze
  return nil unless File.readable?(file)

  data = IO.foreach(file.freeze).find { |x|
    x[/Gid.*\d*/]
  }.split.drop(1)

  {
    real: data[0].to_i,
    effective: data[1].to_i,
    saved_set: data[2].to_i,
    filesystem_uid: data[3].to_i
  }
end

.last_executed_cpu(pid = $$) ⇒ Object

last_executed_cpu(pid = $$)

Where pid is the process ID.

By default it is the id of the current process ($$)

It retuns the last executed CPU in Integer.

For example:

LinuxStat::ProcessInfo.last_executed_cpu

=> 2

But if the info isn’t available, it will return nil.

This method is way more efficient than running LinuxStat::ProcessInfo.cpu_stat()



369
370
371
372
373
374
# File 'lib/linux_stat/process_info.rb', line 369

def last_executed_cpu(pid = $$)
  file = "/proc/#{pid}/stat".freeze
  return nil unless File.readable?(file)

  IO.read(file).split[38].to_i
end

.mem_stat(pid = $$) ⇒ Object

mem_stat(pid = $$)

Where pid is the process ID.

By default it is the id of the current process ($$)

It retuns the memory, virtual memory, and resident memory of the process.

All values are in kilobytes.

The output is a Hash. For example:

LinuxStat::ProcessInfo.mem_stat

{:memory=>8515.584, :virtual_memory=>79781.888, :resident_memory=>13955.072}
  • Note:

  1. If you need only memory usage of a process, run LinuxStat::ProcessInfo.memory(pid)

  2. If you need only virtual memory for a process, run LinuxStat::ProcessInfo.virtual_memory(pid)

  3. If you need only resident memory of a process, run LinuxStat::ProcessInfo.resident_memory(pid)

This method opens opens multiple files.

But if you need all of the info, then running this method once is efficient.

If the info isn’t available it will return an empty Hash.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/linux_stat/process_info.rb', line 114

def mem_stat(pid = $$)
  statm = "/proc/#{pid}/statm".freeze
  return {} unless File.readable?(statm)

  data = IO.read(statm).split

  _rss_anon = (data[1] && data[2]) ? data[1].to_i.-(data[2].to_i).*(pagesize).fdiv(1000) : nil
  _virtual_memory = data[0] ? data[0].to_i*(pagesize).fdiv(1000) : nil
  _resident_memory = data[1] ? data[1].to_i.*(pagesize).fdiv(1000) : nil

  {
    memory: _rss_anon,
    virtual_memory: _virtual_memory,
    resident_memory: _resident_memory
  }
end

.memory(pid = $$) ⇒ Object

memory(pid = $$)

Where pid is the process ID.

By default it is the id of the current process ($$)

It retuns the memory of the process. The value is in kilobytes.

The output is an Integer. For example:

LinuxStat::ProcessInfo.memory

8523.776

If the info isn’t available it will return nil.



147
148
149
150
151
152
153
# File 'lib/linux_stat/process_info.rb', line 147

def memory(pid = $$)
  file = "/proc/#{pid}/statm".freeze
  return nil unless File.readable?(file)

  data = IO.read(file).split
  (data[1] && data[2]) ? data[1].to_i.-(data[2].to_i).*(pagesize).fdiv(1000) : nil
end

.nice(pid = $$) ⇒ Object

nice(pid = $$)

Returns the nice of the process

The output value is an Integer ranging from -20 to 19

-20 means the process has high priority, and 19 means the process has low priority

If the info isn’t available or the argument passed doesn’t exist as a process ID, it will return nil.



540
541
542
543
544
545
# File 'lib/linux_stat/process_info.rb', line 540

def nice(pid = $$)
  file = "/proc/#{pid}/stat"
  return nil unless File.readable?(file)

  IO.foreach(file, ' ').first(19)[-1].to_i
end

.owner(pid = $$) ⇒ Object

owner(pid = $$)

Returns the owner of the process But if the status is not available, it will return an empty frozen String.



427
428
429
430
431
432
433
434
435
436
# File 'lib/linux_stat/process_info.rb', line 427

def owner(pid = $$)
  file = "/proc/#{pid}/status".freeze
  return ''.freeze unless File.readable?(file)

  gid = IO.foreach(file.freeze).find { |x|
    x[/Gid.*\d*/]
  }.split.drop(1)[2].to_i

  LinuxStat::User.username_by_gid(gid)
end

.resident_memory(pid = $$) ⇒ Object

resident_memory(pid = $$)

Where pid is the process ID.

By default it is the id of the current process ($$)

It retuns the resident memory for the process.

The value is in kilobytes.

The output is an Integer. For example:

LinuxStat::ProcessInfo.cpu_stat

=> 13996.032

If the info isn’t available it will return nil.



197
198
199
200
201
202
203
# File 'lib/linux_stat/process_info.rb', line 197

def resident_memory(pid = $$)
  file = "/proc/#{pid}/statm".freeze
  return nil unless File.readable?(file)

  _vm_rss = IO.read(file).split[1]
  _vm_rss ? _vm_rss.to_i.*(pagesize).fdiv(1000) : nil
end

.running_time(pid = $$) ⇒ Object

running_time(pid = $$)

Returns the time (in seconds, as Float) the process is running for.

For example:

LinuxStat::ProcessInfo.running_time 14183

=> 1947.619999999999

If the info isn’t available or the argument passed doesn’t exist as a process ID, it will return nil.



497
498
499
500
501
502
503
504
505
# File 'lib/linux_stat/process_info.rb', line 497

def running_time(pid = $$)
  stat_file = "/proc/#{pid}/stat".freeze
  uptime = "/proc/uptime".freeze

  @@u_readable ||= File.readable?(uptime)
  return nil unless @@u_readable && File.readable?(stat_file)

  IO.foreach(uptime, ' '.freeze).next.to_f - (IO.read(stat_file).split[21].to_i / get_ticks)
end

.start_time(pid = $$) ⇒ Object

start_time(pid = $$)

Returns the time (as Time object) the process was started.

For example:

LinuxStat::ProcessInfo.start_time 14183

=> 2020-12-16 13:31:43 +0000

If the info isn’t available or the argument passed doesn’t exist as a process ID, it will return nil.

The timezone returned based on current TZ. Thus the timezone could be affected by changing the ENV variable.

Don’t trust the timezone returned by the time.



480
481
482
483
484
# File 'lib/linux_stat/process_info.rb', line 480

def start_time(pid = $$)
  # Getting two Time objects and dealing with floating point numbers
  # Just to make sure the time goes monotonically
  Time.at(start_time_epoch(pid))
end

.start_time_epoch(pid = $$) ⇒ Object

start_time_epoch(pid = $$)

Returns the epoch time (as Integer) the process was started.

For example:

LinuxStat::ProcessInfo.start_time_epoch 526

=> 1608097744

If the info isn’t available or the argument passed doesn’t exist as a process ID, it will return nil.



449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/linux_stat/process_info.rb', line 449

def start_time_epoch(pid = $$)
  stat_file = "/proc/#{pid}/stat".freeze
  uptime = "/proc/uptime".freeze

  @@u_readable ||= File.readable?(uptime)
  return nil unless @@u_readable && File.readable?(stat_file)

  u = IO.foreach(uptime, ' '.freeze).next.to_f
  st = (IO.foreach(stat_file, ' '.freeze).first(22)[-1].to_f / get_ticks)

  # Getting two Time objects and dealing with floating point numbers
  # Just to make sure the time goes monotonically
  Time.now.-(u - st).to_i
end

.state(pid = $$) ⇒ Object

state(pid = $$)

Returns the state of the process as a frozen String

  • A process could have multiple states:

  1. S => Sleeping

  2. R => Running

  3. I => Idle

  4. Z => Zombie

It returns any one of them.

If the info isn’t available or the argument passed doesn’t exist as a process ID, it will return an empty String.



525
526
527
528
529
# File 'lib/linux_stat/process_info.rb', line 525

def state(pid = $$)
  file = "/proc/#{pid}/stat".freeze
  return ''.freeze unless File.readable?(file)
  IO.foreach(file, ' '.freeze).first(3)[-1].tap(&:rstrip!).freeze
end

.threads(pid = $$) ⇒ Object

threads(pid = $$)

Where pid is the process ID.

By default it is the id of the current process ($$)

It retuns the threads for the current process in Integer.

For example:

LinuxStat::ProcessInfo.threads

=> 2

But if the info isn’t available, it will return nil.

This method is way more efficient than running LinuxStat::ProcessInfo.cpu_stat()



344
345
346
347
348
349
350
# File 'lib/linux_stat/process_info.rb', line 344

def threads(pid = $$)
  file = "/proc/#{pid}/stat".freeze
  return nil unless File.readable?(file)

  data = IO.foreach(file, ' '.freeze).first(20)[-1]
  data ? data.to_i : nil
end

.total_io(pid = $$) ⇒ Object

total_io(pid = $$)

Where pid is the process ID.

By default it is the id of the current process ($$)

It retuns the total read/write caused by a process.

The output is Hash.

For example:

LinuxStat::ProcessInfo.total_io

{:read_bytes=>0, :write_bytes=>0}

The output is only based on the total disk IO the process has done.

If the info isn’t available it will return an empty Hash.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/linux_stat/process_info.rb', line 23

def total_io(pid = $$)
  return {} unless File.readable?("/proc/#{pid}/io".freeze)
  out = {}

  IO.readlines("/proc/#{pid}/io".freeze).each { |x|
    x.strip!

    if x[/^(read|write)_bytes:\s*\d*$/]
      splitted = x.split
      out.merge!(splitted[0].split(?:)[0].to_sym => splitted[-1].to_i)
    end
  }

  out
end

.uid(pid = $$) ⇒ Object

uid(pid = $$)

returns the UIDs of the process as an Array of Integers.

If the info isn’t available it returns an empty Array.



382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/linux_stat/process_info.rb', line 382

def uid(pid = $$)
  file = "/proc/#{pid}/status".freeze
  return nil unless File.readable?(file)

  data = IO.foreach(file.freeze).find { |x|
    x[/Uid.*\d*/]
  }.to_s.split.drop(1)

  {
    real: data[0].to_i,
    effective: data[1].to_i,
    saved_set: data[2].to_i,
    filesystem_uid: data[3].to_i
  }
end

.virtual_memory(pid = $$) ⇒ Object

virtual_memory(pid = $$)

Where pid is the process ID.

By default it is the id of the current process ($$)

It retuns the virtual memory for the process.

The value is in kilobytes.

The output is an Integer. For example:

LinuxStat::ProcessInfo.virtual_memory

79781.888

If the info isn’t available it will return nil.



172
173
174
175
176
177
178
# File 'lib/linux_stat/process_info.rb', line 172

def virtual_memory(pid = $$)
  file = "/proc/#{pid}/statm".freeze
  return nil unless File.readable?(file)

  _virtual_memory = IO.read(file).split[0]
  _virtual_memory ? _virtual_memory.to_i.*(pagesize).fdiv(1000) : nil
end