singed 0.2.0 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/exe/singed +2 -3
- data/lib/singed/cli.rb +28 -28
- data/lib/singed/flamegraph.rb +6 -6
- data/lib/singed/kernel_ext.rb +4 -4
- data/lib/singed/rack_middleware.rb +4 -4
- data/lib/singed/railtie.rb +4 -4
- data/lib/singed/rspec.rb +3 -7
- data/lib/singed.rb +9 -9
- data/singed.gemspec +25 -25
- metadata +16 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 130f5fb69096f1a09875822dca8c86ca3a05876ca2b742cab274fb863ed7c253
|
4
|
+
data.tar.gz: 342357ad154850a5e5be841820cefe69609825650c8f0401c58a182f1ffd992c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 673f34810201f6050862488b728272ff769e5072b6a5fba90a4669b811efdbaa6fc9a8fd56c4f2cccebca7445c00bdc28700ec22881076dfcaef7bdf4bd5be2a
|
7
|
+
data.tar.gz: a839d09f7b78c96c4b1e38aa101fb755fef1c72ac3cee5068c0aef1df9d199ce3a73cfee3c40f6a2f934b6c14abb90765b3475e2a9ffc1d23e5a29f8eba01aab
|
data/README.md
CHANGED
data/exe/singed
CHANGED
data/lib/singed/cli.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require "shellwords"
|
2
|
+
require "tmpdir"
|
3
|
+
require "optionparser"
|
4
|
+
require "pathname"
|
5
5
|
|
6
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
|
7
7
|
|
@@ -17,24 +17,24 @@ module Singed
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def parse_argv!
|
20
|
-
opts.banner =
|
20
|
+
opts.banner = "Usage: singed [options] <command>"
|
21
21
|
|
22
|
-
opts.on(
|
22
|
+
opts.on("-h", "--help", "Show this message") do
|
23
23
|
@show_help = true
|
24
24
|
end
|
25
25
|
|
26
|
-
opts.on(
|
26
|
+
opts.on("-o", "--output-directory DIRECTORY", "Directory to write flamegraph to") do |directory|
|
27
27
|
@output_directory = directory
|
28
28
|
end
|
29
29
|
|
30
30
|
opts.order(@argv) do |arg|
|
31
|
-
opts.terminate if arg ==
|
31
|
+
opts.terminate if arg == "--"
|
32
32
|
break
|
33
33
|
end
|
34
34
|
|
35
35
|
if @argv.empty?
|
36
36
|
@show_help = true
|
37
|
-
@error_message =
|
37
|
+
@error_message = "missing command to profile"
|
38
38
|
return
|
39
39
|
end
|
40
40
|
|
@@ -49,7 +49,7 @@ module Singed
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def run
|
52
|
-
require
|
52
|
+
require "singed"
|
53
53
|
|
54
54
|
if @error_message
|
55
55
|
puts @error_message
|
@@ -66,31 +66,31 @@ module Singed
|
|
66
66
|
Singed.output_directory = @output_directory if @output_directory
|
67
67
|
Singed.output_directory ||= Dir.tmpdir
|
68
68
|
FileUtils.mkdir_p Singed.output_directory
|
69
|
-
@filename = Singed::Flamegraph.generate_filename(label:
|
69
|
+
@filename = Singed::Flamegraph.generate_filename(label: "cli")
|
70
70
|
|
71
71
|
options = {
|
72
|
-
format:
|
72
|
+
format: "speedscope",
|
73
73
|
file: filename.to_s,
|
74
|
-
silent: nil
|
74
|
+
silent: nil
|
75
75
|
}
|
76
76
|
|
77
77
|
rbspy_args = [
|
78
|
-
|
78
|
+
"record",
|
79
79
|
*options.map { |k, v| ["--#{k}", v].compact }.flatten,
|
80
|
-
|
81
|
-
*argv
|
80
|
+
"--",
|
81
|
+
*argv
|
82
82
|
]
|
83
83
|
|
84
84
|
loop do
|
85
85
|
break unless password_needed?
|
86
86
|
|
87
|
-
puts
|
87
|
+
puts "🔥📈 Singed needs to run as root, but will drop permissions back to your user. Prompting with sudo now..."
|
88
88
|
prompt_password
|
89
89
|
end
|
90
90
|
|
91
91
|
rbspy = lambda do
|
92
92
|
# don't run things with spring, because it forks and rbspy won't see it
|
93
|
-
sudo [
|
93
|
+
sudo ["rbspy", *rbspy_args], reason: "Singed needs to run as root, but will drop permissions back to your user.", env: {"DISABLE_SPRING" => "1"}
|
94
94
|
end
|
95
95
|
|
96
96
|
if defined?(Bundler)
|
@@ -113,8 +113,8 @@ module Singed
|
|
113
113
|
|
114
114
|
# clean the report, similar to how Singed::Report does
|
115
115
|
json = JSON.parse(filename.read)
|
116
|
-
json[
|
117
|
-
frame[
|
116
|
+
json["shared"]["frames"].each do |frame|
|
117
|
+
frame["file"] = Singed.filter_line(frame["file"])
|
118
118
|
end
|
119
119
|
filename.write(JSON.dump(json))
|
120
120
|
|
@@ -123,15 +123,15 @@ module Singed
|
|
123
123
|
end
|
124
124
|
|
125
125
|
def password_needed?
|
126
|
-
!system(
|
126
|
+
!system("sudo --non-interactive true >/dev/null 2>&1")
|
127
127
|
end
|
128
128
|
|
129
129
|
def prompt_password
|
130
|
-
system(
|
130
|
+
system("sudo true")
|
131
131
|
end
|
132
132
|
|
133
133
|
def adjust_ownership!
|
134
|
-
sudo [
|
134
|
+
sudo ["chown", ENV["USER"], filename], reason: "Adjusting ownership of #{filename}, but need root."
|
135
135
|
end
|
136
136
|
|
137
137
|
def show_help?
|
@@ -147,9 +147,9 @@ module Singed
|
|
147
147
|
end
|
148
148
|
|
149
149
|
sudo_args = [
|
150
|
-
|
151
|
-
|
152
|
-
*system_args.map(&:to_s)
|
150
|
+
"sudo",
|
151
|
+
"--preserve-env",
|
152
|
+
*system_args.map(&:to_s)
|
153
153
|
]
|
154
154
|
|
155
155
|
puts "$ #{Shellwords.join(sudo_args)}"
|
@@ -161,7 +161,7 @@ module Singed
|
|
161
161
|
original_cwd = Dir.pwd
|
162
162
|
|
163
163
|
loop do
|
164
|
-
if File.file?(
|
164
|
+
if File.file?("config/environment.rb")
|
165
165
|
return Dir.pwd
|
166
166
|
end
|
167
167
|
|
@@ -171,7 +171,7 @@ module Singed
|
|
171
171
|
end
|
172
172
|
|
173
173
|
# Otherwise keep moving upwards in search of an executable.
|
174
|
-
Dir.chdir(
|
174
|
+
Dir.chdir("..")
|
175
175
|
end
|
176
176
|
end
|
177
177
|
end
|
data/lib/singed/flamegraph.rb
CHANGED
@@ -6,11 +6,11 @@ module Singed
|
|
6
6
|
# it's been created elsewhere, ie rbspy
|
7
7
|
if filename
|
8
8
|
if ignore_gc
|
9
|
-
raise ArgumentError,
|
9
|
+
raise ArgumentError, "ignore_gc not supported when given an existing file"
|
10
10
|
end
|
11
11
|
|
12
12
|
if label
|
13
|
-
raise ArgumentError,
|
13
|
+
raise ArgumentError, "label not supported when given an existing file"
|
14
14
|
end
|
15
15
|
|
16
16
|
@filename = filename
|
@@ -41,7 +41,7 @@ module Singed
|
|
41
41
|
report = Singed::Report.new(@profile)
|
42
42
|
report.filter!
|
43
43
|
filename.dirname.mkpath
|
44
|
-
filename.open(
|
44
|
+
filename.open("w") { |f| report.print_json(f) }
|
45
45
|
end
|
46
46
|
|
47
47
|
def open
|
@@ -53,10 +53,10 @@ 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.strftime(
|
57
|
-
basename_parts = [
|
56
|
+
formatted_time = time.strftime("%Y%m%d%H%M%S-%6N")
|
57
|
+
basename_parts = ["speedscope", label, formatted_time].compact
|
58
58
|
|
59
|
-
file = Singed.output_directory.join("#{basename_parts.join(
|
59
|
+
file = Singed.output_directory.join("#{basename_parts.join("-")}.json")
|
60
60
|
# convert to relative directory if it's an absolute path and within the current
|
61
61
|
pwd = Pathname.pwd
|
62
62
|
file = file.relative_path_from(pwd) if file.absolute? && file.to_s.start_with?(pwd.to_s)
|
data/lib/singed/kernel_ext.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
module Kernel
|
2
|
-
def flamegraph(label = nil, open: true, ignore_gc: false, interval: 1000, io: $stdout, &
|
2
|
+
def flamegraph(label = nil, open: true, ignore_gc: false, interval: 1000, io: $stdout, &)
|
3
3
|
fg = Singed::Flamegraph.new(label: label, ignore_gc: ignore_gc, interval: interval)
|
4
|
-
result = fg.record(&
|
4
|
+
result = fg.record(&)
|
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
|
-
io.puts "🔥📈 #{
|
9
|
+
io.puts "🔥📈 #{"Captured flamegraph, opening with".colorize(:bold).colorize(:red)}: #{fg.open_command}"
|
10
10
|
fg.open
|
11
11
|
else
|
12
|
-
io.puts "🔥📈 #{
|
12
|
+
io.puts "🔥📈 #{"Captured flamegraph to file".colorize(:bold).colorize(:red)}: #{fg.filename}"
|
13
13
|
end
|
14
14
|
|
15
15
|
result
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Rack Middleware
|
2
2
|
|
3
|
-
require
|
3
|
+
require "rack"
|
4
4
|
|
5
5
|
module Singed
|
6
6
|
class RackMiddleware
|
@@ -21,15 +21,15 @@ module Singed
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def capture_flamegraph?(env)
|
24
|
-
self.class.always_capture? || env[
|
24
|
+
self.class.always_capture? || env["HTTP_X_SINGED"] == "true"
|
25
25
|
end
|
26
26
|
|
27
|
-
TRUTHY_STRINGS = [
|
27
|
+
TRUTHY_STRINGS = ["true", "1", "yes"].freeze
|
28
28
|
|
29
29
|
def self.always_capture?
|
30
30
|
return @always_capture if defined?(@always_capture)
|
31
31
|
|
32
|
-
@always_capture = TRUTHY_STRINGS.include?(ENV.fetch(
|
32
|
+
@always_capture = TRUTHY_STRINGS.include?(ENV.fetch("SINGED_MIDDLEWARE_ALWAYS_CAPTURE", "false"))
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
data/lib/singed/railtie.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "singed/backtrace_cleaner_ext"
|
2
|
+
require "singed/controller_ext"
|
3
3
|
|
4
4
|
module Singed
|
5
5
|
class Railtie < Rails::Railtie
|
6
|
-
initializer
|
6
|
+
initializer "singed.configure_rails_initialization" do |app|
|
7
7
|
self.class.init!
|
8
8
|
|
9
9
|
app.middleware.use Singed::RackMiddleware
|
@@ -14,7 +14,7 @@ module Singed
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.init!
|
17
|
-
Singed.output_directory = Rails.root.join(
|
17
|
+
Singed.output_directory = Rails.root.join("tmp/speedscope")
|
18
18
|
Singed.backtrace_cleaner = Rails.backtrace_cleaner
|
19
19
|
end
|
20
20
|
end
|
data/lib/singed/rspec.rb
CHANGED
@@ -1,11 +1,7 @@
|
|
1
|
-
require
|
1
|
+
require "singed"
|
2
2
|
|
3
3
|
RSpec.configure do |config|
|
4
|
-
config.around do |example|
|
5
|
-
|
6
|
-
flamegraph { example.run }
|
7
|
-
else
|
8
|
-
example.run
|
9
|
-
end
|
4
|
+
config.around(flamegraph: true) do |example|
|
5
|
+
flamegraph { example.run }
|
10
6
|
end
|
11
7
|
end
|
data/lib/singed.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "json"
|
4
|
+
require "stackprof"
|
5
|
+
require "colorize"
|
6
6
|
|
7
7
|
module Singed
|
8
8
|
extend self
|
@@ -46,11 +46,11 @@ module Singed
|
|
46
46
|
line
|
47
47
|
end
|
48
48
|
|
49
|
-
autoload :Flamegraph,
|
50
|
-
autoload :Report,
|
51
|
-
autoload :RackMiddleware,
|
49
|
+
autoload :Flamegraph, "singed/flamegraph"
|
50
|
+
autoload :Report, "singed/report"
|
51
|
+
autoload :RackMiddleware, "singed/rack_middleware"
|
52
52
|
end
|
53
53
|
|
54
|
-
require
|
55
|
-
require
|
56
|
-
require
|
54
|
+
require "singed/kernel_ext"
|
55
|
+
require "singed/railtie" if defined?(Rails::Railtie)
|
56
|
+
require "singed/rspec" if defined?(RSpec) && RSpec.respond_to?(:configure)
|
data/singed.gemspec
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
|
-
spec.name
|
5
|
-
|
6
|
-
spec.version
|
7
|
-
spec.
|
8
|
-
spec.
|
9
|
-
|
10
|
-
spec.summary
|
11
|
-
spec.required_ruby_version =
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
spec.
|
22
|
-
spec.
|
23
|
-
|
24
|
-
spec.
|
25
|
-
spec.
|
26
|
-
|
27
|
-
|
28
|
-
|
4
|
+
spec.name = "singed"
|
5
|
+
|
6
|
+
spec.version = "0.2.2"
|
7
|
+
spec.license = "MIT"
|
8
|
+
spec.authors = ["Josh Nichols"]
|
9
|
+
spec.email = ["josh.nichols@gusto.com"]
|
10
|
+
spec.summary = "Quick and easy way to get flamegraphs from a specific part of your code base"
|
11
|
+
spec.required_ruby_version = ">= 2.7.0"
|
12
|
+
spec.homepage = "https://github.com/rubyatscale/singed"
|
13
|
+
spec.metadata = {
|
14
|
+
"source_code_uri" => "https://github.com/rubyatscale/singed.git",
|
15
|
+
"bug_tracker_uri" => "https://github.com/rubyatscale/singed/issues",
|
16
|
+
"homepage_uri" => "https://github.com/rubyatscale/singed"
|
17
|
+
}
|
18
|
+
|
19
|
+
spec.files = Dir["README.md", "*.gemspec", "lib/**/*", "exe/**/*"]
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "colorize"
|
25
|
+
spec.add_dependency "stackprof", ">= 0.2.13"
|
26
|
+
|
27
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
28
|
+
spec.add_development_dependency "rspec"
|
29
29
|
end
|
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.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Nichols
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-05-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 0.2.13
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 0.2.13
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,7 +66,7 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
description:
|
69
|
+
description:
|
70
70
|
email:
|
71
71
|
- josh.nichols@gusto.com
|
72
72
|
executables:
|
@@ -87,10 +87,14 @@ files:
|
|
87
87
|
- lib/singed/report.rb
|
88
88
|
- lib/singed/rspec.rb
|
89
89
|
- singed.gemspec
|
90
|
-
homepage:
|
91
|
-
licenses:
|
92
|
-
|
93
|
-
|
90
|
+
homepage: https://github.com/rubyatscale/singed
|
91
|
+
licenses:
|
92
|
+
- MIT
|
93
|
+
metadata:
|
94
|
+
source_code_uri: https://github.com/rubyatscale/singed.git
|
95
|
+
bug_tracker_uri: https://github.com/rubyatscale/singed/issues
|
96
|
+
homepage_uri: https://github.com/rubyatscale/singed
|
97
|
+
post_install_message:
|
94
98
|
rdoc_options: []
|
95
99
|
require_paths:
|
96
100
|
- lib
|
@@ -105,8 +109,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
109
|
- !ruby/object:Gem::Version
|
106
110
|
version: '0'
|
107
111
|
requirements: []
|
108
|
-
rubygems_version: 3.
|
109
|
-
signing_key:
|
112
|
+
rubygems_version: 3.5.9
|
113
|
+
signing_key:
|
110
114
|
specification_version: 4
|
111
115
|
summary: Quick and easy way to get flamegraphs from a specific part of your code base
|
112
116
|
test_files: []
|