pyroscope 1.0.6-aarch64-linux → 1.0.7-aarch64-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 +4 -4
- data/Gemfile.lock +2 -2
- data/ext/rbspy/Cargo.lock +2782 -0
- data/ext/rbspy/Cargo.toml +18 -0
- data/ext/rbspy/Rakefile +164 -0
- data/ext/rbspy/cbindgen.toml +17 -0
- data/ext/rbspy/include/rbspy.h +45 -0
- data/ext/rbspy/src/backend.rs +199 -0
- data/ext/rbspy/src/lib.rs +348 -0
- data/lib/pyroscope/version.rb +1 -1
- data/lib/rbspy/rbspy.so +0 -0
- data/pyroscope.gemspec +8 -2
- metadata +8 -1
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
mod backend;
|
|
2
|
+
|
|
3
|
+
use rbspy::sampler::Sampler;
|
|
4
|
+
use remoteprocess::Pid;
|
|
5
|
+
use std::env;
|
|
6
|
+
use std::ffi::CStr;
|
|
7
|
+
use std::os::raw::c_char;
|
|
8
|
+
|
|
9
|
+
use crate::backend::Rbspy;
|
|
10
|
+
use pyroscope::backend::{BackendConfig, BackendImpl, Report, StackFrame, Tag};
|
|
11
|
+
use pyroscope::pyroscope::PyroscopeAgentBuilder;
|
|
12
|
+
|
|
13
|
+
const LOG_TAG: &str = "Pyroscope::rbspy::ffi";
|
|
14
|
+
const RBSPY_NAME: &str = "rbspy";
|
|
15
|
+
const RBSPY_VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
16
|
+
|
|
17
|
+
fn transform_report_with_current_dir(report: Report) -> Report {
|
|
18
|
+
let cwd = env::current_dir().ok();
|
|
19
|
+
let cwd = cwd.as_deref().and_then(|p| p.to_str());
|
|
20
|
+
transform_report(report, cwd)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
pub fn transform_report(report: Report, cwd: Option<&str>) -> Report {
|
|
24
|
+
let data = report
|
|
25
|
+
.data
|
|
26
|
+
.iter()
|
|
27
|
+
.map(|(stacktrace, count)| {
|
|
28
|
+
let new_frames = stacktrace
|
|
29
|
+
.frames
|
|
30
|
+
.iter()
|
|
31
|
+
.map(|frame| {
|
|
32
|
+
let frame = frame.to_owned();
|
|
33
|
+
let mut s = frame.filename.unwrap();
|
|
34
|
+
let stripped = cwd
|
|
35
|
+
.and_then(|c| s.strip_prefix(c))
|
|
36
|
+
.and_then(|rest| rest.strip_prefix('/'));
|
|
37
|
+
if let Some(rest) = stripped {
|
|
38
|
+
s = rest.to_string();
|
|
39
|
+
} else if let Some(i) = s.find("/gems/") {
|
|
40
|
+
s = s[(i + 1)..].to_string();
|
|
41
|
+
} else if let Some(i) = s.find("/ruby/") {
|
|
42
|
+
s = s[(i + 6)..].to_string();
|
|
43
|
+
if let Some(i) = s.find('/') {
|
|
44
|
+
s = s[(i + 1)..].to_string();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// something
|
|
49
|
+
StackFrame::new(
|
|
50
|
+
frame.module,
|
|
51
|
+
frame.name,
|
|
52
|
+
Some(s.to_string()),
|
|
53
|
+
frame.relative_path,
|
|
54
|
+
frame.absolute_path,
|
|
55
|
+
frame.line,
|
|
56
|
+
)
|
|
57
|
+
})
|
|
58
|
+
.collect();
|
|
59
|
+
|
|
60
|
+
let mut mystack = stacktrace.to_owned();
|
|
61
|
+
|
|
62
|
+
mystack.frames = new_frames;
|
|
63
|
+
|
|
64
|
+
(mystack, count.to_owned())
|
|
65
|
+
})
|
|
66
|
+
.collect();
|
|
67
|
+
|
|
68
|
+
Report::new(data).metadata(report.metadata.clone())
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#[no_mangle]
|
|
72
|
+
pub extern "C" fn initialize_logging(logging_level: u32) -> bool {
|
|
73
|
+
// Force rustc to display the log messages in the console.
|
|
74
|
+
match logging_level {
|
|
75
|
+
50 => {
|
|
76
|
+
std::env::set_var("RUST_LOG", "error");
|
|
77
|
+
}
|
|
78
|
+
40 => {
|
|
79
|
+
std::env::set_var("RUST_LOG", "warn");
|
|
80
|
+
}
|
|
81
|
+
30 => {
|
|
82
|
+
std::env::set_var("RUST_LOG", "info");
|
|
83
|
+
}
|
|
84
|
+
20 => {
|
|
85
|
+
std::env::set_var("RUST_LOG", "debug");
|
|
86
|
+
}
|
|
87
|
+
10 => {
|
|
88
|
+
std::env::set_var("RUST_LOG", "trace");
|
|
89
|
+
}
|
|
90
|
+
_ => {
|
|
91
|
+
std::env::set_var("RUST_LOG", "debug");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Initialize the logger.
|
|
96
|
+
pretty_env_logger::init_timed();
|
|
97
|
+
|
|
98
|
+
true
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
#[no_mangle]
|
|
102
|
+
/// # Safety
|
|
103
|
+
/// All pointer arguments must be valid, non-null, null-terminated C strings.
|
|
104
|
+
pub unsafe extern "C" fn initialize_agent(
|
|
105
|
+
application_name: *const c_char,
|
|
106
|
+
server_address: *const c_char,
|
|
107
|
+
basic_auth_user: *const c_char,
|
|
108
|
+
basic_auth_password: *const c_char,
|
|
109
|
+
sample_rate: u32,
|
|
110
|
+
oncpu: bool,
|
|
111
|
+
report_pid: bool,
|
|
112
|
+
report_thread_id: bool,
|
|
113
|
+
tags: *const c_char,
|
|
114
|
+
tenant_id: *const c_char,
|
|
115
|
+
http_headers_json: *const c_char,
|
|
116
|
+
) -> bool {
|
|
117
|
+
let application_name = unsafe { CStr::from_ptr(application_name) }
|
|
118
|
+
.to_str()
|
|
119
|
+
.unwrap()
|
|
120
|
+
.to_string();
|
|
121
|
+
|
|
122
|
+
let server_address = unsafe { CStr::from_ptr(server_address) }
|
|
123
|
+
.to_str()
|
|
124
|
+
.unwrap()
|
|
125
|
+
.to_string();
|
|
126
|
+
|
|
127
|
+
let basic_auth_user = unsafe { CStr::from_ptr(basic_auth_user) }
|
|
128
|
+
.to_str()
|
|
129
|
+
.unwrap()
|
|
130
|
+
.to_string();
|
|
131
|
+
|
|
132
|
+
let basic_auth_password = unsafe { CStr::from_ptr(basic_auth_password) }
|
|
133
|
+
.to_str()
|
|
134
|
+
.unwrap()
|
|
135
|
+
.to_string();
|
|
136
|
+
|
|
137
|
+
let tags_string = unsafe { CStr::from_ptr(tags) }
|
|
138
|
+
.to_str()
|
|
139
|
+
.unwrap()
|
|
140
|
+
.to_string();
|
|
141
|
+
|
|
142
|
+
let tenant_id = unsafe { CStr::from_ptr(tenant_id) }
|
|
143
|
+
.to_str()
|
|
144
|
+
.unwrap()
|
|
145
|
+
.to_string();
|
|
146
|
+
|
|
147
|
+
let http_headers_json = unsafe { CStr::from_ptr(http_headers_json) }
|
|
148
|
+
.to_str()
|
|
149
|
+
.unwrap()
|
|
150
|
+
.to_string();
|
|
151
|
+
|
|
152
|
+
let pid = std::process::id();
|
|
153
|
+
|
|
154
|
+
let backend_config = BackendConfig {
|
|
155
|
+
report_thread_id,
|
|
156
|
+
report_thread_name: false,
|
|
157
|
+
report_pid,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
let sampler = Sampler::new(pid as Pid, sample_rate, false, None, false, None, oncpu);
|
|
161
|
+
|
|
162
|
+
let tags_ref = tags_string.as_str();
|
|
163
|
+
let tags = string_to_tags(tags_ref);
|
|
164
|
+
let rbspy = BackendImpl::new(Box::new(Rbspy::new(sampler, sample_rate, backend_config)));
|
|
165
|
+
|
|
166
|
+
let mut agent_builder = PyroscopeAgentBuilder::new(
|
|
167
|
+
server_address,
|
|
168
|
+
application_name,
|
|
169
|
+
sample_rate,
|
|
170
|
+
RBSPY_NAME,
|
|
171
|
+
RBSPY_VERSION,
|
|
172
|
+
rbspy,
|
|
173
|
+
)
|
|
174
|
+
.func(transform_report_with_current_dir)
|
|
175
|
+
.tags(tags);
|
|
176
|
+
|
|
177
|
+
if !basic_auth_user.is_empty() && !basic_auth_password.is_empty() {
|
|
178
|
+
agent_builder = agent_builder.basic_auth(basic_auth_user, basic_auth_password);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if !tenant_id.is_empty() {
|
|
182
|
+
agent_builder = agent_builder.tenant_id(tenant_id);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let http_headers = pyroscope::pyroscope::parse_http_headers_json(http_headers_json);
|
|
186
|
+
match http_headers {
|
|
187
|
+
Ok(http_headers) => {
|
|
188
|
+
agent_builder = agent_builder.http_headers(http_headers);
|
|
189
|
+
}
|
|
190
|
+
Err(e) => match e {
|
|
191
|
+
pyroscope::PyroscopeError::Json(e) => {
|
|
192
|
+
log::error!(target: LOG_TAG, "parse_http_headers_json error {}", e);
|
|
193
|
+
}
|
|
194
|
+
pyroscope::PyroscopeError::AdHoc(e) => {
|
|
195
|
+
log::error!(target: LOG_TAG, "parse_http_headers_json {}", e);
|
|
196
|
+
}
|
|
197
|
+
_ => {}
|
|
198
|
+
},
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
pyroscope::ffikit::run(agent_builder).is_ok()
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
#[no_mangle]
|
|
205
|
+
pub extern "C" fn drop_agent() -> bool {
|
|
206
|
+
pyroscope::ffikit::send(pyroscope::ffikit::Signal::Kill).is_ok()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
#[no_mangle]
|
|
210
|
+
/// # Safety
|
|
211
|
+
/// `key` and `value` must be valid, non-null, null-terminated C strings.
|
|
212
|
+
pub unsafe extern "C" fn add_thread_tag(key: *const c_char, value: *const c_char) -> bool {
|
|
213
|
+
let key = unsafe { CStr::from_ptr(key) }.to_str().unwrap().to_owned();
|
|
214
|
+
let value = unsafe { CStr::from_ptr(value) }
|
|
215
|
+
.to_str()
|
|
216
|
+
.unwrap()
|
|
217
|
+
.to_owned();
|
|
218
|
+
|
|
219
|
+
pyroscope::ffikit::send(pyroscope::ffikit::Signal::AddThreadTag(
|
|
220
|
+
backend::self_thread_id(),
|
|
221
|
+
Tag { key, value },
|
|
222
|
+
))
|
|
223
|
+
.is_ok()
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
#[no_mangle]
|
|
227
|
+
/// # Safety
|
|
228
|
+
/// `key` and `value` must be valid, non-null, null-terminated C strings.
|
|
229
|
+
pub unsafe extern "C" fn remove_thread_tag(key: *const c_char, value: *const c_char) -> bool {
|
|
230
|
+
let key = unsafe { CStr::from_ptr(key) }.to_str().unwrap().to_owned();
|
|
231
|
+
let value = unsafe { CStr::from_ptr(value) }
|
|
232
|
+
.to_str()
|
|
233
|
+
.unwrap()
|
|
234
|
+
.to_owned();
|
|
235
|
+
|
|
236
|
+
pyroscope::ffikit::send(pyroscope::ffikit::Signal::RemoveThreadTag(
|
|
237
|
+
backend::self_thread_id(),
|
|
238
|
+
Tag { key, value },
|
|
239
|
+
))
|
|
240
|
+
.is_ok()
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
fn string_to_tags(tags: &str) -> Vec<(&str, &str)> {
|
|
244
|
+
let mut tags_vec = Vec::new();
|
|
245
|
+
// check if string is empty
|
|
246
|
+
if tags.is_empty() {
|
|
247
|
+
return tags_vec;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
for tag in tags.split(',') {
|
|
251
|
+
let mut tag_split = tag.split('=');
|
|
252
|
+
let key = tag_split.next().unwrap();
|
|
253
|
+
let value = tag_split.next().unwrap();
|
|
254
|
+
tags_vec.push((key, value));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
tags_vec
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
#[cfg(test)]
|
|
261
|
+
mod tests {
|
|
262
|
+
use super::*;
|
|
263
|
+
use pyroscope::backend::{Report, StackFrame, StackTrace};
|
|
264
|
+
use std::collections::HashMap;
|
|
265
|
+
|
|
266
|
+
fn report_with_filename(filename: &str) -> Report {
|
|
267
|
+
let stacktrace = StackTrace {
|
|
268
|
+
frames: vec![StackFrame {
|
|
269
|
+
filename: Some(filename.to_string()),
|
|
270
|
+
..Default::default()
|
|
271
|
+
}],
|
|
272
|
+
..Default::default()
|
|
273
|
+
};
|
|
274
|
+
Report::new(HashMap::from([(stacktrace, 1)]))
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
fn transformed_filename(report: Report) -> String {
|
|
278
|
+
let (stacktrace, _) = report.data.iter().next().unwrap();
|
|
279
|
+
stacktrace.frames[0].filename.clone().unwrap()
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
#[test]
|
|
283
|
+
fn transform_report_does_not_panic_when_cwd_is_a_suffix_of_filename() {
|
|
284
|
+
let report = report_with_filename("bin/rails");
|
|
285
|
+
let out = transform_report(report, Some("/rails"));
|
|
286
|
+
assert_eq!(transformed_filename(out), "bin/rails");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
#[test]
|
|
290
|
+
fn transform_report_strips_cwd_prefix() {
|
|
291
|
+
let report = report_with_filename("/rails/app/models/user.rb");
|
|
292
|
+
let out = transform_report(report, Some("/rails"));
|
|
293
|
+
assert_eq!(transformed_filename(out), "app/models/user.rb");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
#[test]
|
|
297
|
+
fn transform_report_rewrites_gems_path() {
|
|
298
|
+
let report =
|
|
299
|
+
report_with_filename("/usr/local/bundle/gems/activerecord-7.0.0/lib/active_record.rb");
|
|
300
|
+
let out = transform_report(report, None);
|
|
301
|
+
assert_eq!(
|
|
302
|
+
transformed_filename(out),
|
|
303
|
+
"gems/activerecord-7.0.0/lib/active_record.rb",
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
#[test]
|
|
308
|
+
fn transform_report_rewrites_ruby_stdlib_path() {
|
|
309
|
+
let report = report_with_filename("/usr/local/lib/ruby/3.3.0/json/common.rb");
|
|
310
|
+
let out = transform_report(report, None);
|
|
311
|
+
assert_eq!(transformed_filename(out), "json/common.rb");
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
#[test]
|
|
315
|
+
fn transform_report_leaves_unrelated_filename_unchanged() {
|
|
316
|
+
let report = report_with_filename("/tmp/unrelated.rb");
|
|
317
|
+
let out = transform_report(report, Some("/app"));
|
|
318
|
+
assert_eq!(transformed_filename(out), "/tmp/unrelated.rb");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
#[test]
|
|
322
|
+
fn test_cargo_version_matches_ruby_version() {
|
|
323
|
+
let cargo_version = env!("CARGO_PKG_VERSION");
|
|
324
|
+
let version_rb = include_str!("../../../lib/pyroscope/version.rb");
|
|
325
|
+
let ruby_version = version_rb
|
|
326
|
+
.lines()
|
|
327
|
+
.find_map(|line| {
|
|
328
|
+
let line = line.trim();
|
|
329
|
+
if line.starts_with("VERSION") {
|
|
330
|
+
let start = line.find('\'')?;
|
|
331
|
+
let end = line.rfind('\'')?;
|
|
332
|
+
if start < end {
|
|
333
|
+
Some(&line[start + 1..end])
|
|
334
|
+
} else {
|
|
335
|
+
None
|
|
336
|
+
}
|
|
337
|
+
} else {
|
|
338
|
+
None
|
|
339
|
+
}
|
|
340
|
+
})
|
|
341
|
+
.expect("could not find VERSION in lib/pyroscope/version.rb");
|
|
342
|
+
|
|
343
|
+
assert_eq!(
|
|
344
|
+
cargo_version, ruby_version,
|
|
345
|
+
"Cargo.toml version ({cargo_version}) does not match lib/pyroscope/version.rb ({ruby_version})"
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
}
|
data/lib/pyroscope/version.rb
CHANGED
data/lib/rbspy/rbspy.so
CHANGED
|
Binary file
|
data/pyroscope.gemspec
CHANGED
|
@@ -23,13 +23,19 @@ Gem::Specification.new do |s|
|
|
|
23
23
|
"source_code_uri" => "https://github.com/grafana/pyroscope-ruby",
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
# TODO: s.files is incomplete — ext/rbspy/ sources are missing, so building
|
|
27
|
-
# the gem from source does not work. See https://github.com/grafana/pyroscope-ruby/issues/2
|
|
28
26
|
s.files = [
|
|
29
27
|
"Gemfile",
|
|
30
28
|
"Gemfile.lock",
|
|
31
29
|
"LICENSE",
|
|
32
30
|
"README.md",
|
|
31
|
+
"ext/rbspy/Cargo.lock",
|
|
32
|
+
"ext/rbspy/Cargo.toml",
|
|
33
|
+
"ext/rbspy/Rakefile",
|
|
34
|
+
"ext/rbspy/cbindgen.toml",
|
|
35
|
+
"ext/rbspy/extconf.rb",
|
|
36
|
+
"ext/rbspy/include/rbspy.h",
|
|
37
|
+
"ext/rbspy/src/backend.rs",
|
|
38
|
+
"ext/rbspy/src/lib.rs",
|
|
33
39
|
"lib/pyroscope.rb",
|
|
34
40
|
"lib/pyroscope/version.rb",
|
|
35
41
|
"pyroscope.gemspec",
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pyroscope
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.7
|
|
5
5
|
platform: aarch64-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Pyroscope Team
|
|
@@ -62,7 +62,14 @@ files:
|
|
|
62
62
|
- Gemfile.lock
|
|
63
63
|
- LICENSE
|
|
64
64
|
- README.md
|
|
65
|
+
- ext/rbspy/Cargo.lock
|
|
66
|
+
- ext/rbspy/Cargo.toml
|
|
67
|
+
- ext/rbspy/Rakefile
|
|
68
|
+
- ext/rbspy/cbindgen.toml
|
|
65
69
|
- ext/rbspy/extconf.rb
|
|
70
|
+
- ext/rbspy/include/rbspy.h
|
|
71
|
+
- ext/rbspy/src/backend.rs
|
|
72
|
+
- ext/rbspy/src/lib.rs
|
|
66
73
|
- lib/pyroscope.rb
|
|
67
74
|
- lib/pyroscope/version.rb
|
|
68
75
|
- lib/rbspy/rbspy.so
|