From 1694c71cd5f4d39c78a85defdd42d1d3f4c6486c Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 7 Jul 2022 04:42:48 +0200 Subject: [PATCH] Implement memory-pool debug-dump This will allow to trace "POOL OUT OF MEMORY" and see which events fill up the event/memory pool. --- libs/pbd/pbd/pool.h | 16 +++++++++++----- libs/pbd/pool.cc | 35 +++++++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/libs/pbd/pbd/pool.h b/libs/pbd/pbd/pool.h index c265b1ef65..539d3c4385 100644 --- a/libs/pbd/pbd/pool.h +++ b/libs/pbd/pbd/pool.h @@ -2,7 +2,7 @@ * Copyright (C) 1998-2015 Paul Davis * Copyright (C) 2009-2014 David Robillard * Copyright (C) 2010 Carl Hetherington - * Copyright (C) 2015-2017 Robin Gareus + * Copyright (C) 2015-2022 Robin Gareus * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,13 +30,15 @@ #include "pbd/libpbd_visibility.h" #include "pbd/ringbuffer.h" +typedef void (*PoolDumpCallback)(size_t, void*); + /** A pool of data items that can be allocated, read from and written to * without system memory allocation or locking. */ class LIBPBD_API Pool { public: - Pool (std::string name, unsigned long item_size, unsigned long nitems); + Pool (std::string name, unsigned long item_size, unsigned long nitems, PoolDumpCallback cb = NULL); virtual ~Pool (); virtual void *alloc (); @@ -48,11 +50,14 @@ class LIBPBD_API Pool guint total() const { return free_list.bufsize(); } protected: + PBD::RingBuffer free_list; ///< a list of pointers to free items within block + std::string _name; private: - void *block; ///< data storage area + void* _block; ///< data storage area + PoolDumpCallback _dump; ///< callback to print pool contents #ifndef NDEBUG unsigned long max_usage; #endif @@ -102,7 +107,7 @@ class LIBPBD_API PerThreadPool; class LIBPBD_API CrossThreadPool : public Pool { public: - CrossThreadPool (std::string n, unsigned long isize, unsigned long nitems, PerThreadPool *); + CrossThreadPool (std::string n, unsigned long isize, unsigned long nitems, PerThreadPool*, PoolDumpCallback); void* alloc (); void push (void *); @@ -132,7 +137,8 @@ public: const Glib::Threads::Private& key() const { return _key; } - void create_per_thread_pool (std::string name, unsigned long item_size, unsigned long nitems); + void create_per_thread_pool (std::string name, unsigned long item_size, unsigned long nitems, PoolDumpCallback cb = NULL); + CrossThreadPool* per_thread_pool (bool must_exist = true); bool has_per_thread_pool (); void set_trash (PBD::RingBuffer* t); diff --git a/libs/pbd/pool.cc b/libs/pbd/pool.cc index e0028d3055..3551cab29b 100644 --- a/libs/pbd/pool.cc +++ b/libs/pbd/pool.cc @@ -3,7 +3,7 @@ * Copyright (C) 2008-2009 David Robillard * Copyright (C) 2008-2011 Carl Hetherington * Copyright (C) 1998-2015 Paul Davis - * Copyright (C) 2014-2015 Robin Gareus + * Copyright (C) 2014-2022 Robin Gareus * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include #include #include @@ -30,30 +31,34 @@ #include "pbd/error.h" #include "pbd/debug.h" #include "pbd/compose.h" +#include "pbd/stacktrace.h" using namespace std; using namespace PBD; -Pool::Pool (string n, unsigned long item_size, unsigned long nitems) +Pool::Pool (string n, unsigned long item_size, unsigned long nitems, PoolDumpCallback cb) : free_list (nitems) , _name (n) + , _dump (cb) #ifndef NDEBUG , max_usage (0) #endif { _name = n; + /* adjust to actual size (power-of-two) */ + nitems = free_list.bufsize (); /* since some overloaded ::operator new() might use this, its important that we use a "lower level" allocator to get more space. */ - block = malloc (nitems * item_size); + _block = malloc (nitems * item_size); - void **ptrlist = (void **) malloc (sizeof (void *) * nitems); + void **ptrlist = (void **) calloc (nitems, sizeof (void *)); for (unsigned long i = 0; i < nitems; i++) { - ptrlist[i] = static_cast (static_cast(block) + (i * item_size)); + ptrlist[i] = static_cast (static_cast(_block) + (i * item_size)); } free_list.write (ptrlist, nitems); @@ -63,7 +68,7 @@ Pool::Pool (string n, unsigned long item_size, unsigned long nitems) Pool::~Pool () { DEBUG_TRACE (DEBUG::Pool, string_compose ("Pool: '%1' max: %2 / %3\n", name(), max_usage, total())); - free (block); + free (_block); } /** Allocate an item's worth of memory in the Pool by taking one from the free list. @@ -81,6 +86,16 @@ Pool::alloc () #endif if (free_list.read (&ptr, 1) < 1) { + PBD::stacktrace (std::cerr, 20); + + if (_dump) { + void** _block = free_list.buffer (); + for (size_t i = 0; i < free_list.bufsize (); ++i) { + _dump (i, _block[i]); + } + printf ("RingBuffer write-idx: %u read-idx: %u\n", free_list.get_write_idx (), free_list.get_read_idx ()); + } + fatal << "CRITICAL: " << _name << " POOL OUT OF MEMORY - RECOMPILE WITH LARGER SIZE!!" << endmsg; abort(); /*NOTREACHED*/ return 0; @@ -182,9 +197,9 @@ PerThreadPool::PerThreadPool () * @param nitems Number of items in the pool. */ void -PerThreadPool::create_per_thread_pool (string n, unsigned long isize, unsigned long nitems) +PerThreadPool::create_per_thread_pool (string n, unsigned long isize, unsigned long nitems, PoolDumpCallback cb) { - _key.set (new CrossThreadPool (n, isize, nitems, this)); + _key.set (new CrossThreadPool (n, isize, nitems, this, cb)); } /** @return True if CrossThreadPool for the current thread exists, @@ -240,8 +255,8 @@ PerThreadPool::add_to_trash (CrossThreadPool* p) _trash->write (&p, 1); } -CrossThreadPool::CrossThreadPool (string n, unsigned long isize, unsigned long nitems, PerThreadPool* p) - : Pool (n, isize, nitems) +CrossThreadPool::CrossThreadPool (string n, unsigned long isize, unsigned long nitems, PerThreadPool* p, PoolDumpCallback cb) + : Pool (n, isize, nitems, cb) , pending (nitems) , _parent (p) {