singed 0.1.0 → 0.2.0

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: 5276eb42b69baf43de15c7ad5a3549f048d00865f225998eb4479b6e7723f2b8
4
- data.tar.gz: 0f46bfb076f12a4766e060a017f2cb0e9ad6af98a96b8f96ebf05b58205aac21
3
+ metadata.gz: b94bd39d1904f3d339e34974c5ad88bec590c2373ffedc6ffba15a4bce0fbb06
4
+ data.tar.gz: dcf1af83d2b05c7cc2d20566045a99781385408c6316ef183fa3cba77ec8538f
5
5
  SHA512:
6
- metadata.gz: 81b34cf130fbe187680d65ba593ee68ce606e72cbb5db99284aba82beb6bf9ac17cef5104850a49ebba7bce87cc8a11b39f4b0897accb9bc6e0cb268ee35168e
7
- data.tar.gz: 23e265212f1f70ebe105c218dde2025728782ae1574ef2abcf4705b04e13eeab7bd9b6197442b950517492c5a540a2ebaa99478d7841b4170a8611ff668094a9
6
+ metadata.gz: c3155afc39b9bc923718a488ebc170f3c0b03ecdf8eb945af93a85ad489a3825dd464a10dc8289795b30b91e6ff0ce43a488f348a5e2cacd1308417626228edd
7
+ data.tar.gz: f0f054893e83aceb2f3342317f30e732e75c79af1fa61164e7b637fea5ae18b03b8aee5eac68b4d05facc4a37e62a5e1ef833d1aa86df55046de7e8686fc2aac
data/README.md CHANGED
@@ -1,10 +1,20 @@
1
1
  # Singed
2
2
 
