diff options
author | Fabio Berger <me@fabioberger.com> | 2018-06-12 06:23:48 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-06-12 06:23:48 +0800 |
commit | 7e78f5941ad9cb2fd5225899f79823c7376894c0 (patch) | |
tree | 13fadf90f821e9fead1373a2e567e6d86fe27e0f /packages/sol-cov/src/profiler_manager.ts | |
parent | 20f93185975d16c77492f05eb86dd89695539cd9 (diff) | |
parent | bc0ae6be318a15bf8670a6da9a59d9bdb12cadae (diff) | |
download | dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.tar dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.tar.gz dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.tar.bz2 dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.tar.lz dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.tar.xz dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.tar.zst dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.zip |
Merge branch 'v2-prototype' into feature/combinatorial-testing
* v2-prototype: (68 commits)
Stop exporting ArtifactWriter
Fix no-unused-variable tslint rule to include parameters and fix issues
Fix linter exclude rule
Validate all signature types rather then only ECSignatures
Store the instantiated OrderValidationUtils
Remove global hooks from tests and deploy contracts from within the specific tests
Add EmitStatement to ASTVisitor
Fix tslint issues
Add back artifacts file
Fix a bug in SolCompilerArtifacts adapter config overriding
Move OrderValidationUtils (+ tests) and ExchangeTransferSimulator to order-utils
export parseECSignature method
Export ArtifactWriter from migrations package
Remove unused artifact file
Pass in generated contract wrapper to orderValidationUtils at instantiation
Refactor orderValidationUtils to use the generated contract wrapper instead of the higher-level one
Refactor ExchangeTransferSimulator public interface to accet an AbstractBalanceAndProxyAllowanceLazyStore so that this module could be re-used in different contexts.
Increase timeout for contract migrations
Remove some copy-paste code
Await transactions in migrations
...
Diffstat (limited to 'packages/sol-cov/src/profiler_manager.ts')
-rw-r--r-- | packages/sol-cov/src/profiler_manager.ts | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/packages/sol-cov/src/profiler_manager.ts b/packages/sol-cov/src/profiler_manager.ts new file mode 100644 index 000000000..0ab0ea544 --- /dev/null +++ b/packages/sol-cov/src/profiler_manager.ts @@ -0,0 +1,134 @@ +import { promisify } from '@0xproject/utils'; +import { stripHexPrefix } from 'ethereumjs-util'; +import * as fs from 'fs'; +import { Collector } from 'istanbul'; +import * as _ from 'lodash'; +import { getLogger, levels, Logger } from 'loglevel'; +import * as mkdirp from 'mkdirp'; + +import { AbstractArtifactAdapter } from './artifact_adapters/abstract_artifact_adapter'; +import { collectCoverageEntries } from './collect_coverage_entries'; +import { constants } from './constants'; +import { parseSourceMap } from './source_maps'; +import { + ContractData, + Coverage, + SingleFileSourceRange, + SourceRange, + Subtrace, + TraceInfo, + TraceInfoExistingContract, + TraceInfoNewContract, +} from './types'; +import { utils } from './utils'; + +const mkdirpAsync = promisify<undefined>(mkdirp); + +/** + * ProfilerManager is used by ProfilerSubprovider to profile code while running Solidity tests based on collected trace data. + * HACK: It's almost the exact copy of CoverageManager but instead of reporting how much times was each statement executed - it reports - how expensive it was gaswise. + */ +export class ProfilerManager { + private _artifactAdapter: AbstractArtifactAdapter; + private _logger: Logger; + private _traceInfos: TraceInfo[] = []; + /** + * Computed partial coverage for a single file & subtrace + * @param contractData Contract metadata (source, srcMap, bytecode) + * @param subtrace A subset of a transcation/call trace that was executed within that contract + * @param pcToSourceRange A mapping from program counters to source ranges + * @param fileIndex Index of a file to compute coverage for + * @return Partial istanbul coverage for that file & subtrace + */ + private static _getSingleFileCoverageForSubtrace( + contractData: ContractData, + subtrace: Subtrace, + pcToSourceRange: { [programCounter: number]: SourceRange }, + fileIndex: number, + ): Coverage { + const absoluteFileName = contractData.sources[fileIndex]; + const profilerEntriesDescription = collectCoverageEntries(contractData.sourceCodes[fileIndex]); + const gasConsumedByStatement: { [statementId: string]: number } = {}; + const statementIds = _.keys(profilerEntriesDescription.statementMap); + for (const statementId of statementIds) { + const statementDescription = profilerEntriesDescription.statementMap[statementId]; + const totalGasCost = _.sum( + _.map(subtrace, structLog => { + const sourceRange = pcToSourceRange[structLog.pc]; + if (_.isUndefined(sourceRange)) { + return 0; + } + if (sourceRange.fileName !== absoluteFileName) { + return 0; + } + if (utils.isRangeInside(sourceRange.location, statementDescription)) { + return structLog.gasCost; + } else { + return 0; + } + }), + ); + gasConsumedByStatement[statementId] = totalGasCost; + } + const partialProfilerOutput = { + [absoluteFileName]: { + ...profilerEntriesDescription, + path: absoluteFileName, + f: {}, // I's meaningless in profiling context + s: gasConsumedByStatement, + b: {}, // I's meaningless in profiling context + }, + }; + return partialProfilerOutput; + } + constructor(artifactAdapter: AbstractArtifactAdapter, isVerbose: boolean) { + this._artifactAdapter = artifactAdapter; + this._logger = getLogger('sol-cov'); + this._logger.setLevel(isVerbose ? levels.TRACE : levels.ERROR); + } + public appendTraceInfo(traceInfo: TraceInfo): void { + this._traceInfos.push(traceInfo); + } + public async writeProfilerOutputAsync(): Promise<void> { + const finalCoverage = await this._computeCoverageAsync(); + const stringifiedCoverage = JSON.stringify(finalCoverage, null, '\t'); + await mkdirpAsync('coverage'); + fs.writeFileSync('coverage/coverage.json', stringifiedCoverage); + } + private async _computeCoverageAsync(): Promise<Coverage> { + const contractsData = await this._artifactAdapter.collectContractsDataAsync(); + const collector = new Collector(); + for (const traceInfo of this._traceInfos) { + const isContractCreation = traceInfo.address === constants.NEW_CONTRACT; + const bytecode = isContractCreation + ? (traceInfo as TraceInfoNewContract).bytecode + : (traceInfo as TraceInfoExistingContract).runtimeBytecode; + const contractData = utils.getContractDataIfExists(contractsData, bytecode); + if (_.isUndefined(contractData)) { + const errMsg = isContractCreation + ? `Unknown contract creation transaction` + : `Transaction to an unknown address: ${traceInfo.address}`; + this._logger.warn(errMsg); + continue; + } + const bytecodeHex = stripHexPrefix(bytecode); + const sourceMap = isContractCreation ? contractData.sourceMap : contractData.sourceMapRuntime; + const pcToSourceRange = parseSourceMap( + contractData.sourceCodes, + sourceMap, + bytecodeHex, + contractData.sources, + ); + for (let fileIndex = 0; fileIndex < contractData.sources.length; fileIndex++) { + const singleFileCoverageForTrace = ProfilerManager._getSingleFileCoverageForSubtrace( + contractData, + traceInfo.subtrace, + pcToSourceRange, + fileIndex, + ); + collector.add(singleFileCoverageForTrace); + } + } + return collector.getFinalCoverage(); + } +} |