27#include <flow/util/util.hpp>
33#include <flow/util/stat/histo.hpp>
45 static const bool s_enabled = ([]() ->
bool
48 return (
mallctl_read(
"config.stats", &enabled) == 0) && enabled;
55 static const size_t s_page_size = ([]() ->
size_t
68 uint64_t epoch_in = 1;
69 uint64_t epoch_out = 0;
70 size_t epoch_out_sz =
sizeof(epoch_out);
77 uint64_t epoch_out = 0;
88 flow::util::stat::Histogram_counter* alloc_count_by_size_ptr)
90 using flow::util::ostream_op_string;
91 using flow::util::stat::Histogram_counter;
92 using value_t = Histogram_counter::value_t;
96 using uint =
unsigned int;
98 static_assert(
sizeof(value_t) ==
sizeof(
size_t),
99 "We read size_t-typed size-class byte-sizes (arenas.bin.<j>.size, etc.) directly into value_t "
100 "elements; mallctl() checks only the byte-size of the target.");
102 assert(alloc_sz_ptr && dealloc_sz_ptr && alloc_count_by_size_ptr);
103 auto& alloc_sz = *alloc_sz_ptr;
104 auto& dealloc_sz = *dealloc_sz_ptr;
105 auto& histo = *alloc_count_by_size_ptr;
117 vector<value_t> m_bin_szs;
118 vector<value_t> m_lext_szs;
119 vector<value_t> m_all_szs;
120 vector<value_t> m_histo_lo_bounds;
121 value_t m_histo_last_bucket_sz = 1;
122 array<size_t, 6> m_bin_nmalloc_mib{}, m_bin_ndalloc_mib{};
123 array<size_t, 6> m_lext_nmalloc_mib{}, m_lext_ndalloc_mib{};
124 size_t m_bin_mib_len = 6, m_lext_mib_len = 6;
126 static const Tables s_tables = ([]() -> Tables
132 tables.m_bin_szs.resize(n_bins);
133 for (uint idx = 0; idx != n_bins; ++idx)
135 mallctl_read(ostream_op_string(
"arenas.bin.", idx,
".size").c_str(), &tables.m_bin_szs[idx]);
140 tables.m_lext_szs.resize(n_lext);
141 for (uint idx = 0; idx != n_lext; ++idx)
143 mallctl_read(ostream_op_string(
"arenas.lextent.", idx,
".size").c_str(), &tables.m_lext_szs[idx]);
146 tables.m_all_szs.reserve(tables.m_bin_szs.size() + tables.m_lext_szs.size());
147 tables.m_all_szs.insert(tables.m_all_szs.end(), tables.m_bin_szs.begin(), tables.m_bin_szs.end());
148 tables.m_all_szs.insert(tables.m_all_szs.end(), tables.m_lext_szs.begin(), tables.m_lext_szs.end());
153 const auto n_all = tables.m_all_szs.size();
154 tables.m_histo_lo_bounds.resize(n_all);
155 tables.m_histo_lo_bounds[0] = 1;
156 for (
size_t idx = 1; idx != n_all; ++idx)
158 tables.m_histo_lo_bounds[idx] = (tables.m_all_szs[idx - 1] + 1);
160 tables.m_histo_last_bucket_sz = (tables.m_all_szs[n_all - 1] - tables.m_all_szs[n_all - 2]);
164 mallctl_name_to_mib(
"stats.arenas.0.bins.0.nmalloc", tables.m_bin_nmalloc_mib.data(), &tables.m_bin_mib_len);
166 mallctl_name_to_mib(
"stats.arenas.0.bins.0.ndalloc", tables.m_bin_ndalloc_mib.data(), &len);
167 mallctl_name_to_mib(
"stats.arenas.0.lextents.0.nmalloc", tables.m_lext_nmalloc_mib.data(), &tables.m_lext_mib_len);
169 mallctl_name_to_mib(
"stats.arenas.0.lextents.0.ndalloc", tables.m_lext_ndalloc_mib.data(), &len);
174 alloc_sz = dealloc_sz = 0;
176 histo = Histogram_counter{s_tables.m_histo_lo_bounds, s_tables.m_histo_last_bucket_sz};
182 const auto walk = [&](
const auto& nmalloc_mib,
const auto& ndalloc_mib,
183 size_t mib_len,
const vector<value_t>& szs,
size_t bucket_offset)
185 array<size_t, 6> mib_n{};
186 array<size_t, 6> mib_d{};
187 copy_n(nmalloc_mib.data(), mib_len, mib_n.data());
188 copy_n(ndalloc_mib.data(), mib_len, mib_d.data());
189 mib_n[2] = mib_d[2] = arena_id;
190 for (
size_t idx = 0; idx != szs.size(); ++idx)
192 mib_n[4] = mib_d[4] = idx;
193 uint64_t count_n = 0;
194 uint64_t count_d = 0;
197 const auto sz = uint64_t(szs[idx]);
198 alloc_sz += (count_n * sz);
199 dealloc_sz += (count_d * sz);
200 histo.overwrite_count_for_bucket(bucket_offset + idx, count_n);
204 walk(s_tables.m_bin_nmalloc_mib, s_tables.m_bin_ndalloc_mib,
205 s_tables.m_bin_mib_len, s_tables.m_bin_szs, 0);
206 walk(s_tables.m_lext_nmalloc_mib, s_tables.m_lext_ndalloc_mib,
207 s_tables.m_lext_mib_len, s_tables.m_lext_szs, s_tables.m_bin_szs.size());
Stats-related sub-namespace within SHM-jemalloc's detail, for general organization (and ADL segregati...
size_t page_size()
Returns the jemalloc page size in bytes (arenas.page), read once and cached (constant for the process...
bool config_stats_enabled()
Returns whether the linked jemalloc was built with statistics support (its config....
uint64_t epoch_refresh()
Refreshes jemalloc's statistics snapshot (writes mallctl("epoch")), so that subsequently-read "stats....
int mallctl_read_mib(const size_t *mib, size_t mib_len, T *val)
Like mallctl_read() but addresses the value by a previously-resolved MIB (see mallctl_name_to_mib()) ...
bool mallctl_name_to_mib(const char *name, size_t *mib, size_t *mib_len)
Resolves a mallctl name – which may contain integer components (e.g.
uint64_t epoch()
Returns jemalloc's current statistics epoch (reads mallctl("epoch")), without refreshing anything.
void cumulative_alloc_dealloc_sz(arena_id_t arena_id, uint64_t *alloc_sz, uint64_t *dealloc_sz, flow::util::stat::Histogram_counter *alloc_count_by_size)
Computes, for the given jemalloc-arena, the cumulative bytes-ever-allocated and bytes-ever-deallocate...
int mallctl_read(const char *name, T *val)
Reads a single scalar mallctl value, by name, into *val (using sizeof(T) as the value size).
unsigned int arena_id_t
Arena ID as dictated by jemalloc.
#define IPC_SHM_ARENA_LEND_JEMALLOC_API(ARG_api_name)
To be used when invoking jemalloc public API functions, this evaluates to the given argument (which s...