diff --git a/Cargo.lock b/Cargo.lock index 45a7c31af177..c7e71272fb0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2777,6 +2777,10 @@ dependencies = [ "serde_json", "swc", "swc_common", + "tracing", + "tracing-chrome", + "tracing-futures", + "tracing-subscriber", "walkdir", ] diff --git a/crates/swc/src/lib.rs b/crates/swc/src/lib.rs index 012c40cadf10..e6e8406d1074 100644 --- a/crates/swc/src/lib.rs +++ b/crates/swc/src/lib.rs @@ -684,6 +684,7 @@ impl Compiler { } } + #[tracing::instrument(level = "trace", skip_all)] pub fn read_config(&self, opts: &Options, name: &FileName) -> Result, Error> { static CUR_DIR: Lazy = Lazy::new(|| { if cfg!(target_arch = "wasm32") { @@ -789,6 +790,7 @@ impl Compiler { /// This method handles merging of config. /// /// This method does **not** parse module. + #[tracing::instrument(level = "trace", skip_all)] pub fn parse_js_as_input<'a, P>( &'a self, fm: Lrc, @@ -836,6 +838,7 @@ impl Compiler { }) } + #[tracing::instrument(level = "trace", skip_all)] pub fn transform( &self, handler: &Handler, @@ -863,6 +866,7 @@ impl Compiler { /// /// This means, you can use `noop_visit_type`, `noop_fold_type` and /// `noop_visit_mut_type` in your visitor to reduce the binary size. + #[tracing::instrument(level = "trace", skip_all)] pub fn process_js_with_custom_pass( &self, fm: Arc, @@ -923,6 +927,7 @@ impl Compiler { .context("failed to process js file") } + #[tracing::instrument(level = "trace", skip_all)] pub fn process_js_file( &self, fm: Arc, @@ -932,6 +937,7 @@ impl Compiler { self.process_js_with_custom_pass(fm, None, handler, opts, |_| noop(), |_| noop()) } + #[tracing::instrument(level = "trace", skip_all)] pub fn minify( &self, fm: Arc, @@ -1053,6 +1059,7 @@ impl Compiler { /// You can use custom pass with this method. /// /// There exists a [PassBuilder] to help building custom passes. + #[tracing::instrument(level = "trace", skip_all)] pub fn process_js( &self, handler: &Handler, @@ -1065,6 +1072,7 @@ impl Compiler { self.process_js_with_custom_pass(fm, Some(program), handler, opts, |_| noop(), |_| noop()) } + #[tracing::instrument(level = "trace", skip_all)] fn process_js_inner( &self, handler: &Handler, @@ -1107,6 +1115,7 @@ impl Compiler { } } +#[tracing::instrument(level = "trace", skip_all)] fn load_swcrc(path: &Path) -> Result { fn convert_json_err(e: serde_json::Error) -> Error { let line = e.line(); diff --git a/crates/swc_cli/Cargo.toml b/crates/swc_cli/Cargo.toml index a5cc5a2e5034..91bdf1a56165 100644 --- a/crates/swc_cli/Cargo.toml +++ b/crates/swc_cli/Cargo.toml @@ -23,6 +23,10 @@ relative-path = "1.6.1" serde = { version = "1", features = ["derive"] } serde_json = { version = "1", features = ["unbounded_depth"] } atty = "0.2.14" +tracing-chrome = "0.4.0" +tracing-futures = "0.2.5" +tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } +tracing = "0.1.31" [dependencies.path-absolutize] version = "3.0.11" diff --git a/crates/swc_cli/src/commands/compile.rs b/crates/swc_cli/src/commands/compile.rs index ef752c7d04fc..f9495607f85e 100644 --- a/crates/swc_cli/src/commands/compile.rs +++ b/crates/swc_cli/src/commands/compile.rs @@ -15,6 +15,8 @@ use swc::{ try_with_handler, Compiler, TransformOutput, }; use swc_common::{sync::Lazy, FileName, FilePathMapping, SourceMap}; +use tracing_chrome::{ChromeLayerBuilder, FlushGuard}; +use tracing_subscriber::{prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt}; use walkdir::WalkDir; /// Configuration option for transform files. @@ -77,6 +79,16 @@ pub struct CompileOptions { /// Files to compile #[clap(group = "input")] files: Vec, + + /// Enable experimantal trace profiling + /// generates trace compatible with trace event format. + #[clap(group = "experimental_trace", long)] + experimental_trace: bool, + + /// Set file name for the trace output. If not specified, + /// `trace-{unix epoch time}.json` will be used by default. + #[clap(group = "experimental_trace", long)] + trace_out_file: Option, //Flags legacy @swc/cli supports, might need some thoughts if we need support same. //log_watch_compilation: bool, //copy_files: bool, @@ -97,6 +109,7 @@ static DEFAULT_EXTENSIONS: &[&str] = &["js", "jsx", "es6", "es", "mjs", "ts", "t /// Infer list of files to be transformed from cli arguments. /// If given input is a directory, it'll traverse it and collect all supported /// files. +#[tracing::instrument(level = "trace", skip_all)] fn get_files_list( raw_files_input: &[PathBuf], extensions: &[String], @@ -246,8 +259,27 @@ fn collect_stdin_input() -> Option { ) } +fn init_trace(out_file: &Option) -> Option { + let layer = if let Some(trace_out_file) = out_file { + ChromeLayerBuilder::new() + .file(trace_out_file.clone()) + .include_args(true) + } else { + ChromeLayerBuilder::new().include_args(true) + }; + + let (chrome_layer, guard) = layer.build(); + tracing_subscriber::registry() + .with(chrome_layer) + .try_init() + .expect("Should able to register trace"); + + Some(guard) +} + impl super::CommandRunner for CompileOptions { - fn execute(&self) -> anyhow::Result<()> { + #[tracing::instrument(level = "trace", skip_all)] + fn execute_inner(&self) -> anyhow::Result<()> { let stdin_input = collect_stdin_input(); if stdin_input.is_some() && !self.files.is_empty() { @@ -259,6 +291,8 @@ impl super::CommandRunner for CompileOptions { } if let Some(stdin_input) = stdin_input { + let span = tracing::span!(tracing::Level::TRACE, "compile_stdin"); + let _ = span.enter(); let comp = COMPILER.clone(); let result = try_with_handler(comp.cm.clone(), false, |handler| { @@ -323,4 +357,20 @@ impl super::CommandRunner for CompileOptions { Ok(()) } + + fn execute(&self) -> anyhow::Result<()> { + let guard = if self.experimental_trace { + init_trace(&self.trace_out_file) + } else { + None + }; + + let ret = self.execute_inner(); + + if let Some(guard) = guard { + guard.flush(); + } + + ret + } } diff --git a/crates/swc_cli/src/commands/mod.rs b/crates/swc_cli/src/commands/mod.rs index e6b78e696224..a8357c610a6f 100644 --- a/crates/swc_cli/src/commands/mod.rs +++ b/crates/swc_cli/src/commands/mod.rs @@ -13,7 +13,7 @@ pub enum Command { #[clap(subcommand)] Plugin(PluginSubcommand), /// Run SWC's transformer. - Compile(CompileOptions), + Compile(Box), } #[derive(Parser)] @@ -25,4 +25,5 @@ pub struct SwcCliOptions { pub trait CommandRunner { fn execute(&self) -> anyhow::Result<()>; + fn execute_inner(&self) -> anyhow::Result<()>; } diff --git a/crates/swc_cli/src/commands/plugin.rs b/crates/swc_cli/src/commands/plugin.rs index 0775f965f659..58b53c61e571 100644 --- a/crates/swc_cli/src/commands/plugin.rs +++ b/crates/swc_cli/src/commands/plugin.rs @@ -109,6 +109,10 @@ fn write_ignore_file(base_path: &Path) -> Result<()> { } impl super::CommandRunner for PluginScaffoldOptions { + fn execute_inner(&self) -> Result<()> { + Ok(()) + } + /// Create a rust project for the plugin from template. /// This largely mimic https://github.com/rust-lang/cargo/blob/master/src/cargo/ops/cargo_new.rs, /// but also thinner implementation based on some assumptions like skipping @@ -217,7 +221,7 @@ pub struct TransformVisitor; impl VisitMut for TransformVisitor { // Implement necessary visit_mut_* methods for actual custom transform. - // A comprehensive list of possible visitor methods can be found here: + // A comprehensive list of possible visitor methods can be found here: // https://rustdoc.swc.rs/swc_ecma_visit/trait.VisitMut.html }