pask 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0efd58f652add7f0a2601ca79c2bd14cd1a332c2
4
+ data.tar.gz: f2a1dd7ec682b6723e74c93089b379ec9bfaab84
5
+ SHA512:
6
+ metadata.gz: f05dcb5e9c2dbe6c913a0f6a7454560ce1ba45e9a278efdecdfccf5b8697371426755d786cf354a8cd42b2f9df9a3e03e6b5bc11b96c49c4740cfb7196f16f15
7
+ data.tar.gz: 3d5b48e49f330ca4899ae8daf6af9b764f3fac1e34a1e9a18f624e2d51340542155b95207ff35639c822f7e0de4064bef5b7b97682bd89f49aa83473fb2afa2b
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/.pryrc ADDED
@@ -0,0 +1,3 @@
1
+ require "bundler"
2
+ Bundler.require :default
3
+ Pry.plugins.values.map(&:disable!)
@@ -0,0 +1 @@
1
+ 2
@@ -0,0 +1,9 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - ruby-head
7
+
8
+ notifications:
9
+ email: true
@@ -0,0 +1,3 @@
1
+ == v0.1.0
2
+ - release!
3
+ it works. quiet release.
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+ gem "binding.repl"
4
+
5
+ group :development do
6
+ gem "rake"
7
+ end
8
+
9
+ group :development, :test do
10
+ gem "pry"
11
+ gem "pry-doc"
12
+ gem "minitest", require: "minitest/autorun"
13
+ end
@@ -0,0 +1,74 @@
1
+ __OVERVIEW__
2
+
3
+ | Project | pask
4
+ |:----------------|:--------------------------------------------------
5
+ | Homepage | https://github.com/robgleeson/pask
6
+ | Build | [![Build Status](https://travis-ci.org/robgleeson/pask.png?branch=master)](https://travis-ci.org/robgleeson/pask)
7
+
8
+ __DESCRIPTION__
9
+
10
+ pask is a concurrent tracer written with the power of `Thread#set_trace_func`.
11
+
12
+ __EXAMPLES__
13
+
14
+ ```ruby
15
+ tracer = Pask.new
16
+ tracer.trace { Person.call }
17
+ event1 = tracer.start # the event for "Person.call called"
18
+ event2 = tracer.resume # the event for "Person.call returned"
19
+ event3 = tracer.resume # returns nil (no more code to trace)
20
+ ```
21
+
22
+ __REPL IN YO TRACER__
23
+
24
+ pask produces instance's of `Pask::Event` when it traces code. an event is returned to
25
+ the caller and opens up some exciting possibilities(due to the wonders of `Binding`):
26
+
27
+ ```ruby
28
+ def call
29
+ x = 1
30
+ p x
31
+ end
32
+
33
+ tracer = Pask.new
34
+ tracer.trace { Person.call }
35
+ event1 = tracer.start
36
+ event.binding.pry # start pry inside 'call', before assignment of x.
37
+ ```
38
+
39
+ __RUBIES__
40
+
41
+ - MRI
42
+ - 1.9.2
43
+ - 1.9.3
44
+ - 2.0.0
45
+ - 2.1.0
46
+ - 2.1.0+
47
+
48
+ __CONTRIBUTE!__
49
+
50
+ [fork it](https://github.com/robgleeson/binding.repl/fork), clone it, change! <br>
51
+ open a pull request :)
52
+
53
+ some tips for working on the project:
54
+ ```
55
+ cd $CLONED_DIR
56
+ bundle install
57
+ rake test # run tests
58
+ ```
59
+
60
+ __INSTALL__
61
+
62
+ $ gem install pask
63
+
64
+ __SEE ALSO__
65
+
66
+ - [machine.repl](https://github.com/robgleeson/machine.repl)<br>
67
+ the webmachine-ruby repl/debugger.
68
+
69
+ - [examples/ directory](https://github.com/robgleeson/pask/tree/master/examples)<br>
70
+ examples in the `examples/` directory. `$ rake examples` runs em all.
71
+
72
+ __LICENSE__
73
+
74
+ see UNLICENSE.txt. public domain.
@@ -0,0 +1,24 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc "run the tests"
4
+ task :test do
5
+ Dir["test/*_test.rb"].each do |file|
6
+ require_relative(file)
7
+ end
8
+ end
9
+ task :default => :test
10
+
11
+ desc "run the examples in examples/*.rb"
12
+ task :examples do
13
+ Dir["examples/*.rb"].each do |example|
14
+ sh "ruby %s" % [example]
15
+ puts
16
+ end
17
+ end
18
+
19
+ desc "repeat test runs twenty times to look for race conditions"
20
+ task :race do
21
+ 20.times do |index|
22
+ Rake::Task["test"].execute
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
@@ -0,0 +1,15 @@
1
+ require "bundler/setup"
2
+ Bundler.require :default
3
+
4
+ def call
5
+ 2+2
6
+ x = 2
7
+ end
8
+
9
+ tracer = Pask.new
10
+ tracer.interested? { |e| e.call? or e.return? }
11
+ tracer.trace { call }
12
+ event1 = tracer.start
13
+ puts "[-] call() called. value of x is #{event1.binding.eval('x').inspect}."
14
+ event2 = tracer.resume
15
+ puts "[-] call() returned. value of x is now #{event2.binding.eval('x')}."
@@ -0,0 +1,11 @@
1
+ require "bundler"
2
+ Bundler.require :default
3
+ def call
4
+ end
5
+
6
+ puts "[-] counting 11 method calls. 10 ruby method calls, one C method call."
7
+ tracer = Pask.new
8
+ tracer.interested? { |e| e.call? or e.c_call? }
9
+ tracer.trace { 10.times { call } }
10
+ events = tracer.consume!
11
+ puts "[-] code traced. count done! a total of: #{events.size}"
@@ -0,0 +1,13 @@
1
+ require "bundler"
2
+ Bundler.require :default
3
+
4
+ def call
5
+ sleep(1)
6
+ end
7
+
8
+ puts "[-] going to sleep for a second, hold tight!"
9
+ tracer = Pask.new
10
+ tracer.interested? { |e| e.call? or e.return? }
11
+ tracer.trace { call }
12
+ events = tracer.consume!
13
+ puts "[-] #{events.at(0).signature} took #{events.at(1) - events.at(0)} second(s) to finish"
@@ -0,0 +1,114 @@
1
+ class Pask
2
+ require "thread"
3
+ require_relative "pask/event"
4
+
5
+ NotStartedError = Class.new(RuntimeError)
6
+ NotFinishedError = Class.new(RuntimeError)
7
+
8
+ VERSION = "0.1.0".freeze
9
+ RUN_STATE = "run".freeze
10
+ SLEEP_STATE = "sleep".freeze
11
+ TERMINATED_STATE = [nil, false].freeze
12
+ CATCH_ALL = Proc.new { true }
13
+
14
+ def self.version
15
+ VERSION
16
+ end
17
+
18
+ def initialize
19
+ @thread = nil
20
+ @queue = nil
21
+ @predicate = CATCH_ALL
22
+ @predicate_copy = nil
23
+ @block = nil
24
+ @block_copy = nil
25
+ end
26
+
27
+ def interested?(callable = nil, &block)
28
+ predicate = callable || block
29
+ unless predicate
30
+ raise ArgumentError, "no predicate given to #{__method__}()"
31
+ end
32
+ @predicate = predicate
33
+ end
34
+
35
+ def started?
36
+ if @queue and @thread
37
+ true
38
+ else
39
+ false
40
+ end
41
+ end
42
+
43
+ def running?
44
+ if @thread and @thread.status == RUN_STATE
45
+ true
46
+ else
47
+ false
48
+ end
49
+ end
50
+
51
+ def finished?
52
+ if @thread and TERMINATED_STATE.include?(@thread.status)
53
+ true
54
+ else
55
+ false
56
+ end
57
+ end
58
+
59
+ def sleeping?
60
+ if @thread and @thread.status == SLEEP_STATE
61
+ true
62
+ else
63
+ false
64
+ end
65
+ end
66
+
67
+ def resume
68
+ unless started?
69
+ raise NotStartedError, "tracer has not been started"
70
+ end
71
+ if sleeping?
72
+ @thread.wakeup
73
+ @queue.deq
74
+ end
75
+ end
76
+
77
+ def start
78
+ if started? and not finished?
79
+ raise NotFinishedError, "tracer has not finished"
80
+ end
81
+ @queue = Queue.new
82
+ @thread = Thread.new do
83
+ @predicate_copy, @block_copy = @predicate, @block
84
+ Thread.current.set_trace_func method(:on_event).to_proc
85
+ @block_copy.call
86
+ Thread.current.set_trace_func(nil)
87
+ @queue.enq nil
88
+ end
89
+ @queue.deq
90
+ end
91
+
92
+ def trace(&block)
93
+ @block = block
94
+ end
95
+
96
+ def consume!
97
+ events = [start]
98
+ current_event = nil
99
+ events.push(current_event) while current_event = resume
100
+ events
101
+ end
102
+
103
+ private
104
+ def on_event(name, file, lineno, method, binding, _)
105
+ event = Event.new name, file: file, method: method, lineno: lineno, binding: binding
106
+ if event.file != __FILE__ and @predicate_copy.call(event)
107
+ @queue.enq(event)
108
+ Thread.stop
109
+ end
110
+ rescue Exception => e
111
+ warn "TRACER CRASHED"
112
+ Thread.current.set_trace_func(nil)
113
+ end
114
+ end
@@ -0,0 +1,96 @@
1
+ class Pask::Event
2
+ CALL = "call"
3
+ C_CALL = "c-call"
4
+ RETURN = "return"
5
+ C_RETURN = "c-return"
6
+ KNOWN_NAMES = [CALL, C_CALL, RETURN, C_RETURN].map!(&:freeze)
7
+
8
+ def initialize(name, other)
9
+ @name = name
10
+ @other = other
11
+ @created_at = Time.now
12
+ @signature = nil
13
+ @to_f = nil
14
+ @emit_by = binding.eval "self"
15
+ end
16
+
17
+ def emit_by
18
+ # FIXME: messed up for C calls and returns. maybe return nil for them?
19
+ @emit_by
20
+ end
21
+
22
+ def to_f
23
+ @to_f ||= @created_at.to_f
24
+ end
25
+
26
+ def created_at
27
+ @created_at
28
+ end
29
+
30
+ def file
31
+ @other[:file]
32
+ end
33
+
34
+ def lineno
35
+ @other[:lineno]
36
+ end
37
+
38
+ def emit_by_method
39
+ @other[:method]
40
+ end
41
+
42
+ def binding
43
+ @other[:binding]
44
+ end
45
+
46
+ def __binding__
47
+ Kernel.binding
48
+ end
49
+
50
+ def any_call?
51
+ call? or c_call?
52
+ end
53
+
54
+ def return?
55
+ @name == RETURN
56
+ end
57
+
58
+ def c_return?
59
+ @name == C_RETURN
60
+ end
61
+
62
+ def call?
63
+ @name == CALL
64
+ end
65
+
66
+ def c_call?
67
+ @name == C_CALL
68
+ end
69
+
70
+ def any_return?
71
+ return? or c_return?
72
+ end
73
+
74
+ def origin?(mod)
75
+ if emit_by.respond_to?(:ancestors)
76
+ emit_by.ancestors.include?(mod)
77
+ else
78
+ mod === emit_by
79
+ end
80
+ end
81
+
82
+ def signature
83
+ @signature ||= if emit_by.kind_of?(Module)
84
+ [emit_by, @method].join(".")
85
+ else
86
+ [emit_by.class, @method].join("#")
87
+ end
88
+ end
89
+
90
+ def -(other)
91
+ unless other.respond_to?(:to_f)
92
+ raise TypeError, "cannot coerce argument to a float"
93
+ end
94
+ to_f - other.to_f
95
+ end
96
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path('../lib/pask', __FILE__)
2
+ Gem::Specification.new do |gem|
3
+ gem.name = "pask"
4
+ gem.authors = ["Public Domain"]
5
+ gem.email = ["robert@flowof.info"]
6
+ gem.description = "concurrent tracer implemented on top of Thread#set_trace_func"
7
+ gem.summary = gem.description
8
+ gem.homepage = "https://github.com/robgleeson/pask"
9
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
10
+ gem.files = `git ls-files`.split("\n")
11
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
12
+ gem.require_paths = ["lib"]
13
+ gem.license = "Public Domain"
14
+ gem.version = Pask.version
15
+ end
@@ -0,0 +1,12 @@
1
+ require_relative "setup"
2
+ class TracerEventTest < MiniTest::Test
3
+ def test_return_value_of_binding
4
+ event = Pask::Event.new "call", {binding: TOPLEVEL_BINDING}
5
+ assert_equal TOPLEVEL_BINDING, event.binding
6
+ end
7
+
8
+ def test_return_value_of___binding__
9
+ event = Pask::Event.new "call", {binding: TOPLEVEL_BINDING}
10
+ assert_equal event, event.__binding__.eval("self")
11
+ end
12
+ end
@@ -0,0 +1,133 @@
1
+ require_relative 'setup'
2
+ class PaskTest < MiniTest::Test
3
+ def setup
4
+ @code = Code.new
5
+ end
6
+
7
+ class Code
8
+ def call
9
+ 2+2
10
+ x = 5
11
+ end
12
+ end
13
+
14
+ def test_start
15
+ tracer = Pask.new
16
+ tracer.interested? { |e| e.return? }
17
+ tracer.trace { @code.call }
18
+ assert_instance_of Pask::Event, tracer.start
19
+ end
20
+
21
+ def test_start_without_a_match
22
+ tracer = Pask.new
23
+ tracer.interested? { false }
24
+ tracer.trace { @code.call }
25
+ assert_equal nil, tracer.start
26
+ end
27
+
28
+ def test_start_on_started_tracer
29
+ tracer = Pask.new
30
+ tracer.trace { @code.call }
31
+ tracer.start
32
+ assert_raises(Pask::NotFinishedError) { tracer.start }
33
+ end
34
+
35
+ def test_consume!
36
+ tracer = Pask.new
37
+ tracer.interested? { |e| e.call? or e.return? }
38
+ tracer.trace { @code.call }
39
+ events = tracer.consume!
40
+ assert_equal 2, events.size
41
+ end
42
+
43
+ def test_sleeping_predicate_on_started_tracer
44
+ tracer = Pask.new
45
+ tracer.interested? { |event| event.return? }
46
+ tracer.trace { @code.call }
47
+ tracer.start
48
+ assert_equal true, tracer.sleeping?
49
+ end
50
+
51
+ def test_sleeping_predicate_on_finished_tracer
52
+ tracer = Pask.new
53
+ tracer.interested? { |event| event.return? }
54
+ tracer.trace { @code.call }
55
+ tracer.start
56
+ tracer.resume while tracer.resume
57
+ assert_equal false, tracer.sleeping?
58
+ end
59
+
60
+ def test_started_predicate_on_started_tracer
61
+ tracer = Pask.new
62
+ tracer.interested? { |event| event.return? }
63
+ tracer.trace { @code.call }
64
+ tracer.start
65
+ assert_equal true, tracer.started?
66
+ end
67
+
68
+ def test_started_predicate_on_unstarted_tracer
69
+ tracer = Pask.new
70
+ assert_equal false, tracer.started?
71
+ end
72
+
73
+ def test_resume_on_unstarted_tracer
74
+ tracer = Pask.new
75
+ assert_raises(Pask::NotStartedError) { tracer.resume }
76
+ end
77
+
78
+ def test_interested_predicate_without_block
79
+ tracer = Pask.new
80
+ assert_raises(ArgumentError) { tracer.interested? }
81
+ end
82
+
83
+ def test_interested_predicate_with_callable
84
+ obj = Proc.new {}
85
+ tracer = Pask.new
86
+ assert_equal obj, tracer.interested?(obj)
87
+ end
88
+
89
+ def test_finished_predicate_on_finished_tracer
90
+ tracer = Pask.new
91
+ tracer.trace { @code.call }
92
+ tracer.start
93
+ tracer.resume while tracer.resume
94
+ assert_equal true, tracer.finished?
95
+ end
96
+
97
+ def test_finished_predicate_on_unstarted_tracer
98
+ tracer = Pask.new
99
+ assert_equal false, tracer.finished?
100
+ end
101
+
102
+ def test_finished_predicate_on_started_tracer
103
+ tracer = Pask.new
104
+ tracer.interested? { |event| event.return? }
105
+ tracer.trace { @code.call }
106
+ tracer.start
107
+ assert_equal false, tracer.finished?
108
+ end
109
+
110
+ def test_running_predicate_on_dead_tracer
111
+ tracer = Pask.new
112
+ tracer.interested? { |event| event.return? }
113
+ tracer.trace { @code.call }
114
+ tracer.start
115
+ tracer.resume
116
+ assert_equal false, tracer.running?
117
+ end
118
+
119
+ def test_running_predicate_on_unstarted_tracer
120
+ tracer = Pask.new
121
+ assert_equal false, tracer.running?
122
+ end
123
+
124
+ def test_concurrent_trace_with_binding_and_local_variable
125
+ tracer = Pask.new
126
+ tracer.interested? { |e| e.call? or e.return? }
127
+ tracer.trace { @code.call }
128
+ event = tracer.start
129
+ assert_equal nil, event.binding.eval("x")
130
+ event = tracer.resume
131
+ assert_equal 5, event.binding.eval("x")
132
+ end
133
+ end
@@ -0,0 +1,2 @@
1
+ require "bundler/setup"
2
+ Bundler.require :default, :test
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pask
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Public Domain
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-14 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: concurrent tracer implemented on top of Thread#set_trace_func
14
+ email:
15
+ - robert@flowof.info
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - ".pryrc"
22
+ - ".ruby-version"
23
+ - ".travis.yml"
24
+ - ChangeLog.txt
25
+ - Gemfile
26
+ - README.md
27
+ - Rakefile
28
+ - UNLICENSE.txt
29
+ - examples/concurrent_binding_trace.rb
30
+ - examples/count_method_calls_trace.rb
31
+ - examples/time_trace.rb
32
+ - lib/pask.rb
33
+ - lib/pask/event.rb
34
+ - pask.gemspec
35
+ - test/pask_event_test.rb
36
+ - test/pask_test.rb
37
+ - test/setup.rb
38
+ homepage: https://github.com/robgleeson/pask
39
+ licenses:
40
+ - Public Domain
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 2.2.2
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: concurrent tracer implemented on top of Thread#set_trace_func
62
+ test_files:
63
+ - test/pask_event_test.rb
64
+ - test/pask_test.rb
65
+ - test/setup.rb
66
+ has_rdoc: