sunburst 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 21354f28529b35ce4d5897120e0f0b37f3ace979705c9d80f746fc2f55e55444
4
+ data.tar.gz: 8bf865f98151f931e009bf9c8ee0c19397a7cdc3c5c3406c8ac5273f9743963f
5
+ SHA512:
6
+ metadata.gz: b23ae54f7a5d19b065c172ea24d9a9df6e81c048ff07a56b770f8d6abe1765cd78971e8604c5f05179d27b9410d8b23997076c79fc56360f69ca2c76bfb72f80
7
+ data.tar.gz: dd542f7f462a637e909dd3d1f84e59f2adcc3a184fd0a67287d56228f4a5fd4c757a0950e8294825642a6f26462ffefef7ed51435acbb833f19b31d35ae20791
data/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Sourav Goswami
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Sunburst
2
+
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.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'sunburst'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install sunburst
22
+
23
+ ## Usage
24
+
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.
30
+
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).
32
+
33
+ ## 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).
36
+
37
+ ## License
38
+
39
+ 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/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/sunburst ADDED
@@ -0,0 +1,80 @@
1
+ require 'sunburst'
2
+ STDOUT.sync = STDIN.sync = true
3
+
4
+ def help
5
+ puts <<~EOF
6
+ Sunburst lets you run a command for a given time.
7
+ When the time expires, the program will be SIGKILLed.
8
+ Sunburst will then report the total CPU time and last known memory usage
9
+ of the program.
10
+
11
+ If no time is specified, it will run until the command exits.
12
+
13
+ Arguments:
14
+ \s\s\s\s--time=N\s\s\s\s\s\s\s\s\s\sRun the program for N seconds
15
+ \s\s\s\s-h | --help\s\s\s\s\s\s\sShow this help section
16
+ \s\s\s\s--humanize\s\s\s\s\s\s\s\sHuman readable memory units
17
+
18
+ Example:
19
+ \s\s\s\ssunburst echo hello world --time=0.05 --humanize
20
+ \s\s\s\ssunburst "echo hello world" --time=0.05 --humanize
21
+ \s\s\s\ssunburst "ruby -e 'while true do end'" --time=3 --humanize
22
+ \s\s\s\ssunburst "ruby -e 'p :Hello'" --time=3 --humanize
23
+ EOF
24
+
25
+ exit 0
26
+ end
27
+
28
+ help if ARGV.any? { |x| x[/^\-(\-help|h)$/] }
29
+
30
+ time_arg = ARGV.find { |x| x[/^\-\-time=[0-9]+\.?[0-9]*$/] }
31
+ ARGV.delete(time_arg) if time_arg
32
+ time = time_arg ? time_arg.split('=')[-1].to_f : nil
33
+
34
+ _human_readable = ARGV.find { |x| x[/^\-\-humanize$/] }
35
+ ARGV.delete(_human_readable) if _human_readable
36
+ human_readable = _human_readable
37
+
38
+ command = ARGV.join(' ')
39
+
40
+ help if command.empty?
41
+
42
+ puts %Q(:: Running "#{command}" for #{time || 'infinite'} seconds)
43
+ puts ?- * Sunburst.win_width
44
+
45
+ begin
46
+ data = Sunburst.measure(command: command, time: time, sleep_time: 0.0001)
47
+
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}"
51
+
52
+ mem = data[:memory]
53
+
54
+ if mem
55
+ if human_readable
56
+ mem_text = if mem >= 10 ** 12
57
+ "#{mem.fdiv(10 ** 12).round(3)} TB"
58
+ elsif mem >= 10 ** 9
59
+ "#{mem.fdiv(10 ** 9).round(3)} GB"
60
+ elsif mem >= 10 ** 6
61
+ "#{mem.fdiv(10 ** 6).round(3)} MB"
62
+ elsif mem >= 10 ** 3
63
+ "#{mem.fdiv(10 ** 3).round(3)} KB"
64
+ else
65
+ "#{mem} Bytes"
66
+ end
67
+
68
+ puts ":: Memory usage: #{mem_text}"
69
+ else
70
+ puts ":: Memory usage: #{mem} bytes"
71
+ end
72
+ else
73
+ puts ":: The memory usage can't be logged."
74
+ end
75
+ rescue Errno::ENOENT
76
+ puts "sunburst: #{command}: command not found"
77
+ puts ?- * Sunburst.win_width
78
+ rescue StandardError
79
+ puts "$!.full_message"
80
+ end
@@ -0,0 +1,4 @@
1
+ require 'mkmf'
2
+ $CFLAGS << ' -O3 -mtune=native -march=native'
3
+
4
+ create_makefile 'sunburst/stats'
data/ext/stats/stats.c ADDED
@@ -0,0 +1,77 @@
1
+ #include "ruby.h"
2
+ #include <unistd.h>
3
+ #include <time.h>
4
+ #include <sys/ioctl.h>
5
+
6
+ unsigned int PAGESIZE ;
7
+ unsigned int TICKS ;
8
+
9
+ VALUE statm_memory(VALUE obj, VALUE pid) {
10
+ int _pid = FIX2INT(pid) ;
11
+ if (_pid < 0) return Qnil ;
12
+
13
+ char _path[22] ;
14
+ sprintf(_path, "/proc/%d/statm", _pid) ;
15
+
16
+ FILE *f = fopen(_path, "r") ;
17
+ if (!f) return Qnil ;
18
+
19
+ unsigned int resident, shared ;
20
+ char status = fscanf(f, "%*u %u %u", &resident, &shared) ;
21
+ fclose(f) ;
22
+
23
+ if (status != 2) return Qnil ;
24
+
25
+ unsigned int v = resident - shared ;
26
+ return UINT2NUM(v) ;
27
+ }
28
+
29
+ VALUE ps_times(VALUE obj, VALUE pid) {
30
+ int _pid = FIX2INT(pid) ;
31
+ if (_pid < 0) return Qnil ;
32
+
33
+ char _path[22] ;
34
+ sprintf(_path, "/proc/%d/stat", _pid) ;
35
+
36
+ FILE *f = fopen(_path, "r") ;
37
+ if (!f) return Qnil ;
38
+
39
+ unsigned long utime, stime ;
40
+
41
+ char status = fscanf(f, "%*llu (%*[^)]%*[)] %*c %*d %*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu %lu %lu", &utime, &stime) ;
42
+ fclose(f) ;
43
+
44
+ if (status != 2) return Qnil ;
45
+ float total_time = (utime + stime) / (float)TICKS ;
46
+
47
+ return rb_float_new(total_time) ;
48
+ }
49
+
50
+ VALUE clock_monotonic(VALUE obj) {
51
+ struct timespec tv ;
52
+ clock_gettime(CLOCK_MONOTONIC, &tv) ;
53
+ long double time = tv.tv_sec + tv.tv_nsec / 1000000000.0 ;
54
+
55
+ return rb_float_new(time) ;
56
+ }
57
+
58
+ VALUE winWidth(VALUE obj) {
59
+ struct winsize w ;
60
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) ;
61
+ return INT2NUM(w.ws_col) ;
62
+ }
63
+
64
+ void Init_stats() {
65
+ PAGESIZE = sysconf(_SC_PAGESIZE) ;
66
+ TICKS = sysconf(_SC_CLK_TCK) ;
67
+
68
+ VALUE sunburst = rb_define_module("Sunburst") ;
69
+ rb_define_const(sunburst, "PAGESIZE", UINT2NUM(PAGESIZE)) ;
70
+ rb_define_const(sunburst, "TICKS", UINT2NUM(TICKS)) ;
71
+
72
+ rb_define_module_function(sunburst, "get_mem", statm_memory, 1) ;
73
+ rb_define_module_function(sunburst, "get_times", ps_times, 1) ;
74
+ rb_define_module_function(sunburst, "clock_monotonic", clock_monotonic, 0) ;
75
+
76
+ rb_define_module_function(sunburst, "win_width", winWidth, 0) ;
77
+ }
data/lib/sunburst.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sunburst/version'
4
+ require 'sunburst/stats'
5
+ require 'sunburst/sunburst'
@@ -0,0 +1,43 @@
1
+ module Sunburst
2
+ def self.measure(command:, time: nil, sleep_time: 0.001)
3
+ r = {execution_time: nil, cpu_time: nil, memory: nil}
4
+
5
+ IO.popen(command) { |x|
6
+ time1 = Sunburst.clock_monotonic
7
+ pid = x.pid
8
+
9
+ t = Thread.new {
10
+ print x.readpartial(4096) until x.eof?
11
+ }
12
+
13
+ last_mem = 0
14
+
15
+ while true
16
+ _last_mem = Sunburst.get_mem(pid)
17
+
18
+ break if (time && Sunburst.clock_monotonic - time1 > time) || _last_mem == 0
19
+ last_mem = _last_mem
20
+
21
+ sleep(sleep_time)
22
+ end
23
+
24
+ time2 = Sunburst.clock_monotonic
25
+
26
+ # Get CPU Time
27
+ cpu_time = Sunburst.get_times(pid).truncate(5)
28
+
29
+ # Get Memory Usage
30
+ _last_mem = Sunburst.get_mem(pid)
31
+ last_mem = _last_mem unless _last_mem == 0
32
+
33
+ t.kill
34
+ Process.kill(9, pid)
35
+
36
+ r[:execution_time] = time2.-(time1).truncate(5)
37
+ r[:cpu_time] = cpu_time
38
+ r[:memory] = last_mem * Sunburst::PAGESIZE if last_mem > 0
39
+ }
40
+
41
+ r
42
+ end
43
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunburst
4
+ VERSION = "0.1.0"
5
+ end
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sunburst
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sourav Goswami
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-04-08 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Run a process for a given time, kill it with SIGKILL, report CPU time
14
+ and memory usage
15
+ email:
16
+ - souravgoswami@protonmail.com
17
+ executables:
18
+ - sunburst
19
+ extensions:
20
+ - ext/stats/extconf.rb
21
+ extra_rdoc_files:
22
+ - README.md
23
+ files:
24
+ - LICENCE
25
+ - README.md
26
+ - bin/setup
27
+ - exe/sunburst
28
+ - ext/stats/extconf.rb
29
+ - ext/stats/stats.c
30
+ - lib/sunburst.rb
31
+ - lib/sunburst/sunburst.rb
32
+ - lib/sunburst/version.rb
33
+ homepage: https://github.com/Souravgoswami/sunburst/
34
+ licenses:
35
+ - MIT
36
+ metadata: {}
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 2.6.0
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubygems_version: 3.2.13
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: Run a process for a given time, kill it with SIGKILL, report CPU time and
56
+ memory usage
57
+ test_files: []