Skip to content

Commit

Permalink
feat: Add LSP command to profile opcodes in vscode (#3496)
Browse files Browse the repository at this point in the history
  • Loading branch information
kobyhallx authored Nov 17, 2023
1 parent fac19a3 commit 6fbf77a
Show file tree
Hide file tree
Showing 16 changed files with 357 additions and 40 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

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

7 changes: 4 additions & 3 deletions compiler/noirc_errors/src/debug_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct DebugInfo {

/// Holds OpCodes Counts for Acir and Brillig Opcodes
/// To be printed with `nargo info --profile-info`
#[derive(Default, Debug, Serialize, Deserialize, Clone)]
pub struct OpCodesCount {
pub acir_size: usize,
pub brillig_size: usize,
Expand Down Expand Up @@ -51,12 +52,12 @@ impl DebugInfo {
self.locations.get(loc).cloned()
}

pub fn count_span_opcodes(&self) -> HashMap<&Location, OpCodesCount> {
let mut accumulator: HashMap<&Location, Vec<&OpcodeLocation>> = HashMap::new();
pub fn count_span_opcodes(&self) -> HashMap<Location, OpCodesCount> {
let mut accumulator: HashMap<Location, Vec<&OpcodeLocation>> = HashMap::new();

for (opcode_location, locations) in self.locations.iter() {
for location in locations.iter() {
let opcodes = accumulator.entry(location).or_insert(Vec::new());
let opcodes = accumulator.entry(*location).or_insert(Vec::new());
opcodes.push(opcode_location);
}
}
Expand Down
2 changes: 2 additions & 0 deletions tooling/lsp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ serde_json.workspace = true
tower.workspace = true
cfg-if.workspace = true
async-lsp = { workspace = true, features = ["omni-trait"] }
serde_with = "3.2.0"
fm.workspace = true

[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies]
wasm-bindgen.workspace = true
Expand Down
4 changes: 3 additions & 1 deletion tooling/lsp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ use notifications::{
on_did_open_text_document, on_did_save_text_document, on_exit, on_initialized,
};
use requests::{
on_code_lens_request, on_initialize, on_shutdown, on_test_run_request, on_tests_request,
on_code_lens_request, on_initialize, on_profile_run_request, on_shutdown, on_test_run_request,
on_tests_request,
};
use serde_json::Value as JsonValue;
use tower::Service;
Expand Down Expand Up @@ -66,6 +67,7 @@ impl NargoLspService {
.request::<request::CodeLens, _>(on_code_lens_request)
.request::<request::NargoTests, _>(on_tests_request)
.request::<request::NargoTestRun, _>(on_test_run_request)
.request::<request::NargoProfileRun, _>(on_profile_run_request)
.notification::<notification::Initialized>(on_initialized)
.notification::<notification::DidChangeConfiguration>(on_did_change_configuration)
.notification::<notification::DidOpenTextDocument>(on_did_open_text_document)
Expand Down
23 changes: 23 additions & 0 deletions tooling/lsp/src/requests/code_lens_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ const INFO_CODELENS_TITLE: &str = "Info";
const EXECUTE_COMMAND: &str = "nargo.execute";
const EXECUTE_CODELENS_TITLE: &str = "Execute";

const PROFILE_COMMAND: &str = "nargo.profile";
const PROFILE_CODELENS_TITLE: &str = "Profile";

fn with_arrow(title: &str) -> String {
format!("{ARROW} {title}")
}
Expand Down Expand Up @@ -163,6 +166,16 @@ fn on_code_lens_request_inner(
let execute_lens = CodeLens { range, command: Some(execute_command), data: None };

lenses.push(execute_lens);

let profile_command = Command {
title: PROFILE_CODELENS_TITLE.to_string(),
command: PROFILE_COMMAND.into(),
arguments: Some(package_selection_args(&workspace, package)),
};

let profile_lens = CodeLens { range, command: Some(profile_command), data: None };

lenses.push(profile_lens);
}
}

Expand Down Expand Up @@ -200,6 +213,16 @@ fn on_code_lens_request_inner(
let info_lens = CodeLens { range, command: Some(info_command), data: None };

lenses.push(info_lens);

let profile_command = Command {
title: PROFILE_CODELENS_TITLE.to_string(),
command: PROFILE_COMMAND.into(),
arguments: Some(package_selection_args(&workspace, package)),
};

let profile_lens = CodeLens { range, command: Some(profile_command), data: None };

lenses.push(profile_lens);
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion tooling/lsp/src/requests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ use crate::{
// and params passed in.

mod code_lens_request;
mod profile_run;
mod test_run;
mod tests;

pub(crate) use {
code_lens_request::on_code_lens_request, test_run::on_test_run_request, tests::on_tests_request,
code_lens_request::on_code_lens_request, profile_run::on_profile_run_request,
test_run::on_test_run_request, tests::on_tests_request,
};

pub(crate) fn on_initialize(
Expand Down
102 changes: 102 additions & 0 deletions tooling/lsp/src/requests/profile_run.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use std::{
collections::{BTreeMap, HashMap},
future::{self, Future},
};

use acvm::{acir::circuit::Opcode, Language};
use async_lsp::{ErrorCode, ResponseError};
use nargo::artifacts::debug::DebugArtifact;
use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection};
use noirc_driver::{CompileOptions, DebugFile, NOIR_ARTIFACT_VERSION_STRING};
use noirc_errors::{debug_info::OpCodesCount, Location};

use crate::{
types::{NargoProfileRunParams, NargoProfileRunResult},
LspState,
};
use fm::FileId;

pub(crate) fn on_profile_run_request(
state: &mut LspState,
params: NargoProfileRunParams,
) -> impl Future<Output = Result<NargoProfileRunResult, ResponseError>> {
future::ready(on_profile_run_request_inner(state, params))
}

fn on_profile_run_request_inner(
state: &LspState,
params: NargoProfileRunParams,
) -> Result<NargoProfileRunResult, ResponseError> {
let root_path = state.root_path.as_deref().ok_or_else(|| {
ResponseError::new(ErrorCode::REQUEST_FAILED, "Could not find project root")
})?;

let toml_path = find_package_manifest(root_path, root_path).map_err(|err| {
// If we cannot find a manifest, we can't run the test
ResponseError::new(ErrorCode::REQUEST_FAILED, err)
})?;

let crate_name = params.package;

let workspace = resolve_workspace_from_toml(
&toml_path,
PackageSelection::DefaultOrAll,
Some(NOIR_ARTIFACT_VERSION_STRING.to_string()),
)
.map_err(|err| {
// If we found a manifest, but the workspace is invalid, we raise an error about it
ResponseError::new(ErrorCode::REQUEST_FAILED, err)
})?;

// Since we filtered on crate name, this should be the only item in the iterator
match workspace.into_iter().next() {
Some(_package) => {
let (binary_packages, contract_packages): (Vec<_>, Vec<_>) = workspace
.into_iter()
.filter(|package| !package.is_library())
.cloned()
.partition(|package| package.is_binary());

// # TODO(#3504): Consider how to incorporate Backend relevant information in wider context.
let is_opcode_supported = |_opcode: &Opcode| true;
let np_language = Language::PLONKCSat { width: 3 };

let (compiled_programs, compiled_contracts) = nargo::ops::compile_workspace(
&workspace,
&binary_packages,
&contract_packages,
np_language,
is_opcode_supported,
&CompileOptions::default(),
)
.map_err(|err| ResponseError::new(ErrorCode::REQUEST_FAILED, err))?;

let mut opcodes_counts: HashMap<Location, OpCodesCount> = HashMap::new();
let mut file_map: BTreeMap<FileId, DebugFile> = BTreeMap::new();
for compiled_program in &compiled_programs {
let span_opcodes = compiled_program.debug.count_span_opcodes();
let debug_artifact: DebugArtifact = compiled_program.clone().into();
opcodes_counts.extend(span_opcodes);
file_map.extend(debug_artifact.file_map);
}

for compiled_contract in &compiled_contracts {
let functions = &compiled_contract.functions;
let debug_artifact: DebugArtifact = compiled_contract.clone().into();
file_map.extend(debug_artifact.file_map);
for contract_function in functions {
let span_opcodes = contract_function.debug.count_span_opcodes();
opcodes_counts.extend(span_opcodes);
}
}

let result = NargoProfileRunResult { file_map, opcodes_counts };

Ok(result)
}
None => Err(ResponseError::new(
ErrorCode::REQUEST_FAILED,
format!("Could not locate package named: {crate_name}"),
)),
}
}
28 changes: 26 additions & 2 deletions tooling/lsp/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
use fm::FileId;
use noirc_driver::DebugFile;
use noirc_errors::{debug_info::OpCodesCount, Location};
use noirc_frontend::graph::CrateName;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use std::collections::{BTreeMap, HashMap};

// Re-providing lsp_types that we don't need to override
pub(crate) use lsp_types::{
Expand All @@ -14,8 +19,8 @@ pub(crate) mod request {
use lsp_types::{request::Request, InitializeParams};

use super::{
InitializeResult, NargoTestRunParams, NargoTestRunResult, NargoTestsParams,
NargoTestsResult,
InitializeResult, NargoProfileRunParams, NargoProfileRunResult, NargoTestRunParams,
NargoTestRunResult, NargoTestsParams, NargoTestsResult,
};

// Re-providing lsp_types that we don't need to override
Expand Down Expand Up @@ -44,6 +49,14 @@ pub(crate) mod request {
type Result = NargoTestsResult;
const METHOD: &'static str = "nargo/tests";
}

#[derive(Debug)]
pub(crate) struct NargoProfileRun;
impl Request for NargoProfileRun {
type Params = NargoProfileRunParams;
type Result = NargoProfileRunResult;
const METHOD: &'static str = "nargo/profile/run";
}
}

pub(crate) mod notification {
Expand Down Expand Up @@ -186,5 +199,16 @@ pub(crate) struct NargoTestRunResult {
pub(crate) result: String,
pub(crate) message: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct NargoProfileRunParams {
pub(crate) package: CrateName,
}
#[serde_as]
#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct NargoProfileRunResult {
pub(crate) file_map: BTreeMap<FileId, DebugFile>,
#[serde_as(as = "Vec<(_, _)>")]
pub(crate) opcodes_counts: HashMap<Location, OpCodesCount>,
}

pub(crate) type CodeLensResult = Option<Vec<CodeLens>>;
1 change: 1 addition & 0 deletions tooling/nargo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ iter-extended.workspace = true
serde.workspace = true
thiserror.workspace = true
codespan-reporting.workspace = true
rayon = "1.8.0"
25 changes: 24 additions & 1 deletion tooling/nargo/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,36 @@ use acvm::{
acir::circuit::OpcodeLocation,
pwg::{ErrorLocation, OpcodeResolutionError},
};
use noirc_errors::{debug_info::DebugInfo, CustomDiagnostic, FileDiagnostic};
use noirc_errors::{
debug_info::DebugInfo, reporter::ReportedErrors, CustomDiagnostic, FileDiagnostic,
};

pub use noirc_errors::Location;

use noirc_frontend::graph::CrateName;
use noirc_printable_type::ForeignCallError;
use thiserror::Error;

/// Errors covering situations where a package cannot be compiled.
#[derive(Debug, Error)]
pub enum CompileError {
#[error("Package `{0}` has type `lib` but only `bin` types can be compiled")]
LibraryCrate(CrateName),

#[error("Package `{0}` is expected to have a `main` function but it does not")]
MissingMainFunction(CrateName),

/// Errors encountered while compiling the Noir program.
/// These errors are already written to stderr.
#[error("Aborting due to {} previous error{}", .0.error_count, if .0.error_count == 1 { "" } else { "s" })]
ReportedErrors(ReportedErrors),
}
impl From<ReportedErrors> for CompileError {
fn from(errors: ReportedErrors) -> Self {
Self::ReportedErrors(errors)
}
}

#[derive(Debug, Error)]
pub enum NargoError {
/// Error while compiling Noir into ACIR.
Expand Down
Loading

0 comments on commit 6fbf77a

Please sign in to comment.