| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package pprof |
| |
| import "unsafe" |
| |
| // A profMap is a map from (stack, tag) to mapEntry. |
| // It grows without bound, but that's assumed to be OK. |
| type profMap struct { |
| hash map[uintptr]*profMapEntry |
| all *profMapEntry |
| last *profMapEntry |
| free []profMapEntry |
| freeStk []uintptr |
| } |
| |
| // A profMapEntry is a single entry in the profMap. |
| type profMapEntry struct { |
| nextHash *profMapEntry // next in hash list |
| nextAll *profMapEntry // next in list of all entries |
| stk []uintptr |
| tag unsafe.Pointer |
| count int64 |
| } |
| |
| func (m *profMap) lookup(stk []uint64, tag unsafe.Pointer) *profMapEntry { |
| // Compute hash of (stk, tag). |
| h := uintptr(0) |
| for _, x := range stk { |
| h = h<<8 | (h >> (8 * (unsafe.Sizeof(h) - 1))) |
| h += uintptr(x) * 41 |
| } |
| h = h<<8 | (h >> (8 * (unsafe.Sizeof(h) - 1))) |
| h += uintptr(tag) * 41 |
| |
| // Find entry if present. |
| var last *profMapEntry |
| Search: |
| for e := m.hash[h]; e != nil; last, e = e, e.nextHash { |
| if len(e.stk) != len(stk) || e.tag != tag { |
| continue |
| } |
| for j := range stk { |
| if e.stk[j] != uintptr(stk[j]) { |
| continue Search |
| } |
| } |
| // Move to front. |
| if last != nil { |
| last.nextHash = e.nextHash |
| e.nextHash = m.hash[h] |
| m.hash[h] = e |
| } |
| return e |
| } |
| |
| // Add new entry. |
| if len(m.free) < 1 { |
| m.free = make([]profMapEntry, 128) |
| } |
| e := &m.free[0] |
| m.free = m.free[1:] |
| e.nextHash = m.hash[h] |
| e.tag = tag |
| |
| if len(m.freeStk) < len(stk) { |
| m.freeStk = make([]uintptr, 1024) |
| } |
| // Limit cap to prevent append from clobbering freeStk. |
| e.stk = m.freeStk[:len(stk):len(stk)] |
| m.freeStk = m.freeStk[len(stk):] |
| |
| for j := range stk { |
| e.stk[j] = uintptr(stk[j]) |
| } |
| if m.hash == nil { |
| m.hash = make(map[uintptr]*profMapEntry) |
| } |
| m.hash[h] = e |
| if m.all == nil { |
| m.all = e |
| m.last = e |
| } else { |
| m.last.nextAll = e |
| m.last = e |
| } |
| return e |
| } |