pyroscope 0.3.0-arm64-darwin → 0.4.0-arm64-darwin

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: 07e893dbb1d344a6408adf69d7e6692a9722f4afa6ceb1f30441596fa85a0529
4
- data.tar.gz: 5d2fc262abf78dceb05a567f621f33c057dfc60f05a463c8b982db98509037e0
3
+ metadata.gz: 8d97d4e0b7e0fe010ec2db8e909afa56cafdce7fd92a82ec5032d594e39108f0
4
+ data.tar.gz: 2aa2e41ecdf127d0cefbc6165f7c78aa744d7ef75dff23fcb952bba7fcfdd977
5
5
  SHA512:
6
- metadata.gz: 3cf54f64e8830c63de312845c1da9d052ef2e054ae8e174fa28cbed6f6108d9101fcf957ccd61364b0b4354ddddfa71b5c0cbe9b7903c9aceea46b5071c632af
7
- data.tar.gz: 7e8ca2f80a04a5e28f474540eafd1b4cad83e974ef2d6a410eeba33d017dec8a04f167f8979137dd648190845704e67d0385be6894be0fb040eb1978c0a35e57
6
+ metadata.gz: 486beaa9b96dea0b29a24353f92243954074437a43a7ecc7013a314d3d14031018bea098f39a2e2b9dbb534c74b1dd5b15c40782e3abc31a0027b99fde7272d7
7
+ data.tar.gz: 211d3a5a3b7eddd7b6ef138b00cc8843d181eb191c267f58c9aec5b8ad784bef31653b64df72d7855cdb8cf242aa7b6a843ebcdd0af8d90f831669a00186acfe
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.
@@ -2,20 +2,20 @@
2
2
  set -e
3
3
 
4
4
  # Install tooling
5
- #yum -y -q install wget gcc libffi-devel openssl-devel
5
+ yum -y -q install wget gcc libffi-devel openssl-devel
6
6
 
7
7
  # Install Rust
8
- #curl https://sh.rustup.rs -sSf | sh -s -- -y
9
- #export PATH=~/.cargo/bin:$PATH
8
+ curl https://sh.rustup.rs -sSf | sh -s -- -y
9
+ export PATH=~/.cargo/bin:$PATH
10
10
 
11
11
  # Build wheels
12
- #/opt/python/cp37-cp37m/bin/python setup.py bdist_wheel
12
+ /opt/python/cp37-cp37m/bin/python setup.py bdist_wheel
13
13
 
14
14
  # Audit wheels
