--- 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 @@
+