/* * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "config.h" #include "WebMemorySampler.h" #if ENABLE(MEMORY_SAMPLER) #include <wtf/text/CString.h> using namespace WebCore; namespace WebKit { WebMemorySampler* WebMemorySampler::shared() { static WebMemorySampler* sharedMemorySampler; if (!sharedMemorySampler) sharedMemorySampler = new WebMemorySampler(); return sharedMemorySampler; } WebMemorySampler::WebMemorySampler() : m_separator(String("\t")) , m_sampleTimer(this, &WebMemorySampler::sampleTimerFired) , m_stopTimer(this, &WebMemorySampler::stopTimerFired) , m_isRunning(false) , m_runningTime(0) { } void WebMemorySampler::start(const double interval) { if (m_isRunning) return; initializeTempLogFile(); initializeTimers(interval); } void WebMemorySampler::start(const SandboxExtension::Handle& sampleLogFileHandle, const String& sampleLogFilePath, const double interval) { if (m_isRunning) return; // If we are on a system without SandboxExtension the handle and filename will be empty if (sampleLogFilePath.isEmpty()) { start(interval); return; } initializeSandboxedLogFile(sampleLogFileHandle, sampleLogFilePath); initializeTimers(interval); } void WebMemorySampler::initializeTimers(double interval) { m_sampleTimer.startRepeating(1); printf("Started memory sampler for process %s", processName().utf8().data()); if (interval > 0) { m_stopTimer.startOneShot(interval); printf(" for a interval of %g seconds", interval); } printf("; Sampler log file stored at: %s\n", m_sampleLogFilePath.utf8().data()); m_runningTime = interval; m_isRunning = true; } void WebMemorySampler::stop() { if (!m_isRunning) return; m_sampleTimer.stop(); m_sampleLogFile = 0; printf("Stopped memory sampler for process %s\n", processName().utf8().data()); // Flush stdout buffer so python script can be guaranteed to read up to this point. fflush(stdout); m_isRunning = false; if(m_stopTimer.isActive()) m_stopTimer.stop(); if (m_sampleLogSandboxExtension) { m_sampleLogSandboxExtension->invalidate(); m_sampleLogSandboxExtension = 0; } } bool WebMemorySampler::isRunning() const { return m_isRunning; } void WebMemorySampler::initializeTempLogFile() { m_sampleLogFilePath = openTemporaryFile(processName(), m_sampleLogFile); writeHeaders(); } void WebMemorySampler::initializeSandboxedLogFile(const SandboxExtension::Handle& sampleLogSandboxHandle, const String& sampleLogFilePath) { m_sampleLogSandboxExtension = SandboxExtension::create(sampleLogSandboxHandle); if (m_sampleLogSandboxExtension) m_sampleLogSandboxExtension->consume(); m_sampleLogFilePath = sampleLogFilePath; m_sampleLogFile = openFile(m_sampleLogFilePath, OpenForWrite); writeHeaders(); } void WebMemorySampler::writeHeaders() { String processDetails = String("Process: "); processDetails.append(processName()); processDetails.append(String("\n")); writeToFile(m_sampleLogFile, processDetails.utf8().data(), processDetails.utf8().length()); String header; WebMemoryStatistics stats = sampleWebKit(); if (!stats.keys.isEmpty()) { for (size_t i = 0; i < stats.keys.size(); ++i) { header.append(m_separator); header.append(stats.keys[i].utf8().data()); } } header.append(String("\n")); writeToFile(m_sampleLogFile, header.utf8().data(), header.utf8().length()); } void WebMemorySampler::sampleTimerFired(Timer<WebMemorySampler>*) { appendCurrentMemoryUsageToFile(m_sampleLogFile); } void WebMemorySampler::stopTimerFired(Timer<WebMemorySampler>*) { if (!m_isRunning) return; printf("%g seconds elapsed. Stopping memory sampler...\n", m_runningTime); stop(); } void WebMemorySampler::appendCurrentMemoryUsageToFile(PlatformFileHandle& file) { // Collect statistics from allocators and get RSIZE metric String statString; WebMemoryStatistics memoryStats = sampleWebKit(); if (!memoryStats.values.isEmpty()) { statString.append(m_separator); for (size_t i = 0; i < memoryStats.values.size(); ++i) { statString.append(m_separator); statString.append(String::format("%lu", memoryStats.values[i])); } } statString.append(String("\n")); writeToFile(m_sampleLogFile, statString.utf8().data(), statString.utf8().length()); } } #endif