Skip to content

Commit a8b5fa8

Browse files
authored
feat: add profile info print out (#3425)
1 parent 79a2fac commit a8b5fa8

3 files changed

Lines changed: 142 additions & 2 deletions

File tree

compiler/noirc_errors/src/debug_info.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use acvm::compiler::AcirTransformationMap;
44
use serde_with::serde_as;
55
use serde_with::DisplayFromStr;
66
use std::collections::BTreeMap;
7+
use std::collections::HashMap;
78
use std::mem;
89

910
use crate::Location;
@@ -19,6 +20,13 @@ pub struct DebugInfo {
1920
pub locations: BTreeMap<OpcodeLocation, Vec<Location>>,
2021
}
2122

23+
/// Holds OpCodes Counts for Acir and Brillig Opcodes
24+
/// To be printed with `nargo info --profile-info`
25+
pub struct OpCodesCount {
26+
pub acir_size: usize,
27+
pub brillig_size: usize,
28+
}
29+
2230
impl DebugInfo {
2331
pub fn new(locations: BTreeMap<OpcodeLocation, Vec<Location>>) -> Self {
2432
DebugInfo { locations }
@@ -42,4 +50,38 @@ impl DebugInfo {
4250
pub fn opcode_location(&self, loc: &OpcodeLocation) -> Option<Vec<Location>> {
4351
self.locations.get(loc).cloned()
4452
}
53+
54+
pub fn count_span_opcodes(&self) -> HashMap<&Location, OpCodesCount> {
55+
let mut accumulator: HashMap<&Location, Vec<&OpcodeLocation>> = HashMap::new();
56+
57+
for (opcode_location, locations) in self.locations.iter() {
58+
for location in locations.iter() {
59+
let opcodes = accumulator.entry(location).or_insert(Vec::new());
60+
opcodes.push(opcode_location);
61+
}
62+
}
63+
64+
let counted_opcodes = accumulator
65+
.iter()
66+
.map(|(location, opcodes)| {
67+
let acir_opcodes: Vec<_> = opcodes
68+
.iter()
69+
.filter(|opcode_location| matches!(opcode_location, OpcodeLocation::Acir(_)))
70+
.collect();
71+
let brillig_opcodes: Vec<_> = opcodes
72+
.iter()
73+
.filter(|opcode_location| {
74+
matches!(opcode_location, OpcodeLocation::Brillig { .. })
75+
})
76+
.collect();
77+
let opcodes_count = OpCodesCount {
78+
acir_size: acir_opcodes.len(),
79+
brillig_size: brillig_opcodes.len(),
80+
};
81+
(*location, opcodes_count)
82+
})
83+
.collect();
84+
85+
counted_opcodes
86+
}
4587
}

tooling/nargo/src/artifacts/debug.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use codespan_reporting::files::{Error, Files, SimpleFile};
2-
use noirc_driver::DebugFile;
2+
use noirc_driver::{CompiledContract, CompiledProgram, DebugFile};
33
use noirc_errors::{debug_info::DebugInfo, Location};
44
use noirc_evaluator::errors::SsaReport;
55
use serde::{Deserialize, Serialize};
@@ -96,6 +96,32 @@ impl DebugArtifact {
9696
}
9797
}
9898

99+
impl From<CompiledProgram> for DebugArtifact {
100+
fn from(compiled_program: CompiledProgram) -> Self {
101+
DebugArtifact {
102+
debug_symbols: vec![compiled_program.debug],
103+
file_map: compiled_program.file_map,
104+
warnings: compiled_program.warnings,
105+
}
106+
}
107+
}
108+
109+
impl From<&CompiledContract> for DebugArtifact {
110+
fn from(compiled_artifact: &CompiledContract) -> Self {
111+
let all_functions_debug: Vec<DebugInfo> = compiled_artifact
112+
.functions
113+
.iter()
114+
.map(|contract_function| contract_function.debug.clone())
115+
.collect();
116+
117+
DebugArtifact {
118+
debug_symbols: all_functions_debug,
119+
file_map: compiled_artifact.file_map.clone(),
120+
warnings: compiled_artifact.warnings.clone(),
121+
}
122+
}
123+
}
124+
99125
impl<'a> Files<'a> for DebugArtifact {
100126
type FileId = FileId;
101127
type Name = PathString;

tooling/nargo_cli/src/cli/info_cmd.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1+
use std::collections::HashMap;
2+
13
use acvm::Language;
24
use backend_interface::BackendError;
35
use clap::Args;
46
use iter_extended::vecmap;
5-
use nargo::package::Package;
7+
use nargo::{artifacts::debug::DebugArtifact, package::Package};
68
use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection};
79
use noirc_driver::{
810
CompileOptions, CompiledContract, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING,
911
};
12+
use noirc_errors::{debug_info::OpCodesCount, Location};
1013
use noirc_frontend::graph::CrateName;
1114
use prettytable::{row, table, Row};
1215
use rayon::prelude::*;
@@ -36,6 +39,9 @@ pub(crate) struct InfoCommand {
3639
#[clap(long, hide = true)]
3740
json: bool,
3841

42+
#[clap(long, hide = true)]
43+
profile_info: bool,
44+
3945
#[clap(flatten)]
4046
compile_options: CompileOptions,
4147
}
@@ -71,6 +77,23 @@ pub(crate) fn run(
7177
&args.compile_options,
7278
)?;
7379

80+
if args.profile_info {
81+
for compiled_program in &compiled_programs {
82+
let span_opcodes = compiled_program.debug.count_span_opcodes();
83+
let debug_artifact: DebugArtifact = compiled_program.clone().into();
84+
print_span_opcodes(&span_opcodes, &debug_artifact);
85+
}
86+
87+
for compiled_contract in &compiled_contracts {
88+
let debug_artifact: DebugArtifact = compiled_contract.clone().into();
89+
let functions = &compiled_contract.functions;
90+
for contract_function in functions {
91+
let span_opcodes = contract_function.debug.count_span_opcodes();
92+
print_span_opcodes(&span_opcodes, &debug_artifact);
93+
}
94+
}
95+
}
96+
7497
let program_info = binary_packages
7598
.into_par_iter()
7699
.zip(compiled_programs)
@@ -121,6 +144,55 @@ pub(crate) fn run(
121144
Ok(())
122145
}
123146

147+
/// Provides profiling information on
148+
///
149+
/// Number of OpCodes in relation to Noir source file
150+
/// and line number information
151+
fn print_span_opcodes(
152+
span_opcodes_map: &HashMap<&Location, OpCodesCount>,
153+
debug_artifact: &DebugArtifact,
154+
) {
155+
let mut pairs: Vec<(&&Location, &OpCodesCount)> = span_opcodes_map.iter().collect();
156+
157+
pairs.sort_by(|a, b| {
158+
a.1.acir_size.cmp(&b.1.acir_size).then_with(|| a.1.brillig_size.cmp(&b.1.brillig_size))
159+
});
160+
161+
for (location, opcodes_count) in pairs {
162+
let debug_file = debug_artifact.file_map.get(&location.file).unwrap();
163+
164+
let start_byte = byte_index(&debug_file.source, location.span.start() + 1);
165+
let end_byte = byte_index(&debug_file.source, location.span.end() + 1);
166+
let range = start_byte..end_byte;
167+
let span_content = &debug_file.source[range];
168+
let line = debug_artifact.location_line_index(**location).unwrap() + 1;
169+
println!(
170+
"Ln. {}: {} (ACIR:{}, Brillig:{} opcode|s) in file: {}",
171+
line,
172+
span_content,
173+
opcodes_count.acir_size,
174+
opcodes_count.brillig_size,
175+
debug_file.path.to_str().unwrap()
176+
);
177+
}
178+
}
179+
fn byte_index(string: &str, index: u32) -> usize {
180+
let mut byte_index = 0;
181+
let mut char_index = 0;
182+
183+
#[allow(clippy::explicit_counter_loop)]
184+
for (byte_offset, _) in string.char_indices() {
185+
if char_index == index {
186+
return byte_index;
187+
}
188+
189+
byte_index = byte_offset;
190+
char_index += 1;
191+
}
192+
193+
byte_index
194+
}
195+
124196
#[derive(Debug, Default, Serialize)]
125197
struct InfoReport {
126198
programs: Vec<ProgramInfo>,

0 commit comments

Comments
 (0)