diff options
author | Leonid Logvinov <logvinov.leon@gmail.com> | 2019-01-10 21:35:13 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-10 21:35:13 +0800 |
commit | 6c22594882c94146519ec6e3b24d558127bd092c (patch) | |
tree | 522c5ee89e4af0948b8e645237678f46ec8d3d5c /packages/sol-profiler/src | |
parent | 686f27a96f0cd749f6315d7edd2bb56cf1819245 (diff) | |
parent | b8e3829fdbd1f516686618562172cb45fbb63bde (diff) | |
download | dexon-sol-tools-6c22594882c94146519ec6e3b24d558127bd092c.tar dexon-sol-tools-6c22594882c94146519ec6e3b24d558127bd092c.tar.gz dexon-sol-tools-6c22594882c94146519ec6e3b24d558127bd092c.tar.bz2 dexon-sol-tools-6c22594882c94146519ec6e3b24d558127bd092c.tar.lz dexon-sol-tools-6c22594882c94146519ec6e3b24d558127bd092c.tar.xz dexon-sol-tools-6c22594882c94146519ec6e3b24d558127bd092c.tar.zst dexon-sol-tools-6c22594882c94146519ec6e3b24d558127bd092c.zip |
Merge pull request #1492 from 0xProject/feature/sol-cov-sol-profiler-sol-trace-divorce
Refactor out sol-cov, sol-profiler and sol-trace into their separate packages
Diffstat (limited to 'packages/sol-profiler/src')
-rw-r--r-- | packages/sol-profiler/src/globals.d.ts | 7 | ||||
-rw-r--r-- | packages/sol-profiler/src/index.ts | 25 | ||||
-rw-r--r-- | packages/sol-profiler/src/profiler_subprovider.ts | 98 |
3 files changed, 130 insertions, 0 deletions
diff --git a/packages/sol-profiler/src/globals.d.ts b/packages/sol-profiler/src/globals.d.ts new file mode 100644 index 000000000..e799b3529 --- /dev/null +++ b/packages/sol-profiler/src/globals.d.ts @@ -0,0 +1,7 @@ +// tslint:disable:completed-docs +declare module '*.json' { + const json: any; + /* tslint:disable */ + export default json; + /* tslint:enable */ +} diff --git a/packages/sol-profiler/src/index.ts b/packages/sol-profiler/src/index.ts new file mode 100644 index 000000000..5d4806be4 --- /dev/null +++ b/packages/sol-profiler/src/index.ts @@ -0,0 +1,25 @@ +export { + AbstractArtifactAdapter, + SolCompilerArtifactAdapter, + TruffleArtifactAdapter, + ContractData, +} from '@0x/sol-tracing-utils'; + +// HACK: ProfilerSubprovider is a hacky way to do profiling using coverage tools. Not production ready +export { ProfilerSubprovider } from './profiler_subprovider'; + +export { + JSONRPCRequestPayload, + Provider, + JSONRPCErrorCallback, + JSONRPCResponsePayload, + JSONRPCResponseError, +} from 'ethereum-types'; + +export { + JSONRPCRequestPayloadWithMethod, + NextCallback, + ErrorCallback, + OnNextCompleted, + Callback, +} from '@0x/subproviders'; diff --git a/packages/sol-profiler/src/profiler_subprovider.ts b/packages/sol-profiler/src/profiler_subprovider.ts new file mode 100644 index 000000000..c3ed13ea5 --- /dev/null +++ b/packages/sol-profiler/src/profiler_subprovider.ts @@ -0,0 +1,98 @@ +import * as _ from 'lodash'; + +import { + AbstractArtifactAdapter, + collectCoverageEntries, + ContractData, + Coverage, + SingleFileSubtraceHandler, + SourceRange, + Subtrace, + TraceCollector, + TraceInfo, + TraceInfoSubprovider, + utils, +} from '@0x/sol-tracing-utils'; + +/** + * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface. + * ProfilerSubprovider is used to profile Solidity code while running tests. + */ +export class ProfilerSubprovider extends TraceInfoSubprovider { + private readonly _profilerCollector: TraceCollector; + /** + * Instantiates a ProfilerSubprovider instance + * @param artifactAdapter Adapter for used artifacts format (0x, truffle, giveth, etc.) + * @param defaultFromAddress default from address to use when sending transactions + * @param isVerbose If true, we will log any unknown transactions. Otherwise we will ignore them + */ + constructor(artifactAdapter: AbstractArtifactAdapter, defaultFromAddress: string, isVerbose: boolean = true) { + const traceCollectionSubproviderConfig = { + shouldCollectTransactionTraces: true, + shouldCollectGasEstimateTraces: false, + shouldCollectCallTraces: false, + }; + super(defaultFromAddress, traceCollectionSubproviderConfig); + this._profilerCollector = new TraceCollector(artifactAdapter, isVerbose, profilerHandler); + } + protected async _handleTraceInfoAsync(traceInfo: TraceInfo): Promise<void> { + await this._profilerCollector.computeSingleTraceCoverageAsync(traceInfo); + } + /** + * Write the test profiler results to a file in Istanbul format. + */ + public async writeProfilerOutputAsync(): Promise<void> { + await this._profilerCollector.writeOutputAsync(); + } +} + +/** + * Computed partial coverage for a single file & subtrace for the purposes of + * gas profiling. + * @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 + */ +export const profilerHandler: SingleFileSubtraceHandler = ( + 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; +}; |