gitee.com/zhaochuninhefei/gmgo@v0.0.31-0.20240209061119-069254a02979/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 "gitee.com/zhaochuninhefei/gmgo/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 //goland:noinspection GoUnusedExportedFunction 50 func IsEnabled() bool { 51 return atomic.LoadUint32(&profilingEnabled) > 0 52 } 53 54 // Enable turns profiling on and off. 55 // 56 // Note that it is impossible to enable profiling for one server and leave it 57 // turned off for another. This is intentional and by design -- if the status 58 // of profiling was server-specific, clients wouldn't be able to profile 59 // themselves. As a result, Enable turns profiling on and off for all servers 60 // and clients in the binary. Each stat will be, however, tagged with whether 61 // it's a client stat or a server stat; so you should be able to filter for the 62 // right type of stats in post-processing. 63 func Enable(enabled bool) { 64 if enabled { 65 atomic.StoreUint32(&profilingEnabled, 1) 66 } else { 67 atomic.StoreUint32(&profilingEnabled, 0) 68 } 69 } 70 71 // A Timer represents the wall-clock beginning and ending of a logical 72 // operation. 73 type Timer struct { 74 // Tags is a comma-separated list of strings (usually forward-slash-separated 75 // hierarchical strings) used to categorize a Timer. 76 Tags string 77 // Begin marks the beginning of this timer. The timezone is unspecified, but 78 // must use the same timezone as End; this is so shave off the small, but 79 // non-zero time required to convert to a standard timezone such as UTC. 80 Begin time.Time 81 // End marks the end of a timer. 82 End time.Time 83 // Each Timer must be started and ended within the same goroutine; GoID 84 // captures this goroutine ID. The Go runtime does not typically expose this 85 // information, so this is set to zero in the typical case. However, a 86 // trivial patch to the runtime package can make this field useful. See 87 // goid_modified.go in this package for more details. 88 GoID int64 89 } 90 91 // NewTimer creates and returns a new Timer object. This is useful when you 92 // don't already have a Stat object to associate this Timer with; for example, 93 // before the context of a new RPC query is created, a Timer may be needed to 94 // measure transport-related operations. 95 // 96 // Use AppendTimer to append the returned Timer to a Stat. 97 func NewTimer(tags string) *Timer { 98 return &Timer{ 99 Tags: tags, 100 Begin: time.Now(), 101 GoID: goid(), 102 } 103 } 104 105 // Egress sets the End field of a timer to the current time. 106 func (timer *Timer) Egress() { 107 if timer == nil { 108 return 109 } 110 111 timer.End = time.Now() 112 } 113 114 // A Stat is a collection of Timers that represent timing information for 115 // different components within this Stat. For example, a Stat may be used to 116 // reference the entire lifetime of an RPC request, with Timers within it 117 // representing different components such as encoding, compression, and 118 // transport. 119 // 120 // The user is expected to use the included helper functions to do operations 121 // on the Stat such as creating or appending a new timer. Direct operations on 122 // the Stat's exported fields (which are exported for encoding reasons) may 123 // lead to data races. 124 type Stat struct { 125 // Tags is a comma-separated list of strings used to categorize a Stat. 126 Tags string 127 // Stats may also need to store other unstructured information specific to 128 // this stat. For example, a StreamStat will use these bytes to encode the 129 // connection ID and stream ID for each RPC to uniquely identify it. The 130 // encoding that must be used is unspecified. 131 Metadata []byte 132 // A collection of *Timers and a mutex for append operations on the slice. 133 mu sync.Mutex 134 Timers []*Timer 135 } 136 137 // A power of two that's large enough to hold all timers within an average RPC 138 // request (defined to be a unary request) without any reallocation. A typical 139 // unary RPC creates 80-100 timers for various things. While this number is 140 // purely anecdotal and may change in the future as the resolution of profiling 141 // increases or decreases, it serves as a good estimate for what the initial 142 // allocation size should be. 143 const defaultStatAllocatedTimers int32 = 128 144 145 // NewStat creates and returns a new Stat object. 146 func NewStat(tags string) *Stat { 147 return &Stat{ 148 Tags: tags, 149 Timers: make([]*Timer, 0, defaultStatAllocatedTimers), 150 } 151 } 152 153 // NewTimer creates a Timer object within the given stat if stat is non-nil. 154 // The value passed in tags will be attached to the newly created Timer. 155 // NewTimer also automatically sets the Begin value of the Timer to the current 156 // time. The user is expected to call stat.Egress with the returned index as 157 // argument to mark the end. 158 func (stat *Stat) NewTimer(tags string) *Timer { 159 if stat == nil { 160 return nil 161 } 162 163 timer := &Timer{ 164 Tags: tags, 165 GoID: goid(), 166 Begin: time.Now(), 167 } 168 stat.mu.Lock() 169 stat.Timers = append(stat.Timers, timer) 170 stat.mu.Unlock() 171 return timer 172 } 173 174 // AppendTimer appends a given Timer object to the internal slice of timers. A 175 // deep copy of the timer is made (i.e. no reference is retained to this 176 // pointer) and the user is expected to lose their reference to the timer to 177 // allow the Timer object to be garbage collected. 178 func (stat *Stat) AppendTimer(timer *Timer) { 179 if stat == nil || timer == nil { 180 return 181 } 182 183 stat.mu.Lock() 184 stat.Timers = append(stat.Timers, timer) 185 stat.mu.Unlock() 186 } 187 188 // statsInitialized is 0 before InitStats has been called. Changed to 1 by 189 // exactly one call to InitStats. 190 var statsInitialized int32 191 192 // Stats for the last defaultStreamStatsBufsize RPCs will be stored in memory. 193 // This is can be configured by the registering server at profiling service 194 // initialization with google.golang.org/grpc/profiling/service.ProfilingConfig 195 const defaultStreamStatsSize uint32 = 16 << 10 196 197 // StreamStats is a CircularBuffer containing data from the last N RPC calls 198 // served, where N is set by the user. This will contain both server stats and 199 // client stats (but each stat will be tagged with whether it's a server or a 200 // client in its Tags). 201 var StreamStats *buffer.CircularBuffer 202 203 var errAlreadyInitialized = errors.New("profiling may be initialized at most once") 204 205 // InitStats initializes all the relevant Stat objects. Must be called exactly 206 // once per lifetime of a process; calls after the first one will return an 207 // error. 208 func InitStats(streamStatsSize uint32) error { 209 var err error 210 if !atomic.CompareAndSwapInt32(&statsInitialized, 0, 1) { 211 return errAlreadyInitialized 212 } 213 214 if streamStatsSize == 0 { 215 streamStatsSize = defaultStreamStatsSize 216 } 217 218 StreamStats, err = buffer.NewCircularBuffer(streamStatsSize) 219 if err != nil { 220 return err 221 } 222 223 return nil 224 }