github.com/grafana/pyroscope-go/godeltaprof@v0.1.8-0.20240513050943-1b1f97373e2a/internal/pprof/map.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package pprof
     6  
     7  import "unsafe"
     8  
     9  // A profMap is a map from (stack, tag) to mapEntry.
    10  // It grows without bound, but that's assumed to be OK.
    11  type profMap struct {
    12  	hash    map[uintptr]*profMapEntry
    13  	all     *profMapEntry
    14  	last    *profMapEntry
    15  	free    []profMapEntry
    16  	freeStk []uintptr
    17  }
    18  
    19  type count struct {
    20  	// alloc_objects, alloc_bytes for heap
    21  	// mutex_count, mutex_duration for mutex
    22  	v1, v2 int64
    23  }
    24  
    25  // A profMapEntry is a single entry in the profMap.
    26  type profMapEntry struct {
    27  	nextHash *profMapEntry // next in hash list
    28  	nextAll  *profMapEntry // next in list of all entries
    29  	stk      []uintptr
    30  	tag      uintptr
    31  	count    count
    32  }
    33  
    34  func (m *profMap) Lookup(stk []uintptr, tag uintptr) *profMapEntry {
    35  	// Compute hash of (stk, tag).
    36  	h := uintptr(0)
    37  	for _, x := range stk {
    38  		h = h<<8 | (h >> (8 * (unsafe.Sizeof(h) - 1)))
    39  		h += uintptr(x) * 41
    40  	}
    41  	h = h<<8 | (h >> (8 * (unsafe.Sizeof(h) - 1)))
    42  	h += uintptr(tag) * 41
    43  
    44  	// Find entry if present.
    45  	var last *profMapEntry
    46  Search:
    47  	for e := m.hash[h]; e != nil; last, e = e, e.nextHash {
    48  		if len(e.stk) != len(stk) || e.tag != tag {
    49  			continue
    50  		}
    51  		for j := range stk {
    52  			if e.stk[j] != uintptr(stk[j]) {
    53  				continue Search
    54  			}
    55  		}
    56  		// Move to front.
    57  		if last != nil {
    58  			last.nextHash = e.nextHash
    59  			e.nextHash = m.hash[h]
    60  			m.hash[h] = e
    61  		}
    62  		return e
    63  	}
    64  
    65  	// Add new entry.
    66  	if len(m.free) < 1 {
    67  		m.free = make([]profMapEntry, 128)
    68  	}
    69  	e := &m.free[0]
    70  	m.free = m.free[1:]
    71  	e.nextHash = m.hash[h]
    72  	e.tag = tag
    73  
    74  	if len(m.freeStk) < len(stk) {
    75  		m.freeStk = make([]uintptr, 1024)
    76  	}
    77  	// Limit cap to prevent append from clobbering freeStk.
    78  	e.stk = m.freeStk[:len(stk):len(stk)]
    79  	m.freeStk = m.freeStk[len(stk):]
    80  
    81  	for j := range stk {
    82  		e.stk[j] = uintptr(stk[j])
    83  	}
    84  	if m.hash == nil {
    85  		m.hash = make(map[uintptr]*profMapEntry)
    86  	}
    87  	m.hash[h] = e
    88  	if m.all == nil {
    89  		m.all = e
    90  		m.last = e
    91  	} else {
    92  		m.last.nextAll = e
    93  		m.last = e
    94  	}
    95  	return e
    96  }