From 33f066910021bc4969f4d564fc1648f6769ee3ec Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 12 Jun 2018 12:42:14 -0700 Subject: Refactor sol-cov to avoid keeping traceInfo in memory --- packages/sol-cov/src/coverage_manager.ts | 67 ++++++++++------------ packages/sol-cov/src/coverage_subprovider.ts | 8 ++- packages/sol-cov/src/profiler_manager.ts | 67 ++++++++++------------ packages/sol-cov/src/profiler_subprovider.ts | 8 ++- .../sol-cov/src/trace_collection_subprovider.ts | 18 +++--- 5 files changed, 76 insertions(+), 92 deletions(-) (limited to 'packages/sol-cov/src') diff --git a/packages/sol-cov/src/coverage_manager.ts b/packages/sol-cov/src/coverage_manager.ts index 3ab363b52..a55967243 100644 --- a/packages/sol-cov/src/coverage_manager.ts +++ b/packages/sol-cov/src/coverage_manager.ts @@ -36,7 +36,8 @@ const mkdirpAsync = promisify(mkdirp); export class CoverageManager { private _artifactAdapter: AbstractArtifactAdapter; private _logger: Logger; - private _traceInfos: TraceInfo[] = []; + private _contractsData!: ContractData[]; + private _collector = new Collector(); /** * Computed partial coverage for a single file & subtrace * @param contractData Contract metadata (source, srcMap, bytecode) @@ -130,49 +131,39 @@ export class CoverageManager { this._logger = getLogger('sol-cov'); this._logger.setLevel(isVerbose ? levels.TRACE : levels.ERROR); } - public appendTraceInfo(traceInfo: TraceInfo): void { - this._traceInfos.push(traceInfo); - } public async writeCoverageAsync(): Promise { - const finalCoverage = await this._computeCoverageAsync(); + const finalCoverage = this._collector.getFinalCoverage(); const stringifiedCoverage = JSON.stringify(finalCoverage, null, '\t'); await mkdirpAsync('coverage'); fs.writeFileSync('coverage/coverage.json', stringifiedCoverage); } - private async _computeCoverageAsync(): Promise { - 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, + public async computeCoverageAsync(traceInfo: TraceInfo): Promise { + if (_.isUndefined(this._contractsData)) { + this._contractsData = await this._artifactAdapter.collectContractsDataAsync(); + } + const isContractCreation = traceInfo.address === constants.NEW_CONTRACT; + const bytecode = isContractCreation + ? (traceInfo as TraceInfoNewContract).bytecode + : (traceInfo as TraceInfoExistingContract).runtimeBytecode; + const contractData = utils.getContractDataIfExists(this._contractsData, bytecode); + if (_.isUndefined(contractData)) { + const errMsg = isContractCreation + ? `Unknown contract creation transaction` + : `Transaction to an unknown address: ${traceInfo.address}`; + this._logger.warn(errMsg); + return; + } + 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 = CoverageManager._getSingleFileCoverageForSubtrace( + contractData, + traceInfo.subtrace, + pcToSourceRange, + fileIndex, ); - for (let fileIndex = 0; fileIndex < contractData.sources.length; fileIndex++) { - const singleFileCoverageForTrace = CoverageManager._getSingleFileCoverageForSubtrace( - contractData, - traceInfo.subtrace, - pcToSourceRange, - fileIndex, - ); - collector.add(singleFileCoverageForTrace); - } + this._collector.add(singleFileCoverageForTrace); } - return collector.getFinalCoverage(); } } diff --git a/packages/sol-cov/src/coverage_subprovider.ts b/packages/sol-cov/src/coverage_subprovider.ts index 174b7c6ac..1dcbfcbfe 100644 --- a/packages/sol-cov/src/coverage_subprovider.ts +++ b/packages/sol-cov/src/coverage_subprovider.ts @@ -3,6 +3,7 @@ import * as _ from 'lodash'; import { AbstractArtifactAdapter } from './artifact_adapters/abstract_artifact_adapter'; import { CoverageManager } from './coverage_manager'; import { TraceCollectionSubprovider } from './trace_collection_subprovider'; +import { TraceInfo } from './types'; /** * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface. @@ -25,12 +26,13 @@ export class CoverageSubprovider extends TraceCollectionSubprovider { super(defaultFromAddress, traceCollectionSubproviderConfig); this._coverageManager = new CoverageManager(artifactAdapter, isVerbose); } + public async handleTraceInfoAsync(traceInfo: TraceInfo): Promise { + return this._coverageManager.computeCoverageAsync(traceInfo); + } /** * Write the test coverage results to a file in Istanbul format. */ public async writeCoverageAsync(): Promise { - const traceInfos = this.getCollectedTraceInfos(); - _.forEach(traceInfos, traceInfo => this._coverageManager.appendTraceInfo(traceInfo)); - await this._coverageManager.writeCoverageAsync(); + return this._coverageManager.writeCoverageAsync(); } } diff --git a/packages/sol-cov/src/profiler_manager.ts b/packages/sol-cov/src/profiler_manager.ts index 0ab0ea544..bec92f424 100644 --- a/packages/sol-cov/src/profiler_manager.ts +++ b/packages/sol-cov/src/profiler_manager.ts @@ -31,7 +31,8 @@ const mkdirpAsync = promisify(mkdirp); export class ProfilerManager { private _artifactAdapter: AbstractArtifactAdapter; private _logger: Logger; - private _traceInfos: TraceInfo[] = []; + private _contractsData!: ContractData[]; + private _collector = new Collector(); /** * Computed partial coverage for a single file & subtrace * @param contractData Contract metadata (source, srcMap, bytecode) @@ -86,49 +87,39 @@ export class ProfilerManager { 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 { - const finalCoverage = await this._computeCoverageAsync(); + const finalCoverage = this._collector.getFinalCoverage(); const stringifiedCoverage = JSON.stringify(finalCoverage, null, '\t'); await mkdirpAsync('coverage'); fs.writeFileSync('coverage/coverage.json', stringifiedCoverage); } - private async _computeCoverageAsync(): Promise { - 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, + public async computeCoverageAsync(traceInfo: TraceInfo): Promise { + if (_.isUndefined(this._contractsData)) { + this._contractsData = await this._artifactAdapter.collectContractsDataAsync(); + } + const isContractCreation = traceInfo.address === constants.NEW_CONTRACT; + const bytecode = isContractCreation + ? (traceInfo as TraceInfoNewContract).bytecode + : (traceInfo as TraceInfoExistingContract).runtimeBytecode; + const contractData = utils.getContractDataIfExists(this._contractsData, bytecode); + if (_.isUndefined(contractData)) { + const errMsg = isContractCreation + ? `Unknown contract creation transaction` + : `Transaction to an unknown address: ${traceInfo.address}`; + this._logger.warn(errMsg); + return; + } + 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, ); - for (let fileIndex = 0; fileIndex < contractData.sources.length; fileIndex++) { - const singleFileCoverageForTrace = ProfilerManager._getSingleFileCoverageForSubtrace( - contractData, - traceInfo.subtrace, - pcToSourceRange, - fileIndex, - ); - collector.add(singleFileCoverageForTrace); - } + this._collector.add(singleFileCoverageForTrace); } - return collector.getFinalCoverage(); } } diff --git a/packages/sol-cov/src/profiler_subprovider.ts b/packages/sol-cov/src/profiler_subprovider.ts index ac878c070..c66cd1dec 100644 --- a/packages/sol-cov/src/profiler_subprovider.ts +++ b/packages/sol-cov/src/profiler_subprovider.ts @@ -3,6 +3,7 @@ import * as _ from 'lodash'; import { AbstractArtifactAdapter } from './artifact_adapters/abstract_artifact_adapter'; import { ProfilerManager } from './profiler_manager'; import { TraceCollectionSubprovider } from './trace_collection_subprovider'; +import { TraceInfo } from './types'; /** * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface. @@ -25,12 +26,13 @@ export class ProfilerSubprovider extends TraceCollectionSubprovider { super(defaultFromAddress, traceCollectionSubproviderConfig); this._profilerManager = new ProfilerManager(artifactAdapter, isVerbose); } + public async handleTraceInfoAsync(traceInfo: TraceInfo): Promise { + return this._profilerManager.computeCoverageAsync(traceInfo); + } /** * Write the test profiler results to a file in Istanbul format. */ public async writeProfilerOutputAsync(): Promise { - const traceInfos = this.getCollectedTraceInfos(); - _.forEach(traceInfos, traceInfo => this._profilerManager.appendTraceInfo(traceInfo)); - await this._profilerManager.writeProfilerOutputAsync(); + return this._profilerManager.writeProfilerOutputAsync(); } } diff --git a/packages/sol-cov/src/trace_collection_subprovider.ts b/packages/sol-cov/src/trace_collection_subprovider.ts index fe73546e8..5e90e3907 100644 --- a/packages/sol-cov/src/trace_collection_subprovider.ts +++ b/packages/sol-cov/src/trace_collection_subprovider.ts @@ -29,12 +29,11 @@ export interface TraceCollectionSubproviderConfig { * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface. * It collects traces of all transactions that were sent and all calls that were executed through JSON RPC. */ -export class TraceCollectionSubprovider extends Subprovider { +export abstract class TraceCollectionSubprovider extends Subprovider { // Lock is used to not accept normal transactions while doing call/snapshot magic because they'll be reverted later otherwise private _lock = new Lock(); private _defaultFromAddress: string; private _web3Wrapper!: Web3Wrapper; - private _traceInfos: TraceInfo[] = []; private _isEnabled = true; private _config: TraceCollectionSubproviderConfig; /** @@ -46,12 +45,6 @@ export class TraceCollectionSubprovider extends Subprovider { this._defaultFromAddress = defaultFromAddress; this._config = config; } - /** - * Returns all trace infos collected by the subprovider so far - */ - public getCollectedTraceInfos(): TraceInfo[] { - return this._traceInfos; - } /** * Starts trace collection */ @@ -64,6 +57,11 @@ export class TraceCollectionSubprovider extends Subprovider { public stop(): void { this._isEnabled = false; } + /** + * Called for each subtrace. + * @param traceInfo Trace info for this subtrace. + */ + public abstract handleTraceInfoAsync(traceInfo: TraceInfo): Promise; /** * This method conforms to the web3-provider-engine interface. * It is called internally by the ProviderEngine when it is this subproviders @@ -192,7 +190,7 @@ export class TraceCollectionSubprovider extends Subprovider { runtimeBytecode, }; } - this._traceInfos.push(traceInfo); + this.handleTraceInfoAsync(traceInfo); } } else { for (const subcallAddress of subcallAddresses) { @@ -204,7 +202,7 @@ export class TraceCollectionSubprovider extends Subprovider { address: subcallAddress, runtimeBytecode, }; - this._traceInfos.push(traceInfo); + this.handleTraceInfoAsync(traceInfo); } } } -- cgit v1.2.3 From bcc76b37641a5f4c05fbe84059ed1f2a842bade8 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 12 Jun 2018 14:09:42 -0700 Subject: Fix linter errors --- packages/sol-cov/src/coverage_subprovider.ts | 6 ++---- packages/sol-cov/src/profiler_subprovider.ts | 6 ++---- packages/sol-cov/src/trace_collection_subprovider.ts | 4 ++-- 3 files changed, 6 insertions(+), 10 deletions(-) (limited to 'packages/sol-cov/src') diff --git a/packages/sol-cov/src/coverage_subprovider.ts b/packages/sol-cov/src/coverage_subprovider.ts index 1dcbfcbfe..6fb709e86 100644 --- a/packages/sol-cov/src/coverage_subprovider.ts +++ b/packages/sol-cov/src/coverage_subprovider.ts @@ -1,5 +1,3 @@ -import * as _ from 'lodash'; - import { AbstractArtifactAdapter } from './artifact_adapters/abstract_artifact_adapter'; import { CoverageManager } from './coverage_manager'; import { TraceCollectionSubprovider } from './trace_collection_subprovider'; @@ -27,12 +25,12 @@ export class CoverageSubprovider extends TraceCollectionSubprovider { this._coverageManager = new CoverageManager(artifactAdapter, isVerbose); } public async handleTraceInfoAsync(traceInfo: TraceInfo): Promise { - return this._coverageManager.computeCoverageAsync(traceInfo); + await this._coverageManager.computeCoverageAsync(traceInfo); } /** * Write the test coverage results to a file in Istanbul format. */ public async writeCoverageAsync(): Promise { - return this._coverageManager.writeCoverageAsync(); + await this._coverageManager.writeCoverageAsync(); } } diff --git a/packages/sol-cov/src/profiler_subprovider.ts b/packages/sol-cov/src/profiler_subprovider.ts index c66cd1dec..34c125b86 100644 --- a/packages/sol-cov/src/profiler_subprovider.ts +++ b/packages/sol-cov/src/profiler_subprovider.ts @@ -1,5 +1,3 @@ -import * as _ from 'lodash'; - import { AbstractArtifactAdapter } from './artifact_adapters/abstract_artifact_adapter'; import { ProfilerManager } from './profiler_manager'; import { TraceCollectionSubprovider } from './trace_collection_subprovider'; @@ -27,12 +25,12 @@ export class ProfilerSubprovider extends TraceCollectionSubprovider { this._profilerManager = new ProfilerManager(artifactAdapter, isVerbose); } public async handleTraceInfoAsync(traceInfo: TraceInfo): Promise { - return this._profilerManager.computeCoverageAsync(traceInfo); + await this._profilerManager.computeCoverageAsync(traceInfo); } /** * Write the test profiler results to a file in Istanbul format. */ public async writeProfilerOutputAsync(): Promise { - return this._profilerManager.writeProfilerOutputAsync(); + await this._profilerManager.writeProfilerOutputAsync(); } } diff --git a/packages/sol-cov/src/trace_collection_subprovider.ts b/packages/sol-cov/src/trace_collection_subprovider.ts index 5e90e3907..742735935 100644 --- a/packages/sol-cov/src/trace_collection_subprovider.ts +++ b/packages/sol-cov/src/trace_collection_subprovider.ts @@ -190,7 +190,7 @@ export abstract class TraceCollectionSubprovider extends Subprovider { runtimeBytecode, }; } - this.handleTraceInfoAsync(traceInfo); + await this.handleTraceInfoAsync(traceInfo); } } else { for (const subcallAddress of subcallAddresses) { @@ -202,7 +202,7 @@ export abstract class TraceCollectionSubprovider extends Subprovider { address: subcallAddress, runtimeBytecode, }; - this.handleTraceInfoAsync(traceInfo); + await this.handleTraceInfoAsync(traceInfo); } } } -- cgit v1.2.3 From 627ea6c860e97a6e04877f10f9f842c5d5a907d6 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 12 Jun 2018 14:11:11 -0700 Subject: Rename computeCoverageAsync -> computeSingleTraceCoverageAsync --- packages/sol-cov/src/coverage_manager.ts | 2 +- packages/sol-cov/src/coverage_subprovider.ts | 2 +- packages/sol-cov/src/profiler_manager.ts | 2 +- packages/sol-cov/src/profiler_subprovider.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'packages/sol-cov/src') diff --git a/packages/sol-cov/src/coverage_manager.ts b/packages/sol-cov/src/coverage_manager.ts index a55967243..673a3e600 100644 --- a/packages/sol-cov/src/coverage_manager.ts +++ b/packages/sol-cov/src/coverage_manager.ts @@ -137,7 +137,7 @@ export class CoverageManager { await mkdirpAsync('coverage'); fs.writeFileSync('coverage/coverage.json', stringifiedCoverage); } - public async computeCoverageAsync(traceInfo: TraceInfo): Promise { + public async computeSingleTraceCoverageAsync(traceInfo: TraceInfo): Promise { if (_.isUndefined(this._contractsData)) { this._contractsData = await this._artifactAdapter.collectContractsDataAsync(); } diff --git a/packages/sol-cov/src/coverage_subprovider.ts b/packages/sol-cov/src/coverage_subprovider.ts index 6fb709e86..3529fb885 100644 --- a/packages/sol-cov/src/coverage_subprovider.ts +++ b/packages/sol-cov/src/coverage_subprovider.ts @@ -25,7 +25,7 @@ export class CoverageSubprovider extends TraceCollectionSubprovider { this._coverageManager = new CoverageManager(artifactAdapter, isVerbose); } public async handleTraceInfoAsync(traceInfo: TraceInfo): Promise { - await this._coverageManager.computeCoverageAsync(traceInfo); + await this._coverageManager.computeSingleTraceCoverageAsync(traceInfo); } /** * Write the test coverage results to a file in Istanbul format. diff --git a/packages/sol-cov/src/profiler_manager.ts b/packages/sol-cov/src/profiler_manager.ts index bec92f424..88e183cbd 100644 --- a/packages/sol-cov/src/profiler_manager.ts +++ b/packages/sol-cov/src/profiler_manager.ts @@ -93,7 +93,7 @@ export class ProfilerManager { await mkdirpAsync('coverage'); fs.writeFileSync('coverage/coverage.json', stringifiedCoverage); } - public async computeCoverageAsync(traceInfo: TraceInfo): Promise { + public async computeSingleTraceCoverageAsync(traceInfo: TraceInfo): Promise { if (_.isUndefined(this._contractsData)) { this._contractsData = await this._artifactAdapter.collectContractsDataAsync(); } diff --git a/packages/sol-cov/src/profiler_subprovider.ts b/packages/sol-cov/src/profiler_subprovider.ts index 34c125b86..9fd815f07 100644 --- a/packages/sol-cov/src/profiler_subprovider.ts +++ b/packages/sol-cov/src/profiler_subprovider.ts @@ -25,7 +25,7 @@ export class ProfilerSubprovider extends TraceCollectionSubprovider { this._profilerManager = new ProfilerManager(artifactAdapter, isVerbose); } public async handleTraceInfoAsync(traceInfo: TraceInfo): Promise { - await this._profilerManager.computeCoverageAsync(traceInfo); + await this._profilerManager.computeSingleTraceCoverageAsync(traceInfo); } /** * Write the test profiler results to a file in Istanbul format. -- cgit v1.2.3