1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#![warn(missing_docs)]
#![warn(missing_debug_implementations)]
//! A tiny library to measure resource usage of the process it's used in.
//! Currently it measures:
//!
//! * Memory Usage
//! * CPU Usage with breakdown by each thread
//! * Disk Usage
//!
//! More metrics might be added later. Currently, library supports only linux,
//! but pull requests for other platforms are welcome.
//!
//! # Example
//!
//! ```rust,no_run
//!
//! # use std::io::{Write, stderr};
//! # use std::time::Duration;
//! # use std::thread::sleep;
//! # use std::collections::BTreeMap;
//!
//! fn main() {
//!    let mut meter = self_meter::Meter::new(Duration::new(1, 0)).unwrap();
//!    meter.track_current_thread("main");
//!    loop {
//!        meter.scan()
//!            .map_err(|e| writeln!(&mut stderr(), "Scan error: {}", e)).ok();
//!        println!("Report: {:#?}", meter.report());
//!        println!("Threads: {:#?}",
//!            meter.thread_report().map(|x| x.collect::<BTreeMap<_,_>>()));
//!        // Put your task here
//!        // ...
//!        //
//!        sleep(Duration::new(1, 0));
//!    }
//! }
//! ```
extern crate libc;
extern crate num_cpus;
extern crate serde;

#[macro_use] extern crate quick_error;
#[macro_use] extern crate serde_derive;

use std::fs::File;
use std::time::{SystemTime, Instant, Duration};
use std::collections::{VecDeque, HashMap};

mod meter;
mod scan;
mod error;
mod report;
mod serialize;
mod debug;

pub use error::Error;
pub use report::ThreadReportIter;
/// A Pid type used to identify processes and threads
pub type Pid = u32;

struct ThreadInfo {
    user_time: u64,
    system_time: u64,
    child_user_time: u64,
    child_system_time: u64,
}

struct Snapshot {
    timestamp: SystemTime,
    instant: Instant,
    /// System uptime in centisecs
    uptime: u64,
    /// System idle time in centisecs
    idle_time: u64,
    process: ThreadInfo,
    memory_rss: u64,
    memory_virtual: u64,
    memory_virtual_peak: u64,
    memory_swap: u64,
    read_bytes: u64,
    write_bytes: u64,
    read_ops: u64,
    write_ops: u64,
    read_disk_bytes: u64,
    write_disk_bytes: u64,
    write_cancelled_bytes: u64,
    threads: HashMap<Pid, ThreadInfo>,
}


/// CPU usage of a single thread
#[derive(Debug)]
pub struct ThreadUsage {
    /// Thread's own CPU usage. 100% is a single core
    pub cpu_usage: f32,
    /// Thread's CPU usage with its awaited children. 100% is a single core
    pub cpu_usage_with_children: f32,
}

/// Report returned by `Meter::report`
///
/// Note: this structure implements `serde::Serialize`, and all timestamps and
/// durations are stored as integers in milliseconds.
#[derive(Debug, Serialize)]
pub struct Report {
    /// Timestamp
    #[serde(serialize_with="serialize::serialize_timestamp")]
    pub timestamp: SystemTime,

    /// The interval time this data has averaged over in milliseconds
    #[serde(serialize_with="serialize::serialize_duration")]
    pub duration: Duration,

    /// Start time
    #[serde(serialize_with="serialize::serialize_timestamp")]
    pub start_time: SystemTime,

    /// The uptime of the system
    ///
    /// Note this value can be smaller than time since `start_time`
    /// because this value doesn't include time when system was sleeping
    #[serde(serialize_with="serialize::serialize_duration")]
    pub system_uptime: Duration,
    /// Whole system CPU usage. 100% is all cores
    pub global_cpu_usage: f32,
    /// Process' own CPU usage. 100% is a single core
    pub process_cpu_usage: f32,
    /// Process' CPU usage with its awaited children. 100% is a single core
    pub gross_cpu_usage: f32,
    /// Process' memory usage
    pub memory_rss: u64,
    /// Process' virtual memory usage
    pub memory_virtual: u64,
    /// Process' swap usage
    pub memory_swap: u64,
    /// Process' peak memory usage (not precise)
    pub memory_rss_peak: u64,
    /// Process' peak virtual memory usage (tracked by OS)
    pub memory_virtual_peak: u64,
    /// Process' swap usage (not precise)
    pub memory_swap_peak: u64,
    /// Bytes read per second from block-backed filesystems
    pub disk_read: f32,
    /// Bytes written per second from block-backed filesystems
    pub disk_write: f32,
    /// Bytes per second of cancelled writes (i.e. removed temporary files)
    pub disk_cancelled: f32,
    /// Bytes read per second (total)
    pub io_read: f32,
    /// Bytes written per second (total)
    pub io_write: f32,
    /// Read operations (syscalls) per second (total)
    pub io_read_ops: f32,
    /// Write operations (syscalls) per second (total)
    pub io_write_ops: f32,
}

/// Report of CPU usage by single thread
#[derive(Debug, Serialize)]
pub struct ThreadReport {
    /// Threads' own CPU usage. 100% is a single core
    pub cpu_usage: f32,
    /// Threads' own CPU usage in kernel space. 100% is a single core
    pub system_cpu: f32,
    /// Threads' own CPU usage in user space. 100% is a single core
    pub user_cpu: f32,
}

/// The main structure that makes mesurements and reports values
///
/// Create it with `new()` then add threads that you want to track in a thread
/// breakdown information with `meter.track_thread()` and
/// `meter.untrack_thread()`.
///
/// Then add `meter.scan()` with a timer to scan the process info. It's
/// recommended to call it on the interval of one second.
///
/// Method `report()` may be used to get structure with stats. `report_json()`
/// can return a `rustc_serialize::Json` and `report_json_str()` returns that
/// serialized.
///
/// Note that the structure returned with `report()` can be changed when we
/// bump **major version** of the library. And while `report_json()` and
/// `report_json_str()` will never break the type system, their format will
/// always reflect that of `report()` call.
///
/// We don't track all the threads separately because thread ids are useless
/// without names, and we can fine-tune performance in the case we have known
/// number of threads. Obviously, process-wide info accounts all the threads.
pub struct Meter {
    #[allow(dead_code)]
    scan_interval: Duration,
    num_cpus: usize,
    num_snapshots: usize,
    start_time: SystemTime,
    snapshots: VecDeque<Snapshot>,
    thread_names: HashMap<Pid, String>,
    /// This is a buffer for reading some text data from /proc/anything.
    /// We use it to avoid memory allocations. This makes code a little bit
    /// more complex, but we want to avoid overhead as much as possible
    text_buf: String,
    /// This is a smaller buffer for formatting paths, similar to `text_buf`
    path_buf: String,

    /// This file is always open because if we drop privileges and then
    /// try to open a file we can't open it back again
    #[cfg(target_os="linux")]
    io_file: File,

    memory_rss_peak: u64,
    memory_swap_peak: u64,
}