sunburst 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21354f28529b35ce4d5897120e0f0b37f3ace979705c9d80f746fc2f55e55444
4
- data.tar.gz: 8bf865f98151f931e009bf9c8ee0c19397a7cdc3c5c3406c8ac5273f9743963f
3
+ metadata.gz: bac5cf8138ded782d5114cf84c9d05f3f06e748328024aaa2c99783187c734ac
4
+ data.tar.gz: a8959e5ff6b66c568b0c5d7ead18abdb5c1df9305155e4e9f29ca0c449074617
5
5
  SHA512:
6
- metadata.gz: b23ae54f7a5d19b065c172ea24d9a9df6e81c048ff07a56b770f8d6abe1765cd78971e8604c5f05179d27b9410d8b23997076c79fc56360f69ca2c76bfb72f80
7
- data.tar.gz: dd542f7f462a637e909dd3d1f84e59f2adcc3a184fd0a67287d56228f4a5fd4c757a0950e8294825642a6f26462ffefef7ed51435acbb833f19b31d35ae20791
6
+ metadata.gz: bad636441bee759ab7783226a3ea6e0e86b5cfae584b1ddc81f2d181df354101c58c43092f38f80136d00e22846641199b5256bc8c80da5f398e29d32a990432
7
+ data.tar.gz: 8846d22b978e865380622bd7139d0260c7d0f9a3b6c2e43c01230bedeb5f7f473b24136ac1a9dc1a08b85e7efa7f7029a33f439630cd24fa49e6876dd17626c5
data/README.md CHANGED
@@ -1,11 +1,41 @@
1
1
  # Sunburst
2
+ Sunburst lets you run a command for a given time.
3
+ When the time expires, the program will be SIGKILLed.
4
+ Sunburst will then report the total CPU time and last known memory usage
5
+ of the program.
2
6
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/sunburst`. To experiment with that code, run `bin/console` for an interactive prompt.
7
+ For example:
4
8
 
5
- TODO: Delete this and the text above, and describe your gem
9
+ ```
10
+ $ sunburst "while : ; do echo hi ; sleep 1 ; done" --time=3
11
+ :: Running "while : ; do echo hi ; sleep 1 ; done" for 3.0 seconds
12
+ Logging Standard Output and Error
13
+ hi
14
+ hi
15
+ hi
16
+
17
+ ----------------------------------------------------------------------
18
+ :: Total Execution Time: 3.00097 seconds
19
+ :: CPU Time: 0.0 seconds (0.0% exec time)
20
+ :: Memory usage: 577536 bytes (0.007% system mem)
21
+ :: Max Threads: 1
22
+ ```
6
23
 
7
- ## Installation
24
+ Or
25
+
26
+ ```
27
+ $ sunburst "while : ; do : ; done" --time=3
28
+ :: Running "while : ; do : ; done" for 3.0 seconds
29
+ Logging Standard Output and Error
30
+
31
+ ----------------------------------------------------------------------
32
+ :: Total Execution Time: 3.00097 seconds
33
+ :: CPU Time: 2.97 seconds (98.968% exec time)
34
+ :: Memory usage: 360448 bytes (0.004% system mem)
35
+ :: Max Threads: 1
36
+ ```
8
37
 
38
+ ## Installation
9
39
  Add this line to your application's Gemfile:
10
40
 
11
41
  ```ruby
@@ -14,30 +44,35 @@ gem 'sunburst'
14
44
 
15
45
  And then execute:
16
46
 
17
- $ bundle install
47
+ ```
48
+ $ bundle install
49
+ ```
18
50
 
19
51
  Or install it yourself as:
20
-
21
- $ gem install sunburst
52
+ ```
53
+ $ gem install sunburst
54
+ ```
22
55
 
23
56
  ## Usage
57
+ Run sunbust -h for instruction.
24
58
 
25
- TODO: Write usage instructions here
26
-
27
- ## Development
28
-
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
59
+ ```
60
+ Arguments:
61
+ --time=N Run the program for N seconds
62
+ -h | --help Show this help section
63
+ --humanize Human readable memory units
64
+
65
+ Example:
66
+ sunburst echo hello world --time=0.05 --humanize
67
+ sunburst "echo hello world" --time=0.05 --humanize
68
+ sunburst "ruby -e 'while true do end'" --time=3 --humanize
69
+ sunburst "ruby -e 'p :Hello'" --time=3 --humanize
70
+ ```
30
71
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
72
+ If no time is specified, it will run until the command exits.
32
73
 
