chronometer 0.1.1 → 0.1.2

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: 1fff074b1dca3ad619a10befadf147b9eef28c6d45ec30c56dd3d20b9b1e5972
4
- data.tar.gz: daa35ae5b66a491c0b781b6cdc44d0476c8923b3f2aeba5c4ca902ab6a05d7fe
3
+ metadata.gz: 2dc6f536c735b6262063d5a3091e7675208e093ec96951006bec8dca45be910e
4
+ data.tar.gz: d636d8a2d12a4b63d7edc2b1e685dd072e87b728d5984b75a0f9767f140009a2
5
5
  SHA512:
6
- metadata.gz: eb162839dc1cdab0ed373176ee660953e30141616c5c2005fe16a9f0cfe6db5bfed3ef5627b36b293ca6c0cb5d60ee8d20d7b116caf3843ba66ef5ccbc4f0af1
7
- data.tar.gz: 22051243d4d84499be6b9a056f349ac81132c2b436078f071658e9a22323486485cf50b192dae87a2caf027c648c8c4a67d9eec5ccdfb48f8f58a647d4a47664
6
+ metadata.gz: 5d7f49faecbf4ebed41c43d4640a22ff9f9db2ece0cf6907f38a039f7fd05b10187464b260679228dd3c3a56b7db041231d9b44dfcd02b692d1b0e385aa5a42f
7
+ data.tar.gz: 513375c1f1b6a04070deb65253d17fd4598ce3cde492e3436f9797dc7953db126bb8d594ed597081097e31629e2dec54ad1fa0c0e09084b1f88b754ff84411f0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chronometer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Giddins
@@ -88,16 +88,7 @@ executables:
88
88
  extensions: []
89
89
  extra_rdoc_files: []
90
90
  files:
91
- - CODE_OF_CONDUCT.md
92
- - LICENSE.txt
93
- - README.md
94
91
  - exe/chronometer
95
- - lib/chronometer.rb
96
- - lib/chronometer/command.rb
97
- - lib/chronometer/dsl.rb
98
- - lib/chronometer/event.rb
99
- - lib/chronometer/trace_event.rb
100
- - lib/chronometer/version.rb
101
92
  homepage: https://github.com/segiddins
102
93
  licenses:
103
94
  - MIT