15
- #for wheel in dist/*.whl; do
16
- #auditwheel repair $wheel -w dist/
17
- #rm $wheel
18
- #done
15
+ for wheel in dist/*.whl; do
16
+ auditwheel repair $wheel -w dist/
17
+ rm $wheel
18
+ done
19
19
 
20
20
  # Extract wheels
21
21
  for wheel in dist/*.whl; do
@@ -2,20 +2,20 @@
2
2
  set -e
3
3
 
4
4
  # Install tooling
5
- #yum -y -q install wget gcc libffi-devel openssl-devel
5
+ yum -y -q install wget gcc libffi-devel openssl-devel
6
6
 
7
7
  # Install Rust
8
- #curl https://sh.rustup.rs -sSf | sh -s -- -y
9
- #export PATH=~/.cargo/bin:$PATH
8
+ curl https://sh.rustup.rs -sSf | sh -s -- -y
9
+ export PATH=~/.cargo/bin:$PATH
10
10
 
11
11
  # Build wheels
12
- #/opt/python/cp37-cp37m/bin/python setup.py bdist_wheel
12
+ /opt/python/cp37-cp37m/bin/python setup.py bdist_wheel
13
13
 
14
14
  # Audit wheels
15
- #for wheel in dist/*.whl; do
16
- #auditwheel repair $wheel -w dist/
17
- #rm $wheel
18
- #done
15
+ for wheel in dist/*.whl; do
16
+ auditwheel repair $wheel -w dist/
17
+ rm $wheel
18
+ done
19
19
 
20
20
  # Extract wheels
21
21
  for wheel in dist/*.whl; do
data/ext/rbspy/Cargo.toml CHANGED
@@ -12,6 +12,8 @@ crate-type = ["cdylib"]
12
12
  pyroscope = { path = "../../../../" }
13
13
  #pyroscope_rbspy = { version = "0.2" }
14
14
  pyroscope_rbspy = { path = "../../../../pyroscope_backends/pyroscope_rbspy" }
15
+ ffikit = { path = "../../../ffikit" }
16
+ pretty_env_logger = "0.4.0"
15
17
 
16
18
  [patch.crates-io]
17
19
  read-process-memory = {git = "https://github.com/omarabid/read-process-memory.git", branch = "0.1.4-fix"}
data/ext/rbspy/src/lib.rs CHANGED
@@ -1,45 +1,117 @@
1
- use pyroscope::backend::Tag;
2
- use pyroscope::PyroscopeAgent;
3
- use pyroscope_rbspy::{rbspy_backend, RbspyConfig};
1
+ use std::collections::hash_map::DefaultHasher;
2
+ use std::env;
4
3
  use std::ffi::CStr;
5
- use std::mem::MaybeUninit;
4
+ use std::hash::Hasher;
6
5
  use std::os::raw::c_char;
7
- use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
8
- use std::sync::{Mutex, Once};
6
+ use std::str::FromStr;
9
7
 
10
- pub enum Signal {
11
- Kill,
12
- AddTag(u64, String, String),
13
- RemoveTag(u64, String, String),
14
- }
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
+
14
+ pub fn transform_report(report: Report) -> Report {
15
+ let cwd = env::current_dir().unwrap();
16
+ let cwd = cwd.to_str().unwrap_or("");
17
+
18
+ let data = report
19
+ .data
20
+ .iter()
21
+ .map(|(stacktrace, count)| {
22
+ let new_frames = stacktrace
23
+ .frames
24
+ .iter()
25
+ .map(|frame| {
26
+ let frame = frame.to_owned();
27
+ let mut s = frame.filename.unwrap();
28
+ match s.find(cwd) {
29
+ Some(i) => {
30
+ s = s[(i + cwd.len() + 1)..].to_string();
31
+ }
32
+ None => match s.find("/gems/") {
33
+ Some(i) => {
34
+ s = s[(i + 1)..].to_string();
35
+ }
36
+ None => match s.find("/ruby/") {
37
+ Some(i) => {
38
+ s = s[(i + 6)..].to_string();
39
+ match s.find("/") {
40
+ Some(i) => {
41
+ s = s[(i + 1)..].to_string();
42
+ }
43
+ None => {}
44
+ }
45
+ }
46
+ None => {}
47
+ },
48
+ },
49
+ }
50
+
51
+ // something
52
+ StackFrame::new(
53
+ frame.module,
54
+ frame.name,
55
+ Some(s.to_string()),
56
+ frame.relative_path,
57
+ frame.absolute_path,
58
+ frame.line,
59
+ )
60
+ })
61
+ .collect();
62
+
63
+ let mut mystack = stacktrace.to_owned();
64
+
65
+ mystack.frames = new_frames;
15
66
 
16
- pub struct SignalPass {
17
- inner_sender: Mutex<SyncSender<Signal>>,
18
- inner_receiver: Mutex<Receiver<Signal>>,
67
+ (mystack, count.to_owned())
68
+ })
69
+ .collect();
70
+
71
+ let new_report = Report::new(data).metadata(report.metadata.clone());
72
+
73
+ new_report
19
74
  }
20
75
 
21
- fn signalpass() -> &'static SignalPass {
22
- static mut SIGNAL_PASS: MaybeUninit<SignalPass> = MaybeUninit::uninit();
23
- static ONCE: Once = Once::new();
24
-
25
- ONCE.call_once(|| unsafe {
26
- let (sender, receiver) = sync_channel(1);
27
- let singleton = SignalPass {
28
- inner_sender: Mutex::new(sender),
29
- inner_receiver: Mutex::new(receiver),
30
- };
31
- SIGNAL_PASS = MaybeUninit::new(singleton);
32
- });
76
+ #[no_mangle]
77
+ pub extern "C" fn initialize_logging(logging_level: u32) -> bool {
78
+ // Force rustc to display the log messages in the console.
79
+ match logging_level {
80
+ 50 => {
81
+ std::env::set_var("RUST_LOG", "error");
82
+ }
83
+ 40 => {
84
+ std::env::set_var("RUST_LOG", "warn");
85
+ }
86
+ 30 => {
87
+ std::env::set_var("RUST_LOG", "info");
88
+ }
89
+ 20 => {
90
+ std::env::set_var("RUST_LOG", "debug");
91
+ }
92
+ 10 => {
93
+ std::env::set_var("RUST_LOG", "trace");
94
+ }
95
+ _ => {
96
+ std::env::set_var("RUST_LOG", "debug");
97
+ }
98
+ }
33
99
 
34
- unsafe { SIGNAL_PASS.assume_init_ref() }
100
+ // Initialize the logger.
101
+ pretty_env_logger::init_timed();
102
+
103
+ true
35
104
  }
36
105
 
37
106
  #[no_mangle]
38
107
  pub extern "C" fn initialize_agent(
39
108
  application_name: *const c_char, server_address: *const c_char, auth_token: *const c_char,
40
- sample_rate: u32, detect_subprocesses: bool, on_cpu: bool, report_pid: bool,
41
- report_thread_id: bool, tags: *const c_char,
109
+ sample_rate: u32, detect_subprocesses: bool, oncpu: bool, report_pid: bool,
110
+ report_thread_id: bool, tags: *const c_char, compression: *const c_char
42
111
  ) -> bool {
112
+ // Initialize FFIKit
113
+ let recv = ffikit::initialize_ffi().unwrap();
114
+
43
115
  let application_name = unsafe { CStr::from_ptr(application_name) }
44
116
  .to_str()
45
117
  .unwrap()
@@ -60,46 +132,64 @@ pub extern "C" fn initialize_agent(
60
132
  .unwrap()
61
133
  .to_string();
62
134
 
135
+ let compression_string = unsafe { CStr::from_ptr(compression) }
136
+ .to_str()
137
+ .unwrap()
138
+ .to_string();
139
+
140
+ let compression = Compression::from_str(&compression_string);
141
+
63
142
  let pid = std::process::id();
64
143
 
65
- let s = signalpass();
144
+ let rbspy_config = RbspyConfig::new(pid.try_into().unwrap())
145
+ .sample_rate(sample_rate)
146
+ .lock_process(false)
147
+ .detect_subprocesses(detect_subprocesses)
148
+ .oncpu(oncpu)
149
+ .report_pid(report_pid)
150
+ .report_thread_id(report_thread_id);
66
151
 
67
- std::thread::spawn(move || {
68
- let rbspy_config = RbspyConfig::new(pid.try_into().unwrap())
69
- .sample_rate(sample_rate)
70
- .lock_process(false)
71
- .with_subprocesses(detect_subprocesses)
72
- .on_cpu(on_cpu)
73
- .report_pid(report_pid)
74
- .report_thread_id(report_thread_id);
75
-
76
- let tags_ref = tags_string.as_str();
77
- let tags = string_to_tags(tags_ref);
78
- let rbspy = rbspy_backend(rbspy_config);
79
-
80
- let mut agent_builder = PyroscopeAgent::builder(server_address, application_name)
81
- .backend(rbspy)
82
- .tags(tags);
83
-
84
- if auth_token != "" {
85
- agent_builder = agent_builder.auth_token(auth_token);
86
- }
152
+ let tags_ref = tags_string.as_str();
153
+ let tags = string_to_tags(tags_ref);
154
+ let rbspy = rbspy_backend(rbspy_config);
155
+
156
+ let mut agent_builder = PyroscopeAgent::builder(server_address, application_name)
157
+ .backend(rbspy)
158
+ .func(transform_report)
159
+ .tags(tags);
160
+
161
+ if auth_token != "" {
162
+ agent_builder = agent_builder.auth_token(auth_token);
163
+ }
164
+
165
+ if let Ok(compression) = compression {
166
+ agent_builder = agent_builder.compression(compression);
167
+ }
87
168
 
88
- let agent = agent_builder.build().unwrap();
169
+ let agent = agent_builder.build().unwrap();
89
170
 
90
- let agent_running = agent.start().unwrap();
171
+ let agent_running = agent.start().unwrap();
91
172
 
92
- while let Ok(signal) = s.inner_receiver.lock().unwrap().recv() {
173
+ std::thread::spawn(move || {
174
+ while let Ok(signal) = recv.recv() {
93
175
  match signal {
94
176
  Signal::Kill => {
95
177
  agent_running.stop().unwrap();
96
178
  break;
97
179
  }
98
- Signal::AddTag(thread_id, key, value) => {
180
+ Signal::AddGlobalTag(name, value) => {
181
+ agent_running.add_global_tag(Tag::new(name, value)).unwrap();
182
+ }
183
+ Signal::RemoveGlobalTag(name, value) => {
184
+ agent_running
185
+ .remove_global_tag(Tag::new(name, value))
186
+ .unwrap();
187
+ }
188
+ Signal::AddThreadTag(thread_id, key, value) => {
99
189
  let tag = Tag::new(key, value);
100
190
  agent_running.add_thread_tag(thread_id, tag).unwrap();
101
191
  }
102
- Signal::RemoveTag(thread_id, key, value) => {
192
+ Signal::RemoveThreadTag(thread_id, key, value) => {
103
193
  let tag = Tag::new(key, value);
104
194
  agent_running.remove_thread_tag(thread_id, tag).unwrap();
105
195
  }
@@ -112,40 +202,73 @@ pub extern "C" fn initialize_agent(
112
202
 
113
203
  #[no_mangle]
114
204
  pub extern "C" fn drop_agent() -> bool {
115
- let s = signalpass();
116
- s.inner_sender.lock().unwrap().send(Signal::Kill).unwrap();
205
+ // Send Kill signal to the FFI merge channel.
206
+ ffikit::send(ffikit::Signal::Kill).unwrap();
207
+
117
208
  true
118
209
  }
119
210
 
120
211
  #[no_mangle]
121
- pub extern "C" fn add_tag(thread_id: u64, key: *const c_char, value: *const c_char) -> bool {
122
- let s = signalpass();
212
+ pub extern "C" fn add_thread_tag(thread_id: u64, key: *const c_char, value: *const c_char) -> bool {
123
213
  let key = unsafe { CStr::from_ptr(key) }.to_str().unwrap().to_owned();
124
214
  let value = unsafe { CStr::from_ptr(value) }
125
215
  .to_str()
126
216
  .unwrap()
127
217
  .to_owned();
128
- s.inner_sender
129
- .lock()
218
+
219
+ let pid = std::process::id();
220
+ let mut hasher = DefaultHasher::new();
221
+ hasher.write_u64(thread_id % pid as u64);
222
+ let id = hasher.finish();
223
+
224
+ ffikit::send(ffikit::Signal::AddThreadTag(id, key, value)).unwrap();
225
+
226
+ true
227
+ }
228
+
229
+ #[no_mangle]
230
+ pub extern "C" fn remove_thread_tag(
231
+ thread_id: u64, key: *const c_char, value: *const c_char,
232
+ ) -> bool {
233
+ let key = unsafe { CStr::from_ptr(key) }.to_str().unwrap().to_owned();
234
+ let value = unsafe { CStr::from_ptr(value) }
235
+ .to_str()
130
236
  .unwrap()
131
- .send(Signal::AddTag(thread_id, key, value))
132
- .unwrap();
237
+ .to_owned();
238
+
239
+ let pid = std::process::id();
240
+ let mut hasher = DefaultHasher::new();
241
+ hasher.write_u64(thread_id % pid as u64);
242
+ let id = hasher.finish();
243
+
244
+ ffikit::send(ffikit::Signal::RemoveThreadTag(id, key, value)).unwrap();
245
+
133
246
  true
134
247
  }
135
248
 
136
249
  #[no_mangle]
137
- pub extern "C" fn remove_tag(thread_id: u64, key: *const c_char, value: *const c_char) -> bool {
138
- let s = signalpass();
250
+ pub extern "C" fn add_global_tag(key: *const c_char, value: *const c_char) -> bool {
139
251
  let key = unsafe { CStr::from_ptr(key) }.to_str().unwrap().to_owned();
140
252
  let value = unsafe { CStr::from_ptr(value) }
141
253
  .to_str()
142
254
  .unwrap()
143
255
  .to_owned();
144
- s.inner_sender
145
- .lock()
256
+
257
+ ffikit::send(ffikit::Signal::AddGlobalTag(key, value)).unwrap();
258
+
259
+ true
260
+ }
261
+
262
+ #[no_mangle]
263
+ pub extern "C" fn remove_global_tag(key: *const c_char, value: *const c_char) -> bool {
264
+ let key = unsafe { CStr::from_ptr(key) }.to_str().unwrap().to_owned();
265
+ let value = unsafe { CStr::from_ptr(value) }
266
+ .to_str()
146
267
  .unwrap()
147
- .send(Signal::RemoveTag(thread_id, key, value))
148
- .unwrap();
268
+ .to_owned();
269
+
270
+ ffikit::send(ffikit::Signal::RemoveGlobalTag(key, value)).unwrap();
271
+
149
272
  true
150
273
  }
151
274
 
@@ -1,3 +1,3 @@
1
1
  module Pyroscope
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
data/lib/pyroscope.rb CHANGED
@@ -1,34 +1,42 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
1
4
  require 'ffi'
2
5
 
3
- module Rust
4
- extend FFI::Library
5
- ffi_lib File.expand_path(File.dirname(__FILE__)) + "/rbspy/rbspy.#{RbConfig::CONFIG["DLEXT"]}"
6
- attach_function :initialize_agent, [:string, :string, :string, :int, :bool, :bool, :bool, :bool, :string], :bool
7
- attach_function :add_tag, [:uint64, :string, :string], :bool
8
- attach_function :remove_tag, [:uint64, :string, :string], :bool
9
- attach_function :drop_agent, [], :bool
10
- end
6
+ module Pyroscope
7
+ module Rust
8
+ extend FFI::Library
9
+ ffi_lib File.expand_path(File.dirname(__FILE__)) + "/rbspy/rbspy.#{RbConfig::CONFIG["DLEXT"]}"
10
+ attach_function :initialize_logging, [:int], :bool
11
+ attach_function :initialize_agent, [:string, :string, :string, :int, :bool, :bool, :bool, :bool, :string, :string], :bool
12
+ attach_function :add_thread_tag, [:uint64, :string, :string], :bool
13
+ attach_function :remove_thread_tag, [:uint64, :string, :string], :bool
14
+ attach_function :add_global_tag, [:string, :string], :bool
15
+ attach_function :remove_global_tag, [:string, :string], :bool
16
+ attach_function :drop_agent, [], :bool
17
+ end
11
18
 
12
- module Utils
13
- extend FFI::Library
14
- ffi_lib File.expand_path(File.dirname(__FILE__)) + "/thread_id/thread_id.#{RbConfig::CONFIG["DLEXT"]}"
15
- attach_function :thread_id, [], :uint64
16
- end
19
+ module Utils
20
+ extend FFI::Library
21
+ ffi_lib File.expand_path(File.dirname(__FILE__)) + "/thread_id/thread_id.#{RbConfig::CONFIG["DLEXT"]}"
22
+ attach_function :thread_id, [], :uint64
23
+ end
17
24
 
18
- module Pyroscope
19
- 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) do
20
26
  def initialize(*)
21
27
  super
22
- self.application_name ||= ''
23
- self.server_address ||= 'http://localhost:4040'
24
- self.auth_token ||= ''
25
- self.sample_rate ||= 100
26
- self.detect_subprocesses ||= true
27
- self.on_cpu ||= true
28
- self.report_pid ||= false
29
- self.report_thread_id ||= false
30
- self.log_level ||= 'info'
31
- self.tags ||= []
28
+ # defaults:
29
+ self.application_name = ''
30
+ self.server_address = 'http://localhost:4040'
31
+ self.auth_token = ''
32
+ self.sample_rate = 100
33
+ self.detect_subprocesses = false
34
+ self.oncpu = true
35
+ self.report_pid = false
36
+ self.report_thread_id = false
37
+ self.log_level = 'error'
38
+ self.tags = {}
39
+ self.compression = 'gzip'
32
40
  end
33
41
  end
34
42
 
@@ -39,27 +47,49 @@ module Pyroscope
39
47
  # Pass config to the block
40
48
  yield @config
41
49
 
50
+ # Determine Logging level (kinda like an enum).
51
+ case @config.log_level
52
+ when 'trace'
53
+ @log_level = 10
54
+ when 'debug'
55
+ @log_level = 20
56
+ when 'info'
57
+ @log_level = 30
58
+ when 'warn'
59
+ @log_level = 40
60
+ when 'error'
61
+ @log_level = 50
62
+ else
63
+ @log_level = 50
64
+ end
65
+
66
+ # Initialize Logging
67
+ Rust.initialize_logging(@log_level)
68
+
69
+
70
+ # initialize Pyroscope Agent
42
71
  Rust.initialize_agent(
43
- @config.app_name || @config.application_name,
44
- @config.server_address,
45
- @config.auth_token,
46
- @config.sample_rate,
47
- @config.detect_subprocesses,
48
- @config.on_cpu,
49
- @config.report_pid,
50
- @config.report_thread_id,
51
- tags_to_string(@config.tags)
72
+ # these are defaults in case user-provided values are nil:
73
+ @config.app_name || @config.application_name || "",
74
+ @config.server_address || "",
75
+ @config.auth_token || "",
76
+ @config.sample_rate || 100,
77
+ @config.detect_subprocesses || false,
78
+ @config.oncpu || false,
79
+ @config.report_pid || false,
80
+ @config.report_thread_id || false,
81
+ tags_to_string(@config.tags || {}),
82
+ @config.compression || ""
52
83
  )
53
-
54
- puts @config
55
84
  end
56
85
 
57
86
  def tag_wrapper(tags)
58
- add_tags(tags)
87
+ tid = thread_id
88
+ _add_tags(tid, tags)
59
89
  begin
60
90
  yield
61
91
  ensure
62
- remove_tags(tags)
92
+ _remove_tags(tid, tags)
63
93
  end
64
94
  end
65
95
 
@@ -67,33 +97,35 @@ module Pyroscope
67
97
  warn("deprecated. Use `Pyroscope.tag_wrapper` instead.")
68
98
  end
69
99
 
70
- # convert tags object to string
71
- def tags_to_string(tags)
72
- tags.map { |k, v| "#{k}=#{v}" }.join(',')
73
- end
100
+ def remove_tags(*tags)
101
+ warn("deprecated. Use `Pyroscope.tag_wrapper` instead.")
102
+ end
74
103
 
75
- # get thread id
76
- def thread_id
77
- return Utils.thread_id()
78
- end
104
+ # convert tags object to string
105
+ def tags_to_string(tags)
106
+ tags.map { |k, v| "#{k}=#{v}" }.join(',')
107
+ end
79
108
 
80
- # add tags
81
- def add_tags(tags)
82
- tags.each do |tag_name, tag_value|
83
- thread_id = thread_id()
84
- Rust.add_tag(thread_id, tag_name.to_s, tag_value.to_s)
85
- end
86
- end
109
+ # get thread id
110
+ def thread_id
111
+ return Utils.thread_id
112
+ end
87
113
 
88
- # remove tags
89
- def remove_tags(tags)
90
- tags.each do |tag_name, tag_value|
91
- thread_id = thread_id()
92
- Rust.remove_tag(thread_id, tag_name.to_s, tag_value.to_s)
93
- end
94
- end
114
+ # add tags
115
+ def _add_tags(thread_id, tags)
116
+ tags.each do |tag_name, tag_value|
117
+ Rust.add_thread_tag(thread_id, tag_name.to_s, tag_value.to_s)
118
+ end
119
+ end
120
+
121
+ # remove tags
122
+ def _remove_tags(thread_id, tags)
123
+ tags.each do |tag_name, tag_value|
124
+ Rust.remove_thread_tag(thread_id, tag_name.to_s, tag_value.to_s)
125
+ end
126
+ end
95
127
 
96
- def drop
128
+ def shutdown
97
129
  Rust.drop_agent
98
130
  end
99
131
  end
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.0
4
+ version: 0.4.0
5
5
  platform: arm64-darwin
6
6
  authors:
7
7
  - Pyroscope Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-30 00:00:00.000000000 Z
11
+ date: 2022-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -89,10 +89,16 @@ files:
89
89
  - lib/thread_id/thread_id.bundle
90
90
  - pyroscope.gemspec
91
91
  - scripts/docker.sh
92
+ - scripts/tests/test.rb
92
93
  homepage: https://pyroscope.io
93
94
  licenses:
94
95
  - Apache-2.0
95
- metadata: {}
96
+ metadata:
97
+ homepage_uri: https://pyroscope.io
98
+ bug_tracker_uri: https://github.com/pyroscope-io/pyroscope-rs/issues
99
+ documentation_uri: https://pyroscope.io/docs/ruby/
100
+ changelog_uri: https://github.com/pyroscope-io/pyroscope-rs/tree/main/pyroscope_ffi/ruby/CHANGELOG.md
101
+ source_code_uri: https://github.com/pyroscope-io/pyroscope-rs/tree/main/pyroscope_ffi/ruby
96
102
  post_install_message:
97
103
  rdoc_options: []
98
104
  require_paths:
@@ -101,14 +107,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
101
107
  requirements:
102
108
  - - ">="
103
109
  - !ruby/object:Gem::Version
104
- version: 2.5.9
110
+ version: 1.9.3
105
111
  required_rubygems_version: !ruby/object:Gem::Requirement
106
112
  requirements:
107
113
  - - ">="
108
114
  - !ruby/object:Gem::Version
109
115
  version: '0'
110
116
  requirements: []
111
- rubygems_version: 3.2.3
117
+ rubygems_version: 3.3.7
112
118
  signing_key:
113
119
  specification_version: 4
114
120
  summary: Pyroscope