tracing: write some blackbox events as tracing events

Summary:
This is the start of migrating blackbox events to tracing events. The
motivation is to have a single data source for log processing (for simplicity)
and the tracing data seems a better fit, since it can represent a tree of
spans, instead of just a flat list. Eventually blackbox might be mostly
a wrapper for tracing data, with some minimal support for logging some indexed
events.

Reviewed By: DurhamG

Differential Revision: D19797710

fbshipit-source-id: 034f17fb5552242b60e759559a202fd26061f1f1
This commit is contained in:
Jun Wu 2020-03-19 10:19:57 -07:00 committed by Facebook GitHub Bot
parent 609330486e
commit 6ffdcebadf
5 changed files with 86 additions and 51 deletions

View File

@ -1219,7 +1219,7 @@ def getall(repo, subset, x):
@predicate("_all()", safe=True) @predicate("_all()", safe=True)
def getall(repo, subset, x): def _all(repo, subset, x):
"""All changesets regardless of visibility""" """All changesets regardless of visibility"""
return subset return subset

View File

@ -12,6 +12,7 @@ cliparser = { path = "../cliparser" }
indexedlog = { path = "../indexedlog" } indexedlog = { path = "../indexedlog" }
thiserror = "1.0.5" thiserror = "1.0.5"
thrift-types = { path = "../thrift-types" } thrift-types = { path = "../thrift-types" }
tracing = "0.1"
util = { path = "../util" } util = { path = "../util" }
[dev-dependencies] [dev-dependencies]

View File

@ -232,6 +232,10 @@ pub fn dispatch(command_table: &CommandTable, args: Vec<String>, io: &mut IO) ->
let (command_name, command_arg_len) = let (command_name, command_arg_len) =
find_command_name(|name| command_table.get(name).is_some(), &expanded) find_command_name(|name| command_table.get(name).is_some(), &expanded)
.ok_or_else(|| errors::UnknownCommand(command_name))?; .ok_or_else(|| errors::UnknownCommand(command_name))?;
tracing::info!(
name = "log:command-row",
command = AsRef::<str>::as_ref(&command_name)
);
let mut new_args = Vec::with_capacity(args.len()); let mut new_args = Vec::with_capacity(args.len());
new_args.extend_from_slice(&args[..first_arg_index]); new_args.extend_from_slice(&args[..first_arg_index]);

View File

