pyroscope 0.3.1-x86_64-linux → 0.5.0-x86_64-linux

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5246487a997d5c324867807c3c3e39213d0fd3856eba3e83e5931862eed37a2
4
- data.tar.gz: 61a63fd184f87f3513b67328a6ea40f26393de5b1152e963c63d722693673e85
3
+ metadata.gz: b692aa36e6a159bec5b036ac9b55ec84270191f5bfaf187aa47a1d1f3a0de653
4
+ data.tar.gz: c7a365213abab197873d98a74bdf076cb93c5ef3c8471dc39e176c173f43082c
5
5
  SHA512:
6
- metadata.gz: 16ab8963dbcabd6a26cd7457bc50dcaab771ecfdd7f2ee7216b0eb0b24cde58aa2f80b071ded8e4f325a6bcb4541356264d9677a051087e5b73bc2d19b8e94c4
7
- data.tar.gz: cea28aa3b9454b7413444976369e1efff4ca666ac2f99e6a3a54c5346ce0eb6325a2eef3b79e17d8c364fced4971aa067a42d5c864b34efd5ef3c9a8a9d27a70
6
+ metadata.gz: 8b078b81690d727f8f3f602324e1020ea673b0979f56bf60e62e54d4dd718beea20b08074016f8dfdd7968dfdef168d6f2a5a3b8c18d21664d80ab1c09414b36
7
+ data.tar.gz: 4a94af4c840e8aed35d97caab5d3eecc1e2a596f5f787fb84373965f7c88ca5cd57c3be93bd6c0fd77473005b24e1626dd50bfdf9ceb3b42ebfcebece8aeba46
data/README.md CHANGED
@@ -1,57 +1,71 @@
1
- Pyroscope Ruby Integration --Beta--
2
- =====================================
1
+ # Pyroscope Ruby Gem
3
2
 
