debug.go 8.5 KB
Newer Older
1
// Copyright 2015 The go-ethereum Authors
2
// This file is part of the go-ethereum library.
3
//
4
// The go-ethereum library is free software: you can redistribute it and/or modify
5 6 7 8
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
9
// The go-ethereum library is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 13 14
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
15
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16

Bas van Kervel's avatar
Bas van Kervel committed
17 18 19 20
package api

import (
	"fmt"
21 22
	"strings"
	"time"
Bas van Kervel's avatar
Bas van Kervel committed
23 24 25 26 27 28 29 30 31

	"github.com/ethereum/ethash"
	"github.com/ethereum/go-ethereum/core/state"
	"github.com/ethereum/go-ethereum/core/vm"
	"github.com/ethereum/go-ethereum/eth"
	"github.com/ethereum/go-ethereum/rlp"
	"github.com/ethereum/go-ethereum/rpc/codec"
	"github.com/ethereum/go-ethereum/rpc/shared"
	"github.com/ethereum/go-ethereum/xeth"
32
	"github.com/rcrowley/go-metrics"
Bas van Kervel's avatar
Bas van Kervel committed
33 34 35
)

const (
36
	DebugApiVersion = "1.0"
Bas van Kervel's avatar
Bas van Kervel committed
37 38 39 40 41
)

var (
	// mapping between methods and handlers
	DebugMapping = map[string]debughandler{
Bas van Kervel's avatar
Bas van Kervel committed
42 43 44 45 46 47
		"debug_dumpBlock":    (*debugApi).DumpBlock,
		"debug_getBlockRlp":  (*debugApi).GetBlockRlp,
		"debug_printBlock":   (*debugApi).PrintBlock,
		"debug_processBlock": (*debugApi).ProcessBlock,
		"debug_seedHash":     (*debugApi).SeedHash,
		"debug_setHead":      (*debugApi).SetHead,
48
		"debug_metrics":      (*debugApi).Metrics,
Bas van Kervel's avatar
Bas van Kervel committed
49 50 51 52
	}
)

// debug callback handler
Bas van Kervel's avatar
Bas van Kervel committed
53
type debughandler func(*debugApi, *shared.Request) (interface{}, error)
Bas van Kervel's avatar
Bas van Kervel committed
54 55

// admin api provider
Bas van Kervel's avatar
Bas van Kervel committed
56
type debugApi struct {
Bas van Kervel's avatar
Bas van Kervel committed
57 58 59 60 61 62 63
	xeth     *xeth.XEth
	ethereum *eth.Ethereum
	methods  map[string]debughandler
	codec    codec.ApiCoder
}

// create a new debug api instance
Bas van Kervel's avatar
Bas van Kervel committed
64 65
func NewDebugApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *debugApi {
	return &debugApi{
Bas van Kervel's avatar
Bas van Kervel committed
66 67 68 69 70 71 72 73
		xeth:     xeth,
		ethereum: ethereum,
		methods:  DebugMapping,
		codec:    coder.New(nil),
	}
}

// collection with supported methods
Bas van Kervel's avatar
Bas van Kervel committed
74
func (self *debugApi) Methods() []string {
Bas van Kervel's avatar
Bas van Kervel committed
75 76 77 78 79 80 81 82 83 84
	methods := make([]string, len(self.methods))
	i := 0
	for k := range self.methods {
		methods[i] = k
		i++
	}
	return methods
}

// Execute given request
Bas van Kervel's avatar
Bas van Kervel committed
85
func (self *debugApi) Execute(req *shared.Request) (interface{}, error) {
Bas van Kervel's avatar
Bas van Kervel committed
86 87 88 89 90 91 92
	if callback, ok := self.methods[req.Method]; ok {
		return callback(self, req)
	}

	return nil, &shared.NotImplementedError{req.Method}
}

Bas van Kervel's avatar
Bas van Kervel committed
93
func (self *debugApi) Name() string {
94
	return shared.DebugApiName
Bas van Kervel's avatar
Bas van Kervel committed
95 96
}

97 98 99 100
func (self *debugApi) ApiVersion() string {
	return DebugApiVersion
}

