Skip to content

Commit

Permalink
Merge pull request #5 from omarabid/main
Browse files Browse the repository at this point in the history
docs and tests
  • Loading branch information
omarabid authored Jan 21, 2022
2 parents 1298cd5 + 90abfac commit 335196b
Show file tree
Hide file tree
Showing 13 changed files with 407 additions and 16 deletions.
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

0 comments on commit 335196b

Please sign in to comment.