@ -7,6 +7,7 @@
use crate::{commands, HgPython}; use crate::{commands, HgPython};
use anyhow::Result; use anyhow::Result;
use blackbox::serde_json;
use clidispatch::{dispatch, errors}; use clidispatch::{dispatch, errors};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::env; use std::env;
@ -19,7 +20,7 @@ use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::time::SystemTime; use std::time::SystemTime;
use tracing::dispatcher::{self, Dispatch}; use tracing::dispatcher::{self, Dispatch};
use tracing::{span, Level}; use tracing::Level;
use tracing_collector::{TracingCollector, TracingData}; use tracing_collector::{TracingCollector, TracingData};
/// Run a Rust or Python command. /// Run a Rust or Python command.
@ -37,6 +38,10 @@ pub fn run_command(args: Vec<String>, io: &mut clidispatch::io::IO) -> i32 {
return HgPython::new(&args).run_hg(args, io); return HgPython::new(&args).run_hg(args, io);
} }
// Setup tracing early since "log_start" will use it immediately.
// The tracing clock starts ticking from here.
let (_tracing_level, tracing_data) = setup_tracing();
// This is intended to be "process start". "exec/hgmain" seems to be // This is intended to be "process start". "exec/hgmain" seems to be
// a better place for it. However, chg makes it tricky. Because if hgmain // a better place for it. However, chg makes it tricky. Because if hgmain
// decides to use chg, then there is no way to figure out which `blackbox` // decides to use chg, then there is no way to figure out which `blackbox`
@ -47,7 +52,7 @@ pub fn run_command(args: Vec<String>, io: &mut clidispatch::io::IO) -> i32 {
// which is a bit more desiable. Since run_command is very close to process // which is a bit more desiable. Since run_command is very close to process
// start, it should reflect the duration of the command relatively // start, it should reflect the duration of the command relatively
// accurately, at least for non-chg cases. // accurately, at least for non-chg cases.
log_start(args.clone(), now); let span = log_start(args.clone(), now);
// Ad-hoc environment variable: EDENSCM_TRACE_OUTPUT. A more standard way // Ad-hoc environment variable: EDENSCM_TRACE_OUTPUT. A more standard way
// to access the data is via the blackbox interface. // to access the data is via the blackbox interface.
@ -72,13 +77,6 @@ pub fn run_command(args: Vec<String>, io: &mut clidispatch::io::IO) -> i32 {
// if hgcommands is nested (ex. for "hg continue"). // if hgcommands is nested (ex. for "hg continue").
let _ = env_logger::try_init(); let _ = env_logger::try_init();
let span = span!(
Level::INFO,
"run_command",
name = AsRef::<str>::as_ref(&args[1..args.len().min(64)].join(" ")),
exitcode = "",
);
let exit_code = { let exit_code = {
let _guard = span.enter(); let _guard = span.enter();
let table = commands::table(); let table = commands::table();
@ -112,13 +110,13 @@ pub fn run_command(args: Vec<String>, io: &mut clidispatch::io::IO) -> i32 {
HgPython::new(&args).run_hg(args, io) HgPython::new(&args).run_hg(args, io)
} }
}; };
span.record("exitcode", &exit_code); span.record("exit_code", &exit_code);
exit_code exit_code
}; };
let _ = maybe_write_trace(io, &tracing_data, trace_output_path); let _ = maybe_write_trace(io, &tracing_data, trace_output_path);
log_end(exit_code as u8, now, tracing_data); log_end(exit_code as u8, now, tracing_data, &span);
// Sync the blackbox before returning: this exit code is going to be used to process::exit(), // Sync the blackbox before returning: this exit code is going to be used to process::exit(),
// so we need to flush now. // so we need to flush now.
@ -234,7 +232,7 @@ pub(crate) fn write_trace(
} }
Format::SpansJSON => { Format::SpansJSON => {
let spans = data.tree_spans::<&str>(); let spans = data.tree_spans::<&str>();
blackbox::serde_json::to_writer(&mut out, &spans)?; serde_json::to_writer(&mut out, &spans)?;
out.flush()?; out.flush()?;
} }
Format::TraceEventGzip => { Format::TraceEventGzip => {
@ -254,7 +252,7 @@ pub(crate) fn write_trace(
Ok(()) Ok(())
} }
fn log_start(args: Vec<String>, now: SystemTime) { fn log_start(args: Vec<String>, now: SystemTime) -> tracing::Span {
let inside_test = is_inside_test(); let inside_test = is_inside_test();
let (uid, pid, nice) = if inside_test { let (uid, pid, nice) = if inside_test {
(0, 0, 0) (0, 0, 0)
@ -275,15 +273,8 @@ fn log_start(args: Vec<String>, now: SystemTime) {
} }
}; };
blackbox::log(&blackbox::event::Event::Start {
pid,
uid,
nice,
args,
timestamp_ms: epoch_ms(now),
});
if let Ok(tags) = std::env::var("EDENSCM_BLACKBOX_TAGS") { if let Ok(tags) = std::env::var("EDENSCM_BLACKBOX_TAGS") {
tracing::info!(name = "blackbox_tags", tags = AsRef::<str>::as_ref(&tags));
let names: Vec<String> = tags.split_whitespace().map(ToString::to_string).collect(); let names: Vec<String> = tags.split_whitespace().map(ToString::to_string).collect();
blackbox::log(&blackbox::event::Event::Tags { names }); blackbox::log(&blackbox::event::Event::Tags { names });
} }
@ -303,13 +294,42 @@ fn log_start(args: Vec<String>, now: SystemTime) {
ppid = procinfo::parent_pid(ppid); ppid = procinfo::parent_pid(ppid);
} }
} }
let span = tracing::info_span!(
"Run Command",
pid = pid,
uid = uid,
nice = nice,
args = AsRef::<str>::as_ref(&serde_json::to_string(&args).unwrap()),
parent_pids = AsRef::<str>::as_ref(&serde_json::to_string(&parent_pids).unwrap()),
parent_names = AsRef::<str>::as_ref(&serde_json::to_string(&parent_names).unwrap()),
// Reserved for log_end.
exit_code = 0,
max_rss = 0,
);
blackbox::log(&blackbox::event::Event::Start {
pid,
uid,
nice,
args,
timestamp_ms: epoch_ms(now),
});
blackbox::log(&blackbox::event::Event::ProcessTree { blackbox::log(&blackbox::event::Event::ProcessTree {
names: parent_names, names: parent_names,
pids: parent_pids, pids: parent_pids,
}); });
span
} }
fn log_end(exit_code: u8, now: SystemTime, tracing_data: Arc<Mutex<TracingData>>) { fn log_end(
exit_code: u8,
now: SystemTime,
tracing_data: Arc<Mutex<TracingData>>,
span: &tracing::Span,
) {
let inside_test = is_inside_test(); let inside_test = is_inside_test();
let duration_ms = if inside_test { let duration_ms = if inside_test {
0 0
@ -325,6 +345,9 @@ fn log_end(exit_code: u8, now: SystemTime, tracing_data: Arc<Mutex<TracingData>>
procinfo::max_rss_bytes() procinfo::max_rss_bytes()
}; };
span.record("exit_code", &exit_code);
span.record("max_rss", &max_rss);
blackbox::log(&blackbox::event::Event::Finish { blackbox::log(&blackbox::event::Event::Finish {
exit_code, exit_code,
max_rss, max_rss,

View File

@ -15,8 +15,15 @@
$ sed 's/line [0-9]*$/_/' trace $ sed 's/line [0-9]*$/_/' trace
Process _ Thread _: Process _ Thread _:
Start Dur.ms | Name Source Start Dur.ms | Name Source
5 ... | up 26805aba1e600a82e93661149f2313866a221a7b --config tracing.stderr=True hgcommands::run _ 5 ... | Run Command hgcommands::run _
| - exitcode = : | - pid = 0 :
| - uid = 0 :
| - nice = 0 :
| - args = ["hg","up","26805aba1e600a82e9... :
| - parent_pids = [] :
| - parent_names = [] :
| - exit_code = 0 :
| - max_rss = 0 :
10 +5 \ Initialize Python hgcommands::hgpython _ 10 +5 \ Initialize Python hgcommands::hgpython _
20 +5 \ import edenscm hgcommands::hgpython _ 20 +5 \ import edenscm hgcommands::hgpython _
30 +145 \ Main Python Command (perftrace) 30 +145 \ Main Python Command (perftrace)