aboutsummaryrefslogtreecommitdiffstats
path: root/packages/sol-profiler
diff options
context:
space:
mode:
Diffstat (limited to 'packages/sol-profiler')
-rw-r--r--packages/sol-profiler/.npmignore6
-rw-r--r--packages/sol-profiler/CHANGELOG.json11
-rw-r--r--packages/sol-profiler/README.md75
-rw-r--r--packages/sol-profiler/package.json50
-rw-r--r--packages/sol-profiler/src/globals.d.ts7
-rw-r--r--packages/sol-profiler/src/index.ts25
-rw-r--r--packages/sol-profiler/src/profiler_subprovider.ts98
-rw-r--r--packages/sol-profiler/tsconfig.json8
-rw-r--r--packages/sol-profiler/tslint.json3
-rw-r--r--packages/sol-profiler/typedoc-tsconfig.json7
10 files changed, 290 insertions, 0 deletions
diff --git a/packages/sol-profiler/.npmignore b/packages/sol-profiler/.npmignore
new file mode 100644
index 000000000..037786e46
--- /dev/null
+++ b/packages/sol-profiler/.npmignore
@@ -0,0 +1,6 @@
+.*
+yarn-error.log
+/src/
+/scripts/
+tsconfig.json
+/lib/src/monorepo_scripts/
diff --git a/packages/sol-profiler/CHANGELOG.json b/packages/sol-profiler/CHANGELOG.json
new file mode 100644
index 000000000..223400eae
--- /dev/null
+++ b/packages/sol-profiler/CHANGELOG.json
@@ -0,0 +1,11 @@
+[
+ {
+ "version": "1.0.0",
+ "changes": [
+ {
+ "note": "Initial release as a separate package. For historical entries see @0x/sol-tracing-utils",
+ "pr": 1492
+ }
+ ]
+ }
+]
diff --git a/packages/sol-profiler/README.md b/packages/sol-profiler/README.md
new file mode 100644
index 000000000..44fa29e3f
--- /dev/null
+++ b/packages/sol-profiler/README.md
@@ -0,0 +1,75 @@
+## @0x/sol-profiler
+
+Solidity line-by-line gas profiler.
+
+### Read the [Documentation](https://0xproject.com/docs/sol-profiler).
+
+## Installation
+
+```bash
+yarn add @0x/sol-profiler
+```
+
+**Import**
+
+```javascript
+import { ProfilerSubprovider } from '@0x/sol-profiler';
+```
+
+or
+
+```javascript
+var ProfilerSubprovider = require('@0x/sol-profiler').ProfilerSubprovider;
+```
+
+## Contributing
+
+We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository.
+
+Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
+
+### Install dependencies
+
+If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
+
+```bash
+yarn config set workspaces-experimental true
+```
+
+Then install dependencies
+
+```bash
+yarn install
+```
+
+### Build
+
+To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
+
+```bash
+PKG=@0x/sol-profiler yarn build
+```
+
+Or continuously rebuild on change:
+
+```bash
+PKG=@0x/sol-profiler yarn watch
+```
+
+### Clean
+
+```bash
+yarn clean
+```
+
+### Lint
+
+```bash
+yarn lint
+```
+
+### Run Tests
+
+```bash
+yarn test
+```
diff --git a/packages/sol-profiler/package.json b/packages/sol-profiler/package.json
new file mode 100644
index 000000000..b85c82ed2
--- /dev/null
+++ b/packages/sol-profiler/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "@0x/sol-profiler",
+ "version": "1.0.0",
+ "engines": {
+ "node": ">=6.12"
+ },
+ "description": "Generate profiler reports for Solidity code",
+ "main": "lib/src/index.js",
+ "types": "lib/src/index.d.ts",
+ "scripts": {
+ "build": "tsc -b",
+ "build:ci": "yarn build",
+ "lint": "tslint --format stylish --project .",
+ "clean": "shx rm -rf lib src/artifacts generated_docs",
+ "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
+ },
+ "config": {
+ "postpublish": {
+ "assets": []
+ }
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x-monorepo.git"
+ },
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x-monorepo/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x-monorepo/packages/sol-profiler/README.md",
+ "dependencies": {
+ "@0x/subproviders": "^2.1.8",
+ "@0x/typescript-typings": "^3.0.6",
+ "@0x/sol-tracing-utils": "^2.1.16",
+ "ethereum-types": "^1.1.4",
+ "lodash": "^4.17.5"
+ },
+ "devDependencies": {
+ "@0x/tslint-config": "^2.0.0",
+ "@types/node": "*",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "tslint": "5.11.0",
+ "typedoc": "0.13.0",
+ "typescript": "3.0.1"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
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;
+};
diff --git a/packages/sol-profiler/tsconfig.json b/packages/sol-profiler/tsconfig.json
new file mode 100644
index 000000000..233008d61
--- /dev/null
+++ b/packages/sol-profiler/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib",
+ "rootDir": "."
+ },
+ "include": ["./src/**/*"]
+}
diff --git a/packages/sol-profiler/tslint.json b/packages/sol-profiler/tslint.json
new file mode 100644
index 000000000..dd9053357
--- /dev/null
+++ b/packages/sol-profiler/tslint.json
@@ -0,0 +1,3 @@
+{
+ "extends": ["@0x/tslint-config"]
+}
diff --git a/packages/sol-profiler/typedoc-tsconfig.json b/packages/sol-profiler/typedoc-tsconfig.json
new file mode 100644
index 000000000..a4c669cb6
--- /dev/null
+++ b/packages/sol-profiler/typedoc-tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../typedoc-tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["./src/**/*"]
+}