pyroscope 1.0.6-x86_64-linux → 1.0.7-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.
@@ -0,0 +1,18 @@
1
+ [package]
2
+ name = "ffiruby"
3
+ version = "1.0.7" # x-release-please-version
4
+ edition = "2021"
5
+ rust-version = "1.66"
6
+
7
+ [lib]
8
+ name = "rbspy"
9
+ crate-type = ["cdylib"]
10
+
11
+ [dependencies]
12
+ pyroscope = { version="2.0.0", default-features = false, features = ["native-tls"] }
13
+ rbspy = { version = "0.47" }
14
+ remoteprocess = "0.5"
15
+ pretty_env_logger = "0.5"
16
+ log = "0.4"
17
+ libc = "0.2"
18
+ anyhow = "1.0"
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shellwords"
4
+
5
+ class RbspyRakeCargoHelper
6
+ attr_reader :gemname
7
+
8
+ def initialize(gemname="rbspy")
9
+ @gemname = gemname
10
+ end
11
+
12
+ def self.command?(name)
13
+ exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
14
+ ENV["PATH"].split(File::PATH_SEPARATOR).any? do |path|
15
+ exts.any? do |ext|
16
+ exe = File.join(path, "#{name}#{ext}")
17
+ File.executable?(exe) && !File.directory?(exe)
18
+ end
19
+ end
20
+ end
21
+
22
+ def self.rust_toolchain
23
+ # return env variable if set
24
+ target = ENV["RUST_TARGET"]
25
+ return target if target
26
+
27
+ str = `rustc --version --verbose`
28
+ info = str.lines.map {|l| l.chomp.split(/:\s+/, 2)}.drop(1).to_h
29
+ info["host"]
30
+ end
31
+
32
+ def self.cargo_target_dir
33
+ return @cargo_target_dir if defined? @cargo_target_dir
34
+
35
+ str = `cargo metadata --format-version 1 --offline --no-deps --quiet`
36
+ begin
37
+ require "json"
38
+ dir = JSON.parse(str)["target_directory"]
39
+ rescue LoadError # json is usually part of the stdlib, but just in case
40
+ /"target_directory"\s*:\s*"(?<dir>[^"]*)"/ =~ str
41
+ end
42
+ @cargo_target_dir = dir || "target"
43
+ end
44
+
45
+ def self.flags
46
+ cc_flags = Shellwords.split(RbConfig.expand(RbConfig::MAKEFILE_CONFIG["CC"].dup))
47
+
48
+ ["-C", "linker=#{cc_flags.shift}",
49
+ *cc_flags.flat_map {|a| ["-C", "link-arg=#{a}"] },
50
+ "-L", "native=#{RbConfig::CONFIG["libdir"]}",
51
+ *dld_flags,
52
+ *platform_flags,
53
+ ]
54
+ end
55
+
56
+ def self.dld_flags
57
+ Shellwords.split(RbConfig::CONFIG["DLDFLAGS"]).flat_map do |arg|
58
+ arg = arg.gsub(/\$\((\w+)\)/) do
59
+ $1 == "DEFFILE" ? nil : RbConfig::CONFIG[name]
60
+ end.strip
61
+ next [] if arg.empty?
62
+
63
+ transform_flag(arg)
64
+ end
65
+ end
66
+
67
+ def self.platform_flags
68
+ return unless RbConfig::CONFIG["target_os"] =~ /mingw/i
69
+
70
+ [*Shellwords.split(RbConfig::CONFIG["LIBRUBYARG"]).flat_map {|arg| transform_flag(arg)},
71
+ "-C", "link-arg=-Wl,--dynamicbase",
72
+ "-C", "link-arg=-Wl,--disable-auto-image-base",
73
+ "-C", "link-arg=-static-libgcc"]
74
+ end
75
+
76
+ def self.transform_flag(arg)
77
+ k, v = arg.split(/(?<=..)/, 2)
78
+ case k
79
+ when "-L"
80
+ [k, "native=#{v}"]
81
+ when "-l"
82
+ [k, v]
83
+ when "-F"
84
+ ["-l", "framework=#{v}"]
85
+ else
86
+ ["-C", "link_arg=#{k}#{v}"]
87
+ end
88
+ end
89
+
90
+ def install_dir
91
+ File.expand_path(File.join("..", "..", "lib", gemname), __dir__)
92
+ end
93
+
94
+ def rust_name
95
+ prefix = "lib" unless Gem.win_platform?
96
+ suffix = if RbConfig::CONFIG["target_os"] =~ /darwin/i
97
+ ".dylib"
98
+ elsif Gem.win_platform?
99
+ ".dll"
100
+ else
101
+ ".so"
102
+ end
103
+ "#{prefix}#{gemname}#{suffix}"
104
+ end
105
+
106
+ def ruby_name
107
+ "#{gemname}.#{RbConfig::CONFIG["DLEXT"]}"
108
+ end
109
+
110
+ end
111
+
112
+ task default: [:rbspy_install, :rbspy_clean]
113
+ task rbspy: [:rbspy_install, :rbspy_clean]
114
+
115
+ desc "set dev mode for subsequent task, run like `rake dev install`"
116
+ task :rbspy_dev do
117
+ @dev = true
118
+ end
119
+
120
+ desc "build gem native extension and copy to lib"
121
+ task rbspy_install: [:rbspy_cd, :rbspy_build] do
122
+ helper = RbspyRakeCargoHelper.new
123
+ profile_dir = @dev ? "debug" : "release"
124
+ arch_dir = RbspyRakeCargoHelper.rust_toolchain
125
+ source = File.join(RbspyRakeCargoHelper.cargo_target_dir, arch_dir, profile_dir, helper.rust_name)
126
+ dest = File.join(helper.install_dir, helper.ruby_name)
127
+ mkdir_p(helper.install_dir)
128
+ rm(dest) if File.exist?(dest)
129
+ cp(source, dest)
130
+ end
131
+
132
+ desc "build gem native extension"
133
+ task rbspy_build: [:rbspy_cargo, :rbspy_cd] do
134
+ sh "cargo", "rustc", *(["--locked", "--release"] unless @dev), "--target=#{RbspyRakeCargoHelper.rust_toolchain}", "--", *RbspyRakeCargoHelper.flags
135
+ end
136
+
137
+ desc "clean up release build artifacts"
138
+ task rbspy_clean: [:rbspy_cargo, :rbspy_cd] do
139
+ sh "cargo clean --release"
140
+ end
141
+
142
+ desc "clean up build artifacts"
143
+ task rbspy_clobber: [:rbspy_cargo, :rbspy_cd] do
144
+ sh "cargo clean"
145
+ end
146
+
147
+ desc "check for cargo"
148
+ task :rbspy_cargo do
149
+ raise <<-MSG unless RbspyRakeCargoHelper.command?("cargo")
150
+ This gem requires a Rust compiler and the `cargo' build tool to build the
151
+ gem's native extension. See https://www.rust-lang.org/tools/install for
152
+ how to install Rust. `cargo' is usually part of the Rust installation.
153
+ MSG
154
+
155
+ raise <<-MSG if Gem.win_platform? && RbspyRakeCargoHelper.rust_toolchain !~ /gnu/
156
+ Found Rust toolchain `#{RbspyRakeCargoHelper.rust_toolchain}' but the gem native
157
+ extension requires the gnu toolchain on Windows.
158
+ MSG
159
+ end
160
+
161
+ # ensure task is running in the right dir
162
+ task :rbspy_cd do
163
+ cd(__dir__) unless __dir__ == pwd
164
+ end
@@ -0,0 +1,17 @@
1
+ language = "C"
2
+ documentation_style = "C"
3
+
4
+ style = "type"
5
+
6
+ include_guard = "RBSPY_H_"
7
+ include_version = false
8
+
9
+ header = "/* Licensed under Apache-2.0 */"
10
+ autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
11
+
12
+ braces = "SameLine"
13
+ tab_width = 2
14
+ line_length = 80
15
+
16
+ [parse]
17
+ parse_deps = false
@@ -0,0 +1,45 @@
1
+ /* Licensed under Apache-2.0 */
2
+
3
+ #ifndef RBSPY_H_
4
+ #define RBSPY_H_
5
+
6
+ /* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
7
+
8
+ #include <stdarg.h>
9
+ #include <stdbool.h>
10
+ #include <stdint.h>
11
+ #include <stdlib.h>
12
+
13
+ bool initialize_logging(uint32_t logging_level);
14
+
15
+ /*
16
+ # Safety
17
+ All pointer arguments must be valid, non-null, null-terminated C strings.
18
+ */
19
+ bool initialize_agent(const char *application_name,
20
+ const char *server_address,
21
+ const char *basic_auth_user,
22
+ const char *basic_auth_password,
23
+ uint32_t sample_rate,
24
+ bool oncpu,
25
+ bool report_pid,
26
+ bool report_thread_id,
27
+ const char *tags,
28
+ const char *tenant_id,
29
+ const char *http_headers_json);
30
+
31
+ bool drop_agent(void);
32
+
33
+ /*
34
+ # Safety
35
+ `key` and `value` must be valid, non-null, null-terminated C strings.
36
+ */
37
+ bool add_thread_tag(const char *key, const char *value);
38
+
39
+ /*
40
+ # Safety
41
+ `key` and `value` must be valid, non-null, null-terminated C strings.
42
+ */
43
+ bool remove_thread_tag(const char *key, const char *value);
44
+
45
+ #endif /* RBSPY_H_ */
@@ -0,0 +1,199 @@
1
+ use pyroscope::{
2
+ backend::{
3
+ Backend, BackendConfig, Report, ReportBatch, ReportData, StackBuffer, StackFrame,
4
+ StackTrace, ThreadTag, ThreadTagsSet,
5
+ },
6
+ error::{PyroscopeError, Result},
7
+ };
8
+ use rbspy::sampler::Sampler;
9
+ use std::{
10
+ ops::Deref,
11
+ sync::{
12
+ mpsc::{channel, sync_channel, Receiver, Sender, SyncSender},
13
+ Arc, Mutex,
14
+ },
15
+ thread::JoinHandle,
16
+ };
17
+
18
+ const LOG_TAG: &str = "Pyroscope::Rbspy";
19
+
20
+ pub struct Rbspy {
21
+ sample_rate: u32,
22
+ backend_config: BackendConfig,
23
+ sampler: Sampler,
24
+ /// Error Receiver
25
+ error_receiver: Option<Receiver<std::result::Result<(), anyhow::Error>>>,
26
+ /// Profiling buffer
27
+ buffer: Arc<Mutex<StackBuffer>>,
28
+ /// Rulset
29
+ ruleset: ThreadTagsSet,
30
+ }
31
+
32
+ impl std::fmt::Debug for Rbspy {
33
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34
+ write!(f, "Rbspy Backend")
35
+ }
36
+ }
37
+
38
+ impl Rbspy {
39
+ /// Create a new Rbspy instance
40
+ pub fn new(sampler: Sampler, sample_rate: u32, backend_config: BackendConfig) -> Self {
41
+ Rbspy {
42
+ sample_rate,
43
+ sampler,
44
+ backend_config,
45
+ error_receiver: None,
46
+ buffer: Arc::new(Mutex::new(StackBuffer::default())),
47
+ ruleset: ThreadTagsSet::default(),
48
+ }
49
+ }
50
+ }
51
+
52
+ // Type aliases
53
+ type ErrorSender = Sender<std::result::Result<(), anyhow::Error>>;
54
+ type ErrorReceiver = Receiver<std::result::Result<(), anyhow::Error>>;
55
+
56
+ impl Backend for Rbspy {
57
+ fn add_tag(&self, tag: ThreadTag) -> Result<()> {
58
+ self.ruleset.add(tag)?;
59
+
60
+ Ok(())
61
+ }
62
+
63
+ fn remove_tag(&self, tag: ThreadTag) -> Result<()> {
64
+ self.ruleset.remove(tag)?;
65
+
66
+ Ok(())
67
+ }
68
+
69
+ /// Initialize the backend
70
+ fn initialize(&mut self) -> Result<()> {
71
+ // Channel for Errors generated by the RubySpy Sampler
72
+ let (error_sender, error_receiver): (ErrorSender, ErrorReceiver) = channel();
73
+
74
+ // This is provides enough space for 100 threads.
75
+ // It might be a better idea to figure out how many threads are running and determine the
76
+ // size of the channel based on that.
77
+ let queue_size: usize = self.sample_rate as usize * 10 * 100;
78
+
79
+ // Channel for StackTraces generated by the RubySpy Sampler
80
+ let (stack_sender, stack_receiver): (
81
+ SyncSender<rbspy::StackTrace>,
82
+ Receiver<rbspy::StackTrace>,
83
+ ) = sync_channel(queue_size);
84
+
85
+ // Set Error and Stack Receivers
86
+ // self.stack_receiver = Some(stack_receiver);
87
+ self.error_receiver = Some(error_receiver);
88
+
89
+ self.sampler
90
+ .start(stack_sender, error_sender)
91
+ .map_err(|e| PyroscopeError::new(&format!("Rbspy: Sampler Error: {}", e)))?;
92
+
93
+ // Start own thread
94
+ //
95
+ // Get an Arc reference to the Report Buffer
96
+ let buffer = self.buffer.clone();
97
+
98
+ // ruleset reference
99
+ let ruleset = self.ruleset.clone();
100
+
101
+ let backend_config = self.backend_config;
102
+
103
+ let _: JoinHandle<Result<()>> = std::thread::spawn(move || {
104
+ // Iterate over the StackTrace
105
+ while let Ok(stack_trace) = stack_receiver.recv() {
106
+ // convert StackTrace
107
+ let own_trace: StackTrace =
108
+ Into::<StackTraceWrapper>::into((stack_trace, &backend_config)).into();
109
+
110
+ let stacktrace = own_trace.add_tag_rules(&ruleset);
111
+
112
+ buffer.lock()?.record(stacktrace)?;
113
+ }
114
+
115
+ Ok(())
116
+ });
117
+
118
+ Ok(())
119
+ }
120
+
121
+ fn shutdown(self: Box<Self>) -> Result<()> {
122
+ log::trace!(target: LOG_TAG, "Shutting down sampler thread");
123
+
124
+ self.sampler.stop();
125
+
126
+ Ok(())
127
+ }
128
+
129
+ fn report(&mut self) -> Result<ReportBatch> {
130
+ let v8: StackBuffer = self.buffer.lock()?.deref().to_owned();
131
+ let reports: Vec<Report> = v8.into();
132
+
133
+ self.buffer.lock()?.clear();
134
+
135
+ Ok(ReportBatch {
136
+ profile_type: "process_cpu".into(),
137
+ data: ReportData::Reports(reports),
138
+ })
139
+ }
140
+ }
141
+
142
+ struct StackFrameWrapper(StackFrame);
143
+
144
+ impl From<StackFrameWrapper> for StackFrame {
145
+ fn from(frame: StackFrameWrapper) -> Self {
146
+ frame.0
147
+ }
148
+ }
149
+
150
+ impl From<rbspy::StackFrame> for StackFrameWrapper {
151
+ fn from(frame: rbspy::StackFrame) -> Self {
152
+ StackFrameWrapper(StackFrame {
153
+ module: None,
154
+ name: Some(frame.name),
155
+ filename: Some(frame.relative_path.clone()),
156
+ relative_path: Some(frame.relative_path),
157
+ absolute_path: frame.absolute_path,
158
+ line: frame.lineno.map(|l| l as u32),
159
+ })
160
+ }
161
+ }
162
+
163
+ struct StackTraceWrapper(StackTrace);
164
+
165
+ impl From<StackTraceWrapper> for StackTrace {
166
+ fn from(trace: StackTraceWrapper) -> Self {
167
+ trace.0
168
+ }
169
+ }
170
+
171
+ impl From<(rbspy::StackTrace, &BackendConfig)> for StackTraceWrapper {
172
+ fn from(arg: (rbspy::StackTrace, &BackendConfig)) -> Self {
173
+ let (trace, config) = arg;
174
+
175
+ let thread_id = trace.thread_id.map(|tid| {
176
+ // for rbspy we use pthread_t as thread id
177
+ // https://github.com/ruby/ruby/blob/54a74c42033e42869e69e7dc9e67efa1faf225be/include/ruby/thread_native.h#L41
178
+ let thread_id = tid as libc::pthread_t;
179
+ thread_id.into()
180
+ });
181
+
182
+ StackTraceWrapper(StackTrace::new(
183
+ config,
184
+ trace.pid.map(|pid| pid as u32),
185
+ thread_id,
186
+ None,
187
+ trace
188
+ .iter()
189
+ .map(|frame| Into::<StackFrameWrapper>::into(frame.clone()).into())
190
+ .collect(),
191
+ ))
192
+ }
193
+ }
194
+
195
+ pub fn self_thread_id() -> pyroscope::ThreadId {
196
+ // for rbspy we use pthread_t as thread id
197
+ // https://github.com/ruby/ruby/blob/54a74c42033e42869e69e7dc9e67efa1faf225be/include/ruby/thread_native.h#L41
198
+ pyroscope::ThreadId::pthread_self()
199
+ }