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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
use crate::{FromInner, IntoInner};
use std::ffi::{CStr, CString};
use uv::{
    uv_cpu_info, uv_cpu_info_t, uv_free_cpu_info, uv_get_constrained_memory, uv_get_free_memory,
    uv_get_process_title, uv_get_total_memory, uv_getrusage, uv_gettimeofday, uv_hrtime,
    uv_library_shutdown, uv_loadavg, uv_resident_set_memory, uv_rusage_t, uv_set_process_title,
    uv_setup_args, uv_sleep, uv_timeval64_t, uv_timeval_t, uv_uptime,
};

pub mod os;
pub use os::*;

/// Data type for storing times.
pub struct TimeVal {
    pub sec: i64,
    pub usec: i64,
}

impl FromInner<uv_timeval_t> for TimeVal {
    fn from_inner(tv: uv_timeval_t) -> TimeVal {
        TimeVal {
            sec: tv.tv_sec as _,
            usec: tv.tv_usec as _,
        }
    }
}

impl FromInner<uv_timeval64_t> for TimeVal {
    fn from_inner(tv: uv_timeval64_t) -> TimeVal {
        TimeVal {
            sec: tv.tv_sec,
            usec: tv.tv_usec as _,
        }
    }
}

/// Data type for resource usage results.
pub struct ResourceUsage {
    /// user CPU time used
    pub usertime: TimeVal,

    /// system CPU time used
    pub systime: TimeVal,

    /// maximum resident set size
    pub maxrss: u64,

    /// integral shared memory size (no Windows support)
    pub ixrss: u64,

    /// integral unshared data size (no Windows support)
    pub idrss: u64,

    /// integral unshared stack size (no Windows support)
    pub isrss: u64,

    /// page reclaims (soft page faults) (no Windows support)
    pub minflt: u64,

    /// page faults (hard page faults)
    pub majflt: u64,

    /// swaps (no Windows support)
    pub nswap: u64,

    /// block input operations
    pub inblock: u64,

    /// block output operations
    pub oublock: u64,

    /// IPC messages sent (no windows support)
    pub msgsnd: u64,

    /// IPC messages received (no Windows support)
    pub msgrcv: u64,

    /// signals received (no Windows support)
    pub nsignals: u64,

    /// voluntary context switches (no Windows support)
    pub nvcsw: u64,

    /// involuntary context switches (no Windows support)
    pub nivcsw: u64,
}

impl FromInner<uv_rusage_t> for ResourceUsage {
    fn from_inner(usage: uv_rusage_t) -> ResourceUsage {
        ResourceUsage {
            usertime: usage.ru_utime.into_inner(),
            systime: usage.ru_stime.into_inner(),
            maxrss: usage.ru_maxrss,
            ixrss: usage.ru_ixrss,
            idrss: usage.ru_idrss,
            isrss: usage.ru_isrss,
            minflt: usage.ru_minflt,
            majflt: usage.ru_majflt,
            nswap: usage.ru_nswap,
            inblock: usage.ru_inblock,
            oublock: usage.ru_oublock,
            msgsnd: usage.ru_msgsnd,
            msgrcv: usage.ru_msgrcv,
            nsignals: usage.ru_nsignals,
            nvcsw: usage.ru_nvcsw,
            nivcsw: usage.ru_nivcsw,
        }
    }
}

/// Data type for CPU information.
pub struct CpuInfo {
    pub model: String,
    pub speed: i32,
    pub user_time: u64,
    pub nice_time: u64,
    pub sys_time: u64,
    pub idle_time: u64,
    pub irq_time: u64,
}

impl FromInner<&uv_cpu_info_t> for CpuInfo {
    fn from_inner(cpu: &uv_cpu_info_t) -> CpuInfo {
        let model = unsafe { CStr::from_ptr(cpu.model) }
            .to_string_lossy()
            .into_owned();
        CpuInfo {
            model,
            speed: cpu.speed,
            user_time: cpu.cpu_times.user,
            nice_time: cpu.cpu_times.nice,
            sys_time: cpu.cpu_times.sys,
            idle_time: cpu.cpu_times.idle,
            irq_time: cpu.cpu_times.irq,
        }
    }
}

/// Store the program arguments. Required for getting / setting the process title. Libuv may take
/// ownership of the memory that argv points to. This function should be called exactly once, at
/// program start-up.
pub fn setup_args() -> Result<Vec<String>, std::ffi::NulError> {
    // Get arguments, transform into CStrings and then into raw bytes
    let mut args = std::env::args()
        .map(|s| CString::new(s).map(|s| s.into_bytes_with_nul()))
        .collect::<Result<Vec<_>, std::ffi::NulError>>()?;
    let mut argsptr: Vec<*mut std::os::raw::c_char> =
        args.iter_mut().map(|s| s.as_mut_ptr() as _).collect();
    let argc = args.len();

    // rebuild args from the return value
    let args = unsafe { uv_setup_args(argc as _, argsptr.as_mut_ptr()) };
    let args = unsafe { std::slice::from_raw_parts(args, argc) };
    Ok(args
        .iter()
        .map(|arg| {
            unsafe { CStr::from_ptr(*arg) }
                .to_string_lossy()
                .into_owned()
        })
        .collect())
}

