1 /* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "core/timing/MemoryInfo.h" 33 34 #include "core/frame/LocalFrame.h" 35 #include "core/frame/Settings.h" 36 #include "platform/RuntimeEnabledFeatures.h" 37 #include "wtf/CurrentTime.h" 38 #include "wtf/MainThread.h" 39 #include "wtf/MathExtras.h" 40 #include <limits> 41 42 namespace blink { 43 44 class HeapSizeCache { 45 WTF_MAKE_NONCOPYABLE(HeapSizeCache); WTF_MAKE_FAST_ALLOCATED; 46 public: 47 HeapSizeCache() 48 : m_lastUpdateTime(0) 49 { 50 } 51 52 void getCachedHeapSize(HeapInfo& info) 53 { 54 maybeUpdate(); 55 info = m_info; 56 } 57 58 private: 59 void maybeUpdate() 60 { 61 // We rate-limit queries to once every twenty minutes to make it more difficult 62 // for attackers to compare memory usage before and after some event. 63 const double TwentyMinutesInSeconds = 20 * 60; 64 65 double now = monotonicallyIncreasingTime(); 66 if (now - m_lastUpdateTime >= TwentyMinutesInSeconds) { 67 update(); 68 m_lastUpdateTime = now; 69 } 70 } 71 72 void update() 73 { 74 ScriptGCEvent::getHeapSize(m_info); 75 m_info.usedJSHeapSize = quantizeMemorySize(m_info.usedJSHeapSize); 76 m_info.totalJSHeapSize = quantizeMemorySize(m_info.totalJSHeapSize); 77 m_info.jsHeapSizeLimit = quantizeMemorySize(m_info.jsHeapSizeLimit); 78 } 79 80 double m_lastUpdateTime; 81 82 HeapInfo m_info; 83 }; 84 85 // We quantize the sizes to make it more difficult for an attacker to see precise 86 // impact of operations on memory. The values are used for performance tuning, 87 // and hence don't need to be as refined when the value is large, so we threshold 88 // at a list of exponentially separated buckets. 89 size_t quantizeMemorySize(size_t size) 90 { 91 const int numberOfBuckets = 100; 92 DEFINE_STATIC_LOCAL(Vector<size_t>, bucketSizeList, ()); 93 94 if (bucketSizeList.isEmpty()) { 95 bucketSizeList.resize(numberOfBuckets); 96 97 float sizeOfNextBucket = 10000000.0; // First bucket size is roughly 10M. 98 const float largestBucketSize = 4000000000.0; // Roughly 4GB. 99 // We scale with the Nth root of the ratio, so that we use all the bucktes. 100 const float scalingFactor = exp(log(largestBucketSize / sizeOfNextBucket) / numberOfBuckets); 101 102 size_t nextPowerOfTen = static_cast<size_t>(pow(10, floor(log10(sizeOfNextBucket)) + 1) + 0.5); 103 size_t granularity = nextPowerOfTen / 1000; // We want 3 signficant digits. 104 105 for (int i = 0; i < numberOfBuckets; ++i) { 106 size_t currentBucketSize = static_cast<size_t>(sizeOfNextBucket); 107 bucketSizeList[i] = currentBucketSize - (currentBucketSize % granularity); 108 109 sizeOfNextBucket *= scalingFactor; 110 if (sizeOfNextBucket >= nextPowerOfTen) { 111 if (std::numeric_limits<size_t>::max() / 10 <= nextPowerOfTen) { 112 nextPowerOfTen = std::numeric_limits<size_t>::max(); 113 } else { 114 nextPowerOfTen *= 10; 115 granularity *= 10; 116 } 117 } 118 119 // Watch out for overflow, if the range is too large for size_t. 120 if (i > 0 && bucketSizeList[i] < bucketSizeList[i - 1]) 121 bucketSizeList[i] = std::numeric_limits<size_t>::max(); 122 } 123 } 124 125 for (int i = 0; i < numberOfBuckets; ++i) { 126 if (size <= bucketSizeList[i]) 127 return bucketSizeList[i]; 128 } 129 130 return bucketSizeList[numberOfBuckets - 1]; 131 } 132 133 MemoryInfo::MemoryInfo() 134 { 135 if (RuntimeEnabledFeatures::preciseMemoryInfoEnabled()) { 136 ScriptGCEvent::getHeapSize(m_info); 137 } else { 138 DEFINE_STATIC_LOCAL(HeapSizeCache, heapSizeCache, ()); 139 heapSizeCache.getCachedHeapSize(m_info); 140 } 141 } 142 143 } // namespace blink 144