33
74
  ## Contributing
34
-
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/sunburst. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/sunburst/blob/master/CODE_OF_CONDUCT.md).
75
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Souravgoswami/sunburst.
36
76
 
37
77
  ## License
38
-
39
78
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
40
-
41
- ## Code of Conduct
42
-
43
- Everyone interacting in the Sunburst project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/sunburst/blob/master/CODE_OF_CONDUCT.md).
data/exe/sunburst CHANGED
@@ -1,5 +1,7 @@
1
- require 'sunburst'
1
+ #!/usr/bin/env ruby
2
+ $-v = nil
2
3
  STDOUT.sync = STDIN.sync = true
4
+ require 'sunburst'
3
5
 
4
6
  def help
5
7
  puts <<~EOF
@@ -25,6 +27,11 @@ def help
25
27
  exit 0
26
28
  end
27
29
 
30
+ def splitter(sub = 8)
31
+ width = Sunburst.win_width
32
+ puts ?\n, ?-.*(width - sub).center(width)
33
+ end
34
+
28
35
  help if ARGV.any? { |x| x[/^\-(\-help|h)$/] }
29
36
 
30
37
  time_arg = ARGV.find { |x| x[/^\-\-time=[0-9]+\.?[0-9]*$/] }
@@ -40,18 +47,52 @@ command = ARGV.join(' ')
40
47
  help if command.empty?
41
48
 
