]> git.ipfire.org Git - thirdparty/squid.git/blame - src/mem.cc
Renamed acl_name_list to AclNameList and moved ot own header file in acl/
[thirdparty/squid.git] / src / mem.cc
CommitLineData
acf5589a 1/*
7021844c 2 * DEBUG: section 13 High Level Memory Pool Management
acf5589a 3 * AUTHOR: Harvest Derived
4 *
2b6662ba 5 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 6 * ----------------------------------------------------------
acf5589a 7 *
2b6662ba 8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
acf5589a 16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
26ac0430 21 *
acf5589a 22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26ac0430 26 *
acf5589a 27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
cbdec147 29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 30 *
acf5589a 31 */
32
582c2af2 33#include "squid.h"
6be70545 34#include "acl/AclNameList.h"
1c898d4c 35#include "ClientInfo.h"
6be70545 36#include "event.h"
582c2af2 37#include "md5.h"
6be70545 38#include "Mem.h"
582c2af2 39#include "MemBuf.h"
d96ceb8e 40#include "memMeter.h"
582c2af2 41#include "mgr/Registration.h"
4d5904f7 42#include "SquidConfig.h"
41c97755 43#include "SquidList.h"
582c2af2 44#include "SquidTime.h"
e6ccf245 45#include "Store.h"
c21ad0f5 46#include "StoreEntryStream.h"
acf5589a 47
27e059d4
AJ
48#if HAVE_IOMANIP
49#include <iomanip>
50#endif
51#if HAVE_OSTREAM
52#include <ostream>
53#endif
54
8a89c28f
FC
55/* forward declarations */
56static void memFree2K(void *);
57static void memFree4K(void *);
58static void memFree8K(void *);
59static void memFree16K(void *);
60static void memFree32K(void *);
61static void memFree64K(void *);
62
7021844c 63/* module globals */
43d1bbe4 64const size_t squidSystemPageSize=getpagesize();
acf5589a 65
d96ceb8e 66/* local prototypes */
c21ad0f5 67static void memStringStats(std::ostream &);
d96ceb8e 68
69/* module locals */
04eb0689 70static MemAllocator *MemPools[MEM_MAX];
d96ceb8e 71static double xm_time = 0;
72static double xm_deltat = 0;
acf5589a 73
b5a644fc
FC
74/* all pools are ready to be used */
75static bool MemIsInitialized = false;
76
9fe7e747 77/* string pools */
867c718d 78#define mem_str_pool_count 6
62e76326 79
b5a644fc
FC
80// 4 bytes bigger than the biggest string pool size
81// which is in turn calculated from SmallestStringBeforeMemIsInitialized
867c718d 82static const size_t SmallestStringBeforeMemIsInitialized = 1024*16+4;
b5a644fc 83
26ac0430 84static const struct {
ec878047 85 const char *name;
9fe7e747 86 size_t obj_size;
62e76326 87}
88
89StrPoolsAttrs[mem_str_pool_count] = {
90
26ac0430
AJ
91 {
92 "Short Strings", MemAllocator::RoundedSize(36),
93 }, /* to fit rfc1123 and similar */
94 {
95 "Medium Strings", MemAllocator::RoundedSize(128),
96 }, /* to fit most urls */
97 {
867c718d
FC
98 "Long Strings", MemAllocator::RoundedSize(512),
99 },
100 {
101 "1KB Strings", MemAllocator::RoundedSize(1024),
102 },
103 {
104 "4KB Strings", MemAllocator::RoundedSize(4*1024),
105 },
106 {
107 "16KB Strings",
b5a644fc 108 MemAllocator::RoundedSize(SmallestStringBeforeMemIsInitialized-4)
867c718d 109 }
26ac0430
AJ
110};
111
112static struct {
b001e822 113 MemAllocator *pool;
62e76326 114}
115
116StrPools[mem_str_pool_count];
9fe7e747 117static MemMeter StrCountMeter;
118static MemMeter StrVolumeMeter;
119
1eb41ae8 120static MemMeter HugeBufCountMeter;
121static MemMeter HugeBufVolumeMeter;
9fe7e747 122
123/* local routines */
124
9fe7e747 125static void
c21ad0f5 126memStringStats(std::ostream &stream)
9fe7e747 127{
9fe7e747 128 int i;
129 int pooled_count = 0;
130 size_t pooled_volume = 0;
131 /* heading */
35268c70 132 stream << "String Pool\t Impact\t\t\n \t (%strings)\t (%volume)\n";
9fe7e747 133 /* table body */
62e76326 134
89235243 135 for (i = 0; i < mem_str_pool_count; ++i) {
b001e822 136 const MemAllocator *pool = StrPools[i].pool;
137 const int plevel = pool->getMeter().inuse.level;
c21ad0f5 138 stream << std::setw(20) << std::left << pool->objectType();
139 stream << std::right << "\t " << xpercentInt(plevel, StrCountMeter.level);
140 stream << "\t " << xpercentInt(plevel * pool->objectSize(), StrVolumeMeter.level) << "\n";
62e76326 141 pooled_count += plevel;
b001e822 142 pooled_volume += plevel * pool->objectSize();
9fe7e747 143 }
62e76326 144
9fe7e747 145 /* malloc strings */
c21ad0f5 146 stream << std::setw(20) << std::left << "Other Strings";
147
148 stream << std::right << "\t ";
62e76326 149
c21ad0f5 150 stream << xpercentInt(StrCountMeter.level - pooled_count, StrCountMeter.level) << "\t ";
151
152 stream << xpercentInt(StrVolumeMeter.level - pooled_volume, StrVolumeMeter.level) << "\n\n";
1eb41ae8 153}
154
155static void
c21ad0f5 156memBufStats(std::ostream & stream)
1eb41ae8 157{
c21ad0f5 158 stream << "Large buffers: " <<
159 HugeBufCountMeter.level << " (" <<
160 HugeBufVolumeMeter.level / 1024 << " KB)\n";
9fe7e747 161}
162
528b2c61 163void
164Mem::Stats(StoreEntry * sentry)
acf5589a 165{
c21ad0f5 166 StoreEntryStream stream(sentry);
167 Report(stream);
168 memStringStats(stream);
169 memBufStats(stream);
b4bab919 170#if WITH_VALGRIND
171 if (RUNNING_ON_VALGRIND) {
26ac0430
AJ
172 long int leaked = 0, dubious = 0, reachable = 0, suppressed = 0;
173 stream << "Valgrind Report:\n";
174 stream << "Type\tAmount\n";
e0236918 175 debugs(13, DBG_IMPORTANT, "Asking valgrind for memleaks");
26ac0430 176 VALGRIND_DO_LEAK_CHECK;
e0236918 177 debugs(13, DBG_IMPORTANT, "Getting valgrind statistics");
26ac0430
AJ
178 VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed);
179 stream << "Leaked\t" << leaked << "\n";
180 stream << "Dubious\t" << dubious << "\n";
181 stream << "Reachable\t" << reachable << "\n";
182 stream << "Suppressed\t" << suppressed << "\n";
b4bab919 183 }
184#endif
c21ad0f5 185 stream.flush();
acf5589a 186}
187
188/*
9fe7e747 189 * public routines
acf5589a 190 */
191
58a39dc9 192/*
b74cf25f
AJ
193 * we have a limit on _total_ amount of idle memory so we ignore max_pages for now.
194 * Will ignore repeated calls for the same pool type.
195 *
196 * Relies on Mem::Init() having been called beforehand.
58a39dc9 197 */
198void
60da11d3 199memDataInit(mem_type type, const char *name, size_t size, int max_pages_notused, bool zeroOnPush)
58a39dc9 200{
201 assert(name && size);
b74cf25f
AJ
202
203 if (MemPools[type] != NULL)
204 return;
205
04eb0689 206 MemPools[type] = memPoolCreate(name, size);
60da11d3 207 MemPools[type]->zeroOnPush(zeroOnPush);
58a39dc9 208}
209
7021844c 210/* find appropriate pool and use it (pools always init buffer with 0s) */
acf5589a 211void *
7021844c 212memAllocate(mem_type type)
acf5589a 213{
92b77cf7 214 assert(MemPools[type]);
b001e822 215 return MemPools[type]->alloc();
acf5589a 216}
217
db1cd23c 218/* give memory back to the pool */
acf5589a 219void
db1cd23c 220memFree(void *p, int type)
acf5589a 221{
92b77cf7 222 assert(MemPools[type]);
dc47f531 223 MemPools[type]->freeOne(p);
acf5589a 224}
225
b5a644fc 226/* allocate a variable size buffer using best-fit string pool */
9fe7e747 227void *
4be8fe59 228memAllocString(size_t net_size, size_t * gross_size)
9fe7e747 229{
b001e822 230 MemAllocator *pool = NULL;
9fe7e747 231 assert(gross_size);
62e76326 232
b5a644fc
FC
233 // if pools are not yet ready, make sure that
234 // the requested size is not poolable so that the right deallocator
235 // will be used
236 if (!MemIsInitialized && net_size < SmallestStringBeforeMemIsInitialized)
d3a30598 237 net_size = SmallestStringBeforeMemIsInitialized;
b5a644fc
FC
238
239 unsigned int i;
89235243 240 for (i = 0; i < mem_str_pool_count; ++i) {
62e76326 241 if (net_size <= StrPoolsAttrs[i].obj_size) {
242 pool = StrPools[i].pool;
243 break;
244 }
9fe7e747 245 }
62e76326 246
e231a8ce 247 *gross_size = pool ? StrPoolsAttrs[i].obj_size : net_size;
9fe7e747 248 assert(*gross_size >= net_size);
b5a644fc 249 // may forget [de]allocations until MemIsInitialized
9fe7e747 250 memMeterInc(StrCountMeter);
251 memMeterAdd(StrVolumeMeter, *gross_size);
b001e822 252 return pool ? pool->alloc() : xcalloc(1, net_size);
9fe7e747 253}
254
0353e724 255extern size_t memStringCount();
256size_t
257memStringCount()
258{
259 size_t result = 0;
260
261 for (int counter = 0; counter < mem_str_pool_count; ++counter)
262 result += memPoolInUseCount(StrPools[counter].pool);
263
264 return result;
265}
266
4be8fe59 267/* free buffer allocated with memAllocString() */
9fe7e747 268void
4be8fe59 269memFreeString(size_t size, void *buf)
9fe7e747 270{
b001e822 271 MemAllocator *pool = NULL;
b5a644fc
FC
272 assert(buf);
273
274 if (MemIsInitialized) {
275 for (unsigned int i = 0; i < mem_str_pool_count; ++i) {
276 if (size <= StrPoolsAttrs[i].obj_size) {
277 assert(size == StrPoolsAttrs[i].obj_size);
278 pool = StrPools[i].pool;
279 break;
280 }
62e76326 281 }
9fe7e747 282 }
62e76326 283
b5a644fc 284 // may forget [de]allocations until MemIsInitialized
9fe7e747 285 memMeterDec(StrCountMeter);
286 memMeterDel(StrVolumeMeter, size);
dc47f531 287 pool ? pool->freeOne(buf) : xfree(buf);
9fe7e747 288}
289
1eb41ae8 290/* Find the best fit MEM_X_BUF type */
291static mem_type
292memFindBufSizeType(size_t net_size, size_t * gross_size)
293{
294 mem_type type;
295 size_t size;
62e76326 296
fa80a8ef 297 if (net_size <= 2 * 1024) {
62e76326 298 type = MEM_2K_BUF;
299 size = 2 * 1024;
fa80a8ef 300 } else if (net_size <= 4 * 1024) {
62e76326 301 type = MEM_4K_BUF;
302 size = 4 * 1024;
fa80a8ef 303 } else if (net_size <= 8 * 1024) {
62e76326 304 type = MEM_8K_BUF;
305 size = 8 * 1024;
fa80a8ef 306 } else if (net_size <= 16 * 1024) {
62e76326 307 type = MEM_16K_BUF;
308 size = 16 * 1024;
fa80a8ef 309 } else if (net_size <= 32 * 1024) {
62e76326 310 type = MEM_32K_BUF;
311 size = 32 * 1024;
fa80a8ef 312 } else if (net_size <= 64 * 1024) {
62e76326 313 type = MEM_64K_BUF;
314 size = 64 * 1024;
1eb41ae8 315 } else {
62e76326 316 type = MEM_NONE;
317 size = net_size;
1eb41ae8 318 }
62e76326 319
1eb41ae8 320 if (gross_size)
62e76326 321 *gross_size = size;
322
1eb41ae8 323 return type;
324}
325
326/* allocate a variable size buffer using best-fit pool */
327void *
328memAllocBuf(size_t net_size, size_t * gross_size)
329{
330 mem_type type = memFindBufSizeType(net_size, gross_size);
62e76326 331
1eb41ae8 332 if (type != MEM_NONE)
62e76326 333 return memAllocate(type);
1eb41ae8 334 else {
62e76326 335 memMeterInc(HugeBufCountMeter);
336 memMeterAdd(HugeBufVolumeMeter, *gross_size);
337 return xcalloc(1, net_size);
1eb41ae8 338 }
339}
340
341/* resize a variable sized buffer using best-fit pool */
342void *
343memReallocBuf(void *oldbuf, size_t net_size, size_t * gross_size)
344{
345 /* XXX This can be optimized on very large buffers to use realloc() */
edce4d98 346 /* TODO: if the existing gross size is >= new gross size, do nothing */
e6ccf245 347 size_t new_gross_size;
1eb41ae8 348 void *newbuf = memAllocBuf(net_size, &new_gross_size);
62e76326 349
1eb41ae8 350 if (oldbuf) {
62e76326 351 size_t data_size = *gross_size;
352
353 if (data_size > net_size)
354 data_size = net_size;
355
356 memcpy(newbuf, oldbuf, data_size);
357
358 memFreeBuf(*gross_size, oldbuf);
1eb41ae8 359 }
62e76326 360
1eb41ae8 361 *gross_size = new_gross_size;
362 return newbuf;
363}
364
365/* free buffer allocated with memAllocBuf() */
366void
367memFreeBuf(size_t size, void *buf)
368{
369 mem_type type = memFindBufSizeType(size, NULL);
62e76326 370
1eb41ae8 371 if (type != MEM_NONE)
62e76326 372 memFree(buf, type);
1eb41ae8 373 else {
62e76326 374 xfree(buf);
375 memMeterDec(HugeBufCountMeter);
376 memMeterDel(HugeBufVolumeMeter, size);
1eb41ae8 377 }
378}
379
d96ceb8e 380static double clean_interval = 15.0; /* time to live of idle chunk before release */
381
382void
528b2c61 383Mem::CleanIdlePools(void *unused)
d96ceb8e 384{
b001e822 385 MemPools::GetInstance().clean(static_cast<time_t>(clean_interval));
528b2c61 386 eventAdd("memPoolCleanIdlePools", CleanIdlePools, NULL, clean_interval, 1);
d96ceb8e 387}
388
d96ceb8e 389void
390memConfigure(void)
391{
70be1349 392 int64_t new_pool_limit;
62e76326 393
09c5ae5a 394 /** Set to configured value first */
d96ceb8e 395 if (!Config.onoff.mem_pools)
62e76326 396 new_pool_limit = 0;
d96ceb8e 397 else if (Config.MemPools.limit > 0)
62e76326 398 new_pool_limit = Config.MemPools.limit;
89646bd7 399 else {
c8768728 400 if (Config.MemPools.limit == 0)
e0236918 401 debugs(13, DBG_IMPORTANT, "memory_pools_limit 0 has been chagned to memory_pools_limit none. Please update your config");
89646bd7
HN
402 new_pool_limit = -1;
403 }
d96ceb8e 404
af00d03d 405#if 0
09c5ae5a 406 /** \par
af00d03d 407 * DPW 2007-04-12
408 * No debugging here please because this method is called before
409 * the debug log is configured and we'll get the message on
410 * stderr when doing things like 'squid -k reconfigure'
411 */
b001e822 412 if (MemPools::GetInstance().idleLimit() > new_pool_limit)
e0236918 413 debugs(13, DBG_IMPORTANT, "Shrinking idle mem pools to "<< std::setprecision(3) << toMB(new_pool_limit) << " MB");
af00d03d 414#endif
62e76326 415
b001e822 416 MemPools::GetInstance().setIdleLimit(new_pool_limit);
d96ceb8e 417}
1eb41ae8 418
528b2c61 419/* XXX make these classes do their own memory management */
420#include "HttpHdrContRange.h"
421
acf5589a 422void
528b2c61 423Mem::Init(void)
acf5589a 424{
9fe7e747 425 int i;
d96ceb8e 426
09c5ae5a 427 /** \par
33135cfb 428 * NOTE: Mem::Init() is called before the config file is parsed
429 * and before the debugging module has been initialized. Any
430 * debug messages here at level 0 or 1 will always be printed
431 * on stderr.
432 */
d96ceb8e 433
09c5ae5a
AJ
434 /** \par
435 * Set all pointers to null. */
7021844c 436 memset(MemPools, '\0', sizeof(MemPools));
09c5ae5a
AJ
437 /**
438 * Then initialize all pools.
439 * \par
440 * Starting with generic 2kB - 64kB buffr pools, then specific object types.
441 * \par
442 * It does not hurt much to have a lot of pools since sizeof(MemPool) is
7021844c 443 * small; someday we will figure out what to do with all the entries here
444 * that are never used or used only once; perhaps we should simply use
445 * malloc() for those? @?@
446 */
60da11d3 447 memDataInit(MEM_2K_BUF, "2K Buffer", 2048, 10, false);
448 memDataInit(MEM_4K_BUF, "4K Buffer", 4096, 10, false);
449 memDataInit(MEM_8K_BUF, "8K Buffer", 8192, 10, false);
450 memDataInit(MEM_16K_BUF, "16K Buffer", 16384, 10, false);
451 memDataInit(MEM_32K_BUF, "32K Buffer", 32768, 10, false);
452 memDataInit(MEM_64K_BUF, "64K Buffer", 65536, 10, false);
acf5589a 453 memDataInit(MEM_ACL_DENY_INFO_LIST, "acl_deny_info_list",
62e76326 454 sizeof(acl_deny_info_list), 0);
6be70545 455 memDataInit(MEM_ACL_NAME_LIST, "acl_name_list", sizeof(AclNameList), 0);
c68e9c6b 456#if USE_CACHE_DIGESTS
62e76326 457
26c2ce6f 458 memDataInit(MEM_CACHE_DIGEST, "CacheDigest", sizeof(CacheDigest), 0);
c68e9c6b 459#endif
62e76326 460
58cd5bbd 461 memDataInit(MEM_LINK_LIST, "link_list", sizeof(link_list), 10);
acf5589a 462 memDataInit(MEM_DLINK_NODE, "dlink_node", sizeof(dlink_node), 10);
acf5589a 463 memDataInit(MEM_DREAD_CTRL, "dread_ctrl", sizeof(dread_ctrl), 0);
464 memDataInit(MEM_DWRITE_Q, "dwrite_q", sizeof(dwrite_q), 0);
d76fcfa7 465 memDataInit(MEM_HTTP_HDR_CONTENT_RANGE, "HttpHdrContRange", sizeof(HttpHdrContRange), 0);
acf5589a 466 memDataInit(MEM_NETDBENTRY, "netdbEntry", sizeof(netdbEntry), 0);
467 memDataInit(MEM_NET_DB_NAME, "net_db_name", sizeof(net_db_name), 0);
acf5589a 468 memDataInit(MEM_RELIST, "relist", sizeof(relist), 0);
59c4d35b 469 memDataInit(MEM_CLIENT_INFO, "ClientInfo", sizeof(ClientInfo), 0);
c3031d67 470 memDataInit(MEM_MD5_DIGEST, "MD5 digest", SQUID_MD5_DIGEST_LENGTH, 0);
b001e822 471 MemPools[MEM_MD5_DIGEST]->setChunkSize(512 * 1024);
58cd5bbd 472
09c5ae5a 473 /** Lastly init the string pools. */
89235243 474 for (i = 0; i < mem_str_pool_count; ++i) {
04eb0689 475 StrPools[i].pool = memPoolCreate(StrPoolsAttrs[i].name, StrPoolsAttrs[i].obj_size);
60da11d3 476 StrPools[i].pool->zeroOnPush(false);
e231a8ce 477
b001e822 478 if (StrPools[i].pool->objectSize() != StrPoolsAttrs[i].obj_size)
e0236918 479 debugs(13, DBG_IMPORTANT, "Notice: " << StrPoolsAttrs[i].name << " is " << StrPools[i].pool->objectSize() << " bytes instead of requested " << StrPoolsAttrs[i].obj_size << " bytes");
58a39dc9 480 }
ea391f18 481
b5a644fc 482 MemIsInitialized = true;
09c5ae5a
AJ
483 /** \par
484 * finally register with the cache manager */
ea391f18 485 RegisterWithCacheManager();
62ee09ca 486}
62e76326 487
97244680 488void
489Mem::Report()
490{
491 debugs(13, 3, "Memory pools are '" <<
26ac0430
AJ
492 (Config.onoff.mem_pools ? "on" : "off") << "'; limit: " <<
493 std::setprecision(3) << toMB(MemPools::GetInstance().idleLimit()) <<
494 " MB");
97244680 495}
496
62ee09ca 497void
84f50787 498Mem::RegisterWithCacheManager(void)
62ee09ca 499{
8822ebee 500 Mgr::RegisterAction("mem", "Memory Utilization", Mem::Stats, 0, 1);
58a39dc9 501}
502
e6ccf245 503mem_type &operator++ (mem_type &aMem)
504{
1f1ae50a 505 int tmp = (int)aMem;
506 aMem = (mem_type)(++tmp);
e6ccf245 507 return aMem;
508}
509
58a39dc9 510/*
511 * Test that all entries are initialized
512 */
513void
514memCheckInit(void)
515{
92b77cf7 516 mem_type t = MEM_NONE;
62e76326 517
92b77cf7 518 while (++t < MEM_DONTFREE) {
62e76326 519 /*
520 * If you hit this assertion, then you forgot to add a
521 * memDataInit() line for type 't'.
92b77cf7 522 * Or placed the pool type in the wrong section of the enum list.
62e76326 523 */
524 assert(MemPools[t]);
acf5589a 525 }
526}
527
528void
58a39dc9 529memClean(void)
acf5589a 530{
d96ceb8e 531 MemPoolGlobalStats stats;
f5f9e44c
AR
532 if (Config.MemPools.limit > 0) // do not reset if disabled or same
533 MemPools::GetInstance().setIdleLimit(0);
b001e822 534 MemPools::GetInstance().clean(0);
d96ceb8e 535 memPoolGetGlobalStats(&stats);
62e76326 536
d96ceb8e 537 if (stats.tot_items_inuse)
bf8fe701 538 debugs(13, 2, "memCleanModule: " << stats.tot_items_inuse <<
539 " items in " << stats.tot_chunks_inuse << " chunks and " <<
540 stats.tot_pools_inuse << " pools are left dirty");
acf5589a 541}
542
acf5589a 543int
544memInUse(mem_type type)
545{
b4832aa9 546 return memPoolInUseCount(MemPools[type]);
acf5589a 547}
548
549/* ick */
550
e4ae841b 551void
137ee196 552memFree2K(void *p)
553{
db1cd23c 554 memFree(p, MEM_2K_BUF);
137ee196 555}
556
acf5589a 557void
558memFree4K(void *p)
559{
db1cd23c 560 memFree(p, MEM_4K_BUF);
acf5589a 561}
562
563void
564memFree8K(void *p)
565{
db1cd23c 566 memFree(p, MEM_8K_BUF);
acf5589a 567}
58cd5bbd 568
e4ae841b 569void
58cd5bbd 570memFree16K(void *p)
571{
572 memFree(p, MEM_16K_BUF);
573}
574
e4ae841b 575void
58cd5bbd 576memFree32K(void *p)
577{
578 memFree(p, MEM_32K_BUF);
579}
580
e4ae841b 581void
58cd5bbd 582memFree64K(void *p)
583{
584 memFree(p, MEM_64K_BUF);
585}
1eb41ae8 586
59a09b98
FC
587static void
588cxx_xfree(void * ptr)
589{
f673997d 590 xfree(ptr);
59a09b98
FC
591}
592
1eb41ae8 593FREE *
594memFreeBufFunc(size_t size)
595{
fa80a8ef 596 switch (size) {
62e76326 597
fa80a8ef 598 case 2 * 1024:
62e76326 599 return memFree2K;
600
fa80a8ef 601 case 4 * 1024:
62e76326 602 return memFree4K;
603
fa80a8ef 604 case 8 * 1024:
62e76326 605 return memFree8K;
606
fa80a8ef 607 case 16 * 1024:
62e76326 608 return memFree16K;
609
fa80a8ef 610 case 32 * 1024:
62e76326 611 return memFree32K;
612
fa80a8ef 613 case 64 * 1024:
62e76326 614 return memFree64K;
615
1eb41ae8 616 default:
62e76326 617 memMeterDec(HugeBufCountMeter);
618 memMeterDel(HugeBufVolumeMeter, size);
59a09b98 619 return cxx_xfree;
1eb41ae8 620 }
621}
d96ceb8e 622
623/* MemPoolMeter */
624
528b2c61 625void
c21ad0f5 626Mem::PoolReport(const MemPoolStats * mp_st, const MemPoolMeter * AllMeter, std::ostream &stream)
d96ceb8e 627{
60eed7c2 628 int excess = 0;
d96ceb8e 629 int needed = 0;
630 MemPoolMeter *pm = mp_st->meter;
eecdacf6 631 const char *delim = "\t ";
d96ceb8e 632
903a6eec
HN
633#if HAVE_IOMANIP
634 stream.setf(std::ios_base::fixed);
635#endif
eecdacf6 636 stream << std::setw(20) << std::left << mp_st->label << delim;
637 stream << std::setw(4) << std::right << mp_st->obj_size << delim;
d96ceb8e 638
639 /* Chunks */
d96ceb8e 640 if (mp_st->chunk_capacity) {
3b32112a
A
641 stream << std::setw(4) << toKB(mp_st->obj_size * mp_st->chunk_capacity) << delim;
642 stream << std::setw(4) << mp_st->chunk_capacity << delim;
333c86f5 643
62e76326 644 needed = mp_st->items_inuse / mp_st->chunk_capacity;
645
646 if (mp_st->items_inuse % mp_st->chunk_capacity)
89235243 647 ++needed;
62e76326 648
649 excess = mp_st->chunks_inuse - needed;
62e76326 650
3b32112a
A
651 stream << std::setw(4) << mp_st->chunks_alloc << delim;
652 stream << std::setw(4) << mp_st->chunks_inuse << delim;
653 stream << std::setw(4) << mp_st->chunks_free << delim;
654 stream << std::setw(4) << mp_st->chunks_partial << delim;
655 stream << std::setprecision(3) << xpercent(excess, needed) << delim;
333c86f5 656 } else {
3b32112a
A
657 stream << delim;
658 stream << delim;
659 stream << delim;
660 stream << delim;
661 stream << delim;
662 stream << delim;
663 stream << delim;
333c86f5 664 }
62e76326 665 /*
666 * Fragmentation calculation:
667 * needed = inuse.level / chunk_capacity
668 * excess = used - needed
669 * fragmentation = excess / needed * 100%
670 *
671 * Fragm = (alloced - (inuse / obj_ch) ) / alloced
672 */
c21ad0f5 673 /* allocated */
eecdacf6 674 stream << mp_st->items_alloc << delim;
675 stream << toKB(mp_st->obj_size * pm->alloc.level) << delim;
676 stream << toKB(mp_st->obj_size * pm->alloc.hwater_level) << delim;
677 stream << std::setprecision(2) << ((squid_curtime - pm->alloc.hwater_stamp) / 3600.) << delim;
678 stream << std::setprecision(3) << xpercent(mp_st->obj_size * pm->alloc.level, AllMeter->alloc.level) << delim;
c21ad0f5 679 /* in use */
eecdacf6 680 stream << mp_st->items_inuse << delim;
681 stream << toKB(mp_st->obj_size * pm->inuse.level) << delim;
682 stream << toKB(mp_st->obj_size * pm->inuse.hwater_level) << delim;
683 stream << std::setprecision(2) << ((squid_curtime - pm->inuse.hwater_stamp) / 3600.) << delim;
684 stream << std::setprecision(3) << xpercent(pm->inuse.level, pm->alloc.level) << delim;
c21ad0f5 685 /* idle */
eecdacf6 686 stream << mp_st->items_idle << delim;
687 stream << toKB(mp_st->obj_size * pm->idle.level) << delim;
688 stream << toKB(mp_st->obj_size * pm->idle.hwater_level) << delim;
c21ad0f5 689 /* saved */
eecdacf6 690 stream << (int)pm->gb_saved.count << delim;
903a6eec
HN
691 stream << std::setprecision(3) << xpercent(pm->gb_saved.count, AllMeter->gb_allocated.count) << delim;
692 stream << std::setprecision(3) << xpercent(pm->gb_saved.bytes, AllMeter->gb_allocated.bytes) << delim;
693 stream << std::setprecision(3) << xdiv(pm->gb_allocated.count - pm->gb_oallocated.count, xm_deltat) << "\n";
694 pm->gb_oallocated.count = pm->gb_allocated.count;
d96ceb8e 695}
696
729102f4 697static int
698MemPoolReportSorter(const void *a, const void *b)
699{
700 const MemPoolStats *A = (MemPoolStats *) a;
701 const MemPoolStats *B = (MemPoolStats *) b;
702
703 // use this to sort on %Total Allocated
704 //
705 double pa = (double) A->obj_size * A->meter->alloc.level;
706 double pb = (double) B->obj_size * B->meter->alloc.level;
707
708 if (pa > pb)
709 return -1;
710
711 if (pb > pa)
712 return 1;
713
714#if 0
715 // use this to sort on In Use high(hrs)
716 //
717 if (A->meter->inuse.hwater_stamp > B->meter->inuse.hwater_stamp)
718 return -1;
719
720 if (B->meter->inuse.hwater_stamp > A->meter->inuse.hwater_stamp)
721 return 1;
722
723#endif
724
725 return 0;
726}
727
d96ceb8e 728void
c21ad0f5 729Mem::Report(std::ostream &stream)
d96ceb8e 730{
731 static char buf[64];
732 static MemPoolStats mp_stats;
733 static MemPoolGlobalStats mp_total;
734 int not_used = 0;
735 MemPoolIterator *iter;
b001e822 736 MemAllocator *pool;
d96ceb8e 737
738 /* caption */
c21ad0f5 739 stream << "Current memory usage:\n";
d96ceb8e 740 /* heading */
c21ad0f5 741 stream << "Pool\t Obj Size\t"
742 "Chunks\t\t\t\t\t\t\t"
743 "Allocated\t\t\t\t\t"
744 "In Use\t\t\t\t\t"
745 "Idle\t\t\t"
746 "Allocations Saved\t\t\t"
903a6eec 747 "Rate\t"
c21ad0f5 748 "\n"
749 " \t (bytes)\t"
750 "KB/ch\t obj/ch\t"
35268c70 751 "(#)\t used\t free\t part\t %Frag\t "
752 "(#)\t (KB)\t high (KB)\t high (hrs)\t %Tot\t"
753 "(#)\t (KB)\t high (KB)\t high (hrs)\t %alloc\t"
c21ad0f5 754 "(#)\t (KB)\t high (KB)\t"
35268c70 755 "(#)\t %cnt\t %vol\t"
903a6eec 756 "(#)/sec\t"
c21ad0f5 757 "\n";
d96ceb8e 758 xm_deltat = current_dtime - xm_time;
759 xm_time = current_dtime;
760
761 /* Get stats for Totals report line */
762 memPoolGetGlobalStats(&mp_total);
763
729102f4 764 MemPoolStats *sortme = (MemPoolStats *) xcalloc(mp_total.tot_pools_alloc ,sizeof(*sortme));
765 int npools = 0;
766
d96ceb8e 767 /* main table */
768 iter = memPoolIterate();
62e76326 769
d96ceb8e 770 while ((pool = memPoolIterateNext(iter))) {
b001e822 771 pool->getStats(&mp_stats);
62e76326 772
773 if (!mp_stats.pool) /* pool destroyed */
774 continue;
775
f412b2d6
FC
776 if (mp_stats.pool->getMeter().gb_allocated.count > 0) {
777 /* this pool has been used */
778 sortme[npools] = mp_stats;
779 ++npools;
780 } else {
89235243 781 ++not_used;
f412b2d6 782 }
d96ceb8e 783 }
62e76326 784
d96ceb8e 785 memPoolIterateDone(&iter);
786
729102f4 787 qsort(sortme, npools, sizeof(*sortme), MemPoolReportSorter);
788
89235243 789 for (int i = 0; i< npools; ++i) {
c21ad0f5 790 PoolReport(&sortme[i], mp_total.TheMeter, stream);
729102f4 791 }
792
793 xfree(sortme);
794
d96ceb8e 795 mp_stats.pool = NULL;
796 mp_stats.label = "Total";
797 mp_stats.meter = mp_total.TheMeter;
798 mp_stats.obj_size = 1;
799 mp_stats.chunk_capacity = 0;
800 mp_stats.chunk_size = 0;
801 mp_stats.chunks_alloc = mp_total.tot_chunks_alloc;
802 mp_stats.chunks_inuse = mp_total.tot_chunks_inuse;
803 mp_stats.chunks_partial = mp_total.tot_chunks_partial;
804 mp_stats.chunks_free = mp_total.tot_chunks_free;
805 mp_stats.items_alloc = mp_total.tot_items_alloc;
806 mp_stats.items_inuse = mp_total.tot_items_inuse;
807 mp_stats.items_idle = mp_total.tot_items_idle;
808 mp_stats.overhead = mp_total.tot_overhead;
809
c21ad0f5 810 PoolReport(&mp_stats, mp_total.TheMeter, stream);
d96ceb8e 811
812 /* Cumulative */
903a6eec 813 stream << "Cumulative allocated volume: "<< double_to_str(buf, 64, mp_total.TheMeter->gb_allocated.bytes) << "\n";
d96ceb8e 814 /* overhead */
c21ad0f5 815 stream << "Current overhead: " << mp_total.tot_overhead << " bytes (" <<
35268c70 816 std::setprecision(3) << xpercent(mp_total.tot_overhead, mp_total.TheMeter->inuse.level) << "%)\n";
d96ceb8e 817 /* limits */
89646bd7
HN
818 if (mp_total.mem_idle_limit >= 0)
819 stream << "Idle pool limit: " << std::setprecision(2) << toMB(mp_total.mem_idle_limit) << " MB\n";
d96ceb8e 820 /* limits */
c21ad0f5 821 stream << "Total Pools created: " << mp_total.tot_pools_alloc << "\n";
822 stream << "Pools ever used: " << mp_total.tot_pools_alloc - not_used << " (shown above)\n";
823 stream << "Currently in use: " << mp_total.tot_pools_inuse << "\n";
d96ceb8e 824}