github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/goid/idapi.go (about)

     1  package goid
     2  
     3  import (
     4  	"log"
     5  	"runtime"
     6  	"sync"
     7  )
     8  
     9  const (
    10  	stackSize = 1024
    11  )
    12  
    13  var (
    14  	anchor       = []byte("goroutine ")
    15  	stackBufPool = sync.Pool{
    16  		New: func() any {
    17  			buf := make([]byte, 64)
    18  			return &buf
    19  		},
    20  	}
    21  )
    22  
    23  // getGoidByStack parse the current goroutine's id from caller stack.
    24  // This function could be very slow(like 3000us/op), but it's very safe.
    25  func getGoidByStack() (goid int64) {
    26  	bp := stackBufPool.Get().(*[]byte)
    27  	defer stackBufPool.Put(bp)
    28  
    29  	b := *bp
    30  	b = b[:runtime.Stack(b, false)]
    31  	goid, _ = findNextGoid(b, 0)
    32  	return
    33  }
    34  
    35  // getAllGoidByStack find all goid through stack; WARNING: This function could be very inefficient
    36  func getAllGoidByStack() (goids []int64) {
    37  	count := runtime.NumGoroutine()
    38  	size := count * stackSize // it's ok?
    39  	buf := make([]byte, size)
    40  	n := runtime.Stack(buf, true)
    41  	buf = buf[:n]
    42  	// parse all goids
    43  	goids = make([]int64, 0, count+4)
    44  	for i := 0; i < len(buf); {
    45  		goid, off := findNextGoid(buf, i)
    46  		if goid > 0 {
    47  			goids = append(goids, goid)
    48  		}
    49  		i = off
    50  	}
    51  	return
    52  }
    53  
    54  // Find the next goid from `buf[off:]`
    55  func findNextGoid(buf []byte, off int) (goid int64, next int) {
    56  	i := off
    57  	hit := false
    58  	// skip to anchor
    59  	acr := anchor
    60  	for sb := len(buf) - len(acr); i < sb; {
    61  		if buf[i] == acr[0] && buf[i+1] == acr[1] && buf[i+2] == acr[2] && buf[i+3] == acr[3] &&
    62  			buf[i+4] == acr[4] && buf[i+5] == acr[5] && buf[i+6] == acr[6] &&
    63  			buf[i+7] == acr[7] && buf[i+8] == acr[8] && buf[i+9] == acr[9] {
    64  			hit = true
    65  			i += len(acr)
    66  			break
    67  		}
    68  		for ; i < len(buf) && buf[i] != '\n'; i++ {
    69  		}
    70  		i++
    71  	}
    72  	// return if not hit
    73  	if !hit {
    74  		return 0, len(buf)
    75  	}
    76  	// extract goid
    77  	var done bool
    78  	for ; i < len(buf) && !done; i++ {
    79  		switch buf[i] {
    80  		case '0':
    81  			goid *= 10
    82  		case '1':
    83  			goid = goid*10 + 1
    84  		case '2':
    85  			goid = goid*10 + 2
    86  		case '3':
    87  			goid = goid*10 + 3
    88  		case '4':
    89  			goid = goid*10 + 4
    90  		case '5':
    91  			goid = goid*10 + 5
    92  		case '6':
    93  			goid = goid*10 + 6
    94  		case '7':
    95  			goid = goid*10 + 7
    96  		case '8':
    97  			goid = goid*10 + 8
    98  		case '9':
    99  			goid = goid*10 + 9
   100  		case ' ':
   101  			done = true
   102  			break
   103  		default:
   104  			goid = 0
   105  			log.Printf("should never be here, any bug happens\n")
   106  		}
   107  	}
   108  	next = i
   109  	return
   110  }