Commit 6d0de8aa authored by yann300's avatar yann300

Merge pull request #25 from yann300/vmTrace

[vmtrace] load storage async
parents 6449d5a9 7a90aee4
......@@ -2,3 +2,4 @@ build
node_modules
npm-debug.log
lint.xml
.vscode
'use strict'
var React = require('react')
var style = require('./basicStyles')
var codeResolver = require('./codeResolver')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object,
tx: React.PropTypes.object,
web3: React.PropTypes.object
},
getInitialState: function () {
return {
code: [],
selected: -1,
address: '' // selected instruction in the asm
}
},
getDefaultProps: function () {
return {
currentStepIndex: -1
}
},
render: function () {
return (
<select
size='10'
ref='itemsList'
style={style.instructionsList}
value={this.state.selected}>
{this.renderAssemblyItems()}
</select>
)
},
renderAssemblyItems: function () {
if (this.state.code) {
return this.state.code.map(function (item, i) {
return <option key={i} value={i}>{item}</option>
})
}
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
codeResolver.setWeb3(this.context.web3)
var self = this
this.context.traceManager.getCurrentCalledAddressAt(nextProps.currentStepIndex, function (error, address) {
if (error) {
console.log(error)
} else {
self.ensureCodeLoaded(address, nextProps.currentStepIndex)
}
})
},
ensureCodeLoaded: function (address, currentStep) {
if (address !== this.state.address) {
this.setState({
code: ['loading...']
})
var self = this
codeResolver.resolveCode(address, currentStep, this.context.tx, function (address, code) {
if (window.ethDebuggerSelectedItem !== currentStep) {
console.log(currentStep + ' discarded. current is ' + window.ethDebuggerSelectedItem)
return
}
self.setState({
code: code,
address: address
})
self.setInstructionIndex(address, currentStep)
})
} else {
this.setInstructionIndex(this.state.address, currentStep)
}
},
setInstructionIndex: function (address, step) {
var self = this
this.context.traceManager.getCurrentPC(step, function (error, instIndex) {
if (error) {
console.log(error)
} else {
self.setState({
selected: codeResolver.getInstructionIndex(address, instIndex)
})
}
})
}
})
This diff is collapsed.
......@@ -38,7 +38,7 @@ module.exports = React.createClass({
ret.push(
<tr key={key}>
<td>
{this.props.data[key]}
<pre style={style.font} >{this.props.data[key]}</pre>
</td>
</tr>)
}
......
'use strict'
module.exports = {
wrapper: {
font: {
'fontFamily': 'arial,sans-serif'
},
container: {
......@@ -11,7 +11,8 @@ module.exports = {
'fontStyle': 'italic'
},
instructionsList: {
'width': '320px'
'width': '320px',
'height': '300px'
},
transactionInfo: {
'marginTop': '5px'
......@@ -22,7 +23,7 @@ module.exports = {
'width': '600px'
},
tableContainer: {
'height': '150px',
'height': '300px',
'overflowY': 'auto'
},
table: {
......
......@@ -2,6 +2,10 @@
var React = require('react')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object
},
propTypes: {
stepIntoBack: React.PropTypes.func.isRequired,
stepIntoForward: React.PropTypes.func.isRequired,
......@@ -32,7 +36,7 @@ module.exports = React.createClass({
if (incr === -1) {
return this.props.step === 0 ? 'disabled' : ''
} else if (incr === 1) {
return this.props.step >= this.props.vmTraceLength - 1 ? 'disabled' : ''
return this.props.step >= this.props.max - 1 ? 'disabled' : ''
}
}
})
'use strict'
var React = require('react')
var BasicPanel = require('./basicPanel')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object
},
getDefaultProps: function () {
return {
currentStepIndex: -1
}
},
getInitialState: function () {
return {
data: null
}
},
render: function () {
return (
<BasicPanel name='CallData' data={this.state.data} />
)
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return
var self = this
this.context.traceManager.getCallDataAt(nextProps.currentStepIndex, function (error, calldata) {
if (error) {
console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({
data: calldata
})
}
})
}
})
'use strict'
var React = require('react')
var BasicPanel = require('./basicPanel')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object
},
getDefaultProps: function () {
return {
currentStepIndex: -1
}
},
getInitialState: function () {
return {
data: null
}
},
render: function () {
return (
<BasicPanel name='CallStack' data={this.state.data} />
)
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return
var self = this
this.context.traceManager.getCallStackAt(nextProps.currentStepIndex, function (error, callstack) {
if (error) {
console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({
data: callstack
})
}
})
}
})
'use strict'
var codeUtils = require('./codeUtils')
module.exports = {
web3: null,
codes: {}, // assembly items instructions list by contract addesses
instructionsIndexByBytesOffset: {}, // mapping between bytes offset and instructions index.
setWeb3: function (web3) {
this.web3 = web3
},
resolveCode: function (address, vmTraceIndex, transaction, callBack) {
var cache = this.getExecutingCodeFromCache(address)
if (cache) {
callBack(address, cache.code)
return
}
if (vmTraceIndex === 0 && transaction.to === null) { // start of the trace
callBack(address, this.cacheExecutingCode(address, transaction.input).code)
return
}
var self = this
this.loadCode(address, function (code) {
callBack(address, self.cacheExecutingCode(address, code).code)
})
},
loadCode: function (address, callback) {
console.log('loading new code from web3 ' + address)
this.web3.eth.getCode(address, function (error, result) {
if (error) {
console.log(error)
} else {
callback(result)
}
})
},
cacheExecutingCode: function (address, hexCode) {
var code = codeUtils.nameOpCodes(new Buffer(hexCode.substring(2), 'hex'))
this.codes[address] = code[0]
this.instructionsIndexByBytesOffset[address] = code[1]
return {
code: code[0],
instructionsIndexByBytesOffset: code[1]
}
},
getExecutingCodeFromCache: function (address) {
if (this.codes[address]) {
return {
code: this.codes[address],
instructionsIndexByBytesOffset: this.instructionsIndexByBytesOffset[address]
}
} else {
return null
}
},
getInstructionIndex: function (address, pc) {
return this.getExecutingCodeFromCache(address).instructionsIndexByBytesOffset[pc]
}
}
'use strict'
var React = require('react')
var TxBrowser = require('./txBrowser')
var VmTraceBrowser = require('./vmTraceBrowser')
var StepManager = require('./stepManager')
var AssemblyItemsBrowser = require('./vmDebugger')
var TraceManager = require('./traceManager')
var style = require('./basicStyles')
module.exports = React.createClass({
getInitialState: function () {
return {vmTrace: null, state: '', currentStep: -1}
return {
currentStepIndex: -1, // index of the selected item in the vmtrace
tx: null,
traceManager: null
}
},
childContextTypes: {
web3: React.PropTypes.object
web3: React.PropTypes.object,
traceManager: React.PropTypes.object,
tx: React.PropTypes.object
},
getChildContext: function () {
return { web3: this.props.web3 }
return {
web3: this.props.web3,
traceManager: this.state.traceManager,
tx: this.state.tx
}
},
componentDidMount: function () {
this.setState({
traceManager: new TraceManager(this.props.web3)
})
},
render: function () {
return (
<div style={style.wrapper}>
<div style={style.font}>
<h1 style={style.container}>Eth Debugger</h1>
<TxBrowser onNewTxRequested={this.retrieveVmTrace} />
<div style={style.container}>
{this.state.state}
</div>
<VmTraceBrowser vmTrace={this.state.vmTrace} />
<TxBrowser onNewTxRequested={this.startDebugging} />
<StepManager ref='stepManager' onStepChanged={this.stepChanged} />
<AssemblyItemsBrowser ref='assemblyitemsbrowser' currentStepIndex={this.state.currentStepIndex} />
</div>
)
},
retrieveVmTrace: function (blockNumber, txNumber) {
this.setState({state: 'loading...'})
stepChanged: function (stepIndex) {
this.setState({
currentStepIndex: stepIndex
})
},
startDebugging: function (blockNumber, txIndex, tx) {
if (this.state.traceManager.isLoading) {
return
}
console.log('loading trace...')
this.setState({
tx: tx
})
var self = this
this.props.web3.debug.trace(blockNumber, parseInt(txNumber), function (error, result) {
if (error) {
console.log(error)
} else {
self.setState({vmTrace: result, state: ''})
}
this.state.traceManager.resolveTrace(blockNumber, txIndex, function (success) {
console.log('trace loaded ' + success)
self.setState({
currentStepIndex: 0
})
self.refs.stepManager.newTraceAvailable()
})
}
})
'use strict'
var React = require('react')
var BasicPanel = require('./basicPanel')
var style = require('./basicStyles')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object,
web3: React.PropTypes.object
},
getDefaultProps: function () {
return {
currentStepIndex: -1
}
},
getInitialState: function () {
return {
data: null
}
},
render: function () {
return (
<BasicPanel name='Memory' data={this.state.data} renderRow={this.renderMemoryRow} />
)
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return
var self = this
this.context.traceManager.getMemoryAt(nextProps.currentStepIndex, function (error, memory) {
if (error) {
console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({
data: self.formatMemory(memory, 16)
})
}
})
},
renderMemoryRow: function (data) {
var ret = []
if (data) {
for (var key in data) {
var memSlot = data[key]
ret.push(
<tr key={key}>
<td>
<pre style={style.font}>{memSlot.address}</pre>
</td>
<td>
<pre style={style.font}>{memSlot.content.raw}</pre>
</td>
<td>
<pre style={style.font}>{memSlot.content.ascii}</pre>
</td>
</tr>)
}
}
return ret
},
formatMemory: function (mem, width) {
var ret = []
for (var k = 0; k < mem.length; k += (width * 2)) {
var memory = mem.substr(k, width * 2)
ret.push({
address: this.context.web3.toHex(k),
content: this.tryAsciiFormat(memory)
})
}
return ret
},
tryAsciiFormat: function (memorySlot) {
var ret = { ascii: '', raw: '' }
for (var k = 0; k < memorySlot.length; k += 2) {
var raw = memorySlot.substr(k, 2)
var ascii = this.context.web3.toAscii(raw)
if (ascii === String.fromCharCode(0)) {
ret.ascii += '?'
} else {
ret.ascii += ascii
}
ret.raw += ' ' + raw
}
return ret
}
})
......@@ -3,6 +3,10 @@ var React = require('react')
var style = require('./sliderStyles')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object
},
propTypes: {
onChange: React.PropTypes.func.isRequired
},
......@@ -10,7 +14,7 @@ module.exports = React.createClass({
getDefaultProps: function () {
return {
min: 0,
max: 500
max: 1
}
},
......@@ -28,6 +32,10 @@ module.exports = React.createClass({
)
},
componentDidMount: function () {
this.setValue(0)
},
onMouseUp: function (event) {
this.props.onChange(parseInt(this.refs.rule.value))
},
......
'use strict'
var React = require('react')
var BasicPanel = require('./basicPanel')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object
},
getDefaultProps: function () {
return {
currentStepIndex: -1
}
},
getInitialState: function () {
return {
data: null
}
},
render: function () {
return (
<BasicPanel name='Stack' data={this.state.data} />
)
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return
var self = this
this.context.traceManager.getStackAt(nextProps.currentStepIndex, function (error, stack) {
if (error) {
console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({
data: stack
})
}
})
}
})
'use strict'
var React = require('react')
var ButtonNavigator = require('./buttonNavigator')
var Slider = require('./slider')
var style = require('./basicStyles')
module.exports = React.createClass({
propTypes: {
onStepChanged: React.PropTypes.func.isRequired
},
contextTypes: {
traceManager: React.PropTypes.object
},
getInitialState: function () {
return {
currentStepIndex: 0,
traceLength: 0
}
},
render: function () {
return (
<div style={style.container}>
<Slider
ref='slider'
onChange={this.sliderMoved}
min='0'
max={this.state.traceLength} />
<ButtonNavigator
stepIntoBack={this.stepIntoBack}
stepIntoForward={this.stepIntoForward}
stepOverBack={this.stepOverBack}
stepOverForward={this.stepOverForward}
jumpToNextCall={this.jumpToNextCall}
max={this.state.traceLength} />
</div>
)
},
componentDidMount: function () {
this.updateGlobalSelectedItem(0)
},
updateGlobalSelectedItem: function (value) {
window.ethDebuggerSelectedItem = value
},
init: function () {
this.refs.slider.setValue(0)
},
newTraceAvailable: function () {
this.init()
var self = this
this.context.traceManager.getLength(function (error, length) {
if (error) {
console.log(error)
} else {
self.setState({ traceLength: length })
}
})
},
sliderMoved: function (step) {
this.props.onStepChanged(step)
this.changeState(step)
},
stepIntoForward: function () {
var step = this.state.currentStepIndex + 1
this.props.onStepChanged(step)
this.changeState(step)
},
stepIntoBack: function () {
var step = this.state.currentStepIndex - 1
this.props.onStepChanged(step)
this.refs.slider.setValue(step)
this.changeState(step)
},
stepOverForward: function () {
var step = this.context.traceManager.findStepOverForward(this.state.currentStepIndex)
this.props.onStepChanged(step)
this.refs.slider.setValue(step)
this.changeState(step)
},
stepOverBack: function () {
var step = this.context.traceManager.findStepOverBack(this.state.currentStepIndex)
this.props.onStepChanged(step)
this.refs.slider.setValue(step)
this.changeState(step)
},
jumpToNextCall: function () {
var step = this.context.traceManager.findNextCall(this.state.currentStepIndex)
this.props.onStepChanged(step)
this.refs.slider.setValue(step)
this.changeState(step)
},
changeState: function (step) {
this.updateGlobalSelectedItem(step)
this.setState({
currentStepIndex: step
})
}
})
......@@ -2,9 +2,22 @@
var React = require('react')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object
},
getDefaultProps: function () {
return {
data: null
currentStepIndex: -1
}
},
getInitialState: function () {
return {
step: '',
addmemory: '',
gas: '',
remainingGas: ''
}
},
......@@ -13,7 +26,38 @@ module.exports = React.createClass({
<div>
<table>
<tbody>
{this.renderItems()}
<tr key='step'>
<td>
step
</td>
<td>
{this.state.step}
</td>
</tr>
<tr key='addmemory'>
<td>
add memory
</td>
<td>
{this.state.addmemory}
</td>
</tr>
<tr key='gas'>
<td>
gas
</td>
<td>
{this.state.gas}
</td>
</tr>
<tr key='remaininggas'>
<td>
remaining gas
</td>
<td>
{this.state.remainingGas}
</td>
</tr>
</tbody>
</table>
</div>
......@@ -21,9 +65,9 @@ module.exports = React.createClass({
},
renderItems: function () {
if (this.props.data) {
if (this.state.data) {
var ret = []
for (var key in this.props.data) {
for (var key in this.state.data) {
ret.push(
<tr key={key}>
<td>
......@@ -34,5 +78,50 @@ module.exports = React.createClass({
return ret
}
return null
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
var self = this
this.context.traceManager.getCurrentStep(nextProps.currentStepIndex, function (error, step) {
if (error) {
console.log(error)
} else {
self.setState({
step: step
})
}
})
this.context.traceManager.getMemExpand(nextProps.currentStepIndex, function (error, addmem) {
if (error) {
console.log(error)
} else {
self.setState({
addmemory: addmem
})
}
})
this.context.traceManager.getStepCost(nextProps.currentStepIndex, function (error, gas) {
if (error) {
console.log(error)
} else {
self.setState({
gas: gas
})
}
})
this.context.traceManager.getRemainingGas(nextProps.currentStepIndex, function (error, remaingas) {
if (error) {
console.log(error)
} else {
self.setState({
remainingGas: remaingas
})
}
})
}
})
'use strict'
var React = require('react')
var BasicPanel = require('./basicPanel')
var style = require('./basicStyles')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object,
tx: React.PropTypes.object
},
getDefaultProps: function () {
return {
currentStepIndex: -1
}
},
getInitialState: function () {
return {
data: null
}
},
render: function () {
return (
<BasicPanel name='Storage' data={this.state.data} renderRow={this.renderStorageRow} />
)
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return
var self = this
this.context.traceManager.getStorageAt(nextProps.currentStepIndex, this.context.tx.blockNumber.toString(), this.context.tx.transactionIndex, function (error, storage) {
if (error) {
console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({
data: storage
})
}
})
},
renderStorageRow: function (data) {
var ret = []
if (data) {
for (var key in data) {
ret.push(
<tr key={key}>
<td>
<pre style={style.font} >{key}</pre>
</td>
<td>
<pre style={style.font}>{data[key]}</pre>
</td>
</tr>)
}
}
return ret
}
})
'use strict'
function TraceAnalyser (_cache) {
this.traceCache = _cache
this.trace = null
}
TraceAnalyser.prototype.analyse = function (trace, callback) {
this.trace = trace
var currentDepth = 0
var context = {
currentStorageAddress: trace[0].address,
previousStorageAddress: trace[0].address
}
var callStack = []
for (var k in this.trace) {
var step = this.trace[k]
this.buildCalldata(k, step)
this.buildMemory(k, step)
var depth = this.buildDepth(k, step, currentDepth, callStack)
if (depth) {
currentDepth = depth
}
context = this.buildStorage(k, step, context)
}
callback(null, true)
}
TraceAnalyser.prototype.buildCalldata = function (index, step) {
if (step.calldata) {
this.traceCache.pushCallDataChanges(index)
}
}
TraceAnalyser.prototype.buildMemory = function (index, step) {
if (step.memory) {
this.traceCache.pushMemoryChanges(index)
}
}
TraceAnalyser.prototype.buildStorage = function (index, step, context) {
if (step.address) {
// new context
context.currentStorageAddress = step.address
this.traceCache.pushStoreChanges(index, context.currentStorageAddress)
} else if (step.inst === 'SSTORE') {
this.traceCache.pushStoreChanges(index, context.currentStorageAddress, step.stack[step.stack.length - 1], step.stack[step.stack.length - 2])
} else if (!step.address && step.depth) {
// returned from context
context.currentStorageAddress = context.previousStorageAddress
this.traceCache.pushStoreChanges(index, context.currentStorageAddress)
}
return context
}
TraceAnalyser.prototype.buildDepth = function (index, step, currentDepth, callStack) {
if (step.depth === undefined) return
if (step.depth > currentDepth) {
if (index === 0) {
callStack.push('0x' + step.address) // new context
} else {
// getting the address from the stack
var callTrace = this.trace[index - 1]
var address = callTrace.stack[callTrace.stack.length - 2]
callStack.push(address) // new context
}
} else if (step.depth < currentDepth) {
callStack.pop() // returning from context
}
this.traceCache.pushCallStack(index, {
stack: callStack.slice(0),
depth: step.depth
})
this.traceCache.pushDepthChanges(index)
return step.depth
}
module.exports = TraceAnalyser
'use strict'
function TraceCache () {
this.init()
}
TraceCache.prototype.init = function () {
// ...Changes contains index in the vmtrace of the corresponding changes
this.depthChanges = []
this.memoryChanges = []
this.callDataChanges = []
this.storageChanges = []
this.sstore = {} // all sstore occurence in the trace
this.callStack = {} // contains all callStack by vmtrace index (we need to rebuild it, callstack is not included in the vmtrace)
}
TraceCache.prototype.pushCallDataChanges = function (value) {
this.callDataChanges.push(value)
}
TraceCache.prototype.pushMemoryChanges = function (value) {
this.memoryChanges.push(value)
}
TraceCache.prototype.pushDepthChanges = function (value) {
this.depthChanges.push(value)
}
TraceCache.prototype.pushCallStack = function (index, callStack) {
this.callStack[index] = callStack
}
TraceCache.prototype.pushStoreChanges = function (index, address, key, value) {
this.sstore[index] = {
'address': address,
'key': key,
'value': value
}
this.storageChanges.push(index)
}
TraceCache.prototype.rebuildStorage = function (address, storage, index) {
for (var k in this.storageChanges) {
var changesIndex = this.storageChanges[k]
if (changesIndex > index) {
return storage
}
var sstore = this.sstore[changesIndex]
if (sstore.address === address && sstore.key) {
storage[sstore.key] = sstore.value
}
}
return storage
}
module.exports = TraceCache
'use strict'
var TraceAnalyser = require('./traceAnalyser')
var TraceRetriever = require('./traceRetriever')
var TraceCache = require('./traceCache')
var TraceStepManager = require('./traceStepManager')
var traceManagerUtil = require('./traceManagerUtil')
function TraceManager (_web3) {
this.web3 = _web3
this.isLoading = false
this.trace = null
this.traceCache = new TraceCache()
this.traceAnalyser = new TraceAnalyser(this.traceCache)
this.traceRetriever = new TraceRetriever(_web3)
this.traceStepManager = new TraceStepManager(this.traceAnalyser)
}
// init section
TraceManager.prototype.resolveTrace = function (blockNumber, txNumber, callback) {
this.isLoading = true
this.init()
if (!this.web3) callback(false)
var self = this
this.traceRetriever.getTrace(blockNumber, parseInt(txNumber), function (error, result) {
self.trace = result
if (error) {
console.log(error)
} else {
self.traceAnalyser.analyse(result, function (error, result) {
if (error) {
console.log(error)
callback(false)
} else {
callback(true)
}
})
}
})
}
TraceManager.prototype.init = function () {
this.trace = null
this.traceCache.init()
}
// API section
TraceManager.prototype.getLength = function (callback) {
if (!this.trace) callback('no trace available', null)
callback(null, this.trace.length)
}
TraceManager.prototype.getStorageAt = function (stepIndex, blockNumber, txIndex, callback) {
var stoChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.storageChanges)
var address = this.traceCache.sstore[stoChange].address
var self = this
this.traceRetriever.getStorage(blockNumber, txIndex, address, function (error, result) {
if (error) {
console.log(error)
callback(error, null)
} else {
var storage = self.traceCache.rebuildStorage(address, result, stepIndex)
callback(null, storage)
}
})
}
TraceManager.prototype.getCallDataAt = function (stepIndex, callback) {
var callDataChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.callDataChanges)
if (!callDataChange) return callback('no calldata found', null)
callback(null, [this.trace[callDataChange].calldata])
}
TraceManager.prototype.getCallStackAt = function (stepIndex, callback) {
var callStackChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.depthChanges)
if (!callStackChange) return callback('no callstack found', null)
callback(null, this.traceCache.callStack[callStackChange].stack)
}
TraceManager.prototype.getStackAt = function (stepIndex, callback) {
var stack
if (this.trace[stepIndex].stack) { // there's always a stack
stack = this.trace[stepIndex].stack.slice(0)
stack.reverse()
callback(null, stack)
} else {
callback('no stack found', null)
}
}
TraceManager.prototype.getLastDepthIndexChangeSince = function (stepIndex, callback) {
var depthIndex = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.depthChanges)
callback(null, depthIndex)
}
TraceManager.prototype.getCurrentCalledAddressAt = function (stepIndex, callback) {
var self = this
this.getLastDepthIndexChangeSince(stepIndex, function (error, addressIndex) {
if (error) {
callback(error, null)
} else {
callback(null, traceManagerUtil.resolveCalledAddress(addressIndex, self.trace))
}
})
}
TraceManager.prototype.getMemoryAt = function (stepIndex, callback) {
var lastChanges = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.memoryChanges)
if (!lastChanges) return callback('no memory found', null)
callback(null, this.trace[lastChanges].memory)
}
TraceManager.prototype.getCurrentPC = function (stepIndex, callback) {
callback(null, this.trace[stepIndex].pc)
}
TraceManager.prototype.getCurrentStep = function (stepIndex, callback) {
callback(null, this.trace[stepIndex].steps)
}
TraceManager.prototype.getMemExpand = function (stepIndex, callback) {
callback(null, this.trace[stepIndex].memexpand ? this.trace[stepIndex].memexpand : '')
}
TraceManager.prototype.getStepCost = function (stepIndex, callback) {
callback(null, this.trace[stepIndex].gascost)
}
TraceManager.prototype.getRemainingGas = function (stepIndex, callback) {
callback(null, this.trace[stepIndex].gas)
}
// step section
TraceManager.prototype.findStepOverBack = function (currentStep) {
return this.traceStepManager.findStepOverBack(currentStep)
}
TraceManager.prototype.findStepOverForward = function (currentStep) {
return this.traceStepManager.findStepOverForward(currentStep)
}
TraceManager.prototype.findStepOutBack = function (currentStep) {
return this.traceStepManager.findStepOutBack(currentStep)
}
TraceManager.prototype.findStepOutForward = function (currentStep) {
return this.traceStepManager.findStepOutForward(currentStep)
}
module.exports = TraceManager
module.exports = {
// util section
findLowerBound: function (target, changes) {
if (changes.length === 0) {
return undefined
}
if (changes.length === 1) {
if (changes[0] > target) {
// we only a closest maximum, returning O
return 0
} else {
return changes[0]
}
}
var middle = Math.floor(changes.length / 2)
if (changes[middle] > target) {
return this.findLowerBound(target, changes.slice(0, middle))
} else if (changes[middle] < target) {
return this.findLowerBound(target, changes.slice(middle, changes.length))
} else {
return changes[middle]
}
},
resolveCalledAddress: function (vmTraceIndex, trace) {
var address = trace[vmTraceIndex].address
if (vmTraceIndex > 0) {
var stack = trace[vmTraceIndex - 1].stack // callcode, delegatecall, ...
address = stack[stack.length - 2]
}
return address
}
}
'use strict'
function TraceRetriever (_web3) {
this.web3 = _web3
this.storages = {} // contains all intial storage (by addresses)
}
TraceRetriever.prototype.getTrace = function (blockNumber, txNumber, callback) {
this.web3.debug.trace(blockNumber, parseInt(txNumber), function (error, result) {
callback(error, result)
})
}
TraceRetriever.prototype.getStorage = function (blockNumber, txIndex, address, callback) {
if (this.storages[address]) {
callback(null, this.storages[address])
} else {
var self = this
this.web3.debug.storageAt(blockNumber, txIndex, address, function (error, result) {
self.storages[address] = result
callback(error, result)
})
}
}
module.exports = TraceRetriever
'use strict'
function TraceStepManager (_traceAnalyser) {
this.traceAnalyser = _traceAnalyser
}
TraceStepManager.prototype.isCallInstruction = function (index) {
var state = this.traceAnalyser.trace[index]
return state.instname === 'CALL' || state.instname === 'CALLCODE' || state.instname === 'CREATE' || state.instname === 'DELEGATECALL'
}
TraceStepManager.prototype.isReturnInstruction = function (index) {
var state = this.traceAnalyser.trace[index]
return state.instname === 'RETURN'
}
TraceStepManager.prototype.findStepOverBack = function (currentStep) {
if (this.isReturnInstruction(currentStep - 1)) {
return this.findStepOutBack(currentStep)
} else {
return currentStep - 1
}
}
TraceStepManager.prototype.findStepOverForward = function (currentStep) {
if (this.isCallInstruction(currentStep)) {
return this.findStepOutForward(currentStep)
} else {
return currentStep + 1
}
}
TraceStepManager.prototype.findStepOutBack = function (currentStep) {
var i = currentStep - 1
var depth = 0
while (--i >= 0) {
if (this.isCallInstruction(i)) {
if (depth === 0) {
break
} else {
depth--
}
} else if (this.isReturnInstruction(i)) {
depth++
}
}
return i
}
TraceStepManager.prototype.findStepOutForward = function (currentStep) {
var i = currentStep
var depth = 0
while (++i < this.traceAnalyser.length) {
if (this.isReturnInstruction(i)) {
if (depth === 0) {
break
} else {
depth--
}
} else if (this.isCallInstruction(i)) {
depth++
}
}
return i + 1
}
module.exports = TraceStepManager
......@@ -19,8 +19,10 @@ module.exports = React.createClass({
var tx = this.context.web3.eth.getTransactionFromBlock(this.state.blockNumber, this.state.txNumber)
if (tx) {
this.setState({from: tx.from, to: tx.to, hash: tx.hash})
this.props.onNewTxRequested(this.state.blockNumber, parseInt(this.state.txNumber), tx)
} else {
console.log('cannot find ' + this.state.blockNumber + ' ' + this.state.txNumber)
}
this.props.onNewTxRequested(this.state.blockNumber, parseInt(this.state.txNumber))
},
updateBlockN: function (ev) {
......@@ -40,18 +42,22 @@ module.exports = React.createClass({
Get
</button>
<div style={style.transactionInfo}>
<div>
Hash:
{this.state.hash}
</div>
<div>
From:
{this.state.from}
</div>
<div>
To:
{this.state.to}
</div>
<table>
<tbody>
<tr>
<td>Hash: </td>
<td>{this.state.hash}</td>
</tr>
<tr>
<td>From: </td>
<td>{this.state.from}</td>
</tr>
<tr>
<td>To: </td>
<td>{this.state.to}</td>
</tr>
</tbody>
</table>
</div>
</div>
)
......
'use strict'
var React = require('react')
var Sticker = require('./sticker')
var style = require('./basicStyles')
var ASMCode = require('./asmCode')
var CalldataPanel = require('./calldataPanel')
var MemoryPanel = require('./memoryPanel')
var CallstackPanel = require('./callstackPanel')
var StackPanel = require('./stackPanel')
var StoragePanel = require('./storagePanel')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object
},
getInitialState: function () {
return {
currentAddress: null
}
},
getDefaultProps: function () {
return {
currentStepIndex: -1 // index of the selected item in the vmtrace
}
},
render: function () {
return (
<div style={this.props.vmTrace === null ? style.hidden : style.display}>
<div style={style.container}>
<span style={style.address}>Current code: {this.state.currentAddress}</span>
</div>
<div style={style.container}>
<table>
<tbody>
<tr>
<td>
<ASMCode currentStepIndex={this.props.currentStepIndex} />
<div style={Object.assign(style.inline, style.sticker)}>
<Sticker currentStepIndex={this.props.currentStepIndex} />
</div>
</td>
<td>
<CalldataPanel currentStepIndex={this.props.currentStepIndex} />
</td>
</tr>
<tr>
<td>
<StackPanel currentStepIndex={this.props.currentStepIndex} />
</td>
<td>
<CallstackPanel currentStepIndex={this.props.currentStepIndex} />
</td>
</tr>
<tr>
<td>
<StoragePanel currentStepIndex={this.props.currentStepIndex} />
</td>
<td>
<MemoryPanel currentStepIndex={this.props.currentStepIndex} />
</td>
</tr>
</tbody>
</table>
</div>
</div>
)
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return
var self = this
this.context.traceManager.getCurrentCalledAddressAt(nextProps.currentStepIndex, function (error, address) {
if (error) {
console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({
currentAddress: address
})
}
})
}
})
'use strict'
var React = require('react')
var AssemblyItemsBrowser = require('./assemblyItemsBrowser')
module.exports = React.createClass({
render: function () {
return (
<div>
<AssemblyItemsBrowser vmTrace={this.props.vmTrace} />
</div>
)
}
})
'use strict'
module.exports = {
retrieveVmTrace: function (blockNumber, txNumber, callBack) {
this.context.web3.debug.trace(blockNumber, parseInt(txNumber), function (error, result) {
callBack(error, result)
})
}
}
......@@ -90,6 +90,12 @@ module.exports = {
property: 'debug',
methods: [
new web3._extend.Method({
name: 'storageAt',
call: 'debug_storageAt',
inputFormatter: [null, null, null],
params: 3
}),
new web3._extend.Method({
name: 'trace',
call: 'debug_trace',
inputFormatter: [null, null],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment