Grafana/InfluxDB experiment
| [package] | |
| name = "perf-influxdb" | |
| version = "0.1.0" | |
| authors = ["Tatsuyuki Ishi <ishitatsuyuki@gmail.com>"] | |
| [dependencies] | |
| serde = "1.0.75" | |
| serde_derive = "1.0.75" | |
| influx_db_client = "0.3.4" | |
| serde_json = "1.0.26" | |
| chrono = {version = "0.4.6", features = ["serde"]} |
| { | |
| "annotations": { | |
| "list": [ | |
| { | |
| "builtIn": 1, | |
| "datasource": "-- Grafana --", | |
| "enable": true, | |
| "hide": true, | |
| "iconColor": "rgba(0, 211, 255, 1)", | |
| "name": "Annotations & Alerts", | |
| "type": "dashboard" | |
| } | |
| ] | |
| }, | |
| "editable": true, | |
| "gnetId": null, | |
| "graphTooltip": 0, | |
| "id": 1, | |
| "iteration": 1535808279921, | |
| "links": [], | |
| "panels": [ | |
| { | |
| "aliasColors": {}, | |
| "bars": false, | |
| "dashLength": 10, | |
| "dashes": false, | |
| "datasource": null, | |
| "fill": 1, | |
| "gridPos": { | |
| "h": 9, | |
| "w": 24, | |
| "x": 0, | |
| "y": 0 | |
| }, | |
| "id": 2, | |
| "legend": { | |
| "avg": false, | |
| "current": false, | |
| "max": false, | |
| "min": false, | |
| "show": true, | |
| "total": false, | |
| "values": false | |
| }, | |
| "lines": true, | |
| "linewidth": 1, | |
| "links": [], | |
| "minSpan": 8, | |
| "nullPointMode": "null", | |
| "percentage": false, | |
| "pointradius": 5, | |
| "points": false, | |
| "renderer": "flot", | |
| "repeat": "bench", | |
| "repeatDirection": "h", | |
| "scopedVars": { | |
| "bench": { | |
| "selected": true, | |
| "text": "cargo", | |
| "value": "cargo" | |
| } | |
| }, | |
| "seriesOverrides": [], | |
| "spaceLength": 10, | |
| "stack": false, | |
| "steppedLine": false, | |
| "targets": [ | |
| { | |
| "groupBy": [ | |
| { | |
| "params": [ | |
| "state" | |
| ], | |
| "type": "tag" | |
| } | |
| ], | |
| "measurement": "rustc_perf", | |
| "orderByTime": "ASC", | |
| "policy": "default", | |
| "refId": "A", | |
| "resultFormat": "time_series", | |
| "select": [ | |
| [ | |
| { | |
| "params": [ | |
| "instructions:u" | |
| ], | |
| "type": "field" | |
| } | |
| ] | |
| ], | |
| "tags": [ | |
| { | |
| "key": "build", | |
| "operator": "=~", | |
| "value": "/^$build$/" | |
| }, | |
| { | |
| "condition": "AND", | |
| "key": "bench", | |
| "operator": "=~", | |
| "value": "/^$bench$/" | |
| } | |
| ] | |
| } | |
| ], | |
| "thresholds": [], | |
| "timeFrom": null, | |
| "timeShift": null, | |
| "title": "$bench $build", | |
| "tooltip": { | |
| "shared": true, | |
| "sort": 0, | |
| "value_type": "individual" | |
| }, | |
| "type": "graph", | |
| "xaxis": { | |
| "buckets": null, | |
| "mode": "time", | |
| "name": null, | |
| "show": true, | |
| "values": [] | |
| }, | |
| "yaxes": [ | |
| { | |
| "format": "short", | |
| "label": null, | |
| "logBase": 1, | |
| "max": null, | |
| "min": null, | |
| "show": true | |
| }, | |
| { | |
| "format": "short", | |
| "label": null, | |
| "logBase": 1, | |
| "max": null, | |
| "min": null, | |
| "show": true | |
| } | |
| ], | |
| "yaxis": { | |
| "align": false, | |
| "alignLevel": null | |
| } | |
| } | |
| ], | |
| "refresh": false, | |
| "schemaVersion": 16, | |
| "style": "dark", | |
| "tags": [], | |
| "templating": { | |
| "list": [ | |
| { | |
| "allValue": null, | |
| "current": { | |
| "selected": true, | |
| "text": "check", | |
| "value": "check" | |
| }, | |
| "datasource": "Default", | |
| "hide": 0, | |
| "includeAll": false, | |
| "label": null, | |
| "multi": false, | |
| "name": "build", | |
| "options": [ | |
| { | |
| "selected": true, | |
| "text": "check", | |
| "value": "check" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "debug", | |
| "value": "debug" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "release", | |
| "value": "release" | |
| } | |
| ], | |
| "query": "SHOW TAG VALUES WITH KEY = build", | |
| "refresh": 0, | |
| "regex": "", | |
| "sort": 0, | |
| "tagValuesQuery": "", | |
| "tags": [], | |
| "tagsQuery": "", | |
| "type": "query", | |
| "useTags": false | |
| }, | |
| { | |
| "allValue": null, | |
| "current": { | |
| "selected": true, | |
| "tags": [], | |
| "text": "cargo", | |
| "value": [ | |
| "cargo" | |
| ] | |
| }, | |
| "datasource": "Default", | |
| "hide": 0, | |
| "includeAll": true, | |
| "label": null, | |
| "multi": true, | |
| "name": "bench", | |
| "options": [ | |
| { | |
| "selected": false, | |
| "text": "All", | |
| "value": "$__all" | |
| }, | |
| { | |
| "selected": true, | |
| "text": "cargo", | |
| "value": "cargo" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "clap-rs", | |
| "value": "clap-rs" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "coercions", | |
| "value": "coercions" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "crates.io", | |
| "value": "crates.io" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "ctfe-stress", | |
| "value": "ctfe-stress" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "ctfe-stress-cast", | |
| "value": "ctfe-stress-cast" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "ctfe-stress-const-fn", | |
| "value": "ctfe-stress-const-fn" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "ctfe-stress-force-alloc", | |
| "value": "ctfe-stress-force-alloc" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "ctfe-stress-index-check", | |
| "value": "ctfe-stress-index-check" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "ctfe-stress-ops", | |
| "value": "ctfe-stress-ops" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "ctfe-stress-reloc", | |
| "value": "ctfe-stress-reloc" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "ctfe-stress-unsize-slice", | |
| "value": "ctfe-stress-unsize-slice" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "deep-vector", | |
| "value": "deep-vector" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "deeply-nested", | |
| "value": "deeply-nested" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "encoding", | |
| "value": "encoding" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "futures", | |
| "value": "futures" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "helloworld", | |
| "value": "helloworld" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "html5ever", | |
| "value": "html5ever" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "hyper", | |
| "value": "hyper" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "inflate", | |
| "value": "inflate" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "issue-46449", | |
| "value": "issue-46449" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "keccak", | |
| "value": "keccak" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "piston-image", | |
| "value": "piston-image" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "regex", | |
| "value": "regex" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "regression-31157", | |
| "value": "regression-31157" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "ripgrep", | |
| "value": "ripgrep" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "script-servo", | |
| "value": "script-servo" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "sentry-cli", | |
| "value": "sentry-cli" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "serde", | |
| "value": "serde" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "style-servo", | |
| "value": "style-servo" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "syn", | |
| "value": "syn" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "tokio-webpush-simple", | |
| "value": "tokio-webpush-simple" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "tuple-stress", | |
| "value": "tuple-stress" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "ucd", | |
| "value": "ucd" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "unify-linearly", | |
| "value": "unify-linearly" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "unused-warnings", | |
| "value": "unused-warnings" | |
| }, | |
| { | |
| "selected": false, | |
| "text": "webrender", | |
| "value": "webrender" | |
| } | |
| ], | |
| "query": "SHOW TAG VALUES WITH KEY = bench", | |
| "refresh": 0, | |
| "regex": "", | |
| "sort": 0, | |
| "tagValuesQuery": "", | |
| "tags": [], | |
| "tagsQuery": "", | |
| "type": "query", | |
| "useTags": false | |
| } | |
| ] | |
| }, | |
| "time": { | |
| "from": "now-90d", | |
| "to": "now" | |
| }, | |
| "timepicker": { | |
| "refresh_intervals": [ | |
| "5s", | |
| "10s", | |
| "30s", | |
| "1m", | |
| "5m", | |
| "15m", | |
| "30m", | |
| "1h", | |
| "2h", | |
| "1d" | |
| ], | |
| "time_options": [ | |
| "5m", | |
| "15m", | |
| "1h", | |
| "6h", | |
| "12h", | |
| "24h", | |
| "2d", | |
| "7d", | |
| "30d" | |
| ] | |
| }, | |
| "timezone": "", | |
| "title": "Graph", | |
| "uid": "-QhIRphmz", | |
| "version": 5 | |
| } |
| use std::cmp::Ordering; | |
| use std::collections::BTreeMap; | |
| use std::env; | |
| use std::fmt; | |
| use std::fs; | |
| use std::path::PathBuf; | |
| use std::str::FromStr; | |
| extern crate serde; | |
| use serde::{Deserialize, Serialize}; | |
| #[macro_use] | |
| extern crate serde_derive; | |
| extern crate chrono; | |
| extern crate serde_json; | |
| use chrono::prelude::*; | |
| extern crate influx_db_client; | |
| use influx_db_client::{Client, Point, Points, Precision, Value}; | |
| #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] | |
| pub struct Date(pub DateTime<Utc>); | |
| #[derive(Debug, Clone, PartialEq, Eq)] | |
| pub struct DateParseError { | |
| pub input: String, | |
| pub format: String, | |
| pub error: chrono::ParseError, | |
| } | |
| impl FromStr for Date { | |
| type Err = DateParseError; | |
| fn from_str(s: &str) -> Result<Date, DateParseError> { | |
| match DateTime::parse_from_rfc3339(s) { | |
| Ok(value) => Ok(Date(value.with_timezone(&Utc))), | |
| Err(error) => Err(DateParseError { | |
| input: s.to_string(), | |
| format: format!("RFC 3339"), | |
| error, | |
| }), | |
| } | |
| } | |
| } | |
| impl Serialize for Date { | |
| fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error> | |
| where | |
| S: serde::ser::Serializer, | |
| { | |
| serializer.serialize_str(&self.0.to_rfc3339()) | |
| } | |
| } | |
| impl<'de> Deserialize<'de> for Date { | |
| fn deserialize<D>(deserializer: D) -> ::std::result::Result<Date, D::Error> | |
| where | |
| D: serde::de::Deserializer<'de>, | |
| { | |
| struct DateVisitor; | |
| impl<'de> serde::de::Visitor<'de> for DateVisitor { | |
| type Value = Date; | |
| fn visit_str<E>(self, value: &str) -> ::std::result::Result<Date, E> | |
| where | |
| E: serde::de::Error, | |
| { | |
| Date::from_str(value).map_err(|_| { | |
| serde::de::Error::invalid_value(serde::de::Unexpected::Str(value), &self) | |
| }) | |
| } | |
| fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| f.write_str("an RFC 3339 date") | |
| } | |
| } | |
| deserializer.deserialize_str(DateVisitor) | |
| } | |
| } | |
| #[derive(Debug, Clone, Deserialize, Serialize)] | |
| pub struct Commit { | |
| pub sha: String, | |
| pub date: Date, | |
| } | |
| impl Commit { | |
| pub fn is_try(&self) -> bool { | |
| self.date.0.naive_utc().date() == NaiveDate::from_ymd(2000, 1, 1) | |
| } | |
| } | |
| impl std::hash::Hash for Commit { | |
| fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) { | |
| self.sha.hash(hasher); | |
| } | |
| } | |
| impl PartialEq for Commit { | |
| fn eq(&self, other: &Self) -> bool { | |
| self.sha == other.sha | |
| } | |
| } | |
| impl Eq for Commit {} | |
| impl PartialOrd for Commit { | |
| fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | |
| Some(self.cmp(&other)) | |
| } | |
| } | |
| impl Ord for Commit { | |
| fn cmp(&self, other: &Self) -> Ordering { | |
| self.date | |
| .cmp(&other.date) | |
| .then_with(|| self.sha.cmp(&other.sha)) | |
| } | |
| } | |
| #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] | |
| pub struct Patch { | |
| index: usize, | |
| pub name: String, | |
| path: PathBuf, | |
| } | |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize, Serialize)] | |
| pub enum BenchmarkState { | |
| Clean, | |
| Nll, | |
| IncrementalStart, | |
| IncrementalClean, | |
| IncrementalPatched(Patch), | |
| } | |
| impl BenchmarkState { | |
| pub fn is_base_compile(&self) -> bool { | |
| if let BenchmarkState::Clean = *self { | |
| true | |
| } else { | |
| false | |
| } | |
| } | |
| pub fn is_patch(&self) -> bool { | |
| if let BenchmarkState::IncrementalPatched(_) = *self { | |
| true | |
| } else { | |
| false | |
| } | |
| } | |
| pub fn name(&self) -> String { | |
| match *self { | |
| BenchmarkState::Clean => format!("clean"), | |
| BenchmarkState::Nll => format!("nll"), | |
| BenchmarkState::IncrementalStart => format!("baseline incremental"), | |
| BenchmarkState::IncrementalClean => format!("clean incremental"), | |
| BenchmarkState::IncrementalPatched(ref patch) => { | |
| format!("patched incremental: {}", patch.name) | |
| } | |
| } | |
| } | |
| // Otherwise we end up with "equivalent benchmarks" looking different, | |
| // e.g. 8-println.patch vs. 0-println.patch | |
| pub fn erase_path(mut self) -> Self { | |
| match &mut self { | |
| BenchmarkState::IncrementalPatched(patch) => { | |
| patch.index = 0; | |
| patch.path = PathBuf::new(); | |
| } | |
| _ => {} | |
| } | |
| self | |
| } | |
| } | |
| #[derive(Debug, Clone, Deserialize, Serialize)] | |
| pub struct Benchmark { | |
| pub runs: Vec<Run>, | |
| pub name: String, | |
| } | |
| #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] | |
| pub struct Stat { | |
| pub name: String, | |
| pub cnt: f64, | |
| } | |
| #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] | |
| pub struct Run { | |
| pub stats: Vec<Stat>, | |
| #[serde(default)] | |
| pub check: bool, | |
| pub release: bool, | |
| pub state: BenchmarkState, | |
| } | |
| #[derive(Debug, Clone, Deserialize, Serialize)] | |
| pub struct CommitData { | |
| pub commit: Commit, | |
| // String in Result is the output of the command that failed | |
| pub benchmarks: BTreeMap<String, Result<Benchmark, String>>, | |
| pub triple: String, | |
| } | |
| fn main() -> Result<(), std::io::Error> { | |
| let client = Client::new("http://localhost:8086", "rust"); | |
| for arg in env::args().skip(1) { | |
| let file = fs::read(arg)?; | |
| let parsed: CommitData = serde_json::from_slice(&file)?; | |
| let mut points = Vec::new(); | |
| for (bench, result) in parsed.benchmarks { | |
| if let Ok(Benchmark { runs, .. }) = result { | |
| for run in runs { | |
| let mut point = Point::new("rustc_perf"); | |
| point.add_tag("bench", Value::String(bench.clone())); | |
| point.add_tag("state", Value::String(run.state.name().to_owned())); | |
| point.add_field("commit", Value::String(parsed.commit.sha.clone())); | |
| if run.check { | |
| point.add_tag("build", Value::String("check".to_owned())); | |
| } else if run.release { | |
| point.add_tag("build", Value::String("release".to_owned())); | |
| } else { | |
| point.add_tag("build", Value::String("debug".to_owned())); | |
| } | |
| for stat in run.stats { | |
| point.add_field(stat.name, Value::Float(stat.cnt)); | |
| } | |
| point.add_timestamp(parsed.commit.date.0.timestamp()); | |
| points.push(point); | |
| } | |
| } | |
| } | |
| client.write_points(Points::create_new(points), Some(Precision::Seconds), None).unwrap(); | |
| } | |
| Ok(()) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment