flamegraph_generator 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '0943ce9713996a8c3117f708bc598680e7a2b07b14dfa8ce35da38d5d06ea779'
4
+ data.tar.gz: bceb15a1ca53250b0e20fe672cb3fc5523c475c68f47ede4188d4fbf729f8e94
5
+ SHA512:
6
+ metadata.gz: 2222222a3118826de2c7adb7806caba7ab85c2abd25023dc4270c8546066bde9be49c94eff593987f8b9254a5922a5deecc32e3a875757697641d7fed655768b
7
+ data.tar.gz: e3f4f702e17248854823d65c64bb138c0b72abe338c5bded98d0e851ed205f78603200463613ea11babff902a4c645edf93cb070146043be91ac4e4a47f49800
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/testdouble/standard
3
+ ruby_version: 2.6
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in flamegraph_generator.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "minitest", "~> 5.0"
11
+
12
+ gem "standard", "~> 1.3"
data/Gemfile.lock ADDED
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ flamegraph_generator (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.2)
10
+ base64 (0.1.1)
11
+ json (2.6.3)
12
+ language_server-protocol (3.17.0.3)
13
+ lint_roller (1.1.0)
14
+ minitest (5.20.0)
15
+ parallel (1.23.0)
16
+ parser (3.2.2.4)
17
+ ast (~> 2.4.1)
18
+ racc
19
+ racc (1.7.1)
20
+ rainbow (3.1.1)
21
+ rake (13.0.6)
22
+ regexp_parser (2.8.2)
23
+ rexml (3.2.6)
24
+ rubocop (1.56.4)
25
+ base64 (~> 0.1.1)
26
+ json (~> 2.3)
27
+ language_server-protocol (>= 3.17.0)
28
+ parallel (~> 1.10)
29
+ parser (>= 3.2.2.3)
30
+ rainbow (>= 2.2.2, < 4.0)
31
+ regexp_parser (>= 1.8, < 3.0)
32
+ rexml (>= 3.2.5, < 4.0)
33
+ rubocop-ast (>= 1.28.1, < 2.0)
34
+ ruby-progressbar (~> 1.7)
35
+ unicode-display_width (>= 2.4.0, < 3.0)
36
+ rubocop-ast (1.29.0)
37
+ parser (>= 3.2.1.0)
38
+ rubocop-performance (1.19.1)
39
+ rubocop (>= 1.7.0, < 2.0)
40
+ rubocop-ast (>= 0.4.0)
41
+ ruby-progressbar (1.13.0)
42
+ standard (1.31.2)
43
+ language_server-protocol (~> 3.17.0.2)
44
+ lint_roller (~> 1.0)
45
+ rubocop (~> 1.56.4)
46
+ standard-custom (~> 1.0.0)
47
+ standard-performance (~> 1.2)
48
+ standard-custom (1.0.2)
49
+ lint_roller (~> 1.0)
50
+ rubocop (~> 1.50)
51
+ standard-performance (1.2.1)
52
+ lint_roller (~> 1.1)
53
+ rubocop-performance (~> 1.19.1)
54
+ unicode-display_width (2.5.0)
55
+
56
+ PLATFORMS
57
+ arm64-darwin-22
58
+
59
+ DEPENDENCIES
60
+ flamegraph_generator!
61
+ minitest (~> 5.0)
62
+ rake (~> 13.0)
63
+ standard (~> 1.3)
64
+
65
+ BUNDLED WITH
66
+ 2.4.13
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Owais
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,81 @@
1
+ # FlamegraphGenerator
2
+
3
+ A super simple gem that allows you to create flamegraphs that can be opened via [speedscope.app](https://speedscope.app/).
4
+
5
+ This differs from tools like stackprof/singed/rack-mini-profiler in that this allow *you* to specify the events used to create a flamegraph.
6
+ Whereas those tools automatically create flamegraph based on the call stack.
7
+
8
+ You might want to use this gem if you've got a need to create flamegraphs from a custom event source.
9
+
10
+ ## Installation
11
+
12
+ Install the gem and add to the application's Gemfile by executing:
13
+
14
+ $ bundle add flamegraph_generator
15
+
16
+ If bundler is not being used to manage dependencies, install the gem by executing:
17
+
18
+ $ gem install flamegraph_generator
19
+
20
+ ## Usage
21
+
22
+ ```ruby
23
+ # Initialize generator
24
+ #
25
+ # Parameters
26
+ # - name: String
27
+ # Optional. Defaults to 'flamegraph'
28
+ # - units: 'none' | 'nanoseconds' | 'microseconds' | 'milliseconds' | 'seconds' | 'bytes'
29
+ # Optional. Defaults to 'seconds'
30
+ generator = FlamegraphGenerator.new(name: 'myflamegraph', units: 'seconds')
31
+
32
+ # Add your events here
33
+ generator.add_event(name: 'UsersComponent', start: 0, finish: 100)
34
+ generator.add_event(name: 'X', start: 50, finish: 70, file: 'xxx.rb', line: 200, col: 10)
35
+ generator.add_event(name: 'Y', start: 112, finish: 130, file: 'yyy.rb', line: 200, col: 10)
36
+
37
+ # Will save the flamegraph to /tmp/flamegraph.json + open the flamegraph in your browser
38
+ generator.save(path: '/tmp/flamegraph.json', open: true)
39
+
40
+ # You can also get the raw speedscope-compatible flamegraph hash by:
41
+ generator.generate_flamegraph
42
+ ```
43
+
44
+ ## Contrived example
45
+
46
+ The below example hooks into the notifications dispatched by the [view_component](https://viewcomponent.org/guide/instrumentation.html) library and generates a flamegraph during each request for view components.
47
+ ```ruby
48
+ class ApplicationController
49
+ around_action :vc_bm
50
+
51
+ def vc_bm
52
+ return yield unless Rails.env.development?
53
+
54
+ generator = FlamegraphGenerator.new
55
+ callback = lambda do |name, start, finish, id, payload|
56
+ generator.add_event(name: payload[:name], start:, finish:)
57
+ end
58
+
59
+ ActiveSupport::Notifications.subscribed(callback, 'render.view_component', monotonic: true) do
60
+ yield
61
+ end
62
+
63
+ generator.save(path: Rails.root.join('tmp', 'vc_bm.json'), open: true)
64
+ end
65
+ end
66
+ ```
67
+
68
+
69
+ ## Development
70
+
71
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
72
+
73
+ 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).
74
+
75
+ ## Contributing
76
+
77
+ Bug reports and pull requests are welcome on GitHub at https://github.com/owaiswiz/flamegraph_generator.
78
+
79
+ ## License
80
+
81
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/test_*.rb"]
10
+ end
11
+
12
+ require "standard/rake"
13
+
14
+ task default: %i[test standard]
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FlamegraphGenerator
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative "flamegraph_generator/version"
5
+
6
+ class FlamegraphGenerator
7
+ Event = Struct.new(:name, :start, :finish, :file, :line, :col, keyword_init: true)
8
+
9
+ def initialize(name: 'flamegraph', unit: 'seconds')
10
+ @name = name
11
+ @unit = unit
12
+ @events = []
13
+ end
14
+
15
+ def add_event(name:, start:, finish:, file: nil, line: nil, col: nil)
16
+ raise ArgumentError, "name is required" unless name
17
+ raise ArgumentError, "start must be of type Numeric" unless start.is_a?(Numeric)
18
+ raise ArgumentError, "finish must be of type Numeric" unless finish.is_a?(Numeric)
19
+
20
+ @events << Event.new(name: name, start: start, finish: finish, file: file, line: line, col: col)
21
+ end
22
+
23
+ def save(path:, open: true)
24
+ File.write(path, generate_flamegraph.to_json)
25
+ open_with_speedscope(path) if open
26
+ end
27
+
28
+ def generate_flamegraph
29
+ sorted_events = @events.sort_by { |event| [event.start, event.finish] }
30
+
31
+ frames_by_name = {}
32
+ frames = sorted_events.uniq(&:name).map.with_index do |event, index|
33
+ frame = { name: event.name, file: event.file, line: event.line, col: event.col, index: index }.compact
34
+ frames_by_name[event.name] = frame
35
+ frame
36
+ end
37
+
38
+ start= sorted_events.first&.start
39
+ finish = sorted_events.max_by { |event| event.finish }&.finish
40
+
41
+ speedscope_events = sorted_events.map do |event|
42
+ frame_index = frames_by_name[event.name][:index]
43
+ [
44
+ { "type": 'O', "frame": frame_index, at: event.start - start },
45
+ { "type": 'C', "frame": frame_index, at: event.finish - start }
46
+ ]
47
+ end.flatten.sort_by { |speedscope_event| speedscope_event[:at] }
48
+
49
+ {
50
+ "$schema": 'https://www.speedscope.app/file-format-schema.json',
51
+ version: '0.0.1',
52
+ shared: { frames: frames },
53
+ profiles: [({
54
+ type: 'evented',
55
+ name: @name,
56
+ unit: @unit,
57
+ startValue: 0,
58
+ endValue: finish - start,
59
+ events: speedscope_events,
60
+ } if speedscope_events.present?)].compact
61
+ }
62
+ end
63
+
64
+ private
65
+ def open_with_speedscope(path)
66
+ system("npx speedscope #{path}")
67
+ end
68
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flamegraph_generator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Owais
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-10-22 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ - owaiswiz@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".standard.yml"
21
+ - Gemfile
22
+ - Gemfile.lock
23
+ - LICENSE.txt
24
+ - README.md
25
+ - Rakefile
26
+ - lib/flamegraph_generator.rb
27
+ - lib/flamegraph_generator/version.rb
28
+ homepage: https://github.com/owaiswiz/flamegraph_generator
29
+ licenses:
30
+ - MIT
31
+ metadata:
32
+ homepage_uri: https://github.com/owaiswiz/flamegraph_generator
33
+ source_code_uri: https://github.com/owaiswiz/flamegraph_generator
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 2.6.0
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubygems_version: 3.3.26
50
+ signing_key:
51
+ specification_version: 4
52
+ summary: A simple way to generate arbitrary flamegraph that can be viewed by speedscope
53
+ test_files: []