Skip to content

Commit

Permalink
feat(profiling): Add support for Profiling (#479)
Browse files Browse the repository at this point in the history
* Add support for Profiling
  • Loading branch information
viglia committed Jul 1, 2022
1 parent 7062d8d commit 029c29f
Show file tree
Hide file tree
Showing 13 changed files with 413 additions and 18 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,18 @@
# Changelog

## Unreleased

**Breaking Changes**:

**Features**:

- Add support for Profiling feature. ([#479](https://github.com/getsentry/sentry-rust/pull/479))

**Internal**:

**Thank you**:


## 0.27.0

**Breaking Changes**:
Expand Down
10 changes: 9 additions & 1 deletion sentry-core/Cargo.toml
Expand Up @@ -26,14 +26,22 @@ client = ["rand"]
# and macros actually expand features (and extern crate) where they are used!
debug-logs = ["log_"]
test = ["client"]
profiling = ["pprof", "build_id", "uuid", "sys-info", "findshlibs"]

[dependencies]
log_ = { package = "log", version = "0.4.8", optional = true, features = ["std"] }
once_cell = "1"
rand = { version = "0.8.1", optional = true }
sentry-types = { version = "0.27.0", path = "../sentry-types" }
serde = { version = "1.0.104", features = ["derive"] }
serde_json = "1.0.46"
serde_json = { version = "1.0.46" }
uuid = { version = "1.0.0", features = ["v4", "serde"], optional = true }
sys-info = { version = "0.9.1", optional = true }
build_id = { version = "0.2.1", optional = true }
findshlibs = { version = "=0.10.2", optional = true }

[target.'cfg(not(target_os = "windows"))'.dependencies]
pprof = { version = "0.10.0", optional = true }

[dev-dependencies]
# Because we re-export all the public API in `sentry`, we actually run all the
Expand Down
16 changes: 3 additions & 13 deletions sentry-core/src/client.rs
Expand Up @@ -215,7 +215,7 @@ impl Client {
scope.update_session_from_event(&event);
}

if !self.sample_should_send() {
if !self.sample_should_send(self.options.sample_rate) {
None
} else {
Some(event)
Expand Down Expand Up @@ -338,19 +338,9 @@ impl Client {
}
}

fn sample_should_send(&self) -> bool {
let rate = self.options.sample_rate;
if rate >= 1.0 {
true
} else {
random::<f32>() <= rate
}
}

/// Returns a random boolean with a probability defined
/// by the [`ClientOptions`]'s `traces_sample_rate`
pub fn sample_traces_should_send(&self) -> bool {
let rate = self.options.traces_sample_rate;
/// by rate
pub fn sample_should_send(&self, rate: f32) -> bool {
if rate >= 1.0 {
true
} else {
Expand Down
12 changes: 12 additions & 0 deletions sentry-core/src/clientoptions.rs
Expand Up @@ -74,6 +74,14 @@ pub struct ClientOptions {
pub sample_rate: f32,
/// The sample rate for tracing transactions. (0.0 - 1.0, defaults to 0.0)
pub traces_sample_rate: f32,
/// Enables profiling
pub enable_profiling: bool,
/// The sample rate for profiling a transactions. (0.0 - 1.0, defaults to 0.0)
///
/// This is dependent on `traces_sample_rate`. The probability of sending a profile
/// is given by `traces_sample_rate * profiles_sample_rate`.
/// If a given transaction is not sent, then the profile won't be sent neither.
pub profiles_sample_rate: f32,
/// Maximum number of breadcrumbs. (defaults to 100)
pub max_breadcrumbs: usize,
/// Attaches stacktraces to messages.
Expand Down Expand Up @@ -183,6 +191,8 @@ impl fmt::Debug for ClientOptions {
.field("environment", &self.environment)
.field("sample_rate", &self.sample_rate)
.field("traces_sample_rate", &self.traces_sample_rate)
.field("enable_profiling", &self.enable_profiling)
.field("profiles_sample_rate", &self.profiles_sample_rate)
.field("max_breadcrumbs", &self.max_breadcrumbs)
.field("attach_stacktrace", &self.attach_stacktrace)
.field("send_default_pii", &self.send_default_pii)
Expand Down Expand Up @@ -215,6 +225,8 @@ impl Default for ClientOptions {
environment: None,
sample_rate: 1.0,
traces_sample_rate: 0.0,
enable_profiling: false,
profiles_sample_rate: 0.0,
max_breadcrumbs: 100,
attach_stacktrace: false,
send_default_pii: false,
Expand Down
3 changes: 3 additions & 0 deletions sentry-core/src/lib.rs
Expand Up @@ -92,6 +92,9 @@ pub use crate::client::Client;
#[cfg(feature = "test")]
pub mod test;

#[cfg(all(feature = "profiling", not(target_os = "windows")))]
mod profiling;

// public api from other crates
#[doc(inline)]
pub use sentry_types as types;
Expand Down
35 changes: 32 additions & 3 deletions sentry-core/src/performance.rs
@@ -1,6 +1,8 @@
use std::sync::Arc;
use std::sync::Mutex;

#[cfg(all(feature = "profiling", not(target_os = "windows")))]
use crate::profiling;
use crate::{protocol, Hub};

#[cfg(feature = "client")]
Expand Down Expand Up @@ -269,8 +271,10 @@ pub(crate) struct TransactionInner {
#[cfg(feature = "client")]
client: Option<Arc<Client>>,
sampled: bool,
context: protocol::TraceContext,
pub(crate) context: protocol::TraceContext,
pub(crate) transaction: Option<protocol::Transaction<'static>>,
#[cfg(all(feature = "profiling", not(target_os = "windows")))]
pub(crate) profiler_guard: Option<profiling::ProfilerGuard>,
}

type TransactionArc = Arc<Mutex<TransactionInner>>;
Expand All @@ -297,8 +301,9 @@ impl Transaction {

let (sampled, mut transaction) = match client.as_ref() {
Some(client) => (
ctx.sampled
.unwrap_or_else(|| client.sample_traces_should_send()),
ctx.sampled.unwrap_or_else(|| {
client.sample_should_send(client.options().traces_sample_rate)
}),
Some(protocol::Transaction {
name: Some(ctx.name),
..Default::default()
Expand All @@ -313,13 +318,23 @@ impl Transaction {
transaction = None;
client = None;
}
// if the transaction was sampled then a profile, linked to the transaction,
// might as well be sampled
#[cfg(all(feature = "profiling", not(target_os = "windows")))]
let profiler_guard = if sampled {
client.as_deref().and_then(profiling::start_profiling)
} else {
None
};

Self {
inner: Arc::new(Mutex::new(TransactionInner {
client,
sampled,
context,
transaction,
#[cfg(all(feature = "profiling", not(target_os = "windows")))]
profiler_guard,
})),
}
}
Expand All @@ -339,6 +354,8 @@ impl Transaction {
sampled,
context,
transaction: None,
#[cfg(all(feature = "profiling", not(target_os = "windows")))]
profiler_guard: None,
})),
}
}
Expand Down Expand Up @@ -404,9 +421,21 @@ impl Transaction {
transaction.environment = opts.environment.clone();
transaction.sdk = Some(std::borrow::Cow::Owned(client.sdk_info.clone()));

// if the profiler is running for the given transaction
// then call finish_profiling to return the profile
#[cfg(all(feature = "profiling", not(target_os = "windows")))]
let profile = inner.profiler_guard.take().and_then(|profiler_guard| {
profiling::finish_profiling(&transaction, profiler_guard, inner.context.trace_id)
});

let mut envelope = protocol::Envelope::new();
envelope.add_item(transaction);

#[cfg(all(feature = "profiling", not(target_os = "windows")))]
if let Some(profile) = profile {
envelope.add_item(profile);
}

client.send_envelope(envelope)
}
}
Expand Down

0 comments on commit 029c29f

Please sign in to comment.