Merge pull request #2235 from fjl/chaindb-api-and-console-fixes

eth: add chaindbProperty to debug API (+ console fixes)
This commit is contained in:
Felix Lange 2016-04-13 13:05:29 +02:00
commit e50e3bea49
7 changed files with 78 additions and 14 deletions

View File

@ -26,6 +26,7 @@ import (
"math/big" "math/big"
"os" "os"
"runtime" "runtime"
"strings"
"sync" "sync"
"time" "time"
@ -46,6 +47,7 @@ import (
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/syndtr/goleveldb/leveldb"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
@ -1566,6 +1568,22 @@ func NewPrivateDebugAPI(config *core.ChainConfig, eth *Ethereum) *PrivateDebugAP
return &PrivateDebugAPI{config: config, eth: eth} return &PrivateDebugAPI{config: config, eth: eth}
} }
// ChaindbProperty returns leveldb properties of the chain database.
func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) {
ldb, ok := api.eth.chainDb.(interface {
LDB() *leveldb.DB
})
if !ok {
return "", fmt.Errorf("chaindbProperty does not work for memory databases")
}
if property == "" {
property = "leveldb.stats"
} else if !strings.HasPrefix(property, "leveldb.") {
property = "leveldb." + property
}
return ldb.LDB().GetProperty(property)
}
// BlockTraceResults is the returned value when replaying a block to check for // BlockTraceResults is the returned value when replaying a block to check for
// consensus results and full VM trace logs for all included transactions. // consensus results and full VM trace logs for all included transactions.
type BlockTraceResult struct { type BlockTraceResult struct {

View File

@ -27,6 +27,7 @@ import (
"os/user" "os/user"
"path/filepath" "path/filepath"
"runtime" "runtime"
"runtime/debug"
"runtime/pprof" "runtime/pprof"
"strings" "strings"
"sync" "sync"
@ -69,6 +70,20 @@ func (*HandlerT) BacktraceAt(location string) error {
return glog.GetTraceLocation().Set(location) return glog.GetTraceLocation().Set(location)
} }
// MemStats returns detailed runtime memory statistics.
func (*HandlerT) MemStats() *runtime.MemStats {
s := new(runtime.MemStats)
runtime.ReadMemStats(s)
return s
}
// GcStats returns GC statistics.
func (*HandlerT) GcStats() *debug.GCStats {
s := new(debug.GCStats)
debug.ReadGCStats(s)
return s
}
// CpuProfile turns on CPU profiling for nsec seconds and writes // CpuProfile turns on CPU profiling for nsec seconds and writes
// profile data to file. // profile data to file.
func (h *HandlerT) CpuProfile(file string, nsec uint) error { func (h *HandlerT) CpuProfile(file string, nsec uint) error {

View File

@ -27,7 +27,9 @@ import (
// evaluated, callers need to make sure that evaluating line does not have side effects. // evaluated, callers need to make sure that evaluating line does not have side effects.
func (jsre *JSRE) CompleteKeywords(line string) []string { func (jsre *JSRE) CompleteKeywords(line string) []string {
var results []string var results []string
jsre.do(func(vm *otto.Otto) { results = getCompletions(vm, line) }) jsre.Do(func(vm *otto.Otto) {
results = getCompletions(vm, line)
})
return results return results
} }
@ -53,9 +55,18 @@ func getCompletions(vm *otto.Otto, line string) (results []string) {
} }
} }
}) })
// e.g. web3<tab><tab> append dot since its an object
if obj, _ = vm.Object(line); obj != nil { // Append opening parenthesis (for functions) or dot (for objects)
results = append(results, line+".") // if the line itself is the only completion.
if len(results) == 1 && results[0] == line {
obj, _ := vm.Object(line)
if obj != nil {
if obj.Class() == "Function" {
results[0] += "("
} else {
results[0] += "."
}
}
} }
sort.Strings(results) sort.Strings(results)

View File

