linux_stat 0.6.5 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,46 @@ rescue LoadError
7
7
  abort "The Gem needs to be installed before this test can be run!"
8
8
  end
9
9
 
10
+ # Gradient colour to strings
11
+ class String
12
+ def colourize(colour = 1, flip: false)
13
+ colours, line_length = [], -1
14
+ temp = ''
15
+
16
+ each_line do |c|
17
+ n, i = c.length, -1
18
+
19
+ if line_length != n
20
+ step, line_length = 255.0./(n), n
21
+ colours.clear
22
+
23
+ while (i += 1) < n
24
+ l = i.*(step)
25
+ colours.<<(
26
+ case colour
27
+ when 0 then [ l.*(2).to_i.clamp(0, 255), l.to_i.clamp(0, 255), 255.-(l).to_i.clamp(0, 255) ]
28
+ when 1 then [ 255, 255.-(l).to_i.clamp(0, 255), l.to_i.clamp(0, 255) ]
29
+ when 2 then [ l.to_i.clamp(0, 255), 255.-(l).to_i.clamp(0, 255), l.to_i.clamp(0, 255) ]
30
+ when 3 then [ l.*(2).to_i.clamp(0, 255), 255.-(l).to_i.clamp(0, 255), 100.+(l / 2).to_i.clamp(0, 255) ]
31
+ when 4 then [ 30, 255.-(l / 2).to_i.clamp(0, 255), 110.+(l / 2).to_i.clamp(0, 255) ]
32
+ when 5 then [ 255.-(l * 2).to_i.clamp(0, 255), l.to_i.clamp(0, 255), 200 ]
33
+ when 6 then [ 50.+(255 - l).to_i.clamp(0, 255), 255.-(l / 2).to_i.clamp(0, 255), (l * 2).to_i.clamp(0, 255) ]
34
+ else [ l.*(2).to_i.clamp(0, 255), 255.-(l).to_i.clamp(0, 255), 100.+(l / 2).to_i.clamp(0, 255) ]
35
+ end
36
+ )
37
+ end
38
+
39
+ colours.reverse! if flip
40
+ end
41
+
42
+ i = -1
43
+ temp.concat "\e[38;2;#{colours[i][0]};#{colours[i][1]};#{colours[i][2]}m#{c[i]}" while (i += 1) < n
44
+ end
45
+
46
+ temp << "\e[0m".freeze
47
+ end
48
+ end
49
+
10
50
  # Check which conflicting argument (e.g., `-md -html` together) is passed last
11
51
  # Always use the last argument
