]>
Commit | Line | Data |
---|---|---|
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 */ |
31 | static void memFree2K(void *); | |
32 | static void memFree4K(void *); | |
33 | static void memFree8K(void *); | |
34 | static void memFree16K(void *); | |
35 | static void memFree32K(void *); | |
36 | static void memFree64K(void *); | |
37 | ||
d96ceb8e | 38 | /* local prototypes */ |
c21ad0f5 | 39 | static void memStringStats(std::ostream &); |
d96ceb8e | 40 | |
41 | /* module locals */ | |
d96ceb8e | 42 | static double xm_time = 0; |
43 | static double xm_deltat = 0; | |
acf5589a | 44 | |
9fe7e747 | 45 | /* string pools */ |
867c718d | 46 | #define mem_str_pool_count 6 |
62e76326 | 47 | |
ff56eb86 | 48 | struct PoolMeta { |
ec878047 | 49 | const char *name; |
9fe7e747 | 50 | size_t obj_size; |
26ac0430 AJ |
51 | }; |
52 | ||
9663db1c AJ |
53 | static Mem::Meter StrCountMeter; |
54 | static Mem::Meter StrVolumeMeter; | |
9fe7e747 | 55 | |
9663db1c AJ |
56 | static Mem::Meter HugeBufCountMeter; |
57 | static 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 | 63 | static MemAllocator *& |
2d328b70 | 64 | GetPool(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 | 80 | static MemAllocator & |
dcb30b14 | 81 | GetStrPool(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 |
117 | static MemAllocator * | |
118 | memFindStringPool(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 | 130 | static void |
c21ad0f5 | 131 | memStringStats(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 | ||
157 | static void | |
c21ad0f5 | 158 | memBufStats(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 | 165 | void |
166 | Mem::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 | */ |
200 | void | |
ced8def3 | 201 | memDataInit(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 | 213 | void * |
7021844c | 214 | memAllocate(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 | 221 | void |
db1cd23c | 222 | memFree(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 | 229 | void * |
4be8fe59 | 230 | memAllocString(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 |
248 | void * |
249 | memAllocRigid(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 | 264 | size_t |
265 | memStringCount() | |
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 | 276 | void |
4be8fe59 | 277 | memFreeString(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 |
290 | void |
291 | memFreeRigid(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 */ |
308 | static mem_type | |
309 | memFindBufSizeType(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 */ | |
344 | void * | |
345 | memAllocBuf(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 */ | |
359 | void * | |
360 | memReallocBuf(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() */ | |
383 | void | |
384 | memFreeBuf(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 | 397 | static double clean_interval = 15.0; /* time to live of idle chunk before release */ |
d96ceb8e | 398 | |
399 | void | |
ced8def3 | 400 | Mem::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 | 406 | void |
407 | memConfigure(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 | 436 | void |
528b2c61 | 437 | Mem::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 | 478 | void |
479 | Mem::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 | 487 | mem_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 | */ | |
497 | void | |
498 | memCheckInit(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 | ||
511 | void | |
58a39dc9 | 512 | memClean(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 | 526 | int |
527 | memInUse(mem_type type) | |
528 | { | |
e8dcf312 | 529 | return GetPool(type)->inUseCount(); |
acf5589a | 530 | } |
531 | ||
532 | /* ick */ | |
533 | ||
e4ae841b | 534 | void |
137ee196 | 535 | memFree2K(void *p) |
536 | { | |
db1cd23c | 537 | memFree(p, MEM_2K_BUF); |
137ee196 | 538 | } |
539 | ||
acf5589a | 540 | void |
541 | memFree4K(void *p) | |
542 | { | |
db1cd23c | 543 | memFree(p, MEM_4K_BUF); |
acf5589a | 544 | } |
545 | ||
546 | void | |
547 | memFree8K(void *p) | |
548 | { | |
db1cd23c | 549 | memFree(p, MEM_8K_BUF); |
acf5589a | 550 | } |
58cd5bbd | 551 | |
e4ae841b | 552 | void |
58cd5bbd | 553 | memFree16K(void *p) |
554 | { | |
555 | memFree(p, MEM_16K_BUF); | |
556 | } | |
557 | ||
e4ae841b | 558 | void |
58cd5bbd | 559 | memFree32K(void *p) |
560 | { | |
561 | memFree(p, MEM_32K_BUF); | |
562 | } | |
563 | ||
e4ae841b | 564 | void |
58cd5bbd | 565 | memFree64K(void *p) |
566 | { | |
567 | memFree(p, MEM_64K_BUF); | |
568 | } | |
1eb41ae8 | 569 | |
59a09b98 FC |
570 | static void |
571 | cxx_xfree(void * ptr) | |
572 | { | |
f673997d | 573 | xfree(ptr); |
59a09b98 FC |
574 | } |
575 | ||
1eb41ae8 | 576 | FREE * |
577 | memFreeBufFunc(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 | 608 | void |
c21ad0f5 | 609 | Mem::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 | 678 | static int |
679 | MemPoolReportSorter(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 | 709 | void |
c21ad0f5 | 710 | Mem::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 |