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