pyroscope 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +202 -0
- data/README.md +56 -2
- data/elflib/rbspy/manylinux.sh +23 -0
- data/elflib/rbspy/pyproject.toml +7 -0
- data/{ext/pyroscope/lib/.gitkeep → elflib/rbspy/rbspy/__init__.py} +0 -0
- data/elflib/rbspy/setup.cfg +22 -0
- data/elflib/rbspy/setup.py +46 -0
- data/elflib/thread_id/manylinux.sh +23 -0
- data/elflib/thread_id/pyproject.toml +7 -0
- data/elflib/thread_id/setup.cfg +22 -0
- data/elflib/thread_id/setup.py +46 -0
- data/elflib/thread_id/thread_id/__init__.py +0 -0
- data/ext/rbspy/Cargo.toml +27 -0
- data/ext/rbspy/Rakefile +164 -0
- data/ext/rbspy/build.rs +12 -0
- data/ext/rbspy/cbindgen.toml +22 -0
- data/ext/rbspy/extconf.rb +11 -0
- data/ext/rbspy/src/lib.rs +168 -0
- data/ext/thread_id/Cargo.toml +20 -0
- data/ext/thread_id/Rakefile +163 -0
- data/ext/thread_id/build.rs +12 -0
- data/ext/thread_id/cbindgen.toml +22 -0
- data/ext/thread_id/extconf.rb +11 -0
- data/ext/thread_id/src/lib.rs +4 -0
- data/lib/pyroscope/version.rb +1 -1
- data/lib/pyroscope.rb +74 -32
- data/pyroscope.gemspec +28 -23
- data/scripts/docker.sh +16 -0
- metadata +43 -58
- data/.gitignore +0 -7
- data/Gemfile +0 -4
- data/Gemfile.lock +0 -48
- data/Rakefile +0 -56
- data/ext/pyroscope/Makefile.example +0 -265
- data/ext/pyroscope/extconf.rb +0 -79
- data/ext/pyroscope/lib/libpyroscope.rbspy.a +0 -0
- data/ext/pyroscope/lib/libpyroscope.rbspy.combo.a +0 -0
- data/ext/pyroscope/lib/librustdeps.a +0 -0
- data/ext/pyroscope/pyroscope.c +0 -87
- data/test.rb +0 -32
data/ext/rbspy/build.rs
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
extern crate cbindgen;
|
2
|
+
|
3
|
+
use cbindgen::Config;
|
4
|
+
|
5
|
+
fn main() {
|
6
|
+
let bindings = {
|
7
|
+
let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
8
|
+
let config = Config::from_file("cbindgen.toml").unwrap();
|
9
|
+
cbindgen::generate_with_config(&crate_dir, config).unwrap()
|
10
|
+
};
|
11
|
+
bindings.write_to_file("include/rbspy.h");
|
12
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# The language to output bindings in
|
2
|
+
language = "C"
|
3
|
+
documentation_style = "C"
|
4
|
+
|
5
|
+
style = "type"
|
6
|
+
|
7
|
+
# An optional name to use as an include guard
|
8
|
+
include_guard = "RBSPY_H_"
|
9
|
+
# include a comment with the version of cbindgen used to generate the file
|
10
|
+
include_version = true
|
11
|
+
|
12
|
+
# An optional string of text to output at the beginning of the generated file
|
13
|
+
header = "/* Licensed under Apache-2.0 */"
|
14
|
+
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
|
15
|
+
|
16
|
+
braces = "SameLine"
|
17
|
+
tab_width = 2
|
18
|
+
line_length = 80
|
19
|
+
|
20
|
+
[parse]
|
21
|
+
# Do not parse dependent crates
|
22
|
+
parse_deps = false
|
@@ -0,0 +1,168 @@
|
|
1
|
+
use pyroscope::backend::Tag;
|
2
|
+
use pyroscope::PyroscopeAgent;
|
3
|
+
use pyroscope_rbspy::{rbspy_backend, RbspyConfig};
|
4
|
+
use std::ffi::CStr;
|
5
|
+
use std::mem::MaybeUninit;
|
6
|
+
use std::os::raw::c_char;
|
7
|
+
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
|
8
|
+
use std::sync::{Mutex, Once};
|
9
|
+
|
10
|
+
pub enum Signal {
|
11
|
+
Kill,
|
12
|
+
AddTag(u64, String, String),
|
13
|
+
RemoveTag(u64, String, String),
|
14
|
+
}
|
15
|
+
|
16
|
+
pub struct SignalPass {
|
17
|
+
inner_sender: Mutex<SyncSender<Signal>>,
|
18
|
+
inner_receiver: Mutex<Receiver<Signal>>,
|
19
|
+
}
|
20
|
+
|
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
|
+
});
|
33
|
+
|
34
|
+
unsafe { SIGNAL_PASS.assume_init_ref() }
|
35
|
+
}
|
36
|
+
|
37
|
+
#[no_mangle]
|
38
|
+
pub extern "C" fn initialize_agent(
|
39
|
+
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,
|
42
|
+
) -> bool {
|
43
|
+
let application_name = unsafe { CStr::from_ptr(application_name) }
|
44
|
+
.to_str()
|
45
|
+
.unwrap()
|
46
|
+
.to_string();
|
47
|
+
|
48
|
+
let server_address = unsafe { CStr::from_ptr(server_address) }
|
49
|
+
.to_str()
|
50
|
+
.unwrap()
|
51
|
+
.to_string();
|
52
|
+
|
53
|
+
let auth_token = unsafe { CStr::from_ptr(auth_token) }
|
54
|
+
.to_str()
|
55
|
+
.unwrap()
|
56
|
+
.to_string();
|
57
|
+
|
58
|
+
let tags_string = unsafe { CStr::from_ptr(tags) }
|
59
|
+
.to_str()
|
60
|
+
.unwrap()
|
61
|
+
.to_string();
|
62
|
+
|
63
|
+
let pid = std::process::id();
|
64
|
+
|
65
|
+
let s = signalpass();
|
66
|
+
|
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
|
+
}
|
87
|
+
|
88
|
+
let agent = agent_builder.build().unwrap();
|
89
|
+
|
90
|
+
let agent_running = agent.start().unwrap();
|
91
|
+
|
92
|
+
while let Ok(signal) = s.inner_receiver.lock().unwrap().recv() {
|
93
|
+
match signal {
|
94
|
+
Signal::Kill => {
|
95
|
+
agent_running.stop().unwrap();
|
96
|
+
break;
|
97
|
+
}
|
98
|
+
Signal::AddTag(thread_id, key, value) => {
|
99
|
+
let tag = Tag::new(key, value);
|
100
|
+
agent_running.add_thread_tag(thread_id, tag).unwrap();
|
101
|
+
}
|
102
|
+
Signal::RemoveTag(thread_id, key, value) => {
|
103
|
+
let tag = Tag::new(key, value);
|
104
|
+
agent_running.remove_thread_tag(thread_id, tag).unwrap();
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
});
|
109
|
+
|
110
|
+
true
|
111
|
+
}
|
112
|
+
|
113
|
+
#[no_mangle]
|
114
|
+
pub extern "C" fn drop_agent() -> bool {
|
115
|
+
let s = signalpass();
|
116
|
+
s.inner_sender.lock().unwrap().send(Signal::Kill).unwrap();
|
117
|
+
true
|
118
|
+
}
|
119
|
+
|
120
|
+
#[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();
|
123
|
+
let key = unsafe { CStr::from_ptr(key) }.to_str().unwrap().to_owned();
|
124
|
+
let value = unsafe { CStr::from_ptr(value) }
|
125
|
+
.to_str()
|
126
|
+
.unwrap()
|
127
|
+
.to_owned();
|
128
|
+
s.inner_sender
|
129
|
+
.lock()
|
130
|
+
.unwrap()
|
131
|
+
.send(Signal::AddTag(thread_id, key, value))
|
132
|
+
.unwrap();
|
133
|
+
true
|
134
|
+
}
|
135
|
+
|
136
|
+
#[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();
|
139
|
+
let key = unsafe { CStr::from_ptr(key) }.to_str().unwrap().to_owned();
|
140
|
+
let value = unsafe { CStr::from_ptr(value) }
|
141
|
+
.to_str()
|
142
|
+
.unwrap()
|
143
|
+
.to_owned();
|
144
|
+
s.inner_sender
|
145
|
+
.lock()
|
146
|
+
.unwrap()
|
147
|
+
.send(Signal::RemoveTag(thread_id, key, value))
|
148
|
+
.unwrap();
|
149
|
+
true
|
150
|
+
}
|
151
|
+
|
152
|
+
// Convert a string of tags to a Vec<(&str, &str)>
|
153
|
+
fn string_to_tags<'a>(tags: &'a str) -> Vec<(&'a str, &'a str)> {
|
154
|
+
let mut tags_vec = Vec::new();
|
155
|
+
// check if string is empty
|
156
|
+
if tags.is_empty() {
|
157
|
+
return tags_vec;
|
158
|
+
}
|
159
|
+
|
160
|
+
for tag in tags.split(',') {
|
161
|
+
let mut tag_split = tag.split('=');
|
162
|
+
let key = tag_split.next().unwrap();
|
163
|
+
let value = tag_split.next().unwrap();
|
164
|
+
tags_vec.push((key, value));
|
165
|
+
}
|
166
|
+
|
167
|
+
tags_vec
|
168
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
[package]
|
2
|
+
name = "thread_id"
|
3
|
+
version = "0.1.0"
|
4
|
+
edition = "2021"
|
5
|
+
|
6
|
+
[lib]
|
7
|
+
name = "thread_id"
|
8
|
+
crate-type = ["cdylib"]
|
9
|
+
|
10
|
+
[dependencies]
|
11
|
+
libc = "*"
|
12
|
+
|
13
|
+
[build-dependencies]
|
14
|
+
cbindgen = "0.20.0"
|
15
|
+
|
16
|
+
[profile.release]
|
17
|
+
opt-level= "z"
|
18
|
+
debug = false
|
19
|
+
lto = true
|
20
|
+
codegen-units = 1
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "shellwords"
|
4
|
+
|
5
|
+
class ThreadIdRakeCargoHelper
|
6
|
+
attr_reader :gemname
|
7
|
+
|
8
|
+
def initialize(gemname=File.basename(__dir__))
|
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
|
+
end
|
54
|
+
|
55
|
+
def self.dld_flags
|
56
|
+
Shellwords.split(RbConfig::CONFIG["DLDFLAGS"]).flat_map do |arg|
|
57
|
+
arg = arg.gsub(/\$\((\w+)\)/) do
|
58
|
+
$1 == "DEFFILE" ? nil : RbConfig::CONFIG[name]
|
59
|
+
end.strip
|
60
|
+
next [] if arg.empty?
|
61
|
+
|
62
|
+
transform_flag(arg)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.platform_flags
|
67
|
+
return unless RbConfig::CONFIG["target_os"] =~ /mingw/i
|
68
|
+
|
69
|
+
[*Shellwords.split(RbConfig::CONFIG["LIBRUBYARG"]).flat_map {|arg| transform_flag(arg)},
|
70
|
+
"-C", "link-arg=-Wl,--dynamicbase",
|
71
|
+
"-C", "link-arg=-Wl,--disable-auto-image-base",
|
72
|
+
"-C", "link-arg=-static-libgcc"]
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.transform_flag(arg)
|
76
|
+
k, v = arg.split(/(?<=..)/, 2)
|
77
|
+
case k
|
78
|
+
when "-L"
|
79
|
+
[k, "native=#{v}"]
|
80
|
+
when "-l"
|
81
|
+
[k, v]
|
82
|
+
when "-F"
|
83
|
+
["-l", "framework=#{v}"]
|
84
|
+
else
|
85
|
+
["-C", "link_arg=#{k}#{v}"]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def install_dir
|
90
|
+
File.expand_path(File.join("..", "..", "lib", gemname), __dir__)
|
91
|
+
end
|
92
|
+
|
93
|
+
def rust_name
|
94
|
+
prefix = "lib" unless Gem.win_platform?
|
95
|
+
suffix = if RbConfig::CONFIG["target_os"] =~ /darwin/i
|
96
|
+
".dylib"
|
97
|
+
elsif Gem.win_platform?
|
98
|
+
".dll"
|
99
|
+
else
|
100
|
+
".so"
|
101
|
+
end
|
102
|
+
"#{prefix}#{gemname}#{suffix}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def ruby_name
|
106
|
+
"#{gemname}.#{RbConfig::CONFIG["DLEXT"]}"
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
task default: [:thread_id_install, :thread_id_clean]
|
112
|
+
task thread_id: [:thread_id_install, :thread_id_clean]
|
113
|
+
|
114
|
+
desc "set dev mode for subsequent task, run like `rake dev install`"
|
115
|
+
task :thread_id_dev do
|
116
|
+
@dev = true
|
117
|
+
end
|
118
|
+
|
119
|
+
desc "build gem native extension and copy to lib"
|
120
|
+
task thread_id_install: [:thread_id_cd, :thread_id_build] do
|
121
|
+
helper = ThreadIdRakeCargoHelper.new
|
122
|
+
profile_dir = @dev ? "debug" : "release"
|
123
|
+
arch_dir = RbspyRakeCargoHelper.rust_toolchain
|
124
|
+
source = File.join(ThreadIdRakeCargoHelper.cargo_target_dir, arch_dir, profile_dir, helper.rust_name)
|
125
|
+
dest = File.join(helper.install_dir, helper.ruby_name)
|
126
|
+
mkdir_p(helper.install_dir)
|
127
|
+
rm(dest) if File.exist?(dest)
|
128
|
+
cp(source, dest)
|
129
|
+
end
|
130
|
+
|
131
|
+
desc "build gem native extension"
|
132
|
+
task thread_id_build: [:thread_id_cargo, :thread_id_cd] do
|
133
|
+
sh "cargo", "rustc", *(["--locked", "--release"] unless @dev), "--target=#{RbspyRakeCargoHelper.rust_toolchain}", "--", *RbspyRakeCargoHelper.flags
|
134
|
+
end
|
135
|
+
|
136
|
+
desc "clean up release build artifacts"
|
137
|
+
task thread_id_clean: [:thread_id_cargo, :thread_id_cd] do
|
138
|
+
sh "cargo clean --release"
|
139
|
+
end
|
140
|
+
|
141
|
+
desc "clean up build artifacts"
|
142
|
+
task thread_id_clobber: [:thread_id_cargo, :thread_id_cd] do
|
143
|
+
sh "cargo clean"
|
144
|
+
end
|
145
|
+
|
146
|
+
desc "check for cargo"
|
147
|
+
task :thread_id_cargo do
|
148
|
+
raise <<-MSG unless ThreadIdRakeCargoHelper.command?("cargo")
|
149
|
+
This gem requires a Rust compiler and the `cargo' build tool to build the
|
150
|
+
gem's native extension. See https://www.rust-lang.org/tools/install for
|
151
|
+
how to install Rust. `cargo' is usually part of the Rust installation.
|
152
|
+
MSG
|
153
|
+
|
154
|
+
raise <<-MSG if Gem.win_platform? && ThreadIdRakeCargoHelper.rust_toolchain !~ /gnu/
|
155
|
+
Found Rust toolchain `#{ThreadIdRakeCargoHelper.rust_toolchain}' but the gem native
|
156
|
+
extension requires the gnu toolchain on Windows.
|
157
|
+
MSG
|
158
|
+
end
|
159
|
+
|
160
|
+
# ensure task is running in the right dir
|
161
|
+
task :thread_id_cd do
|
162
|
+
cd(__dir__) unless __dir__ == pwd
|
163
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
extern crate cbindgen;
|
2
|
+
|
3
|
+
use cbindgen::Config;
|
4
|
+
|
5
|
+
fn main() {
|
6
|
+
let bindings = {
|
7
|
+
let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
8
|
+
let config = Config::from_file("cbindgen.toml").unwrap();
|
9
|
+
cbindgen::generate_with_config(&crate_dir, config).unwrap()
|
10
|
+
};
|
11
|
+
bindings.write_to_file("include/thread_id.h");
|
12
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# The language to output bindings in
|
2
|
+
language = "C"
|
3
|
+
documentation_style = "C"
|
4
|
+
|
5
|
+
style = "type"
|
6
|
+
|
7
|
+
# An optional name to use as an include guard
|
8
|
+
include_guard = "RBSPY_H_"
|
9
|
+
# include a comment with the version of cbindgen used to generate the file
|
10
|
+
include_version = true
|
11
|
+
|
12
|
+
# An optional string of text to output at the beginning of the generated file
|
13
|
+
header = "/* Licensed under Apache-2.0 */"
|
14
|
+
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
|
15
|
+
|
16
|
+
braces = "SameLine"
|
17
|
+
tab_width = 2
|
18
|
+
line_length = 80
|
19
|
+
|
20
|
+
[parse]
|
21
|
+
# Do not parse dependent crates
|
22
|
+
parse_deps = false
|
data/lib/pyroscope/version.rb
CHANGED
data/lib/pyroscope.rb
CHANGED
@@ -1,59 +1,101 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require 'ffi'
|
2
|
+
|
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
|
11
|
+
|
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
|
3
17
|
|
4
18
|
module Pyroscope
|
5
|
-
Config = Struct.new(:app_name, :server_address, :auth_token, :sample_rate, :
|
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
|
20
|
+
def initialize(*)
|
21
|
+
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 ||= []
|
32
|
+
end
|
33
|
+
end
|
6
34
|
|
7
35
|
class << self
|
8
36
|
def configure
|
9
|
-
@
|
10
|
-
yield @configuration
|
11
|
-
_start(
|
12
|
-
@configuration.app_name,
|
13
|
-
@configuration.server_address,
|
14
|
-
@configuration.auth_token || "",
|
15
|
-
@configuration.sample_rate || 100,
|
16
|
-
@configuration.with_subprocesses || 0,
|
17
|
-
@configuration.log_level || "error",
|
18
|
-
)
|
19
|
-
end
|
37
|
+
@config = Config.new
|
20
38
|
|
21
|
-
|
22
|
-
|
23
|
-
end
|
39
|
+
# Pass config to the block
|
40
|
+
yield @config
|
24
41
|
|
25
|
-
|
26
|
-
|
42
|
+
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)
|
52
|
+
)
|
27
53
|
end
|
28
54
|
|
29
55
|
def tag_wrapper(tags)
|
30
|
-
|
31
|
-
|
56
|
+
tid = thread_id
|
57
|
+
_add_tags(tid, tags)
|
32
58
|
begin
|
33
59
|
yield
|
34
60
|
ensure
|
35
|
-
|
61
|
+
_remove_tags(tid, tags)
|
36
62
|
end
|
37
63
|
end
|
38
64
|
|
39
65
|
def tag(tags)
|
40
|
-
|
41
|
-
_set_tag(key, val)
|
42
|
-
end
|
66
|
+
warn("deprecated. Use `Pyroscope.tag_wrapper` instead.")
|
43
67
|
end
|
44
68
|
|
45
|
-
def remove_tags(*
|
46
|
-
|
47
|
-
|
69
|
+
def remove_tags(*tags)
|
70
|
+
warn("deprecated. Use `Pyroscope.tag_wrapper` instead.")
|
71
|
+
end
|
72
|
+
|
73
|
+
# convert tags object to string
|
74
|
+
def tags_to_string(tags)
|
75
|
+
tags.map { |k, v| "#{k}=#{v}" }.join(',')
|
76
|
+
end
|
77
|
+
|
78
|
+
# get thread id
|
79
|
+
def thread_id
|
80
|
+
return Utils.thread_id
|
81
|
+
end
|
82
|
+
|
83
|
+
# add tags
|
84
|
+
def _add_tags(thread_id, tags)
|
85
|
+
tags.each do |tag_name, tag_value|
|
86
|
+
Rust.add_tag(thread_id, tag_name.to_s, tag_value.to_s)
|
48
87
|
end
|
49
88
|
end
|
50
89
|
|
51
|
-
|
52
|
-
|
90
|
+
# remove tags
|
91
|
+
def _remove_tags(thread_id, tags)
|
92
|
+
tags.each do |tag_name, tag_value|
|
93
|
+
Rust.remove_tag(thread_id, tag_name.to_s, tag_value.to_s)
|
94
|
+
end
|
53
95
|
end
|
54
96
|
|
55
|
-
def
|
56
|
-
|
97
|
+
def drop
|
98
|
+
Rust.drop_agent
|
57
99
|
end
|
58
100
|
end
|
59
101
|
end
|
data/pyroscope.gemspec
CHANGED
@@ -1,27 +1,32 @@
|
|
1
|
-
|
2
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
|
4
|
-
require 'pyroscope/version'
|
1
|
+
require_relative "lib/pyroscope/version"
|
5
2
|
|
6
3
|
Gem::Specification.new do |s|
|
7
|
-
s.name
|
8
|
-
s.version
|
9
|
-
s.summary
|
10
|
-
s.description
|
11
|
-
s.authors
|
12
|
-
s.email
|
13
|
-
s.
|
14
|
-
s.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
s.
|
19
|
-
|
20
|
-
|
4
|
+
s.name = 'pyroscope'
|
5
|
+
s.version = Pyroscope::VERSION
|
6
|
+
s.summary = 'Pyroscope'
|
7
|
+
s.description = 'Pyroscope FFI Integration for Ruby'
|
8
|
+
s.authors = ['Pyroscope Team']
|
9
|
+
s.email = ['contact@pyroscope.io']
|
10
|
+
s.homepage = 'https://pyroscope.io'
|
11
|
+
s.license = 'Apache-2.0'
|
12
|
+
|
13
|
+
# Specify which files should be added to the gem when it is released.
|
14
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
15
|
+
#s.files = Dir.chdir(__dir__) do
|
16
|
+
#`git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
#(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
18
|
+
#end
|
19
|
+
#end
|
20
|
+
s.files = `git ls-files -z`.split("\0").reject { |f| f =~ /^(\.|G|spec|Rakefile)/ }
|
21
|
+
|
22
|
+
s.platform = Gem::Platform::RUBY
|
23
|
+
|
24
|
+
s.required_ruby_version = ">= 2.5.9"
|
25
|
+
|
26
|
+
s.extensions = ['ext/rbspy/extconf.rb', 'ext/thread_id/extconf.rb']
|
27
|
+
|
28
|
+
s.add_dependency 'ffi'
|
21
29
|
|
22
|
-
s.add_development_dependency
|
23
|
-
s.add_development_dependency
|
24
|
-
s.add_development_dependency "rake-compiler"
|
25
|
-
s.add_development_dependency "rubygems-tasks"
|
26
|
-
s.add_development_dependency "rspec"
|
30
|
+
s.add_development_dependency 'bundler'
|
31
|
+
s.add_development_dependency 'rake', '~> 13.0'
|
27
32
|
end
|
data/scripts/docker.sh
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
set -ex
|
3
|
+
|
4
|
+
BUILD_DIR="/work"
|
5
|
+
|
6
|
+
docker run \
|
7
|
+
-w /work/pyroscope_ffi/ruby/elflib/rbspy \
|
8
|
+
-v `pwd`:/work \
|
9
|
+
quay.io/pypa/${BUILD_ARCH} \
|
10
|
+
sh manylinux.sh
|
11
|
+
|
12
|
+
docker run \
|
13
|
+
-w /work/pyroscope_ffi/ruby/elflib/thread_id \
|
14
|
+
-v `pwd`:/work \
|
15
|
+
quay.io/pypa/${BUILD_ARCH} \
|
16
|
+
sh manylinux.sh
|