Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
fd66af5e
Unverified
Commit
fd66af5e
authored
Dec 10, 2018
by
Péter Szilágyi
Committed by
GitHub
Dec 10, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #17914 from holiman/block_analysis
core/vm, eth: add standard json tracing into filesystem dumps
parents
09d588e0
0983d02a
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
175 additions
and
27 deletions
+175
-27
runner.go
cmd/evm/runner.go
+1
-1
staterunner.go
cmd/evm/staterunner.go
+1
-1
logger_json.go
core/vm/logger_json.go
+6
-7
api_tracer.go
eth/api_tracer.go
+155
-18
web3ext.go
internal/web3ext/web3ext.go
+12
-0
No files found.
cmd/evm/runner.go
View file @
fd66af5e
...
...
@@ -89,7 +89,7 @@ func runCmd(ctx *cli.Context) error {
genesisConfig
*
core
.
Genesis
)
if
ctx
.
GlobalBool
(
MachineFlag
.
Name
)
{
tracer
=
NewJSONLogger
(
logconfig
,
os
.
Stdout
)
tracer
=
vm
.
NewJSONLogger
(
logconfig
,
os
.
Stdout
)
}
else
if
ctx
.
GlobalBool
(
DebugFlag
.
Name
)
{
debugLogger
=
vm
.
NewStructLogger
(
logconfig
)
tracer
=
debugLogger
...
...
cmd/evm/staterunner.go
View file @
fd66af5e
...
...
@@ -68,7 +68,7 @@ func stateTestCmd(ctx *cli.Context) error {
)
switch
{
case
ctx
.
GlobalBool
(
MachineFlag
.
Name
)
:
tracer
=
NewJSONLogger
(
config
,
os
.
Stderr
)
tracer
=
vm
.
NewJSONLogger
(
config
,
os
.
Stderr
)
case
ctx
.
GlobalBool
(
DebugFlag
.
Name
)
:
debugger
=
vm
.
NewStructLogger
(
config
)
...
...
c
md/evm/json_logger
.go
→
c
ore/vm/logger_json
.go
View file @
fd66af5e
...
...
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package
main
package
vm
import
(
"encoding/json"
...
...
@@ -24,17 +24,16 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/vm"
)
type
JSONLogger
struct
{
encoder
*
json
.
Encoder
cfg
*
vm
.
LogConfig
cfg
*
LogConfig
}
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
// into the provided stream.
func
NewJSONLogger
(
cfg
*
vm
.
LogConfig
,
writer
io
.
Writer
)
*
JSONLogger
{
func
NewJSONLogger
(
cfg
*
LogConfig
,
writer
io
.
Writer
)
*
JSONLogger
{
return
&
JSONLogger
{
json
.
NewEncoder
(
writer
),
cfg
}
}
...
...
@@ -43,8 +42,8 @@ func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create
}
// CaptureState outputs state information on the logger.
func
(
l
*
JSONLogger
)
CaptureState
(
env
*
vm
.
EVM
,
pc
uint64
,
op
vm
.
OpCode
,
gas
,
cost
uint64
,
memory
*
vm
.
Memory
,
stack
*
vm
.
Stack
,
contract
*
vm
.
Contract
,
depth
int
,
err
error
)
error
{
log
:=
vm
.
StructLog
{
func
(
l
*
JSONLogger
)
CaptureState
(
env
*
EVM
,
pc
uint64
,
op
OpCode
,
gas
,
cost
uint64
,
memory
*
Memory
,
stack
*
Stack
,
contract
*
Contract
,
depth
int
,
err
error
)
error
{
log
:=
StructLog
{
Pc
:
pc
,
Op
:
op
,
Gas
:
gas
,
...
...
@@ -65,7 +64,7 @@ func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos
}
// CaptureFault outputs state information on the logger.
func
(
l
*
JSONLogger
)
CaptureFault
(
env
*
vm
.
EVM
,
pc
uint64
,
op
vm
.
OpCode
,
gas
,
cost
uint64
,
memory
*
vm
.
Memory
,
stack
*
vm
.
Stack
,
contract
*
vm
.
Contract
,
depth
int
,
err
error
)
error
{
func
(
l
*
JSONLogger
)
CaptureFault
(
env
*
EVM
,
pc
uint64
,
op
OpCode
,
gas
,
cost
uint64
,
memory
*
Memory
,
stack
*
Stack
,
contract
*
Contract
,
depth
int
,
err
error
)
error
{
return
nil
}
...
...
eth/api_tracer.go
View file @
fd66af5e
...
...
@@ -17,11 +17,13 @@
package
eth
import
(
"bufio"
"bytes"
"context"
"errors"
"fmt"
"io/ioutil"
"os"
"runtime"
"sync"
"time"
...
...
@@ -60,6 +62,13 @@ type TraceConfig struct {
Reexec
*
uint64
}
// StdTraceConfig holds extra parameters to standard-json trace functions.
type
StdTraceConfig
struct
{
*
vm
.
LogConfig
Reexec
*
uint64
TxHash
common
.
Hash
}
// txTraceResult is the result of a single transaction trace.
type
txTraceResult
struct
{
Result
interface
{}
`json:"result,omitempty"`
// Trace results produced by the tracer
...
...
@@ -366,7 +375,7 @@ func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.B
func
(
api
*
PrivateDebugAPI
)
TraceBlockByHash
(
ctx
context
.
Context
,
hash
common
.
Hash
,
config
*
TraceConfig
)
([]
*
txTraceResult
,
error
)
{
block
:=
api
.
eth
.
blockchain
.
GetBlockByHash
(
hash
)
if
block
==
nil
{
return
nil
,
fmt
.
Errorf
(
"block
#%
x not found"
,
hash
)
return
nil
,
fmt
.
Errorf
(
"block
%#
x not found"
,
hash
)
}
return
api
.
traceBlock
(
ctx
,
block
,
config
)
}
...
...
@@ -391,13 +400,41 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string,
return
api
.
TraceBlock
(
ctx
,
blob
,
config
)
}
// TraceBadBlock returns the structured logs created during the execution of a block
// within the blockchain 'badblocks' cache
func
(
api
*
PrivateDebugAPI
)
TraceBadBlock
(
ctx
context
.
Context
,
index
int
,
config
*
TraceConfig
)
([]
*
txTraceResult
,
error
)
{
if
blocks
:=
api
.
eth
.
blockchain
.
BadBlocks
();
index
<
len
(
blocks
)
{
return
api
.
traceBlock
(
ctx
,
blocks
[
index
],
config
)
// TraceBadBlockByHash returns the structured logs created during the execution of
// EVM against a block pulled from the pool of bad ones and returns them as a JSON
// object.
func
(
api
*
PrivateDebugAPI
)
TraceBadBlock
(
ctx
context
.
Context
,
hash
common
.
Hash
,
config
*
TraceConfig
)
([]
*
txTraceResult
,
error
)
{
blocks
:=
api
.
eth
.
blockchain
.
BadBlocks
()
for
_
,
block
:=
range
blocks
{
if
block
.
Hash
()
==
hash
{
return
api
.
traceBlock
(
ctx
,
block
,
config
)
}
}
return
nil
,
fmt
.
Errorf
(
"bad block %#x not found"
,
hash
)
}
// StandardTraceBlockToFile dumps the structured logs created during the
// execution of EVM to the local file system and returns a list of files
// to the caller.
func
(
api
*
PrivateDebugAPI
)
StandardTraceBlockToFile
(
ctx
context
.
Context
,
hash
common
.
Hash
,
config
*
StdTraceConfig
)
([]
string
,
error
)
{
block
:=
api
.
eth
.
blockchain
.
GetBlockByHash
(
hash
)
if
block
==
nil
{
return
nil
,
fmt
.
Errorf
(
"block %#x not found"
,
hash
)
}
return
nil
,
fmt
.
Errorf
(
"index out of range"
)
return
api
.
standardTraceBlockToFile
(
ctx
,
block
,
config
)
}
// StandardTraceBadBlockToFile dumps the structured logs created during the
// execution of EVM against a block pulled from the pool of bad ones to the
// local file system and returns a list of files to the caller.
func
(
api
*
PrivateDebugAPI
)
StandardTraceBadBlockToFile
(
ctx
context
.
Context
,
hash
common
.
Hash
,
config
*
StdTraceConfig
)
([]
string
,
error
)
{
blocks
:=
api
.
eth
.
blockchain
.
BadBlocks
()
for
_
,
block
:=
range
blocks
{
if
block
.
Hash
()
==
hash
{
return
api
.
standardTraceBlockToFile
(
ctx
,
block
,
config
)
}
}
return
nil
,
fmt
.
Errorf
(
"bad block %#x not found"
,
hash
)
}
// traceBlock configures a new tracer according to the provided configuration, and
...
...
@@ -410,7 +447,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
}
parent
:=
api
.
eth
.
blockchain
.
GetBlock
(
block
.
ParentHash
(),
block
.
NumberU64
()
-
1
)
if
parent
==
nil
{
return
nil
,
fmt
.
Errorf
(
"parent %x not found"
,
block
.
ParentHash
())
return
nil
,
fmt
.
Errorf
(
"parent %
#
x not found"
,
block
.
ParentHash
())
}
reexec
:=
defaultTraceReexec
if
config
!=
nil
&&
config
.
Reexec
!=
nil
{
...
...
@@ -481,6 +518,106 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
return
results
,
nil
}
// standardTraceBlockToFile configures a new tracer which uses standard JSON output,
// and traces either a full block or an individual transaction. The return value will
// be one filename per transaction traced.
func
(
api
*
PrivateDebugAPI
)
standardTraceBlockToFile
(
ctx
context
.
Context
,
block
*
types
.
Block
,
config
*
StdTraceConfig
)
([]
string
,
error
)
{
// If we're tracing a single transaction, make sure it's present
if
config
!=
nil
&&
config
.
TxHash
!=
(
common
.
Hash
{})
{
var
exists
bool
for
_
,
tx
:=
range
block
.
Transactions
()
{
if
exists
=
(
tx
.
Hash
()
==
config
.
TxHash
);
exists
{
break
}
}
if
!
exists
{
return
nil
,
fmt
.
Errorf
(
"transaction %#x not found in block"
,
config
.
TxHash
)
}
}
// Create the parent state database
if
err
:=
api
.
eth
.
engine
.
VerifyHeader
(
api
.
eth
.
blockchain
,
block
.
Header
(),
true
);
err
!=
nil
{
return
nil
,
err
}
parent
:=
api
.
eth
.
blockchain
.
GetBlock
(
block
.
ParentHash
(),
block
.
NumberU64
()
-
1
)
if
parent
==
nil
{
return
nil
,
fmt
.
Errorf
(
"parent %#x not found"
,
block
.
ParentHash
())
}
reexec
:=
defaultTraceReexec
if
config
!=
nil
&&
config
.
Reexec
!=
nil
{
reexec
=
*
config
.
Reexec
}
statedb
,
err
:=
api
.
computeStateDB
(
parent
,
reexec
)
if
err
!=
nil
{
return
nil
,
err
}
// Retrieve the tracing configurations, or use default values
var
(
logConfig
vm
.
LogConfig
txHash
common
.
Hash
)
if
config
!=
nil
{
if
config
.
LogConfig
!=
nil
{
logConfig
=
*
config
.
LogConfig
}
txHash
=
config
.
TxHash
}
logConfig
.
Debug
=
true
// Execute transaction, either tracing all or just the requested one
var
(
signer
=
types
.
MakeSigner
(
api
.
config
,
block
.
Number
())
dumps
[]
string
)
for
i
,
tx
:=
range
block
.
Transactions
()
{
// Prepare the trasaction for un-traced execution
var
(
msg
,
_
=
tx
.
AsMessage
(
signer
)
vmctx
=
core
.
NewEVMContext
(
msg
,
block
.
Header
(),
api
.
eth
.
blockchain
,
nil
)
vmConf
vm
.
Config
dump
*
os
.
File
err
error
)
// If the transaction needs tracing, swap out the configs
if
tx
.
Hash
()
==
txHash
||
txHash
==
(
common
.
Hash
{})
{
// Generate a unique temporary file to dump it into
prefix
:=
fmt
.
Sprintf
(
"block_%#x-%d-%#x-"
,
block
.
Hash
()
.
Bytes
()[
:
4
],
i
,
tx
.
Hash
()
.
Bytes
()[
:
4
])
dump
,
err
=
ioutil
.
TempFile
(
os
.
TempDir
(),
prefix
)
if
err
!=
nil
{
return
nil
,
err
}
dumps
=
append
(
dumps
,
dump
.
Name
())
// Swap out the noop logger to the standard tracer
vmConf
=
vm
.
Config
{
Debug
:
true
,
Tracer
:
vm
.
NewJSONLogger
(
&
logConfig
,
bufio
.
NewWriter
(
dump
)),
EnablePreimageRecording
:
true
,
}
}
// Execute the transaction and flush any traces to disk
vmenv
:=
vm
.
NewEVM
(
vmctx
,
statedb
,
api
.
config
,
vmConf
)
_
,
_
,
_
,
err
=
core
.
ApplyMessage
(
vmenv
,
msg
,
new
(
core
.
GasPool
)
.
AddGas
(
msg
.
Gas
()))
if
dump
!=
nil
{
dump
.
Close
()
log
.
Info
(
"Wrote standard trace"
,
"file"
,
dump
.
Name
())
}
if
err
!=
nil
{
return
dumps
,
err
}
// Finalize the state so any modifications are written to the trie
statedb
.
Finalise
(
true
)
// If we've traced the transaction we were looking for, abort
if
tx
.
Hash
()
==
txHash
{
break
}
}
return
dumps
,
nil
}
// computeStateDB retrieves the state database associated with a certain block.
// If no state is locally available for the given block, a number of blocks are
// attempted to be reexecuted to generate the desired state.
...
...
@@ -506,7 +643,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
if
err
!=
nil
{
switch
err
.
(
type
)
{
case
*
trie
.
MissingNodeError
:
return
nil
,
errors
.
New
(
"required historical state unavailable"
)
return
nil
,
fmt
.
Errorf
(
"required historical state unavailable (reexec=%d)"
,
reexec
)
default
:
return
nil
,
err
}
...
...
@@ -520,7 +657,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
for
block
.
NumberU64
()
<
origin
{
// Print progress logs if long enough time elapsed
if
time
.
Since
(
logged
)
>
8
*
time
.
Second
{
log
.
Info
(
"Regenerating historical state"
,
"block"
,
block
.
NumberU64
()
+
1
,
"target"
,
origin
,
"elapsed"
,
time
.
Since
(
start
))
log
.
Info
(
"Regenerating historical state"
,
"block"
,
block
.
NumberU64
()
+
1
,
"target"
,
origin
,
"
remaining"
,
origin
-
block
.
NumberU64
()
-
1
,
"
elapsed"
,
time
.
Since
(
start
))
logged
=
time
.
Now
()
}
// Retrieve the next block to regenerate and process it
...
...
@@ -529,15 +666,15 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
}
_
,
_
,
_
,
err
:=
api
.
eth
.
blockchain
.
Processor
()
.
Process
(
block
,
statedb
,
vm
.
Config
{})
if
err
!=
nil
{
return
nil
,
err
return
nil
,
fmt
.
Errorf
(
"processing block %d failed: %v"
,
block
.
NumberU64
(),
err
)
}
// Finalize the state so any modifications are written to the trie
root
,
err
:=
statedb
.
Commit
(
true
)
root
,
err
:=
statedb
.
Commit
(
api
.
eth
.
blockchain
.
Config
()
.
IsEIP158
(
block
.
Number
())
)
if
err
!=
nil
{
return
nil
,
err
}
if
err
:=
statedb
.
Reset
(
root
);
err
!=
nil
{
return
nil
,
err
return
nil
,
fmt
.
Errorf
(
"state reset after block %d failed: %v"
,
block
.
NumberU64
(),
err
)
}
database
.
TrieDB
()
.
Reference
(
root
,
common
.
Hash
{})
if
proot
!=
(
common
.
Hash
{})
{
...
...
@@ -556,7 +693,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Ha
// Retrieve the transaction and assemble its EVM context
tx
,
blockHash
,
_
,
index
:=
rawdb
.
ReadTransaction
(
api
.
eth
.
ChainDb
(),
hash
)
if
tx
==
nil
{
return
nil
,
fmt
.
Errorf
(
"transaction %x not found"
,
hash
)
return
nil
,
fmt
.
Errorf
(
"transaction %
#
x not found"
,
hash
)
}
reexec
:=
defaultTraceReexec
if
config
!=
nil
&&
config
.
Reexec
!=
nil
{
...
...
@@ -636,11 +773,11 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
// Create the parent state database
block
:=
api
.
eth
.
blockchain
.
GetBlockByHash
(
blockHash
)
if
block
==
nil
{
return
nil
,
vm
.
Context
{},
nil
,
fmt
.
Errorf
(
"block %x not found"
,
blockHash
)
return
nil
,
vm
.
Context
{},
nil
,
fmt
.
Errorf
(
"block %
#
x not found"
,
blockHash
)
}
parent
:=
api
.
eth
.
blockchain
.
GetBlock
(
block
.
ParentHash
(),
block
.
NumberU64
()
-
1
)
if
parent
==
nil
{
return
nil
,
vm
.
Context
{},
nil
,
fmt
.
Errorf
(
"parent %x not found"
,
block
.
ParentHash
())
return
nil
,
vm
.
Context
{},
nil
,
fmt
.
Errorf
(
"parent %
#
x not found"
,
block
.
ParentHash
())
}
statedb
,
err
:=
api
.
computeStateDB
(
parent
,
reexec
)
if
err
!=
nil
{
...
...
@@ -659,10 +796,10 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
// Not yet the searched for transaction, execute on top of the current state
vmenv
:=
vm
.
NewEVM
(
context
,
statedb
,
api
.
config
,
vm
.
Config
{})
if
_
,
_
,
_
,
err
:=
core
.
ApplyMessage
(
vmenv
,
msg
,
new
(
core
.
GasPool
)
.
AddGas
(
tx
.
Gas
()));
err
!=
nil
{
return
nil
,
vm
.
Context
{},
nil
,
fmt
.
Errorf
(
"t
x %
x failed: %v"
,
tx
.
Hash
(),
err
)
return
nil
,
vm
.
Context
{},
nil
,
fmt
.
Errorf
(
"t
ransaction %#
x failed: %v"
,
tx
.
Hash
(),
err
)
}
// Ensure any modifications are committed to the state
statedb
.
Finalise
(
true
)
}
return
nil
,
vm
.
Context
{},
nil
,
fmt
.
Errorf
(
"t
x index %d out of range for block %
x"
,
txIndex
,
blockHash
)
return
nil
,
vm
.
Context
{},
nil
,
fmt
.
Errorf
(
"t
ransaction index %d out of range for block %#
x"
,
txIndex
,
blockHash
)
}
internal/web3ext/web3ext.go
View file @
fd66af5e
...
...
@@ -384,6 +384,18 @@ web3._extend({
params: 1,
inputFormatter: [null]
}),
new web3._extend.Method({
name: 'standardTraceBadBlockToFile',
call: 'debug_standardTraceBadBlockToFile',
params: 2,
inputFormatter: [null, null]
}),
new web3._extend.Method({
name: 'standardTraceBlockToFile',
call: 'debug_standardTraceBlockToFile',
params: 2,
inputFormatter: [null, null]
}),
new web3._extend.Method({
name: 'traceBlockByNumber',
call: 'debug_traceBlockByNumber',
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment