solc: add tracing (#628)

* feat(solc): add tracing

* chore: apply suggestions from code review

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>

* chore: fix compilation error

* chore: adjust log

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Georgios Konstantopoulos 2021-11-28 19:14:34 +02:00 committed by GitHub
parent 41f8e295a0
commit d06bfdc15c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 55 additions and 6 deletions

View File

@ -43,15 +43,25 @@ impl SolFilesCache {
} }
/// Reads the cache json file from the given path /// Reads the cache json file from the given path
#[tracing::instrument(skip_all, name = "sol-files-cache::read")]
pub fn read(path: impl AsRef<Path>) -> Result<Self> { pub fn read(path: impl AsRef<Path>) -> Result<Self> {
let file = fs::File::open(path.as_ref())?; let path = path.as_ref();
Ok(serde_json::from_reader(file)?) tracing::trace!("reading solfiles cache at {}", path.display());
let file = fs::File::open(path)?;
let file = std::io::BufReader::new(file);
let cache = serde_json::from_reader(file)?;
tracing::trace!("done");
Ok(cache)
} }
/// Write the cache to json file /// Write the cache to json file
pub fn write(&self, path: impl AsRef<Path>) -> Result<()> { pub fn write(&self, path: impl AsRef<Path>) -> Result<()> {
let file = fs::File::create(path.as_ref())?; let path = path.as_ref();
Ok(serde_json::to_writer_pretty(file, self)?) let file = fs::File::create(path)?;
tracing::trace!("writing cache to json file");
serde_json::to_writer_pretty(file, self)?;
tracing::trace!("cache file located: {}", path.display());
Ok(())
} }
pub fn remove_missing_files(&mut self) { pub fn remove_missing_files(&mut self) {

View File

@ -90,15 +90,18 @@ impl Project {
} }
impl<Artifacts: ArtifactOutput> Project<Artifacts> { impl<Artifacts: ArtifactOutput> Project<Artifacts> {
#[tracing::instrument(skip_all, name = "Project::write_cache_file")]
fn write_cache_file( fn write_cache_file(
&self, &self,
sources: Sources, sources: Sources,
artifacts: Vec<(PathBuf, Vec<String>)>, artifacts: Vec<(PathBuf, Vec<String>)>,
) -> Result<()> { ) -> Result<()> {
tracing::trace!("inserting files to cache");
let mut cache = SolFilesCache::builder() let mut cache = SolFilesCache::builder()
.root(&self.paths.root) .root(&self.paths.root)
.solc_config(self.solc_config.clone()) .solc_config(self.solc_config.clone())
.insert_files(sources, Some(self.paths.cache.clone()))?; .insert_files(sources, Some(self.paths.cache.clone()))?;
tracing::trace!("files inserted");
// add the artifacts for each file to the cache entry // add the artifacts for each file to the cache entry
for (file, artifacts) in artifacts { for (file, artifacts) in artifacts {
@ -110,10 +113,13 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
if let Some(cache_dir) = self.paths.cache.parent() { if let Some(cache_dir) = self.paths.cache.parent() {
fs::create_dir_all(cache_dir)? fs::create_dir_all(cache_dir)?
} }
cache.write(&self.paths.cache) cache.write(&self.paths.cache)?;
Ok(())
} }
/// Returns all sources found under the project's sources path /// Returns all sources found under the project's sources path
#[tracing::instrument(skip_all, fields(name = "sources"))]
pub fn sources(&self) -> io::Result<Sources> { pub fn sources(&self) -> io::Result<Sources> {
Source::read_all_from(self.paths.sources.as_path()) Source::read_all_from(self.paths.sources.as_path())
} }
@ -167,11 +173,15 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
/// NB: If the `svm` feature is enabled, this function will automatically detect /// NB: If the `svm` feature is enabled, this function will automatically detect
/// solc versions across files. /// solc versions across files.
#[tracing::instrument(skip_all, name = "compile")]
pub fn compile(&self) -> Result<ProjectCompileOutput<Artifacts>> { pub fn compile(&self) -> Result<ProjectCompileOutput<Artifacts>> {
tracing::trace!("sources");
let sources = self.sources()?; let sources = self.sources()?;
tracing::trace!("done");
#[cfg(all(feature = "svm", feature = "async"))] #[cfg(all(feature = "svm", feature = "async"))]
if self.auto_detect { if self.auto_detect {
tracing::trace!("auto-compile");
return self.svm_compile(sources) return self.svm_compile(sources)
} }
@ -183,15 +193,20 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
} }
#[cfg(all(feature = "svm", feature = "async"))] #[cfg(all(feature = "svm", feature = "async"))]
#[tracing::instrument(skip(self, sources))]
fn svm_compile(&self, sources: Sources) -> Result<ProjectCompileOutput<Artifacts>> { fn svm_compile(&self, sources: Sources) -> Result<ProjectCompileOutput<Artifacts>> {
// split them by version // split them by version
let mut sources_by_version = BTreeMap::new(); let mut sources_by_version = BTreeMap::new();
// we store the solc versions by path, in case there exists a corrupt solc binary // we store the solc versions by path, in case there exists a corrupt solc binary
let mut solc_versions = HashMap::new(); let mut solc_versions = HashMap::new();
// TODO: Rayon
// tracing::trace!("parsing sources");
for (path, source) in sources.into_iter() { for (path, source) in sources.into_iter() {
// will detect and install the solc version // will detect and install the solc version
// tracing::trace!("finding version {}", path.display());
let version = Solc::detect_version(&source)?; let version = Solc::detect_version(&source)?;
// tracing::trace!("found {}", version);
// gets the solc binary for that version, it is expected tha this will succeed // gets the solc binary for that version, it is expected tha this will succeed
// AND find the solc since it was installed right above // AND find the solc since it was installed right above
let mut solc = Solc::find_svm_installed_version(version.to_string())? let mut solc = Solc::find_svm_installed_version(version.to_string())?
@ -204,21 +219,31 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
let entry = sources_by_version.entry(solc).or_insert_with(BTreeMap::new); let entry = sources_by_version.entry(solc).or_insert_with(BTreeMap::new);
entry.insert(path.clone(), source); entry.insert(path.clone(), source);
} }
// tracing::trace!("done");
let mut compiled = let mut compiled =
ProjectCompileOutput::with_ignored_errors(self.ignored_error_codes.clone()); ProjectCompileOutput::with_ignored_errors(self.ignored_error_codes.clone());
// run the compilation step for each version // run the compilation step for each version
tracing::trace!("compiling sources with viable solc versions");
for (solc, sources) in sources_by_version { for (solc, sources) in sources_by_version {
let span = tracing::trace_span!("solc", "{}", solc.version_short()?);
let _enter = span.enter();
// verify that this solc version's checksum matches the checksum found remotely. If // verify that this solc version's checksum matches the checksum found remotely. If
// not, re-install the same version. // not, re-install the same version.
let version = solc_versions.get(&solc.solc).unwrap(); let version = solc_versions.get(&solc.solc).unwrap();
if let Err(_e) = solc.verify_checksum() { if let Err(_e) = solc.verify_checksum() {
tracing::trace!("corrupted solc version, redownloading...");
Solc::blocking_install(version)?; Solc::blocking_install(version)?;
tracing::trace!("done.");
} }
// once matched, proceed to compile with it // once matched, proceed to compile with it
tracing::trace!("compiling_with_version");
compiled.extend(self.compile_with_version(&solc, sources)?); compiled.extend(self.compile_with_version(&solc, sources)?);
tracing::trace!("done compiling_with_version");
} }
tracing::trace!("compiled sources with viable solc versions");
Ok(compiled) Ok(compiled)
} }
@ -228,6 +253,8 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
solc: &Solc, solc: &Solc,
mut sources: Sources, mut sources: Sources,
) -> Result<ProjectCompileOutput<Artifacts>> { ) -> Result<ProjectCompileOutput<Artifacts>> {
let span = tracing::trace_span!("compiling");
let _enter = span.enter();
// add all libraries to the source set while keeping track of their actual disk path // add all libraries to the source set while keeping track of their actual disk path
// (`contracts/contract.sol` -> `/Users/.../contracts.sol`) // (`contracts/contract.sol` -> `/Users/.../contracts.sol`)
let mut source_name_to_path = HashMap::new(); let mut source_name_to_path = HashMap::new();
@ -235,17 +262,21 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
// `contracts/contract.sol`) // `contracts/contract.sol`)
let mut path_to_source_name = HashMap::new(); let mut path_to_source_name = HashMap::new();
tracing::trace!("resolving libraries");
for (import, (source, path)) in self.resolved_libraries(&sources)? { for (import, (source, path)) in self.resolved_libraries(&sources)? {
// inserting with absolute path here and keep track of the source name <-> path mappings // inserting with absolute path here and keep track of the source name <-> path mappings
sources.insert(path.clone(), source); sources.insert(path.clone(), source);
path_to_source_name.insert(path.clone(), import.clone()); path_to_source_name.insert(path.clone(), import.clone());
source_name_to_path.insert(import, path); source_name_to_path.insert(import, path);
} }
tracing::trace!("resolved libraries");
// If there's a cache set, filter to only re-compile the files which were changed // If there's a cache set, filter to only re-compile the files which were changed
let (sources, cached_artifacts) = if self.cached && self.paths.cache.exists() { let (sources, cached_artifacts) = if self.cached && self.paths.cache.exists() {
tracing::trace!("reading solfiles cache for incremental compilation");
let mut cache = SolFilesCache::read(&self.paths.cache)?; let mut cache = SolFilesCache::read(&self.paths.cache)?;
cache.remove_missing_files(); cache.remove_missing_files();
tracing::trace!("done reading solfiles cache for incremental compilation");
let changed_files = cache.get_changed_or_missing_artifacts_files::<Artifacts>( let changed_files = cache.get_changed_or_missing_artifacts_files::<Artifacts>(
sources, sources,
Some(&self.solc_config), Some(&self.solc_config),
@ -254,12 +285,17 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
cache.remove_changed_files(&changed_files); cache.remove_changed_files(&changed_files);
let cached_artifacts = if self.paths.artifacts.exists() { let cached_artifacts = if self.paths.artifacts.exists() {
cache.read_artifacts::<Artifacts>(&self.paths.artifacts)? tracing::trace!("reading artifacts from cache..");
let artifacts = cache.read_artifacts::<Artifacts>(&self.paths.artifacts)?;
tracing::trace!("done reading artifacts from cache");
artifacts
} else { } else {
BTreeMap::default() BTreeMap::default()
}; };
// if nothing changed and all artifacts still exist // if nothing changed and all artifacts still exist
if changed_files.is_empty() { if changed_files.is_empty() {
tracing::trace!("no change");
return Ok(ProjectCompileOutput::from_unchanged(cached_artifacts)) return Ok(ProjectCompileOutput::from_unchanged(cached_artifacts))
} }
// There are changed files and maybe some cached files // There are changed files and maybe some cached files
@ -274,7 +310,9 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
let input = CompilerInput::with_sources(sources) let input = CompilerInput::with_sources(sources)
.normalize_evm_version(&solc.version()?) .normalize_evm_version(&solc.version()?)
.with_remappings(self.paths.remappings.clone()); .with_remappings(self.paths.remappings.clone());
tracing::trace!("calling solc");
let output = solc.compile(&input)?; let output = solc.compile(&input)?;
tracing::trace!("compiled input, output has error: {}", output.has_error());
if output.has_error() { if output.has_error() {
return Ok(ProjectCompileOutput::from_compiler_output( return Ok(ProjectCompileOutput::from_compiler_output(
output, output,
@ -305,6 +343,7 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
if !self.no_artifacts { if !self.no_artifacts {
Artifacts::on_output(&output, &self.paths)?; Artifacts::on_output(&output, &self.paths)?;
} }
Ok(ProjectCompileOutput::from_compiler_output_and_cache( Ok(ProjectCompileOutput::from_compiler_output_and_cache(
output, output,
cached_artifacts, cached_artifacts,