Skip to content

Commit af3c9da

Browse files
committed
Build per allocation statistics.
1 parent 5422e2f commit af3c9da

File tree

6 files changed

+142
-7
lines changed

6 files changed

+142
-7
lines changed

src/snmalloc/mem/allocstats.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#include "../ds_core/ds_core.h"
2+
#include <array>
3+
#include "sizeclasstable.h"
4+
5+
namespace snmalloc
6+
{
7+
class MonotoneStat
8+
{
9+
size_t value{0};
10+
public:
11+
void operator++(int)
12+
{
13+
value++;
14+
}
15+
16+
void operator+=(const MonotoneStat& other)
17+
{
18+
value += other.value;
19+
}
20+
21+
size_t operator*()
22+
{
23+
return value;
24+
}
25+
};
26+
27+
struct AllocStat
28+
{
29+
MonotoneStat objects_allocated{};
30+
MonotoneStat objects_deallocated{};
31+
// MonotoneStat slabs_allocated;
32+
// MonotoneStat slabs_deallocated;
33+
};
34+
35+
class AllocStats
36+
{
37+
std::array<AllocStat, SIZECLASS_REP_SIZE> sizeclass{};
38+
39+
public:
40+
AllocStat& operator[](sizeclass_t index)
41+
{
42+
auto i = index.raw();
43+
return sizeclass[i];
44+
}
45+
46+
AllocStat& operator[](smallsizeclass_t index)
47+
{
48+
return sizeclass[sizeclass_t::from_small_class(index).raw()];
49+
}
50+
51+
AllocStat operator+=(const AllocStats& other)
52+
{
53+
AllocStat result;
54+
for (size_t i = 0; i < SIZECLASS_REP_SIZE; i++)
55+
{
56+
sizeclass[i].objects_allocated += other.sizeclass[i].objects_allocated;
57+
sizeclass[i].objects_deallocated += other.sizeclass[i].objects_deallocated;
58+
// result.slabs_allocated += other.sizeclass[i].slabs_allocated;
59+
// result.slabs_deallocated += other.sizeclass[i].slabs_deallocated;
60+
}
61+
return result;
62+
}
63+
};
64+
} // namespace snmalloc

src/snmalloc/mem/corealloc.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ namespace snmalloc
111111
*/
112112
Ticker<typename Config::Pal> ticker;
113113