@@ -1,74 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- In the interest of fostering an open and welcoming environment, we as
6
- contributors and maintainers pledge to making participation in our project and
7
- our community a harassment-free experience for everyone, regardless of age, body
8
- size, disability, ethnicity, gender identity and expression, level of experience,
9
- nationality, personal appearance, race, religion, or sexual identity and
10
- orientation.
11
-
12
- ## Our Standards
13
-
14
- Examples of behavior that contributes to creating a positive environment
15
- include:
16
-
17
- * Using welcoming and inclusive language
18
- * Being respectful of differing viewpoints and experiences
19
- * Gracefully accepting constructive criticism
20
- * Focusing on what is best for the community
21
- * Showing empathy towards other community members
22
-
23
- Examples of unacceptable behavior by participants include:
24
-
25
- * The use of sexualized language or imagery and unwelcome sexual attention or
26
- advances
27
- * Trolling, insulting/derogatory comments, and personal or political attacks
28
- * Public or private harassment
29
- * Publishing others' private information, such as a physical or electronic
30
- address, without explicit permission
31
- * Other conduct which could reasonably be considered inappropriate in a
32
- professional setting
33
-
34
- ## Our Responsibilities
35
-
36
- Project maintainers are responsible for clarifying the standards of acceptable
37
- behavior and are expected to take appropriate and fair corrective action in
38
- response to any instances of unacceptable behavior.
39
-
40
- Project maintainers have the right and responsibility to remove, edit, or
41
- reject comments, commits, code, wiki edits, issues, and other contributions
42
- that are not aligned to this Code of Conduct, or to ban temporarily or
43
- permanently any contributor for other behaviors that they deem inappropriate,
44
- threatening, offensive, or harmful.
45
-
46
- ## Scope
47
-
48
- This Code of Conduct applies both within project spaces and in public spaces
49
- when an individual is representing the project or its community. Examples of
50
- representing a project or community include using an official project e-mail
51
- address, posting via an official social media account, or acting as an appointed
52
- representative at an online or offline event. Representation of a project may be
53
- further defined and clarified by project maintainers.
54
-
55
- ## Enforcement
56
-
57
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at segiddins@squareup.com. All
59
- complaints will be reviewed and investigated and will result in a response that
60
- is deemed necessary and appropriate to the circumstances. The project team is
61
- obligated to maintain confidentiality with regard to the reporter of an incident.
62
- Further details of specific enforcement policies may be posted separately.
63
-
64
- Project maintainers who do not follow or enforce the Code of Conduct in good
65
- faith may face temporary or permanent repercussions as determined by other
66
- members of the project's leadership.
67
-
68
- ## Attribution
69
-
70
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
- available at [http://contributor-covenant.org/version/1/4][version]
72
-
73
- [homepage]: http://contributor-covenant.org
74
- [version]: http://contributor-covenant.org/version/1/4/
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2018 Samuel Giddins
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 DELETED
@@ -1,43 +0,0 @@
1
- # Chronometer
2
-
3
- A small gem that generates Chrome trace files for Ruby programs.
4
-
5
- ## Installation
6
-
7
- Add this line to your application's Gemfile:
8
-
9
- ```ruby
10
- gem 'chronometer'
11
- ```
12
-
13
- And then execute:
14
-
15
- $ bundle
16
-
17
- Or install it yourself as:
18
-
19
- $ gem install chronometer
20
-
21
- ## Usage
22
-
23
- ```
24
- $ chronometer CocoaPods.chronofile -- pod --version
25
- ```
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 tags, 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]/chronometer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
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 Chronometer project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/chronometer/blob/master/CODE_OF_CONDUCT.md).
@@ -1,130 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'chronometer/dsl'
4
- require_relative 'chronometer/event'
5
- require_relative 'chronometer/trace_event'
6
- require_relative 'chronometer/version'
7
-
8
- class Chronometer
9
- attr_reader :trace_events
10
-
11
- def self.from_file(path, contents: File.read(path))
12
- new do
13
- instance_eval(contents, path)
14
- end
15
- end
16
-
17
- def initialize(&blk)
18
- dsl = DSL.new
19
- dsl.instance_exec(&blk)
20
- @events = dsl.events
21
- @tracepoints = dsl.tracepoints
22
- @trace_event_queue = Queue.new
23
- @trace_events = []
24
- end
25
-
26
- def install!
27
- @events.each { |e| install_method_hook(e) }
28
- @tracepoints.each { |tp| install_tracepoint(tp) }
29
- end
30
-
31
- def drain!
32
- loop { @trace_events << @trace_event_queue.pop(true) }
33
- rescue ThreadError
34
- nil
35
- end
36
-
37
- def print_trace_event_report(dest, metadata: {})
38
- raise ArgumentError, 'cannot manually specify :traceEvents' if metadata.key?(:traceEvents)
39
- require 'json'
40
- File.open(dest, 'w') do |f|
41
- f << JSON.generate(metadata)
42
- f.seek(-1, :CUR) # remove closing }
43
- f << ',' unless metadata.empty?
44
- f << '"traceEvents":['
45
- @trace_events.each_with_index do |te, i|
46
- f << ',' unless i == 0
47
- f << JSON.generate(te.to_h)
48
- end
49
- f << ']}'
50
- end
51
- end
52
-
53
- def self.timestamp_us
54
- Time.now.utc.to_f.*(1_000_000).round
55
- end
56
-
57
- def register_trace_event(event)
58
- @trace_event_queue << event
59
- end
60
-
61
- private
62
-
63
- def install_method_hook(event)
64
- cls = event.cls
65
- method = event.method
66
-
67
- unbound_method = cls.instance_method(method)
68
- arg_labels = unbound_method.parameters.map(&:last)
69
-
70
- timer = self
71
-
72
- cls.send(:define_method, method) do |*args, &blk|
73
- context = event.context&.call(self)
74
- args_dict = arg_labels.zip(args).to_h
75
- args_dict[:context] = context if context
76
-
77
- start_time = ::Chronometer.timestamp_us
78
-
79
- event_type = event.event_type
80
- if event_type == :X
81
- timer.register_trace_event TraceEvent.new(
82
- process_id: Process.pid,
83
- thread_id: Thread.current.object_id,
84
- start_time_usec: ::Chronometer.timestamp_us,
85
- event_type: :B,
86
- name: event.name
87
- )
88
- event_type = :E
89
- end
90
-
91
- r0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
92
- begin
93
- unbound_method.bind(self).call(*args, &blk)
94
- ensure
95
- duration = Process.clock_gettime(Process::CLOCK_MONOTONIC).-(r0).*(1_000_000).round
96
-
97
- timer.register_trace_event TraceEvent.new(
98
- process_id: Process.pid,
99
- thread_id: Thread.current.object_id,
100
- start_time_usec: event_type == :E ? ::Chronometer.timestamp_us : start_time,
101
- event_type: event_type,
102
- name: event.name,
103
- args: args_dict,
104
- category: event.category,
105
- duration: duration,
106
- cls: cls,
107
- method: method
108
- )
109
- end
110
- end
111
- end
112
-
113
- def install_tracepoint(tracepoint)
114
- event_name, blk = tracepoint
115
- TracePoint.trace(event_name) do |tp|
116
- next if tp.path == __FILE__
117
- args = {
118
- process_id: Process.pid,
119
- thread_id: Thread.current.object_id,
120
- start_time_usec: ::Chronometer.timestamp_us,
121
- event_type: :I,
122
- name: event_name
123
- }
124
- args.update blk&.call(tp)
125
-
126
- te = TraceEvent.new(**args)
127
- register_trace_event(te)
128
- end
129
- end
130
- end
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'claide'
4
- require 'chronometer'
5
-
6
- class Chronometer
7
- module Command; end
8
- class Command::Chronometer < CLAide::Command
9
- self.version = VERSION
10
-
11
- self.summary = "Trace a ruby program's execution using a chronofile"
12
-
13
- self.arguments = [
14
- CLAide::Argument.new('CHRONOFILE', true),
15
- CLAide::Argument.new('RUBY_FILE', true),
16
- CLAide::Argument.new('ARGUMENTS', false, true)
17
- ]
18
-
19
- def self.options
20
- [
21
- ['--output=TRACE', 'The path to the tracefile chronometer will write']
22
- ].concat(super)
23
- end
24
-
25
- def initialize(argv)
26
- @chronofile = argv.shift_argument
27
- @output = argv.option('output', "#{@chronofile}.trace")
28
- @file_to_load = argv.shift_argument
29
- @arguments = argv.remainder! if @chronofile && @file_to_load
30
- super
31
- end
32
-
33
- def validate!
34
- super
35
- help! 'Must supply a chronofile' unless @chronofile
36
- @chronofile_contents = begin
37
- File.read(@chronofile)
38
- rescue StandardError
39
- help!("No such chronofile `#{@chronofile}`")
40
- end
41
- help! 'Must supply a ruby file to load' unless @file_to_load
42
- @file_to_load = ENV.fetch('PATH', '').split(File::PATH_SEPARATOR).push('.').reduce do |a, e|
43
- next a if a
44
- a ||= File.join(e, @file_to_load)
45
- a &&= nil unless File.file?(a)
46
- a
47
- end
48
- help! "Could not find `#{@file_to_load}`" unless @file_to_load
49
- end
50
-
51
- def run
52
- argv = ::ARGV.dup
53
- ::ARGV.replace(@arguments)
54
- time { load(@file_to_load) }
55
- ensure
56
- ::ARGV.replace(argv)
57
- end
58
-
59
- private
60
-
61
- def time
62
- timer = ::Chronometer.from_file(@chronofile, contents: @chronofile_contents)
63
- timer.install!
64
-
65
- begin
66
- yield
67
- ensure
68
- timer.drain!
69
- timer.print_trace_event_report(@output, metadata: { meta_success: $ERROR_INFO.nil? })
70
- end
71
- end
72
- end
73
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Chronometer
4
- class DSL
5
- attr_reader :events, :tracepoints
6
-
7
- def initialize
8
- @events = []
9
- @tracepoints = []
10
- end
11
-
12
- def for_class(cls)
13
- @cls && raise('already in for_class')
14
- @cls = cls
15
-
16
- yield
17
- ensure
18
- @cls = nil
19
- end
20
-
21
- def for_singleton_class(cls, &blk)
22
- for_class(cls.singleton_class, &blk)
23
- end
24
-
25
- def method(method_name, **opts)
26
- opts[:name] ||= @cls.singleton_class? ? "#{ObjectSpace.each_object(@cls).to_a.last}.#{method_name}" : "#{@cls}##{method_name}"
27
- opts.delete(:method) && raise('Cannot specify :method')
28
- opts[:method] = method_name
29
- opts[:event_type] ||= :X
30
- opts[:cls] = @cls || raise('must be in for_class block')
31
- events << Event.new(opts)
32
- end
33
-
34
- def methods(*method_names, **opts)
35
- method_names.flatten.each do |method_name|
36
- method method_name, **opts
37
- end
38
- end
39
-
40
- def tracepoint(event_name, &blk)
41
- @cls && raise('in for_class block')
42
-
43
- tracepoints << [event_name, blk]
44
- end
45
- end
46
- private_constant :DSL
47
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Chronometer
4
- class Event
5
- attr_reader :cls, :method, :name, :category, :event_type, :context
6
-
7
- def initialize(cls: nil, method: nil, name: nil, category: nil, event_type: nil, context: nil)
8
- @cls = cls
9
- @method = method
10
- @name = name
11
- @category = category
12
- @event_type = event_type
13
- @context = context
14
- end
15
- end
16
- private_constant :Event
17
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Chronometer
4
- class TraceEvent
5
- attr_reader :process_id, :thread_id, :start_time_usec, :event_type, :name, :args, :category, :duration, :cls, :method
6
-
7
- def initialize(process_id: nil, thread_id: nil, start_time_usec: nil, event_type: nil, name: nil, args: nil, category: nil, duration: nil, cls: nil, method: nil)
8
- @process_id = process_id
9
- @thread_id = thread_id
10
- @start_time_usec = start_time_usec
11
- @event_type = event_type
12
- @name = name
13
- @args = args
14
- @category = category
15
- @duration = duration
16
- @cls = cls
17
- @method = method
18
- end
19
-
20
- def to_h
21
- compact_hash(
22
- pid: process_id,
23
- tid: thread_id,
24
- ts: start_time_usec,
25
- ph: event_type,
26
- name: name,
27
- args: args,
28
- cat: category,
29
- dur: duration
30
- )
31
- end
32
-
33
- if {}.respond_to?(:compact)
34
- def compact_hash(hash)
35
- hash.compact
36
- end
37
- else
38
- def compact_hash(hash)
39
- hash.reject { |_k, v| v.nil? }
40
- end
41
- end
42
- end
43
- private_constant :TraceEvent
44
- end
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Chronometer
4
- VERSION = File.read(File.expand_path('../../VERSION', __dir__)).strip.freeze
5
- end