Merge pull request #742 from ethereum/remix_debug

Add Remix debug module
pull/3094/head
yann300 7 years ago committed by GitHub
commit 9c5f02d103
  1. 65
      remix-debug/README.md
  2. 22
      remix-debug/index.js
  3. 96
      remix-debug/package.json
  4. 224
      remix-debug/src/Ethdebugger.js
  5. 3
      remix-debugger/README.md

@ -0,0 +1,65 @@
# `remix-debug`
remix-debug wrap other remix-* libraries and can be used to debug Ethereum transactions.
+ [Installation](#installation)
+ [Development](#development)
## Installation
```bash
npm install remix-debug
```
## Development
```bash
var Debugger = require('remix-debug').EthDebugger
var BreakpointManager = require('remix-debug').EthDebugger
var debugger = new Debugger({
compilationResult: () => {
return compilationResult // that helps resolving source location
}
})
debugger.addProvider(web3, 'web3')
debugger.switchProvider('web3')
var breakPointManager = new remixCore.code.BreakpointManager(this.debugger, (sourceLocation) => {
// return offsetToLineColumn
})
debugger.setBreakpointManager(breakPointManager)
breakPointManager.add({fileName, row})
breakPointManager.add({fileName, row})
debugger.debug(<tx_hash>)
// this.traceManager.getCurrentCalledAddressAt
debugger.event.register('newTraceLoaded', () => {
// start doing basic stuff like retrieving step details
debugger.traceManager.getCallStackAt(34, (error, callstack) => {})
})
debugger.callTree.register('callTreeReady', () => {
// start doing more complex stuff like resolvng local variables
breakPointManager.jumpNextBreakpoint(true)
var storageView = debugger.storageViewAt(38, <contract address>,
storageView.storageSlot(0, (error, storage) => {})
storageView.storageRange(error, storage) => {}) // retrieve 0 => 1000 slots
debugger.extractStateAt(23, (error, state) => {
debugger.decodeStateAt(23, state, (error, decodedState) => {})
})
debugger.sourceLocationFromVMTraceIndex(<contract address>, 23, (error, location) => {
debugger.decodeLocalsAt(23, location, (error, decodedlocals) => {})
})
debugger.extractLocalsAt(23, (null, locals) => {}
})
```

@ -0,0 +1,22 @@
'use strict'
var remixCore = require('remix-core')
var EthDebugger = require('./src/Ethdebugger')
/*
Use of breakPointManager :
var breakPointManager = new BreakpointManager(this.debugger, (sourceLocation) => {
return line/column from offset (sourceLocation)
})
this.debugger.setBreakpointManager(breakPointManager)
*/
module.exports = {
EthDebugger: EthDebugger,
/**
* constructor
*
* @param {Object} _debugger - type of EthDebugger
* @return {Function} _locationToRowConverter - function implemented by editor which return a column/line position for a char source location
*/
BreakpointManager: remixCore.code.BreakpointManager
}

@ -0,0 +1,96 @@
{
"name": "remix-debug",
"version": "0.0.2",
"description": "Ethereum IDE and tools for the web",
"contributors": [
{
"name": "Yann Levreau",
"email": "yann@ethdev.com"
},
{
"name": "Liana Husikyan",
"email": "liana@ethdev.com"
}
],
"main": "./index.js",
"devDependencies": {
"babel-eslint": "^7.1.1",
"babel-plugin-transform-object-assign": "^6.22.0",
"babel-plugin-yo-yoify": "^0.3.3",
"babel-polyfill": "^6.22.0",
"babel-preset-env": "^1.6.1",
"babel-preset-es2015": "^6.24.0",
"babel-preset-stage-0": "^6.24.1",
"babelify": "^7.3.0",
"notify-error": "^1.2.0",
"npm-run-all": "^4.1.2",
"remix-core": "latest",
"remix-lib": "latest",
"remix-solidity": "latest",
"standard": "^7.0.1",
"standard-reporter": "^1.0.5"
},
"scripts": {
"build": "mkdirp build; browserify index.js > build/app.js",
"lint": "standard | notify-error"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ethereum/remix.git"
},
"author": "cpp-ethereum team",
"license": "MIT",
"bugs": {
"url": "https://github.com/ethereum/remix/issues"
},
"homepage": "https://github.com/ethereum/remix#readme",
"standard": {
"ignore": [
"node_modules/*",
"build/*",
"test/resources/*"
]
},
"babel": {
"plugins": [
"transform-es2015-template-literals",
"transform-es2015-literals",
"transform-es2015-function-name",
"transform-es2015-arrow-functions",
"transform-es2015-block-scoped-functions",
"transform-es2015-classes",
"transform-es2015-object-super",
"transform-es2015-shorthand-properties",
"transform-es2015-duplicate-keys",
"transform-es2015-computed-properties",
"transform-es2015-for-of",
"transform-es2015-sticky-regex",
"transform-es2015-unicode-regex",
"check-es2015-constants",
"transform-es2015-spread",
"transform-es2015-parameters",
"transform-es2015-destructuring",
"transform-es2015-block-scoping",
"transform-object-assign"
]
},
"browserify": {
"transform": [
[
"babelify",
{
"sourceMapsAbsolute": false,
"sourceMaps": true,
"plugins": [
[
"transform-object-assign"
]
],
"presets": [
"es2015"
]
}
]
]
}
}

@ -0,0 +1,224 @@
'use strict'
var remixCore = require('remix-core')
var TraceManager = remixCore.trace.TraceManager
var StorageViewer = remixCore.storage.StorageViewer
var remixLib = require('remix-lib')
var traceHelper = remixLib.helpers.trace
var global = remixLib.global
var init = remixLib.init
var executionContext = remixLib.execution.executionContext
var EventManager = remixLib.EventManager
var Web3Providers = remixLib.vm.Web3Providers
var DummyProvider = remixLib.vm.DummyProvider
var CodeManager = remixCore.code.CodeManager
var remixSolidity = require('remix-solidity')
var SolidityProxy = remixSolidity.SolidityProxy
var stateDecoder = remixSolidity.stateDecoder
var localDecoder = remixSolidity.localDecoder
var InternalCallTree = remixSolidity.InternalCallTree
var StorageResolver = remixCore.storage.StorageResolver
/**
* Ethdebugger is a wrapper around a few classes that helps debugging a transaction
*
* - Web3Providers - define which environment (web3) the transaction will be retrieved from
* - TraceManager - Load / Analyze the trace and retrieve details of specific test
* - CodeManager - Retrieve loaded byte code and help to resolve AST item from vmtrace index
* - SolidityProxy - Basically used to extract state variable from AST
* - Breakpoint Manager - Used to add / remove / jumpto breakpoint
* - InternalCallTree - Used to retrieved local variables
* - StorageResolver - Help resolving the storage accross different steps
*
* @param {Map} opts - { function compilationResult } //
*/
function Ethdebugger (opts) {
this.opts = opts || {}
if (!this.opts.compilationResult) this.opts.compilationResult = () => { return null }
this.event = new EventManager()
this.tx
this.web3Providers = new Web3Providers()
this.addProvider('DUMMYWEB3', new DummyProvider())
this.switchProvider('DUMMYWEB3')
this.traceManager = new TraceManager()
this.codeManager = new CodeManager(this.traceManager)
this.solidityProxy = new SolidityProxy(this.traceManager, this.codeManager)
this.storageResolver = null
this.callTree = new InternalCallTree(this.event, this.traceManager, this.solidityProxy, this.codeManager, { includeLocalVariables: true })
}
Ethdebugger.prototype.resolveStep = function (index) {
this.codeManager.resolveStep(index, this.tx)
}
Ethdebugger.prototype.setCompilationResult = function (compilationResult) {
if (compilationResult && compilationResult.sources && compilationResult.contracts) {
this.solidityProxy.reset(compilationResult)
} else {
this.solidityProxy.reset({})
}
}
/* resolve source location */
Ethdebugger.prototype.sourceLocationFromVMTraceIndex = function (address, stepIndex, callback) {
this.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, stepIndex, this.solidityProxy.contracts, (error, rawLocation) => {
callback(error, rawLocation)
})
}
Ethdebugger.prototype.sourceLocationFromInstructionIndex = function (address, instIndex, callback) {
this.debugger.callTree.sourceLocationTracker.getSourceLocationFromInstructionIndex(address, instIndex, this.solidityProxy.contracts, function (error, rawLocation) {
callback(error, rawLocation)
})
}
/* breakpoint */
Ethdebugger.prototype.setBreakpointManager = function (breakpointManager) {
this.breakpointManager = breakpointManager
}
/* decode locals */
Ethdebugger.prototype.extractLocalsAt = function (step, callback) {
callback(null, this.callTree.findScope(step))
}
Ethdebugger.prototype.decodeLocalsAt = function (step, sourceLocation, callback) {
this.traceManager.waterfall([
this.traceManager.getStackAt,
this.traceManager.getMemoryAt,
this.traceManager.getCurrentCalledAddressAt],
step,
(error, result) => {
if (!error) {
var stack = result[0].value
var memory = result[1].value
try {
var storageViewer = new StorageViewer({
stepIndex: step,
tx: this.tx,
address: result[2].value
}, this.storageResolver, this.traceManager)
localDecoder.solidityLocals(step, this.callTree, stack, memory, storageViewer, sourceLocation).then((locals) => {
if (!locals.error) {
callback(null, locals)
} else {
callback(locals.error)
}
})
} catch (e) {
callback(e.message)
}
} else {
callback(error)
}
})
}
/* decode state */
Ethdebugger.prototype.extractStateAt = function (step, callback) {
this.solidityProxy.extractStateVariablesAt(step, function (error, stateVars) {
callback(error, stateVars)
})
}
Ethdebugger.prototype.decodeStateAt = function (step, stateVars, callback) {
this.traceManager.getCurrentCalledAddressAt(step, (error, address) => {
if (error) return callback(error)
var storageViewer = new StorageViewer({
stepIndex: step,
tx: this.tx,
address: address
}, this.storageResolver, this.traceManager)
stateDecoder.decodeState(stateVars, storageViewer).then((result) => {
if (!result.error) {
callback(null, result)
} else {
callback(result.error)
}
})
})
}
Ethdebugger.prototype.storageViewAt = function (step, address) {
return new StorageViewer({
stepIndex: step,
tx: this.tx,
address: address
}, this.storageResolver, this.traceManager)
}
/* set env */
Ethdebugger.prototype.web3 = function () {
return global.web3
}
Ethdebugger.prototype.addProvider = function (type, obj) {
this.web3Providers.addProvider(type, obj)
this.event.trigger('providerAdded', [type])
}
Ethdebugger.prototype.switchProvider = function (type) {
var self = this
this.web3Providers.get(type, function (error, obj) {
if (error) {
console.log('provider ' + type + ' not defined')
} else {
global.web3 = obj
executionContext.detectNetwork((error, network) => {
if (error || !network) {
global.web3Debug = obj
} else {
var webDebugNode = init.web3DebugNode(network.name)
global.web3Debug = !webDebugNode ? obj : webDebugNode
}
})
self.event.trigger('providerChanged', [type])
}
})
}
Ethdebugger.prototype.debug = function (tx) {
this.setCompilationResult(this.opts.compilationResult())
if (tx instanceof Object) {
this.txBrowser.load(tx.hash)
} else if (tx instanceof String) {
this.txBrowser.load(tx)
}
}
Ethdebugger.prototype.unLoad = function () {
this.traceManager.init()
this.codeManager.clear()
this.stepManager.reset()
this.event.trigger('traceUnloaded')
}
Ethdebugger.prototype.debug = function (tx) {
if (this.traceManager.isLoading) {
return
}
if (!tx.to) {
tx.to = traceHelper.contractCreationToken('0')
}
this.setCompilationResult(this.opts.compilationResult())
console.log('loading trace...')
this.tx = tx
var self = this
this.traceManager.resolveTrace(tx, function (error, result) {
console.log('trace loaded ' + result)
if (result) {
self.event.trigger('newTraceLoaded', [self.traceManager.trace])
if (self.breakpointManager && self.breakpointManager.hasBreakpoint()) {
self.breakpointManager.jumpNextBreakpoint(false)
}
self.storageResolver = new StorageResolver()
} else {
self.statusMessage = error ? error.message : 'Trace not loaded'
}
})
}
module.exports = Ethdebugger

@ -1,5 +1,6 @@
# `remix-debugger`
# (Remix debugger has been deprecated and is not maintained anymore - the `remix-debug` module can be used to build your own debugger)
The Remix Debugger is a webapp to debug the Ethereum VM and transactions.
+ [Installation](#installation)

Loading…
Cancel
Save