1 /*
2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
5 Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
21 */
22
23 #include "config.h"
24 #include "MemoryCache.h"
25
26 #include "CachedCSSStyleSheet.h"
27 #include "CachedFont.h"
28 #include "CachedImage.h"
29 #include "CachedScript.h"
30 #include "CachedXSLStyleSheet.h"
31 #include "CachedResourceLoader.h"
32 #include "Document.h"
33 #include "FrameLoader.h"
34 #include "FrameLoaderTypes.h"
35 #include "FrameView.h"
36 #include "Image.h"
37 #include "Logging.h"
38 #include "ResourceHandle.h"
39 #include "SecurityOrigin.h"
40 #include "SecurityOriginHash.h"
41 #include <stdio.h>
42 #include <wtf/CurrentTime.h>
43 #include <wtf/text/CString.h>
44
45 using namespace std;
46
47 namespace WebCore {
48
49 static const int cDefaultCacheCapacity = 8192 * 1024;
50 static const double cMinDelayBeforeLiveDecodedPrune = 1; // Seconds.
51 static const float cTargetPrunePercentage = .95f; // Percentage of capacity toward which we prune, to avoid immediately pruning again.
52 static const double cDefaultDecodedDataDeletionInterval = 0;
53
memoryCache()54 MemoryCache* memoryCache()
55 {
56 static MemoryCache* staticCache = new MemoryCache;
57 return staticCache;
58 }
59
MemoryCache()60 MemoryCache::MemoryCache()
61 : m_disabled(false)
62 , m_pruneEnabled(true)
63 , m_inPruneDeadResources(false)
64 , m_capacity(cDefaultCacheCapacity)
65 , m_minDeadCapacity(0)
66 , m_maxDeadCapacity(cDefaultCacheCapacity)
67 , m_deadDecodedDataDeletionInterval(cDefaultDecodedDataDeletionInterval)
68 , m_liveSize(0)
69 , m_deadSize(0)
70 {
71 }
72
removeFragmentIdentifierIfNeeded(const KURL & originalURL)73 KURL MemoryCache::removeFragmentIdentifierIfNeeded(const KURL& originalURL)
74 {
75 if (!originalURL.hasFragmentIdentifier())
76 return originalURL;
77 // Strip away fragment identifier from HTTP and file urls.
78 // Data urls must be unmodified and it is also safer to keep them for custom protocols.
79 if (!(originalURL.protocolInHTTPFamily() || originalURL.isLocalFile()))
80 return originalURL;
81 KURL url = originalURL;
82 url.removeFragmentIdentifier();
83 return url;
84 }
85
add(CachedResource * resource)86 bool MemoryCache::add(CachedResource* resource)
87 {
88 if (disabled())
89 return false;
90
91 m_resources.set(resource->url(), resource);
92 resource->setInCache(true);
93
94 resourceAccessed(resource);
95
96 LOG(ResourceLoading, "MemoryCache::add Added '%s', resource %p\n", resource->url().latin1().data(), resource);
97 return true;
98 }
99
revalidationSucceeded(CachedResource * revalidatingResource,const ResourceResponse & response)100 void MemoryCache::revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse& response)
101 {
102 CachedResource* resource = revalidatingResource->resourceToRevalidate();
103 ASSERT(resource);
104 ASSERT(!resource->inCache());
105 ASSERT(resource->isLoaded());
106 ASSERT(revalidatingResource->inCache());
107
108 evict(revalidatingResource);
109
110 ASSERT(!m_resources.get(resource->url()));
111 m_resources.set(resource->url(), resource);
112 resource->setInCache(true);
113 resource->updateResponseAfterRevalidation(response);
114 insertInLRUList(resource);
115 int delta = resource->size();
116 if (resource->decodedSize() && resource->hasClients())
117 insertInLiveDecodedResourcesList(resource);
118 if (delta)
119 adjustSize(resource->hasClients(), delta);
120
121 revalidatingResource->switchClientsToRevalidatedResource();
122 // this deletes the revalidating resource
123 revalidatingResource->clearResourceToRevalidate();
124 }
125
revalidationFailed(CachedResource * revalidatingResource)126 void MemoryCache::revalidationFailed(CachedResource* revalidatingResource)
127 {
128 LOG(ResourceLoading, "Revalidation failed for %p", revalidatingResource);
129 ASSERT(revalidatingResource->resourceToRevalidate());
130 revalidatingResource->clearResourceToRevalidate();
131 }
132
resourceForURL(const KURL & resourceURL)133 CachedResource* MemoryCache::resourceForURL(const KURL& resourceURL)
134 {
135 KURL url = removeFragmentIdentifierIfNeeded(resourceURL);
136 CachedResource* resource = m_resources.get(url);
137 bool wasPurgeable = MemoryCache::shouldMakeResourcePurgeableOnEviction() && resource && resource->isPurgeable();
138 if (resource && !resource->makePurgeable(false)) {
139 ASSERT(!resource->hasClients());
140 evict(resource);
141 return 0;
142 }
143 // Add the size back since we had subtracted it when we marked the memory as purgeable.
144 if (wasPurgeable)
145 adjustSize(resource->hasClients(), resource->size());
146 return resource;
147 }
148
deadCapacity() const149 unsigned MemoryCache::deadCapacity() const
150 {
151 // Dead resource capacity is whatever space is not occupied by live resources, bounded by an independent minimum and maximum.
152 unsigned capacity = m_capacity - min(m_liveSize, m_capacity); // Start with available capacity.
153 capacity = max(capacity, m_minDeadCapacity); // Make sure it's above the minimum.
154 capacity = min(capacity, m_maxDeadCapacity); // Make sure it's below the maximum.
155 return capacity;
156 }
157
liveCapacity() const158 unsigned MemoryCache::liveCapacity() const
159 {
160 // Live resource capacity is whatever is left over after calculating dead resource capacity.
161 return m_capacity - deadCapacity();
162 }
163
pruneLiveResources()164 void MemoryCache::pruneLiveResources()
165 {
166 if (!m_pruneEnabled)
167 return;
168
169 unsigned capacity = liveCapacity();
170 if (capacity && m_liveSize <= capacity)
171 return;
172
173 unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again.
174 double currentTime = FrameView::currentPaintTimeStamp();
175 if (!currentTime) // In case prune is called directly, outside of a Frame paint.
176 currentTime = WTF::currentTime();
177
178 // Destroy any decoded data in live objects that we can.
179 // Start from the tail, since this is the least recently accessed of the objects.
180
181 // The list might not be sorted by the m_lastDecodedAccessTime. The impact
182 // of this weaker invariant is minor as the below if statement to check the
183 // elapsedTime will evaluate to false as the currentTime will be a lot
184 // greater than the current->m_lastDecodedAccessTime.
185 // For more details see: https://bugs.webkit.org/show_bug.cgi?id=30209
186 CachedResource* current = m_liveDecodedResources.m_tail;
187 while (current) {
188 CachedResource* prev = current->m_prevInLiveResourcesList;
189 ASSERT(current->hasClients());
190 if (current->isLoaded() && current->decodedSize()) {
191 // Check to see if the remaining resources are too new to prune.
192 double elapsedTime = currentTime - current->m_lastDecodedAccessTime;
193 if (elapsedTime < cMinDelayBeforeLiveDecodedPrune)
194 return;
195
196 // Destroy our decoded data. This will remove us from
197 // m_liveDecodedResources, and possibly move us to a different LRU
198 // list in m_allResources.
199 current->destroyDecodedData();
200
201 if (targetSize && m_liveSize <= targetSize)
202 return;
203 }
204 current = prev;
205 }
206 }
207
pruneDeadResources()208 void MemoryCache::pruneDeadResources()
209 {
210 if (!m_pruneEnabled)
211 return;
212
213 unsigned capacity = deadCapacity();
214 if (capacity && m_deadSize <= capacity)
215 return;
216
217 unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again.
218 int size = m_allResources.size();
219
220 if (!m_inPruneDeadResources) {
221 // See if we have any purged resources we can evict.
222 for (int i = 0; i < size; i++) {
223 CachedResource* current = m_allResources[i].m_tail;
224 while (current) {
225 CachedResource* prev = current->m_prevInAllResourcesList;
226 if (current->wasPurged()) {
227 ASSERT(!current->hasClients());
228 ASSERT(!current->isPreloaded());
229 evict(current);
230 }
231 current = prev;
232 }
233 }
234 if (targetSize && m_deadSize <= targetSize)
235 return;
236 }
237
238 bool canShrinkLRULists = true;
239 m_inPruneDeadResources = true;
240 for (int i = size - 1; i >= 0; i--) {
241 // Remove from the tail, since this is the least frequently accessed of the objects.
242 CachedResource* current = m_allResources[i].m_tail;
243
244 // First flush all the decoded data in this queue.
245 while (current) {
246 CachedResource* prev = current->m_prevInAllResourcesList;
247 if (!current->hasClients() && !current->isPreloaded() && current->isLoaded()) {
248 // Destroy our decoded data. This will remove us from
249 // m_liveDecodedResources, and possibly move us to a different
250 // LRU list in m_allResources.
251 current->destroyDecodedData();
252
253 if (targetSize && m_deadSize <= targetSize) {
254 m_inPruneDeadResources = false;
255 return;
256 }
257 }
258 current = prev;
259 }
260
261 // Now evict objects from this queue.
262 current = m_allResources[i].m_tail;
263 while (current) {
264 CachedResource* prev = current->m_prevInAllResourcesList;
265 if (!current->hasClients() && !current->isPreloaded() && !current->isCacheValidator()) {
266 if (!makeResourcePurgeable(current))
267 evict(current);
268
269 // If evict() caused pruneDeadResources() to be re-entered, bail out. This can happen when removing an
270 // SVG CachedImage that has subresources.
271 if (!m_inPruneDeadResources)
272 return;
273
274 if (targetSize && m_deadSize <= targetSize) {
275 m_inPruneDeadResources = false;
276 return;
277 }
278 }
279 current = prev;
280 }
281
282 // Shrink the vector back down so we don't waste time inspecting
283 // empty LRU lists on future prunes.
284 if (m_allResources[i].m_head)
285 canShrinkLRULists = false;
286 else if (canShrinkLRULists)
287 m_allResources.resize(i);
288 }
289 m_inPruneDeadResources = false;
290 }
291
setCapacities(unsigned minDeadBytes,unsigned maxDeadBytes,unsigned totalBytes)292 void MemoryCache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes)
293 {
294 ASSERT(minDeadBytes <= maxDeadBytes);
295 ASSERT(maxDeadBytes <= totalBytes);
296 m_minDeadCapacity = minDeadBytes;
297 m_maxDeadCapacity = maxDeadBytes;
298 m_capacity = totalBytes;
299 prune();
300 }
301
makeResourcePurgeable(CachedResource * resource)302 bool MemoryCache::makeResourcePurgeable(CachedResource* resource)
303 {
304 if (!MemoryCache::shouldMakeResourcePurgeableOnEviction())
305 return false;
306
307 if (!resource->inCache())
308 return false;
309
310 if (resource->isPurgeable())
311 return true;
312
313 if (!resource->isSafeToMakePurgeable())
314 return false;
315
316 if (!resource->makePurgeable(true))
317 return false;
318
319 adjustSize(resource->hasClients(), -static_cast<int>(resource->size()));
320
321 return true;
322 }
323
evict(CachedResource * resource)324 void MemoryCache::evict(CachedResource* resource)
325 {
326 LOG(ResourceLoading, "Evicting resource %p for '%s' from cache", resource, resource->url().latin1().data());
327 // The resource may have already been removed by someone other than our caller,
328 // who needed a fresh copy for a reload. See <http://bugs.webkit.org/show_bug.cgi?id=12479#c6>.
329 if (resource->inCache()) {
330 // Remove from the resource map.
331 m_resources.remove(resource->url());
332 resource->setInCache(false);
333
334 // Remove from the appropriate LRU list.
335 removeFromLRUList(resource);
336 removeFromLiveDecodedResourcesList(resource);
337
338 // If the resource was purged, it means we had already decremented the size when we made the
339 // resource purgeable in makeResourcePurgeable(). So adjust the size if we are evicting a
340 // resource that was not marked as purgeable.
341 if (!MemoryCache::shouldMakeResourcePurgeableOnEviction() || !resource->isPurgeable())
342 adjustSize(resource->hasClients(), -static_cast<int>(resource->size()));
343 } else
344 ASSERT(m_resources.get(resource->url()) != resource);
345
346 if (resource->canDelete())
347 delete resource;
348 }
349
fastLog2(unsigned i)350 static inline unsigned fastLog2(unsigned i)
351 {
352 unsigned log2 = 0;
353 if (i & (i - 1))
354 log2 += 1;
355 if (i >> 16)
356 log2 += 16, i >>= 16;
357 if (i >> 8)
358 log2 += 8, i >>= 8;
359 if (i >> 4)
360 log2 += 4, i >>= 4;
361 if (i >> 2)
362 log2 += 2, i >>= 2;
363 if (i >> 1)
364 log2 += 1;
365 return log2;
366 }
367
lruListFor(CachedResource * resource)368 MemoryCache::LRUList* MemoryCache::lruListFor(CachedResource* resource)
369 {
370 unsigned accessCount = max(resource->accessCount(), 1U);
371 unsigned queueIndex = fastLog2(resource->size() / accessCount);
372 #ifndef NDEBUG
373 resource->m_lruIndex = queueIndex;
374 #endif
375 if (m_allResources.size() <= queueIndex)
376 m_allResources.grow(queueIndex + 1);
377 return &m_allResources[queueIndex];
378 }
379
removeFromLRUList(CachedResource * resource)380 void MemoryCache::removeFromLRUList(CachedResource* resource)
381 {
382 // If we've never been accessed, then we're brand new and not in any list.
383 if (resource->accessCount() == 0)
384 return;
385
386 #if !ASSERT_DISABLED
387 unsigned oldListIndex = resource->m_lruIndex;
388 #endif
389
390 LRUList* list = lruListFor(resource);
391
392 #if !ASSERT_DISABLED
393 // Verify that the list we got is the list we want.
394 ASSERT(resource->m_lruIndex == oldListIndex);
395
396 // Verify that we are in fact in this list.
397 bool found = false;
398 for (CachedResource* current = list->m_head; current; current = current->m_nextInAllResourcesList) {
399 if (current == resource) {
400 found = true;
401 break;
402 }
403 }
404 ASSERT(found);
405 #endif
406
407 CachedResource* next = resource->m_nextInAllResourcesList;
408 CachedResource* prev = resource->m_prevInAllResourcesList;
409
410 if (next == 0 && prev == 0 && list->m_head != resource)
411 return;
412
413 resource->m_nextInAllResourcesList = 0;
414 resource->m_prevInAllResourcesList = 0;
415
416 if (next)
417 next->m_prevInAllResourcesList = prev;
418 else if (list->m_tail == resource)
419 list->m_tail = prev;
420
421 if (prev)
422 prev->m_nextInAllResourcesList = next;
423 else if (list->m_head == resource)
424 list->m_head = next;
425 }
426
insertInLRUList(CachedResource * resource)427 void MemoryCache::insertInLRUList(CachedResource* resource)
428 {
429 // Make sure we aren't in some list already.
430 ASSERT(!resource->m_nextInAllResourcesList && !resource->m_prevInAllResourcesList);
431 ASSERT(resource->inCache());
432 ASSERT(resource->accessCount() > 0);
433
434 LRUList* list = lruListFor(resource);
435
436 resource->m_nextInAllResourcesList = list->m_head;
437 if (list->m_head)
438 list->m_head->m_prevInAllResourcesList = resource;
439 list->m_head = resource;
440
441 if (!resource->m_nextInAllResourcesList)
442 list->m_tail = resource;
443
444 #ifndef NDEBUG
445 // Verify that we are in now in the list like we should be.
446 list = lruListFor(resource);
447 bool found = false;
448 for (CachedResource* current = list->m_head; current; current = current->m_nextInAllResourcesList) {
449 if (current == resource) {
450 found = true;
451 break;
452 }
453 }
454 ASSERT(found);
455 #endif
456
457 }
458
resourceAccessed(CachedResource * resource)459 void MemoryCache::resourceAccessed(CachedResource* resource)
460 {
461 ASSERT(resource->inCache());
462
463 // Need to make sure to remove before we increase the access count, since
464 // the queue will possibly change.
465 removeFromLRUList(resource);
466
467 // If this is the first time the resource has been accessed, adjust the size of the cache to account for its initial size.
468 if (!resource->accessCount())
469 adjustSize(resource->hasClients(), resource->size());
470
471 // Add to our access count.
472 resource->increaseAccessCount();
473
474 // Now insert into the new queue.
475 insertInLRUList(resource);
476 }
477
removeResourcesWithOrigin(SecurityOrigin * origin)478 void MemoryCache::removeResourcesWithOrigin(SecurityOrigin* origin)
479 {
480 Vector<CachedResource*> resourcesWithOrigin;
481
482 CachedResourceMap::iterator e = m_resources.end();
483 for (CachedResourceMap::iterator it = m_resources.begin(); it != e; ++it) {
484 CachedResource* resource = it->second;
485 RefPtr<SecurityOrigin> resourceOrigin = SecurityOrigin::createFromString(resource->url());
486 if (!resourceOrigin)
487 continue;
488 if (resourceOrigin->equal(origin))
489 resourcesWithOrigin.append(resource);
490 }
491
492 for (size_t i = 0; i < resourcesWithOrigin.size(); ++i)
493 remove(resourcesWithOrigin[i]);
494 }
495
getOriginsWithCache(SecurityOriginSet & origins)496 void MemoryCache::getOriginsWithCache(SecurityOriginSet& origins)
497 {
498 CachedResourceMap::iterator e = m_resources.end();
499 for (CachedResourceMap::iterator it = m_resources.begin(); it != e; ++it)
500 origins.add(SecurityOrigin::create(KURL(KURL(), it->second->url())));
501 }
502
removeFromLiveDecodedResourcesList(CachedResource * resource)503 void MemoryCache::removeFromLiveDecodedResourcesList(CachedResource* resource)
504 {
505 // If we've never been accessed, then we're brand new and not in any list.
506 if (!resource->m_inLiveDecodedResourcesList)
507 return;
508 resource->m_inLiveDecodedResourcesList = false;
509
510 #ifndef NDEBUG
511 // Verify that we are in fact in this list.
512 bool found = false;
513 for (CachedResource* current = m_liveDecodedResources.m_head; current; current = current->m_nextInLiveResourcesList) {
514 if (current == resource) {
515 found = true;
516 break;
517 }
518 }
519 ASSERT(found);
520 #endif
521
522 CachedResource* next = resource->m_nextInLiveResourcesList;
523 CachedResource* prev = resource->m_prevInLiveResourcesList;
524
525 if (next == 0 && prev == 0 && m_liveDecodedResources.m_head != resource)
526 return;
527
528 resource->m_nextInLiveResourcesList = 0;
529 resource->m_prevInLiveResourcesList = 0;
530
531 if (next)
532 next->m_prevInLiveResourcesList = prev;
533 else if (m_liveDecodedResources.m_tail == resource)
534 m_liveDecodedResources.m_tail = prev;
535
536 if (prev)
537 prev->m_nextInLiveResourcesList = next;
538 else if (m_liveDecodedResources.m_head == resource)
539 m_liveDecodedResources.m_head = next;
540 }
541
insertInLiveDecodedResourcesList(CachedResource * resource)542 void MemoryCache::insertInLiveDecodedResourcesList(CachedResource* resource)
543 {
544 // Make sure we aren't in the list already.
545 ASSERT(!resource->m_nextInLiveResourcesList && !resource->m_prevInLiveResourcesList && !resource->m_inLiveDecodedResourcesList);
546 resource->m_inLiveDecodedResourcesList = true;
547
548 resource->m_nextInLiveResourcesList = m_liveDecodedResources.m_head;
549 if (m_liveDecodedResources.m_head)
550 m_liveDecodedResources.m_head->m_prevInLiveResourcesList = resource;
551 m_liveDecodedResources.m_head = resource;
552
553 if (!resource->m_nextInLiveResourcesList)
554 m_liveDecodedResources.m_tail = resource;
555
556 #ifndef NDEBUG
557 // Verify that we are in now in the list like we should be.
558 bool found = false;
559 for (CachedResource* current = m_liveDecodedResources.m_head; current; current = current->m_nextInLiveResourcesList) {
560 if (current == resource) {
561 found = true;
562 break;
563 }
564 }
565 ASSERT(found);
566 #endif
567
568 }
569
addToLiveResourcesSize(CachedResource * resource)570 void MemoryCache::addToLiveResourcesSize(CachedResource* resource)
571 {
572 m_liveSize += resource->size();
573 m_deadSize -= resource->size();
574 }
575
removeFromLiveResourcesSize(CachedResource * resource)576 void MemoryCache::removeFromLiveResourcesSize(CachedResource* resource)
577 {
578 m_liveSize -= resource->size();
579 m_deadSize += resource->size();
580 }
581
adjustSize(bool live,int delta)582 void MemoryCache::adjustSize(bool live, int delta)
583 {
584 if (live) {
585 ASSERT(delta >= 0 || ((int)m_liveSize + delta >= 0));
586 m_liveSize += delta;
587 } else {
588 ASSERT(delta >= 0 || ((int)m_deadSize + delta >= 0));
589 m_deadSize += delta;
590 }
591 }
592
addResource(CachedResource * o)593 void MemoryCache::TypeStatistic::addResource(CachedResource* o)
594 {
595 bool purged = o->wasPurged();
596 bool purgeable = o->isPurgeable() && !purged;
597 int pageSize = (o->encodedSize() + o->overheadSize() + 4095) & ~4095;
598 count++;
599 size += purged ? 0 : o->size();
600 liveSize += o->hasClients() ? o->size() : 0;
601 decodedSize += o->decodedSize();
602 purgeableSize += purgeable ? pageSize : 0;
603 purgedSize += purged ? pageSize : 0;
604 }
605
getStatistics()606 MemoryCache::Statistics MemoryCache::getStatistics()
607 {
608 Statistics stats;
609 CachedResourceMap::iterator e = m_resources.end();
610 for (CachedResourceMap::iterator i = m_resources.begin(); i != e; ++i) {
611 CachedResource* resource = i->second;
612 switch (resource->type()) {
613 case CachedResource::ImageResource:
614 stats.images.addResource(resource);
615 break;
616 case CachedResource::CSSStyleSheet:
617 stats.cssStyleSheets.addResource(resource);
618 break;
619 case CachedResource::Script:
620 stats.scripts.addResource(resource);
621 break;
622 #if ENABLE(XSLT)
623 case CachedResource::XSLStyleSheet:
624 stats.xslStyleSheets.addResource(resource);
625 break;
626 #endif
627 case CachedResource::FontResource:
628 stats.fonts.addResource(resource);
629 break;
630 default:
631 break;
632 }
633 }
634 return stats;
635 }
636
setDisabled(bool disabled)637 void MemoryCache::setDisabled(bool disabled)
638 {
639 m_disabled = disabled;
640 if (!m_disabled)
641 return;
642
643 for (;;) {
644 CachedResourceMap::iterator i = m_resources.begin();
645 if (i == m_resources.end())
646 break;
647 evict(i->second);
648 }
649 }
650
evictResources()651 void MemoryCache::evictResources()
652 {
653 if (disabled())
654 return;
655
656 setDisabled(true);
657 setDisabled(false);
658 }
659
660 #ifndef NDEBUG
dumpStats()661 void MemoryCache::dumpStats()
662 {
663 Statistics s = getStatistics();
664 printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "LiveSize", "DecodedSize", "PurgeableSize", "PurgedSize");
665 printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------");
666 printf("%-13s %13d %13d %13d %13d %13d %13d\n", "Images", s.images.count, s.images.size, s.images.liveSize, s.images.decodedSize, s.images.purgeableSize, s.images.purgedSize);
667 printf("%-13s %13d %13d %13d %13d %13d %13d\n", "CSS", s.cssStyleSheets.count, s.cssStyleSheets.size, s.cssStyleSheets.liveSize, s.cssStyleSheets.decodedSize, s.cssStyleSheets.purgeableSize, s.cssStyleSheets.purgedSize);
668 #if ENABLE(XSLT)
669 printf("%-13s %13d %13d %13d %13d %13d %13d\n", "XSL", s.xslStyleSheets.count, s.xslStyleSheets.size, s.xslStyleSheets.liveSize, s.xslStyleSheets.decodedSize, s.xslStyleSheets.purgeableSize, s.xslStyleSheets.purgedSize);
670 #endif
671 printf("%-13s %13d %13d %13d %13d %13d %13d\n", "JavaScript", s.scripts.count, s.scripts.size, s.scripts.liveSize, s.scripts.decodedSize, s.scripts.purgeableSize, s.scripts.purgedSize);
672 printf("%-13s %13d %13d %13d %13d %13d %13d\n", "Fonts", s.fonts.count, s.fonts.size, s.fonts.liveSize, s.fonts.decodedSize, s.fonts.purgeableSize, s.fonts.purgedSize);
673 printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------");
674 }
675
dumpLRULists(bool includeLive) const676 void MemoryCache::dumpLRULists(bool includeLive) const
677 {
678 printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced, isPurgeable, wasPurged):\n");
679
680 int size = m_allResources.size();
681 for (int i = size - 1; i >= 0; i--) {
682 printf("\n\nList %d: ", i);
683 CachedResource* current = m_allResources[i].m_tail;
684 while (current) {
685 CachedResource* prev = current->m_prevInAllResourcesList;
686 if (includeLive || !current->hasClients())
687 printf("(%.1fK, %.1fK, %uA, %dR, %d, %d); ", current->decodedSize() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, current->accessCount(), current->hasClients(), current->isPurgeable(), current->wasPurged());
688
689 current = prev;
690 }
691 }
692 }
693 #endif
694
695 } // namespace WebCore
696