mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-13 15:33:47 +00:00
Add grpc server + debug endpoint
This commit is contained in:
parent
7282cf9c25
commit
2c9b2c31dc
8 changed files with 504 additions and 48 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -48,3 +48,4 @@ profile.cov
|
||||||
|
|
||||||
**/yarn-error.log
|
**/yarn-error.log
|
||||||
./test
|
./test
|
||||||
|
./bor-debug-*
|
||||||
|
|
|
||||||
197
command/debug.go
197
command/debug.go
|
|
@ -1,13 +1,29 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"compress/gzip"
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/command/flagset"
|
"github.com/ethereum/go-ethereum/command/flagset"
|
||||||
|
"github.com/ethereum/go-ethereum/command/server/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DebugCommand struct {
|
type DebugCommand struct {
|
||||||
*Meta2
|
*Meta2
|
||||||
|
|
||||||
|
seconds uint64
|
||||||
|
output string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Help implements the cli.Command interface
|
// Help implements the cli.Command interface
|
||||||
|
|
@ -18,7 +34,20 @@ func (d *DebugCommand) Help() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DebugCommand) Flags() *flagset.Flagset {
|
func (d *DebugCommand) Flags() *flagset.Flagset {
|
||||||
return d.NewFlagSet("debug")
|
flags := d.NewFlagSet("debug")
|
||||||
|
|
||||||
|
flags.Uint64Flag(&flagset.Uint64Flag{
|
||||||
|
Name: "seconds",
|
||||||
|
Usage: "seconds to trace",
|
||||||
|
Value: &d.seconds,
|
||||||
|
})
|
||||||
|
flags.StringFlag(&flagset.StringFlag{
|
||||||
|
Name: "output",
|
||||||
|
Value: &d.output,
|
||||||
|
Usage: "Output directory",
|
||||||
|
})
|
||||||
|
|
||||||
|
return flags
|
||||||
}
|
}
|
||||||
|
|
||||||
// Synopsis implements the cli.Command interface
|
// Synopsis implements the cli.Command interface
|
||||||
|
|
@ -39,6 +68,170 @@ func (d *DebugCommand) Run(args []string) int {
|
||||||
d.UI.Error(err.Error())
|
d.UI.Error(err.Error())
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
fmt.Println(clt)
|
|
||||||
|
stamped := "bor-debug-" + time.Now().UTC().Format("2006-01-02-150405Z")
|
||||||
|
|
||||||
|
// Create the output directory
|
||||||
|
var tmp string
|
||||||
|
if d.output != "" {
|
||||||
|
// User specified output directory
|
||||||
|
tmp = filepath.Join(d.output, stamped)
|
||||||
|
_, err := os.Stat(tmp)
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
d.UI.Error("Output directory already exists")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Generate temp directory
|
||||||
|
tmp, err = ioutil.TempDir(os.TempDir(), stamped)
|
||||||
|
if err != nil {
|
||||||
|
d.UI.Error(fmt.Sprintf("Error creating tmp directory: %s", err.Error()))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.UI.Output("Starting debugger...")
|
||||||
|
d.UI.Output("")
|
||||||
|
|
||||||
|
// ensure destine folder exists
|
||||||
|
if err := os.MkdirAll(tmp, os.ModePerm); err != nil {
|
||||||
|
d.UI.Error(fmt.Sprintf("failed to create parent directory: %v", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pprofProfile := func(ctx context.Context, profile string, filename string) error {
|
||||||
|
req := &proto.PprofRequest{
|
||||||
|
Seconds: int64(d.seconds),
|
||||||
|
}
|
||||||
|
switch profile {
|
||||||
|
case "cpu":
|
||||||
|
req.Type = proto.PprofRequest_CPU
|
||||||
|
case "trace":
|
||||||
|
req.Type = proto.PprofRequest_TRACE
|
||||||
|
default:
|
||||||
|
req.Type = proto.PprofRequest_LOOKUP
|
||||||
|
req.Profile = profile
|
||||||
|
}
|
||||||
|
resp, err := clt.Pprof(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// write file
|
||||||
|
raw, err := hex.DecodeString(resp.Payload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(tmp, filename+".prof"), raw, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancelFn := context.WithCancel(context.Background())
|
||||||
|
trapSignal(cancelFn)
|
||||||
|
|
||||||
|
profiles := map[string]string{
|
||||||
|
"heap": "heap",
|
||||||
|
"cpu": "cpu",
|
||||||
|
"trace": "trace",
|
||||||
|
}
|
||||||
|
for profile, filename := range profiles {
|
||||||
|
if err := pprofProfile(ctx, profile, filename); err != nil {
|
||||||
|
d.UI.Error(fmt.Sprintf("Error creating profile '%s': %v", profile, err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit before archive if output directory was specified
|
||||||
|
if d.output != "" {
|
||||||
|
d.UI.Output(fmt.Sprintf("Created debug directory: %s", tmp))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create archive tarball
|
||||||
|
archiveFile := stamped + ".tar.gz"
|
||||||
|
if err = tarCZF(archiveFile, tmp, stamped); err != nil {
|
||||||
|
d.UI.Error(fmt.Sprintf("Error creating archive: %s", err.Error()))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
d.UI.Output(fmt.Sprintf("Created debug archive: %s", archiveFile))
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func trapSignal(cancel func()) {
|
||||||
|
sigCh := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigCh,
|
||||||
|
syscall.SIGHUP,
|
||||||
|
syscall.SIGINT,
|
||||||
|
syscall.SIGTERM,
|
||||||
|
syscall.SIGQUIT)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-sigCh
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func tarCZF(archive string, src, target string) error {
|
||||||
|
// ensure the src actually exists before trying to tar it
|
||||||
|
if _, err := os.Stat(src); err != nil {
|
||||||
|
return fmt.Errorf("unable to tar files - %v", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the archive
|
||||||
|
fh, err := os.Create(archive)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fh.Close()
|
||||||
|
|
||||||
|
zz := gzip.NewWriter(fh)
|
||||||
|
defer zz.Close()
|
||||||
|
|
||||||
|
tw := tar.NewWriter(zz)
|
||||||
|
defer tw.Close()
|
||||||
|
|
||||||
|
// tar
|
||||||
|
return filepath.Walk(src, func(file string, fi os.FileInfo, err error) error {
|
||||||
|
// return on any error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !fi.Mode().IsRegular() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
header, err := tar.FileInfoHeader(fi, fi.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove leading path to the src, so files are relative to the archive
|
||||||
|
path := strings.ReplaceAll(file, src, "")
|
||||||
|
if target != "" {
|
||||||
|
path = filepath.Join([]string{target, path}...)
|
||||||
|
}
|
||||||
|
path = strings.TrimPrefix(path, string(filepath.Separator))
|
||||||
|
|
||||||
|
header.Name = path
|
||||||
|
|
||||||
|
if err := tw.WriteHeader(header); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the file contents
|
||||||
|
f, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.Copy(tw, f); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Close()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ func (m *Meta2) NewFlagSet(n string) *flagset.Flagset {
|
||||||
|
|
||||||
func (m *Meta2) Conn() (*grpc.ClientConn, error) {
|
func (m *Meta2) Conn() (*grpc.ClientConn, error) {
|
||||||
if m.addr == "" {
|
if m.addr == "" {
|
||||||
m.addr = "http://localhost:3131"
|
m.addr = "127.0.0.1:3131"
|
||||||
}
|
}
|
||||||
conn, err := grpc.Dial(m.addr, grpc.WithInsecure())
|
conn, err := grpc.Dial(m.addr, grpc.WithInsecure())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,12 @@ package pprof
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
|
"runtime/trace"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Profile generates a pprof.Profile report for the given profile name.
|
// Profile generates a pprof.Profile report for the given profile name.
|
||||||
|
|
@ -34,3 +37,57 @@ func Profile(profile string, debug, gc int) ([]byte, map[string]string, error) {
|
||||||
}
|
}
|
||||||
return buf.Bytes(), headers, nil
|
return buf.Bytes(), headers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CPUProfile generates a CPU Profile for a given duration
|
||||||
|
func CPUProfile(ctx context.Context, sec int) ([]byte, map[string]string, error) {
|
||||||
|
if sec <= 0 {
|
||||||
|
sec = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := pprof.StartCPUProfile(&buf); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(ctx, time.Duration(sec)*time.Second)
|
||||||
|
|
||||||
|
pprof.StopCPUProfile()
|
||||||
|
|
||||||
|
return buf.Bytes(),
|
||||||
|
map[string]string{
|
||||||
|
"X-Content-Type-Options": "nosniff",
|
||||||
|
"Content-Type": "application/octet-stream",
|
||||||
|
"Content-Disposition": `attachment; filename="profile"`,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace runs a trace profile for a given duration
|
||||||
|
func Trace(ctx context.Context, sec int) ([]byte, map[string]string, error) {
|
||||||
|
if sec <= 0 {
|
||||||
|
sec = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := trace.Start(&buf); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(ctx, time.Duration(sec)*time.Second)
|
||||||
|
|
||||||
|
trace.Stop()
|
||||||
|
|
||||||
|
return buf.Bytes(),
|
||||||
|
map[string]string{
|
||||||
|
"X-Content-Type-Options": "nosniff",
|
||||||
|
"Content-Type": "application/octet-stream",
|
||||||
|
"Content-Disposition": `attachment; filename="trace"`,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sleep(ctx context.Context, d time.Duration) {
|
||||||
|
// Sleep until duration is met or ctx is cancelled
|
||||||
|
select {
|
||||||
|
case <-time.After(d):
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,14 +25,67 @@ const (
|
||||||
// of the legacy proto package is being used.
|
// of the legacy proto package is being used.
|
||||||
const _ = proto.ProtoPackageIsVersion4
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type DebugInput struct {
|
type PprofRequest_Type int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
PprofRequest_LOOKUP PprofRequest_Type = 0
|
||||||
|
PprofRequest_CPU PprofRequest_Type = 1
|
||||||
|
PprofRequest_TRACE PprofRequest_Type = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for PprofRequest_Type.
|
||||||
|
var (
|
||||||
|
PprofRequest_Type_name = map[int32]string{
|
||||||
|
0: "LOOKUP",
|
||||||
|
1: "CPU",
|
||||||
|
2: "TRACE",
|
||||||
|
}
|
||||||
|
PprofRequest_Type_value = map[string]int32{
|
||||||
|
"LOOKUP": 0,
|
||||||
|
"CPU": 1,
|
||||||
|
"TRACE": 2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x PprofRequest_Type) Enum() *PprofRequest_Type {
|
||||||
|
p := new(PprofRequest_Type)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x PprofRequest_Type) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PprofRequest_Type) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_command_server_proto_server_proto_enumTypes[0].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PprofRequest_Type) Type() protoreflect.EnumType {
|
||||||
|
return &file_command_server_proto_server_proto_enumTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x PprofRequest_Type) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use PprofRequest_Type.Descriptor instead.
|
||||||
|
func (PprofRequest_Type) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_command_server_proto_server_proto_rawDescGZIP(), []int{0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PprofRequest struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Type PprofRequest_Type `protobuf:"varint,1,opt,name=type,proto3,enum=proto.PprofRequest_Type" json:"type,omitempty"`
|
||||||
|
Profile string `protobuf:"bytes,2,opt,name=profile,proto3" json:"profile,omitempty"`
|
||||||
|
Seconds int64 `protobuf:"varint,3,opt,name=seconds,proto3" json:"seconds,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *DebugInput) Reset() {
|
func (x *PprofRequest) Reset() {
|
||||||
*x = DebugInput{}
|
*x = PprofRequest{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_command_server_proto_server_proto_msgTypes[0]
|
mi := &file_command_server_proto_server_proto_msgTypes[0]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
|
@ -40,13 +93,13 @@ func (x *DebugInput) Reset() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *DebugInput) String() string {
|
func (x *PprofRequest) String() string {
|
||||||
return protoimpl.X.MessageStringOf(x)
|
return protoimpl.X.MessageStringOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*DebugInput) ProtoMessage() {}
|
func (*PprofRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *DebugInput) ProtoReflect() protoreflect.Message {
|
func (x *PprofRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_command_server_proto_server_proto_msgTypes[0]
|
mi := &file_command_server_proto_server_proto_msgTypes[0]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
|
@ -58,23 +111,119 @@ func (x *DebugInput) ProtoReflect() protoreflect.Message {
|
||||||
return mi.MessageOf(x)
|
return mi.MessageOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Use DebugInput.ProtoReflect.Descriptor instead.
|
// Deprecated: Use PprofRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*DebugInput) Descriptor() ([]byte, []int) {
|
func (*PprofRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_command_server_proto_server_proto_rawDescGZIP(), []int{0}
|
return file_command_server_proto_server_proto_rawDescGZIP(), []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *PprofRequest) GetType() PprofRequest_Type {
|
||||||
|
if x != nil {
|
||||||
|
return x.Type
|
||||||
|
}
|
||||||
|
return PprofRequest_LOOKUP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PprofRequest) GetProfile() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Profile
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PprofRequest) GetSeconds() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Seconds
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type PprofResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Payload string `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
|
||||||
|
Headers map[string]string `protobuf:"bytes,2,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PprofResponse) Reset() {
|
||||||
|
*x = PprofResponse{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_command_server_proto_server_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PprofResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PprofResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *PprofResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_command_server_proto_server_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use PprofResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*PprofResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_command_server_proto_server_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PprofResponse) GetPayload() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Payload
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PprofResponse) GetHeaders() map[string]string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Headers
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var File_command_server_proto_server_proto protoreflect.FileDescriptor
|
var File_command_server_proto_server_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_command_server_proto_server_proto_rawDesc = []byte{
|
var file_command_server_proto_server_proto_rawDesc = []byte{
|
||||||
0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
|
0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||||
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72,
|
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72,
|
||||||
0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x0c, 0x0a, 0x0a, 0x44, 0x65,
|
0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x98, 0x01, 0x0a, 0x0c, 0x50,
|
||||||
0x62, 0x75, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x32, 0x34, 0x0a, 0x03, 0x42, 0x6f, 0x72, 0x12,
|
0x70, 0x72, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x04, 0x74,
|
||||||
0x2d, 0x0a, 0x05, 0x44, 0x65, 0x62, 0x75, 0x67, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x1a, 0x11, 0x2e, 0x70, 0x72,
|
0x6f, 0x2e, 0x50, 0x70, 0x72, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x54,
|
||||||
0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x42, 0x17,
|
0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f,
|
||||||
0x5a, 0x15, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65,
|
0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x66,
|
||||||
0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x69, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03,
|
||||||
|
0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0x26, 0x0a,
|
||||||
|
0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x4f, 0x4f, 0x4b, 0x55, 0x50, 0x10,
|
||||||
|
0x00, 0x12, 0x07, 0x0a, 0x03, 0x43, 0x50, 0x55, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52,
|
||||||
|
0x41, 0x43, 0x45, 0x10, 0x02, 0x22, 0xa2, 0x01, 0x0a, 0x0d, 0x50, 0x70, 0x72, 0x6f, 0x66, 0x52,
|
||||||
|
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f,
|
||||||
|
0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61,
|
||||||
|
0x64, 0x12, 0x3b, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03,
|
||||||
|
0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x70, 0x72, 0x6f, 0x66,
|
||||||
|
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73,
|
||||||
|
0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x3a,
|
||||||
|
0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
|
||||||
|
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
|
||||||
|
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
|
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0x39, 0x0a, 0x03, 0x42, 0x6f,
|
||||||
|
0x72, 0x12, 0x32, 0x0a, 0x05, 0x50, 0x70, 0x72, 0x6f, 0x66, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x2e, 0x50, 0x70, 0x72, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||||
|
0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x70, 0x72, 0x6f, 0x66, 0x52, 0x65, 0x73,
|
||||||
|
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x17, 0x5a, 0x15, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
|
||||||
|
0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -89,18 +238,24 @@ func file_command_server_proto_server_proto_rawDescGZIP() []byte {
|
||||||
return file_command_server_proto_server_proto_rawDescData
|
return file_command_server_proto_server_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_command_server_proto_server_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
var file_command_server_proto_server_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
|
var file_command_server_proto_server_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||||
var file_command_server_proto_server_proto_goTypes = []interface{}{
|
var file_command_server_proto_server_proto_goTypes = []interface{}{
|
||||||
(*DebugInput)(nil), // 0: proto.DebugInput
|
(PprofRequest_Type)(0), // 0: proto.PprofRequest.Type
|
||||||
|
(*PprofRequest)(nil), // 1: proto.PprofRequest
|
||||||
|
(*PprofResponse)(nil), // 2: proto.PprofResponse
|
||||||
|
nil, // 3: proto.PprofResponse.HeadersEntry
|
||||||
}
|
}
|
||||||
var file_command_server_proto_server_proto_depIdxs = []int32{
|
var file_command_server_proto_server_proto_depIdxs = []int32{
|
||||||
0, // 0: proto.Bor.Debug:input_type -> proto.DebugInput
|
0, // 0: proto.PprofRequest.type:type_name -> proto.PprofRequest.Type
|
||||||
0, // 1: proto.Bor.Debug:output_type -> proto.DebugInput
|
3, // 1: proto.PprofResponse.headers:type_name -> proto.PprofResponse.HeadersEntry
|
||||||
1, // [1:2] is the sub-list for method output_type
|
1, // 2: proto.Bor.Pprof:input_type -> proto.PprofRequest
|
||||||
0, // [0:1] is the sub-list for method input_type
|
2, // 3: proto.Bor.Pprof:output_type -> proto.PprofResponse
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
3, // [3:4] is the sub-list for method output_type
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
2, // [2:3] is the sub-list for method input_type
|
||||||
0, // [0:0] is the sub-list for field type_name
|
2, // [2:2] is the sub-list for extension type_name
|
||||||
|
2, // [2:2] is the sub-list for extension extendee
|
||||||
|
0, // [0:2] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_command_server_proto_server_proto_init() }
|
func init() { file_command_server_proto_server_proto_init() }
|
||||||
|
|
@ -110,7 +265,19 @@ func file_command_server_proto_server_proto_init() {
|
||||||
}
|
}
|
||||||
if !protoimpl.UnsafeEnabled {
|
if !protoimpl.UnsafeEnabled {
|
||||||
file_command_server_proto_server_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
file_command_server_proto_server_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*DebugInput); i {
|
switch v := v.(*PprofRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_command_server_proto_server_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*PprofResponse); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
|
|
@ -127,13 +294,14 @@ func file_command_server_proto_server_proto_init() {
|
||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_command_server_proto_server_proto_rawDesc,
|
RawDescriptor: file_command_server_proto_server_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 1,
|
||||||
NumMessages: 1,
|
NumMessages: 3,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
||||||
GoTypes: file_command_server_proto_server_proto_goTypes,
|
GoTypes: file_command_server_proto_server_proto_goTypes,
|
||||||
DependencyIndexes: file_command_server_proto_server_proto_depIdxs,
|
DependencyIndexes: file_command_server_proto_server_proto_depIdxs,
|
||||||
|
EnumInfos: file_command_server_proto_server_proto_enumTypes,
|
||||||
MessageInfos: file_command_server_proto_server_proto_msgTypes,
|
MessageInfos: file_command_server_proto_server_proto_msgTypes,
|
||||||
}.Build()
|
}.Build()
|
||||||
File_command_server_proto_server_proto = out.File
|
File_command_server_proto_server_proto = out.File
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,24 @@ package proto;
|
||||||
option go_package = "/command/server/proto";
|
option go_package = "/command/server/proto";
|
||||||
|
|
||||||
service Bor {
|
service Bor {
|
||||||
rpc Debug(DebugInput) returns (DebugInput);
|
rpc Pprof(PprofRequest) returns (PprofResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
message DebugInput {
|
message PprofRequest {
|
||||||
|
Type type = 1;
|
||||||
|
|
||||||
|
string profile = 2;
|
||||||
|
|
||||||
|
int64 seconds = 3;
|
||||||
|
|
||||||
|
enum Type {
|
||||||
|
LOOKUP = 0;
|
||||||
|
CPU = 1;
|
||||||
|
TRACE = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message PprofResponse {
|
||||||
|
string payload = 1;
|
||||||
|
map<string, string> headers = 2;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const _ = grpc.SupportPackageIsVersion7
|
||||||
//
|
//
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
type BorClient interface {
|
type BorClient interface {
|
||||||
Debug(ctx context.Context, in *DebugInput, opts ...grpc.CallOption) (*DebugInput, error)
|
Pprof(ctx context.Context, in *PprofRequest, opts ...grpc.CallOption) (*PprofResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type borClient struct {
|
type borClient struct {
|
||||||
|
|
@ -29,9 +29,9 @@ func NewBorClient(cc grpc.ClientConnInterface) BorClient {
|
||||||
return &borClient{cc}
|
return &borClient{cc}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *borClient) Debug(ctx context.Context, in *DebugInput, opts ...grpc.CallOption) (*DebugInput, error) {
|
func (c *borClient) Pprof(ctx context.Context, in *PprofRequest, opts ...grpc.CallOption) (*PprofResponse, error) {
|
||||||
out := new(DebugInput)
|
out := new(PprofResponse)
|
||||||
err := c.cc.Invoke(ctx, "/proto.Bor/Debug", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/proto.Bor/Pprof", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +42,7 @@ func (c *borClient) Debug(ctx context.Context, in *DebugInput, opts ...grpc.Call
|
||||||
// All implementations must embed UnimplementedBorServer
|
// All implementations must embed UnimplementedBorServer
|
||||||
// for forward compatibility
|
// for forward compatibility
|
||||||
type BorServer interface {
|
type BorServer interface {
|
||||||
Debug(context.Context, *DebugInput) (*DebugInput, error)
|
Pprof(context.Context, *PprofRequest) (*PprofResponse, error)
|
||||||
mustEmbedUnimplementedBorServer()
|
mustEmbedUnimplementedBorServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,8 +50,8 @@ type BorServer interface {
|
||||||
type UnimplementedBorServer struct {
|
type UnimplementedBorServer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedBorServer) Debug(context.Context, *DebugInput) (*DebugInput, error) {
|
func (UnimplementedBorServer) Pprof(context.Context, *PprofRequest) (*PprofResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Debug not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method Pprof not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedBorServer) mustEmbedUnimplementedBorServer() {}
|
func (UnimplementedBorServer) mustEmbedUnimplementedBorServer() {}
|
||||||
|
|
||||||
|
|
@ -66,20 +66,20 @@ func RegisterBorServer(s grpc.ServiceRegistrar, srv BorServer) {
|
||||||
s.RegisterService(&Bor_ServiceDesc, srv)
|
s.RegisterService(&Bor_ServiceDesc, srv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _Bor_Debug_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _Bor_Pprof_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(DebugInput)
|
in := new(PprofRequest)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if interceptor == nil {
|
if interceptor == nil {
|
||||||
return srv.(BorServer).Debug(ctx, in)
|
return srv.(BorServer).Pprof(ctx, in)
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/proto.Bor/Debug",
|
FullMethod: "/proto.Bor/Pprof",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(BorServer).Debug(ctx, req.(*DebugInput))
|
return srv.(BorServer).Pprof(ctx, req.(*PprofRequest))
|
||||||
}
|
}
|
||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
@ -92,8 +92,8 @@ var Bor_ServiceDesc = grpc.ServiceDesc{
|
||||||
HandlerType: (*BorServer)(nil),
|
HandlerType: (*BorServer)(nil),
|
||||||
Methods: []grpc.MethodDesc{
|
Methods: []grpc.MethodDesc{
|
||||||
{
|
{
|
||||||
MethodName: "Debug",
|
MethodName: "Pprof",
|
||||||
Handler: _Bor_Debug_Handler,
|
Handler: _Bor_Pprof_Handler,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{},
|
Streams: []grpc.StreamDesc{},
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,32 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/command/server/pprof"
|
||||||
"github.com/ethereum/go-ethereum/command/server/proto"
|
"github.com/ethereum/go-ethereum/command/server/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) Debug(ctx context.Context, req *proto.DebugInput) (*proto.DebugInput, error) {
|
func (s *Server) Pprof(ctx context.Context, req *proto.PprofRequest) (*proto.PprofResponse, error) {
|
||||||
return &proto.DebugInput{}, nil
|
var payload []byte
|
||||||
|
var headers map[string]string
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch req.Type {
|
||||||
|
case proto.PprofRequest_CPU:
|
||||||
|
payload, headers, err = pprof.CPUProfile(ctx, int(req.Seconds))
|
||||||
|
case proto.PprofRequest_TRACE:
|
||||||
|
payload, headers, err = pprof.Trace(ctx, int(req.Seconds))
|
||||||
|
case proto.PprofRequest_LOOKUP:
|
||||||
|
payload, headers, err = pprof.Profile(req.Profile, 0, 0)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &proto.PprofResponse{
|
||||||
|
Payload: hex.EncodeToString(payload),
|
||||||
|
Headers: headers,
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue