mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Add metrics
This commit is contained in:
parent
14a3a713c7
commit
de714640bd
4 changed files with 255 additions and 0 deletions
38
.github/workflows/metrics.yaml
vendored
Normal file
38
.github/workflows/metrics.yaml
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
name: rustdoc
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_INCREMENTAL: 0
|
||||||
|
CARGO_NET_RETRY: 10
|
||||||
|
RUSTFLAGS: -D warnings
|
||||||
|
RUSTUP_MAX_RETRIES: 10
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
rustdoc:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Checkout metrics repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: "rust-analyzer/metrics"
|
||||||
|
path: "target/metrics"
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
profile: minimal
|
||||||
|
override: true
|
||||||
|
components: rust-src
|
||||||
|
|
||||||
|
- name: Collect metrics
|
||||||
|
run: cargo xtask metrics
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -7,6 +7,7 @@ pub mod install;
|
||||||
pub mod release;
|
pub mod release;
|
||||||
pub mod dist;
|
pub mod dist;
|
||||||
pub mod pre_commit;
|
pub mod pre_commit;
|
||||||
|
pub mod metrics;
|
||||||
|
|
||||||
pub mod codegen;
|
pub mod codegen;
|
||||||
mod ast_src;
|
mod ast_src;
|
||||||
|
|
|
@ -15,6 +15,7 @@ use xtask::{
|
||||||
codegen::{self, Mode},
|
codegen::{self, Mode},
|
||||||
dist::DistCmd,
|
dist::DistCmd,
|
||||||
install::{ClientOpt, InstallCmd, Malloc, ServerOpt},
|
install::{ClientOpt, InstallCmd, Malloc, ServerOpt},
|
||||||
|
metrics::run_metrics,
|
||||||
not_bash::pushd,
|
not_bash::pushd,
|
||||||
pre_commit, project_root,
|
pre_commit, project_root,
|
||||||
release::{PromoteCmd, ReleaseCmd},
|
release::{PromoteCmd, ReleaseCmd},
|
||||||
|
@ -117,6 +118,7 @@ FLAGS:
|
||||||
args.finish()?;
|
args.finish()?;
|
||||||
DistCmd { nightly, client_version }.run()
|
DistCmd { nightly, client_version }.run()
|
||||||
}
|
}
|
||||||
|
"metrics" => run_metrics(),
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"\
|
"\
|
||||||
|
|
214
xtask/src/metrics.rs
Normal file
214
xtask/src/metrics.rs
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
use std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
env,
|
||||||
|
fmt::{self, Write as _},
|
||||||
|
io::Write as _,
|
||||||
|
time::{Instant, SystemTime, UNIX_EPOCH},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{bail, format_err, Result};
|
||||||
|
|
||||||
|
use crate::not_bash::{fs2, pushd, rm_rf, run};
|
||||||
|
|
||||||
|
type Unit = &'static str;
|
||||||
|
|
||||||
|
pub fn run_metrics() -> Result<()> {
|
||||||
|
let mut metrics = Metrics::new()?;
|
||||||
|
metrics.measure_build()?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let _d = pushd("target/metrics");
|
||||||
|
let mut file = std::fs::OpenOptions::new().append(true).open("metrics.json")?;
|
||||||
|
writeln!(file, "{}", metrics.json())?;
|
||||||
|
run!("git commit -am'📈'")?;
|
||||||
|
|
||||||
|
if let Ok(actor) = env::var("GITHUB_ACTOR") {
|
||||||
|
let token = env::var("GITHUB_TOKEN").unwrap();
|
||||||
|
let repo = format!("https://{}:{}@github.com/rust-analyzer/metrics.git", actor, token);
|
||||||
|
run!("git push {}", repo)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eprintln!("{:#?}\n", metrics);
|
||||||
|
eprintln!("{}", metrics.json());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metrics {
|
||||||
|
fn measure_build(&mut self) -> Result<()> {
|
||||||
|
run!("cargo fetch")?;
|
||||||
|
rm_rf("./target/release")?;
|
||||||
|
|
||||||
|
let build = Instant::now();
|
||||||
|
run!("cargo build --release --package rust-analyzer --bin rust-analyzer")?;
|
||||||
|
let build = build.elapsed();
|
||||||
|
self.report("build", build.as_millis() as u64, "ms");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Metrics {
|
||||||
|
host: Host,
|
||||||
|
timestamp: SystemTime,
|
||||||
|
revision: String,
|
||||||
|
metrics: BTreeMap<String, (u64, Unit)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Host {
|
||||||
|
os: String,
|
||||||
|
cpu: String,
|
||||||
|
mem: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metrics {
|
||||||
|
fn new() -> Result<Metrics> {
|
||||||
|
let host = Host::new()?;
|
||||||
|
let timestamp = SystemTime::now();
|
||||||
|
let revision = run!("git rev-parse HEAD")?;
|
||||||
|
Ok(Metrics { host, timestamp, revision, metrics: BTreeMap::new() })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn report(&mut self, name: &str, value: u64, unit: Unit) {
|
||||||
|
self.metrics.insert(name.into(), (value, unit));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json(&self) -> Json {
|
||||||
|
let mut json = Json::default();
|
||||||
|
self.to_json(&mut json);
|
||||||
|
json
|
||||||
|
}
|
||||||
|
fn to_json(&self, json: &mut Json) {
|
||||||
|
json.begin_object();
|
||||||
|
{
|
||||||
|
json.field("host");
|
||||||
|
self.host.to_json(json);
|
||||||
|
|
||||||
|
json.field("timestamp");
|
||||||
|
let timestamp = self.timestamp.duration_since(UNIX_EPOCH).unwrap();
|
||||||
|
json.number(timestamp.as_secs() as f64);
|
||||||
|
|
||||||
|
json.field("revision");
|
||||||
|
json.string(&self.revision);
|
||||||
|
|
||||||
|
json.field("metrics");
|
||||||
|
json.begin_object();
|
||||||
|
{
|
||||||
|
for (k, &(value, unit)) in &self.metrics {
|
||||||
|
json.field(k);
|
||||||
|
json.begin_array();
|
||||||
|
{
|
||||||
|
json.number(value as f64);
|
||||||
|
json.string(unit);
|
||||||
|
}
|
||||||
|
json.end_array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json.end_object()
|
||||||
|
}
|
||||||
|
json.end_object();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Host {
|
||||||
|
fn new() -> Result<Host> {
|
||||||
|
if cfg!(not(target_os = "linux")) {
|
||||||
|
bail!("can only collect metrics on Linux ");
|
||||||
|
}
|
||||||
|
|
||||||
|
let os = read_field("/etc/os-release", "PRETTY_NAME=")?.trim_matches('"').to_string();
|
||||||
|
|
||||||
|
let cpu =
|
||||||
|
read_field("/proc/cpuinfo", "model name")?.trim_start_matches(':').trim().to_string();
|
||||||
|
|
||||||
|
let mem = read_field("/proc/meminfo", "MemTotal:")?;
|
||||||
|
|
||||||
|
return Ok(Host { os, cpu, mem });
|
||||||
|
|
||||||
|
fn read_field<'a>(path: &str, field: &str) -> Result<String> {
|
||||||
|
let text = fs2::read_to_string(path)?;
|
||||||
|
|
||||||
|
let line = text
|
||||||
|
.lines()
|
||||||
|
.find(|it| it.starts_with(field))
|
||||||
|
.ok_or_else(|| format_err!("can't parse {}", path))?;
|
||||||
|
Ok(line[field.len()..].trim().to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn to_json(&self, json: &mut Json) {
|
||||||
|
json.begin_object();
|
||||||
|
{
|
||||||
|
json.field("os");
|
||||||
|
json.string(&self.os);
|
||||||
|
|
||||||
|
json.field("cpu");
|
||||||
|
json.string(&self.cpu);
|
||||||
|
|
||||||
|
json.field("mem");
|
||||||
|
json.string(&self.mem);
|
||||||
|
}
|
||||||
|
json.end_object();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Json {
|
||||||
|
object_comma: bool,
|
||||||
|
array_comma: bool,
|
||||||
|
buf: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Json {
|
||||||
|
fn begin_object(&mut self) {
|
||||||
|
self.object_comma = false;
|
||||||
|
self.buf.push('{');
|
||||||
|
}
|
||||||
|
fn end_object(&mut self) {
|
||||||
|
self.buf.push('}')
|
||||||
|
}
|
||||||
|
fn begin_array(&mut self) {
|
||||||
|
self.array_comma = false;
|
||||||
|
self.buf.push('[');
|
||||||
|
}
|
||||||
|
fn end_array(&mut self) {
|
||||||
|
self.buf.push(']')
|
||||||
|
}
|
||||||
|
fn field(&mut self, name: &str) {
|
||||||
|
self.object_comma();
|
||||||
|
self.string_token(name);
|
||||||
|
self.buf.push(':');
|
||||||
|
}
|
||||||
|
fn string(&mut self, value: &str) {
|
||||||
|
self.array_comma();
|
||||||
|
self.string_token(value);
|
||||||
|
}
|
||||||
|
fn string_token(&mut self, value: &str) {
|
||||||
|
self.buf.push('"');
|
||||||
|
self.buf.extend(value.escape_default());
|
||||||
|
self.buf.push('"');
|
||||||
|
}
|
||||||
|
fn number(&mut self, value: f64) {
|
||||||
|
self.array_comma();
|
||||||
|
write!(self.buf, "{}", value).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn array_comma(&mut self) {
|
||||||
|
if self.array_comma {
|
||||||
|
self.buf.push(',');
|
||||||
|
}
|
||||||
|
self.array_comma = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn object_comma(&mut self) {
|
||||||
|
if self.object_comma {
|
||||||
|
self.buf.push(',');
|
||||||
|
}
|
||||||
|
self.object_comma = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Json {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.buf)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue