15ffd83dbSDimitry Andric //===-- stack_depot.h -------------------------------------------*- C++ -*-===// 25ffd83dbSDimitry Andric // 35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65ffd83dbSDimitry Andric // 75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 85ffd83dbSDimitry Andric 95ffd83dbSDimitry Andric #ifndef SCUDO_STACK_DEPOT_H_ 105ffd83dbSDimitry Andric #define SCUDO_STACK_DEPOT_H_ 115ffd83dbSDimitry Andric 125ffd83dbSDimitry Andric #include "atomic_helpers.h" 13*0fca6ea1SDimitry Andric #include "common.h" 145ffd83dbSDimitry Andric #include "mutex.h" 155ffd83dbSDimitry Andric 165ffd83dbSDimitry Andric namespace scudo { 175ffd83dbSDimitry Andric 185ffd83dbSDimitry Andric class MurMur2HashBuilder { 195ffd83dbSDimitry Andric static const u32 M = 0x5bd1e995; 205ffd83dbSDimitry Andric static const u32 Seed = 0x9747b28c; 215ffd83dbSDimitry Andric static const u32 R = 24; 225ffd83dbSDimitry Andric u32 H; 235ffd83dbSDimitry Andric 245ffd83dbSDimitry Andric public: 255ffd83dbSDimitry Andric explicit MurMur2HashBuilder(u32 Init = 0) { H = Seed ^ Init; } add(u32 K)265ffd83dbSDimitry Andric void add(u32 K) { 275ffd83dbSDimitry Andric K *= M; 285ffd83dbSDimitry Andric K ^= K >> R; 295ffd83dbSDimitry Andric K *= M; 305ffd83dbSDimitry Andric H *= M; 315ffd83dbSDimitry Andric H ^= K; 325ffd83dbSDimitry Andric } get()335ffd83dbSDimitry Andric u32 get() { 345ffd83dbSDimitry Andric u32 X = H; 355ffd83dbSDimitry Andric X ^= X >> 13; 365ffd83dbSDimitry Andric X *= M; 375ffd83dbSDimitry Andric X ^= X >> 15; 385ffd83dbSDimitry Andric return X; 395ffd83dbSDimitry Andric } 405ffd83dbSDimitry Andric }; 415ffd83dbSDimitry Andric 42*0fca6ea1SDimitry Andric class alignas(atomic_u64) StackDepot { 435ffd83dbSDimitry Andric HybridMutex RingEndMu; 44fe6060f1SDimitry Andric u32 RingEnd = 0; 455ffd83dbSDimitry Andric 465ffd83dbSDimitry Andric // This data structure stores a stack trace for each allocation and 475ffd83dbSDimitry Andric // deallocation when stack trace recording is enabled, that may be looked up 485ffd83dbSDimitry Andric // using a hash of the stack trace. The lower bits of the hash are an index 495ffd83dbSDimitry Andric // into the Tab array, which stores an index into the Ring array where the 505ffd83dbSDimitry Andric // stack traces are stored. As the name implies, Ring is a ring buffer, so a 515ffd83dbSDimitry Andric // stack trace may wrap around to the start of the array. 525ffd83dbSDimitry Andric // 535ffd83dbSDimitry Andric // Each stack trace in Ring is prefixed by a stack trace marker consisting of 545ffd83dbSDimitry Andric // a fixed 1 bit in bit 0 (this allows disambiguation between stack frames 555ffd83dbSDimitry Andric // and stack trace markers in the case where instruction pointers are 4-byte 565ffd83dbSDimitry Andric // aligned, as they are on arm64), the stack trace hash in bits 1-32, and the 575ffd83dbSDimitry Andric // size of the stack trace in bits 33-63. 585ffd83dbSDimitry Andric // 595ffd83dbSDimitry Andric // The insert() function is potentially racy in its accesses to the Tab and 605ffd83dbSDimitry Andric // Ring arrays, but find() is resilient to races in the sense that, barring 615ffd83dbSDimitry Andric // hash collisions, it will either return the correct stack trace or no stack 625ffd83dbSDimitry Andric // trace at all, even if two instances of insert() raced with one another. 635ffd83dbSDimitry Andric // This is achieved by re-checking the hash of the stack trace before 645ffd83dbSDimitry Andric // returning the trace. 655ffd83dbSDimitry Andric 66*0fca6ea1SDimitry Andric u32 RingSize = 0; 67*0fca6ea1SDimitry Andric u32 RingMask = 0; 68*0fca6ea1SDimitry Andric u32 TabMask = 0; 69*0fca6ea1SDimitry Andric // This is immediately followed by RingSize atomic_u64 and 70*0fca6ea1SDimitry Andric // (TabMask + 1) atomic_u32. 715ffd83dbSDimitry Andric getRing()72*0fca6ea1SDimitry Andric atomic_u64 *getRing() { 73*0fca6ea1SDimitry Andric return reinterpret_cast<atomic_u64 *>(reinterpret_cast<char *>(this) + 74*0fca6ea1SDimitry Andric sizeof(StackDepot)); 75*0fca6ea1SDimitry Andric } 76*0fca6ea1SDimitry Andric getTab()77*0fca6ea1SDimitry Andric atomic_u32 *getTab() { 78*0fca6ea1SDimitry Andric return reinterpret_cast<atomic_u32 *>(reinterpret_cast<char *>(this) + 79*0fca6ea1SDimitry Andric sizeof(StackDepot) + 80*0fca6ea1SDimitry Andric sizeof(atomic_u64) * RingSize); 81*0fca6ea1SDimitry Andric } 82*0fca6ea1SDimitry Andric getRing()83*0fca6ea1SDimitry Andric const atomic_u64 *getRing() const { 84*0fca6ea1SDimitry Andric return reinterpret_cast<const atomic_u64 *>( 85*0fca6ea1SDimitry Andric reinterpret_cast<const char *>(this) + sizeof(StackDepot)); 86*0fca6ea1SDimitry Andric } 87*0fca6ea1SDimitry Andric getTab()88*0fca6ea1SDimitry Andric const atomic_u32 *getTab() const { 89*0fca6ea1SDimitry Andric return reinterpret_cast<const atomic_u32 *>( 90*0fca6ea1SDimitry Andric reinterpret_cast<const char *>(this) + sizeof(StackDepot) + 91*0fca6ea1SDimitry Andric sizeof(atomic_u64) * RingSize); 92*0fca6ea1SDimitry Andric } 935ffd83dbSDimitry Andric 945ffd83dbSDimitry Andric public: init(u32 RingSz,u32 TabSz)95*0fca6ea1SDimitry Andric void init(u32 RingSz, u32 TabSz) { 96*0fca6ea1SDimitry Andric DCHECK(isPowerOfTwo(RingSz)); 97*0fca6ea1SDimitry Andric DCHECK(isPowerOfTwo(TabSz)); 98*0fca6ea1SDimitry Andric RingSize = RingSz; 99*0fca6ea1SDimitry Andric RingMask = RingSz - 1; 100*0fca6ea1SDimitry Andric TabMask = TabSz - 1; 101*0fca6ea1SDimitry Andric } 102*0fca6ea1SDimitry Andric 103*0fca6ea1SDimitry Andric // Ensure that RingSize, RingMask and TabMask are set up in a way that 104*0fca6ea1SDimitry Andric // all accesses are within range of BufSize. isValid(uptr BufSize)105*0fca6ea1SDimitry Andric bool isValid(uptr BufSize) const { 106*0fca6ea1SDimitry Andric if (!isPowerOfTwo(RingSize)) 107*0fca6ea1SDimitry Andric return false; 108*0fca6ea1SDimitry Andric uptr RingBytes = sizeof(atomic_u64) * RingSize; 109*0fca6ea1SDimitry Andric if (RingMask + 1 != RingSize) 110*0fca6ea1SDimitry Andric return false; 111*0fca6ea1SDimitry Andric 112*0fca6ea1SDimitry Andric if (TabMask == 0) 113*0fca6ea1SDimitry Andric return false; 114*0fca6ea1SDimitry Andric uptr TabSize = TabMask + 1; 115*0fca6ea1SDimitry Andric if (!isPowerOfTwo(TabSize)) 116*0fca6ea1SDimitry Andric return false; 117*0fca6ea1SDimitry Andric uptr TabBytes = sizeof(atomic_u32) * TabSize; 118*0fca6ea1SDimitry Andric 119*0fca6ea1SDimitry Andric // Subtract and detect underflow. 120*0fca6ea1SDimitry Andric if (BufSize < sizeof(StackDepot)) 121*0fca6ea1SDimitry Andric return false; 122*0fca6ea1SDimitry Andric BufSize -= sizeof(StackDepot); 123*0fca6ea1SDimitry Andric if (BufSize < TabBytes) 124*0fca6ea1SDimitry Andric return false; 125*0fca6ea1SDimitry Andric BufSize -= TabBytes; 126*0fca6ea1SDimitry Andric if (BufSize < RingBytes) 127*0fca6ea1SDimitry Andric return false; 128*0fca6ea1SDimitry Andric return BufSize == RingBytes; 129*0fca6ea1SDimitry Andric } 130*0fca6ea1SDimitry Andric 1315ffd83dbSDimitry Andric // Insert hash of the stack trace [Begin, End) into the stack depot, and 1325ffd83dbSDimitry Andric // return the hash. insert(uptr * Begin,uptr * End)1335ffd83dbSDimitry Andric u32 insert(uptr *Begin, uptr *End) { 134*0fca6ea1SDimitry Andric auto *Tab = getTab(); 135*0fca6ea1SDimitry Andric auto *Ring = getRing(); 136*0fca6ea1SDimitry Andric 1375ffd83dbSDimitry Andric MurMur2HashBuilder B; 1385ffd83dbSDimitry Andric for (uptr *I = Begin; I != End; ++I) 1395ffd83dbSDimitry Andric B.add(u32(*I) >> 2); 1405ffd83dbSDimitry Andric u32 Hash = B.get(); 1415ffd83dbSDimitry Andric 1425ffd83dbSDimitry Andric u32 Pos = Hash & TabMask; 1435ffd83dbSDimitry Andric u32 RingPos = atomic_load_relaxed(&Tab[Pos]); 1445ffd83dbSDimitry Andric u64 Entry = atomic_load_relaxed(&Ring[RingPos]); 1455ffd83dbSDimitry Andric u64 Id = (u64(End - Begin) << 33) | (u64(Hash) << 1) | 1; 1465ffd83dbSDimitry Andric if (Entry == Id) 1475ffd83dbSDimitry Andric return Hash; 1485ffd83dbSDimitry Andric 1495ffd83dbSDimitry Andric ScopedLock Lock(RingEndMu); 1505ffd83dbSDimitry Andric RingPos = RingEnd; 1515ffd83dbSDimitry Andric atomic_store_relaxed(&Tab[Pos], RingPos); 1525ffd83dbSDimitry Andric atomic_store_relaxed(&Ring[RingPos], Id); 1535ffd83dbSDimitry Andric for (uptr *I = Begin; I != End; ++I) { 1545ffd83dbSDimitry Andric RingPos = (RingPos + 1) & RingMask; 1555ffd83dbSDimitry Andric atomic_store_relaxed(&Ring[RingPos], *I); 1565ffd83dbSDimitry Andric } 1575ffd83dbSDimitry Andric RingEnd = (RingPos + 1) & RingMask; 1585ffd83dbSDimitry Andric return Hash; 1595ffd83dbSDimitry Andric } 1605ffd83dbSDimitry Andric 1615ffd83dbSDimitry Andric // Look up a stack trace by hash. Returns true if successful. The trace may be 1625ffd83dbSDimitry Andric // accessed via operator[] passing indexes between *RingPosPtr and 1635ffd83dbSDimitry Andric // *RingPosPtr + *SizePtr. find(u32 Hash,uptr * RingPosPtr,uptr * SizePtr)1645ffd83dbSDimitry Andric bool find(u32 Hash, uptr *RingPosPtr, uptr *SizePtr) const { 165*0fca6ea1SDimitry Andric auto *Tab = getTab(); 166*0fca6ea1SDimitry Andric auto *Ring = getRing(); 167*0fca6ea1SDimitry Andric 1685ffd83dbSDimitry Andric u32 Pos = Hash & TabMask; 1695ffd83dbSDimitry Andric u32 RingPos = atomic_load_relaxed(&Tab[Pos]); 1705ffd83dbSDimitry Andric if (RingPos >= RingSize) 1715ffd83dbSDimitry Andric return false; 1725ffd83dbSDimitry Andric u64 Entry = atomic_load_relaxed(&Ring[RingPos]); 1735ffd83dbSDimitry Andric u64 HashWithTagBit = (u64(Hash) << 1) | 1; 1745ffd83dbSDimitry Andric if ((Entry & 0x1ffffffff) != HashWithTagBit) 1755ffd83dbSDimitry Andric return false; 1765ffd83dbSDimitry Andric u32 Size = u32(Entry >> 33); 1775ffd83dbSDimitry Andric if (Size >= RingSize) 1785ffd83dbSDimitry Andric return false; 1795ffd83dbSDimitry Andric *RingPosPtr = (RingPos + 1) & RingMask; 1805ffd83dbSDimitry Andric *SizePtr = Size; 1815ffd83dbSDimitry Andric MurMur2HashBuilder B; 1825ffd83dbSDimitry Andric for (uptr I = 0; I != Size; ++I) { 1835ffd83dbSDimitry Andric RingPos = (RingPos + 1) & RingMask; 1845ffd83dbSDimitry Andric B.add(u32(atomic_load_relaxed(&Ring[RingPos])) >> 2); 1855ffd83dbSDimitry Andric } 1865ffd83dbSDimitry Andric return B.get() == Hash; 1875ffd83dbSDimitry Andric } 1885ffd83dbSDimitry Andric at(uptr RingPos)189*0fca6ea1SDimitry Andric u64 at(uptr RingPos) const { 190*0fca6ea1SDimitry Andric auto *Ring = getRing(); 1915ffd83dbSDimitry Andric return atomic_load_relaxed(&Ring[RingPos & RingMask]); 1925ffd83dbSDimitry Andric } 193*0fca6ea1SDimitry Andric 194*0fca6ea1SDimitry Andric // This is done for the purpose of fork safety in multithreaded programs and 195*0fca6ea1SDimitry Andric // does not fully disable StackDepot. In particular, find() still works and 196*0fca6ea1SDimitry Andric // only insert() is blocked. disable()197*0fca6ea1SDimitry Andric void disable() NO_THREAD_SAFETY_ANALYSIS { RingEndMu.lock(); } 198*0fca6ea1SDimitry Andric enable()199*0fca6ea1SDimitry Andric void enable() NO_THREAD_SAFETY_ANALYSIS { RingEndMu.unlock(); } 2005ffd83dbSDimitry Andric }; 2015ffd83dbSDimitry Andric 202*0fca6ea1SDimitry Andric // We need StackDepot to be aligned to 8-bytes so the ring we store after 203*0fca6ea1SDimitry Andric // is correctly assigned. 204*0fca6ea1SDimitry Andric static_assert(sizeof(StackDepot) % alignof(atomic_u64) == 0); 205*0fca6ea1SDimitry Andric 2065ffd83dbSDimitry Andric } // namespace scudo 2075ffd83dbSDimitry Andric 2085ffd83dbSDimitry Andric #endif // SCUDO_STACK_DEPOT_H_ 209