mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-11-04 02:01:14 +01:00 
			
		
		
		
	Updates #14520 Updates #14517 (in that I pulled this out of there) Change-Id: Ibc28162816e083fcadf550586c06805c76e378fc Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
		
			
				
	
	
		
			94 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			94 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (c) Tailscale Inc & AUTHORS
 | 
						|
// SPDX-License-Identifier: BSD-3-Clause
 | 
						|
 | 
						|
// The goroutines package contains utilities for tracking and getting active goroutines.
 | 
						|
package goroutines
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"runtime"
 | 
						|
	"strconv"
 | 
						|
)
 | 
						|
 | 
						|
// ScrubbedGoroutineDump returns either the current goroutine's stack or all
 | 
						|
// goroutines' stacks, but with the actual values of arguments scrubbed out,
 | 
						|
// lest it contain some private key material.
 | 
						|
func ScrubbedGoroutineDump(all bool) []byte {
 | 
						|
	var buf []byte
 | 
						|
	// Grab stacks multiple times into increasingly larger buffer sizes
 | 
						|
	// to minimize the risk that we blow past our iOS memory limit.
 | 
						|
	for size := 1 << 10; size <= 1<<20; size += 1 << 10 {
 | 
						|
		buf = make([]byte, size)
 | 
						|
		buf = buf[:runtime.Stack(buf, all)]
 | 
						|
		if len(buf) < size {
 | 
						|
			// It fit.
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return scrubHex(buf)
 | 
						|
}
 | 
						|
 | 
						|
func scrubHex(buf []byte) []byte {
 | 
						|
	saw := map[string][]byte{} // "0x123" => "v1%3" (unique value 1 and its value mod 8)
 | 
						|
 | 
						|
	foreachHexAddress(buf, func(in []byte) {
 | 
						|
		if string(in) == "0x0" {
 | 
						|
			return
 | 
						|
		}
 | 
						|
		if v, ok := saw[string(in)]; ok {
 | 
						|
			for i := range in {
 | 
						|
				in[i] = '_'
 | 
						|
			}
 | 
						|
			copy(in, v)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		inStr := string(in)
 | 
						|
		u64, err := strconv.ParseUint(string(in[2:]), 16, 64)
 | 
						|
		for i := range in {
 | 
						|
			in[i] = '_'
 | 
						|
		}
 | 
						|
		if err != nil {
 | 
						|
			in[0] = '?'
 | 
						|
			return
 | 
						|
		}
 | 
						|
		v := []byte(fmt.Sprintf("v%d%%%d", len(saw)+1, u64%8))
 | 
						|
		saw[inStr] = v
 | 
						|
		copy(in, v)
 | 
						|
	})
 | 
						|
	return buf
 | 
						|
}
 | 
						|
 | 
						|
var ohx = []byte("0x")
 | 
						|
 | 
						|
// foreachHexAddress calls f with each subslice of b that matches
 | 
						|
// regexp `0x[0-9a-f]*`.
 | 
						|
func foreachHexAddress(b []byte, f func([]byte)) {
 | 
						|
	for len(b) > 0 {
 | 
						|
		i := bytes.Index(b, ohx)
 | 
						|
		if i == -1 {
 | 
						|
			return
 | 
						|
		}
 | 
						|
		b = b[i:]
 | 
						|
		hx := hexPrefix(b)
 | 
						|
		f(hx)
 | 
						|
		b = b[len(hx):]
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func hexPrefix(b []byte) []byte {
 | 
						|
	for i, c := range b {
 | 
						|
		if i < 2 {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if !isHexByte(c) {
 | 
						|
			return b[:i]
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return b
 | 
						|
}
 | 
						|
 | 
						|
func isHexByte(b byte) bool {
 | 
						|
	return '0' <= b && b <= '9' || 'a' <= b && b <= 'f' || 'A' <= b && b <= 'F'
 | 
						|
}
 |