42
49
  puts %Q(:: Running "#{command}" for #{time || 'infinite'} seconds)
43
- puts ?- * Sunburst.win_width
50
+
44
51
 
45
52
  begin
53
+ print ""
54
+
55
+ message = "\e[4mLogging Standard Output and Error\e[0m"
56
+ puts "\e[38;2;243;156;18m#{message.center(Sunburst.win_width + 6)}\e[0m"
57
+
46
58
  data = Sunburst.measure(command: command, time: time, sleep_time: 0.0001)
59
+ print "\e[38;2;243;156;18m"
60
+ splitter()
61
+ print "\e[0m"
62
+
63
+ exec_time = data[:execution_time]
64
+ cpu_time = data[:cpu_time]
65
+ percent_time = cpu_time.*(100).fdiv(exec_time)
66
+
67
+ style = "\e[1;"
68
+ style << if percent_time > 75
69
+ "38;2;230;80;70m"
70
+ elsif percent_time > 50
71
+ "38;2;45;125;255m"
72
+ elsif percent_time > 25
73
+ "38;2;255;225;0m"
74
+ else
75
+ "38;2;40;175;95m"
76
+ end
47
77
 
48
- puts ?- * Sunburst.win_width
49
- puts ":: Total Execution Time: #{data[:execution_time]} seconds"
50
- puts ":: CPU Time: #{data[:cpu_time]} second#{?s if data[:cpu_time] != 1}"
78
+ puts ":: Total Execution Time: #{data[:execution_time]} seconds\e[0m"
79
+ puts ":: CPU Time: #{style}#{data[:cpu_time]}\e[0m second#{?s if data[:cpu_time] != 1} (#{percent_time.round(3)}% exec time)"
51
80
 
52
81
  mem = data[:memory]
53
-
54
82
  if mem
83
+ percent_mem = mem.*(100).fdiv(Sunburst.total_ram)
84
+
85
+ style = "\e[1;"
86
+ style << if percent_mem > 50
87
+ "38;2;230;80;70m"
88
+ elsif percent_mem > 30
89
+ "38;2;45;125;255m"
90
+ elsif percent_mem > 10
91
+ "38;2;255;225;0m"
92
+ else
93
+ "38;2;40;175;95m"
94
+ end
95
+
55
96
  if human_readable
56
97
  mem_text = if mem >= 10 ** 12
57
98
  "#{mem.fdiv(10 ** 12).round(3)} TB"
@@ -65,16 +106,31 @@ begin
65
106
  "#{mem} Bytes"
66
107
  end
67
108
 
68
- puts ":: Memory usage: #{mem_text}"
109
+ puts ":: Memory usage: #{style}#{mem_text}\e[0m (#{percent_mem.round(3)}% system mem)"
69
110
  else
70
- puts ":: Memory usage: #{mem} bytes"
111
+ puts ":: Memory usage: #{style}#{mem} bytes\e[0m (#{percent_mem.round(3)}% system mem)"
71
112
  end
72
113
  else
73
114
  puts ":: The memory usage can't be logged."
74
115
  end
116
+
117
+ max_threads = data[:max_threads]
118
+ style = "\e[1;"
119
+ style << if max_threads > 16
120
+ "38;2;230;80;70m"
121
+ elsif max_threads > 8
122
+ "38;2;45;125;255m"
123
+ elsif max_threads > 4
124
+ "38;2;255;225;0m"
125
+ else
126
+ "38;2;40;175;95m"
127
+ end
128
+
129
+ puts ":: Max Threads: #{style}#{max_threads}\e[0m"
130
+
75
131
  rescue Errno::ENOENT
76
132
  puts "sunburst: #{command}: command not found"
77
- puts ?- * Sunburst.win_width
133
+ puts ?\n, ?- * Sunburst.win_width
78
134
  rescue StandardError
79
- puts "$!.full_message"
135
+ puts $!.full_message
80
136
  end
data/ext/stats/stats.c CHANGED
@@ -2,11 +2,12 @@
2
2
  #include <unistd.h>
3
3
  #include <time.h>
4
4
  #include <sys/ioctl.h>
5
+ #include <sys/sysinfo.h>
5
6
 
6
7
  unsigned int PAGESIZE ;
7
8
  unsigned int TICKS ;
8
9
 
9
- VALUE statm_memory(VALUE obj, VALUE pid) {
10
+ VALUE statm_memory(volatile VALUE obj, volatile VALUE pid) {
10
11
  int _pid = FIX2INT(pid) ;
11
12
  if (_pid < 0) return Qnil ;
12
13
 
@@ -26,41 +27,71 @@ VALUE statm_memory(VALUE obj, VALUE pid) {
26
27
  return UINT2NUM(v) ;
27
28
  }
28
29
 
29
- VALUE ps_times(VALUE obj, VALUE pid) {
30
+ VALUE ps_stat(volatile VALUE obj, volatile VALUE pid) {
30
31
  int _pid = FIX2INT(pid) ;
31
- if (_pid < 0) return Qnil ;
32
+ if (_pid < 0) return rb_str_new_cstr("") ;
32
33
 
33
34
  char _path[22] ;
34
35
  sprintf(_path, "/proc/%d/stat", _pid) ;
35
36
 
36
37
  FILE *f = fopen(_path, "r") ;
37
- if (!f) return Qnil ;
38
38
 
39
- unsigned long utime, stime ;
39
+ if (!f) return rb_ary_new() ;
40
+
41
+ // For this info
42
+ // follow https://man7.org/linux/man-pages/man5/proc.5.html
43
+ int ppid, processor ;
44
+ long unsigned utime, stime ;
45
+ long num_threads ;
46
+
47
+ char status = fscanf(
48
+ f, "%*llu (%*[^)]%*[)] %*c "
49
+ "%d %*d %*d %*d %*d %*u "
50
+ "%*lu %*lu %*lu %*lu %lu %lu "
51
+ "%*ld %*ld %*ld %*ld %ld",
52
+ &ppid, &utime, &stime, &num_threads
53
+ ) ;
40
54
 
41
- char status = fscanf(f, "%*llu (%*[^)]%*[)] %*c %*d %*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu %lu %lu", &utime, &stime) ;
42
55
  fclose(f) ;
43
56
 
44
- if (status != 2) return Qnil ;
45
- float total_time = (utime + stime) / (float)TICKS ;
57
+ if (status != 4) return rb_ary_new() ;
46
58
 
47
- return rb_float_new(total_time) ;
59
+ return rb_ary_new_from_args(4,
60
+ INT2NUM(ppid),
61
+ ULONG2NUM(utime),
62
+ ULONG2NUM(stime),
63
+ LONG2NUM(num_threads)
64
+ ) ;
48
65
  }
49
66
 
50
- VALUE clock_monotonic(VALUE obj) {
67
+ VALUE clock_monotonic(volatile VALUE obj) {
51
68
  struct timespec tv ;
52
69
  clock_gettime(CLOCK_MONOTONIC, &tv) ;
53
- long double time = tv.tv_sec + tv.tv_nsec / 1000000000.0 ;
70
+ float time = tv.tv_sec + tv.tv_nsec / 1000000000.0 ;
54
71
 
55
72
  return rb_float_new(time) ;
56
73
  }
57
74
 
58
- VALUE winWidth(VALUE obj) {
75
+ VALUE winWidth(volatile VALUE obj) {
59
76
  struct winsize w ;
60
77
  ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) ;
61
78
  return INT2NUM(w.ws_col) ;
62
79
  }
63
80
 
81
+ VALUE totalRAM(volatile VALUE obj) {
82
+ struct sysinfo buf ;
83
+ char status = sysinfo(&buf) ;
84
+
85
+ if (status != 0) return Qnil ;
86
+
87
+ return rb_funcall(
88
+ ULONG2NUM(buf.totalram),
89
+ rb_intern("*"),
90
+ 1,
91
+ ULONG2NUM(buf.mem_unit)
92
+ ) ;
93
+ }
94
+
64
95
  void Init_stats() {
65
96
  PAGESIZE = sysconf(_SC_PAGESIZE) ;
66
97
  TICKS = sysconf(_SC_CLK_TCK) ;
@@ -70,8 +101,10 @@ void Init_stats() {
70
101
  rb_define_const(sunburst, "TICKS", UINT2NUM(TICKS)) ;
71
102
 
72
103
  rb_define_module_function(sunburst, "get_mem", statm_memory, 1) ;
73
- rb_define_module_function(sunburst, "get_times", ps_times, 1) ;
74
104
  rb_define_module_function(sunburst, "clock_monotonic", clock_monotonic, 0) ;
75
105
 
76
106
  rb_define_module_function(sunburst, "win_width", winWidth, 0) ;
107
+ rb_define_module_function(sunburst, "ps_stat", ps_stat, 1) ;
108
+
109
+ rb_define_module_function(sunburst, "total_ram", totalRAM, 0) ;
77
110
  }
@@ -1,16 +1,29 @@
1
1
  module Sunburst
2
+ def self.get_stats(pid)
3
+ stats = Sunburst.ps_stat(pid)
4
+
5
+ if stats.empty?
6
+ Process.kill(9, pid)
7
+ fail RuntimeError, 'Something horribly wrong happened! Exiting.'
8
+ end
9
+
10
+ stats
11
+ end
12
+
2
13
  def self.measure(command:, time: nil, sleep_time: 0.001)
3
- r = {execution_time: nil, cpu_time: nil, memory: nil}
14
+ r = {
15
+ execution_time: nil, cpu_time: nil,
16
+ memory: nil, max_threads: nil
17
+ }
4
18
 
5
19
  IO.popen(command) { |x|
6
20
  time1 = Sunburst.clock_monotonic
7
21
  pid = x.pid
8
22
 
9
- t = Thread.new {
10
- print x.readpartial(4096) until x.eof?
11
- }
23
+ t = Thread.new { print x.readpartial(4096) until x.eof? }
12
24
 
13
25
  last_mem = 0
26
+ max_threads = 0
14
27
 
15
28
  while true
16
29
  _last_mem = Sunburst.get_mem(pid)
@@ -18,13 +31,22 @@ module Sunburst
18
31
  break if (time && Sunburst.clock_monotonic - time1 > time) || _last_mem == 0
19
32
  last_mem = _last_mem
20
33
 
34
+ # Get stats
35
+ stats = get_stats(pid)
36
+ _threads = stats[3]
37
+ max_threads = _threads if max_threads < _threads
38
+
21
39
  sleep(sleep_time)
22
40
  end
23
41
 
24
42
  time2 = Sunburst.clock_monotonic
25
43
 
26
- # Get CPU Time
27
- cpu_time = Sunburst.get_times(pid).truncate(5)
44
+ # Get Stats
45
+ stats = get_stats(pid)
46
+ cpu_time = stats[1].+(stats[2]).fdiv(Sunburst::TICKS)
47
+
48
+ _threads = stats[3]
49
+ max_threads = _threads if max_threads < _threads
28
50
 
29
51
  # Get Memory Usage
30
52
  _last_mem = Sunburst.get_mem(pid)
@@ -33,9 +55,11 @@ module Sunburst
33
55
  t.kill
34
56
  Process.kill(9, pid)
35
57
 
36
- r[:execution_time] = time2.-(time1).truncate(5)
37
58
  r[:cpu_time] = cpu_time
59
+ r[:max_threads] = max_threads unless max_threads == 0
38
60
  r[:memory] = last_mem * Sunburst::PAGESIZE if last_mem > 0
61
+
62
+ r[:execution_time] = time2.-(time1).truncate(5)
39
63
  }
40
64
 
41
65
  r
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sunburst
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sunburst
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sourav Goswami
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-04-08 00:00:00.000000000 Z
11
+ date: 2021-04-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Run a process for a given time, kill it with SIGKILL, report CPU time
14
14
  and memory usage