mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-31 09:03:46 +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
|
||||
./test
|
||||
./bor-debug-*
|
||||
|
|
|
|||
197
command/debug.go
197
command/debug.go
|
|
@ -1,13 +1,29 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"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/server/proto"
|
||||
)
|
||||
|
||||
type DebugCommand struct {
|
||||
*Meta2
|
||||
|
||||
seconds uint64
|
||||
output string
|
||||
}
|
||||
|
||||
// Help implements the cli.Command interface
|
||||
|
|
@ -18,7 +34,20 @@ func (d *DebugCommand) Help() string {
|
|||
}
|
||||
|
||||
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
|
||||
|
|
@ -39,6 +68,170 @@ func (d *DebugCommand) Run(args []string) int {
|
|||
d.UI.Error(err.Error())
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
if m.addr == "" {
|
||||
m.addr = "http://localhost:3131"
|
||||
m.addr = "127.0.0.1:3131"
|
||||
}
|
||||
conn, err := grpc.Dial(m.addr, grpc.WithInsecure())
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ package pprof
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"runtime/trace"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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.
|
||||
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
|
||||
sizeCache protoimpl.SizeCache
|
||||
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() {
|
||||
*x = DebugInput{}
|
||||
func (x *PprofRequest) Reset() {
|
||||
*x = PprofRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_command_server_proto_server_proto_msgTypes[0]
|
||||
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)
|
||||
}
|
||||
|
||||
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]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
|
|
@ -58,23 +111,119 @@ func (x *DebugInput) ProtoReflect() protoreflect.Message {
|
|||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use DebugInput.ProtoReflect.Descriptor instead.
|
||||
func (*DebugInput) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use PprofRequest.ProtoReflect.Descriptor instead.
|
||||
func (*PprofRequest) Descriptor() ([]byte, []int) {
|
||||
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_rawDesc = []byte{
|
||||
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,
|
||||
0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x0c, 0x0a, 0x0a, 0x44, 0x65,
|
||||
0x62, 0x75, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x32, 0x34, 0x0a, 0x03, 0x42, 0x6f, 0x72, 0x12,
|
||||
0x2d, 0x0a, 0x05, 0x44, 0x65, 0x62, 0x75, 0x67, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x1a, 0x11, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 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,
|
||||
0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x98, 0x01, 0x0a, 0x0c, 0x50,
|
||||
0x70, 0x72, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x04, 0x74,
|
||||
0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x2e, 0x50, 0x70, 0x72, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x54,
|
||||
0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f,
|
||||
0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x66,
|
||||
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 (
|
||||
|
|
@ -89,18 +238,24 @@ func file_command_server_proto_server_proto_rawDescGZIP() []byte {
|
|||
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{}{
|
||||
(*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{
|
||||
0, // 0: proto.Bor.Debug:input_type -> proto.DebugInput
|
||||
0, // 1: proto.Bor.Debug:output_type -> proto.DebugInput
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
0, // 0: proto.PprofRequest.type:type_name -> proto.PprofRequest.Type
|
||||
3, // 1: proto.PprofResponse.headers:type_name -> proto.PprofResponse.HeadersEntry
|
||||
1, // 2: proto.Bor.Pprof:input_type -> proto.PprofRequest
|
||||
2, // 3: proto.Bor.Pprof:output_type -> proto.PprofResponse
|
||||
3, // [3:4] is the sub-list for method output_type
|
||||
2, // [2:3] is the sub-list for method input_type
|
||||
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() }
|
||||
|
|
@ -110,7 +265,19 @@ func file_command_server_proto_server_proto_init() {
|
|||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
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:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
|
@ -127,13 +294,14 @@ func file_command_server_proto_server_proto_init() {
|
|||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_command_server_proto_server_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumEnums: 1,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_command_server_proto_server_proto_goTypes,
|
||||
DependencyIndexes: file_command_server_proto_server_proto_depIdxs,
|
||||
EnumInfos: file_command_server_proto_server_proto_enumTypes,
|
||||
MessageInfos: file_command_server_proto_server_proto_msgTypes,
|
||||
}.Build()
|
||||
File_command_server_proto_server_proto = out.File
|
||||
|
|
|
|||
|
|
@ -5,9 +5,24 @@ package proto;
|
|||
option go_package = "/command/server/proto";
|
||||
|
||||
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.
|
||||
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 {
|
||||
|
|
@ -29,9 +29,9 @@ func NewBorClient(cc grpc.ClientConnInterface) BorClient {
|
|||
return &borClient{cc}
|
||||
}
|
||||
|
||||
func (c *borClient) Debug(ctx context.Context, in *DebugInput, opts ...grpc.CallOption) (*DebugInput, error) {
|
||||
out := new(DebugInput)
|
||||
err := c.cc.Invoke(ctx, "/proto.Bor/Debug", in, out, opts...)
|
||||
func (c *borClient) Pprof(ctx context.Context, in *PprofRequest, opts ...grpc.CallOption) (*PprofResponse, error) {
|
||||
out := new(PprofResponse)
|
||||
err := c.cc.Invoke(ctx, "/proto.Bor/Pprof", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ func (c *borClient) Debug(ctx context.Context, in *DebugInput, opts ...grpc.Call
|
|||
// All implementations must embed UnimplementedBorServer
|
||||
// for forward compatibility
|
||||
type BorServer interface {
|
||||
Debug(context.Context, *DebugInput) (*DebugInput, error)
|
||||
Pprof(context.Context, *PprofRequest) (*PprofResponse, error)
|
||||
mustEmbedUnimplementedBorServer()
|
||||
}
|
||||
|
||||
|
|
@ -50,8 +50,8 @@ type BorServer interface {
|
|||
type UnimplementedBorServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedBorServer) Debug(context.Context, *DebugInput) (*DebugInput, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Debug not implemented")
|
||||
func (UnimplementedBorServer) Pprof(context.Context, *PprofRequest) (*PprofResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Pprof not implemented")
|
||||
}
|
||||
func (UnimplementedBorServer) mustEmbedUnimplementedBorServer() {}
|
||||
|
||||
|
|
@ -66,20 +66,20 @@ func RegisterBorServer(s grpc.ServiceRegistrar, srv BorServer) {
|
|||
s.RegisterService(&Bor_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _Bor_Debug_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DebugInput)
|
||||
func _Bor_Pprof_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PprofRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BorServer).Debug(ctx, in)
|
||||
return srv.(BorServer).Pprof(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proto.Bor/Debug",
|
||||
FullMethod: "/proto.Bor/Pprof",
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
|
@ -92,8 +92,8 @@ var Bor_ServiceDesc = grpc.ServiceDesc{
|
|||
HandlerType: (*BorServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Debug",
|
||||
Handler: _Bor_Debug_Handler,
|
||||
MethodName: "Pprof",
|
||||
Handler: _Bor_Pprof_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
|
|
|
|||
|
|
@ -2,10 +2,32 @@ package server
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/ethereum/go-ethereum/command/server/pprof"
|
||||
"github.com/ethereum/go-ethereum/command/server/proto"
|
||||
)
|
||||
|
||||
func (s *Server) Debug(ctx context.Context, req *proto.DebugInput) (*proto.DebugInput, error) {
|
||||
return &proto.DebugInput{}, nil
|
||||
func (s *Server) Pprof(ctx context.Context, req *proto.PprofRequest) (*proto.PprofResponse, error) {
|
||||
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