diff --git a/.gitignore b/.gitignore
index 15ccd00ed0f77a816351c0e2d6b11d174a6855ac..a56d2523228d6ba8f6a6f0cb17e1d052eba159ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,3 +48,4 @@ profile.cov
 
 **/yarn-error.log
 ./test
+./bor-debug-*
diff --git a/command/debug.go b/command/debug.go
index c62f862a99013396d2370cf9c454cabc30d2c6aa..0168bb74a702422cd8fe32f7896e38ecd25797dc 100644
--- a/command/debug.go
+++ b/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
+	})
+}
diff --git a/command/main.go b/command/main.go
index 702fce9942bc25a3dd23fda543ff10a8429d9513..0fd38908304d1713a18c1d36b10229c1ef4eebed 100644
--- a/command/main.go
+++ b/command/main.go
@@ -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 {
diff --git a/command/server/pprof/pprof.go b/command/server/pprof/pprof.go
index 7b25f4374343065d1878f2e2f9601c86ed797fea..44034f3bb8ddb4ba4437ce1389775e775d241255 100644
--- a/command/server/pprof/pprof.go
+++ b/command/server/pprof/pprof.go
@@ -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():
+	}
+}
diff --git a/command/server/proto/server.pb.go b/command/server/proto/server.pb.go
index c2f6c316a3eb39e96802f3e2e6c1958f6f1f1717..87ace6509ae345ce07469b9495a45372a672c22f 100644
--- a/command/server/proto/server.pb.go
+++ b/command/server/proto/server.pb.go
@@ -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
diff --git a/command/server/proto/server.proto b/command/server/proto/server.proto
index 64972fa1551f564eaacf376127a6b8eb02aa4de0..2348e6dd6ae3b65fc611e7ab1455e411c885bfc9 100644
--- a/command/server/proto/server.proto
+++ b/command/server/proto/server.proto
@@ -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;
 }
diff --git a/command/server/proto/server_grpc.pb.go b/command/server/proto/server_grpc.pb.go
index e23aa7fda5932160d5cc2ad1dc09cf6c3e6ca1b7..1c101f5e85596005350b5d6aa3ff0cd0a2d04036 100644
--- a/command/server/proto/server_grpc.pb.go
+++ b/command/server/proto/server_grpc.pb.go
@@ -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{},
diff --git a/command/server/service.go b/command/server/service.go
index f6a41281b0f0c9f7fc549f9604a9442eb0484d8c..86ab38d5e2db8bd20510b244afa26bd1d6a7e9ef 100644
--- a/command/server/service.go
+++ b/command/server/service.go
@@ -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
 }