Bug 1425787 - Rust library for collecting system resource metrics draft
authorGregory Szorc <gps@mozilla.com>
Sun, 17 Dec 2017 20:17:25 -0800
changeset 712769 4a26bace182a698df6c1cdfeb0990092534b2e15
parent 711933 04433cdc9395327cdeff5fe8cbccaa361f9c3c22
child 744135 908169d36e0be01d5c93b4dedaab95edd299d875
push id93423
push userbmo:gps@mozilla.com
push dateMon, 18 Dec 2017 18:55:14 +0000
bugs1425787
milestone59.0a1
Bug 1425787 - Rust library for collecting system resource metrics This commit adds a Rust crate providing an implementation of a system resource collector. The crate provides functionality similar to the mozsystemmonitor Python package. Unlike the Python version (which uses psutil), the Rust version has no runtime dependencies outside of the system. This makes it easy to drop into a statically linked binary so it can be used more universally. While we have an eventual goal for this crate to run on all tier 1 supported platforms, our immediate goal is to get it deployed on Linux. So that is where the bulk of the code lives. MozReview-Commit-ID: BOjXruhfhBE
.hgignore
tools/rust-resourcemonitor/.hgignore
tools/rust-resourcemonitor/Cargo.lock
tools/rust-resourcemonitor/Cargo.toml
tools/rust-resourcemonitor/src/collector.rs
tools/rust-resourcemonitor/src/lib.rs
tools/rust-resourcemonitor/src/linux.rs
tools/rust-resourcemonitor/src/macos.rs
tools/rust-resourcemonitor/src/windows.rs
--- a/.hgignore
+++ b/.hgignore
@@ -162,14 +162,15 @@ GPATH
 # Ignore sync tps logs and reports
 tps\.log
 tps_result\.json
 
 # Ignore Visual Studio Code workspace files.
 \.vscode/(?!extensions\.json|tasks\.json)
 
 subinclude:servo/.hgignore
+subinclude:tools/rust-resourcemonitor/.hgignore
 
 # Ignore Infer output
 ^infer-out/
 
 # https://bz.mercurial-scm.org/show_bug.cgi?id=5322
 ^comm/
