pyroscope 1.0.9 → 1.1.0

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.
data/ext/rbspy/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "ffiruby"
3
- version = "1.0.9" # x-release-please-version
3
+ version = "1.1.0" # x-release-please-version
4
4
  edition = "2021"
5
5
  rust-version = "1.66"
6
6
 
@@ -9,10 +9,17 @@ name = "rbspy"
9
9
  crate-type = ["cdylib"]
10
10
 
11
11
  [dependencies]
12
- pyroscope = { version="2.0.0", default-features = false, features = ["native-tls"] }
13
- rbspy = { version = "0.48" }
12
+ rbspy = { version = "0.49" }
14
13
  remoteprocess = "0.5"
15
14
  pretty_env_logger = "0.5"
16
15
  log = "0.4"
17
16
  libc = "0.2"
18
17
  anyhow = "1.0"
18
+ thiserror = "2.0"
19
+ reqwest = { version = "0.13", default-features = false, features = ["blocking", "query", "native-tls"] }
20
+ uuid = { version = "1.20.0", features = ["v4"] }
21
+ url = "2.2.2"
22
+ libflate = "2.1.0"
23
+ prost = "0.14"
24
+ serde_json = "1.0.115"
25
+ lazy_static = "1.5.0"
@@ -24,6 +24,8 @@ bool initialize_agent(const char *application_name,
24
24
  bool oncpu,
25
25
  bool report_pid,
26
26
  bool report_thread_id,
27
+ const char *runtime_name,
28
+ const char *runtime_version,
27
29
  const char *tags,
28
30
  const char *tenant_id,
29
31
  const char *http_headers_json);
