/* * Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "AboutData.h" #include "AboutTemplate.html.cpp" #include "CacheHelper.h" #include "CookieManager.h" #include "JSDOMWindow.h" #include "MemoryCache.h" #include "MemoryStatistics.h" #include "SurfacePool.h" #include "WebKitVersion.h" #include #include #include #include #include #include #include #include #include #include using namespace WebCore; namespace BlackBerry { namespace WebKit { static String writeFeatures(const Vector& trueList, const Vector& falseList) { String ret; for (unsigned int i = 0, j = 0; i < trueList.size() || j < falseList.size();) { bool pickFromFalse = ((i >= trueList.size()) || (j < falseList.size() && strcmp(falseList[j].ascii().data(), trueList[i].ascii().data()) < 0)); const String& item = (pickFromFalse ? falseList : trueList)[ (pickFromFalse ? j : i)++ ]; ret += String("
= 30 ? " style='font-size:10px;' " : "") + ">" + item + "
"); } return ret; } template static String numberToHTMLTr(const String& description, T number) { return String("") + description + "" + String::number(number) + ""; } template<> String numberToHTMLTr(const String& description, bool truth) { return String("") + description + "" + (truth?"true":"false") + ""; } static String configPage() { StringBuilder builder; #if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD builder.append(writeHeader("Configuration")); builder.appendLiteral("
Compiler Information
"); #if COMPILER(MSVC) builder.appendLiteral(""); builder.append(""); builder.append(""); builder.append(""); #elif COMPILER(RVCT) builder.appendLiteral(""); builder.append(""); #if COMPILER(RVCT4GNU) builder.appendLiteral(""); #endif #elif COMPILER(GCC) builder.append(""); #endif // Add "" to satisfy check-webkit-style. builder.appendLiteral(""); builder.append(String("
Microsoft Visual C++MSVC
_MSC_VER" + String::number(_MSC_VER) + "
_MSC_FULL_VER" + String::number(_MSC_FULL_VER) + "
_MSC_BUILD" + String::number(_MSC_BUILD) + "
ARM RealView Compiler ToolkitRVCT
__ARMCC_VERSION" + String::number(__ARMCC_VERSION) + "
RVCT 4+ in --gnu mode1
GCC" + String::number(__GNUC__) + "." + String::number(__GNUC_MINOR__) + "." + String::number(__GNUC_PATCHLEVEL__) + "

CPU Information
")); #if CPU(X86) builder.appendLiteral(""); #elif CPU(ARM) builder.appendLiteral(""); builder.append(""); builder.append(""); builder.append(""); #endif builder.appendLiteral(""); builder.append(String("
X86
ARM
ARM_ARCH_VERSION" + String::number(WTF_ARM_ARCH_VERSION) + "
THUMB_ARCH_VERSION" + String::number(WTF_THUMB_ARCH_VERSION) + "
THUMB2" + String::number(WTF_CPU_ARM_THUMB2) + "
Endianness"); #if CPU(BIG_ENDIAN) builder.appendLiteral("big"); #elif CPU(MIDDLE_ENDIAN) builder.appendLiteral("middle"); #else builder.appendLiteral("little"); #endif builder.appendLiteral("

Platform Information
")); builder.append(""); builder.appendLiteral(""); builder.appendLiteral(""); BlackBerry::Platform::Settings* settings = BlackBerry::Platform::Settings::instance(); builder.append(String("
WebKit Version" + String::number(WEBKIT_MAJOR_VERSION) + "." + String::number(WEBKIT_MINOR_VERSION) + "
BlackBerry"); #if PLATFORM(BLACKBERRY) builder.appendLiteral("1"); #else builder.appendLiteral("0"); #endif builder.appendLiteral("
__STDC_ISO_10646__"); #ifdef __STDC_ISO_10646__ builder.appendLiteral("1"); #else builder.appendLiteral("0"); #endif builder.appendLiteral("

Platform Settings
")); builder.append(numberToHTMLTr("isRSSFilteringEnabled", settings->isRSSFilteringEnabled())); builder.append(numberToHTMLTr("maxPixelsPerDecodedImage", settings->maxPixelsPerDecodedImage())); builder.append(numberToHTMLTr("shouldReportLowMemoryToUser", settings->shouldReportLowMemoryToUser())); builder.append(numberToHTMLTr("numberOfBackingStoreFrontBuffers", settings->numberOfBackingStoreFrontBuffers())); builder.append(numberToHTMLTr("numberOfBackingStoreBackBuffers", settings->numberOfBackingStoreBackBuffers())); builder.append(numberToHTMLTr("tabsSupportedByClient", settings->tabsSupportedByClient())); builder.append(numberToHTMLTr("contextMenuEnabled", settings->contextMenuEnabled())); builder.append(numberToHTMLTr("selectionEnabled", settings->selectionEnabled())); builder.append(numberToHTMLTr("fineCursorControlEnabled", settings->fineCursorControlEnabled())); builder.append(numberToHTMLTr("alwaysShowKeyboardOnFocus", settings->alwaysShowKeyboardOnFocus())); builder.append(numberToHTMLTr("allowedScrollAdjustmentForInputFields", settings->allowedScrollAdjustmentForInputFields())); builder.append(numberToHTMLTr("unrestrictedResizeEvents", settings->unrestrictedResizeEvents())); builder.append(numberToHTMLTr("isBridgeBrowser", settings->isBridgeBrowser())); builder.append(numberToHTMLTr("showImageLocationOptionsInGCM", settings->showImageLocationOptionsInGCM())); builder.append(numberToHTMLTr("forceGLES2WindowUsage", settings->forceGLES2WindowUsage())); builder.append(numberToHTMLTr("maxClickableSpeed", settings->maxClickableSpeed())); builder.append(numberToHTMLTr("maxJitterRadiusClick", settings->maxJitterRadiusClick())); builder.append(numberToHTMLTr("maxJitterRadiusTap", settings->maxJitterRadiusTap())); builder.append(numberToHTMLTr("maxJitterRadiusSingleTouchMove", settings->maxJitterRadiusSingleTouchMove())); builder.append(numberToHTMLTr("maxJitterRadiusTouchHold", settings->maxJitterRadiusTouchHold())); builder.append(numberToHTMLTr("maxJitterRadiusHandleDrag", settings->maxJitterRadiusHandleDrag())); builder.append(numberToHTMLTr("maxJitterDistanceClick", settings->maxJitterDistanceClick())); builder.append(numberToHTMLTr("maxJitterDistanceTap", settings->maxJitterDistanceTap())); builder.append(numberToHTMLTr("maxJitterDistanceSingleTouchMove", settings->maxJitterDistanceSingleTouchMove())); builder.append(numberToHTMLTr("maxJitterDistanceTouchHold", settings->maxJitterDistanceTouchHold())); builder.append(numberToHTMLTr("maxJitterDistanceHandleDrag", settings->maxJitterDistanceHandleDrag())); builder.append(numberToHTMLTr("topFatFingerPadding", settings->topFatFingerPadding())); builder.append(numberToHTMLTr("rightFatFingerPadding", settings->rightFatFingerPadding())); builder.append(numberToHTMLTr("bottomFatFingerPadding", settings->bottomFatFingerPadding())); builder.append(numberToHTMLTr("maxSelectionNeckHeight", settings->maxSelectionNeckHeight())); builder.append(numberToHTMLTr("leftFatFingerPadding", settings->leftFatFingerPadding())); Vector trueList, falseList; #include "AboutDataEnableFeatures.cpp" builder.append(String("

WebKit Features (ENABLE_)
")); builder.append(writeFeatures(trueList, falseList)); trueList.clear(); falseList.clear(); #include "AboutDataHaveFeatures.cpp" builder.append(String("

WebKit Features (HAVE_)
")); builder.append(writeFeatures(trueList, falseList)); trueList.clear(); falseList.clear(); #include "AboutDataUseFeatures.cpp" builder.append(String("

WebKit Features (USE_)
")); builder.append(writeFeatures(trueList, falseList)); builder.append(String("
")); #endif return builder.toString(); } static String cacheTypeStatisticToHTMLTr(const String& description, const MemoryCache::TypeStatistic& statistic) { const int s_kiloByte = 1024; return String("") + description + "" + "" + String::number(statistic.count) + "" + "" + String::number(statistic.size / s_kiloByte) + "" + "" + String::number(statistic.liveSize / s_kiloByte) + "" + "" + String::number(statistic.decodedSize / s_kiloByte) + "" + ""; } static void dumpJSCTypeCountSetToTableHTML(StringBuilder& tableHTML, JSC::TypeCountSet* typeCountSet) { if (!typeCountSet) return; for (JSC::TypeCountSet::const_iterator iter = typeCountSet->begin(); iter != typeCountSet->end(); ++iter) tableHTML.append(numberToHTMLTr(iter->key, iter->value)); } #if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD static inline struct malloc_stats mallocStats() { struct malloc_stats ms; mallopt(MALLOC_STATS, reinterpret_cast(&ms)); return ms; } #endif static String memoryPage() { StringBuilder builder; builder.append(writeHeader("Memory")); builder.appendLiteral("
Cache Information
Size, Living, and Decoded are expressed in KB.

"); // generate cache information MemoryCache* cacheInc = memoryCache(); MemoryCache::Statistics cacheStat = cacheInc->getStatistics(); builder.appendLiteral(""); MemoryCache::TypeStatistic total; total.count = cacheStat.images.count + cacheStat.cssStyleSheets.count + cacheStat.scripts.count + cacheStat.xslStyleSheets.count + cacheStat.fonts.count; total.size = cacheInc->totalSize(); total.liveSize = cacheStat.images.liveSize + cacheStat.cssStyleSheets.liveSize + cacheStat.scripts.liveSize + cacheStat.xslStyleSheets.liveSize + cacheStat.fonts.liveSize; total.decodedSize = cacheStat.images.decodedSize + cacheStat.cssStyleSheets.decodedSize + cacheStat.scripts.decodedSize + cacheStat.xslStyleSheets.decodedSize + cacheStat.fonts.decodedSize; builder.append(cacheTypeStatisticToHTMLTr("Total", total)); builder.append(cacheTypeStatisticToHTMLTr("Images", cacheStat.images)); builder.append(cacheTypeStatisticToHTMLTr("CSS Style Sheets", cacheStat.cssStyleSheets)); builder.append(cacheTypeStatisticToHTMLTr("Scripts", cacheStat.scripts)); #if ENABLE(XSLT) builder.append(cacheTypeStatisticToHTMLTr("XSL Style Sheets", cacheStat.xslStyleSheets)); #endif builder.append(cacheTypeStatisticToHTMLTr("Fonts", cacheStat.fonts)); builder.appendLiteral("
Item Count Size Living Decoded

"); #if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD // JS engine memory usage. JSC::GlobalMemoryStatistics jscMemoryStat = JSC::globalMemoryStatistics(); JSC::Heap& mainHeap = JSDOMWindow::commonVM()->heap; OwnPtr objectTypeCounts = mainHeap.objectTypeCounts(); OwnPtr protectedObjectTypeCounts = mainHeap.protectedObjectTypeCounts(); // Malloc info. struct malloc_stats mallocInfo = mallocStats(); builder.appendLiteral("
Process memory usage summary
"); builder.append(numberToHTMLTr("Total used memory (malloc + JSC)", mallocInfo.m_small_allocmem + mallocInfo.m_allocmem + jscMemoryStat.stackBytes + jscMemoryStat.JITBytes + mainHeap.capacity())); if (unsigned totalCommittedMemoryOfChromeProcess = BlackBerry::Platform::totalCommittedMemoryOfChromeProcess()) { builder.append(numberToHTMLTr("Total committed memory of tab process", BlackBerry::Platform::totalCommittedMemoryOfCurrentProcess())); builder.append(numberToHTMLTr("Total committed memory of chrome process", totalCommittedMemoryOfChromeProcess)); } else builder.append(numberToHTMLTr("Total committed memory", BlackBerry::Platform::totalCommittedMemoryOfCurrentProcess())); struct stat processInfo; if (!stat(String::format("/proc/%u/as", getpid()).latin1().data(), &processInfo)) builder.append(numberToHTMLTr("Total mapped memory", processInfo.st_size)); builder.append(numberToHTMLTr("System free memory", BlackBerry::Platform::systemFreeMemory())); builder.appendLiteral("

"); builder.appendLiteral("
JS engine memory usage
"); builder.append(numberToHTMLTr("Stack size", jscMemoryStat.stackBytes)); builder.append(numberToHTMLTr("JIT memory usage", jscMemoryStat.JITBytes)); builder.append(numberToHTMLTr("Main heap capacity", mainHeap.capacity())); builder.append(numberToHTMLTr("Main heap size", mainHeap.size())); builder.append(numberToHTMLTr("Object count", mainHeap.objectCount())); builder.append(numberToHTMLTr("Global object count", mainHeap.globalObjectCount())); builder.append(numberToHTMLTr("Protected object count", mainHeap.protectedObjectCount())); builder.append(numberToHTMLTr("Protected global object count", mainHeap.protectedGlobalObjectCount())); builder.appendLiteral("

"); builder.appendLiteral("
JS object type counts
"); dumpJSCTypeCountSetToTableHTML(builder, objectTypeCounts.get()); builder.appendLiteral("

"); builder.appendLiteral("
JS protected object type counts
"); dumpJSCTypeCountSetToTableHTML(builder, protectedObjectTypeCounts.get()); builder.appendLiteral("

"); builder.appendLiteral("
Malloc Information
"); builder.append(numberToHTMLTr("Total space in use", mallocInfo.m_small_allocmem + mallocInfo.m_allocmem)); builder.append(numberToHTMLTr("Total space in free blocks", mallocInfo.m_small_freemem + mallocInfo.m_freemem)); builder.append(numberToHTMLTr("Memory in free small blocks", mallocInfo.m_small_freemem)); builder.append(numberToHTMLTr("Memory in free big blocks", mallocInfo.m_freemem)); builder.append(numberToHTMLTr("Space in header block headers", mallocInfo.m_small_overhead)); builder.append(numberToHTMLTr("Space used by block headers", mallocInfo.m_overhead)); builder.append(numberToHTMLTr("Space in small blocks in use", mallocInfo.m_small_allocmem)); builder.append(numberToHTMLTr("Space in big blocks in use", mallocInfo.m_allocmem)); builder.append(numberToHTMLTr("Number of core allocations", mallocInfo.m_coreallocs)); builder.append(numberToHTMLTr("Number of core de-allocations performed", mallocInfo.m_corefrees)); builder.append(numberToHTMLTr("Size of the arena", mallocInfo.m_heapsize)); builder.append(numberToHTMLTr("Number of frees performed", mallocInfo.m_frees)); builder.append(numberToHTMLTr("Number of allocations performed", mallocInfo.m_allocs)); builder.append(numberToHTMLTr("Number of realloc functions performed", mallocInfo.m_reallocs)); builder.append(numberToHTMLTr("Number of small blocks", mallocInfo.m_small_blocks)); builder.append(numberToHTMLTr("Number of big blocks", mallocInfo.m_blocks)); builder.append(numberToHTMLTr("Number of header blocks", mallocInfo.m_hblocks)); builder.append("
"); #endif builder.append(""); return builder.toString(); } #if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD class MemoryTracker { public: static MemoryTracker& instance(); void start(); void stop(); bool isActive() const { return m_memoryTrackingTimer.isActive(); } void clear() { m_peakTotalUsedMemory = 0; m_peakTotalCommittedMemoryOfCurrentProcess = 0; m_peakTotalCommittedMemoryOfChromeProcess = 0; m_peakTotalMappedMemory = 0; } void updateMemoryPeaks(Timer*); unsigned peakTotalUsedMemory() const { return m_peakTotalUsedMemory; } unsigned peakTotalCommittedMemoryOfCurrentProcess() const { return m_peakTotalCommittedMemoryOfCurrentProcess; } unsigned peakTotalCommittedMemoryOfChromeProcess() const { return m_peakTotalCommittedMemoryOfChromeProcess; } unsigned peakTotalMappedMemory() const { return m_peakTotalMappedMemory; } private: MemoryTracker(); Timer m_memoryTrackingTimer; unsigned m_peakTotalUsedMemory; unsigned m_peakTotalCommittedMemoryOfCurrentProcess; unsigned m_peakTotalCommittedMemoryOfChromeProcess; unsigned m_peakTotalMappedMemory; }; MemoryTracker::MemoryTracker() : m_memoryTrackingTimer(this, &MemoryTracker::updateMemoryPeaks) , m_peakTotalUsedMemory(0) , m_peakTotalCommittedMemoryOfCurrentProcess(0) , m_peakTotalCommittedMemoryOfChromeProcess(0) , m_peakTotalMappedMemory(0) { } MemoryTracker& MemoryTracker::instance() { DEFINE_STATIC_LOCAL(MemoryTracker, s_memoryTracker, ()); return s_memoryTracker; } void MemoryTracker::start() { clear(); if (!m_memoryTrackingTimer.isActive()) m_memoryTrackingTimer.start(0, 0.01); } void MemoryTracker::stop() { m_memoryTrackingTimer.stop(); } void MemoryTracker::updateMemoryPeaks(Timer*) { // JS engine memory usage. JSC::GlobalMemoryStatistics jscMemoryStat = JSC::globalMemoryStatistics(); JSC::Heap& mainHeap = JSDOMWindow::commonVM()->heap; // Malloc info. struct malloc_stats mallocInfo = mallocStats(); // Malloc and JSC memory. unsigned totalUsedMemory = static_cast(mallocInfo.m_small_allocmem + mallocInfo.m_allocmem + jscMemoryStat.stackBytes + jscMemoryStat.JITBytes + mainHeap.capacity()); if (totalUsedMemory > m_peakTotalUsedMemory) m_peakTotalUsedMemory = totalUsedMemory; unsigned totalCommittedMemoryOfCurrentProcess = BlackBerry::Platform::totalCommittedMemoryOfCurrentProcess(); if (totalCommittedMemoryOfCurrentProcess > m_peakTotalCommittedMemoryOfCurrentProcess) m_peakTotalCommittedMemoryOfCurrentProcess = totalCommittedMemoryOfCurrentProcess; unsigned totalCommittedMemoryOfChromeProcess = BlackBerry::Platform::totalCommittedMemoryOfChromeProcess(); if (totalCommittedMemoryOfChromeProcess > m_peakTotalCommittedMemoryOfChromeProcess) m_peakTotalCommittedMemoryOfChromeProcess = totalCommittedMemoryOfChromeProcess; struct stat processInfo; if (!stat(String::format("/proc/%u/as", getpid()).latin1().data(), &processInfo)) { unsigned totalMappedMemory = static_cast(processInfo.st_size); if (totalMappedMemory > m_peakTotalMappedMemory) m_peakTotalMappedMemory = totalMappedMemory; } } static String memoryPeaksToHtmlTable(MemoryTracker& memoryTracker) { String htmlTable = String("") + numberToHTMLTr("Total used memory(malloc + JSC):", memoryTracker.peakTotalUsedMemory()); if (unsigned peakTotalCommittedMemoryOfChromeProcess = memoryTracker.peakTotalCommittedMemoryOfChromeProcess()) { htmlTable += numberToHTMLTr("Total committed memory of tab process:", memoryTracker.peakTotalCommittedMemoryOfCurrentProcess()); htmlTable += numberToHTMLTr("Total committed memory of chrome process:", peakTotalCommittedMemoryOfChromeProcess); } else htmlTable += numberToHTMLTr("Total committed memory:", memoryTracker.peakTotalCommittedMemoryOfCurrentProcess()); htmlTable += numberToHTMLTr("Total mapped memory:", memoryTracker.peakTotalMappedMemory()) + "
"; return htmlTable; } static String memoryLivePage(String memoryLiveCommand) { StringBuilder builder; builder.append(writeHeader("Memory Live Page")); builder.appendLiteral("
Memory Peaks
"); builder.appendLiteral("
\"about:memory-live/start\": start tracking memory peaks.
"); builder.appendLiteral("
\"about:memory-live\": show memory peaks every 30ms.
"); builder.appendLiteral("
\"about:memory-live/stop\": stop tracking and show memory peaks.

"); MemoryTracker& memoryTracker = MemoryTracker::instance(); if (memoryLiveCommand.isEmpty()) { if (!memoryTracker.isActive()) builder.appendLiteral("
Memory tracker isn't running, please use \"about:memory-live/start\" to start the tracker.
"); else { builder.append(memoryPeaksToHtmlTable(memoryTracker)); builder.appendLiteral(""); } } else if (equalIgnoringCase(memoryLiveCommand, "/start")) { memoryTracker.start(); builder.appendLiteral("
Memory tracker is running.
"); } else if (equalIgnoringCase(memoryLiveCommand, "/stop")) { if (!memoryTracker.isActive()) builder.appendLiteral("
Memory tracker isn't running.
"); else { memoryTracker.stop(); builder.append(memoryPeaksToHtmlTable(memoryTracker)); builder.appendLiteral("
Memory tracker is stopped.
"); } } else builder.appendLiteral("
Unknown command! Please input a correct command!
"); builder.appendLiteral("

"); return builder.toString(); } #endif static String cachePage(String cacheCommand) { String result; result.append(String("BlackBerry Browser Disk Cache")); if (cacheCommand.isEmpty()) result.append(String(BlackBerry::Platform::generateHtmlFragmentForCacheKeys().data())); else if (cacheCommand.startsWith("?query=", false)) { String key(cacheCommand.substring(7).utf8().data()); // 7 is length of "query=". result.append(key); result.append(String("
")); result.append(String(BlackBerry::Platform::generateHtmlFragmentForCacheHeaders(key).data())); } else { // Unknown cache command. return String(); } result.append(String("")); return result; } static String buildPage() { String result; result.append(writeHeader("Build")); result.append(String("
Basic
")); result.append(String("")); result.append(String("")); result.append(String("
Built On: ")); result.append(String(BlackBerry::Platform::BUILDCOMPUTER)); result.append(String("
Build User: ")); result.append(String(BlackBerry::Platform::BUILDUSER)); result.append(String("
Build Time: ")); result.append(String(BlackBerry::Platform::BUILDTIME)); result.append(String("

")); result.append(String(BlackBerry::Platform::BUILDINFO_WEBKIT)); result.append(String(BlackBerry::Platform::BUILDINFO_PLATFORM)); result.append(String(BlackBerry::Platform::BUILDINFO_LIBWEBVIEW)); result.append(String(BlackBerry::Platform::BUILDINFO_WEBPLATFORM)); result.append(String("")); return result; } static String cookiePage() { String result; result.append(String("BlackBerry Browser cookie information")); result.append(cookieManager().generateHtmlFragmentForCookies()); result.append(String("")); return result; } String aboutData(String aboutWhat) { if (aboutWhat.startsWith("cache")) return cachePage(aboutWhat.substring(5)); if (equalIgnoringCase(aboutWhat, "memory")) return memoryPage(); #if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD if (equalIgnoringCase(aboutWhat, "cookie")) return cookiePage(); if (BlackBerry::Platform::debugSetting() > 0 && equalIgnoringCase(aboutWhat, "build")) return buildPage(); if (BlackBerry::Platform::debugSetting() > 0 && equalIgnoringCase(aboutWhat, "config")) return configPage(); if (aboutWhat.startsWith("memory-live")) return memoryLivePage(aboutWhat.substring(11)); #endif return String(); } } // namespace WebKit } // namespace BlackBerry