estackprof 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: 2fb2e9ab5ae1157f4f88c4ddf9091aba225e97036725c1641598df88a9959c91
4
+ data.tar.gz: b9b994ef293a0f97df4a1a2c72395fd2d2765e7023ce4cf4e86e19898137bf12
5
+ SHA512:
6
+ metadata.gz: 8e98f8ebe4b4051c418e6f75d7f93aab8a40b0d6a1cf9f02d24d00b0ae5909371a020c1c59d76a3de243cbe0f0b16c98d18627ac40d4977c6870cbeccc7fff63
7
+ data.tar.gz: 0e5de4a47cd8217c29b4fd8956796a14674bfab44992b7e58e9152e619d870bd20ef343cec94dc6e4865dbc1e2c534f11031b008791f416a49db712748d88b2d
@@ -0,0 +1,28 @@
1
+ version: 2.1
2
+ jobs:
3
+ build:
4
+ docker:
5
+ - image: ruby:3.0.1
6
+ steps:
7
+ - checkout
8
+ - restore_cache:
9
+ name: Restore bundle cache
10
+ keys:
11
+ - bundle-{{ arch }}-{{ checksum "Gemfile" }}-{{ checksum "estackprof.gemspec" }}
12
+ - bundle-{{ arch }}-{{ checksum "Gemfile" }}
13
+ - bundle-{{ arch }}
14
+ - bundle
15
+ - run:
16
+ name: Bundle Install
17
+ command: bundle install
18
+ - save_cache:
19
+ name: Store bundle cache
20
+ key: bundle-{{ arch }}-{{ checksum "Gemfile" }}-{{ checksum "estackprof.gemspec" }}
21
+ paths:
22
+ - vendor/bundle
23
+ - run:
24
+ name: Rubocop
25
+ command: bundle exec rubocop
26
+ - run:
27
+ name: RSpec
28
+ command: bundle exec rspec
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /vendor/bundle/
9
+ Gemfile.lock
10
+
11
+ /example/vendor/bundle/
12
+ /example/bundle/
13
+
14
+ # rspec failure tracking
15
+ .rspec_status
16
+
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,30 @@
1
+ # The behavior of RuboCop can be controlled via the .rubocop.yml
2
+ # configuration file. It makes it possible to enable/disable
3
+ # certain cops (checks) and to alter their behavior if they accept
4
+ # any parameters. The file can be placed either in your home
5
+ # directory or in some project directory.
6
+ #
7
+ # RuboCop will start looking for the configuration file in the directory
8
+ # where the inspected file is and continue its way up to the root directory.
9
+ #
10
+ # See https://docs.rubocop.org/rubocop/configuration
11
+ require:
12
+ - rubocop-rspec
13
+ - rubocop-rake
14
+
15
+ AllCops:
16
+ NewCops: enable
17
+ TargetRubyVersion: 2.5
18
+
19
+ Style/Documentation:
20
+ Enabled: false
21
+
22
+ Metrics/BlockLength:
23
+ Exclude:
24
+ - "spec/**/*.rb"
25
+
26
+ RSpec/MessageSpies:
27
+ EnforcedStyle: receive
28
+
29
+ Security/MarshalLoad:
30
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.0.1
data/Gemfile ADDED
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in estackprof.gemspec
6
+ gemspec
7
+
8
+ gem 'rake', '~> 13.0'
9
+
10
+ group :development do
11
+ gem 'guard-rubocop'
12
+ gem 'pry'
13
+ gem 'rubocop'
14
+ gem 'rubocop-rake'
15
+ gem 'rubocop-rspec'
16
+ gem 'sinatra'
17
+ gem 'webrick'
18
+ end
19
+
20
+ group :test do
21
+ gem 'aruba', '~> 2.0.0'
22
+ gem 'guard-rspec'
23
+ gem 'rspec', '~> 3.0'
24
+ end
data/Guardfile ADDED
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A sample Guardfile
4
+ # More info at https://github.com/guard/guard#readme
5
+
6
+ ## Uncomment and set this to only include directories you want to watch
7
+ # directories %w(app lib config test spec features) \
8
+ # .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
9
+
10
+ ## Note: if you are using the `directories` clause above and you are not
11
+ ## watching the project directory ('.'), then you will want to move
12
+ ## the Guardfile to a watched dir and symlink it back, e.g.
13
+ #
14
+ # $ mkdir config
15
+ # $ mv Guardfile config/
16
+ # $ ln -s config/Guardfile .
17
+ #
18
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
19
+
20
+ # NOTE: The cmd option is now required due to the increasing number of ways
21
+ # rspec may be run, below are examples of the most common uses.
22
+ # * bundler: 'bundle exec rspec'
23
+ # * bundler binstubs: 'bin/rspec'
24
+ # * spring: 'bin/rspec' (This will use spring if running and you have
25
+ # installed the spring binstubs per the docs)
26
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
27
+ # * 'just' rspec: 'rspec'
28
+
29
+ guard :rspec, cmd: 'bundle exec rspec' do
30
+ require 'guard/rspec/dsl'
31
+ dsl = Guard::RSpec::Dsl.new(self)
32
+
33
+ # Feel free to open issues for suggestions and improvements
34
+
35
+ # RSpec files
36
+ rspec = dsl.rspec
37
+ watch(rspec.spec_helper) { rspec.spec_dir }
38
+ watch(rspec.spec_support) { rspec.spec_dir }
39
+ watch(rspec.spec_files)
40
+
41
+ # Ruby files
42
+ ruby = dsl.ruby
43
+ dsl.watch_spec_files_for(ruby.lib_files)
44
+ dsl.watch_spec_files_for(%r{^(exe/.+)\.rb$})
45
+ end
46
+
47
+ guard :rubocop, cli: %w[-a] do
48
+ watch(/.+\.rb$/)
49
+ watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
50
+ end
data/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # Estackprof
2
+
3
+ Estackprof is a wrapper to make it easier to use [Stackprof](https://github.com/tmm1/stackprof) in your rack application.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'estackprof'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install estackprof
20
+
21
+ ## Usage
22
+
23
+ ### Profiling
24
+
25
+ Add the following code to your rack application to enable the rack middleware.
26
+ `Estackprof::Middleware` supports the same options as `StackProf::Middleware`.
27
+
28
+ ```ruby
29
+ require 'estackprof'
30
+
31
+ use Estackprof::Middleware
32
+
33
+ # ...your rack application
34
+ ```
35
+
36
+ ### Reporting
37
+
38
+ Use the CLI to report.
39
+
40
+ ```sh
41
+ # Report the top 3 frames
42
+ $ estackprof top -l 3
43
+ ==================================
44
+ Mode: cpu(1000)
45
+ Samples: 516 (0.00% miss rate)
46
+ GC: 77 (14.92%)
47
+ ==================================
48
+ TOTAL (pct) SAMPLES (pct) FRAME
49
+ 434 (84.1%) 434 (84.1%) Object#bubble_sort(example/app.rb:9)
50
+ 45 (8.7%) 45 (8.7%) (sweeping)
51
+ 31 (6.0%) 31 (6.0%) (marking)
52
+
53
+ # Report the top 3 cumlative frames
54
+ $ estackprof top -l 3 -c
55
+ ==================================
56
+ Mode: cpu(1000)
57
+ Samples: 516 (0.00% miss rate)
58
+ GC: 77 (14.92%)
59
+ ==================================
60
+ TOTAL (pct) SAMPLES (pct) FRAME
61
+ 439 (85.1%) 0 (0.0%) Rack::MethodOverride#call(vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/method_override.rb:15)
62
+ 439 (85.1%) 0 (0.0%) Sinatra::ExtendedRack#call(vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb:215)
63
+ 439 (85.1%) 0 (0.0%) Sinatra::Wrapper#call(vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb:1990)
64
+
65
+ # Report the top frames filtered by patten(file name).
66
+ $ estackprof top -p app.rb
67
+ ==================================
68
+ Mode: cpu(1000)
69
+ Samples: 516 (0.00% miss rate)
70
+ GC: 77 (14.92%)
71
+ ==================================
72
+ TOTAL (pct) SAMPLES (pct) FRAME
73
+ 434 (84.1%) 434 (84.1%) Object#bubble_sort(example/app.rb:9)
74
+ 438 (84.9%) 0 (0.0%) block in <main>(example/app.rb:23)
75
+
76
+ # Report the top frames filtered by patten(method name).
77
+ $ estackprof top -p bubble
78
+ ==================================
79
+ Mode: cpu(1000)
80
+ Samples: 516 (0.00% miss rate)
81
+ GC: 77 (14.92%)
82
+ ==================================
83
+ TOTAL (pct) SAMPLES (pct) FRAME
84
+ 434 (84.1%) 434 (84.1%) Object#bubble_sort(example/app.rb:9)
85
+
86
+ # Report the list in the specified file.
87
+ $ estackprof list -f app.rb
88
+ | 1 | # frozen_string_literal: true
89
+ | 2 |
90
+ | 3 | require 'sinatra'
91
+ | 4 | require 'json'
92
+ | 5 | require 'estackprof'
93
+ | 6 |
94
+ | 7 | use Estackprof::Middleware
95
+ | 8 |
96
+ | 9 | def bubble_sort(array)
97
+ | 10 | ary = array.dup
98
+ | 11 | pos_max = ary.size - 1
99
+ | 12 |
100
+ 434 (84.1%) | 13 | (0...pos_max).each do |n|
101
+ 433 (83.9%) | 14 | (0...(pos_max - n)).each do |ix|
102
+ | 15 | iy = ix + 1
103
+ 139 (26.9%) / 139 (26.9%) | 16 | ary[ix], ary[iy] = ary[iy], ary[ix] if ary[ix] > ary[iy]
104
+ 294 (57.0%) / 294 (57.0%) | 17 | end
105
+ 1 (0.2%) / 1 (0.2%) | 18 | end
106
+ | 19 |
107
+ | 20 | ary
108
+ | 21 | end
109
+ | 22 |
110
+ | 23 | get '/' do
111
+ | 24 | array = Array.new(1000) { rand(10_000) }
112
+ 438 (84.9%) | 25 | bubble_sort(array).to_s
113
+ | 26 | end
114
+
115
+ # Report the list in the specified method.
116
+ $ estackprof list -m bubble
117
+ Object#bubble_sort (/estackprof/example/app.rb:9)
118
+ samples: 434 self (84.1%) / 434 total (84.1%)
119
+ callers:
120
+ 867 ( 199.8%) Range#each
121
+ 434 ( 100.0%) block in <main>
122
+ callees (0 total):
123
+ 867 ( Inf%) Range#each
124
+ code:
125
+ | 9 | def bubble_sort(array)
126
+ | 10 | ary = array.dup
127
+ | 11 | pos_max = ary.size - 1
128
+ | 12 |
129
+ 434 (84.1%) | 13 | (0...pos_max).each do |n|
130
+ 433 (83.9%) | 14 | (0...(pos_max - n)).each do |ix|
131
+ | 15 | iy = ix + 1
132
+ 139 (26.9%) / 139 (26.9%) | 16 | ary[ix], ary[iy] = ary[iy], ary[ix] if ary[ix] > ary[iy]
133
+ 294 (57.0%) / 294 (57.0%) | 17 | end
134
+ 1 (0.2%) / 1 (0.2%) | 18 | end
135
+ | 19 |
136
+
137
+ # Display the flamegraph
138
+ $ estackprof flamegraph
139
+ #=> Open flamegraph in your browser.
140
+ ```
141
+
142
+ ## Development
143
+
144
+ 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.
145
+
146
+ 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).
147
+
148
+ ## Contributing
149
+
150
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/estackprof.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'estackprof'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
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
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/estackprof/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'estackprof'
7
+ spec.version = Estackprof::VERSION
8
+ spec.authors = ['Yuhei Okazaki']
9
+ spec.email = ['okazaki@fusic.co.jp']
10
+
11
+ spec.summary = 'We want to make stackprof easier to use.'
12
+ spec.description = 'Estackprof is a wrapper to make it easier to use Stackprof in your rack application'
13
+ spec.homepage = 'https://github.com/fusic/estackprof'
14
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = 'https://github.com/fusic/estackprof'
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
23
+ end
24
+ spec.bindir = 'exe'
25
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ['lib']
27
+
28
+ # Uncomment to register a new dependency of your gem
29
+ spec.add_dependency 'launchy'
30
+ spec.add_dependency 'stackprof', '~> 0.2'
31
+ spec.add_dependency 'thor', '~> 1.1.0'
32
+
33
+ # For more information and examples about making a new gem, checkout our
34
+ # guide at: https://bundler.io/guides/creating_gem.html
35
+ end
data/example/app.rb ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sinatra'
4
+ require 'json'
5
+ require 'estackprof'
6
+
7
+ use Estackprof::Middleware
8
+
9
+ def bubble_sort(array)
10
+ ary = array.dup
11
+ pos_max = ary.size - 1
12
+
13
+ (0...pos_max).each do |n|
14
+ (0...(pos_max - n)).each do |ix|
15
+ iy = ix + 1
16
+ ary[ix], ary[iy] = ary[iy], ary[ix] if ary[ix] > ary[iy]
17
+ end
18
+ end
19
+
20
+ ary
21
+ end
22
+
23
+ get '/' do
24
+ array = Array.new(1000) { rand(10_000) }
25
+ bubble_sort(array).to_s
26
+ end
data/exe/estackprof ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'estackprof'
5
+
6
+ Estackprof::CLI.start(ARGV)
data/flamegraph.html ADDED
@@ -0,0 +1,153 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+
8
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
9
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/spiermar/d3-flame-graph@2.0.3/dist/d3-flamegraph.css">
10
+
11
+ <style>
12
+
13
+ /* Space out content a bit */
14
+ body {
15
+ padding-top: 20px;
16
+ padding-bottom: 20px;
17
+ }
18
+
19
+ /* Custom page header */
20
+ .header {
21
+ padding-bottom: 20px;
22
+ padding-right: 15px;
23
+ padding-left: 15px;
24
+ border-bottom: 1px solid #e5e5e5;
25
+ }
26
+
27
+ /* Make the masthead heading the same height as the navigation */
28
+ .header h3 {
29
+ margin-top: 0;
30
+ margin-bottom: 0;
31
+ line-height: 40px;
32
+ }
33
+
34
+ /* Customize container */
35
+ .container {
36
+ max-width: 990px;
37
+ }
38
+
39
+ address {
40
+ text-align: right;
41
+ }
42
+ </style>
43
+
44
+ <title>stackprof (mode: cpu)</title>
45
+
46
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
47
+ <!--[if lt IE 9]>
48
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
49
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
50
+ <![endif]-->
51
+ </head>
52
+ <body>
53
+ <div class="container">
54
+ <div class="header clearfix">
55
+ <nav>
56
+ <div class="pull-right">
57
+ <form class="form-inline" id="form">
58
+ <a class="btn" href="javascript: resetZoom();">Reset zoom</a>
59
+ <a class="btn" href="javascript: clear();">Clear</a>
60
+ <div class="form-group">
61
+ <input type="text" class="form-control" id="term">
62
+ </div>
63
+ <a class="btn btn-primary" href="javascript: search();">Search</a>
64
+ </form>
65
+ </div>
66
+ </nav>
67
+ <h3 class="text-muted">stackprof (mode: cpu)</h3>
68
+ </div>
69
+ <div id="chart">
70
+ </div>
71
+ <address>
72
+ powered by <a href="https://github.com/spiermar/d3-flame-graph">d3-flame-graph</a>
73
+ </address>
74
+ <hr>
75
+ <div id="details">
76
+ </div>
77
+ </div>
78
+
79
+ <!-- D3.js -->
80
+ <script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
81
+
82
+ <!-- d3-tip -->
83
+ <script type="text/javascript" src=https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.9.1/d3-tip.min.js></script>
84
+
85
+ <!-- d3-flamegraph -->
86
+ <script type="text/javascript" src="https://cdn.jsdelivr.net/gh/spiermar/d3-flame-graph@2.0.3/dist/d3-flamegraph.min.js"></script>
87
+
88
+ <script type="text/javascript">
89
+ var flameGraph = d3.flamegraph()
90
+ .width(960)
91
+ .cellHeight(18)
92
+ .transitionDuration(750)
93
+ .minFrameSize(5)
94
+ .transitionEase(d3.easeCubic)
95
+ .sort(true)
96
+ //Example to sort in reverse order
97
+ //.sort(function(a,b){ return d3.descending(a.name, b.name);})
98
+ .title("")
99
+ .onClick(onClick)
100
+ .differential(false)
101
+ .selfValue(false);
102
+
103
+
104
+ // Example on how to use custom tooltips using d3-tip.
105
+ // var tip = d3.tip()
106
+ // .direction("s")
107
+ // .offset([8, 0])
108
+ // .attr('class', 'd3-flame-graph-tip')
109
+ // .html(function(d) { return "name: " + d.data.name + ", value: " + d.data.value; });
110
+
111
+ // flameGraph.tooltip(tip);
112
+
113
+ var details = document.getElementById("details");
114
+ flameGraph.setDetailsElement(details);
115
+
116
+ // Example on how to use custom labels
117
+ // var label = function(d) {
118
+ // return "name: " + d.name + ", value: " + d.value;
119
+ // }
120
+ // flameGraph.label(label);
121
+
122
+ // Example of how to set fixed chart height
123
+ // flameGraph.height(540);
124
+
125
+ d3.select("#chart")
126
+ .datum({"name":"<root>","value":278,"children":[{"name":"(garbage collection) : ","value":36,"children":[{"name":"(marking) : ","value":12,"children":[]},{"name":"(sweeping) : ","value":24,"children":[]}]},{"name":"WEBrick::GenericServer#start_thread : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/webrick-1.7.0/lib/webrick/server.rb","value":241,"children":[{"name":"WEBrick::HTTPServer#run : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/webrick-1.7.0/lib/webrick/httpserver.rb","value":241,"children":[{"name":"WEBrick::HTTPServer#service : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/webrick-1.7.0/lib/webrick/httpserver.rb","value":241,"children":[{"name":"Rack::Handler::WEBrick#service : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/handler/webrick.rb","value":241,"children":[{"name":"Sinatra::Base.call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":241,"children":[{"name":"Sinatra::Base.synchronize : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":241,"children":[{"name":"Sinatra::Base.call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":241,"children":[{"name":"Sinatra::Wrapper#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":241,"children":[{"name":"Sinatra::ExtendedRack#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":241,"children":[{"name":"Sinatra::ShowExceptions#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/show_exceptions.rb","value":241,"children":[{"name":"Rack::MethodOverride#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/method_override.rb","value":241,"children":[{"name":"Rack::Head#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/head.rb","value":241,"children":[{"name":"Sinatra::CommonLogger#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":241,"children":[{"name":"Rack::CommonLogger#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":241,"children":[{"name":"Rack::CommonLogger#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/common_logger.rb","value":241,"children":[{"name":"Rack::Logger#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/logger.rb","value":241,"children":[{"name":"Rack::Protection::FrameOptions#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-protection-2.1.0/lib/rack/protection/frame_options.rb","value":241,"children":[{"name":"Rack::Protection::Base#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-protection-2.1.0/lib/rack/protection/base.rb","value":241,"children":[{"name":"Rack::Protection::Base#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-protection-2.1.0/lib/rack/protection/base.rb","value":241,"children":[{"name":"Rack::Protection::JsonCsrf#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-protection-2.1.0/lib/rack/protection/json_csrf.rb","value":241,"children":[{"name":"Rack::Protection::PathTraversal#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-protection-2.1.0/lib/rack/protection/path_traversal.rb","value":241,"children":[{"name":"Rack::Protection::XSSHeader#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-protection-2.1.0/lib/rack/protection/xss_header.rb","value":241,"children":[{"name":"StackProf::Middleware#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/stackprof-0.2.17/lib/stackprof/middleware.rb","value":241,"children":[{"name":"Sinatra::Base#call : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":241,"children":[{"name":"Sinatra::Base#call! : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":241,"children":[{"name":"Sinatra::Base#invoke : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":241,"children":[{"name":"Kernel#catch : <cfunc>","value":241,"children":[{"name":"Sinatra::Base#invoke : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":241,"children":[{"name":"Sinatra::Base#call! : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":241,"children":[{"name":"Sinatra::Base#dispatch! : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":241,"children":[{"name":"Sinatra::Request#params : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":1,"children":[{"name":"Rack::Request#params : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/request.rb","value":1,"children":[{"name":"Rack::Request::Helpers#params : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/request.rb","value":1,"children":[{"name":"Rack::Request::Helpers#POST : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/request.rb","value":1,"children":[{"name":"Rack::Request::Helpers#form_data? : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/request.rb","value":1,"children":[{"name":"Rack::Request::Helpers#media_type : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/request.rb","value":1,"children":[{"name":"Kernel#require : <cfunc>","value":1,"children":[{"name":"<top (required)> : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/media_type.rb","value":1,"children":[{"name":"<module:Rack> : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/media_type.rb","value":1,"children":[{"name":"Class#inherited : <cfunc>","value":1,"children":[]}]}]}]}]}]}]}]}]}]},{"name":"Sinatra::Base#invoke : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":240,"children":[{"name":"Kernel#catch : <cfunc>","value":238,"children":[{"name":"Sinatra::Base#invoke : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":238,"children":[{"name":"Sinatra::Base#dispatch! : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":238,"children":[{"name":"Sinatra::Base#route! : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":238,"children":[{"name":"Array#each : <cfunc>","value":238,"children":[{"name":"Sinatra::Base#route! : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":238,"children":[{"name":"Sinatra::Base#process_route : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":238,"children":[{"name":"Kernel#catch : <cfunc>","value":238,"children":[{"name":"Sinatra::Base#process_route : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":238,"children":[{"name":"Sinatra::Base#route! : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":238,"children":[{"name":"Sinatra::Base#route_eval : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":238,"children":[{"name":"Sinatra::Base#route! : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":238,"children":[{"name":"Sinatra::Base.compile! : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":238,"children":[{"name":"Method#call : <cfunc>","value":238,"children":[{"name":"block in <main> : /Users/yokazaki/src/github.com/fusic/estackprof/example/app.rb","value":238,"children":[{"name":"Object#bubble_sort : /Users/yokazaki/src/github.com/fusic/estackprof/example/app.rb","value":237,"children":[{"name":"Range#each : <cfunc>","value":237,"children":[{"name":"Object#bubble_sort : /Users/yokazaki/src/github.com/fusic/estackprof/example/app.rb","value":237,"children":[{"name":"Range#each : <cfunc>","value":237,"children":[{"name":"Object#bubble_sort : /Users/yokazaki/src/github.com/fusic/estackprof/example/app.rb","value":237,"children":[]}]}]}]}]},{"name":"Array#inspect : <cfunc>","value":1,"children":[{"name":"Integer#to_s : <cfunc>","value":1,"children":[]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]},{"name":"Sinatra::Helpers#body : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":2,"children":[{"name":"Kernel#require : <cfunc>","value":1,"children":[{"name":"<top (required)> : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/file.rb","value":1,"children":[{"name":"Kernel#require_relative : <cfunc>","value":1,"children":[]}]}]},{"name":"Kernel#block_given? : <cfunc>","value":1,"children":[]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]},{"name":"<main> : example/app.rb","value":1,"children":[{"name":"block in <module:Sinatra> : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/main.rb","value":1,"children":[{"name":"Sinatra::Base.run! : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":1,"children":[{"name":"Sinatra::Base.start_server : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/sinatra-2.1.0/lib/sinatra/base.rb","value":1,"children":[{"name":"Rack::Handler::WEBrick.run : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/handler/webrick.rb","value":1,"children":[{"name":"WEBrick::GenericServer#start : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/webrick-1.7.0/lib/webrick/server.rb","value":1,"children":[{"name":"WEBrick::SimpleServer.start : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/webrick-1.7.0/lib/webrick/server.rb","value":1,"children":[{"name":"WEBrick::GenericServer#start : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/webrick-1.7.0/lib/webrick/server.rb","value":1,"children":[{"name":"Array#each : <cfunc>","value":1,"children":[{"name":"WEBrick::GenericServer#start : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/webrick-1.7.0/lib/webrick/server.rb","value":1,"children":[{"name":"WEBrick::GenericServer#start_thread : /Users/yokazaki/src/github.com/fusic/estackprof/vendor/bundle/ruby/3.0.0/gems/webrick-1.7.0/lib/webrick/server.rb","value":1,"children":[{"name":"Thread.start : <cfunc>","value":1,"children":[]}]}]}]}]}]}]}]}]}]}]}]}]})
127
+ .call(flameGraph);
128
+
129
+ document.getElementById("form").addEventListener("submit", function(event){
130
+ event.preventDefault();
131
+ search();
132
+ });
133
+
134
+ function search() {
135
+ var term = document.getElementById("term").value;
136
+ flameGraph.search(term);
137
+ }
138
+
139
+ function clear() {
140
+ document.getElementById('term').value = '';
141
+ flameGraph.clear();
142
+ }
143
+
144
+ function resetZoom() {
145
+ flameGraph.resetZoom();
146
+ }
147
+
148
+ function onClick(d) {
149
+ console.info("Clicked on " + d.data.name);
150
+ }
151
+ </script>
152
+ </body>
153
+ </html>
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+ require 'pry'
5
+ require 'estackprof'
6
+
7
+ module Estackprof
8
+ class CLI < Thor
9
+ class_option :debug, type: :boolean, aliases: '-d', desc: 'debug mode'
10
+
11
+ desc 'top [OPTIONS] [FILE...]', 'Report to top of methods'
12
+ option :limit, aliases: '-l', desc: 'Limit reports.', type: :numeric
13
+ option :pattern, aliases: '-p', desc: 'Filter reports by pattern match.'
14
+ option :cumlative, aliases: '-c', desc: 'Sort by cumulative count.', type: :boolean
15
+ def top(*files)
16
+ puts Estackprof.top(
17
+ files: files.empty? ? Dir.glob('./tmp/*.dump') : files,
18
+ options: options
19
+ )
20
+ exit
21
+ rescue StandardError => e
22
+ output_error_if_debug_mode(e)
23
+ exit(-1)
24
+ end
25
+
26
+ desc 'list [OPTIONS] [FILE...]', 'Display sample counts of each line'
27
+ option :file, aliases: '-f', desc: 'Filter by file name.'
28
+ option :method, aliases: '-m', desc: 'Filter by method name'
29
+ def list(*files)
30
+ puts Estackprof.list(
31
+ files: files.empty? ? Dir.glob('./tmp/*.dump') : files,
32
+ options: options
33
+ )
34
+ exit
35
+ rescue StandardError => e
36
+ output_error_if_debug_mode(e)
37
+ exit(-1)
38
+ end
39
+
40
+ desc 'flamegraph [OPTIONS] [FILE]', 'Generate and open flamegraph'
41
+ def flamegraph(*files)
42
+ puts Estackprof.flamegraph(
43
+ files: files.empty? ? Dir.glob('./tmp/*.dump') : files
44
+ )
45
+ exit
46
+ rescue StandardError => e
47
+ output_error_if_debug_mode(e)
48
+ exit(-1)
49
+ end
50
+
51
+ map %w[--version -v] => :version
52
+ desc 'version', 'version'
53
+ def version
54
+ puts Estackprof::VERSION
55
+ end
56
+
57
+ private
58
+
59
+ def output_error_if_debug_mode(exception)
60
+ return unless options[:debug]
61
+
62
+ warn(exception.message)
63
+ warn(exception.backtrace)
64
+ end
65
+
66
+ class << self
67
+ private
68
+
69
+ def exit_on_failure?
70
+ true
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stackprof'
4
+ require 'launchy'
5
+
6
+ module Estackprof
7
+ def flamegraph(files:)
8
+ mkdir(tmp_path = './tmp')
9
+ html_path = File.expand_path("#{tmp_path}/flamegraph.html")
10
+ File.open(html_path, 'w') { |f| Report.create([files[0]]).print_d3_flamegraph(f) }
11
+ Launchy.open(html_path)
12
+ html_path
13
+ rescue StandardError
14
+ puts 'Dump files are missing or incorrect.'
15
+ end
16
+
17
+ private
18
+
19
+ def mkdir(path)
20
+ FileUtils.mkdir_p(path) unless FileTest.exist?(path)
21
+ end
22
+
23
+ module_function :flamegraph, :mkdir
24
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stackprof'
4
+
5
+ module Estackprof
6
+ def list(files:, options:)
7
+ io = StringIO.new
8
+
9
+ print_by_options(Report.create(files), options, io)
10
+
11
+ io.rewind
12
+ io.read.to_s
13
+ rescue StandardError
14
+ puts 'Dump files are missing or incorrect.'
15
+ end
16
+
17
+ private
18
+
19
+ def print_by_options(report, options, io)
20
+ if options[:method]
21
+ report.print_method(options[:method], io)
22
+ elsif options[:file]
23
+ report.print_file(options[:file], io)
24
+ else
25
+ report.print_files(false, nil, io)
26
+ end
27
+ end
28
+
29
+ module_function :list, :print_by_options
30
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stackprof'
4
+
5
+ module Estackprof
6
+ class Middleware < StackProf::Middleware
7
+ def initialize(app, options = {})
8
+ options[:enabled] = true if options[:enabled].nil?
9
+ options[:raw] = true if options[:raw].nil?
10
+ options[:save_every] ||= 10
11
+
12
+ super(app, **options)
13
+ end
14
+
15
+ class << self
16
+ %i[enabled mode interval raw path metadata].each do |sym|
17
+ define_method(sym) do
18
+ StackProf::Middleware.send(sym)
19
+ end
20
+ define_method("#{sym}=") do |value|
21
+ StackProf::Middleware.send("#{sym}=", value)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stackprof'
4
+
5
+ module Estackprof
6
+ class Report < StackProf::Report
7
+ def self.create(files)
8
+ reports = files.map do |file|
9
+ new(Marshal.load(IO.binread(file)))
10
+ end
11
+ reports.inject(:+)
12
+ end
13
+
14
+ def print_text(limit:, pattern:, sort_by_total: false, out: $stdout)
15
+ print_summary(out)
16
+ print_header(out)
17
+
18
+ list = frames(sort_by_total)
19
+ if pattern
20
+ list = list.filter do |_frame, info|
21
+ %i[file name].any? { |s| info[s].match?(pattern) }
22
+ end
23
+ end
24
+ list = list.first(limit) if limit
25
+ print_body(list, out)
26
+ end
27
+
28
+ private
29
+
30
+ def print_summary(out)
31
+ out.puts '=================================='
32
+ out.printf " Mode: #{modeline}\n"
33
+ print_summary_samples(out)
34
+ print_summary_gc(out)
35
+ out.puts '=================================='
36
+ end
37
+
38
+ def print_summary_samples(out)
39
+ out.printf " Samples: #{@data[:samples]} (%.2f%% miss rate)\n",
40
+ 100.0 * @data[:missed_samples] / (@data[:missed_samples] + @data[:samples])
41
+ end
42
+
43
+ def print_summary_gc(out)
44
+ out.printf " GC: #{@data[:gc_samples]} (%.2f%%)\n", 100.0 * @data[:gc_samples] / @data[:samples]
45
+ end
46
+
47
+ def print_header(out)
48
+ out.printf format("%<total>10s (pct) %<samples>10s (pct) FRAME\n", total: 'TOTAL', samples: 'SAMPLES')
49
+ end
50
+
51
+ def print_body(list, out)
52
+ list.each do |_frame, info|
53
+ call, total = info.values_at(:samples, :total_samples)
54
+ out.printf(
55
+ "%<total>10d %<total_pct>8s %<samples>10d %<samples_pct>8s %<frame>s\n",
56
+ total: total, total_pct: format('(%2.1f%%)', (total * 100.0 / overall_samples)),
57
+ samples: call, samples_pct: format('(%2.1f%%)', (call * 100.0 / overall_samples)),
58
+ frame: frame(info[:name], info[:file], info[:line])
59
+ )
60
+ end
61
+ end
62
+
63
+ def frame(name, file, line)
64
+ file_path = name
65
+ begin
66
+ file_path += "(#{Pathname(file).relative_path_from(Dir.pwd)}:#{line})"
67
+ rescue StandardError
68
+ # NOP
69
+ end
70
+ file_path
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stackprof'
4
+
5
+ module Estackprof
6
+ def top(files:, options:)
7
+ io = StringIO.new
8
+ Report.create(files).print_text(**parse_options(options), out: io)
9
+ io.rewind
10
+ io.read.to_s
11
+ rescue StandardError
12
+ puts 'Dump files are missing or incorrect.'
13
+ end
14
+
15
+ private
16
+
17
+ def parse_options(options)
18
+ limit = options[:limit] || 10
19
+ pattern = options[:pattern] && Regexp.new(options[:pattern])
20
+ sort_by_total = options[:cumlative]
21
+ { limit: limit, pattern: pattern, sort_by_total: sort_by_total }
22
+ end
23
+
24
+ module_function :top, :parse_options
25
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Estackprof
4
+ VERSION = '0.1.0'
5
+ end
data/lib/estackprof.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'estackprof/cli'
4
+ require_relative 'estackprof/top'
5
+ require_relative 'estackprof/list'
6
+ require_relative 'estackprof/flamegraph'
7
+ require_relative 'estackprof/version'
8
+ require_relative 'estackprof/report'
9
+ require_relative 'estackprof/middleware'
10
+
11
+ module Estackprof
12
+ class Error < StandardError; end
13
+ # Your code goes here...
14
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: estackprof
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yuhei Okazaki
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-09-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: launchy
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: stackprof
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.1.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.1.0
55
+ description: Estackprof is a wrapper to make it easier to use Stackprof in your rack
56
+ application
57
+ email:
58
+ - okazaki@fusic.co.jp
59
+ executables:
60
+ - estackprof
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".circleci/config.yml"
65
+ - ".gitignore"
66
+ - ".rspec"
67
+ - ".rubocop.yml"
68
+ - ".ruby-version"
69
+ - Gemfile
70
+ - Guardfile
71
+ - README.md
72
+ - Rakefile
73
+ - bin/console
74
+ - bin/setup
75
+ - estackprof.gemspec
76
+ - example/app.rb
77
+ - exe/estackprof
78
+ - flamegraph.html
79
+ - lib/estackprof.rb
80
+ - lib/estackprof/cli.rb
81
+ - lib/estackprof/flamegraph.rb
82
+ - lib/estackprof/list.rb
83
+ - lib/estackprof/middleware.rb
84
+ - lib/estackprof/report.rb
85
+ - lib/estackprof/top.rb
86
+ - lib/estackprof/version.rb
87
+ homepage: https://github.com/fusic/estackprof
88
+ licenses: []
89
+ metadata:
90
+ homepage_uri: https://github.com/fusic/estackprof
91
+ source_code_uri: https://github.com/fusic/estackprof
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: 2.5.0
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubygems_version: 3.2.15
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: We want to make stackprof easier to use.
111
+ test_files: []