Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add noir-inspector #7136

Merged
merged 5 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading