Skip to content

Commit

Permalink
feat: add noir-inspector (#7136)
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite authored Jan 24, 2025
1 parent 3e2fe1f commit a0704aa
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 113 deletions.
30 changes: 30 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ members = [
"tooling/nargo_cli",
"tooling/nargo_toml",
"tooling/noirc_artifacts",
"tooling/noirc_artifacts_info",
"tooling/noirc_abi",
"tooling/noirc_abi_wasm",
"tooling/acvm_cli",
"tooling/profiler",
"tooling/inspector",
# ACVM
"acvm-repo/acir_field",
"acvm-repo/acir",
Expand All @@ -35,7 +37,12 @@ members = [
# Utility crates
"utils/iter-extended",
]
default-members = ["tooling/nargo_cli", "tooling/acvm_cli", "tooling/profiler"]
default-members = [
"tooling/nargo_cli",
"tooling/acvm_cli",
"tooling/profiler",
"tooling/inspector",
]
resolver = "2"

[workspace.package]
Expand Down Expand Up @@ -83,6 +90,7 @@ noir_lsp = { path = "tooling/lsp" }
noir_debugger = { path = "tooling/debugger" }
noirc_abi = { path = "tooling/noirc_abi" }
noirc_artifacts = { path = "tooling/noirc_artifacts" }
noirc_artifacts_info = { path = "tooling/noirc_artifacts_info" }

# Arkworks
ark-bn254 = { version = "^0.5.0", default-features = false, features = [
Expand Down
28 changes: 28 additions & 0 deletions tooling/inspector/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "noir_inspector"
description = "Inspector for noir build artifacts"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
repository.workspace = true

[lints]
workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[[bin]]
name = "noir-inspector"
path = "src/main.rs"

[dependencies]
clap.workspace = true
serde.workspace = true
serde_json.workspace = true
color-eyre.workspace = true
const_format.workspace = true
acir.workspace = true
noirc_artifacts.workspace = true
noirc_artifacts_info.workspace = true
35 changes: 35 additions & 0 deletions tooling/inspector/src/cli/info_cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use std::path::PathBuf;

use clap::Args;
use color_eyre::eyre;
use noirc_artifacts::program::ProgramArtifact;
use noirc_artifacts_info::{count_opcodes_and_gates_in_program, show_info_report, InfoReport};

#[derive(Debug, Clone, Args)]
pub(crate) struct InfoCommand {
/// The artifact to inspect
artifact: PathBuf,

/// Output a JSON formatted report. Changes to this format are not currently considered breaking.
#[clap(long, hide = true)]
json: bool,
}

pub(crate) fn run(args: InfoCommand) -> eyre::Result<()> {
let file = std::fs::File::open(args.artifact.clone())?;
let artifact: ProgramArtifact = serde_json::from_reader(file)?;

let package_name = args
.artifact
.with_extension("")
.file_name()
.map(|s| s.to_string_lossy().to_string())
.unwrap_or_else(|| "artifact".to_string());

let program_info = count_opcodes_and_gates_in_program(artifact, package_name.to_string(), None);

let info_report = InfoReport { programs: vec![program_info] };
show_info_report(info_report, args.json);

Ok(())
}
33 changes: 33 additions & 0 deletions tooling/inspector/src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use clap::{command, Parser, Subcommand};
use color_eyre::eyre;
use const_format::formatcp;

mod info_cmd;
mod print_acir_cmd;

const INSPECTOR_VERSION: &str = env!("CARGO_PKG_VERSION");

static VERSION_STRING: &str = formatcp!("version = {}\n", INSPECTOR_VERSION,);

#[derive(Parser, Debug)]
#[command(name="Noir inspector", author, version=VERSION_STRING, about, long_about = None)]
struct InspectorCli {
#[command(subcommand)]
command: InspectorCommand,
}

#[non_exhaustive]
#[derive(Subcommand, Clone, Debug)]
enum InspectorCommand {
Info(info_cmd::InfoCommand),
PrintAcir(print_acir_cmd::PrintAcirCommand),
}

pub(crate) fn start_cli() -> eyre::Result<()> {
let InspectorCli { command } = InspectorCli::parse();

match command {
InspectorCommand::Info(args) => info_cmd::run(args),
InspectorCommand::PrintAcir(args) => print_acir_cmd::run(args),
}
}
21 changes: 21 additions & 0 deletions tooling/inspector/src/cli/print_acir_cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use std::path::PathBuf;

use clap::Args;
use color_eyre::eyre;
use noirc_artifacts::program::ProgramArtifact;

#[derive(Debug, Clone, Args)]
pub(crate) struct PrintAcirCommand {
/// The artifact to print
artifact: PathBuf,
}

pub(crate) fn run(args: PrintAcirCommand) -> eyre::Result<()> {
let file = std::fs::File::open(args.artifact.clone())?;
let artifact: ProgramArtifact = serde_json::from_reader(file)?;

println!("Compiled ACIR for main:");
println!("{}", artifact.bytecode);

Ok(())
}
8 changes: 8 additions & 0 deletions tooling/inspector/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
mod cli;

fn main() {
if let Err(report) = cli::start_cli() {
eprintln!("{report:?}");
std::process::exit(1);
}
}
1 change: 1 addition & 0 deletions tooling/nargo_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ noirc_frontend = { workspace = true, features = ["bn254"] }
noirc_abi.workspace = true
noirc_errors.workspace = true
noirc_artifacts.workspace = true
noirc_artifacts_info.workspace = true
acvm = { workspace = true, features = ["bn254"] }
bn254_blackbox_solver.workspace = true
toml.workspace = true
Expand Down
120 changes: 8 additions & 112 deletions tooling/nargo_cli/src/cli/info_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ use nargo::{
use nargo_toml::{get_package_manifest, resolve_workspace_from_toml};
use noirc_abi::input_parser::Format;
use noirc_artifacts::program::ProgramArtifact;
use noirc_artifacts_info::{
count_opcodes_and_gates_in_program, show_info_report, FunctionInfo, InfoReport, ProgramInfo,
};
use noirc_driver::{CompileOptions, NOIR_ARTIFACT_VERSION_STRING};
use prettytable::{row, table, Row};
use prettytable::{row, Row};
use rayon::prelude::*;
use serde::Serialize;

Expand Down Expand Up @@ -94,74 +97,18 @@ pub(crate) fn run(mut args: InfoCommand, config: NargoConfig) -> Result<(), CliE
package.expression_width,
args.compile_options.expression_width,
);
count_opcodes_and_gates_in_program(program, &package, target_width)
let package_name = package.name.to_string();
count_opcodes_and_gates_in_program(program, package_name, Some(target_width))
})
.collect()
};

let info_report = InfoReport { programs: program_info };

if args.json {
// Expose machine-readable JSON data.
println!("{}", serde_json::to_string(&info_report).unwrap());
} else {
// Otherwise print human-readable table.
if !info_report.programs.is_empty() {
let mut program_table = table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes", Fm->"Brillig Opcodes"]);

for program_info in info_report.programs {
let program_rows: Vec<Row> = program_info.into();
for row in program_rows {
program_table.add_row(row);
}
}
program_table.printstd();
}
}
show_info_report(info_report, args.json);

Ok(())
}

