gas cost per line

pull/3068/head
yann300 2 years ago
parent 11b7e038bb
commit ef4e096554
  1. 10
      apps/debugger/src/app/debugger-api.ts
  2. 10
      libs/remix-debug/src/Ethdebugger.ts
  3. 12
      libs/remix-debug/src/debugger/debugger.ts
  4. 25
      libs/remix-debug/src/solidity-decoder/internalCallTree.ts
  5. 7
      libs/remix-debug/src/solidity-decoder/solidityProxy.ts
  6. 5
      libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx
  7. 9
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  8. 2
      libs/remix-ui/debugger-ui/src/lib/idebugger-api.ts

@ -1,13 +1,7 @@
import Web3 from 'web3' import Web3 from 'web3'
import remixDebug, { TransactionDebugger as Debugger } from '@remix-project/remix-debug' import remixDebug, { TransactionDebugger as Debugger } from '@remix-project/remix-debug'
<<<<<<< HEAD
import { CompilerAbstract } from '@remix-project/remix-solidity' import { CompilerAbstract } from '@remix-project/remix-solidity'
=======
import { CompilationOutput, Sources } from '@remix-ui/debugger-ui'
import { lineText } from '@remix-ui/editor' import { lineText } from '@remix-ui/editor'
import type { CompilationResult } from '@remix-project/remix-solidity-ts'
>>>>>>> show inline gas cost
export const DebuggerApiMixin = (Base) => class extends Base { export const DebuggerApiMixin = (Base) => class extends Base {
@ -48,9 +42,9 @@ export const DebuggerApiMixin = (Base) => class extends Base {
await this.call('editor', 'discardLineTexts' as any) await this.call('editor', 'discardLineTexts' as any)
} }
async highlight (lineColumnPos, path, rawLocation, stepDetail) { async highlight (lineColumnPos, path, rawLocation, stepDetail, lineGasCost) {
await this.call('editor', 'highlight', lineColumnPos, path, '', { focus: true }) await this.call('editor', 'highlight', lineColumnPos, path, '', { focus: true })
const label = `${stepDetail.op} costs ${stepDetail.gasCost} gas - ${stepDetail.gas} gas left` const label = `${stepDetail.op} costs ${stepDetail.gasCost} gas - this line costs ${lineGasCost} gas - ${stepDetail.gas} total gas left`
const linetext: lineText = { const linetext: lineText = {
content: label, content: label,
position: lineColumnPos, position: lineColumnPos,

@ -32,9 +32,11 @@ export class Ethdebugger {
storageResolver storageResolver
callTree callTree
breakpointManager breakpointManager
offsetToLineColumnConverter
constructor (opts) { constructor (opts) {
this.compilationResult = opts.compilationResult || function (contractAddress) { return null } this.compilationResult = opts.compilationResult || function (contractAddress) { return null }
this.offsetToLineColumnConverter = opts.offsetToLineColumnConverter
this.web3 = opts.web3 this.web3 = opts.web3
this.opts = opts this.opts = opts
@ -49,7 +51,8 @@ export class Ethdebugger {
this.traceManager, this.traceManager,
this.solidityProxy, this.solidityProxy,
this.codeManager, this.codeManager,
{ ...opts, includeLocalVariables }) { ...opts, includeLocalVariables },
this.offsetToLineColumnConverter)
} }
setManagers () { setManagers () {
@ -63,7 +66,8 @@ export class Ethdebugger {
this.traceManager, this.traceManager,
this.solidityProxy, this.solidityProxy,
this.codeManager, this.codeManager,
{ ...this.opts, includeLocalVariables }) { ...this.opts, includeLocalVariables },
this.offsetToLineColumnConverter)
} }
resolveStep (index) { resolveStep (index) {
@ -71,7 +75,7 @@ export class Ethdebugger {
} }
setCompilationResult (compilationResult) { setCompilationResult (compilationResult) {
this.solidityProxy.reset((compilationResult && compilationResult.data) || {}) this.solidityProxy.reset((compilationResult && compilationResult.data) || {}, (compilationResult && compilationResult.source && compilationResult.source.sources) || {})
} }
async sourceLocationFromVMTraceIndex (address, stepIndex) { async sourceLocationFromVMTraceIndex (address, stepIndex) {

@ -26,7 +26,8 @@ export class Debugger {
this.debugger = new Ethdebugger({ this.debugger = new Ethdebugger({
web3: options.web3, web3: options.web3,
debugWithGeneratedSources: options.debugWithGeneratedSources, debugWithGeneratedSources: options.debugWithGeneratedSources,
compilationResult: this.compilationResult compilationResult: this.compilationResult,
offsetToLineColumnConverter: this.offsetToLineColumnConverter
}) })
const { traceManager, callTree, solidityProxy } = this.debugger const { traceManager, callTree, solidityProxy } = this.debugger
@ -90,7 +91,14 @@ export class Debugger {
} }
} }
const lineColumnPos = await this.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, sources, astSources) const lineColumnPos = await this.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, sources, astSources)
this.event.trigger('newSourceLocation', [lineColumnPos, rawLocation, generatedSources, address, stepDetail])
let lineGasCost = -1
try {
lineGasCost = await this.debugger.callTree.getGasCostPerLine(rawLocation.file, lineColumnPos.start.line)
} catch (e) {
console.log(e)
}
this.event.trigger('newSourceLocation', [lineColumnPos, rawLocation, generatedSources, address, stepDetail, lineGasCost])
this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [rawLocation]) this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [rawLocation])
} else { } else {
this.event.trigger('newSourceLocation', [null]) this.event.trigger('newSourceLocation', [null])

@ -41,6 +41,8 @@ export class InternalCallTree {
locationAndOpcodePerVMTraceIndex: { locationAndOpcodePerVMTraceIndex: {
[Key: number]: any [Key: number]: any
} }
gasCostPerLine
offsetToLineColumnConverter
/** /**
* constructor * constructor
@ -51,12 +53,13 @@ export class InternalCallTree {
* @param {Object} codeManager - code manager * @param {Object} codeManager - code manager
* @param {Object} opts - { includeLocalVariables, debugWithGeneratedSources } * @param {Object} opts - { includeLocalVariables, debugWithGeneratedSources }
*/ */
constructor (debuggerEvent, traceManager, solidityProxy, codeManager, opts) { constructor (debuggerEvent, traceManager, solidityProxy, codeManager, opts, offsetToLineColumnConverter?) {
this.includeLocalVariables = opts.includeLocalVariables this.includeLocalVariables = opts.includeLocalVariables
this.debugWithGeneratedSources = opts.debugWithGeneratedSources this.debugWithGeneratedSources = opts.debugWithGeneratedSources
this.event = new EventManager() this.event = new EventManager()
this.solidityProxy = solidityProxy this.solidityProxy = solidityProxy
this.traceManager = traceManager this.traceManager = traceManager
this.offsetToLineColumnConverter = offsetToLineColumnConverter
this.sourceLocationTracker = new SourceLocationTracker(codeManager, { debugWithGeneratedSources: opts.debugWithGeneratedSources }) this.sourceLocationTracker = new SourceLocationTracker(codeManager, { debugWithGeneratedSources: opts.debugWithGeneratedSources })
debuggerEvent.register('newTraceLoaded', (trace) => { debuggerEvent.register('newTraceLoaded', (trace) => {
this.reset() this.reset()
@ -99,6 +102,7 @@ export class InternalCallTree {
this.functionCallStack = [] this.functionCallStack = []
this.functionDefinitionsByScope = {} this.functionDefinitionsByScope = {}
this.scopeStarts = {} this.scopeStarts = {}
this.gasCostPerLine = {}
this.variableDeclarationByFile = {} this.variableDeclarationByFile = {}
this.functionDefinitionByFile = {} this.functionDefinitionByFile = {}
this.astWalker = new AstWalker() this.astWalker = new AstWalker()
@ -181,6 +185,13 @@ export class InternalCallTree {
async getValidSourceLocationFromVMTraceIndexFromCache (address: string, step: number, contracts: any) { async getValidSourceLocationFromVMTraceIndexFromCache (address: string, step: number, contracts: any) {
return await this.sourceLocationTracker.getValidSourceLocationFromVMTraceIndexFromCache(address, step, contracts, this.locationAndOpcodePerVMTraceIndex) return await this.sourceLocationTracker.getValidSourceLocationFromVMTraceIndexFromCache(address, step, contracts, this.locationAndOpcodePerVMTraceIndex)
} }
async getGasCostPerLine(file: number, line: number) {
if (this.gasCostPerLine[file] && this.gasCostPerLine[file][line]) {
return this.gasCostPerLine[file][line]
}
throw new Error('Could not find gas cost per line')
}
} }
async function buildTree (tree, step, scopeId, isExternalCall, isCreation) { async function buildTree (tree, step, scopeId, isExternalCall, isCreation) {
@ -211,6 +222,7 @@ async function buildTree (tree, step, scopeId, isExternalCall, isCreation) {
let newLocation = false let newLocation = false
try { try {
sourceLocation = await tree.extractSourceLocation(step) sourceLocation = await tree.extractSourceLocation(step)
if (!includedSource(sourceLocation, currentSourceLocation)) { if (!includedSource(sourceLocation, currentSourceLocation)) {
tree.reducedTrace.push(step) tree.reducedTrace.push(step)
currentSourceLocation = sourceLocation currentSourceLocation = sourceLocation
@ -229,7 +241,16 @@ async function buildTree (tree, step, scopeId, isExternalCall, isCreation) {
} }
tree.locationAndOpcodePerVMTraceIndex[step] = { sourceLocation, stepDetail } tree.locationAndOpcodePerVMTraceIndex[step] = { sourceLocation, stepDetail }
tree.scopes[scopeId].gasCost += stepDetail.gasCost tree.scopes[scopeId].gasCost += stepDetail.gasCost
console.log(step, stepDetail.op, stepDetail.gas, nextStepDetail.gas)
// gas per line
if (tree.offsetToLineColumnConverter) {
try {
const lineColumnPos = await tree.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file, tree.solidityProxy.sourcesCode, tree.solidityProxy.sources)
if (!tree.gasCostPerLine[sourceLocation.file]) tree.gasCostPerLine[sourceLocation.file] = {}
if (!tree.gasCostPerLine[sourceLocation.file][lineColumnPos.start.line]) tree.gasCostPerLine[sourceLocation.file][lineColumnPos.start.line] = 0
tree.gasCostPerLine[sourceLocation.file][lineColumnPos.start.line] += stepDetail.gasCost
} catch (e) {}
}
const isCallInstrn = isCallInstruction(stepDetail) const isCallInstrn = isCallInstruction(stepDetail)
const isCreateInstrn = isCreateInstruction(stepDetail) const isCreateInstrn = isCreateInstruction(stepDetail)

@ -10,6 +10,8 @@ export class SolidityProxy {
getCode getCode
sources sources
contracts contracts
compilationResult
sourcesCode
constructor ({ getCurrentCalledAddressAt, getCode }) { constructor ({ getCurrentCalledAddressAt, getCode }) {
this.cache = new Cache() this.cache = new Cache()
@ -23,9 +25,10 @@ export class SolidityProxy {
* *
* @param {Object} compilationResult - result os a compilatiion (diectly returned by the compiler) * @param {Object} compilationResult - result os a compilatiion (diectly returned by the compiler)
*/ */
reset (compilationResult) { reset (compilationResult, sources?) {
this.sources = compilationResult.sources this.sources = compilationResult.sources // ast
this.contracts = compilationResult.contracts this.contracts = compilationResult.contracts
if (sources) this.sourcesCode = sources
this.cache.reset() this.cache.reset()
} }

@ -1,6 +1,11 @@
import React, { useContext, useEffect, useState } from 'react' import React, { useContext, useEffect, useState } from 'react'
import { AppContext } from '../../context/context' import { AppContext } from '../../context/context'
import { useDialogDispatchers } from '../../context/provider' import { useDialogDispatchers } from '../../context/provider'
declare global {
interface Window {
_paq: any
}
}
const _paq = window._paq = window._paq || [] const _paq = window._paq = window._paq || []
const MatomoDialog = (props) => { const MatomoDialog = (props) => {

@ -121,7 +121,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}) })
}) })
debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources, address, stepDetail) => { debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources, address, stepDetail, lineGasCost) => {
if (!lineColumnPos) { if (!lineColumnPos) {
await debuggerModule.discardHighlight() await debuggerModule.discardHighlight()
setState(prevState => { setState(prevState => {
@ -158,7 +158,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
return { ...prevState, sourceLocationStatus: '' } return { ...prevState, sourceLocationStatus: '' }
}) })
await debuggerModule.discardHighlight() await debuggerModule.discardHighlight()
await debuggerModule.highlight(lineColumnPos, path, rawLocation, stepDetail) await debuggerModule.highlight(lineColumnPos, path, rawLocation, stepDetail, lineGasCost)
} }
} }
}) })
@ -266,13 +266,14 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
console.log(e.message) console.log(e.message)
} }
const localCache = {}
const debuggerInstance = new Debugger({ const debuggerInstance = new Debugger({
web3, web3,
offsetToLineColumnConverter: debuggerModule.offsetToLineColumnConverter, offsetToLineColumnConverter: debuggerModule.offsetToLineColumnConverter,
compilationResult: async (address) => { compilationResult: async (address) => {
try { try {
const ret = await debuggerModule.fetchContractAndCompile(address, currentReceipt) if (!localCache[address]) localCache[address] = await debuggerModule.fetchContractAndCompile(address, currentReceipt)
return ret return localCache[address]
} catch (e) { } catch (e) {
// debuggerModule.showMessage('Debugging error', 'Unable to fetch a transaction.') // debuggerModule.showMessage('Debugging error', 'Unable to fetch a transaction.')
console.error(e) console.error(e)

@ -44,7 +44,7 @@ export interface IDebuggerApi {
onEditorContentChanged: (listener: onEditorContentChanged) => void onEditorContentChanged: (listener: onEditorContentChanged) => void
onEnvChanged: (listener: onEnvChangedListener) => void onEnvChanged: (listener: onEnvChangedListener) => void
discardHighlight: () => Promise<void> discardHighlight: () => Promise<void>
highlight: (lineColumnPos: LineColumnLocation, path: string, rawLocation: any, stepDetail: any) => Promise<void> highlight: (lineColumnPos: LineColumnLocation, path: string, rawLocation: any, stepDetail: any, highlight: any) => Promise<void>
fetchContractAndCompile: (address: string, currentReceipt: TransactionReceipt) => Promise<CompilerAbstract> fetchContractAndCompile: (address: string, currentReceipt: TransactionReceipt) => Promise<CompilerAbstract>
getFile: (path: string) => Promise<string> getFile: (path: string) => Promise<string>
setFile: (path: string, content: string) => Promise<void> setFile: (path: string, content: string) => Promise<void>

Loading…
Cancel
Save