4
- **note**: This is a beta release. It requires local compilation, might be
5
- buggy and is frequently updated. For the initial implementation, find it [here](https://github.com/pyroscope-io/pyroscope-ruby). Please report any [issues](https://github.com/pyroscope-io/pyroscope-rs/issues).
3
+ **Pyroscope integration for Ruby**
6
4
 
7
- ## Installation
5
+ [![license](https://img.shields.io/badge/license-Apache2.0-blue.svg)](LICENSE)
6
+ ![tests](https://github.com/pyroscope-io/pyroscope-rs/workflows/Tests/badge.svg)
7
+ ![build](https://github.com/pyroscope-io/pyroscope-rs/workflows/Build/badge.svg)
8
+ [![Gem version](https://badge.fury.io/rb/pyroscope.svg)](https://badge.fury.io/rb/pyroscope)
8
9
 
9
- 1. You need the Rust toolchain to compile the library locally. To install
10
- Rust:
10
+ ---
11
11
 
12
- ```
13
- curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain stable -y
14
- export PATH=$PATH:/root/.cargo/bin
15
- ```
12
+ ### What is Pyroscope
13
+ [Pyroscope](https://github.com/pyroscope-io/pyroscope) is a tool that lets you continuously profile your applications to prevent and debug performance issues in your code. It consists of a low-overhead agent which sends data to the Pyroscope server which includes a custom-built storage engine. This allows for you to store and query any applications profiling data in an extremely efficient and cost effective way.
16
14
 
17
- 2. Building/Insalling from Rubygems
18
15
 
19
- ```
20
- gem install pyroscope_beta
21
- ```
16
+ ### Supported platforms
22
17
 
23
- 3. Building/Installing from source
18
+ | Linux | macOS | Windows | Docker |
19
+ |:-----:|:-----:|:-------:|:------:|
20
+ | ✅ | ✅ | | ✅ |
24
21
 
25
- Change directory to `pyroscope_ffi/ruby` and run
22
+ ### Profiling Ruby applications
26
23
 
24
+ Add the `pyroscope` gem to your Gemfile:
25
+
26
+ ```bash
27
+ bundle add pyroscope
27
28
  ```
28
- gem build pyroscope.gemspec
29
- gem install ./pyroscope.gemspec
30
- ```
31
29
 
32
- ## Configuration
30
+ ### Basic Configuration
31
+
32
+ Add the following code to your application. If you're using rails, put this into `config/initializers` directory. This code will initialize pyroscope profiler and start profiling:
33
33
 
34
- Configuration is similar to the old package except for `application_name`:
34
+ ```ruby
35
+ require 'pyroscope'
35
36
 
37
+ Pyroscope.configure do |config|
38
+ config.application_name = "my.ruby.app" # replace this with some name for your application
39
+ config.server_address = "http://my-pyroscope-server:4040" # replace this with the address of your pyroscope server
40
+ # config.auth_token = "{YOUR_API_KEY}" # optionally, if authentication is enabled, specify the API key
41
+ end
36
42
  ```
37
- require 'pyroscope_beta'
43
+
44
+ ### Tags
45
+
46
+ Pyroscope ruby integration provides a number of ways to tag profiling data. For example, you can provide tags when you're initializing the profiler:
47
+
48
+ ```ruby
49
+ require 'pyroscope'
38
50
 
39
51
  Pyroscope.configure do |config|
40
- config.application_name = "ruby.app"
41
- config.server_address = "http://localhost:4040"
42
- config.detect_subprocesses = true
52
+ config.application_name = "my.ruby.app"
53
+ config.server_address = "http://my-pyroscope-server:4040"
54
+
43
55
  config.tags = {
44
- :key => "value",
56
+ "hostname" => ENV["HOSTNAME"],
45
57
  }
46
58
  end
47
59
  ```
48
60
 
49
- ## Adding tags
61
+ or you can dynamically tag certain parts of your code:
50
62
 
51
- Tags passed to configure are global. To tag code locally, you can use:
52
-
53
- ```
54
- Pyroscope.tag_wrapper({"profile": "profile-1"}) do
55
- // Tagged profile
63
+ ```ruby
64
+ Pyroscope.tag_wrapper({ "controller": "slow_controller_i_want_to_profile" }) do
65
+ slow_code
56
66
  end
57
67
  ```
68
+
69
+ ### Example
70
+
71
+ Check out this [example ruby project in our repository](https://github.com/pyroscope-io/pyroscope/tree/main/examples/ruby) for examples of how you can use these features.
data/ext/rbspy/src/lib.rs CHANGED
@@ -1,17 +1,115 @@
1
- use ffikit::Signal;
2
- use pyroscope::backend::Tag;
3
- use pyroscope::PyroscopeAgent;
4
- use pyroscope_rbspy::{rbspy_backend, RbspyConfig};
5
1
  use std::collections::hash_map::DefaultHasher;
2
+ use std::env;
6
3
  use std::ffi::CStr;
7
4
  use std::hash::Hasher;
8
5
  use std::os::raw::c_char;
6
+ use std::str::FromStr;
7
+
8
+ use ffikit::Signal;
9
+ use pyroscope_rbspy::{rbspy_backend, RbspyConfig};
10
+
11
+ use pyroscope::{pyroscope::Compression, PyroscopeAgent};
12
+ use pyroscope::backend::{Report, StackFrame, Tag};
13
+ use pyroscope::pyroscope::ReportEncoding;
14
+
15
+ pub fn transform_report(report: Report) -> Report {
16
+ let cwd = env::current_dir().unwrap();
17
+ let cwd = cwd.to_str().unwrap_or("");
18
+
19
+ let data = report
20
+ .data
21
+ .iter()
22
+ .map(|(stacktrace, count)| {
23
+ let new_frames = stacktrace
24
+ .frames
25
+ .iter()
26
+ .map(|frame| {
27
+ let frame = frame.to_owned();
28
+ let mut s = frame.filename.unwrap();
29
+ match s.find(cwd) {
30
+ Some(i) => {
31
+ s = s[(i + cwd.len() + 1)..].to_string();
32
+ }
33
+ None => match s.find("/gems/") {
34
+ Some(i) => {
35
+ s = s[(i + 1)..].to_string();
36
+ }
37
+ None => match s.find("/ruby/") {
38
+ Some(i) => {
39
+ s = s[(i + 6)..].to_string();
40
+ match s.find("/") {
41
+ Some(i) => {
42
+ s = s[(i + 1)..].to_string();
43
+ }
44
+ None => {}
45
+ }
46
+ }
47
+ None => {}
48
+ },
49
+ },
50
+ }
51
+
52
+ // something
53
+ StackFrame::new(
54
+ frame.module,
55
+ frame.name,
56
+ Some(s.to_string()),
57
+ frame.relative_path,
58
+ frame.absolute_path,
59
+ frame.line,
60
+ )
61
+ })
62
+ .collect();
63
+
64
+ let mut mystack = stacktrace.to_owned();
65
+
66
+ mystack.frames = new_frames;
67
+
68
+ (mystack, count.to_owned())
69
+ })
70
+ .collect();
71
+
72
+ let new_report = Report::new(data).metadata(report.metadata.clone());
73
+
74
+ new_report
75
+ }
76
+
77
+ #[no_mangle]
78
+ pub extern "C" fn initialize_logging(logging_level: u32) -> bool {
79
+ // Force rustc to display the log messages in the console.
80
+ match logging_level {
81
+ 50 => {
82
+ std::env::set_var("RUST_LOG", "error");
83
+ }
84
+ 40 => {
85
+ std::env::set_var("RUST_LOG", "warn");
86
+ }
87
+ 30 => {
88
+ std::env::set_var("RUST_LOG", "info");
89
+ }
90
+ 20 => {
91
+ std::env::set_var("RUST_LOG", "debug");
92
+ }
93
+ 10 => {
94
+ std::env::set_var("RUST_LOG", "trace");
95
+ }
96
+ _ => {
97
+ std::env::set_var("RUST_LOG", "debug");
98
+ }
99
+ }
100
+
101
+ // Initialize the logger.
102
+ pretty_env_logger::init_timed();
103
+
104
+ true
105
+ }
9
106
 
10
107
  #[no_mangle]
11
108
  pub extern "C" fn initialize_agent(
12
109
  application_name: *const c_char, server_address: *const c_char, auth_token: *const c_char,
13
- sample_rate: u32, detect_subprocesses: bool, on_cpu: bool, report_pid: bool,
14
- report_thread_id: bool, tags: *const c_char,
110
+ sample_rate: u32, detect_subprocesses: bool, oncpu: bool, report_pid: bool,
111
+ report_thread_id: bool, tags: *const c_char, compression: *const c_char,
112
+ report_encoding: *const c_char
15
113
  ) -> bool {
16
114
  // Initialize FFIKit
17
115
  let recv = ffikit::initialize_ffi().unwrap();
@@ -36,13 +134,27 @@ pub extern "C" fn initialize_agent(
36
134
  .unwrap()
37
135
  .to_string();
38
136
 
137
+ let compression_string = unsafe { CStr::from_ptr(compression) }
138
+ .to_str()
139
+ .unwrap()
140
+ .to_string();
141
+
142
+ let report_encoding = unsafe { CStr::from_ptr(report_encoding) }
143
+ .to_str()
144
+ .unwrap()
145
+ .to_string();
146
+
147
+ let compression = Compression::from_str(&compression_string);
148
+ let report_encoding = ReportEncoding::from_str(&report_encoding)
149
+ .unwrap_or(ReportEncoding::FOLDED);
150
+
39
151
  let pid = std::process::id();
40
152
 
41
153
  let rbspy_config = RbspyConfig::new(pid.try_into().unwrap())
42
154
  .sample_rate(sample_rate)
43
155
  .lock_process(false)
44
- .with_subprocesses(detect_subprocesses)
45
- .on_cpu(on_cpu)
156
+ .detect_subprocesses(detect_subprocesses)
157
+ .oncpu(oncpu)
46
158
  .report_pid(report_pid)
47
159
  .report_thread_id(report_thread_id);
48
160
 
@@ -52,12 +164,18 @@ pub extern "C" fn initialize_agent(
52
164
 
53
165
  let mut agent_builder = PyroscopeAgent::builder(server_address, application_name)
54
166
  .backend(rbspy)
55
- .tags(tags);
167
+ .func(transform_report)
168
+ .tags(tags)
169
+ .report_encoding(report_encoding);
56
170
 
57
171
  if auth_token != "" {
58
172
  agent_builder = agent_builder.auth_token(auth_token);
59
173
  }
60
174
 
175
+ if let Ok(compression) = compression {
176
+ agent_builder = agent_builder.compression(compression);
177
+ }
178
+
61
179
  let agent = agent_builder.build().unwrap();
62
180
 
63
181
  let agent_running = agent.start().unwrap();
@@ -1,3 +1,3 @@
1
1
  module Pyroscope
2
- VERSION = '0.3.1'.freeze
2
+ VERSION = '0.5.0'.freeze
3
3
  end
data/lib/pyroscope.rb CHANGED
@@ -1,10 +1,14 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
1
4
  require 'ffi'
2
5
 
3
6
  module Pyroscope
4
7
  module Rust
5
8
  extend FFI::Library
6
9
  ffi_lib File.expand_path(File.dirname(__FILE__)) + "/rbspy/rbspy.#{RbConfig::CONFIG["DLEXT"]}"
7
- attach_function :initialize_agent, [:string, :string, :string, :int, :bool, :bool, :bool, :bool, :string], :bool
10
+ attach_function :initialize_logging, [:int], :bool
11
+ attach_function :initialize_agent, [:string, :string, :string, :int, :bool, :bool, :bool, :bool, :string, :string, :string], :bool
8
12
  attach_function :add_thread_tag, [:uint64, :string, :string], :bool
9
13
  attach_function :remove_thread_tag, [:uint64, :string, :string], :bool
10
14
  attach_function :add_global_tag, [:string, :string], :bool
@@ -18,19 +22,22 @@ module Pyroscope
18
22
  attach_function :thread_id, [], :uint64
19
23
  end
20
24
 
21
- Config = Struct.new(:application_name, :app_name, :server_address, :auth_token, :sample_rate, :detect_subprocesses, :on_cpu, :report_pid, :report_thread_id, :log_level, :tags) do
25
+ Config = Struct.new(:application_name, :app_name, :server_address, :auth_token, :log_level, :sample_rate, :detect_subprocesses, :oncpu, :report_pid, :report_thread_id, :tags, :compression, :report_encoding) do
22
26
  def initialize(*)
27
+ super
28
+ # defaults:
23
29
  self.application_name = ''
24
30
  self.server_address = 'http://localhost:4040'
25
31
  self.auth_token = ''
26
32
  self.sample_rate = 100
27
33
  self.detect_subprocesses = false
28
- self.on_cpu = true
34
+ self.oncpu = true
29
35
  self.report_pid = false
30
36
  self.report_thread_id = false
31
- self.log_level = 'info'
37
+ self.log_level = 'error'
32
38
  self.tags = {}
33
- super
39
+ self.compression = 'gzip'
40
+ self.report_encoding = 'pprof'
34
41
  end
35
42
  end
36
43
 
@@ -41,16 +48,40 @@ module Pyroscope
41
48
  # Pass config to the block
42
49
  yield @config
43
50
 
51
+ # Determine Logging level (kinda like an enum).
52
+ case @config.log_level
53
+ when 'trace'
54
+ @log_level = 10
55
+ when 'debug'
56
+ @log_level = 20
57
+ when 'info'
58
+ @log_level = 30
59
+ when 'warn'
60
+ @log_level = 40
61
+ when 'error'
62
+ @log_level = 50
63
+ else
64
+ @log_level = 50
65
+ end
66
+
67
+ # Initialize Logging
68
+ Rust.initialize_logging(@log_level)
69
+
70
+
71
+ # initialize Pyroscope Agent
44
72
  Rust.initialize_agent(
73
+ # these are defaults in case user-provided values are nil:
45
74
  @config.app_name || @config.application_name || "",
46
75
  @config.server_address || "",
47
76
  @config.auth_token || "",
48
77
  @config.sample_rate || 100,
49
78
  @config.detect_subprocesses || false,
50
- @config.on_cpu || false,
79
+ @config.oncpu || false,
51
80
  @config.report_pid || false,
52
81
  @config.report_thread_id || false,
53
- tags_to_string(@config.tags || {})
82
+ tags_to_string(@config.tags || {}),
83
+ @config.compression || "",
84
+ @config.report_encoding || "pprof"
54
85
  )
55
86
  end
56
87
 
@@ -96,7 +127,7 @@ module Pyroscope
96
127
  end
97
128
  end
98
129
 
99
- def drop
130
+ def shutdown
100
131
  Rust.drop_agent
101
132
  end
102
133
  end
data/lib/rbspy/rbspy.so CHANGED
Binary file
Binary file
data/pyroscope.gemspec CHANGED
@@ -1,4 +1,11 @@
1
- require_relative "lib/pyroscope/version"
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ begin
5
+ require File.expand_path(File.join(File.dirname(__FILE__), "lib/pyroscope/version"))
6
+ rescue LoadError
7
+ puts "WARNING: Could not load Pyroscope::VERSION"
8
+ end
2
9
 
3
10
  Gem::Specification.new do |s|
4
11
  s.name = 'pyroscope'
@@ -9,6 +16,13 @@ Gem::Specification.new do |s|
9
16
  s.email = ['contact@pyroscope.io']
10
17
  s.homepage = 'https://pyroscope.io'
11
18
  s.license = 'Apache-2.0'
19
+ s.metadata = {
20
+ "homepage_uri" => "https://pyroscope.io",
21
+ "bug_tracker_uri" => "https://github.com/pyroscope-io/pyroscope-rs/issues",
22
+ "documentation_uri" => "https://pyroscope.io/docs/ruby/",
23
+ "changelog_uri" => "https://github.com/pyroscope-io/pyroscope-rs/tree/main/pyroscope_ffi/ruby/CHANGELOG.md",
24
+ "source_code_uri" => "https://github.com/pyroscope-io/pyroscope-rs/tree/main/pyroscope_ffi/ruby",
25
+ }
12
26
 
13
27
  # Specify which files should be added to the gem when it is released.
14
28
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -21,7 +35,7 @@ Gem::Specification.new do |s|
21
35
 
22
36
  s.platform = Gem::Platform::RUBY
23
37
 
24
- s.required_ruby_version = ">= 2.5.9"
38
+ s.required_ruby_version = ">= 1.9.3"
25
39
 
26
40
  s.extensions = ['ext/rbspy/extconf.rb', 'ext/thread_id/extconf.rb']
27
41
 
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pyroscope"
4
+ require "pyroscope/version"
5
+
6
+ puts Pyroscope::VERSION
7
+ puts RUBY_VERSION
8
+
9
+ Pyroscope.configure do |config|
10
+ config.application_name = "#{ENV["PYROSCOPE_RUN_ID"]}"
11
+ config.server_address = "https://ingest.pyroscope.cloud"
12
+ config.auth_token = ENV["PYROSCOPE_API_TOKEN"]
13
+ config.detect_subprocesses = ENV["PYROSCOPE_DETECT_SUBPROCESSES"] == "1"
14
+ config.oncpu = ENV["PYROSCOPE_ONCPU"] == "1"
15
+ config.log_level = "trace"
16
+ config.report_pid = true
17
+ config.report_thread_id = true
18
+ config.tags = {
19
+ :region => "us-east",
20
+ :detect_subprocesses => ENV["PYROSCOPE_DETECT_SUBPROCESSES"],
21
+ :oncpu => ENV["PYROSCOPE_ONCPU"],
22
+ :version => ENV["RUBY_VERSION"],
23
+ :arch => ENV["PYROSCOPE_ARCH"]
24
+ }
25
+ end
26
+
27
+ def work(n)
28
+ i = 0
29
+ while i < n
30
+ i += 1
31
+ end
32
+ end
33
+
34
+ def fast_function
35
+ Pyroscope.tag_wrapper({"function": "fast"}) do
36
+ work(2001002000)
37
+ end
38
+ end
39
+
40
+ def slow_function
41
+ work(8001008000)
42
+ end
43
+
44
+ child_pid = fork do
45
+ puts "This is the child process"
46
+ Pyroscope.tag_wrapper({"fork": "forked"}) do
47
+ slow_function()
48
+ end
49
+ end
50
+
51
+ puts "This is the master process."
52
+
53
+ Pyroscope.tag_wrapper({"fork": "master"}) do
54
+ fast_function()
55
+ end
56
+
57
+ puts "The PID of the child process is #{child_pid}"
58
+
59
+ Pyroscope.shutdown()
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pyroscope
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.5.0
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Pyroscope Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-19 00:00:00.000000000 Z
11
+ date: 2022-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -98,10 +98,16 @@ files:
98
98
  - lib/thread_id/thread_id.so
99
99
  - pyroscope.gemspec
100
100
  - scripts/docker.sh
101
+ - scripts/tests/test.rb
101
102
  homepage: https://pyroscope.io
102
103
  licenses:
103
104
  - Apache-2.0
104
- metadata: {}
105
+ metadata:
106
+ homepage_uri: https://pyroscope.io
107
+ bug_tracker_uri: https://github.com/pyroscope-io/pyroscope-rs/issues
108
+ documentation_uri: https://pyroscope.io/docs/ruby/
109
+ changelog_uri: https://github.com/pyroscope-io/pyroscope-rs/tree/main/pyroscope_ffi/ruby/CHANGELOG.md
110
+ source_code_uri: https://github.com/pyroscope-io/pyroscope-rs/tree/main/pyroscope_ffi/ruby
105
111
  post_install_message:
106
112
  rdoc_options: []
107
113
  require_paths:
@@ -110,7 +116,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
110
116
  requirements:
111
117
  - - ">="
112
118
  - !ruby/object:Gem::Version
113
- version: 2.5.9
119
+ version: 1.9.3
114
120
  required_rubygems_version: !ruby/object:Gem::Requirement
115
121
  requirements:
116
122
  - - ">="