114+
/**
115+
* Tracks this allocators memory usage
116+
*/
117+
AllocStats stats;
118+
114119
/**
115120
* The message queue needs to be accessible from other threads
116121
*
@@ -672,13 +677,14 @@ namespace snmalloc
672677
// pointers
673678
auto& entry =
674679
Config::Backend::template get_metaentry(snmalloc::address_cast(p));
675-
if (SNMALLOC_LIKELY(dealloc_local_object_fast(entry, p, entropy)))
680+
if (SNMALLOC_LIKELY(dealloc_local_object_fast<false>(entry, p, entropy)))
676681
return;
677682

678683
dealloc_local_object_slow(p, entry);
679684
}
680685

681-
SNMALLOC_FAST_PATH static bool dealloc_local_object_fast(
686+
template<bool Statistics = true>
687+
SNMALLOC_FAST_PATH bool dealloc_local_object_fast(
682688
const PagemapEntry& entry,
683689
CapPtr<void, capptr::bounds::Alloc> p,
684690
LocalEntropy& entropy)
@@ -699,6 +705,10 @@ namespace snmalloc
699705
// Update the head and the next pointer in the free list.
700706
meta->free_queue.add(cp, key, entropy);
701707

708+
if constexpr (Statistics)
709+
{
710+
stats[entry.get_sizeclass()].objects_deallocated++;
711+
}
702712
return SNMALLOC_LIKELY(!meta->return_object());
703713
}
704714

@@ -745,6 +755,7 @@ namespace snmalloc
745755
}
746756

747757
auto r = finish_alloc<zero_mem, Config>(p, sizeclass);
758+
stats[sizeclass].objects_allocated++;
748759
return ticker.check_tick(r);
749760
}
750761
return small_alloc_slow<zero_mem>(sizeclass, fast_free_list);
@@ -817,6 +828,8 @@ namespace snmalloc
817828
}
818829

819830
auto r = finish_alloc<zero_mem, Config>(p, sizeclass);
831+
832+
stats[sizeclass].objects_allocated++;
820833
return ticker.check_tick(r);
821834
}
822835

@@ -992,6 +1005,11 @@ namespace snmalloc
9921005

9931006
return debug_is_empty_impl(result);
9941007
}
1008+
1009+
const AllocStats& get_stats()
1010+
{
1011+
return stats;
1012+
}
9951013
};
9961014

9971015
/**

src/snmalloc/mem/globalalloc.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,36 @@ namespace snmalloc
137137
}
138138
}
139139

140+
template<SNMALLOC_CONCEPT(ConceptBackendGlobals) SharedStateHandle>
141+
inline static AllocStats get_stats()
142+
{
143+
auto alloc = AllocPool<SharedStateHandle>::iterate();
144+
AllocStats stats;
145+
while (alloc != nullptr)
146+
{
147+
stats += alloc->get_stats();
148+
alloc = AllocPool<SharedStateHandle>::iterate(alloc);
149+
}
150+
return stats;
151+
}
152+
153+
template<SNMALLOC_CONCEPT(ConceptBackendGlobals) SharedStateHandle>
154+
inline static void print_alloc_stats()
155+
{
156+
#ifndef SNMALLOC_PASS_THROUGH // This test depends on snmalloc internals
157+
auto stats = snmalloc::get_stats<SharedStateHandle>();
158+
for (size_t i = 0; i < snmalloc::SIZECLASS_REP_SIZE; i++)
159+
{
160+
auto sc = snmalloc::sizeclass_t::from_raw(i);
161+
auto allocated = *stats[sc].objects_allocated;
162+
auto deallocated = *stats[sc].objects_deallocated;
163+
if (allocated == 0 && deallocated == 0)
164+
continue;
165+
auto size =
166+
snmalloc::sizeclass_full_to_size(snmalloc::sizeclass_t::from_raw(i));
167+
auto in_use = allocated - deallocated;
168+
snmalloc::message<1024>("{},{},{},{},{}", i, size, allocated, deallocated, in_use);
169+
}
170+
#endif
171+
}
140172
} // namespace snmalloc

src/snmalloc/mem/localalloc.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ namespace snmalloc
211211
chunk.unsafe_ptr(), bits::next_pow2(size));
212212
}
213213

214+
if (chunk.unsafe_ptr() != nullptr)
215+
core_alloc->stats[size_to_sizeclass_full(size)].objects_allocated++;
216+
214217
return capptr_chunk_is_alloc(capptr_to_user_address_control(chunk));
215218
});
216219
}
@@ -246,7 +249,7 @@ namespace snmalloc
246249
};
247250

248251
return local_cache.template alloc<zero_mem, Config>(
249-
domesticate, size, slowpath);
252+
domesticate, core_alloc->stats, size, slowpath);
250253
}
251254

252255
/**
@@ -648,7 +651,7 @@ namespace snmalloc
648651
{
649652
dealloc_cheri_checks(p_tame.unsafe_ptr());
650653

651-
if (SNMALLOC_LIKELY(CoreAlloc::dealloc_local_object_fast(
654+
if (SNMALLOC_LIKELY(core_alloc->dealloc_local_object_fast(
652655
entry, p_tame, local_cache.entropy)))
653656
return;
654657
core_alloc->dealloc_local_object_slow(p_tame, entry);

src/snmalloc/mem/localcache.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include "../ds/ds.h"
4+
#include "allocstats.h"
45
#include "freelist.h"
56
#include "remotecache.h"
67
#include "sizeclasstable.h"
@@ -95,14 +96,15 @@ namespace snmalloc
9596
typename Slowpath,
9697
typename Domesticator>
9798
SNMALLOC_FAST_PATH capptr::Alloc<void>
98-
alloc(Domesticator domesticate, size_t size, Slowpath slowpath)
99+
alloc(Domesticator domesticate, AllocStats& stats, size_t size, Slowpath slowpath)
99100
{
100101
auto& key = entropy.get_free_list_key();
101102
smallsizeclass_t sizeclass = size_to_sizeclass(size);
102103
auto& fl = small_fast_free_lists[sizeclass];
103104
if (SNMALLOC_LIKELY(!fl.empty()))
104105
{
105106
auto p = fl.take(key, domesticate);
107+
stats[sizeclass].objects_allocated++;
106108
return finish_alloc<zero_mem, Config>(p, sizeclass);
107109
}
108110
return slowpath(sizeclass, &fl);

src/test/func/statistics/stats.cc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@ void debug_check_empty_1()
1818
auto r = a.alloc(size);
1919

2020
snmalloc::debug_check_empty<snmalloc::StandardConfig>(&result);
21+
snmalloc::print_alloc_stats<snmalloc::StandardConfig>();
2122
if (result != false)
2223
{
2324
std::cout << "debug_check_empty failed to detect leaked memory:" << size
2425
<< std::endl;
2526
abort();
2627
}
2728

28-
a.dealloc(r);
29+
snmalloc::print_alloc_stats<snmalloc::Globals>();
30+
31+
a.dealloc(r);
32+
33+
snmalloc::print_alloc_stats<snmalloc::Globals>();
2934

3035
snmalloc::debug_check_empty<snmalloc::StandardConfig>(&result);
3136
if (result != true)
@@ -34,7 +39,11 @@ void debug_check_empty_1()
3439
abort();
3540
}
3641

37-
r = a.alloc(size);
42+
snmalloc::print_alloc_stats<snmalloc::StandardConfig>();
43+
44+
r = a.alloc(16);
45+
46+
snmalloc::print_alloc_stats<snmalloc::StandardConfig>();
3847

3948
snmalloc::debug_check_empty<snmalloc::StandardConfig>(&result);
4049
if (result != false)
@@ -44,14 +53,21 @@ void debug_check_empty_1()
4453
abort();
4554
}
4655

56+
snmalloc::print_alloc_stats<snmalloc::Globals>();
57+
4758
a.dealloc(r);
4859

60+
snmalloc::print_alloc_stats<snmalloc::StandardConfig>();
61+
4962
snmalloc::debug_check_empty<snmalloc::StandardConfig>(&result);
5063
if (result != true)
5164
{
5265
std::cout << "debug_check_empty failed to say empty:" << size << std::endl;
5366
abort();
5467
}
68+
69+
snmalloc::print_alloc_stats<snmalloc::StandardConfig>();
70+
#endif
5571
}
5672

5773
template<size_t size>

0 commit comments

Comments
 (0)