Bas van Kervel's avatar
Bas van Kervel committed
101
func (self *debugApi) PrintBlock(req *shared.Request) (interface{}, error) {
Bas van Kervel's avatar
Bas van Kervel committed
102 103 104 105 106 107 108 109 110
	args := new(BlockNumArg)
	if err := self.codec.Decode(req.Params, &args); err != nil {
		return nil, shared.NewDecodeParamError(err.Error())
	}

	block := self.xeth.EthBlockByNumber(args.BlockNumber)
	return fmt.Sprintf("%s", block), nil
}

Bas van Kervel's avatar
Bas van Kervel committed
111
func (self *debugApi) DumpBlock(req *shared.Request) (interface{}, error) {
Bas van Kervel's avatar
Bas van Kervel committed
112 113 114 115 116 117 118 119 120 121
	args := new(BlockNumArg)
	if err := self.codec.Decode(req.Params, &args); err != nil {
		return nil, shared.NewDecodeParamError(err.Error())
	}

	block := self.xeth.EthBlockByNumber(args.BlockNumber)
	if block == nil {
		return nil, fmt.Errorf("block #%d not found", args.BlockNumber)
	}

122
	stateDb := state.New(block.Root(), self.ethereum.ChainDb())
Bas van Kervel's avatar
Bas van Kervel committed
123 124 125 126
	if stateDb == nil {
		return nil, nil
	}

127
	return stateDb.RawDump(), nil
Bas van Kervel's avatar
Bas van Kervel committed
128 129
}

Bas van Kervel's avatar
Bas van Kervel committed
130
func (self *debugApi) GetBlockRlp(req *shared.Request) (interface{}, error) {
Bas van Kervel's avatar
Bas van Kervel committed
131 132 133 134 135 136 137 138 139 140 141 142 143
	args := new(BlockNumArg)
	if err := self.codec.Decode(req.Params, &args); err != nil {
		return nil, shared.NewDecodeParamError(err.Error())
	}

	block := self.xeth.EthBlockByNumber(args.BlockNumber)
	if block == nil {
		return nil, fmt.Errorf("block #%d not found", args.BlockNumber)
	}
	encoded, err := rlp.EncodeToBytes(block)
	return fmt.Sprintf("%x", encoded), err
}

Bas van Kervel's avatar
Bas van Kervel committed
144
func (self *debugApi) SetHead(req *shared.Request) (interface{}, error) {
Bas van Kervel's avatar
Bas van Kervel committed
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
	args := new(BlockNumArg)
	if err := self.codec.Decode(req.Params, &args); err != nil {
		return nil, shared.NewDecodeParamError(err.Error())
	}

	block := self.xeth.EthBlockByNumber(args.BlockNumber)
	if block == nil {
		return nil, fmt.Errorf("block #%d not found", args.BlockNumber)
	}

	self.ethereum.ChainManager().SetHead(block)

	return nil, nil
}

Bas van Kervel's avatar
Bas van Kervel committed
160
func (self *debugApi) ProcessBlock(req *shared.Request) (interface{}, error) {
Bas van Kervel's avatar
Bas van Kervel committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
	args := new(BlockNumArg)
	if err := self.codec.Decode(req.Params, &args); err != nil {
		return nil, shared.NewDecodeParamError(err.Error())
	}

	block := self.xeth.EthBlockByNumber(args.BlockNumber)
	if block == nil {
		return nil, fmt.Errorf("block #%d not found", args.BlockNumber)
	}

	old := vm.Debug
	defer func() { vm.Debug = old }()
	vm.Debug = true

	_, err := self.ethereum.BlockProcessor().RetryProcess(block)
	if err == nil {
		return true, nil
	}
	return false, err
}

Bas van Kervel's avatar
Bas van Kervel committed
182
func (self *debugApi) SeedHash(req *shared.Request) (interface{}, error) {
Bas van Kervel's avatar
Bas van Kervel committed
183 184 185 186 187 188 189 190 191 192 193
	args := new(BlockNumArg)
	if err := self.codec.Decode(req.Params, &args); err != nil {
		return nil, shared.NewDecodeParamError(err.Error())
	}

	if hash, err := ethash.GetSeedHash(uint64(args.BlockNumber)); err == nil {
		return fmt.Sprintf("0x%x", hash), nil
	} else {
		return nil, err
	}
}
194 195

