github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/internal/profiling/profiling.go (about) 1 /* 2 * 3 * Copyright 2019 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 // Package profiling contains two logical components: buffer.go and 20 // profiling.go. The former implements a circular buffer (a.k.a. ring buffer) 21 // in a lock-free manner using atomics. This ring buffer is used by 22 // profiling.go to store various statistics. For example, StreamStats is a 23 // circular buffer of Stat objects, each of which is comprised of Timers. 24 // 25 // This abstraction is designed to accommodate more stats in the future; for 26 // example, if one wants to profile the load balancing layer, which is 27 // independent of RPC queries, a separate CircularBuffer can be used. 28 // 29 // Note that the circular buffer simply takes any interface{}. In the future, 30 // more types of measurements (such as the number of memory allocations) could 31 // be measured, which might require a different type of object being pushed 32 // into the circular buffer. 33 package profiling 34 35 import ( 36 "errors" 37 "sync" 38 "sync/atomic" 39 "time" 40 41 "github.com/hxx258456/ccgo/grpc/internal/profiling/buffer" 42 ) 43 44 // 0 or 1 representing profiling off and on, respectively. Use IsEnabled and 45 // Enable to get and set this in a safe manner. 46 var profilingEnabled uint32 47 48 // IsEnabled returns whether or not profiling is enabled. 49 func IsEnabled() bool { 50 return atomic.LoadUint32(&profilingEnabled) > 0 51 } 52 53 // Enable turns profiling on and off. 54 // 55 // Note that it is impossible to enable profiling for one server and leave it 56 // turned off for another. This is intentional and by design -- if the status 57 // of profiling was server-specific, clients wouldn't be able to profile 58 // themselves. As a result, Enable turns profiling on and off for all servers 59 // and clients in the binary. Each stat will be, however, tagged with whether 60 // it's a client stat or a server stat; so you should be able to filter for the 61 // right type of stats in post-processing. 62 func Enable(enabled bool) { 63 if enabled { 64 atomic.StoreUint32(&profilingEnabled, 1) 65 } else { 66 atomic.StoreUint32(&profilingEnabled, 0) 67 } 68 } 69 70 // A Timer represents the wall-clock beginning and ending of a logical 71 // operation. 72 type Timer struct { 73 // Tags is a comma-separated list of strings (usually forward-slash-separated 74 // hierarchical strings) used to categorize a Timer. 75 Tags string 76 // Begin marks the beginning of this timer. The timezone is unspecified, but 77 // must use the same timezone as End; this is so shave off the small, but 78 // non-zero time required to convert to a standard timezone such as UTC. 79 Begin time.Time 80 // End marks the end of a timer. 81 End time.Time 82 // Each Timer must be started and ended within the same goroutine; GoID 83 // captures this goroutine ID. The Go runtime does not typically expose this 84 // information, so this is set to zero in the typical case. However, a 85 // trivial patch to the runtime package can make this field useful. See 86 // goid_modified.go in this package for more details. 87 GoID int64 88 } 89 90 // NewTimer creates and returns a new Timer object. This is useful when you 91 // don't already have a Stat object to associate this Timer with; for example, 92 // before the context of a new RPC query is created, a Timer may be needed to 93 // measure transport-related operations. 94 // 95 // Use AppendTimer to append the returned Timer to a Stat. 96 func NewTimer(tags string) *Timer { 97 return &Timer{ 98 Tags: tags, 99 Begin: time.Now(), 100 GoID: goid(), 101 } 102 } 103 104 // Egress sets the End field of a timer to the current time. 105 func (timer *Timer) Egress() { 106 if timer == nil { 107 return 108 } 109 110 timer.End = time.Now() 111 } 112 113 // A Stat is a collection of Timers that represent timing information for 114 // different components within this Stat. For example, a Stat may be used to 115 // reference the entire lifetime of an RPC request, with Timers within it 116 // representing different components such as encoding, compression, and 117 // transport. 118 // 119 // The user is expected to use the included helper functions to do operations 120 // on the Stat such as creating or appending a new timer. Direct operations on 121 // the Stat's exported fields (which are exported for encoding reasons) may 122 // lead to data races. 123 type Stat struct { 124 // Tags is a comma-separated list of strings used to categorize a Stat. 125 Tags string 126 // Stats may also need to store other unstructured information specific to 127 // this stat. For example, a StreamStat will use these bytes to encode the 128 // connection ID and stream ID for each RPC to uniquely identify it. The 129 // encoding that must be used is unspecified. 130 Metadata []byte 131 // A collection of *Timers and a mutex for append operations on the slice. 132 mu sync.Mutex 133 Timers []*Timer 134 } 135 136 // A power of two that's large enough to hold all timers within an average RPC 137 // request (defined to be a unary request) without any reallocation. A typical 138 // unary RPC creates 80-100 timers for various things. While this number is 139 // purely anecdotal and may change in the future as the resolution of profiling 140 // increases or decreases, it serves as a good estimate for what the initial 141 // allocation size should be. 142 const defaultStatAllocatedTimers int32 = 128 143 144 // NewStat creates and returns a new Stat object. 145 func NewStat(tags string) *Stat { 146 return &Stat{ 147 Tags: tags, 148 Timers: make([]*Timer, 0, defaultStatAllocatedTimers), 149 } 150 } 151 152 // NewTimer creates a Timer object within the given stat if stat is non-nil. 153 // The value passed in tags will be attached to the newly created Timer. 154 // NewTimer also automatically sets the Begin value of the Timer to the current 155 // time. The user is expected to call stat.Egress with the returned index as 156 // argument to mark the end. 157 func (stat *Stat) NewTimer(tags string) *Timer { 158 if stat == nil { 159 return nil 160 } 161 162 timer := &Timer{ 163 Tags: tags, 164 GoID: goid(), 165 Begin: time.Now(), 166 } 167 stat.mu.Lock() 168 stat.Timers = append(stat.Timers, timer) 169 stat.mu.Unlock() 170 return timer 171 } 172 173 // AppendTimer appends a given Timer object to the internal slice of timers. A 174 // deep copy of the timer is made (i.e. no reference is retained to this 175 // pointer) and the user is expected to lose their reference to the timer to 176 // allow the Timer object to be garbage collected. 177 func (stat *Stat) AppendTimer(timer *Timer) { 178 if stat == nil || timer == nil { 179 return 180 } 181 182 stat.mu.Lock() 183 stat.Timers = append(stat.Timers, timer) 184 stat.mu.Unlock() 185 } 186 187 // statsInitialized is 0 before InitStats has been called. Changed to 1 by 188 // exactly one call to InitStats. 189 var statsInitialized int32 190 191 // Stats for the last defaultStreamStatsBufsize RPCs will be stored in memory. 192 // This is can be configured by the registering server at profiling service 193 // initialization with google.golang.org/grpc/profiling/service.ProfilingConfig 194 const defaultStreamStatsSize uint32 = 16 << 10 195 196 // StreamStats is a CircularBuffer containing data from the last N RPC calls 197 // served, where N is set by the user. This will contain both server stats and 198 // client stats (but each stat will be tagged with whether it's a server or a 199 // client in its Tags). 200 var StreamStats *buffer.CircularBuffer 201 202 var errAlreadyInitialized = errors.New("profiling may be initialized at most once") 203 204 // InitStats initializes all the relevant Stat objects. Must be called exactly 205 // once per lifetime of a process; calls after the first one will return an 206 // error. 207 func InitStats(streamStatsSize uint32) error { 208 var err error 209 if !atomic.CompareAndSwapInt32(&statsInitialized, 0, 1) { 210 return errAlreadyInitialized 211 } 212 213 if streamStatsSize == 0 { 214 streamStatsSize = defaultStreamStatsSize 215 } 216 217 StreamStats, err = buffer.NewCircularBuffer(streamStatsSize) 218 if err != nil { 219 return err 220 } 221 222 return nil 223 }