/// Release any global state that libuv is holding onto. Libuv will normally do so automatically
/// when it is unloaded but it can be instructed to perform cleanup manually.
///
/// Warning: Only call shutdown() once.
///
/// Warning: Don’t call shutdown() when there are still event loops or I/O requests active.
///
/// Warning: Don’t call libuv functions after calling shutdown().
pub fn shutdown() {
    unsafe { uv_library_shutdown() };
}

/// Gets the title of the current process. You must call setup_args before calling this function.
pub fn get_process_title() -> crate::Result<String> {
    let mut size = 16usize;
    let mut buf: Vec<std::os::raw::c_char> = vec![];
    loop {
        // title didn't fit in old size - double our allocation and try again
        size *= 2;
        buf.reserve(size - buf.len());

        let result = crate::uvret(unsafe { uv_get_process_title(buf.as_mut_ptr() as _, size as _) });
        if let Err(e) = result {
            if e != crate::Error::ENOBUFS {
                return Err(e);
            }
        } else {
            break;
        }
    }

    Ok(unsafe { CStr::from_ptr(buf.as_ptr()) }
        .to_string_lossy()
        .into_owned())
}

/// Sets the current process title. You must call uv_setup_args before calling this function. On
/// platforms with a fixed size buffer for the process title the contents of title will be copied
/// to the buffer and truncated if larger than the available space. Other platforms will return
/// ENOMEM if they cannot allocate enough space to duplicate the contents of title.
pub fn set_process_title(title: &str) -> Result<(), Box<dyn std::error::Error>> {
    let title = CString::new(title)?;
    crate::uvret(unsafe { uv_set_process_title(title.as_ptr()) }).map_err(|e| Box::new(e) as _)
}

/// Gets the resident set size (RSS) for the current process.
pub fn resident_set_memory() -> crate::Result<usize> {
    let mut rss = 0u64;
    crate::uvret(unsafe { uv_resident_set_memory(&mut rss as _) }).map(|_| rss as _)
}

/// Gets the current system uptime.
pub fn uptime() -> crate::Result<f64> {
    let mut uptime = 0f64;
    crate::uvret(unsafe { uv_uptime(&mut uptime as _) }).map(|_| uptime)
}

/// Gets the resource usage measures for the current process.
///
/// Note: On Windows not all fields are set, the unsupported fields are filled with zeroes. See
/// ResourceUsage for more details.
pub fn getrusage() -> crate::Result<ResourceUsage> {
    let mut usage: uv_rusage_t = unsafe { std::mem::zeroed() };
    crate::uvret(unsafe { uv_getrusage(&mut usage as _) }).map(|_| usage.into_inner())
}

/// Gets information about the CPUs on the system.
pub fn cpu_info() -> crate::Result<Vec<CpuInfo>> {
    let mut infos: *mut uv_cpu_info_t = unsafe { std::mem::zeroed() };
    let mut count: std::os::raw::c_int = 0;
    crate::uvret(unsafe { uv_cpu_info(&mut infos as _, &mut count as _) })?;

    let result = unsafe { std::slice::from_raw_parts(infos, count as _) }
        .iter()
        .map(|info| info.into_inner())
        .collect();
    unsafe { uv_free_cpu_info(infos, count as _) };
    Ok(result)
}

/// Gets the load average. See: https://en.wikipedia.org/wiki/Load_(computing)
///
/// Note: Returns [0,0,0] on Windows (i.e., it’s not implemented).
pub fn loadavg() -> [f64; 3] {
    let mut avg = [0f64; 3];
    unsafe { uv_loadavg(avg.as_mut_ptr()) };
    return avg;
}

/// Gets memory information (in bytes).
pub fn get_free_memory() -> u64 {
    unsafe { uv_get_free_memory() }
}

/// Gets memory information (in bytes).
pub fn get_total_memory() -> u64 {
    unsafe { uv_get_total_memory() }
}

/// Gets the amount of memory available to the process (in bytes) based on limits imposed by the
/// OS. If there is no such constraint, or the constraint is unknown, 0 is returned. Note that it
/// is not unusual for this value to be less than or greater than uv_get_total_memory().
///
/// Note: This function currently only returns a non-zero value on Linux, based on cgroups if it is
/// present.
pub fn get_constrained_memory() -> u64 {
    unsafe { uv_get_constrained_memory() }
}

/// Returns the current high-resolution real time. This is expressed in nanoseconds. It is relative
/// to an arbitrary time in the past. It is not related to the time of day and therefore not
/// subject to clock drift. The primary use is for measuring performance between intervals.
///
/// Note: Not every platform can support nanosecond resolution; however, this value will always be
/// in nanoseconds.
pub fn hrtime() -> u64 {
    unsafe { uv_hrtime() }
}

/// Cross-platform implementation of gettimeofday(2). The timezone argument to gettimeofday() is
/// not supported, as it is considered obsolete.
pub fn gettimeofday() -> crate::Result<TimeVal> {
    let mut tv: uv_timeval64_t = unsafe { std::mem::zeroed() };
    crate::uvret(unsafe { uv_gettimeofday(&mut tv as _) }).map(|_| tv.into_inner())
}

/// Causes the calling thread to sleep for msec milliseconds.
pub fn sleep(msec: u32) {
    unsafe { uv_sleep(msec) };
}