2 * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #ifndef PLATFORM_WINDOWS
27 #include "pbd/reallocpool.h"
29 #ifdef RAP_WITH_SEGMENT_STATS
30 #define STATS_segment collect_segment_stats();
35 #ifdef RAP_WITH_CALL_STATS
36 #define STATS_inc(VAR) ++VAR;
37 #define STATS_if(COND, VAR) if (COND) {++VAR;}
38 #define STATS_used(DELTA) { _cur_used += (DELTA); if (_cur_used > _max_used) { _max_used = _cur_used; } }
40 #define STATS_inc(VAR)
41 #define STATS_if(COND, VAR)
42 #define STATS_used(DELTA)
45 #ifdef RAP_WITH_HISTOGRAM
46 #define STATS_hist(VAR, SIZE) ++VAR[hist_bin(SIZE)];
48 #define STATS_hist(VAR, SIZE)
53 typedef int poolsize_t;
55 ReallocPool::ReallocPool (std::string name, size_t bytes)
59 #ifdef RAP_WITH_SEGMENT_STATS
68 #ifdef RAP_WITH_CALL_STATS
79 _pool = (char*) ::malloc (bytes);
81 memset (_pool, 0, bytes); // make resident
82 #ifndef PLATFORM_WINDOWS
86 poolsize_t *in = (poolsize_t*) _pool;
87 *in = - (bytes - sizeof (poolsize_t));
90 #ifdef RAP_WITH_HISTOGRAM
91 for (int i = 0; i < RAP_WITH_HISTOGRAM; ++i) {
92 _hist_alloc[i] = _hist_free[i] = _hist_grow[i] = _hist_shrink[i] = 0;
97 ReallocPool::~ReallocPool ()
105 // realloc() does it all, malloc(), realloc() and free()
107 ReallocPool::_realloc (void *ptr, size_t oldsize, size_t newsize) {
110 if (ptr == 0 && newsize == 0) {
116 rv = _malloc (newsize);
117 STATS_if (!rv, _n_oom);
119 STATS_hist(_hist_alloc, newsize);
125 STATS_hist(_hist_free, _asize(ptr));
132 if (newsize == oldsize) {
137 if (newsize > oldsize) {
139 const size_t ns = (newsize + RAP_BLOCKSIZE) & (~RAP_BLOCKSIZE);
140 if (ns <= _asize(ptr)) {
145 if ((rv = _malloc (newsize))) {
146 memcpy (rv, ptr, oldsize);
149 STATS_if(!rv, _n_oom);
151 STATS_hist(_hist_grow, newsize);
156 if (newsize < oldsize) {
158 if ((rv = _malloc (newsize))) {
159 memccpy (rv, ptr, newsize);
161 STATS_if(!rv, _n_oom);
163 #elif 1 // shrink current segment
164 const size_t ns = (newsize + RAP_BLOCKSIZE) & (~RAP_BLOCKSIZE);
170 STATS_inc(_n_shrink);
171 STATS_hist (_hist_shrink, newsize);
175 return NULL; // not reached
178 #define SEGSIZ (*((poolsize_t*) p))
181 ReallocPool::consolidate_ptr (char *p) {
182 const poolsize_t sop = sizeof(poolsize_t);
183 if (p - SEGSIZ + sop >= _pool + _poolsize) {
184 return; // reached end
186 poolsize_t *next = (poolsize_t*)(p - SEGSIZ + sop);
188 SEGSIZ = SEGSIZ + (*next) - sop;
189 if (p - SEGSIZ + sop >= _pool + _poolsize) {
192 next = (poolsize_t*)(p -SEGSIZ + sop);
198 ReallocPool::_malloc (size_t s) {
199 const poolsize_t sop = sizeof(poolsize_t);
200 size_t traversed = 0;
204 s = (s + RAP_BLOCKSIZE) & (~RAP_BLOCKSIZE); // optional, helps to reduce fragmentation
207 while (1) { // iterates at most once over the available pool
209 traversed += SEGSIZ + sop;
210 if (traversed >= _poolsize) {
211 return NULL; // reached last segment. OOM.
214 if (p == _pool + _poolsize) {
219 // found free segment.
220 const poolsize_t avail = -SEGSIZ;
221 const poolsize_t sp = (poolsize_t)s;
222 const poolsize_t ss = sop + s;
232 // segment is larger than required space,
233 // (we need to fit data + two non-zero poolsize_t)
234 SEGSIZ = sp; // mark area as used.
235 *((poolsize_t*)(p + ss)) = ss - avail; // mark free space after.
236 consolidate_ptr (p + ss);
242 // segment is not large enough
243 consolidate_ptr (p); // try to consolidate with next segment (if any)
245 // check segment (again) and skip over too small free ones
246 while (SEGSIZ < 0 && (-SEGSIZ) <= ss && (-SEGSIZ) != sp) {
247 traversed += -SEGSIZ + sop;
248 if (traversed >= _poolsize) {
249 return NULL; // reached last segment. OOM.
251 p += (-SEGSIZ) + sop;
252 if (p >= _pool + _poolsize) {
254 if (SEGSIZ < 0) consolidate_ptr (p);
262 ReallocPool::_free (void *ptr) {
263 poolsize_t *in = (poolsize_t*) ptr;
265 *in = -*in; // mark as free
271 ReallocPool::_shrink (void *ptr, size_t newsize) {
272 poolsize_t *in = (poolsize_t*) ptr;
274 const poolsize_t avail = *in;
275 const poolsize_t ss = newsize + sizeof(poolsize_t);
277 return; // can't shrink
279 const poolsize_t sp = (poolsize_t)newsize;
280 STATS_used (newsize - avail);
281 *in = sp; // set new size
282 char *p = (char*) in;
283 *((poolsize_t*)(p + ss)) = ss - avail; // mark free space after.
288 ReallocPool::_asize (void *ptr) {
289 if (ptr == 0) return 0;
290 poolsize_t *in = (poolsize_t*) ptr;
299 ReallocPool::printstats ()
301 #ifdef RAP_WITH_SEGMENT_STATS
302 printf ("ReallocPool '%s': used: %ld (%.1f%%) (max: %ld), free: %ld [bytes]\n"
303 "|| segments: cur: %ld (max: %ld), largest-used: %ld, largest-free: %ld\n",
305 _cur_allocated, _cur_allocated * 100.f / _poolsize, _max_allocated, _cur_avail,
306 _seg_cur_count, _seg_max_count, _seg_max_used, _seg_max_avail);
307 #elif defined RAP_WITH_CALL_STATS
308 printf ("ReallocPool '%s':\n", _name.c_str());
310 #ifdef RAP_WITH_CALL_STATS
311 printf("|| malloc(): %ld, free(): %ld, realloc()+:%ld, realloc()-: %ld NOOP:%ld OOM:%ld\n",
312 _n_alloc, _n_free, _n_grow, _n_shrink, _n_noop, _n_oom);
313 printf("|| used: %ld / %ld, max: %ld (%.1f%%)\n",
314 _cur_used, _poolsize,
315 _max_used, 100.f *_max_used / _poolsize);
317 #ifdef RAP_WITH_HISTOGRAM
318 printf("--- malloc()\n");
319 print_histogram (_hist_alloc);
320 printf("--- realloc()/grow-to\n");
321 print_histogram (_hist_grow);
322 printf("--- realloc()/shrink-to\n");
323 print_histogram (_hist_shrink);
324 printf("--- free() histogram\n");
325 print_histogram (_hist_free);
326 printf("--------------------\n");
331 ReallocPool::dumpsegments ()
334 const poolsize_t sop = sizeof(poolsize_t);
335 poolsize_t *in = (poolsize_t*) p;
336 unsigned int traversed = 0;
337 printf ("<<<<< %s\n", _name.c_str());
340 printf ("0x%08x used %4d\n", traversed, *in);
341 printf ("0x%08x data %p\n", traversed + sop , p + sop);
342 traversed += *in + sop;
344 } else if ((*in) < 0) {
345 printf ("0x%08x free %4d [+%d]\n", traversed, -*in, sop);
346 traversed += -*in + sop;
349 printf ("0x%08x Corrupt!\n", traversed);
352 in = (poolsize_t*) p;
353 if (p == _pool + _poolsize) {
354 printf ("%08x end\n", traversed);
357 if (p > _pool + _poolsize) {
358 printf ("%08x Beyond End!\n", traversed);
365 #ifdef RAP_WITH_SEGMENT_STATS
367 ReallocPool::collect_segment_stats ()
370 poolsize_t *in = (poolsize_t*) p;
372 _cur_allocated = _cur_avail = 0;
373 _seg_cur_count = _seg_max_avail = _seg_max_used = 0;
378 _cur_allocated += *in;
380 if (*in > (poolsize_t)_seg_max_used) {
386 if (-*in > (poolsize_t)_seg_max_avail) {
387 _seg_max_avail = -*in;
390 p += sizeof(poolsize_t);
391 in = (poolsize_t*) p;
392 if (p == _pool + _poolsize) {
396 _seg_cur_count = _seg_cur_count;
398 if (_cur_allocated > _max_allocated) {
399 _max_allocated = _cur_allocated;
401 if (_seg_cur_count > _seg_max_count) {
402 _seg_max_count = _seg_cur_count;
407 #ifdef RAP_WITH_HISTOGRAM
409 ReallocPool::print_histogram (size_t const * const histogram) const
412 for (int i = 0; i < RAP_WITH_HISTOGRAM; ++i) {
413 if (histogram[i] > maxhist) maxhist = histogram[i];
415 const int termwidth = 50;
417 const int fact = RAP_BLOCKSIZE + 1;
419 if (maxhist > 0) for (int i = 0; i < RAP_WITH_HISTOGRAM; ++i) {
420 if (histogram[i] == 0) { continue; }
421 if (i == RAP_WITH_HISTOGRAM -1 ) {
423 printf(" > %4d: %7lu ", i * fact, histogram[i]);
425 printf(">%4d:%7lu ", i * fact, histogram[i]);
429 printf("%4d .. %4d: %7lu ", i * fact, (i + 1) * fact -1, histogram[i]);
431 printf("%4d: %7lu ", i * fact, (i + 1) * fact -1 , histogram[i]);
434 int bar_width = (histogram[i] * termwidth ) / maxhist;
435 if (bar_width == 0 && histogram[i] > 0) bar_width = 1;
436 for (int j = 0; j < bar_width; ++j) printf("#");
442 ReallocPool::hist_bin (size_t s) const {
444 s = (s + RAP_BLOCKSIZE) & (~RAP_BLOCKSIZE);
445 s /= (RAP_BLOCKSIZE + 1);
447 if (s > RAP_WITH_HISTOGRAM - 1) s = RAP_WITH_HISTOGRAM - 1;