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