new file mode 100644
--- /dev/null
+++ b/tools/rust-resourcemonitor/.hgignore
@@ -0,0 +1,1 @@
+target/
new file mode 100644
--- /dev/null
+++ b/tools/rust-resourcemonitor/Cargo.lock
@@ -0,0 +1,361 @@
+[[package]]
+name = "bitflags"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "byteorder"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "bytes"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cfg-if"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "chrono"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "fuchsia-zircon"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "fuchsia-zircon-sys"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "futures"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "iovec"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "kernel32-sys"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "lazycell"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "log"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "mach"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "mio"
+version = "0.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "mio-named-pipes"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "mio-uds"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "miow"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "net2"
+version = "0.2.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ntdll-sys"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "redox_syscall"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "resourcemonitor"
+version = "0.1.0"
+dependencies = [
+ "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mach 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ntdll-sys 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
+ "timer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-process 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "slab"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "slab"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "time"
+version = "0.1.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "timer"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-core"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-io"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-process"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio-named-pipes 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-core 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-signal 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-signal"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-core 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "ws2_32-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[metadata]
+"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
+"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
+"checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6"
+"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
+"checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9"
+"checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159"
+"checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82"
+"checksum futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "118b49cac82e04121117cbd3121ede3147e885627d82c4546b87c702debb90c1"
+"checksum iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6e8b9c2247fcf6c6a1151f1156932be5606c9fd6f55a2d7f9fc1cb29386b2f7"
+"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
+"checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b"
+"checksum libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "36fbc8a8929c632868295d0178dd8f63fc423fd7537ad0738372bd010b3ac9b0"
+"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
+"checksum mach 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfca93c5d567689bf268a4e0992adb280bedaabf0cb86afd3b504acb3011c2c0"
+"checksum mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0e8411968194c7b139e9105bc4ae7db0bae232af087147e72f0616ebf5fdb9cb"
+"checksum mio-named-pipes 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "82f43a815b57d2d652550f3d20cec88a495bb2d0956aa873dc43040278455677"
+"checksum mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1731a873077147b626d89cc6c2a0db6288d607496c5d10c0cfcf3adc697ec673"
+"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
+"checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09"
+"checksum ntdll-sys 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4037b8f42a08a6961e56fad1a58ce58165561c03a01d6def52317023b8fb7d61"
+"checksum num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cc4083e14b542ea3eb9b5f33ff48bd373a92d78687e74f4cc0a30caeb754f0ca"
+"checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba"
+"checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01"
+"checksum num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cacfcab5eb48250ee7d0c7896b51a2c5eec99c1feea5f32025635f5ae4b00070"
+"checksum redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ab105df655884ede59d45b7070c8a65002d921461ee813a024558ca16030eea0"
+"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
+"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
+"checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d"
+"checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520"
+"checksum timer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "31d42176308937165701f50638db1c31586f183f1aab416268216577aec7306b"
+"checksum tokio-core 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c87c27560184212c9dc45cd8f38623f37918248aad5b58fb65303b5d07a98c6e"
+"checksum tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "514aae203178929dbf03318ad7c683126672d4d96eccb77b29603d33c9e25743"
+"checksum tokio-process 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9acd5b40e096aa0804c6b0a2f79ce71bada3594b5c7e46cae296f14b5d12b884"
+"checksum tokio-signal 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d121715f6917878a0df69f39365d01dd66c4463e4ba19efdcddcdfeb1bcb2bc"
+"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
+"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
new file mode 100644
--- /dev/null
+++ b/tools/rust-resourcemonitor/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "resourcemonitor"
+version = "0.1.0"
+authors = ["Gregory Szorc <gps@mozilla.com>"]
+license = "MPL-2.0"
+
+[dependencies]
+libc = "0.2"
+timer = "0.2"
+time = "0.1"
+tokio-process = "0.1"
+
+[target.'cfg(target_os = "macos")'.dependencies]
+mach = "0.1"
+
+[target.'cfg(target_os = "windows")'.dependencies]
+winapi = "0.2"
+ntdll-sys = "0.0.1"
new file mode 100644
--- /dev/null
+++ b/tools/rust-resourcemonitor/src/collector.rs
@@ -0,0 +1,60 @@
+use std::collections::{LinkedList};
+use std::time::{Instant};
+
+use timer::{Guard, Timer};
+
+#[cfg(target_os = "linux")]
+use linux::*;
+
+#[cfg(target_os = "macos")]
+use macos::*;
+
+use super::{CpuTimes};
+
+#[allow(dead_code)]
+#[derive(Debug)]
+pub struct SystemSample {
+    time: Instant,
+    cpu: Vec<CpuTimes>,
+}
+
+type SystemSamples = LinkedList<SystemSample>;
+
+/// Collect system metrics at an instance in time.
+pub fn collect_system_metrics() -> Result<SystemSample, String> {
+    let now = Instant::now();
+
+    let cpu = per_cpu_times()?;
+
+    Ok(SystemSample {
+        time: now,
+        cpu: cpu,
+    })
+}
+
+pub fn append_system_metrics(samples: &mut SystemSamples) -> Result<(), String> {
+    let sample = collect_system_metrics()?;
+    samples.push_back(sample);
+    Ok(())
+}
+
+pub struct ResourceCollector {
+    timer: Timer,
+    timer_guard: Option<Guard>,
+    system_samples: SystemSamples,
+}
+
+impl ResourceCollector {
+    pub fn new() -> ResourceCollector {
+        ResourceCollector {
+            timer: Timer::new(),
+            timer_guard: None,
+            system_samples: SystemSamples::new(),
+        }
+    }
+
+    #[inline]
+    pub fn collect(&mut self) -> Result<(), String> {
+        append_system_metrics(&mut self.system_samples)
+    }
+}
new file mode 100644
--- /dev/null
+++ b/tools/rust-resourcemonitor/src/lib.rs
@@ -0,0 +1,57 @@
+
+extern crate libc;
+#[cfg(target_os = "macos")]
+extern crate mach;
+extern crate timer;
+extern crate time;
+#[cfg(target_os = "windows")]
+extern crate winapi;
+
+//mod collector;
+
+#[cfg(target_os = "linux")]
+mod linux;
+
+#[cfg(target_os = "macos")]
+mod macos;
+
+#[cfg(target_os = "windows")]
+mod windows;
+
+#[cfg(target_os = "linux")]
+pub use linux::*;
+
+#[cfg(target_os = "macos")]
+pub use macos::*;
+
+#[cfg(target_os = "windows")]
+pub use windows::*;
+
+#[allow(dead_code)]
+#[derive(Debug)]
+pub struct CpuTimes {
+    user: u64,
+    nice: u64,
+    system: u64,
+    idle: u64,
+    iowait: Option<u64>,
+    irq: Option<u64>,
+    softirq: Option<u64>,
+    steal: Option<u64>,
+    guest: Option<u64>,
+    guest_nice: Option<u64>,
+}
+
+#[allow(dead_code)]
+static EMPTY_CPU_TIMES: CpuTimes = CpuTimes {
+    user: 0,
+    nice: 0,
+    system: 0,
+    idle: 0,
+    iowait: None,
+    irq: None,
+    softirq: None,
+    steal: None,
+    guest: None,
+    guest_nice: None,
+};
new file mode 100644
--- /dev/null
+++ b/tools/rust-resourcemonitor/src/linux.rs
@@ -0,0 +1,410 @@
+extern crate libc;
+
+use std::collections::{HashMap};
+use std::error::{Error};
+use std::fs::File;
+use std::io::{ErrorKind};
+use std::io;
+use std::io::Read;
+use std::time::{Instant};
+use libc::c_int;
+
+use super::{CpuTimes};
+
+fn read_file(path: &str) -> io::Result<String> {
+    let mut file = File::open(path)?;
+    let mut content = String::new();
+
+    file.read_to_string(&mut content).expect(format!("could not read {}", path).as_str());
+
+    Ok(content)
+}
+
+#[inline]
+fn wrapping_integer_difference(a: &u64, b: &u64) -> u64 {
+    if b >= a {
+        return b - a
+    }
+
+    // New value is smaller than original. Assume it wrapped. But for our
+    // purposes, we don't assume the original value was a u64. Instead, we
+    // assume it could be an unsigned 32 or 64 bit integer and that
+    // the value could not increase by more than u32's max value.
+    if *a > u32::max_value() as u64 {
+        return b + u64::max_value() as u64;
+    }
+    else {
+        return b + u32::max_value() as u64;
+    }
+}
+
+pub fn cpu_times_from_stat(line: &str, clock_ticks: isize) -> CpuTimes {
+    let mut iter = line.split_whitespace();
+    iter.next();
+    let fields: Vec<u64> = iter.map(|f| f.parse::<u64>().unwrap() / clock_ticks as u64).collect();
+
+    let user = fields[0];
+    let nice = fields[1];
+    let system = fields[2];
+    let idle = fields[3];
+    let iowait = fields[4];
+    let irq = fields[5];
+    let softirq = fields[6];
+    let steal = if fields.len() > 7 { fields[7] } else { 0 };
+    let guest = if fields.len() > 8 { fields[8] } else { 0 };
+    let guest_nice = if fields.len() > 9 { fields[9] } else { 0 };
+
+    CpuTimes {
+        user,
+        nice,
+        system,
+        idle,
+        iowait: Some(iowait),
+        irq: Some(irq),
+        softirq: Some(softirq),
+        steal: Some(steal),
+        guest: Some(guest),
+        guest_nice: Some(guest_nice),
+    }
+}
+pub fn sysconf(name: i32) -> Result<isize, String> {
+    match unsafe { libc::sysconf(name) as c_int } {
+        -1 => Err(String::from("error querying sysconf")),
+        ret => Ok(ret as isize),
+    }
+}
+
+pub fn per_cpu_times() -> Result<Vec<CpuTimes>, String> {
+    let stat = read_file("/proc/stat").expect("unable to read /proc/stat");
+
+    let clock_ticks = sysconf(libc::_SC_CLK_TCK)?;
+
+    let mut lines = stat.lines();
+    // First line is global stats.
+    lines.next();
+
+    let mut times = Vec::new();
+
+    for line in lines {
+        // cpu0 245494 5532 78858 746861111 51817 0 3478 27788 0 0
+        if !line.starts_with("cpu") {
+            break;
+        }
+
+        times.push(cpu_times_from_stat(line, clock_ticks));
+    }
+
+    Ok(times)
+}
+
+// TODO consider caching this.
+pub fn disk_sector_size(name: &str) -> Result<u64, String> {
+    let path = format!("/sys/block/{}/queue/hw_sector_size", name);
+
+    match read_file(path.as_str()) {
+        Ok(s) => Ok(s.trim().parse::<u64>().unwrap()),
+        // The default is 512 bytes to a sector.
+        Err(ref error) if error.kind() == ErrorKind::NotFound => Ok(512),
+        Err(error) => Err(String::from(error.description())),
+    }
+}
+
+#[allow(dead_code)]
+#[derive(Debug)]
+pub struct DiskIOCounters {
+    name: String,
+    reads: u64,
+    reads_merged: u64,
+    read_bytes: u64,
+    read_time: u64,
+    writes: u64,
+    writes_merged: u64,
+    write_bytes: u64,
+    write_time: u64,
+    in_progress: i64,
+    in_progress_time: i64,
+    weighted_time: i64,
+}
+
+impl DiskIOCounters {
+    /// Compute the difference between this and another instance.
+    ///
+    /// Returns the change between one instance and another.
+    pub fn diff(&self, other: &DiskIOCounters) -> DiskIOCounters {
+        DiskIOCounters {
+            name: self.name.clone(),
+            reads: wrapping_integer_difference(&self.reads, &other.reads),
+            reads_merged: wrapping_integer_difference(&self.reads_merged, &other.reads_merged),
+            read_bytes: wrapping_integer_difference(&self.read_bytes, &other.read_bytes),
+            read_time: wrapping_integer_difference(&self.read_time, &other.read_time),
+            writes: wrapping_integer_difference(&self.writes, &other.writes),
+            writes_merged: wrapping_integer_difference(&self.writes_merged, &other.writes_merged),
+            write_bytes: wrapping_integer_difference(&self.write_bytes, &other.write_bytes),
+            write_time: wrapping_integer_difference(&self.write_time, &other.write_time),
+            in_progress: other.in_progress - self.in_progress,
+            in_progress_time: other.in_progress_time - self.in_progress_time,
+            weighted_time: other.weighted_time - self.weighted_time,
+        }
+    }
+}
+
+pub fn disk_io_counters() -> Result<Vec<DiskIOCounters>, String> {
+    let disk_stats = read_file("/proc/diskstats").expect("unable to read /proc/diskstats");
+
+    let mut counters = Vec::new();
+
+    // See https://www.kernel.org/doc/Documentation/iostats.txt for format.
+    for line in disk_stats.lines() {
+        // 8 0 sda 196006 51812 6816785 95388 124616 178180 12103612 607744 0 155840 703456
+        let fields: Vec<&str> = line.split_whitespace().collect();
+
+        if fields.len() != 14 {
+            return Err(String::from("unexpected number of fields in diskstats"));
+        }
+
+        let name = fields[2];
+        let int_fields: Vec<u64> = fields[3..14].iter().map(|f| f.parse::<u64>().unwrap()).collect();
+
+        let reads = int_fields[0];
+        let reads_merged = int_fields[1];
+        let read_sectors = int_fields[2];
+        let read_ms = int_fields[3];
+        let writes = int_fields[4];
+        let writes_merged = int_fields[5];
+        let write_sectors = int_fields[6];
+        let write_ms = int_fields[7];
+        let in_progress = int_fields[8] as i64;
+        let in_progress_ms = int_fields[9] as i64;
+        let weighted_time = int_fields[10] as i64;
+
+        let read_time = read_ms;
+        let write_time = write_ms;
+        let in_progress_time = in_progress_ms;
+
+        let sector_size = disk_sector_size(name)?;
+        let read_bytes = read_sectors * sector_size;
+        let write_bytes = write_sectors * sector_size;
+
+        counters.push(DiskIOCounters {
+            name: String::from(name),
+            reads,
+            reads_merged,
+            read_bytes,
+            read_time,
+            writes,
+            writes_merged,
+            write_bytes,
+            write_time,
+            in_progress,
+            in_progress_time,
+            weighted_time,
+        });
+    }
+
+    Ok(counters)
+}
+
+pub type MemInfo = HashMap<String, u64>;
+
+pub fn read_meminfo() -> Result<MemInfo, String> {
+    let data = read_file("/proc/meminfo").expect("unable to read meminfo");
+
+    // TODO consider using a struct or caching last used size.
+    let mut res = MemInfo::with_capacity(30);
+
+    for line in data.lines() {
+        let fields: Vec<&str> = line.split_whitespace().collect();
+
+        // Key ends with a colon.
+        let key = fields[0].trim_right_matches(":");
+
+        let mut value = fields[1].parse::<u64>().unwrap();
+
+        if fields.len() > 2 {
+            match fields[2] {
+                "kB" => value = value * 1024,
+                _ => return Err(String::from("unknown value in meminfo")),
+            }
+        }
+
+        res.insert(String::from(key), value);
+    }
+
+    Ok(res)
+}
+
+pub type VmStat = HashMap<String, u64>;
+
+pub fn read_vmstat() -> Result<VmStat, String> {
+    let data = read_file("/proc/vmstat").expect("unable to read vmstat");
+
+    let mut res = VmStat::with_capacity(30);
+
+    for line in data.lines() {
+        let mut it = line.split_whitespace();
+        let key = it.next().unwrap();
+        let value = it.next().unwrap();
+        let value = value.parse::<u64>().unwrap();
+
+        res.insert(String::from(key), value);
+    }
+
+    Ok(res)
+}
+
+#[allow(dead_code)]
+#[derive(Debug)]
+pub struct VirtualMemory {
+    total: u64,
+    available: u64,
+    used: u64,
+    free: u64,
+    active: u64,
+    inactive: u64,
+    buffers: u64,
+    cached: u64,
+    shared: u64,
+}
+
+pub fn virtual_memory(mem_info: &MemInfo) -> Result<VirtualMemory, String> {
+    let total = mem_info.get("MemTotal").unwrap();
+    let free = mem_info.get("MemFree").unwrap();
+    let buffers = mem_info.get("Buffers").unwrap();
+    let cached = mem_info.get("Cached").unwrap() + mem_info.get("SReclaimable").unwrap();
+    let shared = mem_info.get("Shmem").unwrap();
+    let active = mem_info.get("Active").unwrap();
+    let inactive = mem_info.get("Inactive").unwrap();
+    let mut available = mem_info.get("MemAvailable").unwrap().clone();
+
+    let in_use = free + cached + buffers;
+
+    let used = if in_use > *total {
+        total - free
+    }
+    else {
+        total - in_use
+    };
+
+    // This can happen in containers.
+    if available > *total {
+        available = free.clone();
+    }
+
+    Ok(VirtualMemory {
+        total: total.clone(),
+        available,
+        used: used.clone(),
+        free: free.clone(),
+        active: active.clone(),
+        inactive: inactive.clone(),
+        buffers: buffers.clone(),
+        cached: cached.clone(),
+        shared: shared.clone(),
+    })
+}
+
+#[allow(dead_code)]
+#[derive(Debug)]
+pub struct SwapMemory {
+    total: u64,
+    free: u64,
+    used: u64,
+    swap_in: u64,
+    swap_out: u64,
+}
+
+pub fn swap_memory(mem_info: &MemInfo, vm_stat: &VmStat) -> Result<SwapMemory, String> {
+    let total = mem_info.get("SwapTotal").unwrap();
+    let free = mem_info.get("SwapFree").unwrap();
+    let used = total - free;
+
+    let swap_in = vm_stat.get("pswpin").unwrap() * 4096;
+    let swap_out = vm_stat.get("pswpout").unwrap() * 4096;
+
+    Ok(SwapMemory {
+        total: total.clone(),
+        free: free.clone(),
+        used,
+        swap_in,
+        swap_out,
+    })
+}
+
+#[allow(dead_code)]
+#[derive(Debug)]
+pub struct SystemSample {
+    time: Instant,
+    cpu: Vec<CpuTimes>,
+    disk_counters: Vec<DiskIOCounters>,
+    virtual_memory: VirtualMemory,
+    swap_memory: SwapMemory,
+}
+
+pub fn collect_system() -> Result<SystemSample, String> {
+    let time = Instant::now();
+
+    let cpu = per_cpu_times()?;
+    let disk_counters = disk_io_counters()?;
+    let mem_info = read_meminfo()?;
+    let vmstat = read_vmstat()?;
+    let virtual_memory = virtual_memory(&mem_info)?;
+    let swap_memory = swap_memory(&mem_info, &vmstat)?;
+
+    Ok(SystemSample {
+        time,
+        cpu,
+        disk_counters,
+        virtual_memory,
+        swap_memory,
+    })
+}
+
+
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn per_cpu_times() {
+        let res = super::per_cpu_times().unwrap();
+        assert!(res.len() > 0);
+    }
+
+    #[test]
+    fn disk_io_counters() {
+        let res1 = super::disk_io_counters().unwrap();
+        let res2 = super::disk_io_counters().unwrap();
+
+        let a = &res1[0];
+        let b = &res2[0];
+
+        let diff = b.diff(a);
+    }
+
+    #[test]
+    fn read_meminfo() {
+        let res = super::read_meminfo().unwrap();
+        assert!(res.len() > 10);
+    }
+
+    #[test]
+    fn read_vmstat() {
+        let res = super::read_vmstat().unwrap();
+        assert!(res.len() > 10);
+    }
+
+    #[test]
+    fn virtual_memory() {
+        let m = super::read_meminfo().unwrap();
+        let res = super::virtual_memory(&m).unwrap();
+
+        assert!(res.total > 0);
+        assert!(res.available > 0);
+    }
+
+    #[test]
+    fn swap_memory() {
+        let m = super::read_meminfo().unwrap();
+        let v = super::read_vmstat().unwrap();
+
+        let swap = super::swap_memory(&m, &v).unwrap();
+    }
+}
new file mode 100644
--- /dev/null
+++ b/tools/rust-resourcemonitor/src/macos.rs
@@ -0,0 +1,95 @@
+#![allow(non_camel_case_types)]
+
+extern crate libc;
+
+use libc::c_uint;
+use std::ptr::{null_mut};
+use std::slice::{from_raw_parts};
+
+use mach::kern_return::{kern_return_t, KERN_SUCCESS};
+use mach::mach_port::{mach_port_deallocate};
+use mach::message::{mach_msg_type_number_t};
+use mach::port::{mach_port_t};
+use mach::traps::{mach_task_self};
+use mach::types::{host_t};
+use mach::vm_types::{natural_t, vm_address_t, vm_size_t};
+
+use super::{CpuTimes, EMPTY_CPU_TIMES};
+
+// From mach/processor_info.h.
+
+type processor_flavor_t = mach_port_t;
+
+#[repr(C)]
+#[derive(Debug)]
+struct processor_info_t {
+    pub user: c_uint,
+    pub system: c_uint,
+    pub idle: c_uint,
+    pub nice: c_uint,
+}
+
+type processor_info_array_t = *mut processor_info_t;
+
+extern "C" {
+    fn mach_host_self() -> host_t;
+
+    fn host_processor_info(host: host_t,
+                           flavor: processor_flavor_t,
+                           processor_count: *mut natural_t,
+                           processor_info: *mut processor_info_array_t,
+                           processor_info_count: *mut mach_msg_type_number_t) -> kern_return_t;
+
+    fn vm_deallocate(task: mach_port_t, address: vm_address_t, size: vm_size_t) -> kern_return_t;
+}
+
+pub fn per_cpu_times() -> Result<Vec<CpuTimes>, String> {
+    let host = unsafe { mach_host_self() };
+
+    let mut cpu_count : natural_t = 0;
+    let mut info_array : processor_info_array_t = null_mut();
+    let mut info_size : mach_msg_type_number_t = 0;
+
+    let error = unsafe {
+        // PROCESSOR_CPU_LOAD_INFO
+        let flavor = 2;
+        host_processor_info(host, flavor, &mut cpu_count as *mut natural_t, &mut info_array as *mut processor_info_array_t, &mut info_size as *mut mach_msg_type_number_t)
+    };
+
+    if error != KERN_SUCCESS {
+        panic!("unable to query host_processor_info()");
+    }
+
+    unsafe {
+        mach_port_deallocate(mach_task_self(), host);
+    }
+
+    let mut res = Vec::new();
+
+    let infos = unsafe { from_raw_parts(info_array, cpu_count as usize) };
+    for info in infos {
+        // TODO divide by CLK_TCK?
+        res.push(CpuTimes {
+            user: info.user as u64,
+            system: info.system as u64,
+            idle: info.idle as u64,
+            nice: info.nice as u64,
+            ..EMPTY_CPU_TIMES
+        })
+    }
+
+    unsafe {
+        vm_deallocate(mach_task_self(), info_array as vm_address_t, info_size as usize);
+    }
+
+    Ok(res)
+}
+
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn per_cpu_times() {
+        let res = super::per_cpu_times().unwrap();
+        assert!(res.len() > 0);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/tools/rust-resourcemonitor/src/windows.rs
@@ -0,0 +1,1 @@
+