xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stack_store.cpp (revision cb14a3fe5122c879eae1fb480ed7ce82a699ddb6)
1349cc55cSDimitry Andric //===-- sanitizer_stack_store.cpp -------------------------------*- C++ -*-===//
2349cc55cSDimitry Andric //
3349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6349cc55cSDimitry Andric //
7349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
8349cc55cSDimitry Andric 
9349cc55cSDimitry Andric #include "sanitizer_stack_store.h"
10349cc55cSDimitry Andric 
11349cc55cSDimitry Andric #include "sanitizer_atomic.h"
12349cc55cSDimitry Andric #include "sanitizer_common.h"
130eae32dcSDimitry Andric #include "sanitizer_internal_defs.h"
140eae32dcSDimitry Andric #include "sanitizer_leb128.h"
150eae32dcSDimitry Andric #include "sanitizer_lzw.h"
160eae32dcSDimitry Andric #include "sanitizer_placement_new.h"
17349cc55cSDimitry Andric #include "sanitizer_stacktrace.h"
18349cc55cSDimitry Andric 
19349cc55cSDimitry Andric namespace __sanitizer {
20349cc55cSDimitry Andric 
214824e7fdSDimitry Andric namespace {
224824e7fdSDimitry Andric struct StackTraceHeader {
234824e7fdSDimitry Andric   static constexpr u32 kStackSizeBits = 8;
24349cc55cSDimitry Andric 
254824e7fdSDimitry Andric   u8 size;
264824e7fdSDimitry Andric   u8 tag;
StackTraceHeader__sanitizer::__anon74b28d1f0111::StackTraceHeader274824e7fdSDimitry Andric   explicit StackTraceHeader(const StackTrace &trace)
284824e7fdSDimitry Andric       : size(Min<uptr>(trace.size, (1u << 8) - 1)), tag(trace.tag) {
294824e7fdSDimitry Andric     CHECK_EQ(trace.tag, static_cast<uptr>(tag));
304824e7fdSDimitry Andric   }
StackTraceHeader__sanitizer::__anon74b28d1f0111::StackTraceHeader314824e7fdSDimitry Andric   explicit StackTraceHeader(uptr h)
324824e7fdSDimitry Andric       : size(h & ((1 << kStackSizeBits) - 1)), tag(h >> kStackSizeBits) {}
334824e7fdSDimitry Andric 
ToUptr__sanitizer::__anon74b28d1f0111::StackTraceHeader344824e7fdSDimitry Andric   uptr ToUptr() const {
354824e7fdSDimitry Andric     return static_cast<uptr>(size) | (static_cast<uptr>(tag) << kStackSizeBits);
364824e7fdSDimitry Andric   }
374824e7fdSDimitry Andric };
384824e7fdSDimitry Andric }  // namespace
394824e7fdSDimitry Andric 
Store(const StackTrace & trace,uptr * pack)404824e7fdSDimitry Andric StackStore::Id StackStore::Store(const StackTrace &trace, uptr *pack) {
414824e7fdSDimitry Andric   if (!trace.size && !trace.tag)
424824e7fdSDimitry Andric     return 0;
434824e7fdSDimitry Andric   StackTraceHeader h(trace);
444824e7fdSDimitry Andric   uptr idx = 0;
454824e7fdSDimitry Andric   *pack = 0;
464824e7fdSDimitry Andric   uptr *stack_trace = Alloc(h.size + 1, &idx, pack);
47*cb14a3feSDimitry Andric   // No more space.
48*cb14a3feSDimitry Andric   if (stack_trace == nullptr)
49*cb14a3feSDimitry Andric     return 0;
504824e7fdSDimitry Andric   *stack_trace = h.ToUptr();
514824e7fdSDimitry Andric   internal_memcpy(stack_trace + 1, trace.trace, h.size * sizeof(uptr));
524824e7fdSDimitry Andric   *pack += blocks_[GetBlockIdx(idx)].Stored(h.size + 1);
534824e7fdSDimitry Andric   return OffsetToId(idx);
54349cc55cSDimitry Andric }
55349cc55cSDimitry Andric 
Load(Id id)56349cc55cSDimitry Andric StackTrace StackStore::Load(Id id) {
574824e7fdSDimitry Andric   if (!id)
584824e7fdSDimitry Andric     return {};
594824e7fdSDimitry Andric   uptr idx = IdToOffset(id);
604824e7fdSDimitry Andric   uptr block_idx = GetBlockIdx(idx);
614824e7fdSDimitry Andric   CHECK_LT(block_idx, ARRAY_SIZE(blocks_));
620eae32dcSDimitry Andric   const uptr *stack_trace = blocks_[block_idx].GetOrUnpack(this);
634824e7fdSDimitry Andric   if (!stack_trace)
644824e7fdSDimitry Andric     return {};
654824e7fdSDimitry Andric   stack_trace += GetInBlockIdx(idx);
664824e7fdSDimitry Andric   StackTraceHeader h(*stack_trace);
674824e7fdSDimitry Andric   return StackTrace(stack_trace + 1, h.size, h.tag);
68349cc55cSDimitry Andric }
69349cc55cSDimitry Andric 
Allocated() const704824e7fdSDimitry Andric uptr StackStore::Allocated() const {
710eae32dcSDimitry Andric   return atomic_load_relaxed(&allocated_) + sizeof(*this);
724824e7fdSDimitry Andric }
734824e7fdSDimitry Andric 
Alloc(uptr count,uptr * idx,uptr * pack)744824e7fdSDimitry Andric uptr *StackStore::Alloc(uptr count, uptr *idx, uptr *pack) {
75349cc55cSDimitry Andric   for (;;) {
764824e7fdSDimitry Andric     // Optimisic lock-free allocation, essentially try to bump the
774824e7fdSDimitry Andric     // total_frames_.
784824e7fdSDimitry Andric     uptr start = atomic_fetch_add(&total_frames_, count, memory_order_relaxed);
794824e7fdSDimitry Andric     uptr block_idx = GetBlockIdx(start);
804824e7fdSDimitry Andric     uptr last_idx = GetBlockIdx(start + count - 1);
814824e7fdSDimitry Andric     if (LIKELY(block_idx == last_idx)) {
82*cb14a3feSDimitry Andric       // Fits into a single block.
83*cb14a3feSDimitry Andric       // No more available blocks.  Indicate inability to allocate more memory.
84*cb14a3feSDimitry Andric       if (block_idx >= ARRAY_SIZE(blocks_))
85*cb14a3feSDimitry Andric         return nullptr;
864824e7fdSDimitry Andric       *idx = start;
870eae32dcSDimitry Andric       return blocks_[block_idx].GetOrCreate(this) + GetInBlockIdx(start);
884824e7fdSDimitry Andric     }
894824e7fdSDimitry Andric 
904824e7fdSDimitry Andric     // Retry. We can't use range allocated in two different blocks.
914824e7fdSDimitry Andric     CHECK_LE(count, kBlockSizeFrames);
924824e7fdSDimitry Andric     uptr in_first = kBlockSizeFrames - GetInBlockIdx(start);
934824e7fdSDimitry Andric     // Mark tail/head of these blocks as "stored".to avoid waiting before we can
944824e7fdSDimitry Andric     // Pack().
954824e7fdSDimitry Andric     *pack += blocks_[block_idx].Stored(in_first);
964824e7fdSDimitry Andric     *pack += blocks_[last_idx].Stored(count - in_first);
97349cc55cSDimitry Andric   }
98349cc55cSDimitry Andric }
99349cc55cSDimitry Andric 
Map(uptr size,const char * mem_type)1000eae32dcSDimitry Andric void *StackStore::Map(uptr size, const char *mem_type) {
1010eae32dcSDimitry Andric   atomic_fetch_add(&allocated_, size, memory_order_relaxed);
1020eae32dcSDimitry Andric   return MmapNoReserveOrDie(size, mem_type);
1030eae32dcSDimitry Andric }
1040eae32dcSDimitry Andric 
Unmap(void * addr,uptr size)1050eae32dcSDimitry Andric void StackStore::Unmap(void *addr, uptr size) {
1060eae32dcSDimitry Andric   atomic_fetch_sub(&allocated_, size, memory_order_relaxed);
1070eae32dcSDimitry Andric   UnmapOrDie(addr, size);
1080eae32dcSDimitry Andric }
1090eae32dcSDimitry Andric 
Pack(Compression type)1104824e7fdSDimitry Andric uptr StackStore::Pack(Compression type) {
1114824e7fdSDimitry Andric   uptr res = 0;
1120eae32dcSDimitry Andric   for (BlockInfo &b : blocks_) res += b.Pack(type, this);
1134824e7fdSDimitry Andric   return res;
114349cc55cSDimitry Andric }
115349cc55cSDimitry Andric 
LockAll()1160eae32dcSDimitry Andric void StackStore::LockAll() {
1170eae32dcSDimitry Andric   for (BlockInfo &b : blocks_) b.Lock();
1180eae32dcSDimitry Andric }
1190eae32dcSDimitry Andric 
UnlockAll()1200eae32dcSDimitry Andric void StackStore::UnlockAll() {
1210eae32dcSDimitry Andric   for (BlockInfo &b : blocks_) b.Unlock();
1220eae32dcSDimitry Andric }
1230eae32dcSDimitry Andric 
TestOnlyUnmap()124349cc55cSDimitry Andric void StackStore::TestOnlyUnmap() {
1250eae32dcSDimitry Andric   for (BlockInfo &b : blocks_) b.TestOnlyUnmap(this);
126349cc55cSDimitry Andric   internal_memset(this, 0, sizeof(*this));
127349cc55cSDimitry Andric }
128349cc55cSDimitry Andric 
Get() const1294824e7fdSDimitry Andric uptr *StackStore::BlockInfo::Get() const {
1304824e7fdSDimitry Andric   // Idiomatic double-checked locking uses memory_order_acquire here. But
1310eae32dcSDimitry Andric   // relaxed is fine for us, justification is similar to
1324824e7fdSDimitry Andric   // TwoLevelMap::GetOrCreate.
1334824e7fdSDimitry Andric   return reinterpret_cast<uptr *>(atomic_load_relaxed(&data_));
1344824e7fdSDimitry Andric }
1354824e7fdSDimitry Andric 
Create(StackStore * store)1360eae32dcSDimitry Andric uptr *StackStore::BlockInfo::Create(StackStore *store) {
1374824e7fdSDimitry Andric   SpinMutexLock l(&mtx_);
1384824e7fdSDimitry Andric   uptr *ptr = Get();
1394824e7fdSDimitry Andric   if (!ptr) {
1400eae32dcSDimitry Andric     ptr = reinterpret_cast<uptr *>(store->Map(kBlockSizeBytes, "StackStore"));
1414824e7fdSDimitry Andric     atomic_store(&data_, reinterpret_cast<uptr>(ptr), memory_order_release);
1424824e7fdSDimitry Andric   }
1434824e7fdSDimitry Andric   return ptr;
1444824e7fdSDimitry Andric }
1454824e7fdSDimitry Andric 
GetOrCreate(StackStore * store)1460eae32dcSDimitry Andric uptr *StackStore::BlockInfo::GetOrCreate(StackStore *store) {
1474824e7fdSDimitry Andric   uptr *ptr = Get();
1484824e7fdSDimitry Andric   if (LIKELY(ptr))
1494824e7fdSDimitry Andric     return ptr;
1500eae32dcSDimitry Andric   return Create(store);
1514824e7fdSDimitry Andric }
1524824e7fdSDimitry Andric 
1530eae32dcSDimitry Andric class SLeb128Encoder {
1540eae32dcSDimitry Andric  public:
SLeb128Encoder(u8 * begin,u8 * end)1550eae32dcSDimitry Andric   SLeb128Encoder(u8 *begin, u8 *end) : begin(begin), end(end) {}
1560eae32dcSDimitry Andric 
operator ==(const SLeb128Encoder & other) const1570eae32dcSDimitry Andric   bool operator==(const SLeb128Encoder &other) const {
1580eae32dcSDimitry Andric     return begin == other.begin;
1590eae32dcSDimitry Andric   }
1600eae32dcSDimitry Andric 
operator !=(const SLeb128Encoder & other) const1610eae32dcSDimitry Andric   bool operator!=(const SLeb128Encoder &other) const {
1620eae32dcSDimitry Andric     return begin != other.begin;
1630eae32dcSDimitry Andric   }
1640eae32dcSDimitry Andric 
operator =(uptr v)1650eae32dcSDimitry Andric   SLeb128Encoder &operator=(uptr v) {
1660eae32dcSDimitry Andric     sptr diff = v - previous;
1670eae32dcSDimitry Andric     begin = EncodeSLEB128(diff, begin, end);
1680eae32dcSDimitry Andric     previous = v;
1690eae32dcSDimitry Andric     return *this;
1700eae32dcSDimitry Andric   }
operator *()1710eae32dcSDimitry Andric   SLeb128Encoder &operator*() { return *this; }
operator ++()1720eae32dcSDimitry Andric   SLeb128Encoder &operator++() { return *this; }
1730eae32dcSDimitry Andric 
base() const1740eae32dcSDimitry Andric   u8 *base() const { return begin; }
1750eae32dcSDimitry Andric 
1760eae32dcSDimitry Andric  private:
1770eae32dcSDimitry Andric   u8 *begin;
1780eae32dcSDimitry Andric   u8 *end;
1790eae32dcSDimitry Andric   uptr previous = 0;
1800eae32dcSDimitry Andric };
1810eae32dcSDimitry Andric 
1820eae32dcSDimitry Andric class SLeb128Decoder {
1830eae32dcSDimitry Andric  public:
SLeb128Decoder(const u8 * begin,const u8 * end)1840eae32dcSDimitry Andric   SLeb128Decoder(const u8 *begin, const u8 *end) : begin(begin), end(end) {}
1850eae32dcSDimitry Andric 
operator ==(const SLeb128Decoder & other) const1860eae32dcSDimitry Andric   bool operator==(const SLeb128Decoder &other) const {
1870eae32dcSDimitry Andric     return begin == other.begin;
1880eae32dcSDimitry Andric   }
1890eae32dcSDimitry Andric 
operator !=(const SLeb128Decoder & other) const1900eae32dcSDimitry Andric   bool operator!=(const SLeb128Decoder &other) const {
1910eae32dcSDimitry Andric     return begin != other.begin;
1920eae32dcSDimitry Andric   }
1930eae32dcSDimitry Andric 
operator *()1940eae32dcSDimitry Andric   uptr operator*() {
1950eae32dcSDimitry Andric     sptr diff;
1960eae32dcSDimitry Andric     begin = DecodeSLEB128(begin, end, &diff);
1970eae32dcSDimitry Andric     previous += diff;
1980eae32dcSDimitry Andric     return previous;
1990eae32dcSDimitry Andric   }
operator ++()2000eae32dcSDimitry Andric   SLeb128Decoder &operator++() { return *this; }
2010eae32dcSDimitry Andric 
operator ++(int)2020eae32dcSDimitry Andric   SLeb128Decoder operator++(int) { return *this; }
2030eae32dcSDimitry Andric 
2040eae32dcSDimitry Andric  private:
2050eae32dcSDimitry Andric   const u8 *begin;
2060eae32dcSDimitry Andric   const u8 *end;
2070eae32dcSDimitry Andric   uptr previous = 0;
2080eae32dcSDimitry Andric };
2090eae32dcSDimitry Andric 
CompressDelta(const uptr * from,const uptr * from_end,u8 * to,u8 * to_end)2100eae32dcSDimitry Andric static u8 *CompressDelta(const uptr *from, const uptr *from_end, u8 *to,
2110eae32dcSDimitry Andric                          u8 *to_end) {
2120eae32dcSDimitry Andric   SLeb128Encoder encoder(to, to_end);
2130eae32dcSDimitry Andric   for (; from != from_end; ++from, ++encoder) *encoder = *from;
2140eae32dcSDimitry Andric   return encoder.base();
2150eae32dcSDimitry Andric }
2160eae32dcSDimitry Andric 
UncompressDelta(const u8 * from,const u8 * from_end,uptr * to,uptr * to_end)2170eae32dcSDimitry Andric static uptr *UncompressDelta(const u8 *from, const u8 *from_end, uptr *to,
2180eae32dcSDimitry Andric                              uptr *to_end) {
2190eae32dcSDimitry Andric   SLeb128Decoder decoder(from, from_end);
2200eae32dcSDimitry Andric   SLeb128Decoder end(from_end, from_end);
2210eae32dcSDimitry Andric   for (; decoder != end; ++to, ++decoder) *to = *decoder;
2220eae32dcSDimitry Andric   CHECK_EQ(to, to_end);
2230eae32dcSDimitry Andric   return to;
2240eae32dcSDimitry Andric }
2250eae32dcSDimitry Andric 
CompressLzw(const uptr * from,const uptr * from_end,u8 * to,u8 * to_end)2260eae32dcSDimitry Andric static u8 *CompressLzw(const uptr *from, const uptr *from_end, u8 *to,
2270eae32dcSDimitry Andric                        u8 *to_end) {
2280eae32dcSDimitry Andric   SLeb128Encoder encoder(to, to_end);
2290eae32dcSDimitry Andric   encoder = LzwEncode<uptr>(from, from_end, encoder);
2300eae32dcSDimitry Andric   return encoder.base();
2310eae32dcSDimitry Andric }
2320eae32dcSDimitry Andric 
UncompressLzw(const u8 * from,const u8 * from_end,uptr * to,uptr * to_end)2330eae32dcSDimitry Andric static uptr *UncompressLzw(const u8 *from, const u8 *from_end, uptr *to,
2340eae32dcSDimitry Andric                            uptr *to_end) {
2350eae32dcSDimitry Andric   SLeb128Decoder decoder(from, from_end);
2360eae32dcSDimitry Andric   SLeb128Decoder end(from_end, from_end);
2370eae32dcSDimitry Andric   to = LzwDecode<uptr>(decoder, end, to);
2380eae32dcSDimitry Andric   CHECK_EQ(to, to_end);
2390eae32dcSDimitry Andric   return to;
2400eae32dcSDimitry Andric }
2410eae32dcSDimitry Andric 
24204eeddc0SDimitry Andric #if defined(_MSC_VER) && !defined(__clang__)
24304eeddc0SDimitry Andric #  pragma warning(push)
24404eeddc0SDimitry Andric // Disable 'nonstandard extension used: zero-sized array in struct/union'.
24504eeddc0SDimitry Andric #  pragma warning(disable : 4200)
24604eeddc0SDimitry Andric #endif
2470eae32dcSDimitry Andric namespace {
2480eae32dcSDimitry Andric struct PackedHeader {
2490eae32dcSDimitry Andric   uptr size;
2500eae32dcSDimitry Andric   StackStore::Compression type;
2510eae32dcSDimitry Andric   u8 data[];
2520eae32dcSDimitry Andric };
2530eae32dcSDimitry Andric }  // namespace
25404eeddc0SDimitry Andric #if defined(_MSC_VER) && !defined(__clang__)
25504eeddc0SDimitry Andric #  pragma warning(pop)
25604eeddc0SDimitry Andric #endif
2570eae32dcSDimitry Andric 
GetOrUnpack(StackStore * store)2580eae32dcSDimitry Andric uptr *StackStore::BlockInfo::GetOrUnpack(StackStore *store) {
2594824e7fdSDimitry Andric   SpinMutexLock l(&mtx_);
2604824e7fdSDimitry Andric   switch (state) {
2614824e7fdSDimitry Andric     case State::Storing:
2624824e7fdSDimitry Andric       state = State::Unpacked;
2634824e7fdSDimitry Andric       FALLTHROUGH;
2644824e7fdSDimitry Andric     case State::Unpacked:
2654824e7fdSDimitry Andric       return Get();
2664824e7fdSDimitry Andric     case State::Packed:
2674824e7fdSDimitry Andric       break;
2684824e7fdSDimitry Andric   }
2694824e7fdSDimitry Andric 
2700eae32dcSDimitry Andric   u8 *ptr = reinterpret_cast<u8 *>(Get());
2714824e7fdSDimitry Andric   CHECK_NE(nullptr, ptr);
2720eae32dcSDimitry Andric   const PackedHeader *header = reinterpret_cast<const PackedHeader *>(ptr);
2730eae32dcSDimitry Andric   CHECK_LE(header->size, kBlockSizeBytes);
2740eae32dcSDimitry Andric   CHECK_GE(header->size, sizeof(PackedHeader));
2750eae32dcSDimitry Andric 
2760eae32dcSDimitry Andric   uptr packed_size_aligned = RoundUpTo(header->size, GetPageSizeCached());
2770eae32dcSDimitry Andric 
2780eae32dcSDimitry Andric   uptr *unpacked =
2790eae32dcSDimitry Andric       reinterpret_cast<uptr *>(store->Map(kBlockSizeBytes, "StackStoreUnpack"));
2800eae32dcSDimitry Andric 
2810eae32dcSDimitry Andric   uptr *unpacked_end;
2820eae32dcSDimitry Andric   switch (header->type) {
2830eae32dcSDimitry Andric     case Compression::Delta:
2840eae32dcSDimitry Andric       unpacked_end = UncompressDelta(header->data, ptr + header->size, unpacked,
2850eae32dcSDimitry Andric                                      unpacked + kBlockSizeFrames);
2860eae32dcSDimitry Andric       break;
2870eae32dcSDimitry Andric     case Compression::LZW:
2880eae32dcSDimitry Andric       unpacked_end = UncompressLzw(header->data, ptr + header->size, unpacked,
2890eae32dcSDimitry Andric                                    unpacked + kBlockSizeFrames);
2900eae32dcSDimitry Andric       break;
2910eae32dcSDimitry Andric     default:
2920eae32dcSDimitry Andric       UNREACHABLE("Unexpected type");
2930eae32dcSDimitry Andric       break;
2940eae32dcSDimitry Andric   }
2950eae32dcSDimitry Andric 
2960eae32dcSDimitry Andric   CHECK_EQ(kBlockSizeFrames, unpacked_end - unpacked);
2970eae32dcSDimitry Andric 
2980eae32dcSDimitry Andric   MprotectReadOnly(reinterpret_cast<uptr>(unpacked), kBlockSizeBytes);
2990eae32dcSDimitry Andric   atomic_store(&data_, reinterpret_cast<uptr>(unpacked), memory_order_release);
3000eae32dcSDimitry Andric   store->Unmap(ptr, packed_size_aligned);
3010eae32dcSDimitry Andric 
3024824e7fdSDimitry Andric   state = State::Unpacked;
3034824e7fdSDimitry Andric   return Get();
3044824e7fdSDimitry Andric }
3054824e7fdSDimitry Andric 
Pack(Compression type,StackStore * store)3060eae32dcSDimitry Andric uptr StackStore::BlockInfo::Pack(Compression type, StackStore *store) {
3074824e7fdSDimitry Andric   if (type == Compression::None)
3084824e7fdSDimitry Andric     return 0;
3094824e7fdSDimitry Andric 
3104824e7fdSDimitry Andric   SpinMutexLock l(&mtx_);
3114824e7fdSDimitry Andric   switch (state) {
3124824e7fdSDimitry Andric     case State::Unpacked:
3134824e7fdSDimitry Andric     case State::Packed:
3144824e7fdSDimitry Andric       return 0;
3154824e7fdSDimitry Andric     case State::Storing:
3164824e7fdSDimitry Andric       break;
3174824e7fdSDimitry Andric   }
3184824e7fdSDimitry Andric 
3194824e7fdSDimitry Andric   uptr *ptr = Get();
3204824e7fdSDimitry Andric   if (!ptr || !Stored(0))
3214824e7fdSDimitry Andric     return 0;
3224824e7fdSDimitry Andric 
3230eae32dcSDimitry Andric   u8 *packed =
3240eae32dcSDimitry Andric       reinterpret_cast<u8 *>(store->Map(kBlockSizeBytes, "StackStorePack"));
3250eae32dcSDimitry Andric   PackedHeader *header = reinterpret_cast<PackedHeader *>(packed);
3260eae32dcSDimitry Andric   u8 *alloc_end = packed + kBlockSizeBytes;
3270eae32dcSDimitry Andric 
3280eae32dcSDimitry Andric   u8 *packed_end = nullptr;
3290eae32dcSDimitry Andric   switch (type) {
3300eae32dcSDimitry Andric     case Compression::Delta:
3310eae32dcSDimitry Andric       packed_end =
3320eae32dcSDimitry Andric           CompressDelta(ptr, ptr + kBlockSizeFrames, header->data, alloc_end);
3330eae32dcSDimitry Andric       break;
3340eae32dcSDimitry Andric     case Compression::LZW:
3350eae32dcSDimitry Andric       packed_end =
3360eae32dcSDimitry Andric           CompressLzw(ptr, ptr + kBlockSizeFrames, header->data, alloc_end);
3370eae32dcSDimitry Andric       break;
3380eae32dcSDimitry Andric     default:
3390eae32dcSDimitry Andric       UNREACHABLE("Unexpected type");
3400eae32dcSDimitry Andric       break;
3410eae32dcSDimitry Andric   }
3420eae32dcSDimitry Andric 
3430eae32dcSDimitry Andric   header->type = type;
3440eae32dcSDimitry Andric   header->size = packed_end - packed;
3450eae32dcSDimitry Andric 
3460eae32dcSDimitry Andric   VPrintf(1, "Packed block of %zu KiB to %zu KiB\n", kBlockSizeBytes >> 10,
3470eae32dcSDimitry Andric           header->size >> 10);
3480eae32dcSDimitry Andric 
3490eae32dcSDimitry Andric   if (kBlockSizeBytes - header->size < kBlockSizeBytes / 8) {
3500eae32dcSDimitry Andric     VPrintf(1, "Undo and keep block unpacked\n");
3510eae32dcSDimitry Andric     MprotectReadOnly(reinterpret_cast<uptr>(ptr), kBlockSizeBytes);
3520eae32dcSDimitry Andric     store->Unmap(packed, kBlockSizeBytes);
3530eae32dcSDimitry Andric     state = State::Unpacked;
3540eae32dcSDimitry Andric     return 0;
3550eae32dcSDimitry Andric   }
3560eae32dcSDimitry Andric 
3570eae32dcSDimitry Andric   uptr packed_size_aligned = RoundUpTo(header->size, GetPageSizeCached());
3580eae32dcSDimitry Andric   store->Unmap(packed + packed_size_aligned,
3590eae32dcSDimitry Andric                kBlockSizeBytes - packed_size_aligned);
3600eae32dcSDimitry Andric   MprotectReadOnly(reinterpret_cast<uptr>(packed), packed_size_aligned);
3610eae32dcSDimitry Andric 
3620eae32dcSDimitry Andric   atomic_store(&data_, reinterpret_cast<uptr>(packed), memory_order_release);
3630eae32dcSDimitry Andric   store->Unmap(ptr, kBlockSizeBytes);
3640eae32dcSDimitry Andric 
3654824e7fdSDimitry Andric   state = State::Packed;
3660eae32dcSDimitry Andric   return kBlockSizeBytes - packed_size_aligned;
3674824e7fdSDimitry Andric }
3684824e7fdSDimitry Andric 
TestOnlyUnmap(StackStore * store)3690eae32dcSDimitry Andric void StackStore::BlockInfo::TestOnlyUnmap(StackStore *store) {
3704824e7fdSDimitry Andric   if (uptr *ptr = Get())
3710eae32dcSDimitry Andric     store->Unmap(ptr, kBlockSizeBytes);
3724824e7fdSDimitry Andric }
3734824e7fdSDimitry Andric 
Stored(uptr n)3744824e7fdSDimitry Andric bool StackStore::BlockInfo::Stored(uptr n) {
3754824e7fdSDimitry Andric   return n + atomic_fetch_add(&stored_, n, memory_order_release) ==
3764824e7fdSDimitry Andric          kBlockSizeFrames;
3774824e7fdSDimitry Andric }
3784824e7fdSDimitry Andric 
IsPacked() const3794824e7fdSDimitry Andric bool StackStore::BlockInfo::IsPacked() const {
3804824e7fdSDimitry Andric   SpinMutexLock l(&mtx_);
3814824e7fdSDimitry Andric   return state == State::Packed;
3824824e7fdSDimitry Andric }
3834824e7fdSDimitry Andric 
384349cc55cSDimitry Andric }  // namespace __sanitizer
385