@@ -0,0 +1,134 @@
1
+ #![allow(clippy::module_inception)]
2
+
3
+ use crate::error::{PyroscopeError, Result};
4
+ use std::{
5
+ fmt::Debug,
6
+ sync::{Arc, Mutex},
7
+ };
8
+
9
+ use super::{ReportBatch, ThreadTag};
10
+
11
+ /// Backend Config
12
+ #[derive(Debug, Copy, Clone, Default)]
13
+ pub struct BackendConfig {
14
+ pub report_thread_id: bool,
15
+ pub report_thread_name: bool,
16
+ pub report_pid: bool,
17
+ }
18
+
19
+ /// Backend Trait
20
+ pub trait Backend: Send {
21
+ /// Initialize the backend.
22
+ fn initialize(&mut self) -> Result<()>;
23
+ /// Drop the backend.
24
+ fn shutdown(self: Box<Self>) -> Result<()>;
25
+ /// Generate profiling report
26
+ fn report(&mut self) -> Result<ReportBatch>;
27
+ fn add_tag(&self, tag: ThreadTag) -> Result<()>;
28
+ fn remove_tag(&self, tag: ThreadTag) -> Result<()>;
29
+ }
30
+
31
+ /// Marker struct for Empty BackendImpl
32
+ #[derive(Debug)]
33
+ pub struct BackendBare;
34
+
35
+ /// Marker struct for Uninitialized Backend
36
+ #[derive(Debug)]
37
+ pub struct BackendUninitialized;
38
+
39
+ /// Marker struct for Initialized Backend
40
+ #[derive(Debug)]
41
+ pub struct BackendReady;
42
+
43
+ /// Backend State Trait
44
+ pub trait BackendState {}
45
+ impl BackendState for BackendBare {}
46
+ impl BackendState for BackendUninitialized {}
47
+ impl BackendState for BackendReady {}
48
+
49
+ /// Backend Accessibility Trait
50
+ pub trait BackendAccessible: BackendState {}
51
+ impl BackendAccessible for BackendUninitialized {}
52
+ impl BackendAccessible for BackendReady {}
53
+
54
+ /// Precursor Backend Implementation
55
+ /// This struct is used to implement the Backend trait. It serves two purposes:
56
+ /// 1. It enforces state transitions using the Type System.
57
+ /// 2. It manages the lifetime of the backend through an Arc<Mutex<T>>.
58
+ pub struct BackendImpl<S: BackendState + ?Sized> {
59
+ /// Backend
60
+ pub backend: Arc<Mutex<Option<Box<dyn Backend>>>>,
61
+
62
+ /// Backend State
63
+ _state: std::marker::PhantomData<S>,
64
+ }
65
+
66
+ impl BackendImpl<BackendBare> {
67
+ /// Create a new BackendImpl instance
68
+ pub fn new(backend_box: Box<dyn Backend>) -> BackendImpl<BackendUninitialized> {
69
+ BackendImpl {
70
+ backend: Arc::new(Mutex::new(Some(backend_box))),
71
+ _state: std::marker::PhantomData,
72
+ }
73
+ }
74
+ }
75
+
76
+ impl BackendImpl<BackendUninitialized> {
77
+ /// Initialize the backend
78
+ pub fn initialize(self) -> Result<BackendImpl<BackendReady>> {
79
+ let backend = self.backend.clone();
80
+
81
+ // Initialize the backend
82
+ backend
83
+ .lock()?
84
+ .as_mut()
85
+ .ok_or(PyroscopeError::BackendImpl)?
86
+ .initialize()?;
87
+
88
+ // Transition to BackendReady
89
+ Ok(BackendImpl {
90
+ backend,
91
+ _state: std::marker::PhantomData,
92
+ })
93
+ }
94
+ }
95
+
96
+ impl<S: BackendAccessible> BackendImpl<S> {
97
+ pub fn add_tag(&self, tag: ThreadTag) -> Result<()> {
98
+ self.backend
99
+ .lock()?
100
+ .as_ref()
101
+ .ok_or(PyroscopeError::BackendImpl)?
102
+ .add_tag(tag)
103
+ }
104
+
105
+ pub fn remove_tag(&self, rule: ThreadTag) -> Result<()> {
106
+ self.backend
107
+ .lock()?
108
+ .as_ref()
109
+ .ok_or(PyroscopeError::BackendImpl)?
110
+ .remove_tag(rule)
111
+ }
112
+ }
113
+
114
+ impl BackendImpl<BackendReady> {
115
+ /// Shutdown the backend and destroy BackendImpl
116
+ pub fn shutdown(self) -> Result<()> {
117
+ self.backend
118
+ .lock()?
119
+ .take()
120
+ .ok_or(PyroscopeError::BackendImpl)?
121
+ .shutdown()?;
122
+
123
+ Ok(())
124
+ }
125
+
126
+ /// Generate profiling report
127
+ pub fn report(&mut self) -> Result<ReportBatch> {
128
+ self.backend
129
+ .lock()?
130
+ .as_mut()
131
+ .ok_or(PyroscopeError::BackendImpl)?
132
+ .report()
133
+ }
134
+ }
@@ -0,0 +1,8 @@
1
+ pub mod backend;
2
+ pub mod ruleset;
3
+ pub mod tests;
4
+ pub mod types;
5
+
6
+ pub use backend::*;
7
+ pub use ruleset::*;
8
+ pub use types::*;
@@ -0,0 +1,82 @@
1
+ use super::{StackTrace, Tag};
2
+ use crate::error::Result;
3
+ use std::collections::HashSet;
4
+ use std::sync::{Arc, Mutex};
5
+
6
+ #[derive(Debug, Eq, PartialEq, Hash, Clone)]
7
+ pub struct ThreadTag {
8
+ tid: crate::utils::ThreadId,
9
+ tag: Tag,
10
+ }
11
+
12
+ impl ThreadTag {
13
+ pub fn new(tid: crate::ThreadId, tag: Tag) -> Self {
14
+ Self { tid, tag }
15
+ }
16
+ }
17
+
18
+ #[derive(Debug, Default, Clone)]
19
+ pub struct ThreadTagsSet {
20
+ pub rules: Arc<Mutex<HashSet<ThreadTag>>>,
21
+ }
22
+
23
+ impl ThreadTagsSet {
24
+ pub fn new() -> Self {
25
+ Self {
26
+ rules: Arc::new(Mutex::new(HashSet::new())),
27
+ }
28
+ }
29
+
30
+ pub fn add(&self, rule: ThreadTag) -> Result<bool> {
31
+ let rules = self.rules.clone();
32
+
33
+ let insert = rules.lock()?.insert(rule);
34
+
35
+ Ok(insert)
36
+ }
37
+
38
+ pub fn remove(&self, rule: ThreadTag) -> Result<bool> {
39
+ let rules = self.rules.clone();
40
+
41
+ let remove = rules.lock()?.remove(&rule);
42
+
43
+ Ok(remove)
44
+ }
45
+
46
+ #[cfg(test)]
47
+ pub fn thread_tags(&self, tid: crate::ThreadId) -> Vec<Tag> {
48
+ let s = StackTrace {
49
+ pid: None,
50
+ thread_id: Some(tid.clone()),
51
+ thread_name: None,
52
+ frames: vec![],
53
+ metadata: Default::default(),
54
+ };
55
+ let tags: Vec<Tag> = s.add_tag_rules(self).metadata.tags.into_iter().collect();
56
+ tags
57
+ }
58
+ }
59
+
60
+ impl StackTrace {
61
+ pub fn add_tag_rules(self, other: &ThreadTagsSet) -> Self {
62
+ let mut metadata = self.metadata;
63
+
64
+ if let Ok(rules) = other.rules.lock() {
65
+ rules.iter().for_each(|rule| {
66
+ if let Some(stack_thread_id) = &self.thread_id {
67
+ if rule.tid == *stack_thread_id {
68
+ metadata.add_tag(rule.tag.clone());
69
+ }
70
+ }
71
+ })
72
+ }
73
+
74
+ Self {
75
+ pid: self.pid,
76
+ thread_id: self.thread_id,
77
+ thread_name: self.thread_name,
78
+ frames: self.frames,
79
+ metadata,
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,378 @@
1
+ #[cfg(test)]
2
+ use crate::backend::{
3
+ BackendConfig, Report, StackBuffer, StackFrame, StackTrace, Tag, ThreadTag, ThreadTagsSet,
4
+ };
5
+ #[cfg(test)]
6
+ use std::collections::{HashMap, HashSet};
7
+
8
+ #[test]
9
+ fn test_stack_frame_display() {
10
+ let frame = StackFrame::new(
11
+ Some("module".to_string()),
12
+ Some("name".to_string()),
13
+ Some("filename".to_string()),
14
+ Some("relative_path".to_string()),
15
+ Some("absolute_path".to_string()),
16
+ Some(1),
17
+ );
18
+
19
+ assert_eq!(format!("{frame}"), "filename:1 - name");
20
+ }
21
+
22
+ #[test]
23
+ fn test_stack_trace_display() {
24
+ let frames = vec![
25
+ StackFrame::new(
26
+ Some("module".to_string()),
27
+ Some("name".to_string()),
28
+ Some("filename".to_string()),
29
+ Some("relative_path".to_string()),
30
+ Some("absolute_path".to_string()),
31
+ Some(1),
32
+ ),
33
+ StackFrame::new(
34
+ Some("module".to_string()),
35
+ Some("name".to_string()),
36
+ Some("filename".to_string()),
37
+ Some("relative_path".to_string()),
38
+ Some("absolute_path".to_string()),
39
+ Some(2),
40
+ ),
41
+ ];
42
+
43
+ let stack_trace = StackTrace::new(&BackendConfig::default(), None, None, None, frames);
44
+
45
+ assert_eq!(
46
+ format!("{stack_trace}"),
47
+ "filename:2 - name;filename:1 - name"
48
+ );
49
+ }
50
+
51
+ #[test]
52
+ fn test_report_record() {
53
+ let mut report = Report::new(HashMap::new());
54
+
55
+ let stack_trace = StackTrace::new(&BackendConfig::default(), None, None, None, vec![]);
56
+
57
+ report.record(stack_trace);
58
+ assert_eq!(report.data.len(), 1);
59
+ }
60
+
61
+ #[test]
62
+ fn test_tag_new() {
63
+ let tag = Tag::new("key".to_string(), "value".to_string());
64
+
65
+ assert_eq!(tag.key, "key");
66
+ assert_eq!(tag.value, "value");
67
+ }
68
+
69
+ #[test]
70
+ fn test_rule_new() {
71
+ let tid = crate::ThreadId::pthread_self();
72
+ let rule = ThreadTag::new(
73
+ tid.clone(),
74
+ Tag::new("key".to_string(), "value".to_string()),
75
+ );
76
+
77
+ assert_eq!(
78
+ rule,
79
+ ThreadTag::new(tid, Tag::new("key".to_string(), "value".to_string()))
80
+ );
81
+ }
82
+
83
+ #[cfg(test)]
84
+ fn test_thread_id(v: u64) -> crate::utils::ThreadId {
85
+ (v as libc::pthread_t).into()
86
+ }
87
+
88
+ #[test]
89
+ fn test_ruleset_new() {
90
+ let ruleset = ThreadTagsSet::new();
91
+
92
+ assert_eq!(ruleset.rules.lock().unwrap().len(), 0);
93
+ }
94
+
95
+ #[test]
96
+ fn test_ruleset_add_rule() {
97
+ let tid = crate::ThreadId::pthread_self();
98
+ let ruleset = ThreadTagsSet::new();
99
+
100
+ let rule = ThreadTag::new(tid, Tag::new("key".to_string(), "value".to_string()));
101
+
102
+ ruleset.add(rule).unwrap();
103
+
104
+ assert_eq!(ruleset.rules.lock().unwrap().len(), 1);
105
+ }
106
+
107
+ #[test]
108
+ fn test_ruleset_remove_rule() {
109
+ let tid = crate::ThreadId::pthread_self();
110
+ let ruleset = ThreadTagsSet::new();
111
+
112
+ let add_rule = ThreadTag::new(
113
+ tid.clone(),
114
+ Tag::new("key".to_string(), "value".to_string()),
115
+ );
116
+
117
+ ruleset.add(add_rule).unwrap();
118
+
119
+ assert_eq!(ruleset.rules.lock().unwrap().len(), 1);
120
+
121
+ let remove_rule = ThreadTag::new(tid, Tag::new("key".to_string(), "value".to_string()));
122
+
123
+ ruleset.remove(remove_rule).unwrap();
124
+
125
+ assert_eq!(ruleset.rules.lock().unwrap().len(), 0);
126
+ }
127
+
128
+ #[test]
129
+ fn test_ruleset() {
130
+ let ruleset = ThreadTagsSet::new();
131
+
132
+ ruleset
133
+ .add(ThreadTag::new(
134
+ test_thread_id(1),
135
+ Tag::new("key1".to_string(), "value".to_string()),
136
+ ))
137
+ .unwrap();
138
+
139
+ ruleset
140
+ .add(ThreadTag::new(
141
+ test_thread_id(2),
142
+ Tag::new("key1".to_string(), "value".to_string()),
143
+ ))
144
+ .unwrap();
145
+
146
+ ruleset
147
+ .add(ThreadTag::new(
148
+ test_thread_id(3),
149
+ Tag::new("key1".to_string(), "value".to_string()),
150
+ ))
151
+ .unwrap();
152
+
153
+ // Remove ThreadTag number 2
154
+ ruleset
155
+ .remove(ThreadTag::new(
156
+ test_thread_id(2),
157
+ Tag::new("key1".to_string(), "value".to_string()),
158
+ ))
159
+ .unwrap();
160
+
161
+ // Verify ThreadTag number 2 is removed from the ruleset Vector
162
+ assert_eq!(
163
+ ruleset.rules.lock().unwrap().clone(),
164
+ HashSet::from([
165
+ ThreadTag::new(
166
+ test_thread_id(1),
167
+ Tag::new("key1".to_string(), "value".to_string(),)
168
+ ),
169
+ ThreadTag::new(
170
+ test_thread_id(3),
171
+ Tag::new("key1".to_string(), "value".to_string(),)
172
+ )
173
+ ])
174
+ );
175
+ }
176
+
177
+ #[test]
178
+ fn test_ruleset_duplicates() {
179
+ let ruleset = ThreadTagsSet::new();
180
+
181
+ let tid = crate::ThreadId::pthread_self();
182
+ ruleset
183
+ .add(ThreadTag::new(
184
+ tid.clone(),
185
+ Tag::new("key1".to_string(), "value".to_string()),
186
+ ))
187
+ .unwrap();
188
+
189
+ ruleset
190
+ .add(ThreadTag::new(
191
+ tid.clone(),
192
+ Tag::new("key1".to_string(), "value".to_string()),
193
+ ))
194
+ .unwrap();
195
+
196
+ assert_eq!(
197
+ ruleset.thread_tags(tid),
198
+ vec![Tag::new("key1".to_string(), "value".to_string())]
199
+ );
200
+ }
201
+
202
+ #[test]
203
+ fn test_ruleset_remove_nonexistent() {
204
+ let ruleset = ThreadTagsSet::new();
205
+
206
+ let tid = crate::ThreadId::pthread_self();
207
+ ruleset
208
+ .add(ThreadTag::new(
209
+ tid.clone(),
210
+ Tag::new("key1".to_string(), "value".to_string()),
211
+ ))
212
+ .unwrap();
213
+
214
+ ruleset
215
+ .remove(ThreadTag::new(
216
+ tid.clone(),
217
+ Tag::new("key2".to_string(), "value".to_string()),
218
+ ))
219
+ .unwrap();
220
+
221
+ assert_eq!(
222
+ ruleset.thread_tags(tid),
223
+ vec![Tag::new("key1".to_string(), "value".to_string())]
224
+ );
225
+ }
226
+
227
+ #[test]
228
+ fn test_stacktrace_add() {
229
+ let ruleset = ThreadTagsSet::new();
230
+
231
+ ruleset
232
+ .add(ThreadTag::new(
233
+ test_thread_id(55),
234
+ Tag::new("keyA".to_string(), "valueA".to_string()),
235
+ ))
236
+ .unwrap();
237
+
238
+ ruleset
239
+ .add(ThreadTag::new(
240
+ test_thread_id(100),
241
+ Tag::new("keyB".to_string(), "valueB".to_string()),
242
+ ))
243
+ .unwrap();
244
+
245
+ let backend_config = BackendConfig {
246
+ report_pid: true,
247
+ report_thread_id: true,
248
+ report_thread_name: true,
249
+ };
250
+
251
+ let stacktrace = StackTrace::new(
252
+ &backend_config,
253
+ Some(1),
254
+ Some(test_thread_id(55)),
255
+ Some("thread_name".to_string()),
256
+ vec![crate::backend::StackFrame::new(
257
+ Some("file1".to_string()),
258
+ Some("function1".to_string()),
259
+ Some("file1".to_string()),
260
+ Some("file1".to_string()),
261
+ Some("file1".to_string()),
262
+ Some(1),
263
+ )],
264
+ );
265
+
266
+ // assert initial metadata of the stacktrace
267
+ let mut initial_metadata = crate::backend::Metadata::default();
268
+ initial_metadata.add_tag(Tag::new("pid".to_string(), "1".to_string()));
269
+ initial_metadata.add_tag(Tag::new("thread_id".to_string(), "55".to_string()));
270
+ initial_metadata.add_tag(Tag::new(
271
+ "thread_name".to_string(),
272
+ "thread_name".to_string(),
273
+ ));
274
+
275
+ assert_eq!(stacktrace.metadata, initial_metadata);
276
+
277
+ // Add the Stacktrace to the Ruleset
278
+ let applied_stacktrace = stacktrace.add_tag_rules(&ruleset);
279
+
280
+ initial_metadata.add_tag(Tag::new("keyA".to_string(), "valueA".to_string()));
281
+
282
+ // assert that the metadata of the stacktrace is updated
283
+ assert_eq!(applied_stacktrace.metadata, initial_metadata);
284
+
285
+ // Re-apply the Ruleset
286
+ let re_applied_stacktrace = applied_stacktrace.add_tag_rules(&ruleset);
287
+
288
+ // assert that the metadata of the stacktrace is the same
289
+ assert_eq!(re_applied_stacktrace.metadata, initial_metadata);
290
+ }
291
+
292
+ #[test]
293
+ fn test_stackbuffer_record() {
294
+ let mut buffer = StackBuffer::new(HashMap::new());
295
+ let stack_trace = StackTrace::new(
296
+ &BackendConfig::default(),
297
+ None,
298
+ None,
299
+ None,
300
+ vec![StackFrame::new(
301
+ None,
302
+ Some("test_record".to_string()),
303
+ None,
304
+ None,
305
+ None,
306
+ None,
307
+ )],
308
+ );
309
+ // First record
310
+ buffer.record(stack_trace.clone()).unwrap();
311
+ assert_eq!(buffer.data.len(), 1);
312
+ assert_eq!(buffer.data[&stack_trace], 1);
313
+
314
+ // Second record
315
+ buffer.record(stack_trace.clone()).unwrap();
316
+ assert_eq!(buffer.data.len(), 1);
317
+ assert_eq!(buffer.data[&stack_trace], 2);
318
+ }
319
+
320
+ #[test]
321
+ fn test_stackbuffer_record_with_count() {
322
+ let mut buffer = StackBuffer::new(HashMap::new());
323
+ let stack_trace = StackTrace::new(
324
+ &BackendConfig::default(),
325
+ None,
326
+ None,
327
+ None,
328
+ vec![StackFrame::new(
329
+ None,
330
+ Some("test_record".to_string()),
331
+ None,
332
+ None,
333
+ None,
334
+ None,
335
+ )],
336
+ );
337
+ // First record
338
+ buffer.record_with_count(stack_trace.clone(), 1).unwrap();
339
+ assert_eq!(buffer.data.len(), 1);
340
+ assert_eq!(buffer.data[&stack_trace], 1);
341
+
342
+ // Second record
343
+ buffer.record_with_count(stack_trace.clone(), 2).unwrap();
344
+ assert_eq!(buffer.data.len(), 1);
345
+ assert_eq!(buffer.data[&stack_trace], 3);
346
+ }
347
+
348
+ #[test]
349
+ fn test_stackbuffer_clear() {
350
+ let mut buffer = StackBuffer::new(HashMap::new());
351
+ let stack_trace = StackTrace::new(
352
+ &BackendConfig::default(),
353
+ None,
354
+ None,
355
+ None,
356
+ vec![StackFrame::new(
357
+ None,
358
+ Some("test_record".to_string()),
359
+ None,
360
+ None,
361
+ None,
362
+ None,
363
+ )],
364
+ );
365
+ // First record
366
+ buffer.record(stack_trace.clone()).unwrap();
367
+ assert_eq!(buffer.data.len(), 1);
368
+ assert_eq!(buffer.data[&stack_trace], 1);
369
+
370
+ // Second record
371
+ buffer.record(stack_trace.clone()).unwrap();
372
+ assert_eq!(buffer.data.len(), 1);
373
+ assert_eq!(buffer.data[&stack_trace], 2);
374
+
375
+ // Clear
376
+ buffer.clear();
377
+ assert_eq!(buffer.data.len(), 0);
378
+ }