#[derive(Debug, Default, Serialize)]
struct InfoReport {
programs: Vec<ProgramInfo>,
}

#[derive(Debug, Serialize)]
struct ProgramInfo {
package_name: String,
#[serde(skip)]
expression_width: ExpressionWidth,
functions: Vec<FunctionInfo>,
#[serde(skip)]
unconstrained_functions_opcodes: usize,
unconstrained_functions: Vec<FunctionInfo>,
}

impl From<ProgramInfo> for Vec<Row> {
fn from(program_info: ProgramInfo) -> Self {
let mut main = vecmap(program_info.functions, |function| {
row![
Fm->format!("{}", program_info.package_name),
Fc->format!("{}", function.name),
format!("{:?}", program_info.expression_width),
Fc->format!("{}", function.opcodes),
Fc->format!("{}", program_info.unconstrained_functions_opcodes),
]
});
main.extend(vecmap(program_info.unconstrained_functions, |function| {
row![
Fm->format!("{}", program_info.package_name),
Fc->format!("{}", function.name),
format!("N/A", ),
Fc->format!("N/A"),
Fc->format!("{}", function.opcodes),
]
}));
main
}
}

#[derive(Debug, Serialize)]
struct ContractInfo {
name: String,
Expand All @@ -171,12 +118,6 @@ struct ContractInfo {
functions: Vec<FunctionInfo>,
}

#[derive(Debug, Serialize)]
struct FunctionInfo {
name: String,
opcodes: usize,
}

impl From<ContractInfo> for Vec<Row> {
fn from(contract_info: ContractInfo) -> Self {
vecmap(contract_info.functions, |function| {
Expand All @@ -190,51 +131,6 @@ impl From<ContractInfo> for Vec<Row> {
}
}

fn count_opcodes_and_gates_in_program(
compiled_program: ProgramArtifact,
package: &Package,
expression_width: ExpressionWidth,
) -> ProgramInfo {
let functions = compiled_program
.bytecode
.functions
.into_par_iter()
.enumerate()
.map(|(i, function)| FunctionInfo {
name: compiled_program.names[i].clone(),
opcodes: function.opcodes.len(),
})
.collect();

let opcodes_len: Vec<usize> = compiled_program
.bytecode
.unconstrained_functions
.iter()
.map(|func| func.bytecode.len())
.collect();
let unconstrained_functions_opcodes = compiled_program
.bytecode
.unconstrained_functions
.into_par_iter()
.map(|function| function.bytecode.len())
.sum();
let unconstrained_info: Vec<FunctionInfo> = compiled_program
.brillig_names
.clone()
.iter()
.zip(opcodes_len)
.map(|(name, len)| FunctionInfo { name: name.clone(), opcodes: len })
.collect();

ProgramInfo {
package_name: package.name.to_string(),
expression_width,
functions,
unconstrained_functions_opcodes,
unconstrained_functions: unconstrained_info,
}
}

fn profile_brillig_execution(
binary_packages: Vec<(Package, ProgramArtifact)>,
prover_name: &str,
Expand Down Expand Up @@ -270,7 +166,7 @@ fn profile_brillig_execution(

program_info.push(ProgramInfo {
package_name: package.name.to_string(),
expression_width,
expression_width: Some(expression_width),
functions: vec![FunctionInfo { name: "main".to_string(), opcodes: 0 }],
unconstrained_functions_opcodes: profiling_samples.len(),
unconstrained_functions: vec![FunctionInfo {
Expand Down
Loading

0 comments on commit a0704aa

Please sign in to comment.