Skip to content

Commit

Permalink
profiler: add profiler command
Browse files Browse the repository at this point in the history
    We add the profiler command as below:

    - profiler show/enable/disable
      Users could use the above command to enable profiler runtime
      through gRPC on engine or replica side.

    - Also, enable could use --port to specific the port number for
      profiler server. The port number should bigger than 30000.

Signed-off-by: Vicente Cheng <[email protected]>
  • Loading branch information
Vicente-Cheng authored and David Ko committed Jan 5, 2024
1 parent 358264d commit b41e810
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 0 deletions.
161 changes: 161 additions & 0 deletions app/cmd/profiler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package cmd

import (
"fmt"
"strconv"
"strings"

"github.com/longhorn/go-common-libs/profiler"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"google.golang.org/grpc"

"github.com/longhorn/longhorn-engine/proto/ptypes"
)

const (
opShow = "SHOW"
opEnable = "ENABLE"
opDisable = "DISABLE"

// ProfilerPortBase is the base number that we used to generate the profiler server port.
// We reserve the port 10000 - 30000 for engine v1/v2.
// We need to avoid to use them, so 20001 is big enough of the above interval.
profilerPortBase = 20001
)

func ProfilerCmd() cli.Command {
return cli.Command{
Name: "profiler",
Subcommands: []cli.Command{
ProfilerShowCmd(),
ProfilerEnableCmd(),
ProfilerDisableCmd(),
},
}
}

func getProfilerClient(c *cli.Context) (*profiler.Client, error) {
url := c.GlobalString("url")
volumeName := c.GlobalString("volume-name")
engineInstanceName := c.GlobalString("engine-instance-name")
dialOpts := []grpc.DialOption{ptypes.WithIdentityValidationClientInterceptor(volumeName, engineInstanceName)}
return profiler.NewClient(url, volumeName, dialOpts...)
}

func ProfilerShowCmd() cli.Command {
return cli.Command{
Name: "show",
Action: func(c *cli.Context) {
if err := showProfiler(c); err != nil {
logrus.WithError(err).Fatalf("Error running profiler show")
}
},
}
}

func ProfilerEnableCmd() cli.Command {
return cli.Command{
Name: "enable",
Flags: []cli.Flag{
cli.IntFlag{
Name: "port",
Hidden: false,
Required: false,
Value: 0,
Usage: "run profiler with specific port. The port number should bigger than 30000.",
},
},
Action: func(c *cli.Context) {
if err := enableProfiler(c); err != nil {
logrus.WithError(err).Fatalf("Error running profiler enable")
}
},
}
}

func ProfilerDisableCmd() cli.Command {
return cli.Command{
Name: "disable",
Action: func(c *cli.Context) {
if err := disableProfiler(c); err != nil {
logrus.WithError(err).Fatalf("Error running profiler disable")
}
},
}
}

func showProfiler(c *cli.Context) error {
client, err := getProfilerClient(c)
if err != nil {
return err
}
defer client.Close()

profilerAddr, err := client.ProfilerOP(opShow, 0)
if err != nil {
fmt.Printf("Failed to show profiler: %v", err)
return err
}

if profilerAddr == "" {
fmt.Println("Profiler is not enabled")
} else {
fmt.Printf("Profiler enabled at Addr: *%v\n", profilerAddr)
}
return nil
}

func enableProfiler(c *cli.Context) error {
portNumber := int32(c.Int("port"))
if err := validatePortNumber(portNumber); err != nil {
return err
}

// get grpc server port as a base port number and add profilerPortBase as target port number
if portNumber == 0 {
grpcURL := c.GlobalString("url")
grpcPort, err := strconv.Atoi(strings.Split(grpcURL, ":")[1])
if err != nil {
return fmt.Errorf("failed to get grpc server port")
}
portNumber = int32(grpcPort) + profilerPortBase
}

client, err := getProfilerClient(c)
if err != nil {
return err
}
defer client.Close()

profilerAddr, err := client.ProfilerOP(opEnable, portNumber)
if err != nil {
fmt.Printf("Failed to enable profiler: %v", err)
return err
}
fmt.Printf("Profiler enabled at Addr: *%v\n", profilerAddr)
return nil
}

func disableProfiler(c *cli.Context) error {
client, err := getProfilerClient(c)
if err != nil {
return err
}
defer client.Close()

_, err = client.ProfilerOP(opDisable, 0)
if err != nil {
fmt.Printf("Failed to disable profiler: %v", err)
return err
}
fmt.Println("Profiler is disabled!")
return nil
}

func validatePortNumber(portNumber int32) error {
if portNumber > 0 && portNumber < 30000 {
return fmt.Errorf("port number should be bigger than 30000. The reserved port number is [10000 - 30000]")
}
return nil
}
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ func longhornCli() {
cmd.InfoCmd(),
cmd.FrontendCmd(),
cmd.SystemBackupCmd(),
cmd.ProfilerCmd(),
VersionCmd(),
}
a.CommandNotFound = cmdNotFound
Expand Down
4 changes: 4 additions & 0 deletions pkg/controller/rpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import (
"github.com/longhorn/longhorn-engine/pkg/controller"
"github.com/longhorn/longhorn-engine/pkg/types"
"github.com/longhorn/longhorn-engine/proto/ptypes"

"github.com/longhorn/go-common-libs/generated/profilerpb"
"github.com/longhorn/go-common-libs/profiler"
)

const (
Expand Down Expand Up @@ -48,6 +51,7 @@ func GetControllerGRPCServer(volumeName, instanceName string, c *controller.Cont
ptypes.RegisterControllerServiceServer(server, cs)
healthpb.RegisterHealthServer(server, NewControllerHealthCheckServer(cs))
reflection.Register(server)
profilerpb.RegisterProfilerServer(server, profiler.NewServer(volumeName))
return server
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/replica/rpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import (
"github.com/longhorn/longhorn-engine/pkg/replica"
"github.com/longhorn/longhorn-engine/pkg/types"
"github.com/longhorn/longhorn-engine/proto/ptypes"

"github.com/longhorn/go-common-libs/generated/profilerpb"
"github.com/longhorn/go-common-libs/profiler"
)

type ReplicaServer struct {
Expand All @@ -31,6 +34,7 @@ func NewReplicaServer(volumeName, instanceName string, s *replica.Server) *grpc.
ptypes.RegisterReplicaServiceServer(server, rs)
healthpb.RegisterHealthServer(server, NewReplicaHealthCheckServer(rs))
reflection.Register(server)
profilerpb.RegisterProfilerServer(server, profiler.NewServer(volumeName))
return server
}

Expand Down

0 comments on commit b41e810

Please sign in to comment.