Add grpc server + debug endpoint

This commit is contained in:
Ferran Borreguero 2021-10-18 14:24:46 +02:00
parent 7282cf9c25
commit 2c9b2c31dc
8 changed files with 504 additions and 48 deletions

1
.gitignore vendored
View file

@ -48,3 +48,4 @@ profile.cov
**/yarn-error.log
./test
./bor-debug-*

View file

@ -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
})
}

View file

@ -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 {

View file

@ -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():
}
}

View file

@ -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

View 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;
}

View file

@ -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{},

View file

@ -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
}