Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

Commit

Permalink
Merge pull request #1638 from 0xProject/feature/sol-profiler-improvem…
Browse files Browse the repository at this point in the history
…ents

Sol profiler improvements
  • Loading branch information
LogvinovLeon authored Feb 25, 2019
2 parents df8ea3b + 7067d4a commit 3383224
Show file tree
Hide file tree
Showing 17 changed files with 473 additions and 59 deletions.
4 changes: 4 additions & 0 deletions packages/ethereum-types/CHANGELOG.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
{
"note": "Added types SupportedProvider, GanacheProvider, Web3JsProvider, ZeroExProvider, EIP1193Provider",
"pr": 1627
},
{
"note": "Added all existing opcodes to `OpCode` type",
"pr": 1638
}
]
},
Expand Down
147 changes: 141 additions & 6 deletions packages/ethereum-types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,150 @@ export interface DataItem {
}

export enum OpCode {
DelegateCall = 'DELEGATECALL',
Revert = 'REVERT',
Create = 'CREATE',
// 0s: Stop and Arithmetic Operations
Stop = 'STOP',
Invalid = 'INVALID',
Add = 'ADD',
Mul = 'MUL',
Sub = 'SUB',
Div = 'DIV',
SDiv = 'SDIV',
Mod = 'MOD',
SMod = 'SMOD',
AddMod = 'ADDMOD',
MulMod = 'MULMOD',
Exp = 'EXP',
SignExtend = 'SIGNEXTEND',
// 10s: Comparison & Bitwise Logic Operations
Lt = 'LT',
Gt = 'GT',
SLt = 'SLT',
SGt = 'SGT',
Eq = 'EQ',
IsZero = 'ISZERO',
And = 'AND',
Or = 'OR',
Xor = 'XOR',
Not = 'NOT',
Byte = 'BYTE',
// 20s: SHA3
Sha3 = 'SHA3',
// 30s: Environmental Information
Address = 'ADDRESS',
Balance = 'BALANCE',
Origin = 'ORIGIN',
Caller = 'CALLER',
CallValue = 'CALLVALUE',
CallDataLoad = 'CALLDATALOAD',
CallDataSize = 'CALLDATASIZE',
CallDataCopy = 'CALLDATACOPY',
CodeSize = 'CODESIZE',
CodeCopy = 'CODECOPY',
GasPrice = 'GASPRICE',
ExtCodeSize = 'EXTCODESIZE',
ExtCodeCopy = 'EXTCODECOPY',
ReturnDataSize = 'RETURNDATASIZE',
ReturnDataCopy = 'RETURNDATACOPY',
// 40s: Block Information
BlockHash = 'BLOCKHASH',
Coinbase = 'COINBASE',
TimeStamp = 'TimeStamp',
Number = 'NUMBER',
Difficulty = 'DIFFICULTY',
Gaslimit = 'GASLIMIT',
// 50s: Stack, Memory, Storage and Flow Operations
Pop = 'POP',
MLoad = 'MLOAD',
MStore = 'MSTORE',
MStore8 = 'MSTORE8',
SLoad = 'SLOAD',
SStore = 'SSTORE',
Jump = 'JUMP',
Jumpi = 'JUMPI',
Pc = 'PC',
MSize = 'MSIZE',
Gas = 'GAS',
JumpDest = 'JUMPDEST',
// 60s & 70s: Push Operations
Push1 = 'PUSH1',
Push2 = 'PUSH2',
Push3 = 'PUSH3',
Push4 = 'PUSH4',
Push5 = 'PUSH5',
Push6 = 'PUSH6',
Push7 = 'PUSH7',
Push8 = 'PUSH8',
Push9 = 'PUSH9',
Push10 = 'PUSH10',
Push11 = 'PUSH11',
Push12 = 'PUSH12',
Push13 = 'PUSH13',
Push14 = 'PUSH14',
Push15 = 'PUSH15',
Push16 = 'PUSH16',
Push17 = 'PUSH17',
Push18 = 'PUSH18',
Push19 = 'PUSH19',
Push20 = 'PUSH20',
Push21 = 'PUSH21',
Push22 = 'PUSH22',
Push23 = 'PUSH23',
Push24 = 'PUSH24',
Push25 = 'PUSH25',
Push26 = 'PUSH26',
Push27 = 'PUSH27',
Push28 = 'PUSH28',
Push29 = 'PUSH29',
Push30 = 'PUSH30',
Push31 = 'PUSH31',
Push32 = 'PUSH32',
// 80s: Duplication Operation
Dup1 = 'DUP1',
Dup2 = 'DUP2',
Dup3 = 'DUP3',
Dup4 = 'DUP4',
Dup5 = 'DUP5',
Dup6 = 'DUP6',
Dup7 = 'DUP7',
Dup8 = 'DUP8',
Dup9 = 'DUP9',
Dup10 = 'DUP10',
Dup11 = 'DUP11',
Dup12 = 'DUP12',
Dup13 = 'DUP13',
Dup14 = 'DUP14',
Dup15 = 'DUP15',
Dup16 = 'DUP16',
// 90s: Exchange Operation
Swap1 = 'SWAP1',
Swap2 = 'SWAP2',
Swap3 = 'SWAP3',
Swap4 = 'SWAP4',
Swap5 = 'SWAP5',
Swap6 = 'SWAP6',
Swap7 = 'SWAP7',
Swap8 = 'SWAP8',
Swap9 = 'SWAP9',
Swap10 = 'SWAP10',
Swap11 = 'SWAP11',
Swap12 = 'SWAP12',
Swap13 = 'SWAP13',
Swap14 = 'SWAP14',
Swap15 = 'SWAP15',
Swap16 = 'SWAP16',
// a0s: Logging Operations
Log1 = 'LOG1',
Log2 = 'LOG2',
Log3 = 'LOG3',
Log4 = 'LOG4',
// f0s: System operations
Create = 'CREATE',
Call = 'CALL',
CallCode = 'CALLCODE',
StaticCall = 'STATICCALL',
Return = 'RETURN',
Call = 'CALL',
DelegateCall = 'DELEGATECALL',
StaticCall = 'STATICCALL',
Revert = 'REVERT',
Invalid = 'INVALID',
SelfDestruct = 'SELFDESTRUCT',
}

