Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/backends/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ use crate::Result;
use std::fmt::Debug;

/// Backend State
#[derive(Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum State {
/// Backend is uninitialized.
Uninitialized,
/// Backend is ready to be used.
Ready,
/// Backend is running.
Running,
}

Expand All @@ -24,10 +27,15 @@ impl Default for State {

/// Backend Trait
pub trait Backend: Send + Debug {
/// Get the backend state.
fn get_state(&self) -> State;
/// Initialize the backend.
fn initialize(&mut self, sample_rate: i32) -> Result<()>;
/// Start the backend.
fn start(&mut self) -> Result<()>;
/// Stop the backend.
fn stop(&mut self) -> Result<()>;
/// Generate profiling report
fn report(&mut self) -> Result<Vec<u8>>;
}

Expand Down
2 changes: 1 addition & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use thiserror::Error;
pub type Result<T> = std::result::Result<T, PyroscopeError>;

/// Error type of Pyroscope
#[derive(Error, Debug)]
#[derive(Error, Debug, PartialEq)]
pub struct PyroscopeError {
pub msg: String,
}
Expand Down
124 changes: 114 additions & 10 deletions src/pyroscope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@ use crate::session::SessionManager;
use crate::session::SessionSignal;
use crate::timer::Timer;

/// Represent PyroscopeAgent Configuration
/// Pyroscope Agent Configuration. This is the configuration that is passed to the agent.
/// # Example
/// ```
/// use pyroscope::pyroscope::PyroscopeConfig;
/// let config = PyroscopeConfig::new("http://localhost:8080", "my-app");
/// ```
#[derive(Clone, Debug)]
pub struct PyroscopeConfig {
/// Pyroscope Server Address
pub url: String,
/// Application Name
pub application_name: String,
/// Tags
pub tags: HashMap<String, String>,
/// Sample rate used in Hz
pub sample_rate: i32,
Expand All @@ -37,7 +44,11 @@ pub struct PyroscopeConfig {

impl PyroscopeConfig {
/// Create a new PyroscopeConfig object. url and application_name are required.
/// tags and sample_rate are optional.
/// tags and sample_rate are optional. If sample_rate is not specified, it will default to 100.
/// # Example
/// ```ignore
/// let config = PyroscopeConfig::new("http://localhost:8080", "my-app");
/// ```
pub fn new<S: AsRef<str>>(url: S, application_name: S) -> Self {
Self {
url: url.as_ref().to_owned(),
Expand All @@ -48,14 +59,27 @@ impl PyroscopeConfig {
}

/// Set the Sample rate
/// # Example
/// ```ignore
/// let mut config = PyroscopeConfig::new("http://localhost:8080", "my-app");
/// config.set_sample_rate(10)
/// .unwrap();
/// ```
pub fn sample_rate(self, sample_rate: i32) -> Self {
Self {
sample_rate,
..self
}
}

/// Set Tags
/// Set the tags
/// # Example
/// ```ignore
/// use pyroscope::pyroscope::PyroscopeConfig;
/// let config = PyroscopeConfig::new("http://localhost:8080", "my-app")
/// .tags(vec![("env", "dev")])
/// .unwrap();
/// ```
pub fn tags(self, tags: &[(&str, &str)]) -> Self {
// Convert &[(&str, &str)] to HashMap(String, String)
let tags_hashmap: HashMap<String, String> = tags
Expand All @@ -76,6 +100,13 @@ impl PyroscopeConfig {
///
/// Alternatively, you can use PyroscopeAgent::build() which is a short-hand
/// for calling PyroscopeAgentBuilder::new()
///
/// # Example
/// ```ignore
/// use pyroscope::pyroscope::PyroscopeAgentBuilder;
/// let builder = PyroscopeAgentBuilder::new("http://localhost:8080", "my-app");
/// let agent = builder.build().unwrap();
/// ```
pub struct PyroscopeAgentBuilder {
/// Profiler backend
backend: Arc<Mutex<dyn Backend>>,
Expand All @@ -84,8 +115,13 @@ pub struct PyroscopeAgentBuilder {
}

impl PyroscopeAgentBuilder {
/// Create a new PyroscopeConfig object. url and application_name are required.
/// Create a new PyroscopeAgentBuilder object. url and application_name are required.
/// tags and sample_rate are optional.
///
/// # Example
/// ```ignore
/// let builder = PyroscopeAgentBuilder::new("http://localhost:8080", "my-app");
/// ```
pub fn new<S: AsRef<str>>(url: S, application_name: S) -> Self {
Self {
backend: Arc::new(Mutex::new(Pprof::default())), // Default Backend
Expand All @@ -94,6 +130,13 @@ impl PyroscopeAgentBuilder {
}

/// Set the agent backend. Default is pprof.
/// # Example
/// ```ignore
/// let builder = PyroscopeAgentBuilder::new("http://localhost:8080", "my-app")
/// .backend(Pprof::default())
/// .build()
/// .unwrap();
/// ```
pub fn backend<T: 'static>(self, backend: T) -> Self
where
T: Backend,
Expand All @@ -105,6 +148,13 @@ impl PyroscopeAgentBuilder {
}

/// Set the Sample rate. Default value is 100.
/// # Example
/// ```ignore
/// let builder = PyroscopeAgentBuilder::new("http://localhost:8080", "my-app")
/// .sample_rate(99)
/// .build()
/// .unwrap();
/// ```
pub fn sample_rate(self, sample_rate: i32) -> Self {
Self {
config: self.config.sample_rate(sample_rate),
Expand All @@ -113,6 +163,13 @@ impl PyroscopeAgentBuilder {
}

/// Set tags. Default is empty.
/// # Example
/// ```ignore
/// let builder = PyroscopeAgentBuilder::new("http://localhost:8080", "my-app")
/// .tags(vec![("env", "dev")])
/// .build()
/// .unwrap();
/// ```
pub fn tags(self, tags: &[(&str, &str)]) -> Self {
Self {
config: self.config.tags(tags),
Expand Down Expand Up @@ -148,17 +205,18 @@ impl PyroscopeAgentBuilder {
}
}

/// PyroscopeAgent
/// PyroscopeAgent is the main object of the library. It is used to start and stop the profiler, schedule the timer, and send the profiler data to the server.
#[derive(Debug)]
pub struct PyroscopeAgent {
pub backend: Arc<Mutex<dyn Backend>>,
timer: Timer,
session_manager: SessionManager,
tx: Option<Sender<u64>>,
handle: Option<JoinHandle<Result<()>>>,
running: Arc<(Mutex<bool>, Condvar)>,

// Session Data
/// Profiler backend
pub backend: Arc<Mutex<dyn Backend>>,
/// Configuration Object
pub config: PyroscopeConfig,
}

Expand Down Expand Up @@ -192,13 +250,22 @@ impl Drop for PyroscopeAgent {
}

impl PyroscopeAgent {
/// Short-hand for PyroscopeAgentBuilder::build()
/// Short-hand for PyroscopeAgentBuilder::build(). This is a convenience method.
/// # Example
/// ```ignore
/// let agent = PyroscopeAgent::builder("http://localhost:8080", "my-app").build().unwrap();
/// ```
pub fn builder<S: AsRef<str>>(url: S, application_name: S) -> PyroscopeAgentBuilder {
// Build PyroscopeAgent
PyroscopeAgentBuilder::new(url, application_name)
}

/// Start profiling and sending data. The agent will keep running until stopped.
/// Start profiling and sending data. The agent will keep running until stopped. The agent will send data to the server every 10s secondy.
/// # Example
/// ```ignore
/// let agent = PyroscopeAgent::builder("http://localhost:8080", "my-app").build().unwrap();
/// agent.start().unwrap();
/// ```
pub fn start(&mut self) -> Result<()> {
log::debug!("PyroscopeAgent - Starting");

Expand Down Expand Up @@ -258,7 +325,14 @@ impl PyroscopeAgent {
Ok(())
}

/// Stop the agent.
/// Stop the agent. The agent will stop profiling and send a last report to the server.
/// # Example
/// ```ignore
/// let agent = PyroscopeAgent::builder("http://localhost:8080", "my-app").build().unwrap();
/// agent.start().unwrap();
/// // Expensive operation
/// agent.stop().unwrap();
/// ```
pub fn stop(&mut self) -> Result<()> {
log::debug!("PyroscopeAgent - Stopping");
// get tx and send termination signal
Expand All @@ -278,8 +352,22 @@ impl PyroscopeAgent {
}

/// Add tags. This will restart the agent.
/// # Example
/// ```ignore
/// let agent = PyroscopeAgent::builder("http://localhost:8080", "my-app").build().unwrap();
/// agent.start().unwrap();
/// // Expensive operation
/// agent.add_tags(vec!["tag", "value"]).unwrap();
/// // Tagged operation
/// agent.stop().unwrap();
/// ```
pub fn add_tags(&mut self, tags: &[(&str, &str)]) -> Result<()> {
log::debug!("PyroscopeAgent - Adding tags");
// Check that tags are not empty
if tags.is_empty() {
return Ok(());
}

// Stop Agent
self.stop()?;

Expand All @@ -300,8 +388,24 @@ impl PyroscopeAgent {
}

/// Remove tags. This will restart the agent.
/// # Example
/// ```ignore
/// let agent = PyroscopeAgent::builder("http://localhost:8080", "my-app")
/// .tags(vec![("tag", "value")])
/// .build().unwrap();
/// agent.start().unwrap();
/// // Expensive operation
/// agent.remove_tags(vec!["tag"]).unwrap();
/// // Un-Tagged operation
/// agent.stop().unwrap();
pub fn remove_tags(&mut self, tags: &[&str]) -> Result<()> {
log::debug!("PyroscopeAgent - Removing tags");

// Check that tags are not empty
if tags.is_empty() {
return Ok(());
}

// Stop Agent
self.stop()?;

Expand Down
27 changes: 26 additions & 1 deletion src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,22 @@ use crate::utils::merge_tags_with_app_name;
use crate::Result;

/// Session Signal
///
/// This enum is used to send data to the session thread. It can also kill the session thread.
#[derive(Debug)]
pub enum SessionSignal {
/// Send session data to the session thread.
Session(Session),
/// Kill the session thread.
Kill,
}

/// SessionManager
/// Manage sessions and send data to the server.
#[derive(Debug)]
pub struct SessionManager {
/// The SessionManager thread.
pub handle: Option<JoinHandle<Result<()>>>,
/// Channel to send data to the SessionManager thread.
pub tx: SyncSender<SessionSignal>,
}

Expand Down Expand Up @@ -71,6 +77,8 @@ impl SessionManager {
}

/// Pyroscope Session
///
/// Used to contain the session data, and send it to the server.
#[derive(Clone, Debug)]
pub struct Session {
pub config: PyroscopeConfig,
Expand All @@ -80,6 +88,14 @@ pub struct Session {
}

impl Session {
/// Create a new Session
/// # Example
/// ```ignore
/// let config = PyroscopeConfig::new("https://localhost:8080", "my-app");
/// let report = vec![1, 2, 3];
/// let until = 154065120;
/// let session = Session::new(until, config, report).unwrap();
/// ```
pub fn new(mut until: u64, config: PyroscopeConfig, report: Vec<u8>) -> Result<Self> {
log::info!("Session - Creating Session");
// Session interrupted (0 signal), determine the time
Expand All @@ -103,6 +119,15 @@ impl Session {
})
}

/// Send the session to the server and consumes the session object.
/// # Example
/// ```ignore
/// let config = PyroscopeConfig::new("https://localhost:8080", "my-app");
/// let report = vec![1, 2, 3];
/// let until = 154065120;
/// let session = Session::new(until, config, report).unwrap();
/// session.send().unwrap();
/// ```
pub fn send(self) -> Result<()> {
log::info!("Session - Sending Session");

Expand Down
10 changes: 10 additions & 0 deletions src/timer/epoll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ use std::sync::{
};
use std::{thread, thread::JoinHandle};

/// A thread that sends a notification every 10th second
///
/// Timer will send an event to attached listeners (mpsc::Sender) every 10th
/// second (...10, ...20, ...)
///
/// The Timer thread will run continously until all Senders are dropped.
/// The Timer thread will be joined when all Senders are dropped.

#[derive(Debug, Default)]
pub struct Timer {
/// A vector to store listeners (mpsc::Sender)
Expand All @@ -23,6 +31,7 @@ pub struct Timer {
}

impl Timer {
/// Initialize Timer and run a thread to send events to attached listeners
pub fn initialize(self) -> Result<Self> {
let txs = Arc::clone(&self.txs);

Expand Down Expand Up @@ -136,6 +145,7 @@ impl Timer {
Ok(epoll_fd)
}

/// Wait for an event on the epoll file descriptor
fn epoll_wait(timer_fd: libc::c_int, epoll_fd: libc::c_int) -> Result<()> {
// vector to store events
let mut events = Vec::with_capacity(1);
Expand Down
Loading