12
52
  conflicting, hash = [
@@ -54,7 +94,6 @@ execute.sort.each do |c|
54
94
  next if e.class != Module && e.class != Class
55
95
 
56
96
  meths = e.methods(false).sort
57
- next if meths.any? { |a| e.method(a).arity > 0 }
58
97
 
59
98
  if meths.length > 0
60
99
  if MARKDOWN
@@ -67,20 +106,68 @@ execute.sort.each do |c|
67
106
  end
68
107
 
69
108
  meths.each do |meth|
109
+ arg = nil
110
+ params = e.method(meth).parameters
111
+
112
+ param = ''
113
+
114
+ params.each do |p|
115
+ case p[0]
116
+ when :opt
117
+ param << "#{p[1]}, "
118
+ when :key
119
+ param << "#{p[1]}:, "
120
+ when :req
121
+ param << "#{p[1] || 'arg'}, "
122
+ end
123
+ end
124
+
125
+ param.delete_suffix!(", ")
126
+
127
+ if e.method(meth).arity > 0
128
+ if c == :PrettifyBytes
129
+ arg = rand(10 ** 15)
130
+ elsif c == :FS
131
+ arg = '/'
132
+ else
133
+ next
134
+ end
135
+ end
136
+
137
+ disp_meth = "#{meth}"
138
+ disp_meth.concat(arg ? "(#{param} = #{arg.inspect})" : "(#{param})")
139
+
70
140
  time = Time.now
71
- v = e.send(meth)
141
+ v = arg ? e.send(meth, arg) : e.send(meth)
72
142
  time2 = Time.now
73
143
  time = time2.-(time).*(1_000_000).round(3)
74
144
 
75
145
  v = v.inspect
76
146
  dis = v.length > 253 ? v[0..250].strip + '...'.freeze : v
77
147
 
148
+ source = e.singleton_method(meth).source_location.to_a
149
+ src, src_meth = '', ''
150
+
151
+ unless source.empty?
152
+ src << " File: #{File.split(source[0])[-1]} | Line: #{source[1]}\n"
153
+ src_meth << " Definition: #{IO.foreach(source[0]).first(source[1])[-1].strip}\n"
154
+
155
+ if MARKDOWN || HTML
156
+ src.prepend('#'.freeze)
157
+ src_meth.prepend('#'.freeze)
158
+ else
159
+ src.prepend(?\u2B23.freeze)
160
+ src_meth.prepend(?\u2B23.freeze)
161
+ end
162
+ end
163
+
164
+
78
165
  if MARKDOWN
79
- puts "#{e}.#{meth}\n=> #{dis}"
166
+ puts "#{src}#{src_meth}#{e}.#{disp_meth}\n=> #{dis}"
80
167
  elsif HTML
81
- puts "#{e}.#{meth}\n=> #{dis}"
168
+ puts "#{src}#{src_meth}#{e}.#{disp_meth}\n=> #{dis}"
82
169
  else
83
- puts "\e[1;38;2;80;80;255m#{e}.#{meth}\e[0m\n=> #{dis}"
170
+ puts "\e[1m#{src.colourize}\e[1m#{src_meth.colourize(4)}\e[0m\e[1;38;2;80;80;255m#{e}.#{disp_meth}\e[0m\n=> #{dis}"
84
171
  end
85
172
 
86
173
  puts( "(" +
@@ -1,9 +1,13 @@
1
1
  #include <sys/statvfs.h>
2
2
  #include "ruby.h"
3
3
 
4
- #pragma GCC optimize ("O3")
5
- #pragma clang optimize on
6
- #pragma once
4
+ #ifdef __GNUC__
5
+ #pragma GCC optimize ("O3")
6
+ #pragma GCC diagnostic warning "-Wall"
7
+ #elif __clang__
8
+ #pragma clang optimize on
9
+ #pragma clang diagnostic warning "-Wall"
10
+ #endif
7
11
 
8
12
  static VALUE statfs(VALUE obj, VALUE dir) {
9
13
  struct statvfs buf ;
@@ -1,9 +1,13 @@
1
1
  #include <unistd.h>
2
2
  #include "ruby.h"
3
3
 
4
- #pragma GCC optimize ("O3")
5
- #pragma clang optimize on
6
- #pragma once
4
+ #ifdef __GNUC__
5
+ #pragma GCC optimize ("O3")
6
+ #pragma GCC diagnostic warning "-Wall"
7
+ #elif __clang__
8
+ #pragma clang optimize on
9
+ #pragma clang diagnostic warning "-Wall"
10
+ #endif
7
11
 
8
12
  static VALUE getTick(VALUE obj) {
9
13
  return INT2FIX(sysconf(_SC_CLK_TCK)) ;
@@ -41,6 +45,22 @@ static VALUE getPosixVersion(VALUE obj) {
41
45
  return INT2FIX(sysconf(_SC_VERSION)) ;
42
46
  }
43
47
 
48
+ static VALUE getLineMax(VALUE obj) {
49
+ return INT2FIX(sysconf(_SC_LINE_MAX)) ;
50
+ }
51
+
52
+ static VALUE getExprNestMax(VALUE obj) {
53
+ return INT2FIX(sysconf(_SC_EXPR_NEST_MAX)) ;
54
+ }
55
+
56
+ VALUE getProcessorConfigured(VALUE obj) {
57
+ return INT2FIX(sysconf(_SC_NPROCESSORS_CONF)) ;
58
+ }
59
+
60
+ VALUE getProcessorOnline(VALUE obj) {
61
+ return INT2FIX(sysconf(_SC_NPROCESSORS_ONLN)) ;
62
+ }
63
+
44
64
  static VALUE getUser(VALUE obj) {
45
65
  char *name = getlogin() ;
46
66
  return name ? rb_str_new_cstr(name) : rb_str_new_cstr("") ;
@@ -71,6 +91,11 @@ void Init_sysconf() {
71
91
  rb_define_module_function(_sysconf, "stream_max", getStreamMax, 0) ;
72
92
  rb_define_module_function(_sysconf, "tty_name_max", getTTYNameMax, 0) ;
73
93
  rb_define_module_function(_sysconf, "posix_version", getPosixVersion, 0) ;
94
+ rb_define_module_function(_sysconf, "line_max", getLineMax, 0) ;
95
+ rb_define_module_function(_sysconf, "expr_nest_max", getExprNestMax, 0) ;
96
+
97
+ rb_define_module_function(_sysconf, "processor_online", getProcessorOnline, 0) ;
98
+ rb_define_module_function(_sysconf, "processor_configured", getProcessorConfigured, 0) ;
74
99
 
75
100
  rb_define_module_function(_sysconf, "get_uid", getUID, 0) ;
76
101
  rb_define_module_function(_sysconf, "get_gid", getGID, 0) ;
@@ -1,40 +1,49 @@
1
1
  #include <sys/utsname.h>
2
2
  #include "ruby.h"
3
3
 
4
- #pragma GCC optimize ("O3")
5
- #pragma clang optimize on
6
- #pragma once
4
+ #ifdef __GNUC__
5
+ #pragma GCC optimize ("O3")
6
+ #pragma GCC diagnostic warning "-Wall"
7
+ #elif __clang__
8
+ #pragma clang optimize on
9
+ #pragma clang diagnostic warning "-Wall"
10
+ #endif
7
11
 
8
12
  static struct utsname buf ;
9
- static short status ;
13
+
14
+ static char *sysname = "", *nodename = "" ;
15
+ static char *release = "", *version = "", *machine = "" ;
10
16
 
11
17
  void init_buf() {
12
- status = uname(&buf) ;
18
+ short status = uname(&buf) ;
19
+
20
+ if (status > -1) {
21
+ sysname = buf.sysname ;
22
+ nodename = buf.nodename ;
23
+ release = buf.release ;
24
+ version = buf.version ;
25
+ machine = buf.machine ;
26
+ }
13
27
  }
14
28
 
15
29
  static VALUE getSysname(VALUE obj) {
16
- VALUE sysname = status < 0 ? rb_str_new_cstr("") : rb_str_new_cstr(buf.sysname) ;
17
- return sysname ;
30
+ return rb_str_new_cstr(sysname) ;
18
31
  }
19
32
 
20
33
  static VALUE getNodename(VALUE obj) {
21
- VALUE nodename = status < 0 ? rb_str_new_cstr("") : rb_str_new_cstr(buf.nodename) ;
22
- return nodename ;
34
+ return rb_str_new_cstr(nodename) ;
23
35
  }
24
36
 
25
37
  static VALUE getRelease(VALUE obj) {
26
- VALUE release = status < 0 ? rb_str_new_cstr("") : rb_str_new_cstr(buf.release) ;
27
- return release ;
38
+ return rb_str_new_cstr(release) ;
28
39
  }
29
40
 
30
41
  static VALUE getVersion(VALUE obj) {
31
- VALUE version = status < 0 ? rb_str_new_cstr("") : rb_str_new_cstr(buf.version) ;
32
- return version ;
42
+ return rb_str_new_cstr(version) ;
33
43
  }
34
44
 
35
45
  static VALUE getMachine(VALUE obj) {
36
- VALUE machine = status < 0 ? rb_str_new_cstr("") : rb_str_new_cstr(buf.machine) ;
37
- return machine ;
46
+ return rb_str_new_cstr(machine) ;
38
47
  }
39
48
 
40
49
  void Init_utsname() {
@@ -2,7 +2,7 @@ module LinuxStat
2
2
  module CPU
3
3
  class << self
4
4
  ##
5
- # = stat(sleep = 1.0 / LinuxStat::Sysconf.sc_clk_tck)
5
+ # = stat(sleep = 1.0 / LinuxStat::Sysconf.sc_clk_tck * 5)
6
6
  #
7
7
  # Where sleep is the delay to gather the data.
8
8
  #
@@ -18,14 +18,14 @@ module LinuxStat
18
18
  # {0=>84.38, 1=>100.0, 2=>50.0, 3=>87.5, 4=>87.5}
19
19
  #
20
20
  # If the information is not available, it will return an empty Hash
21
- def stat(sleep = ticks_to_ms)
21
+ def stat(sleep = ticks_to_ms_t5)
22
22
  return {} unless stat?
23
23
 
24
24
  data = IO.readlines('/proc/stat').select! { |x| x[/^cpu\d*/] }.map! { |x| x.split.map!(&:to_f) }
25
25
  sleep(sleep)
26
26
  data2 = IO.readlines('/proc/stat').select! { |x| x[/^cpu\d*/] }.map! { |x| x.split.map!(&:to_f) }
27
27
 
28
- # On devices like android, the core count can change anytime.
28
+ # On devices like android, the core count can change anytime (hotplugging).
29
29
  # I had crashes on Termux.
30
30
  # So better just count the min number of CPU and iterate over that
31
31
  # If data.length is smaller than data2.length, we don't have enough data to compare.
@@ -60,7 +60,7 @@ module LinuxStat
60
60
  # It's like running LinuxStat::CPU.stat[0] but it's much more efficient and calculates just the aggregated usage which is available at the top of the /proc/stat file.
61
61
  #
62
62
  # If the information is not available, it will return nil.
63
- def total_usage(sleep = ticks_to_ms)
63
+ def total_usage(sleep = ticks_to_ms_t5)
64
64
  return nil unless stat?
65
65
 
66
66
  data = IO.foreach('/proc/stat').first.split.tap(&:shift).map!(&:to_f)
@@ -76,12 +76,27 @@ module LinuxStat
76
76
  end
77
77
 
78
78
  ##
79
- # Returns the total number of CPU threads.
79
+ # Returns the total number of CPU threads online now.
80
+ #
81
+ # This value can change while a CPU is offline, due to kernel's hotplugging feature.
82
+ #
83
+ # If the information isn't available, it will return 0.
84
+ def online
85
+ # Let's rely on sysconf(_SC_NPROCESSORS_CONF), which is 100x faster than
86
+ # counting online cpu from /sys/devices/system/cpu
87
+
88
+ LinuxStat::Sysconf.processor_configured
89
+ end
90
+
91
+ ##
92
+ # Returns the total number of CPU threads configured for the sysetm.
80
93
  #
81
94
  # If the information isn't available, it will return 0.
82
95
  def count
83
- # CPU count can change during the program runtime
84
- cpuinfo.count { |x| x.start_with?('processor') }
96
+ # Let's rely on sysconf(_SC_NPROCESSORS_ONLN), which is 100x faster than running:
97
+ # IO.read('/sys/devices/system/cpu/online')[-2].to_i + 1
98
+
99
+ LinuxStat::Sysconf.processor_online
85
100
  end
86
101
 
87
102
  ##
@@ -136,8 +151,11 @@ module LinuxStat
136
151
  @@stat_readable ||= File.readable?('/proc/stat')
137
152
  end
138
153
 
139
- def ticks_to_ms
140
- @@ms ||= 1.0 / LinuxStat::Sysconf.sc_clk_tck
154
+ # Just to avoid duplicate calculations
155
+ # ticks to ms times 5
156
+ # If the ticks is 100, it will return 0.05
157
+ def ticks_to_ms_t5
158
+ @@ms_t5 ||= 1.0 / LinuxStat::Sysconf.sc_clk_tck * 5
141
159
  end
142
160
  end
143
161
  end
@@ -135,8 +135,8 @@ module LinuxStat
135
135
  key = splitted[0].to_s.strip
136
136
  value = splitted[1..-1].join(?=).to_s.strip
137
137
 
138
- value[0] = '' if value[0] == ?"
139
- value[-1] = '' if value[-1] == ?"
138
+ value[0] = ''.freeze if value[0] == ?".freeze
139
+ value[-1] = ''.freeze if value[-1] == ?".freeze
140
140
 
141
141
  h.merge!( key.to_sym => value )
142
142
  }
@@ -100,10 +100,7 @@ module LinuxStat
100
100
 
101
101
  private
102
102
  def pad_left(n, mantissa_length = 2)
103
- n = n.round(mantissa_length)
104
- exp, mant = n.to_s.split(?..freeze)
105
- m = mant.length < mantissa_length ? mant + ?0.freeze * (mantissa_length - mant.length) : mant
106
- exp + ?..freeze + m
103
+ sprintf("%.#{mantissa_length}f".freeze, n)
107
104
  end
108
105
  end
109
106
  end
@@ -203,13 +203,13 @@ module LinuxStat
203
203
  end
204
204
 
205
205
  ##
206
- # = cpu_stat(pid: $$, sleep: 1.0 / LinuxStat::Sysconf.sc_clk_tck)
206
+ # = cpu_stat(pid: $$, sleep: 1.0 / LinuxStat::Sysconf.sc_clk_tck * 5)
207
207
  #
208
208
  # Where pid is the process ID and sleep time is the interval between measurements.
209
209
  #
210
210
  # By default it is the id of the current process ($$), and sleep is LinuxStat::Sysconf.sc_clk_tck
211
211
  #
212
- # The smallest amount of available sleep time is 1.0 / LinuxStat::Sysconf.sc_clk_tck.
212
+ # The smallest amount of available sleep time is 1.0 / LinuxStat::Sysconf.sc_clk_tck * 5.
213
213
  #
214
214
  # * Note 1:
215
215
  # 1. Do note that the sleep time can slow down your application.
@@ -244,7 +244,7 @@ module LinuxStat
244
244
  # Only use this method if you need all of the data at once, in such case, it's more efficient to use this method.
245
245
  #
246
246
  # The :last_executed_cpu also returns an Integer indicating the last executed cpu of the process.
247
- def cpu_stat(pid: $$, sleep: ticks_to_ms)
247
+ def cpu_stat(pid: $$, sleep: ticks_to_ms_t5)
248
248
  file = "/proc/#{pid}/stat"
249
249
  return {} unless File.readable?(file)
250
250
 
@@ -277,15 +277,15 @@ module LinuxStat
277
277
  end
278
278
 
279
279
  ##
280
- # = cpu_usage(pid: $$, sleep: 1.0 / LinuxStat::Sysconf.sc_clk_tck)
280
+ # = cpu_usage(pid: $$, sleep: 1.0 / LinuxStat::Sysconf.sc_clk_tck * 5)
281
281
  #
282
282
  # Where pid is the process ID and sleep time is the interval between measurements.
283
283
  #
284
- # By default it is the id of the current process ($$), and sleep is 1.0 / LinuxStat::Sysconf.sc_clk_tck
284
+ # By default it is the id of the current process ($$), and sleep is 1.0 / LinuxStat::Sysconf.sc_clk_tck * 5
285
285
  #
286
286
  # The smallest amount of available sleep time is LinuxStat::Sysconf.sc_clk_tck.
287
287
  #
288
- # It retuns the CPU usage in Float.
288
+ # It retuns the CPU usage as Float.
289
289
  #
290
290
  # For example:
291
291
  #
@@ -298,7 +298,7 @@ module LinuxStat
298
298
  # But if the info isn't available, it will return nil.
299
299
  #
300
300
  # This method is more efficient than running LinuxStat::ProcessInfo.cpu_stat()
301
- def cpu_usage(pid: $$, sleep: ticks_to_ms)
301
+ def cpu_usage(pid: $$, sleep: ticks_to_ms_t5)
302
302
  file = "/proc/#{pid}/stat"
303
303
  return nil unless File.readable?(file)
304
304
 
@@ -324,6 +324,54 @@ module LinuxStat
324
324
  totald.-(idle2 - idle1).fdiv(totald).*(100).round(2).abs./(LinuxStat::CPU.count)
325
325
  end
326
326
 
327
+ ##
328
+ # = thread_usage(pid: $$, sleep: 1.0 / LinuxStat::Sysconf.sc_clk_tck * 5)
329
+ #
330
+ # Where pid is the process ID and sleep time is the interval between measurements.
331
+ #
332
+ # By default it is the id of the current process ($$), and sleep is 1.0 / LinuxStat::Sysconf.sc_clk_tck * 5
333
+ #
334
+ # The smallest amount of available sleep time is LinuxStat::Sysconf.sc_clk_tck.
335
+ #
336
+ # It retuns the per core CPU usage as Float.
337
+ #
338
+ # For example:
339
+ #
340
+ # LinuxStat::ProcessInfo.core_usage
341
+ #
342
+ # => 200.0
343
+ #
344
+ # A value of 100.0 indicates it is using 100% processing power of a core.
345
+ #
346
+ # The value could be 0 to (100 * the number of CPU threads (including hyperthreading) in the system)
347
+ #
348
+ # But if the info isn't available, it will return nil.
349
+ def thread_usage(pid: $$, sleep: ticks_to_ms_t5)
350
+ file = "/proc/#{pid}/stat"
351
+ return nil unless File.readable?(file)
352
+
353
+ ticks = get_ticks
354
+
355
+ utime, stime, starttime = IO.read(file)
356
+ .split.values_at(13, 14, 21).map(&:to_f)
357
+ uptime = IO.read('/proc/uptime'.freeze).to_f * ticks
358
+
359
+ total_time = utime + stime
360
+ idle1 = uptime - starttime - total_time
361
+
362
+ sleep(sleep)
363
+
364
+ utime2, stime2, starttime2 = IO.read(file)
365
+ .split.values_at(13, 14, 21).map(&:to_f)
366
+ uptime = IO.read('/proc/uptime'.freeze).to_f * ticks
367
+
368
+ total_time2 = utime2 + stime2
369
+ idle2 = uptime - starttime2 - total_time2
370
+
371
+ totald = idle2.+(total_time2).-(idle1 + total_time)
372
+ totald.-(idle2 - idle1).fdiv(totald).*(100).round(2).abs
373
+ end
374
+
327
375
  ##
328
376
  # = threads(pid = $$)
329
377
  #
@@ -345,7 +393,7 @@ module LinuxStat
345
393
  file = "/proc/#{pid}/stat".freeze
346
394
  return nil unless File.readable?(file)
347
395
 
348
- data = IO.read(file).split[19]
396
+ data = IO.foreach(file, ' '.freeze).first(20)[-1]
349
397
  data ? data.to_i : nil
350
398
  end
351
399
 
@@ -383,7 +431,7 @@ module LinuxStat
383
431
  file = "/proc/#{pid}/status".freeze
384
432
  return nil unless File.readable?(file)
385
433
 
386
- data = IO.readlines(file.freeze).find { |x|
434
+ data = IO.foreach(file.freeze).find { |x|
387
435
  x[/Uid.*\d*/]
388
436
  }.to_s.split.drop(1)
389
437
 
@@ -402,12 +450,12 @@ module LinuxStat
402
450
  #
403
451
  # :real, :effective, :saved_set, :filesystem_uid
404
452
  #
405
- # If the info isn't available it returns an empty Hash.
453
+ # If the info isn't available or the argument passed doesn't exist as a process ID, it will return an empty Hash.
406
454
  def gid(pid = $$)
407
455
  file = "/proc/#{pid}/status".freeze
408
456
  return nil unless File.readable?(file)
409
457
 
410
- data = IO.readlines(file.freeze).find { |x|
458
+ data = IO.foreach(file.freeze).find { |x|
411
459
  x[/Gid.*\d*/]
412
460
  }.split.drop(1)
413
461
 
@@ -428,21 +476,132 @@ module LinuxStat
428
476
  file = "/proc/#{pid}/status".freeze
429
477
  return ''.freeze unless File.readable?(file)
430
478
 
431
- gid = IO.readlines(file.freeze).find { |x|
479
+ gid = IO.foreach(file.freeze).find { |x|
432
480
  x[/Gid.*\d*/]
433
481
  }.split.drop(1)[2].to_i
434
482
 
435
483
  LinuxStat::User.username_by_gid(gid)
436
484
  end
437
485
 
486
+ ##
487
+ # = start_time_epoch(pid = $$)
488
+ #
489
+ # Returns the epoch time (as Integer) the process was started.
490
+ #
491
+ # For example:
492
+ # LinuxStat::ProcessInfo.start_time_epoch 526
493
+ #
494
+ # => 1608097744
495
+ #
496
+ # If the info isn't available or the argument passed doesn't exist as a process ID, it will return nil.
497
+ def start_time_epoch(pid = $$)
498
+ stat_file = "/proc/#{pid}/stat".freeze
499
+ uptime = "/proc/uptime".freeze
500
+
501
+ @@u_readable ||= File.readable?(uptime)
502
+ return nil unless @@u_readable && File.readable?(stat_file)
503
+
504
+ u = IO.foreach(uptime, ' '.freeze).next.to_f
505
+ st = (IO.foreach(stat_file, ' '.freeze).first(22)[-1].to_f / get_ticks)
506
+
507
+ # Getting two Time objects and dealing with floating point numbers
508
+ # Just to make sure the time goes monotonically
509
+ Time.now.-(u - st).to_i
510
+ end
511
+
512
+ ##
513
+ # = start_time(pid = $$)
514
+ #
515
+ # Returns the time (as Time object) the process was started.
516
+ #
517
+ # For example:
518
+ # LinuxStat::ProcessInfo.start_time 14183
519
+ #
520
+ # => 2020-12-16 13:31:43 +0000
521
+ #
522
+ # If the info isn't available or the argument passed doesn't exist as a process ID, it will return nil.
523
+ #
524
+ # The timezone returned based on current TZ.
525
+ # Thus the timezone could be affected by changing the ENV['TZ'] variable.
526
+ #
527
+ # Don't trust the timezone returned by the time.
528
+ def start_time(pid = $$)
529
+ # Getting two Time objects and dealing with floating point numbers
530
+ # Just to make sure the time goes monotonically
531
+ Time.at(start_time_epoch(pid))
532
+ end
533
+
534
+ ##
535
+ # = running_time(pid = $$)
536
+ #
537
+ # Returns the time (in seconds, as Float) the process is running for.
538
+ #
539
+ # For example:
540
+ # LinuxStat::ProcessInfo.running_time 14183
541
+ #
542
+ # => 1947.619999999999
543
+ #
544
+ # If the info isn't available or the argument passed doesn't exist as a process ID, it will return nil.
545
+ def running_time(pid = $$)
546
+ stat_file = "/proc/#{pid}/stat".freeze
547
+ uptime = "/proc/uptime".freeze
548
+
549
+ @@u_readable ||= File.readable?(uptime)
550
+ return nil unless @@u_readable && File.readable?(stat_file)
551
+
552
+ IO.foreach(uptime, ' '.freeze).next.to_f - (IO.read(stat_file).split[21].to_i / get_ticks)
553
+ end
554
+
555
+ ##
556
+ # = state(pid = $$)
557
+ # Returns the state of the process as a frozen String
558
+ #
559
+ # * A process could have multiple states:
560
+ #
561
+ # 1. S => Sleeping
562
+ #
563
+ # 2. R => Running
564
+ #
565
+ # 3. I => Idle
566
+ #
567
+ # 4. Z => Zombie
568
+ #
569
+ # It returns any one of them.
570
+ #
571
+ # If the info isn't available or the argument passed doesn't exist as a process ID,
572
+ # it will return an empty String.
573
+ def state(pid = $$)
574
+ file = "/proc/#{pid}/stat".freeze
575
+ return ''.freeze unless File.readable?(file)
576
+ IO.foreach(file, ' '.freeze).first(3)[-1].tap(&:rstrip!).freeze
577
+ end
578
+
579
+ ##
580
+ # = nice(pid = $$)
581
+ # Returns the nice of the process
582
+ #
583
+ # The output value is an Integer ranging from -20 to 19
584
+ #
585
+ # -20 means the process has high priority, and 19 means the process has low priority
586
+ #
587
+ # If the info isn't available or the argument passed doesn't exist as a process ID, it will return nil.
588
+ def nice(pid = $$)
589
+ file = "/proc/#{pid}/stat"
590
+ return nil unless File.readable?(file)
591
+
592
+ IO.foreach(file, ' ').first(19)[-1].to_i
593
+ end
594
+
438
595
  private
439
596
  def get_ticks
440
597
  @@ticks ||= Sysconf.sc_clk_tck
441
598
  end
442
599
 
443
600
  # Just to avoid multiple calculations!...
444
- def ticks_to_ms
445
- @@ms ||= 1.0 / get_ticks
601
+ # ticks to ms * 5
602
+ # If ticks is 100, it will return 0.05
603
+ def ticks_to_ms_t5
604
+ @@ms_t5 ||= 1.0 / get_ticks * 5
446
605
  end
447
606
 
448
607
  def pagesize