Expand Down
6 changes: 3 additions & 3 deletions packages/sol-coverage/src/coverage_subprovider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {
StatementCoverage,
StatementDescription,
Subtrace,
SubTraceInfo,
TraceCollector,
TraceInfo,
TraceInfoSubprovider,
utils,
} from '@0x/sol-tracing-utils';
Expand All @@ -39,8 +39,8 @@ export class CoverageSubprovider extends TraceInfoSubprovider {
super(defaultFromAddress, traceCollectionSubproviderConfig);
this._coverageCollector = new TraceCollector(artifactAdapter, isVerbose, coverageHandler);
}
protected async _handleTraceInfoAsync(traceInfo: TraceInfo): Promise<void> {
await this._coverageCollector.computeSingleTraceCoverageAsync(traceInfo);
protected async _handleSubTraceInfoAsync(subTraceInfo: SubTraceInfo): Promise<void> {
await this._coverageCollector.computeSingleTraceCoverageAsync(subTraceInfo);
}
/**
* Write the test coverage results to a file in Istanbul format.
Expand Down
21 changes: 21 additions & 0 deletions packages/sol-profiler/CHANGELOG.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
[
{
"version": "3.0.0",
"changes": [
{
"note": "Big @0x/sol-profiler overhaul. It now has a bunch of new features",
"pr": 1638
},
{
"note": "Added a CLI interface for reporting non line-based profiling info",
"pr": 1638
},
{
"note": "Add memory consumption analysis",
"pr": 1638
},
{
"note": "Add calldata analysis",
"pr": 1638
}
]
},
{
"timestamp": 1549733923,
"version": "2.0.4",
Expand Down
4 changes: 3 additions & 1 deletion packages/sol-profiler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"@0x/subproviders": "^3.0.3",
"@0x/typescript-typings": "^4.0.0",
"ethereum-types": "^2.0.0",
"ethereumjs-util": "^5.1.1",
"@0x/utils": "^4.1.0",
"lodash": "^4.17.11",
"web3-provider-engine": "14.0.6"
},
Expand All @@ -49,4 +51,4 @@
"publishConfig": {
"access": "public"
}
}
}
94 changes: 94 additions & 0 deletions packages/sol-profiler/src/cost_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { TraceInfo } from '@0x/sol-tracing-utils';
import { logUtils } from '@0x/utils';
import { OpCode } from 'ethereum-types';
import { stripHexPrefix } from 'ethereumjs-util';
import * as _ from 'lodash';

const ZERO_BYTE_CALL_DATA_COST = 4;
const NON_ZERO_BYTE_CALL_DATA_COST = 68;
const WORD_SIZE = 32;
const G_MEMORY = 3;
const G_QUAD_COEF = 512;
const HEX_BASE = 16;
const G_COPY = 3;

export const costUtils = {
reportCallDataCost(traceInfo: TraceInfo): number {
if (_.isUndefined(traceInfo.dataIfExists)) {
// No call data to report
return 0;
}
const callData = traceInfo.dataIfExists;
const callDataBuf = Buffer.from(stripHexPrefix(callData), 'hex');
const { true: zeroBytesCountIfExist, false: nonZeroBytesCountIfExist } = _.countBy(
callDataBuf,
byte => byte === 0,
);
const zeroBytesCost = (zeroBytesCountIfExist || 0) * ZERO_BYTE_CALL_DATA_COST;
const nonZeroBytesCost = (nonZeroBytesCountIfExist || 0) * NON_ZERO_BYTE_CALL_DATA_COST;
const callDataCost = zeroBytesCost + nonZeroBytesCost;
logUtils.header('Call data breakdown', '-');
logUtils.table({
'call data size (bytes)': callData.length,
callDataCost,
zeroBytesCost,
nonZeroBytesCost,
zeroBytesCountIfExist,
nonZeroBytesCountIfExist,
});
return callDataCost;
},
reportMemoryCost(traceInfo: TraceInfo): number {
const structLogs = traceInfo.trace.structLogs;
const MEMORY_OPCODES = [OpCode.MLoad, OpCode.MStore, OpCode.MStore8];
const CALL_DATA_OPCODES = [OpCode.CallDataCopy];
const memoryLogs = _.filter(structLogs, structLog =>
_.includes([...MEMORY_OPCODES, ...CALL_DATA_OPCODES], structLog.op),
);
const memoryLocationsAccessed = _.map(memoryLogs, structLog => {
if (_.includes(CALL_DATA_OPCODES, structLog.op)) {
const memOffset = parseInt(structLog.stack[0], HEX_BASE);
const length = parseInt(structLog.stack[2], HEX_BASE);
return memOffset + length;
} else {
return parseInt(structLog.stack[0], HEX_BASE);
}
});
const highestMemoryLocationAccessed = _.max(memoryLocationsAccessed);
return costUtils._printMemoryCost(highestMemoryLocationAccessed);
},
reportCopyingCost(traceInfo: TraceInfo): number {
const structLogs = traceInfo.trace.structLogs;
const COPY_OPCODES = [OpCode.CallDataCopy];
const copyLogs = _.filter(structLogs, structLog => _.includes(COPY_OPCODES, structLog.op));
const copyCosts = _.map(copyLogs, structLog => {
const length = parseInt(structLog.stack[2], HEX_BASE);
return Math.ceil(length / WORD_SIZE) * G_COPY;
});
return _.sum(copyCosts);
},
reportOpcodesCost(traceInfo: TraceInfo): number {
const structLogs = traceInfo.trace.structLogs;
const gasCosts = _.map(structLogs, structLog => structLog.gasCost);
const gasCost = _.sum(gasCosts);
return gasCost;
},
_printMemoryCost(highestMemoryLocationAccessed?: number): number {
if (_.isUndefined(highestMemoryLocationAccessed)) {
return 0;
}
const memoryWordsUsed = Math.ceil((highestMemoryLocationAccessed + WORD_SIZE) / WORD_SIZE);
const linearMemoryCost = G_MEMORY * memoryWordsUsed;
const quadraticMemoryCost = Math.floor((memoryWordsUsed * memoryWordsUsed) / G_QUAD_COEF);
const memoryCost = linearMemoryCost + quadraticMemoryCost;
logUtils.header('Memory breakdown', '-');
logUtils.table({
'memoryCost = linearMemoryCost + quadraticMemoryCost': memoryCost,
linearMemoryCost,
quadraticMemoryCost,
highestMemoryLocationAccessed,
memoryWordsUsed,
});
return memoryCost;
},
};
Loading

0 comments on commit 3383224

Please sign in to comment.