@ -40,7 +40,11 @@ func TestCompleteKeywords(t *testing.T) {
}{ }{
{ {
input: "x", input: "x",
want: []string{"x", "x."}, want: []string{"x."},
},
{
input: "x.someMethod",
want: []string{"x.someMethod("},
}, },
{ {
input: "x.", input: "x.",

View File

@ -214,8 +214,8 @@ loop:
self.loopWg.Done() self.loopWg.Done()
} }
// do schedules the given function on the event loop. // Do executes the given function on the JS event loop.
func (self *JSRE) do(fn func(*otto.Otto)) { func (self *JSRE) Do(fn func(*otto.Otto)) {
done := make(chan bool) done := make(chan bool)
req := &evalReq{fn, done} req := &evalReq{fn, done}
self.evalQueue <- req self.evalQueue <- req
@ -235,7 +235,7 @@ func (self *JSRE) Exec(file string) error {
if err != nil { if err != nil {
return err return err
} }
self.do(func(vm *otto.Otto) { _, err = vm.Run(code) }) self.Do(func(vm *otto.Otto) { _, err = vm.Run(code) })
return err return err
} }
@ -247,19 +247,19 @@ func (self *JSRE) Bind(name string, v interface{}) error {
// Run runs a piece of JS code. // Run runs a piece of JS code.
func (self *JSRE) Run(code string) (v otto.Value, err error) { func (self *JSRE) Run(code string) (v otto.Value, err error) {
self.do(func(vm *otto.Otto) { v, err = vm.Run(code) }) self.Do(func(vm *otto.Otto) { v, err = vm.Run(code) })
return v, err return v, err
} }
// Get returns the value of a variable in the JS environment. // Get returns the value of a variable in the JS environment.
func (self *JSRE) Get(ns string) (v otto.Value, err error) { func (self *JSRE) Get(ns string) (v otto.Value, err error) {
self.do(func(vm *otto.Otto) { v, err = vm.Get(ns) }) self.Do(func(vm *otto.Otto) { v, err = vm.Get(ns) })
return v, err return v, err
} }
// Set assigns value v to a variable in the JS environment. // Set assigns value v to a variable in the JS environment.
func (self *JSRE) Set(ns string, v interface{}) (err error) { func (self *JSRE) Set(ns string, v interface{}) (err error) {
self.do(func(vm *otto.Otto) { err = vm.Set(ns, v) }) self.Do(func(vm *otto.Otto) { err = vm.Set(ns, v) })
return err return err
} }
@ -288,7 +288,7 @@ func (self *JSRE) loadScript(call otto.FunctionCall) otto.Value {
// EvalAndPrettyPrint evaluates code and pretty prints the result to // EvalAndPrettyPrint evaluates code and pretty prints the result to
// standard output. // standard output.
func (self *JSRE) EvalAndPrettyPrint(code string) (err error) { func (self *JSRE) EvalAndPrettyPrint(code string) (err error) {
self.do(func(vm *otto.Otto) { self.Do(func(vm *otto.Otto) {
var val otto.Value var val otto.Value
val, err = vm.Run(code) val, err = vm.Run(code)
if err != nil { if err != nil {
@ -302,7 +302,7 @@ func (self *JSRE) EvalAndPrettyPrint(code string) (err error) {
// Compile compiles and then runs a piece of JS code. // Compile compiles and then runs a piece of JS code.
func (self *JSRE) Compile(filename string, src interface{}) (err error) { func (self *JSRE) Compile(filename string, src interface{}) (err error) {
self.do(func(vm *otto.Otto) { _, err = compileAndRun(vm, filename, src) }) self.Do(func(vm *otto.Otto) { _, err = compileAndRun(vm, filename, src) })
return err return err
} }

View File

@ -177,7 +177,7 @@ func (ctx ppctx) fields(obj *otto.Object) []string {
seen = make(map[string]bool) seen = make(map[string]bool)
) )
add := func(k string) { add := func(k string) {
if seen[k] || boringKeys[k] { if seen[k] || boringKeys[k] || strings.HasPrefix(k, "_") {
return return
} }
seen[k] = true seen[k] = true

View File

@ -295,6 +295,12 @@ web3._extend({
call: 'debug_dumpBlock', call: 'debug_dumpBlock',
params: 1 params: 1
}), }),
new web3._extend.Method({
name: 'chaindbProperty',
call: 'debug_chaindbProperty',
params: 1,
outputFormatter: console.log
}),
new web3._extend.Method({ new web3._extend.Method({
name: 'metrics', name: 'metrics',
call: 'debug_metrics', call: 'debug_metrics',
@ -321,6 +327,16 @@ web3._extend({
params: 0, params: 0,
outputFormatter: console.log outputFormatter: console.log
}), }),
new web3._extend.Method({
name: 'memStats',
call: 'debug_memStats',
params: 0,
}),
new web3._extend.Method({
name: 'gcStats',
call: 'debug_gcStats',
params: 0,
}),
new web3._extend.Method({ new web3._extend.Method({
name: 'cpuProfile', name: 'cpuProfile',
call: 'debug_cpuProfile', call: 'debug_cpuProfile',