github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/internal/profiling/goid_modified.go (about) 1 //go:build grpcgoid 2 // +build grpcgoid 3 4 /* 5 * 6 * Copyright 2019 gRPC authors. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 */ 21 22 package profiling 23 24 import ( 25 "runtime" 26 ) 27 28 // This stubbed function usually returns zero (see goid_regular.go); however, 29 // if grpc is built with `-tags 'grpcgoid'`, a runtime.Goid function, which 30 // does not exist in the Go standard library, is expected. While not necessary, 31 // sometimes, visualising grpc profiling data in trace-viewer is much nicer 32 // with goroutines separated from each other. 33 // 34 // Several other approaches were considered before arriving at this: 35 // 36 // 1. Using a CGO module: CGO usually has access to some things that regular 37 // Go does not. Till go1.4, CGO used to have access to the goroutine struct 38 // because the Go runtime was written in C. However, 1.5+ uses a native Go 39 // runtime; as a result, CGO does not have access to the goroutine structure 40 // anymore in modern Go. Besides, CGO interop wasn't fast enough (estimated 41 // to be ~170ns/op). This would also make building grpc require a C 42 // compiler, which isn't a requirement currently, breaking a lot of stuff. 43 // 44 // 2. Using runtime.Stack stacktrace: While this would remove the need for a 45 // modified Go runtime, this is ridiculously slow, thanks to the all the 46 // string processing shenanigans required to extract the goroutine ID (about 47 // ~2000ns/op). 48 // 49 // 3. Using Go version-specific build tags: For any given Go version, the 50 // goroutine struct has a fixed structure. As a result, the goroutine ID 51 // could be extracted if we know the offset using some assembly. This would 52 // be faster then #1 and #2, but is harder to maintain. This would require 53 // special Go code that's both architecture-specific and go version-specific 54 // (a quadratic number of variants to maintain). 55 // 56 // 4. This approach, which requires a simple modification [1] to the Go runtime 57 // to expose the current goroutine's ID. This is the chosen approach and it 58 // takes about ~2 ns/op, which is negligible in the face of the tens of 59 // microseconds that grpc takes to complete a RPC request. 60 // 61 // [1] To make the goroutine ID visible to Go programs apply the following 62 // change to the runtime2.go file in your Go runtime installation: 63 // 64 // diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go 65 // --- a/src/runtime/runtime2.go 66 // +++ b/src/runtime/runtime2.go 67 // @@ -392,6 +392,10 @@ type stack struct { 68 // hi uintptr 69 // } 70 // 71 // +func Goid() int64 { 72 // + return getg().goid 73 // +} 74 // + 75 // type g struct { 76 // // Stack parameters. 77 // // stack describes the actual stack memory: [stack.lo, stack.hi). 78 // 79 // The exposed runtime.Goid() function will return a int64 goroutine ID. 80 func goid() int64 { 81 return runtime.Goid() 82 }