27#include "ipc/shm/arena_lend/jemalloc/jemalloc_pages.hpp"
32#include "ipc/shm/arena_lend/owner_shm_pool_listener.hpp"
35#include "ipc/shm/arena_lend/shm_pool.hpp"
39#include <flow/util/util.hpp>
40#include <flow/util/stat/stat_set.hpp>
41#include <flow/log/log.hpp>
42#include <flow/error/error.hpp>
43#include <boost/range/algorithm/sort.hpp>
44#include <boost/range/adaptor/transformed.hpp>
45#include <boost/algorithm/string.hpp>
46#include <boost/system/errc.hpp>
64 const std::shared_ptr<Memory_manager>& memory_manager,
68 using std::shared_ptr;
69 using std::unique_ptr;
72 Thread_lcl_obj_db_admin<Ipc_arena>::this_thread_piggy_scan();
75 unique_ptr<Ipc_arena> arena{
new Ipc_arena{logger, memory_manager, std::move(pool_name_base), permissions}};
81 return shared_ptr<Ipc_arena>{arena.release(),
82 [](
auto* this_arena) { this_arena->destroy(); }};
90 using flow::util::ostream_op_string;
92 using std::shared_ptr;
93 namespace str_algo = boost::algorithm;
94 using boost::adaptors::transformed;
182 Thread_lcl_obj_db_admin<Ipc_arena>::this_thread_piggy_scan();
185 FLOW_LOG_INFO(
"Ipc_arena [" <<
this <<
"] handle has reached ref-count=0. "
186 "Before totally destroying it, we issue order to the cross-thread obj-DB admin/client-modules "
187 "to forget this arena; admin-part shall occur potentially asynchronously, piggybacked onto "
188 "user API calls and/or relevant threads exiting; once all have finished this "
189 "forget-arena op, the last one to do so shall continue to destroy the Ipc_arena.");
190 FLOW_LOG_INFO(
"Ipc_arena [" <<
this <<
"] shutdown: jemalloc-arena-ID list: "
191 "[" << str_algo::join(
m_arenas | transformed([](
auto arena_id) ->
auto
192 {
return ostream_op_string(arena_id); }),
199 FLOW_LOG_INFO(
"Ipc_arena [" <<
this <<
"] shutdown: "
200 "~Final state (includes ~final this-arena + ~current global):\n"
206 Thread_lcl_obj_db_client<Ipc_arena>::forgetting_shm_arena(get_id());
218 const Log_context_mt log_ctx_sync{get_logger(), get_log_component()};
219 FLOW_LOG_SET_LOCKED_CONTEXT(&log_ctx_sync);
231 const bool obj_db_forgot_us_synchronously
232 = Thread_lcl_obj_db_admin<Ipc_arena>
233 ::forgetting_shm_arena(get_id(),
235 [this_arena_on_death_row_ptr =
this]
236 (
const Log_context_mt* log_ctx)
mutable
238 Thread_lcl_obj_db_admin<Ipc_arena>::this_thread_piggy_scan();
251 FLOW_LOG_SET_LOCKED_CONTEXT(log_ctx);
252 FLOW_LOG_INFO_LOCKED(
"Ipc_arena death-row disposer executing for one of the per-thread admins, in thread "
253 "different from the one in which the shared_ptr<Ipc_arena> disposer initiated shutdown; "
254 "according to that last obj-DB admin: arena is safe to continue to destroy.");
256 this_arena_on_death_row_ptr->destroy_on_obj_db_forgot_us(log_ctx);
259 if (!obj_db_forgot_us_synchronously)
268 FLOW_LOG_INFO_LOCKED(
"shared_ptr<Ipc_arena> disposer initiated shutdown; obj-DB reports that no async "
269 "arena-forgetting work remains, so we can immediately/synchronously continue to "
281 using std::shared_ptr;
283 assert((!get_logger()) &&
"Regular get_logger()-based logging should have been disabled earlier by "
284 "destroy(); it's *log_ctx or bust from now on.");
289 FLOW_LOG_SET_LOCKED_CONTEXT(log_ctx);
343 FLOW_LOG_TRACE_LOCKED(
"Notifying listeners of [" <<
m_shm_pools.size() <<
"] shared memory pool removals; these "
344 "will not run via jemalloc extent-hooks anymore (though extent-hooks will execute soon; but "
345 "listeners will be skipped).");
350 cur_listener->notify_removed_shm_pool(cur_shm_pool);
369 Owner_shm_pool_repository::get_instance().erase(cur_shm_pool->get_id());
409 shared_ptr<Ipc_arena> this_arena_on_death_row{
this};
411 const auto disposer_func = [this_arena_on_death_row]
412 (
const Log_context_mt* log_ctx,
auto&& destroy_arena_func)
mutable
433 FLOW_LOG_SET_LOCKED_CONTEXT(log_ctx);
434 FLOW_LOG_INFO_LOCKED(
"Ipc_arena death-row disposer executing for one of the jemalloc-arenas; it is safe to "
435 "destroy. For internal reasons logging from resulting extent-hooks firing during this "
436 "arena-destruction has been disabled. Destroying jemalloc arena now.");
455 destroy_arena_func();
464 this_arena_on_death_row.reset();
466 using Disposer_func =
decltype(disposer_func);
471 for (
const auto arena_id :
m_arenas)
473 FLOW_LOG_INFO_LOCKED(
"Destroying arena (possibly deferred due to tcache dependency), "
474 "jemalloc-ID [" << arena_id <<
"].");
477 Disposer_func{disposer_func});
480 assert(this_arena_on_death_row);
487 const std::shared_ptr<Memory_manager>& memory_manager,
491 ++m_collection_id_counter,
492 std::move(pool_name_base),
494 m_owner_id(uint64_t(util::Process_credentials::own_process_id())),
495 m_memory_manager(memory_manager),
505 .purge_lazy =
nullptr,
510 m_destroy_started(
false)
522 assert((n_arenas != 0) &&
"At least 1 arena please.");
523 assert(
m_arenas.empty() &&
"Do not call start() twice.");
524 for (
unsigned int i = 0; i != n_arenas; ++i)
526 FLOW_LOG_INFO(
"Creating arena (idx [" << i <<
"]/0-based of [" << n_arenas <<
"]/1-based).");
551 assert(result.second);
555 FLOW_LOG_INFO(
"Created arena, jemalloc-ID [" << arena_id <<
"].");
570 using flow::util::ostream_op_string;
571 using flow::error::Runtime_error;
572 using boost::system::system_category;
573 namespace errc = boost::system::errc;
581 if (
const int ec =
mallctl_read(
"opt.retain", &retain); ec != 0)
583 throw Runtime_error{
Error_code{ec, system_category()},
"je_mallctl() error reading opt.retain."};
588 "jemalloc opt.retain is disabled (perhaps via run-time configuration such as "
589 "MALLOC_CONF); SHM-jemalloc requires it."};
595 static_assert(
false,
"The logic below might be Linux-specific and has only been tested in Linux as of this "
618 size_t input_param = Shm_pool_offset_ptr_data_base::max_pool_size();
621 nullptr,
nullptr, &input_param,
sizeof(input_param));
627 const Error_code sys_err_code{ec, system_category()};
628 FLOW_LOG_FATAL(
"Setting retain_grow_limit to [" << input_param <<
"] for new jemalloc-arena [" << arena_id <<
"] "
629 "failed despite opt.retain check: system error [" << sys_err_code <<
"] "
630 "[" << sys_err_code.message() <<
"]. Bug?");
631 assert(
false &&
"Setting retain_grow_limit failed despite opt.retain check: bug?");
640 using std::bad_alloc;
643#if IPC_SHM_ARENA_LEND_JEMALLOC_NO_TCACHE
644 constexpr bool TCACHE_ON =
false;
646 constexpr bool TCACHE_ON =
true;
656 FLOW_LOG_WARNING(
"Tried to allocate size [" << size <<
"], arena [" <<
m_arena0 <<
"], tcache on? = "
657 "[" << TCACHE_ON <<
"]; but it failed (jemalloc-allocate function returned null); "
658 "preceding logs may or may not provide more detail. Note both jemalloc-proper code and "
659 "(potentially) Flow-IPC code, via extent hooks, are executed as part of jemalloc-allocate "
660 "function. Throwing std::bad_alloc.");
670 assert(!
m_arenas.empty() &&
"start() must have been called by now.");
673#if IPC_SHM_ARENA_LEND_JEMALLOC_NO_TCACHE
675 if (skip_fast_path_verbose_logging())
681 FLOW_LOG_DATA(
"Allocating size [" << size <<
"], arena [" << arena_id <<
"], no tcache.");
683 FLOW_LOG_DATA(
"Allocated size [" << size <<
"], arena [" << arena_id <<
"], no tcache, "
684 "resulting in [" << buf <<
"].");
715 if (skip_fast_path_verbose_logging())
721 FLOW_LOG_DATA(
"Allocating size [" << size <<
"], arena [" << arena_id <<
"], "
722 "tcache [" << tcache_id_for_arena <<
"].");
724 FLOW_LOG_DATA(
"Allocated size [" << size <<
"], arena [" << arena_id <<
"], tcache "
725 "[" << tcache_id_for_arena <<
"], resulting in [" << buf <<
"].");
732 assert(!m_arenas.empty() &&
"start() must have been called by now.");
733 const auto& arena_id = m_arena0;
742#if IPC_SHM_ARENA_LEND_JEMALLOC_NO_TCACHE
743 get_jemalloc_memory_manager()->deallocate_no_tcache(address);
801 const auto tcache_id_for_arena = this_thread_cache_or_null ? this_thread_cache_or_null->id_or_none(arena_id)
812 get_jemalloc_memory_manager()->deallocate(address, tcache_id_for_arena);
820 get_jemalloc_memory_manager()->deallocate_no_tcache(address);
867#if !IPC_SHM_ARENA_LEND_JEMALLOC_NO_TCACHE
873 void* address,
size_t size,
size_t alignment,
bool* zero,
874 bool* commit,
unsigned arena_id)
876 assert(extent_hooks);
877 return static_cast<Extent_hooks*
>(extent_hooks)->get_owner()
878 ->create_shm_pool(address, size, alignment, zero, commit, arena_id);
882 void* address,
size_t size,
883 bool committed,
unsigned arena_id)
885 assert(extent_hooks);
886 return !
static_cast<Extent_hooks*
>(extent_hooks)->get_owner()
887 ->optional_remove_shm_pool(address, size, committed, arena_id);
891 void* address,
size_t size,
bool committed,
unsigned arena_id)
893 assert(extent_hooks);
895 ->remove_shm_pool(address, size, committed, arena_id);
899 void* address,
size_t size,
size_t offset,
900 size_t length,
unsigned arena_id)
902 assert(extent_hooks);
903 return !
static_cast<Extent_hooks*
>(extent_hooks)->get_owner()
904 ->commit_memory_pages(address, size, offset, length, arena_id);
908 void* address,
size_t size,
size_t offset,
size_t length,
911 assert(extent_hooks);
912 return !
static_cast<Extent_hooks*
>(extent_hooks)->get_owner()
913 ->decommit_memory_pages(address, size, offset, length, arena_id);
917 void* address,
size_t size,
size_t offset,
size_t length,
920 assert(extent_hooks);
921 return !
static_cast<Extent_hooks*
>(extent_hooks)->get_owner()
922 ->purge_forced_memory_pages(address, size, offset, length, arena_id);
926 void* address,
size_t size,
size_t size_a,
size_t size_b,
927 bool committed,
unsigned arena_id)
929 assert(extent_hooks);
930 return !
static_cast<Extent_hooks*
>(extent_hooks)->get_owner()
931 ->split_memory_pages(address, size, size_a, size_b, committed, arena_id);
935 void* address_a,
size_t size_a,
void* address_b,
size_t size_b,
936 bool committed,
unsigned arena_id)
938 assert(extent_hooks);
939 return !
static_cast<Extent_hooks*
>(extent_hooks)->get_owner()
940 ->merge_memory_pages(address_a, size_a, address_b, size_b, committed, arena_id);
947 using flow::util::stat::fetch_add;
949 using std::shared_ptr;
954 if (size > Shm_pool_offset_ptr_data_base::max_pool_size())
960 FLOW_LOG_WARNING(
"Asked to create SHM pool of size [" << size <<
"], arena [" << arena_id <<
"], exceeding the "
961 "max pool size [" << Shm_pool_offset_ptr_data_base::max_pool_size() <<
"] addressable by "
962 "our fancy-pointer type. Probably a single allocation of (nearly) that size or more was "
963 "attempted. Declining; the allocation shall fail.");
975 const auto id = Shm_pool_offset_ptr_data_base::generate_pool_id();
977 const string name = generate_shm_object_name(
id).str();
979 shared_ptr<Shm_pool> pool
980 = Owner_shm_pool_collection::create_shm_pool(
id, name, size, address,
981 [&](
int fd,
size_t size,
void* address) ->
void*
983 void*
const actual_address = Jemalloc_pages::map(address, size, alignment, *commit, fd);
992 FLOW_LOG_TRACE(
"Mapped SHM pool at address [" << actual_address <<
"].");
994 return actual_address;
1000 pool_address = pool->get_address();
1001 FLOW_LOG_TRACE(
"Created SHM pool at address [" << pool_address <<
"], name [" << name <<
"], "
1002 "size [" << size <<
"], arena [" << arena_id <<
"].");
1012 pool_address =
nullptr;
1013 FLOW_LOG_WARNING(
"Failed to create SHM pool of size [" << size <<
"].");
1031 const auto logger = get_logger();
1032 if (logger && logger->should_log(flow::log::Sev::S_TRACE, get_log_component()))
1034 print_shm_pool_map();
1037 return pool_address;
1041 [[maybe_unused]]
size_t size,
1042 [[maybe_unused]]
bool committed,
1045 using flow::util::stat::fetch_add;
1093 using flow::util::stat::fetch_add;
1094 using std::shared_ptr;
1097 Memory_decommit_functor decommit_functor
1098 = [](
const shared_ptr<Shm_pool>& shm_pool,
size_t offset,
size_t length) ->
bool
1100 return Jemalloc_pages::decommit(
reinterpret_cast<void*
>(
reinterpret_cast<uint8_t*
>(shm_pool->get_address())
1102 shm_pool->get_fd(), offset, length,
true);
1108 = remove_range_and_pool_if_empty
1111 committed ? &decommit_functor :
nullptr,
1113 [&pool_size](
const shared_ptr<Shm_pool>& shm_pool) ->
bool
1114 { Jemalloc_pages::unmap(shm_pool->get_address(), pool_size = shm_pool->get_size());
return true; },
1118 FLOW_LOG_WARNING(
"Failure when performing range removal at address [" << address <<
"], size "
1119 "[" << size <<
"], arena [" << arena_id <<
"], removed-range [" << removed_range <<
"], "
1120 "unmapped-pool [" << unmapped_pool <<
"].");
1121 return removed_range;
1126 FLOW_LOG_TRACE(
"Unmapped SHM pool when removing range at address [" << address <<
"], "
1127 "size [" << size <<
"], arena [" << arena_id <<
"].");
1128 print_shm_pool_map();
1141 return removed_range;
1149 std::shared_ptr<Shm_pool> pool;
1157 void*
const page_address =
reinterpret_cast<void*
>(
reinterpret_cast<uint8_t*
>(address) + offset);
1158 const bool result = Jemalloc_pages::commit(page_address, length);
1160 if (!skip_fast_path_verbose_logging())
1162 FLOW_LOG_TRACE(
"Committing with success [" << result <<
"], page address "
1163 "[" << page_address <<
"], length [" << length <<
"] in pool [" << *pool <<
"].");
1174 std::shared_ptr<Shm_pool> pool;
1182 void*
const page_address =
reinterpret_cast<void*
>(
reinterpret_cast<uint8_t*
>(address) + offset);
1183 const bool result = Jemalloc_pages::decommit(page_address, pool->get_fd(), pool_offset, length);
1185 if (!skip_fast_path_verbose_logging())
1187 FLOW_LOG_TRACE(
"Decommitting with success [" << result <<
"], page address [" << page_address <<
1188 "], pool offset [" << pool_offset <<
"], length [" << length <<
1189 "] in pool [" << *pool <<
"].");
1200 std::shared_ptr<Shm_pool> pool;
1214 const bool result = Jemalloc_pages::purge_forced(pool->get_fd(), pool_offset, length);
1216 if (!skip_fast_path_verbose_logging())
1218 FLOW_LOG_TRACE(
"Force-purge with success [" << result <<
"], pool offset [" << pool_offset <<
"], "
1219 "length [" << length <<
"] in pool [" << *pool <<
"].");
1228 [[maybe_unused]]
bool committed,
arena_id_t arena_id)
1231 if (!skip_fast_path_verbose_logging())
1233 FLOW_LOG_TRACE(
"Allowed split of memory pages at [" << address <<
"], size [" << size <<
"] "
1234 "to sizes [" << size_a <<
", " << size_b <<
"], arena [" << arena_id <<
"].");
1241 [[maybe_unused]]
bool committed,
1244 using std::shared_ptr;
1246 shared_ptr<Shm_pool> pool_a = lookup_shm_pool(address_a);
1249 FLOW_LOG_WARNING(
"Could not find SHM pool for address [" << address_a <<
"] in merge request.");
1253 shared_ptr<Shm_pool> pool_b = lookup_shm_pool(address_b);
1256 FLOW_LOG_WARNING(
"Could not find SHM pool for address [" << address_b <<
"] in merge request.");
1260 if (pool_a != pool_b)
1264 if (!skip_fast_path_verbose_logging())
1266 FLOW_LOG_TRACE(
"Could not merge distinct pools from address A [" << address_a <<
"] and "
1267 "B [" << address_b <<
"].");
1272 if (!Shm_pool::is_adjacent(address_a, size_a, address_b, size_b))
1276 FLOW_LOG_WARNING(
"Merge request for non-adjacent regions (address, size) [" << address_a <<
", " << size_a <<
"] "
1277 "and [" << address_b <<
", " << size_b <<
"], arena [" << arena_id <<
"].");
1282 if ((!pool_a->is_subset(address_a, size_a)) || (!pool_a->is_subset(address_b, size_b)))
1285 FLOW_LOG_WARNING(
"Merge request for a region (address, size) [" << address_a <<
", " << size_a <<
"] or "
1286 "[" << address_b <<
", " << size_b <<
"] that is not wholly within pool [" << *pool_a <<
"].");
1290 if (!skip_fast_path_verbose_logging())
1292 FLOW_LOG_TRACE(
"Allowed merge of memory pages at [" << address_a <<
"], size [" << size_a <<
"] "
1293 "with [" << address_b <<
"], size [" << size_b <<
"], arena [" << arena_id <<
"].");
1301 std::shared_ptr<Shm_pool>& pool,
size_t& pool_offset)
const
1307 pool = lookup_shm_pool(address);
1311 FLOW_LOG_WARNING(
"When " << use_case <<
" pages, could not find pool with address [" << address <<
"].");
1315 Shm_pool::size_t address_offset;
1316 if (!pool->is_subset(address, size, &address_offset))
1319 FLOW_LOG_WARNING(
"Requested range [" << address <<
"], size [" << size <<
"] does not completely "
1325 if ((offset > size) || (length > size) || (offset + length) > size)
1327 FLOW_LOG_WARNING(
"Requested offset [" << offset <<
"] and length [" << length <<
"] is > size "
1328 "[" << size <<
"].");
1332 pool_offset = address_offset + offset;
1341 const auto result_pair =
m_listeners.emplace(listener);
1342 if (!result_pair.second)
1344 FLOW_LOG_WARNING(
"Could not add already existing SHM pool listener [" << listener <<
"] to "
1345 "collection [" << get_id() <<
"].");
1352 FLOW_LOG_TRACE(
"Successfully added SHM pool listener [" << listener <<
"] to collection [" << get_id() <<
"].");
1364 FLOW_LOG_WARNING(
"Could not remove non-existent SHM pool listener [" << listener <<
"] from "
1365 "collection [" << get_id() <<
"].");
1370 FLOW_LOG_TRACE(
"Successfully removed SHM pool listener [" << listener <<
"] from collection [" << get_id() <<
"].");
1378 using std::shared_ptr;
1387 const auto shm_pool_id = shm_pool->get_id();
1394 if (!result_pair.second)
1397 FLOW_LOG_FATAL(
"Could not insert SHM pool [" << *shm_pool <<
"], existing registration "
1398 "[" << *(result_pair.first) <<
"] in collection [" << get_id() <<
"]. Bug?");
1399 assert(
false &&
"Could not insert SHM pool; existing registration; bug?");
1403 FLOW_LOG_TRACE(
"Registered SHM pool [" << shm_pool_id <<
"] in collection [" << get_id() <<
"].");
1406 Owner_shm_pool_repository::get_instance().insert(shared_ptr<Shm_pool>{shm_pool});
1411 cur_listener->notify_created_shm_pool(shm_pool);
1415 FLOW_LOG_TRACE(
"Successfully handled SHM pool creation notification of pool [" << shm_pool_id <<
"] in collection "
1416 "[" << get_id() <<
"].");
1430 const auto shm_pool_id = shm_pool->get_id();
1438 FLOW_LOG_WARNING(
"SHM pool [" << shm_pool_id <<
"] not found in collection [" << get_id() <<
"].");
1442 FLOW_LOG_TRACE(
"Deregistered SHM pool [" << shm_pool_id <<
"] from collection [" << get_id() <<
"].");
1447 cur_listener->notify_removed_shm_pool(shm_pool);
1451 Owner_shm_pool_repository::get_instance().erase(shm_pool_id);
1454 FLOW_LOG_TRACE(
"Successfully handled SHM pool removal notification of pool [" << shm_pool_id <<
"] in "
1455 "collection [" << get_id() <<
"].");
1480 using flow::util::stat::stats_assign;
1483 assert(target_info_dump);
1503 vector<Shm_pool_info> shm_pools_id_sorted;
1504 for_each_shm_pool([&](
auto&& shm_pool_ptr)
1506 shm_pools_id_sorted.push_back({ shm_pool_ptr->get_id(),
static_cast<size_t>(shm_pool_ptr->get_size()),
1510 return shm_pools_id_sorted;
1516 using flow::util::stat::stats_assign;
1546 return Pool_lookup_global_stats<Ipc_arena, true>::stats();
1553 return Thread_lcl_obj_db_client<Ipc_arena>::obj_db_aux_pool_global_stats();
1561 Pool_lookup_global_stats<Ipc_arena, true>::stats_reset();
1562 Thread_lcl_obj_db_client<Ipc_arena>::obj_db_aux_pool_global_stats_reset();
1569 Thread_lcl_obj_db_admin<Ipc_arena>::this_thread_piggy_scan();
1575 using flow::util::stat::stats_since_reset_state;
1581 vector<Memory_manager_stats> result_id_sorted(
m_arenas.size());
1585 for (
const auto arena_id :
m_arenas)
1587 auto& row = result_id_sorted[idx++];
1588 row.m_uniq_arena_id = uniq_id;
1589 row.m_native_arena_id = uint64_t(arena_id);
1597 return result_id_sorted;
1613 for (
const auto arena_id :
m_arenas)
1618 return result_id_sorted;
1624 using flow::util::stat::stats_mark_reset_state;
1641 vector<Memory_manager_stats> rows(
m_arenas.size());
1651 for (
const auto arena_id :
m_arenas)
1654 auto& row = rows[idx++];
1655 base.m_vaddr = std::move(row.m_vaddr);
1656 base.m_alloc = std::move(row.m_alloc);
1657 stats_mark_reset_state(&base);
1666 assert(rows && (rows->size() ==
m_arenas.size()));
1673 for (
const auto arena_id :
m_arenas)
1675 auto& row = (*rows)[idx++];
1680 const auto epoch_now =
epoch();
1681 if (epoch_now == snapshot_epoch)
1687 FLOW_LOG_INFO(
"Ipc_arena [" <<
this <<
"]: jemalloc stats were refreshed elsewhere (epoch "
1688 "[" << snapshot_epoch <<
"] => [" << epoch_now <<
"]) while we were reading them; reading them "
1689 "again. This is normal but should be infrequent.");
1690 snapshot_epoch = epoch_now;
1696 using flow::util::ostream_op_string;
1703 const auto read_sz = [&](
const char* leaf,
size_t* target)
1705 mallctl_read(ostream_op_string(
"stats.arenas.", arena_id,
'.', leaf).c_str(), target);
1707 const auto read_pages_sz = [&](
const char* leaf) ->
size_t
1710 mallctl_read(ostream_op_string(
"stats.arenas.", arena_id,
'.', leaf).c_str(), &n_pages);
1711 return n_pages * page_sz;
1724 using flow::util::ostream_op_string;
1729 const auto read_u64 = [&](
const char* leaf) -> uint64_t
1732 mallctl_read(ostream_op_string(
"stats.arenas.", arena_id,
'.', leaf).c_str(), &val);
1735 const auto read_sz = [&](
const char* leaf) ->
size_t
1738 mallctl_read(ostream_op_string(
"stats.arenas.", arena_id,
'.', leaf).c_str(), &val);
1742 alloc->
m_alloc_count = read_u64(
"small.nmalloc") + read_u64(
"large.nmalloc");
1743 alloc->
m_dealloc_count = read_u64(
"small.ndalloc") + read_u64(
"large.ndalloc");
1745 alloc->
m_live_sz = read_sz(
"small.allocated") + read_sz(
"large.allocated");
1756 FLOW_LOG_TRACE(
"Ipc_arena [" <<
this <<
"]: Stat-consuming a couple things: high-water-mark refresh requested.");
1765 return os <<
'@' << &val;
Singleton repository for SHM-pool data pertaining to all SHM-arenas owned by this process.
Global stats singleton for either Owner_pool_lookup_global_stats or Borrower_pool_lookup_global_stats...
Parameterization-independent aspects of Shm_pool_offset_ptr_data segregated in non-template base.
(Internal-use) Thread_lcl_obj_db_admin<A>::this_thread_obj_db() is the current thread's module that t...
(Internal-use) Where a Thread_lcl_obj_db_admin cannot handle lend-object and dispose-object tracking,...
Represents/manages a segregated collection of in-SHM (shared memory) objects and buffers allocated us...
arena_id_t m_arena0
The first (and for now only, in practice) arena-ID in m_arenas. Used for perf between start() and des...
bool compute_pool_and_offset(void *address, size_t size, size_t offset, size_t length, util::String_view use_case, std::shared_ptr< Shm_pool > &pool, size_t &pool_offset) const
Extent hook impl helper for certain extent hooks: given a jemalloc extent at address of size bytes,...
bool add_shm_pool_listener(Owner_shm_pool_listener *listener)
(Internal-use) Adds a listener to get updates on shared memory pools.
void destroy()
Important helper synchronously invoked, in lieu of delete arena, when the create()-returned shared_pt...
arena_id_t create_jemalloc_arena()
start_impl() helper: creates one jemalloc-arena, wired to our extent hooks, and applies the per-arena...
void sample_hi_wmarks()
Internally samples certain stats so as to update _hi_wmarks that are computed at stat-consume time as...
void start_impl(unsigned int n_arenas)
start() helper.
void pool_stats_reset()
Resets pool_stats(). The formal meaning of a reset is discussed in flow::util::stat doc header.
static void this_thread_gc()
Synchronously garbage-collects, in the calling thread, any objects x = A.construct()ed by this thread...
Uniq_arena_id uniq_arena_id() const
Returns the stat::Uniq_arena_id (owner-process PID + collection-ID) identifying *this arena; used by ...
Extent_hooks m_extent_hooks_wrapper
The callback functions for jemalloc. Contains the jemalloc extent-hook table + a pointer back to *thi...
std::map< arena_id_t, Memory_manager_stats > m_mem_mgr_base_stats
Per-native-arena reset-state ("base") for memory_manager_stats() / memory_manager_stats_reset(): for ...
bool decommit_memory_pages(void *address, size_t size, size_t offset, size_t length, arena_id_t arena_id)
jemalloc extent hook impl.
static bool merge_memory_pages_handler(extent_hooks_t *extent_hooks, void *address_a, size_t size_a, void *address_b, size_t size_b, bool committed, unsigned arena_id)
jemalloc extent hook impl: forwards to similarly named member function of the proper this (which is s...
std::set< std::shared_ptr< Shm_pool > > m_shm_pools
Collection of created SHM-pools.
std::vector< Shm_pool_info > shm_pool_live_info() const
For informational/stats-adjacent purposes, returns information on currently live SHM-pools within *th...
bool remove_shm_pool(void *address, size_t size, bool committed, arena_id_t arena_id)
jemalloc extent hook impl.
void * allocate(size_t size)
Performs a non-garbage-collected allocation of a buffer in SHM.
Mutex m_shm_pools_and_listeners_mutex
Protects access to m_shm_pools and m_listeners.
void on_shm_pool_removed(const std::shared_ptr< Shm_pool > &shm_pool, bool removed_shared_memory) override
Implements super-class API: forwards to handle_created_shm_pool().
static const Obj_db_aux_pool_global_stats & obj_db_aux_pool_global_stats()
Like owner_pool_lookup_global_stats() but returns the process-global stat-set stat::Obj_db_aux_pool_g...
bool purge_forced_memory_pages(void *address, size_t size, size_t offset, size_t length, arena_id_t arena_id)
jemalloc extent hook impl.
static std::shared_ptr< Ipc_arena > create(flow::log::Logger *logger, const std::shared_ptr< Memory_manager > &memory_manager, Shared_name &&pool_name_base, const util::Permissions &permissions)
Creates an instance of this class, factory-style.
void sharded_stats_reset()
Resets sharded_stats(). The formal meaning of a reset is discussed in flow::util::stat doc header.
static void * create_shm_pool_handler(extent_hooks_t *extent_hooks, void *address, size_t size, size_t alignment, bool *zero, bool *commit, unsigned arena_id)
jemalloc extent hook impl: forwards to similarly named member function of the proper this (which is s...
void * allocate_impl(size_t size)
Helper core of allocate() that, in reaction to jemalloc-allocate failure, simply returns null (no fur...
static std::atomic< collection_id_t > m_collection_id_counter
The monotonically increasing collection (arena ID).
static bool split_memory_pages_handler(extent_hooks_t *extent_hooks, void *address, size_t size, size_t size_a, size_t size_b, bool committed, unsigned arena_id)
jemalloc extent hook impl: forwards to similarly named member function of the proper this (which is s...
Mutex m_sharded_stats_mutex
Protects m_sharded_stats, so that sharded_stats() and sharded_stats_reset() can be thread-safe as adv...
void on_shm_pool_created(const std::shared_ptr< Shm_pool > &shm_pool) override
Implements super-class API.
void memory_manager_snapshot_read(std::vector< Memory_manager_stats > *rows) const
Reads the raw jemalloc-sourced stat-members – the m_vaddr and m_alloc sub-groups – of each (*rows)[id...
bool merge_memory_pages(const void *address_a, size_t size_a, const void *address_b, size_t size_b, bool committed, arena_id_t arena_id)
jemalloc extent hook impl.
uint64_t m_owner_id
Owner (this) process's PID, captured at construction; the first half of uniq_arena_id().
void memory_manager_stats_reset()
Resets memory_manager_stats().
std::set< arena_id_t > m_arenas
The segregated memory areas. Empty until start(); non-empty and unchanging thereafter.
void start()
Creates the jemalloc arena; create() calls it, once, immediately upon construction; and nothing else ...
void memory_manager_vaddr_stats_read(arena_id_t arena_id, Memory_manager_stats::Vaddr *vaddr) const
Helper that for the given jemalloc-arena arena_id reads the values into stat::Memory_manager_stats::V...
std::atomic< bool > m_destroy_started
Start at false; at some point in the destroy() flow becomes true to prevent event listeners from firi...
void this_thread_ensure_tcache_exists() const
Ensures the calling thread has a jemalloc tcache for *this arena, creating it if needed – making subs...
void memory_manager_alloc_stats_read(arena_id_t arena_id, Memory_manager_stats::Alloc *alloc) const
Similar to memory_manager_vaddr_stats_read() but applied to stat::Memory_manager_stats::Alloc group.
const std::shared_ptr< Memory_manager > m_memory_manager
The jemalloc memory manager given to create().
static void global_stats_reset()
Resets owner_pool_lookup_global_stats() and obj_db_aux_pool_global_stats().
static bool commit_memory_pages_handler(extent_hooks_t *extent_hooks, void *address, size_t size, size_t offset, size_t length, unsigned arena_id)
jemalloc extent hook impl: forwards to similarly named member function of the proper this (which is s...
std::set< Owner_shm_pool_listener * > m_listeners
The SHM pool listeners, each to be informed of relevant events (basically: pool(s) added,...
static void remove_shm_pool_handler(extent_hooks_t *extent_hooks, void *address, size_t size, bool committed, unsigned arena_id)
jemalloc extent hook impl: forwards to similarly named member function of the proper this (which is s...
static bool purge_forced_memory_pages_handler(extent_hooks_t *extent_hooks, void *address, size_t size, size_t offset, size_t length, unsigned arena_id)
jemalloc extent hook impl: forwards to similarly named member function of the proper this (which is s...
Sharded_stats m_sharded_stats
The stats for *this arena that are in the stat-collection purview of detail::Thread_lcl_obj_db_admin ...
Ipc_arena(flow::log::Logger *logger, const std::shared_ptr< Memory_manager > &memory_manager, Shared_name &&pool_name_base, const util::Permissions &permissions)
Constructor.
std::vector< Memory_manager_stats > memory_manager_stats()
Returns the memory-manager-level statistics – one stat::Memory_manager_stats per native (jemalloc) ar...
flow::util::Lock_guard< Mutex > Lock
Exclusive lock for the mutex.
arena_id_t get_jemalloc_arena_id() const
Returns the jemalloc-ID for the (only, as of this writing) jemalloc-arena maintained by *this; this i...
bool remove_shm_pool_listener(Owner_shm_pool_listener *listener)
(Internal-use) Removes a listener from getting further updates on shared memory pools.
static const Owner_pool_lookup_global_stats & owner_pool_lookup_global_stats()
Returns the process-global owner-side stat-set stat::Owner_pool_lookup_global_stats,...
static bool optional_remove_shm_pool_handler(extent_hooks_t *extent_hooks, void *address, size_t size, bool committed, unsigned arena_id)
jemalloc extent hook impl: forwards to similarly named member function of the proper this (which is s...
bool split_memory_pages(const void *address, size_t size, size_t size_a, size_t size_b, bool committed, arena_id_t arena_id)
jemalloc extent hook impl.
Pool_stats m_pool_stats
For *this arena, the non-sharded, non-jemalloc-sourced stats, bundled into stat::Pool_stats.
const std::shared_ptr< Memory_manager > & get_jemalloc_memory_manager() const
Returns the memory manager.
void info_dump(Info_dump *target_info_dump)
Fills-out the stats/info contents of the given stat::Arena_info_dump: a printable bundling of stats/i...
void * create_shm_pool(void *address, size_t size, size_t alignment, bool *zero, bool *commit, arena_id_t arena_id)
Creates a shared memory pool (jemalloc extent hook).
void destroy_on_obj_db_forgot_us(const flow::log::Log_context_mt *log_ctx)
destroy() helper; handles the steps that remain once the possibly-async forgetting_shm_arena() finish...
static bool decommit_memory_pages_handler(extent_hooks_t *extent_hooks, void *address, size_t size, size_t offset, size_t length, unsigned arena_id)
jemalloc extent hook impl: forwards to similarly named member function of the proper this (which is s...
const Pool_stats & pool_stats() const
Returns the stat-set stat::Pool_stats for *this arena – the arena's non-sharded SHM-pool stats (obj-D...
void deallocate(void *address) noexcept
Undoes allocate(): deallocates a previously allocated, by this arena, buffer in SHM.
void sharded_stats(Sharded_stats *target_stats)
Outputs the stat-set stat::Sharded_stats for *this arena.
bool commit_memory_pages(void *address, size_t size, size_t offset, size_t length, arena_id_t arena_id)
jemalloc extent hook impl.
bool optional_remove_shm_pool(void *address, size_t size, bool committed, arena_id_t arena_id)
jemalloc extent hook impl.
static constexpr tcache_id_t S_NO_TCACHE_ID
Special value returned by id_or_none() indicating no tcache yet exists for the given arena_id_t.
static Thread_cache * this_thread_cache_or_null()
Returns pointer to the one cache-object in the calling thread, or null if none exists (this_thread_ca...
static Thread_cache * this_thread_cache()
Returns pointer to the one cache-object in the calling thread, first creating it if it is the first s...
tcache_id_t id(arena_id_t arena_id)
Returns tcache ID of the tcache in this thread with a 1-1 relationship to the given arena,...
String-wrapping abstraction representing a name uniquely distinguishing a kernel-persistent entity fr...
Stats-related sub-namespace, for ADL segregation and general organization.
void sharded_stats(const Shm_arena &shm_arena, Sharded_stats *target_stats)
Assembles and outputs the current stats tracked by the obj-DB module (as anchored by Thread_lcl_obj_d...
void sharded_stats_reset(const Shm_arena &shm_arena, Sharded_stats *target_stats)
Resets Sharded_stats; companion to sharded_stats().
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....
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).
flow::util::Mutex_non_recursive & jemalloc_stats_mutex()
Returns the process-global mutex held throughout each of Flow-IPC's jemalloc-statistics operations – ...
ipc::shm sub-module with the SHM-jemalloc SHM-provider. See ipc::shm doc header for introduction.
unsigned int arena_id_t
Arena ID as dictated by jemalloc.
std::ostream & operator<<(std::ostream &os, const Ipc_arena &val)
Prints string representation of the given Ipc_arena to the given ostream.
void set_logger(flow::log::Logger *logger_ptr)
Convenience function that sets (or clears) the Logger used by all SHM-arena-lend singleton subsystems...
Error_code make_error_code(Code err_code)
Given a Code enum value, creates a lightweight Error_code (a/k/a boost.system error_code) representin...
bipc::permissions Permissions
Short-hand for Unix (POSIX) permissions class.
flow::util::String_view String_view
Short-hand for Flow's String_view.
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
#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...
Printable (with knobs in contained util::stat::Info_dump_format) bundling of stats/info relevant to (...
std::vector< arena_lend::stat::Shm_pool_info > m_shm_pool_live_info
Target Ipc_arena's info from: Ipc_arena::shm_pool_live_info().
arena_lend::stat::Obj_db_aux_pool_global_stats m_obj_db_aux_pool_global_stats
Global stats from: static Ipc_arena::obj_db_aux_pool_global_stats().
util::stat::Info_dump_format m_fmt
Print-format knobs in effect at ostream << *this time.
arena_lend::stat::Owner_pool_lookup_global_stats m_owner_pool_lookup_global_stats
Global stats from: static Ipc_arena::owner_pool_lookup_global_stats().
std::vector< arena_lend::stat::Memory_manager_stats > m_mem_mgr_stats
Target Ipc_arena's stats from: Ipc_arena::memory_manager_stats().
arena_lend::stat::Sharded_stats m_sharded_stats
Target Ipc_arena's stats from: Ipc_arena::sharded_stats().
std::string m_mem_mgr_stats_dump
If unavailable then empty; otherwise the newline-terminated output of Memory_manager::stats_dump_to_o...
arena_lend::stat::Pool_stats m_pool_stats
Target Ipc_arena's stats from: Ipc_arena::pool_stats().
Sub-group: raw allocation activity – cumulative counts of memory-manager allocate/deallocate operatio...
uint64_t m_live_count
(Gauge) Currently-live allocation count (= m_alloc_count minus m_dealloc_count).
flow::util::stat::Histogram_counter m_histo_alloc_count_by_sz
Cumulative allocation count broken down by allocation size-class, as a histogram: each bucket counts ...
uint64_t m_dealloc_count
Cumulative count of deallocate ops (for SHM-jemalloc: jemalloc small + large ndalloc).
size_t m_live_sz
(Gauge) Currently-live allocated bytes (for SHM-jemalloc: jemalloc small + large allocated).
uint64_t m_alloc_count
Cumulative count of allocate ops (for SHM-jemalloc: jemalloc small + large nmalloc).
uint64_t m_dealloc_sz
Cumulative bytes deallocated (for SHM-jemalloc: sum over size-classes of ndalloc x size-class-byte-si...
uint64_t m_alloc_sz
Cumulative bytes allocated (for SHM-jemalloc: sum over size-classes of nmalloc x size-class-byte-size...
Sub-group: the arena's virtual-address-space / page-state footprint – how much memory it currently ho...
size_t m_active_sz
(Gauge) Bytes in pages backing live (active) allocations: ~= live allocated bytes + unused space on t...
size_t m_retained_sz
(Gauge) Bytes of once-used vaddr space the memory-manager has excluded from m_mapped_sz book-keeping ...
size_t m_inactive_resident_sz
(Gauge) Not-active (so freed from/not yet used for allocated user-buffers; contrast with m_active_sz)...
size_t m_resident_sz
(Gauge) Roughly: the peak, since arena creation, of concurrently-allocated bytes (rounded up to jemal...
size_t m_mapped_sz
(Gauge) Total bytes currently mapped from the OS into the arena's address space.
size_t m_inactive_muzzy_sz
(Gauge) Not-active (see also m_inactive_resident_sz) bytes that the memory manager has returned to th...
Similar mission to Obj_db_aux_pool_stats but covers information that, by nature, is not per-arena but...
Similar mission to Owner_pool_stats but covers information that, by nature, is not per-arena but rath...
std::atomic< uint64_t > m_pool_destroy_sz
Across the pool-destructions counted in m_pool_destroy_count, the sum of the actual sizes of those po...
std::atomic< uint64_t > m_pool_destroy_count
Number of times the memory manager (e.g., jemalloc for SHM-jemalloc) requested something,...
std::atomic< uint64_t > m_pool_optional_destroy_request_count
Number of times the memory manager (e.g., jemalloc for SHM-jemalloc) requested the optional removal (...
std::atomic< uint64_t > m_pool_create_count
Number of times the memory manager (e.g., jemalloc for SHM-jemalloc) requested the creation of a new ...
std::atomic< uint64_t > m_pool_create_sz
Across the pool-creations counted in m_pool_create_count, the sum of the requested/actual sizes of th...
Bundles together per-arena Owner_pool_stats and Obj_db_aux_pool_stats, so they can be accessed via on...
Owner_pool_stats m_owner_pool
Sub-struct with individual stats; see type's doc header.
A bundle of several individually-documented stat-sets – object construct/dispose/live and zombie-reap...
IDs an arena, as a GAUGE in contexts like Shm_pool_info and per-arena Borrower_pool_stats,...