func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) {
196 197 198 199
	args := new(MetricsArgs)
	if err := self.codec.Decode(req.Params, &args); err != nil {
		return nil, shared.NewDecodeParamError(err.Error())
	}
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
	// Create a rate formatter
	units := []string{"", "K", "M", "G", "T", "E", "P"}
	round := func(value float64, prec int) string {
		unit := 0
		for value >= 1000 {
			unit, value, prec = unit+1, value/1000, 2
		}
		return fmt.Sprintf(fmt.Sprintf("%%.%df%s", prec, units[unit]), value)
	}
	format := func(total float64, rate float64) string {
		return fmt.Sprintf("%s (%s/s)", round(total, 0), round(rate, 2))
	}
	// Iterate over all the metrics, and just dump for now
	counters := make(map[string]interface{})
	metrics.DefaultRegistry.Each(func(name string, metric interface{}) {
		// Create or retrieve the counter hierarchy for this metric
		root, parts := counters, strings.Split(name, "/")
		for _, part := range parts[:len(parts)-1] {
			if _, ok := root[part]; !ok {
				root[part] = make(map[string]interface{})
			}
			root = root[part].(map[string]interface{})
		}
		name = parts[len(parts)-1]

225 226 227 228 229
		// Fill the counter with the metric details, formatting if requested
		if args.Raw {
			switch metric := metric.(type) {
			case metrics.Meter:
				root[name] = map[string]interface{}{
230 231 232 233
					"AvgRate01Min": metric.Rate1(),
					"AvgRate05Min": metric.Rate5(),
					"AvgRate15Min": metric.Rate15(),
					"MeanRate":     metric.RateMean(),
234
					"Overall":      float64(metric.Count()),
235 236 237 238
				}

			case metrics.Timer:
				root[name] = map[string]interface{}{
239 240 241 242
					"AvgRate01Min": metric.Rate1(),
					"AvgRate05Min": metric.Rate5(),
					"AvgRate15Min": metric.Rate15(),
					"MeanRate":     metric.RateMean(),
243 244 245 246 247 248 249 250
					"Overall":      float64(metric.Count()),
					"Percentiles": map[string]interface{}{
						"5":  metric.Percentile(0.05),
						"20": metric.Percentile(0.2),
						"50": metric.Percentile(0.5),
						"80": metric.Percentile(0.8),
						"95": metric.Percentile(0.95),
					},
251 252 253 254
				}

			default:
				root[name] = "Unknown metric type"
255
			}
256 257 258 259 260 261 262
		} else {
			switch metric := metric.(type) {
			case metrics.Meter:
				root[name] = map[string]interface{}{
					"Avg01Min": format(metric.Rate1()*60, metric.Rate1()),
					"Avg05Min": format(metric.Rate5()*300, metric.Rate5()),
					"Avg15Min": format(metric.Rate15()*900, metric.Rate15()),
263
					"Overall":  format(float64(metric.Count()), metric.RateMean()),
264 265 266 267 268 269 270
				}

			case metrics.Timer:
				root[name] = map[string]interface{}{
					"Avg01Min": format(metric.Rate1()*60, metric.Rate1()),
					"Avg05Min": format(metric.Rate5()*300, metric.Rate5()),
					"Avg15Min": format(metric.Rate15()*900, metric.Rate15()),
271
					"Overall":  format(float64(metric.Count()), metric.RateMean()),
272 273
					"Maximum":  time.Duration(metric.Max()).String(),
					"Minimum":  time.Duration(metric.Min()).String(),
274
					"Percentiles": map[string]interface{}{
275
						"5":  time.Duration(metric.Percentile(0.05)).String(),
276 277 278 279 280 281 282 283 284
						"20": time.Duration(metric.Percentile(0.2)).String(),
						"50": time.Duration(metric.Percentile(0.5)).String(),
						"80": time.Duration(metric.Percentile(0.8)).String(),
						"95": time.Duration(metric.Percentile(0.95)).String(),
					},
				}

			default:
				root[name] = "Unknown metric type"
285 286 287 288 289
			}
		}
	})
	return counters, nil
}