diff --git a/eth/api.go b/eth/api.go
index 508070646fbc2add34c902cbe83b665e24a0446c..10588675258d48a9b6d26ceca2e17dc23f2fcb17 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -26,6 +26,7 @@ import (
 	"math/big"
 	"os"
 	"runtime"
+	"strings"
 	"sync"
 	"time"
 
@@ -46,6 +47,7 @@ import (
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rpc"
+	"github.com/syndtr/goleveldb/leveldb"
 	"golang.org/x/net/context"
 )
 
@@ -1566,6 +1568,22 @@ func NewPrivateDebugAPI(config *core.ChainConfig, eth *Ethereum) *PrivateDebugAP
 	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
 // consensus results and full VM trace logs for all included transactions.
 type BlockTraceResult struct {
diff --git a/internal/debug/api.go b/internal/debug/api.go
index 83857eb2e1c5c63b9e9a4785a5be7e19dd183ba7..2cb264040d349dac5dfc89674ef40047a349b3b8 100644
--- a/internal/debug/api.go
+++ b/internal/debug/api.go
@@ -27,6 +27,7 @@ import (
 	"os/user"
 	"path/filepath"
 	"runtime"
+	"runtime/debug"
 	"runtime/pprof"
 	"strings"
 	"sync"
@@ -69,6 +70,20 @@ func (*HandlerT) BacktraceAt(location string) error {
 	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
 // profile data to file.
 func (h *HandlerT) CpuProfile(file string, nsec uint) error {
diff --git a/jsre/completion.go b/jsre/completion.go
index e84a5b75c35ed21d454c244aace646c93aae4277..7f94dabfcea8789d0f5fbd903ff8c0037743823a 100644
--- a/jsre/completion.go
+++ b/jsre/completion.go
@@ -27,7 +27,9 @@ import (
 // evaluated, callers need to make sure that evaluating line does not have side effects.
 func (jsre *JSRE) CompleteKeywords(line string) []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
 }
 
@@ -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 {
-		results = append(results, line+".")
+
+	// Append opening parenthesis (for functions) or dot (for objects)
+	// 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)
diff --git a/jsre/completion_test.go b/jsre/completion_test.go
index 6d42b2bd1c608e101dc3bce1af309b86d2a09471..8765281e5e86b234cd41dc0c50b84f7a3158b9f4 100644
--- a/jsre/completion_test.go
+++ b/jsre/completion_test.go
@@ -40,7 +40,11 @@ func TestCompleteKeywords(t *testing.T) {
 	}{
 		{
 			input: "x",
-			want:  []string{"x", "x."},
+			want:  []string{"x."},
+		},
+		{
+			input: "x.someMethod",
+			want:  []string{"x.someMethod("},
 		},
 		{
 			input: "x.",
diff --git a/jsre/jsre.go b/jsre/jsre.go
index f4464910de4961e208cfed9e58f79bea0b0cbf12..7df022cb186bcf1b9019fb325c52ecd225ff06a0 100644
--- a/jsre/jsre.go
+++ b/jsre/jsre.go
@@ -214,8 +214,8 @@ loop:
 	self.loopWg.Done()
 }
 
-// do schedules the given function on the event loop.
-func (self *JSRE) do(fn func(*otto.Otto)) {
+// Do executes the given function on the JS event loop.
+func (self *JSRE) Do(fn func(*otto.Otto)) {
 	done := make(chan bool)
 	req := &evalReq{fn, done}
 	self.evalQueue <- req
@@ -235,7 +235,7 @@ func (self *JSRE) Exec(file string) error {
 	if err != nil {
 		return err
 	}
-	self.do(func(vm *otto.Otto) { _, err = vm.Run(code) })
+	self.Do(func(vm *otto.Otto) { _, err = vm.Run(code) })
 	return err
 }
 
@@ -247,19 +247,19 @@ func (self *JSRE) Bind(name string, v interface{}) error {
 
 // Run runs a piece of JS code.
 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
 }
 
 // Get returns the value of a variable in the JS environment.
 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
 }
 
 // Set assigns value v to a variable in the JS environment.
 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
 }
 
@@ -288,7 +288,7 @@ func (self *JSRE) loadScript(call otto.FunctionCall) otto.Value {
 // EvalAndPrettyPrint evaluates code and pretty prints the result to
 // standard output.
 func (self *JSRE) EvalAndPrettyPrint(code string) (err error) {
-	self.do(func(vm *otto.Otto) {
+	self.Do(func(vm *otto.Otto) {
 		var val otto.Value
 		val, err = vm.Run(code)
 		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.
 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
 }
 
diff --git a/jsre/pretty.go b/jsre/pretty.go
index 8b2b35e8e25a46e3f8b0556c2ecbf5377820628d..cd7fa5232be6aa86086bd5779afe664fc8e02c4c 100644
--- a/jsre/pretty.go
+++ b/jsre/pretty.go
@@ -177,7 +177,7 @@ func (ctx ppctx) fields(obj *otto.Object) []string {
 		seen          = make(map[string]bool)
 	)
 	add := func(k string) {
-		if seen[k] || boringKeys[k] {
+		if seen[k] || boringKeys[k] || strings.HasPrefix(k, "_") {
 			return
 		}
 		seen[k] = true
diff --git a/rpc/javascript.go b/rpc/javascript.go
index d6ffcdf222108ebc41f8159f4295ae2d088e09c7..475691a874a2b54b80d6de841fdd90a9dc06710a 100644
--- a/rpc/javascript.go
+++ b/rpc/javascript.go
@@ -295,6 +295,12 @@ web3._extend({
 			call: 'debug_dumpBlock',
 			params: 1
 		}),
+		new web3._extend.Method({
+			name: 'chaindbProperty',
+			call: 'debug_chaindbProperty',
+			params: 1,
+			outputFormatter: console.log
+		}),
 		new web3._extend.Method({
 			name: 'metrics',
 			call: 'debug_metrics',
@@ -321,6 +327,16 @@ web3._extend({
 			params: 0,
 			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({
 			name: 'cpuProfile',
 			call: 'debug_cpuProfile',