profiling: add proto and service (#3160)

This commit is contained in:
Adhityaa Chandrasekar
2019-12-19 08:54:38 -08:00
committed by Doug Fawley
parent fcf817f67c
commit d44b1112a2
5 changed files with 777 additions and 3 deletions

View File

@ -33,6 +33,7 @@
package profiling
import (
"errors"
"sync"
"sync/atomic"
"time"
@ -198,13 +199,15 @@ const defaultStreamStatsSize uint32 = 16 << 10
// client in its Tags).
var StreamStats *buffer.CircularBuffer
var errAlreadyInitialized = errors.New("profiling may be initialized at most once")
// InitStats initializes all the relevant Stat objects. Must be called exactly
// once per lifetime of a process; calls after the first one are ignored.
// once per lifetime of a process; calls after the first one will return an
// error.
func InitStats(streamStatsSize uint32) error {
var err error
if !atomic.CompareAndSwapInt32(&statsInitialized, 0, 1) {
// If initialized, do nothing.
return nil
return errAlreadyInitialized
}
if streamStatsSize == 0 {

37
profiling/profiling.go Normal file
View File

@ -0,0 +1,37 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package profiling exposes methods to manage profiling within gRPC.
//
// This package and all its methods are EXPERIMENTAL.
package profiling
import (
internal "google.golang.org/grpc/internal/profiling"
)
// Enable turns profiling on and off. This operation is safe for concurrent
// access from different goroutines.
//
// Note that this is the only operation that's accessible through the publicly
// exposed profiling package. Everything else (such as retrieving stats) must
// be done through the profiling service. This is allowed so that users can use
// heuristics to turn profiling on and off automatically.
func Enable(enabled bool) {
internal.Enable(enabled)
}

View File

@ -0,0 +1,490 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: service.proto
package proto
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// EnableRequest defines the fields in a /Profiling/Enable method request to
// toggle profiling on and off within a gRPC program.
type EnableRequest struct {
// Setting this to true will enable profiling. Setting this to false will
// disable profiling.
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *EnableRequest) Reset() { *m = EnableRequest{} }
func (m *EnableRequest) String() string { return proto.CompactTextString(m) }
func (*EnableRequest) ProtoMessage() {}
func (*EnableRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_a0b84a42fa06f626, []int{0}
}
func (m *EnableRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EnableRequest.Unmarshal(m, b)
}
func (m *EnableRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_EnableRequest.Marshal(b, m, deterministic)
}
func (m *EnableRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_EnableRequest.Merge(m, src)
}
func (m *EnableRequest) XXX_Size() int {
return xxx_messageInfo_EnableRequest.Size(m)
}
func (m *EnableRequest) XXX_DiscardUnknown() {
xxx_messageInfo_EnableRequest.DiscardUnknown(m)
}
var xxx_messageInfo_EnableRequest proto.InternalMessageInfo
func (m *EnableRequest) GetEnabled() bool {
if m != nil {
return m.Enabled
}
return false
}
// EnableResponse defines the fields in a /Profiling/Enable method response.
type EnableResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *EnableResponse) Reset() { *m = EnableResponse{} }
func (m *EnableResponse) String() string { return proto.CompactTextString(m) }
func (*EnableResponse) ProtoMessage() {}
func (*EnableResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_a0b84a42fa06f626, []int{1}
}
func (m *EnableResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EnableResponse.Unmarshal(m, b)
}
func (m *EnableResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_EnableResponse.Marshal(b, m, deterministic)
}
func (m *EnableResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_EnableResponse.Merge(m, src)
}
func (m *EnableResponse) XXX_Size() int {
return xxx_messageInfo_EnableResponse.Size(m)
}
func (m *EnableResponse) XXX_DiscardUnknown() {
xxx_messageInfo_EnableResponse.DiscardUnknown(m)
}
var xxx_messageInfo_EnableResponse proto.InternalMessageInfo
// GetStreamStatsRequest defines the fields in a /Profiling/GetStreamStats
// method request to retrieve stream-level stats in a gRPC client/server.
type GetStreamStatsRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetStreamStatsRequest) Reset() { *m = GetStreamStatsRequest{} }
func (m *GetStreamStatsRequest) String() string { return proto.CompactTextString(m) }
func (*GetStreamStatsRequest) ProtoMessage() {}
func (*GetStreamStatsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_a0b84a42fa06f626, []int{2}
}
func (m *GetStreamStatsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetStreamStatsRequest.Unmarshal(m, b)
}
func (m *GetStreamStatsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetStreamStatsRequest.Marshal(b, m, deterministic)
}
func (m *GetStreamStatsRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetStreamStatsRequest.Merge(m, src)
}
func (m *GetStreamStatsRequest) XXX_Size() int {
return xxx_messageInfo_GetStreamStatsRequest.Size(m)
}
func (m *GetStreamStatsRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GetStreamStatsRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GetStreamStatsRequest proto.InternalMessageInfo
// GetStreamStatsResponse defines the fields in a /Profiling/GetStreamStats
// method response.
type GetStreamStatsResponse struct {
StreamStats []*Stat `protobuf:"bytes,1,rep,name=stream_stats,json=streamStats,proto3" json:"stream_stats,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetStreamStatsResponse) Reset() { *m = GetStreamStatsResponse{} }
func (m *GetStreamStatsResponse) String() string { return proto.CompactTextString(m) }
func (*GetStreamStatsResponse) ProtoMessage() {}
func (*GetStreamStatsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_a0b84a42fa06f626, []int{3}
}
func (m *GetStreamStatsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetStreamStatsResponse.Unmarshal(m, b)
}
func (m *GetStreamStatsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetStreamStatsResponse.Marshal(b, m, deterministic)
}
func (m *GetStreamStatsResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetStreamStatsResponse.Merge(m, src)
}
func (m *GetStreamStatsResponse) XXX_Size() int {
return xxx_messageInfo_GetStreamStatsResponse.Size(m)
}
func (m *GetStreamStatsResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GetStreamStatsResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GetStreamStatsResponse proto.InternalMessageInfo
func (m *GetStreamStatsResponse) GetStreamStats() []*Stat {
if m != nil {
return m.StreamStats
}
return nil
}
// A Timer measures the start and end of execution of a component within
// gRPC that's being profiled. It includes a tag and some additional metadata
// to identify itself.
type Timer struct {
// tags is a comma-separated list of strings used to tag a timer.
Tags string `protobuf:"bytes,1,opt,name=tags,proto3" json:"tags,omitempty"`
// begin_sec and begin_nsec are the start epoch second and nanosecond,
// respectively, of the component profiled by this timer in UTC. begin_nsec
// must be a non-negative integer.
BeginSec int64 `protobuf:"varint,2,opt,name=begin_sec,json=beginSec,proto3" json:"begin_sec,omitempty"`
BeginNsec int32 `protobuf:"varint,3,opt,name=begin_nsec,json=beginNsec,proto3" json:"begin_nsec,omitempty"`
// end_sec and end_nsec are the end epoch second and nanosecond,
// respectively, of the component profiled by this timer in UTC. end_nsec
// must be a non-negative integer.
EndSec int64 `protobuf:"varint,4,opt,name=end_sec,json=endSec,proto3" json:"end_sec,omitempty"`
EndNsec int32 `protobuf:"varint,5,opt,name=end_nsec,json=endNsec,proto3" json:"end_nsec,omitempty"`
// go_id is the goroutine ID of the component being profiled.
GoId int64 `protobuf:"varint,6,opt,name=go_id,json=goId,proto3" json:"go_id,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Timer) Reset() { *m = Timer{} }
func (m *Timer) String() string { return proto.CompactTextString(m) }
func (*Timer) ProtoMessage() {}
func (*Timer) Descriptor() ([]byte, []int) {
return fileDescriptor_a0b84a42fa06f626, []int{4}
}
func (m *Timer) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Timer.Unmarshal(m, b)
}
func (m *Timer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Timer.Marshal(b, m, deterministic)
}
func (m *Timer) XXX_Merge(src proto.Message) {
xxx_messageInfo_Timer.Merge(m, src)
}
func (m *Timer) XXX_Size() int {
return xxx_messageInfo_Timer.Size(m)
}
func (m *Timer) XXX_DiscardUnknown() {
xxx_messageInfo_Timer.DiscardUnknown(m)
}
var xxx_messageInfo_Timer proto.InternalMessageInfo
func (m *Timer) GetTags() string {
if m != nil {
return m.Tags
}
return ""
}
func (m *Timer) GetBeginSec() int64 {
if m != nil {
return m.BeginSec
}
return 0
}
func (m *Timer) GetBeginNsec() int32 {
if m != nil {
return m.BeginNsec
}
return 0
}
func (m *Timer) GetEndSec() int64 {
if m != nil {
return m.EndSec
}
return 0
}
func (m *Timer) GetEndNsec() int32 {
if m != nil {
return m.EndNsec
}
return 0
}
func (m *Timer) GetGoId() int64 {
if m != nil {
return m.GoId
}
return 0
}
// A Stat is a collection of Timers along with some additional
// metadata to tag and identify itself.
type Stat struct {
// tags is a comma-separated list of strings used to categorize a stat.
Tags string `protobuf:"bytes,1,opt,name=tags,proto3" json:"tags,omitempty"`
// timers is an array of Timers, each representing a different
// (but possibly overlapping) component within this stat.
Timers []*Timer `protobuf:"bytes,2,rep,name=timers,proto3" json:"timers,omitempty"`
// metadata is an array of bytes used to uniquely identify a stat with an
// undefined encoding format. For example, the Stats returned by the
// /Profiling/GetStreamStats service use the metadata field to encode the
// connection ID and the stream ID of each query.
Metadata []byte `protobuf:"bytes,3,opt,name=metadata,proto3" json:"metadata,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Stat) Reset() { *m = Stat{} }
func (m *Stat) String() string { return proto.CompactTextString(m) }
func (*Stat) ProtoMessage() {}
func (*Stat) Descriptor() ([]byte, []int) {
return fileDescriptor_a0b84a42fa06f626, []int{5}
}
func (m *Stat) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Stat.Unmarshal(m, b)
}
func (m *Stat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Stat.Marshal(b, m, deterministic)
}
func (m *Stat) XXX_Merge(src proto.Message) {
xxx_messageInfo_Stat.Merge(m, src)
}
func (m *Stat) XXX_Size() int {
return xxx_messageInfo_Stat.Size(m)
}
func (m *Stat) XXX_DiscardUnknown() {
xxx_messageInfo_Stat.DiscardUnknown(m)
}
var xxx_messageInfo_Stat proto.InternalMessageInfo
func (m *Stat) GetTags() string {
if m != nil {
return m.Tags
}
return ""
}
func (m *Stat) GetTimers() []*Timer {
if m != nil {
return m.Timers
}
return nil
}
func (m *Stat) GetMetadata() []byte {
if m != nil {
return m.Metadata
}
return nil
}
func init() {
proto.RegisterType((*EnableRequest)(nil), "grpc.go.profiling.v1alpha.EnableRequest")
proto.RegisterType((*EnableResponse)(nil), "grpc.go.profiling.v1alpha.EnableResponse")
proto.RegisterType((*GetStreamStatsRequest)(nil), "grpc.go.profiling.v1alpha.GetStreamStatsRequest")
proto.RegisterType((*GetStreamStatsResponse)(nil), "grpc.go.profiling.v1alpha.GetStreamStatsResponse")
proto.RegisterType((*Timer)(nil), "grpc.go.profiling.v1alpha.Timer")
proto.RegisterType((*Stat)(nil), "grpc.go.profiling.v1alpha.Stat")
}
func init() { proto.RegisterFile("service.proto", fileDescriptor_a0b84a42fa06f626) }
var fileDescriptor_a0b84a42fa06f626 = []byte{
// 388 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0x4f, 0xcf, 0xd2, 0x40,
0x10, 0xc6, 0x53, 0x68, 0x6b, 0x19, 0xfe, 0xc4, 0xac, 0x51, 0x0a, 0xc6, 0xd8, 0xf4, 0x60, 0xca,
0xa5, 0x08, 0x5e, 0x3c, 0x93, 0x18, 0xe3, 0xc5, 0x98, 0xc5, 0x93, 0xd1, 0x90, 0xa5, 0x1d, 0xd7,
0x26, 0xa5, 0x5b, 0xbb, 0x0b, 0x9f, 0xc7, 0xaf, 0xe6, 0x37, 0x31, 0x3b, 0x05, 0x0c, 0x6f, 0x78,
0xc9, 0xfb, 0x9e, 0x60, 0x66, 0x9e, 0xdf, 0xd3, 0x67, 0x32, 0x0b, 0x43, 0x8d, 0xcd, 0xa1, 0xc8,
0x30, 0xad, 0x1b, 0x65, 0x14, 0x9b, 0xc8, 0xa6, 0xce, 0x52, 0xa9, 0x6c, 0xf9, 0xb3, 0x28, 0x8b,
0x4a, 0xa6, 0x87, 0x85, 0x28, 0xeb, 0x5f, 0x22, 0x9e, 0xc1, 0xf0, 0x43, 0x25, 0xb6, 0x25, 0x72,
0xfc, 0xbd, 0x47, 0x6d, 0x58, 0x08, 0x4f, 0x90, 0x1a, 0x79, 0xe8, 0x44, 0x4e, 0x12, 0xf0, 0x53,
0x19, 0x3f, 0x85, 0xd1, 0x49, 0xaa, 0x6b, 0x55, 0x69, 0x8c, 0xc7, 0xf0, 0xfc, 0x23, 0x9a, 0xb5,
0x69, 0x50, 0xec, 0xd6, 0x46, 0x18, 0x7d, 0x34, 0x89, 0xbf, 0xc3, 0x8b, 0xbb, 0x83, 0x16, 0x61,
0x2b, 0x18, 0x68, 0x6a, 0x6f, 0xb4, 0xed, 0x87, 0x4e, 0xd4, 0x4d, 0xfa, 0xcb, 0xd7, 0xe9, 0xbd,
0x09, 0x53, 0xcb, 0xf3, 0xbe, 0xfe, 0xef, 0x15, 0xff, 0x71, 0xc0, 0xfb, 0x5a, 0xec, 0xb0, 0x61,
0x0c, 0x5c, 0x23, 0xa4, 0xa6, 0xa4, 0x3d, 0x4e, 0xff, 0xd9, 0x4b, 0xe8, 0x6d, 0x51, 0x16, 0xd5,
0x46, 0x63, 0x16, 0x76, 0x22, 0x27, 0xe9, 0xf2, 0x80, 0x1a, 0x6b, 0xcc, 0xd8, 0x2b, 0x80, 0x76,
0x58, 0xd9, 0x69, 0x37, 0x72, 0x12, 0x8f, 0xb7, 0xf2, 0xcf, 0x1a, 0x33, 0x36, 0xb6, 0xcb, 0xe7,
0x44, 0xba, 0x44, 0xfa, 0x58, 0xe5, 0x96, 0x9b, 0x40, 0x60, 0x07, 0x44, 0x79, 0x44, 0x59, 0x21,
0x31, 0xcf, 0xc0, 0x93, 0x6a, 0x53, 0xe4, 0xa1, 0x4f, 0x84, 0x2b, 0xd5, 0xa7, 0x3c, 0xae, 0xc1,
0xb5, 0x59, 0xaf, 0x06, 0x7c, 0x0f, 0xbe, 0xb1, 0xe9, 0x75, 0xd8, 0xa1, 0xe5, 0xa3, 0x1b, 0xcb,
0xd3, 0x9a, 0xfc, 0xa8, 0x67, 0x53, 0x08, 0x76, 0x68, 0x44, 0x2e, 0x8c, 0xa0, 0xec, 0x03, 0x7e,
0xae, 0x97, 0x7f, 0x1d, 0xe8, 0x7d, 0x39, 0xf1, 0xec, 0x07, 0xf8, 0xed, 0xad, 0x58, 0x72, 0xc3,
0xfd, 0xe2, 0xf2, 0xd3, 0xd9, 0x03, 0x94, 0xc7, 0x2b, 0xee, 0x61, 0x74, 0x79, 0x5f, 0xf6, 0xf6,
0x06, 0x7c, 0xf5, 0x8d, 0x4c, 0x17, 0x8f, 0x20, 0xda, 0xcf, 0xae, 0x92, 0x6f, 0x6f, 0xa4, 0x52,
0xb2, 0xc4, 0x54, 0xaa, 0x52, 0x54, 0x32, 0x55, 0x8d, 0x9c, 0x5b, 0x97, 0xf9, 0xd9, 0x62, 0x4e,
0x2f, 0x7e, 0xeb, 0xd3, 0xcf, 0xbb, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x13, 0x63, 0x69, 0xce,
0x09, 0x03, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// ProfilingClient is the client API for Profiling service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type ProfilingClient interface {
// Enable allows users to toggle profiling on and off remotely.
Enable(ctx context.Context, in *EnableRequest, opts ...grpc.CallOption) (*EnableResponse, error)
// GetStreamStats is used to retrieve an array of stream-level stats from a
// gRPC client/server.
GetStreamStats(ctx context.Context, in *GetStreamStatsRequest, opts ...grpc.CallOption) (*GetStreamStatsResponse, error)
}
type profilingClient struct {
cc *grpc.ClientConn
}
func NewProfilingClient(cc *grpc.ClientConn) ProfilingClient {
return &profilingClient{cc}
}
func (c *profilingClient) Enable(ctx context.Context, in *EnableRequest, opts ...grpc.CallOption) (*EnableResponse, error) {
out := new(EnableResponse)
err := c.cc.Invoke(ctx, "/grpc.go.profiling.v1alpha.Profiling/Enable", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *profilingClient) GetStreamStats(ctx context.Context, in *GetStreamStatsRequest, opts ...grpc.CallOption) (*GetStreamStatsResponse, error) {
out := new(GetStreamStatsResponse)
err := c.cc.Invoke(ctx, "/grpc.go.profiling.v1alpha.Profiling/GetStreamStats", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// ProfilingServer is the server API for Profiling service.
type ProfilingServer interface {
// Enable allows users to toggle profiling on and off remotely.
Enable(context.Context, *EnableRequest) (*EnableResponse, error)
// GetStreamStats is used to retrieve an array of stream-level stats from a
// gRPC client/server.
GetStreamStats(context.Context, *GetStreamStatsRequest) (*GetStreamStatsResponse, error)
}
// UnimplementedProfilingServer can be embedded to have forward compatible implementations.
type UnimplementedProfilingServer struct {
}
func (*UnimplementedProfilingServer) Enable(ctx context.Context, req *EnableRequest) (*EnableResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Enable not implemented")
}
func (*UnimplementedProfilingServer) GetStreamStats(ctx context.Context, req *GetStreamStatsRequest) (*GetStreamStatsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetStreamStats not implemented")
}
func RegisterProfilingServer(s *grpc.Server, srv ProfilingServer) {
s.RegisterService(&_Profiling_serviceDesc, srv)
}
func _Profiling_Enable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(EnableRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProfilingServer).Enable(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.go.profiling.v1alpha.Profiling/Enable",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProfilingServer).Enable(ctx, req.(*EnableRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Profiling_GetStreamStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetStreamStatsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProfilingServer).GetStreamStats(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.go.profiling.v1alpha.Profiling/GetStreamStats",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProfilingServer).GetStreamStats(ctx, req.(*GetStreamStatsRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Profiling_serviceDesc = grpc.ServiceDesc{
ServiceName: "grpc.go.profiling.v1alpha.Profiling",
HandlerType: (*ProfilingServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Enable",
Handler: _Profiling_Enable_Handler,
},
{
MethodName: "GetStreamStats",
Handler: _Profiling_GetStreamStats_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "service.proto",
}

View File

@ -0,0 +1,98 @@
// Copyright 2019 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package grpc.go.profiling.v1alpha;
// This package defines the proto messages and RPC services exposed by gRPC for
// profiling management. A reference client implementation to interact with
// this service is provided as a command-line application. This service can be
// used to toggle profiling on and off and retrieve stats from a gRPC
// application.
option go_package = "google.golang.org/grpc/profiling/proto";
// EnableRequest defines the fields in a /Profiling/Enable method request to
// toggle profiling on and off within a gRPC program.
message EnableRequest {
// Setting this to true will enable profiling. Setting this to false will
// disable profiling.
bool enabled = 1;
}
// EnableResponse defines the fields in a /Profiling/Enable method response.
message EnableResponse {
}
// GetStreamStatsRequest defines the fields in a /Profiling/GetStreamStats
// method request to retrieve stream-level stats in a gRPC client/server.
message GetStreamStatsRequest {
}
// GetStreamStatsResponse defines the fields in a /Profiling/GetStreamStats
// method response.
message GetStreamStatsResponse {
repeated Stat stream_stats = 1;
}
// A Timer measures the start and end of execution of a component within
// gRPC that's being profiled. It includes a tag and some additional metadata
// to identify itself.
message Timer {
// tags is a comma-separated list of strings used to tag a timer.
string tags = 1;
// begin_sec and begin_nsec are the start epoch second and nanosecond,
// respectively, of the component profiled by this timer in UTC. begin_nsec
// must be a non-negative integer.
int64 begin_sec = 2;
int32 begin_nsec = 3;
// end_sec and end_nsec are the end epoch second and nanosecond,
// respectively, of the component profiled by this timer in UTC. end_nsec
// must be a non-negative integer.
int64 end_sec = 4;
int32 end_nsec = 5;
// go_id is the goroutine ID of the component being profiled.
int64 go_id = 6;
}
// A Stat is a collection of Timers along with some additional
// metadata to tag and identify itself.
message Stat {
// tags is a comma-separated list of strings used to categorize a stat.
string tags = 1;
// timers is an array of Timers, each representing a different
// (but possibly overlapping) component within this stat.
repeated Timer timers = 2;
// metadata is an array of bytes used to uniquely identify a stat with an
// undefined encoding format. For example, the Stats returned by the
// /Profiling/GetStreamStats service use the metadata field to encode the
// connection ID and the stream ID of each query.
bytes metadata = 3;
}
// The Profiling service exposes functions to remotely manage the gRPC
// profiling behaviour in a program.
service Profiling {
// Enable allows users to toggle profiling on and off remotely.
rpc Enable (EnableRequest) returns (EnableResponse);
// GetStreamStats is used to retrieve an array of stream-level stats from a
// gRPC client/server.
rpc GetStreamStats (GetStreamStatsRequest) returns (GetStreamStatsResponse);
}

View File

@ -0,0 +1,146 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package service defines methods to register a gRPC client/service for a
// profiling service that is exposed in the same server. This service can be
// queried by a client to remotely manage the gRPC profiling behaviour of an
// application.
//
// This package and all its methods are EXPERIMENTAL.
package service
//go:generate protoc --go_out=plugins=grpc,paths=source_relative:../proto -I../proto service.proto
import (
"context"
"errors"
"sync"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/profiling"
ppb "google.golang.org/grpc/profiling/proto"
)
// ProfilingConfig defines configuration options for the Init method.
type ProfilingConfig struct {
// Setting this to true will enable profiling.
Enabled bool
// Profiling uses a circular buffer (ring buffer) to store statistics for
// only the last few RPCs so that profiling stats do not grow unbounded. This
// parameter defines the upper limit on the number of RPCs for which
// statistics should be stored at any given time. An average RPC requires
// approximately 2-3 KiB of memory for profiling-related statistics, so
// choose an appropriate number based on the amount of memory you can afford.
StreamStatsSize uint32
// To expose the profiling service and its methods, a *grpc.Server must be
// provided.
Server *grpc.Server
}
var errorNilServer = errors.New("profiling: no grpc.Server provided")
// Init takes a *ProfilingConfig to initialize profiling (turned on/off
// depending on the value set in pc.Enabled) and register the profiling service
// in the server provided in pc.Server.
func Init(pc *ProfilingConfig) error {
if pc.Server == nil {
return errorNilServer
}
if err := profiling.InitStats(pc.StreamStatsSize); err != nil {
return err
}
ppb.RegisterProfilingServer(pc.Server, getProfilingServerInstance())
// Do this last after everything has been initialized and allocated.
profiling.Enable(pc.Enabled)
return nil
}
type profilingServer struct {
drainMutex sync.Mutex
}
var profilingServerInstance *profilingServer
var profilingServerOnce sync.Once
// getProfilingServerInstance creates and returns a singleton instance of
// profilingServer. Only one instance of profilingServer is created to use a
// shared mutex across all profilingServer instances.
func getProfilingServerInstance() *profilingServer {
profilingServerOnce.Do(func() {
profilingServerInstance = &profilingServer{}
})
return profilingServerInstance
}
func (s *profilingServer) Enable(ctx context.Context, req *ppb.EnableRequest) (*ppb.EnableResponse, error) {
if req.Enabled {
grpclog.Infof("profilingServer: Enable: enabling profiling")
} else {
grpclog.Infof("profilingServer: Enable: disabling profiling")
}
profiling.Enable(req.Enabled)
return &ppb.EnableResponse{}, nil
}
func timerToProtoTimer(timer *profiling.Timer) *ppb.Timer {
return &ppb.Timer{
Tags: timer.Tags,
BeginSec: timer.Begin.Unix(),
BeginNsec: int32(timer.Begin.Nanosecond()),
EndSec: timer.End.Unix(),
EndNsec: int32(timer.End.Nanosecond()),
GoId: timer.GoID,
}
}
func statToProtoStat(stat *profiling.Stat) *ppb.Stat {
protoStat := &ppb.Stat{
Tags: stat.Tags,
Timers: make([]*ppb.Timer, 0, len(stat.Timers)),
Metadata: stat.Metadata,
}
for _, t := range stat.Timers {
protoStat.Timers = append(protoStat.Timers, timerToProtoTimer(t))
}
return protoStat
}
func (s *profilingServer) GetStreamStats(ctx context.Context, req *ppb.GetStreamStatsRequest) (*ppb.GetStreamStatsResponse, error) {
// Since the drain operation is destructive, only one client request should
// be served at a time.
grpclog.Infof("profilingServer: GetStreamStats: processing request")
s.drainMutex.Lock()
results := profiling.StreamStats.Drain()
s.drainMutex.Unlock()
grpclog.Infof("profilingServer: GetStreamStats: returning %v records", len(results))
streamStats := make([]*ppb.Stat, 0)
for _, stat := range results {
streamStats = append(streamStats, statToProtoStat(stat.(*profiling.Stat)))
}
return &ppb.GetStreamStatsResponse{StreamStats: streamStats}, nil
}