3
- Singed makes it easy to get a flamegraph anywhere in your code base:
3
+ Singed makes it easy to get a flamegraph anywhere in your code base. It wraps profiling your code with [stackprof](https://github.com/tmm1/stackprof) or [rbspy](https://github.com/rbspy/rbspy), and then launching [speedscope](https://github.com/jlfwong/speedscope) to view it.
4
+
5
+ ## Installation
6
+
7
+ Add to `Gemfile`:
8
+
9
+ ```ruby
10
+ gem "singed"
11
+ ```
12
+
13
+ Then run `bundle install`
4
14
 
5
15
  ## Usage
6
16
 
7
- To profile your code, and launch [speedscope](https://github.com/jlfwong/speedscope) for viewing it:
17
+ Simplest is calling with a block:
8
18
 
9
19
  ```ruby
10
20
  flamegraph {
@@ -22,7 +32,7 @@ Singed.output_directory = "tmp/slowness-exploration"
22
32
  If you are calling it in a loop, or with different variations, you can include a label on the filename:
23
33
 
24
34
  ```ruby
25
- flamegraph(label: "rspec") {
35
+ flamegraph("rspec") {
26
36
  # your code here
27
37
  }
28
38
  ```
@@ -64,7 +74,7 @@ class EmployeesController < ApplicationController
64
74
  end
65
75
  ```
66
76
 
67
- This won't catch the entire request though, just once it's been routed to controller and a response has been served.
77
+ This won't catch the entire request though, just once it's been routed to controller and a response has been served (ie no middleware).
68
78
 
69
79
  ### Rack/Rails requests
70
80
 
@@ -84,7 +94,7 @@ There is a `singed` command line you can use that will record a flamegraph from
84
94
 
85
95
  ```shell
86
96
  $ bundle binstub singed # if you want to be able to call it like bin/singed
87
- $ bundle exec singed -- bin/rails
97
+ $ bundle exec singed -- bin/rails runner 'Model.all.to_a'
88
98
  ```
89
99
 
90
100
  The flamegraph is opened afterwards.
@@ -98,5 +108,5 @@ The `open` is expected to be available.
98
108
 
99
109
  ## Alternatives
100
110
 
101
- - using [rbspy](https://rbspy.github.io/) directory
111
+ - using [rbspy](https://rbspy.github.io/) directly
102
112
  - using [stackprof](https://github.com/tmm1/stackprof) (a dependency of singed) directly
data/lib/singed/cli.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'shellwords'
2
2
  require 'tmpdir'
3
3
  require 'optionparser'
4
+ require 'pathname'
4
5
 
5
6
  # NOTE: we defer requiring singed until we run. that lets Rails load it if its in the gemfile, so the railtie has had a chance to run
6
7
 
@@ -64,6 +65,7 @@ module Singed
64
65
 
65
66
  Singed.output_directory = @output_directory if @output_directory
66
67
  Singed.output_directory ||= Dir.tmpdir
68
+ FileUtils.mkdir_p Singed.output_directory
67
69
  @filename = Singed::Flamegraph.generate_filename(label: 'cli')
68
70
 
69
71
  options = {
@@ -86,11 +88,19 @@ module Singed
86
88
  prompt_password
87
89
  end
88
90
 
89
- Bundler.with_unbundled_env do
91
+ rbspy = lambda do
90
92
  # don't run things with spring, because it forks and rbspy won't see it
91
93
  sudo ['rbspy', *rbspy_args], reason: 'Singed needs to run as root, but will drop permissions back to your user.', env: { 'DISABLE_SPRING' => '1' }
92
94
  end
93
95
 
96
+ if defined?(Bundler)
97
+ Bundler.with_unbundled_env do
98
+ rbspy.call
99
+ end
100
+ else
101
+ rbspy.call
102
+ end
103
+
94
104
  unless filename.exist?
95
105
  puts "#{filename} doesn't exist. Maybe rbspy had a failure capturing it? Check the scrollback."
96
106
  exit 1
@@ -102,9 +112,9 @@ module Singed
102
112
  end
103
113
 
104
114
  # clean the report, similar to how Singed::Report does
105
- json = JSON.parse(filename.read).with_indifferent_access
115
+ json = JSON.parse(filename.read)
106
116
  json['shared']['frames'].each do |frame|
107
- frame[:file] = Singed.filter_line(frame[:file])
117
+ frame['file'] = Singed.filter_line(frame['file'])
108
118
  end
109
119
  filename.write(JSON.dump(json))
110
120
 
@@ -53,7 +53,7 @@ module Singed
53
53
  end
54
54
 
55
55
  def self.generate_filename(label: nil, time: Time.now) # rubocop:disable Rails/TimeZone
56
- formatted_time = time.to_formatted_s(:number)
56
+ formatted_time = time.strftime('%Y%m%d%H%M%S')
57
57
  basename_parts = ['speedscope', label, formatted_time].compact
58
58
 
59
59
  file = Singed.output_directory.join("#{basename_parts.join('-')}.json")
@@ -1,15 +1,15 @@
1
1
  module Kernel
2
- def flamegraph(label = nil, open: true, ignore_gc: false, interval: 1000, &block)
2
+ def flamegraph(label = nil, open: true, ignore_gc: false, interval: 1000, io: $stdout, &block)
3
3
  fg = Singed::Flamegraph.new(label: label, ignore_gc: ignore_gc, interval: interval)
4
4
  result = fg.record(&block)
5
5
  fg.save
6
6
 
7
7
  if open
8
8
  # use npx, so we don't have to add it as a dependency
9
- puts "🔥📈 #{'Captured flamegraph, opening with'.colorize(:bold).colorize(:red)}: #{fg.open_command}"
9
+ io.puts "🔥📈 #{'Captured flamegraph, opening with'.colorize(:bold).colorize(:red)}: #{fg.open_command}"
10
10
  fg.open
11
11
  else
12
- puts "🔥📈 #{'Captured flamegraph to file'.colorize(:bold).colorize(:red)}: #{fg.filename}"
12
+ io.puts "🔥📈 #{'Captured flamegraph to file'.colorize(:bold).colorize(:red)}: #{fg.filename}"
13
13
  end
14
14
 
15
15
  result
data/lib/singed.rb CHANGED
@@ -13,7 +13,7 @@ module Singed
13
13
  end
14
14
 
15
15
  def self.output_directory
16
- @output_directory || raise("output directory hasn't been set!")
16
+ @output_directory
17
17
  end
18
18
 
19
19
  def enabled=(enabled)
data/singed.gemspec CHANGED
@@ -3,7 +3,7 @@
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = 'singed'
5
5
 
6
- spec.version = '0.1.0'
6
+ spec.version = '0.2.0'
7
7
  spec.authors = ['Josh Nichols']
8
8
  spec.email = ['josh.nichols@gusto.com']
9
9
 
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.add_dependency 'stackprof'
23
23
 
24
24
  spec.add_development_dependency 'rake', '~> 13.0'
25
+ spec.add_development_dependency 'rspec'
25
26
 
26
27
  # For more information and examples about making a new gem, checkout our
27
28
  # guide at: https://bundler.io/guides/creating_gem.html
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: singed
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Nichols
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-20 00:00:00.000000000 Z
11
+ date: 2023-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  description:
56
70
  email:
57
71
  - josh.nichols@gusto.com