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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/ext/rbspy/Cargo.lock +201 -292
- data/ext/rbspy/Cargo.toml +10 -3
- data/ext/rbspy/include/rbspy.h +2 -0
- data/ext/rbspy/src/backend/backend.rs +134 -0
- data/ext/rbspy/src/backend/mod.rs +8 -0
- data/ext/rbspy/src/backend/ruleset.rs +82 -0
- data/ext/rbspy/src/backend/tests.rs +378 -0
- data/ext/rbspy/src/backend/types.rs +303 -0
- data/ext/rbspy/src/encode/gen/google.rs +233 -0
- data/ext/rbspy/src/encode/gen/mod.rs +3 -0
- data/ext/rbspy/src/encode/gen/push.rs +31 -0
- data/ext/rbspy/src/encode/gen/types.rs +11 -0
- data/ext/rbspy/src/encode/mod.rs +2 -0
- data/ext/rbspy/src/encode/pprof.rs +159 -0
- data/ext/rbspy/src/error.rs +67 -0
- data/ext/rbspy/src/ffikit.rs +76 -0
- data/ext/rbspy/src/lib.rs +41 -15
- data/ext/rbspy/src/pyroscope.rs +726 -0
- data/ext/rbspy/src/{backend.rs → rbspy_backend.rs} +3 -3
- data/ext/rbspy/src/session.rs +275 -0
- data/ext/rbspy/src/timer/epoll.rs +268 -0
- data/ext/rbspy/src/timer/kqueue.rs +191 -0
- data/ext/rbspy/src/timer/mod.rs +38 -0
- data/ext/rbspy/src/timer/sleep.rs +114 -0
- data/ext/rbspy/src/utils.rs +141 -0
- data/lib/pyroscope/version.rb +1 -1
- data/lib/pyroscope.rb +11 -1
- data/pyroscope.gemspec +1 -3
- metadata +22 -2
data/ext/rbspy/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "ffiruby"
|
|
3
|
-
version = "1.0
|
|
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
|
-
|
|
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"
|
data/ext/rbspy/include/rbspy.h
CHANGED
|
@@ -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,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
|
+
}
|