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