InspectorProfilerAgent.cpp 13.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/*
 * Copyright (C) 2010 Apple Inc. All rights reserved.
 * Copyright (C) 2010 Google 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.
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE 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 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 "InspectorProfilerAgent.h"

33
#if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)
34 35

#include "Console.h"
36
#include "InspectorConsoleAgent.h"
37
#include "InspectorFrontend.h"
38
#include "InspectorState.h"
39
#include "InspectorValues.h"
40
#include "InstrumentingAgents.h"
41 42
#include "KURL.h"
#include "Page.h"
43
#include "PageScriptDebugServer.h"
44
#include "ScriptHeapSnapshot.h"
45 46 47
#include "ScriptProfile.h"
#include "ScriptProfiler.h"
#include <wtf/OwnPtr.h>
48
#include <wtf/text/StringConcatenate.h>
49 50 51 52 53 54 55

#if USE(JSC)
#include "JSDOMWindow.h"
#endif

namespace WebCore {

56 57 58 59 60
namespace ProfilerAgentState {
static const char userInitiatedProfiling[] = "userInitiatedProfiling";
static const char profilerEnabled[] = "profilerEnabled";
}

61 62
static const char* const UserInitiatedProfileName = "org.webkit.profiles.user-initiated";
static const char* const CPUProfileType = "CPU";
63
static const char* const HeapProfileType = "HEAP";
64

65
PassOwnPtr<InspectorProfilerAgent> InspectorProfilerAgent::create(InstrumentingAgents* instrumentingAgents, InspectorConsoleAgent* consoleAgent, Page* inspectedPage, InspectorState* inspectorState)
66
{
67
    return adoptPtr(new InspectorProfilerAgent(instrumentingAgents, consoleAgent, inspectedPage, inspectorState));
68 69
}

70
InspectorProfilerAgent::InspectorProfilerAgent(InstrumentingAgents* instrumentingAgents, InspectorConsoleAgent* consoleAgent, Page* inspectedPage, InspectorState* inspectorState)
71 72 73
    : m_instrumentingAgents(instrumentingAgents)
    , m_consoleAgent(consoleAgent)
    , m_inspectedPage(inspectedPage)
74
    , m_inspectorState(inspectorState)
75
    , m_frontend(0)
76
    , m_enabled(false)
77 78 79
    , m_recordingUserInitiatedProfile(false)
    , m_currentUserInitiatedProfileNumber(-1)
    , m_nextUserInitiatedProfileNumber(1)
80
    , m_nextUserInitiatedHeapSnapshotNumber(1)
81
{
82
    m_instrumentingAgents->setInspectorProfilerAgent(this);
83 84 85 86
}

InspectorProfilerAgent::~InspectorProfilerAgent()
{
87
    m_instrumentingAgents->setInspectorProfilerAgent(0);
88 89 90 91 92 93
}

void InspectorProfilerAgent::addProfile(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
{
    RefPtr<ScriptProfile> profile = prpProfile;
    m_profiles.add(profile->uid(), profile);
94 95
    if (m_frontend)
        m_frontend->addProfileHeader(createProfileHeader(*profile));
96 97 98 99 100
    addProfileFinishedMessageToConsole(profile, lineNumber, sourceURL);
}

void InspectorProfilerAgent::addProfileFinishedMessageToConsole(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
{
101 102
    if (!m_frontend)
        return;
103 104
    RefPtr<ScriptProfile> profile = prpProfile;
    String title = profile->title();
105
    String message = makeString("Profile \"webkit-profile://", CPUProfileType, '/', encodeWithURLEscapeSequences(title), '#', String::number(profile->uid()), "\" finished.");
106
    m_consoleAgent->addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
107 108 109 110
}

void InspectorProfilerAgent::addStartProfilingMessageToConsole(const String& title, unsigned lineNumber, const String& sourceURL)
{
111 112
    if (!m_frontend)
        return;
113
    String message = makeString("Profile \"webkit-profile://", CPUProfileType, '/', encodeWithURLEscapeSequences(title), "#0\" started.");
114
    m_consoleAgent->addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
115 116
}

117 118 119 120 121
void InspectorProfilerAgent::collectGarbage(WebCore::ErrorString*)
{
    ScriptProfiler::collectGarbage();
}

122 123 124 125 126 127 128 129 130
PassRefPtr<InspectorObject> InspectorProfilerAgent::createProfileHeader(const ScriptProfile& profile)
{
    RefPtr<InspectorObject> header = InspectorObject::create();
    header->setString("title", profile.title());
    header->setNumber("uid", profile.uid());
    header->setString("typeId", String(CPUProfileType));
    return header;
}

131 132 133 134 135 136 137 138 139
PassRefPtr<InspectorObject> InspectorProfilerAgent::createSnapshotHeader(const ScriptHeapSnapshot& snapshot)
{
    RefPtr<InspectorObject> header = InspectorObject::create();
    header->setString("title", snapshot.title());
    header->setNumber("uid", snapshot.uid());
    header->setString("typeId", String(HeapProfileType));
    return header;
}

140 141 142 143 144 145 146 147 148 149 150 151 152 153
void InspectorProfilerAgent::enable(ErrorString*)
{
    if (enabled())
        return;
    m_inspectorState->setBoolean(ProfilerAgentState::profilerEnabled, true);
    enable(false);
}

void InspectorProfilerAgent::disable(ErrorString*)
{
    m_inspectorState->setBoolean(ProfilerAgentState::profilerEnabled, false);
    disable();
}

154 155 156 157 158
void InspectorProfilerAgent::disable()
{
    if (!m_enabled)
        return;
    m_enabled = false;
159
    PageScriptDebugServer::shared().recompileAllJSFunctionsSoon();
160 161
    if (m_frontend)
        m_frontend->profilerWasDisabled();
162 163 164 165 166 167 168 169
}

void InspectorProfilerAgent::enable(bool skipRecompile)
{
    if (m_enabled)
        return;
    m_enabled = true;
    if (!skipRecompile)
170
        PageScriptDebugServer::shared().recompileAllJSFunctionsSoon();
171 172
    if (m_frontend)
        m_frontend->profilerWasEnabled();
173 174 175 176 177 178 179
}

String InspectorProfilerAgent::getCurrentUserInitiatedProfileName(bool incrementProfileNumber)
{
    if (incrementProfileNumber)
        m_currentUserInitiatedProfileNumber = m_nextUserInitiatedProfileNumber++;

180
    return makeString(UserInitiatedProfileName, '.', String::number(m_currentUserInitiatedProfileNumber));
181 182
}

183
void InspectorProfilerAgent::getProfileHeaders(ErrorString*, RefPtr<InspectorArray>* headers)
184 185 186 187
{
    ProfilesMap::iterator profilesEnd = m_profiles.end();
    for (ProfilesMap::iterator it = m_profiles.begin(); it != profilesEnd; ++it)
        (*headers)->pushObject(createProfileHeader(*it->second));
188 189 190 191 192
    HeapSnapshotsMap::iterator snapshotsEnd = m_snapshots.end();
    for (HeapSnapshotsMap::iterator it = m_snapshots.begin(); it != snapshotsEnd; ++it)
        (*headers)->pushObject(createSnapshotHeader(*it->second));
}

193 194 195 196
namespace {

class OutputStream : public ScriptHeapSnapshot::OutputStream {
public:
197
    OutputStream(InspectorFrontend::Profiler* frontend, unsigned uid)
198 199 200 201
        : m_frontend(frontend), m_uid(uid) { }
    void Write(const String& chunk) { m_frontend->addHeapSnapshotChunk(m_uid, chunk); }
    void Close() { m_frontend->finishHeapSnapshot(m_uid); }
private:
202
    InspectorFrontend::Profiler* m_frontend;
203
    int m_uid;
204 205 206 207
};

} // namespace

208
void InspectorProfilerAgent::getProfile(ErrorString*, const String& type, unsigned uid, RefPtr<InspectorObject>* profileObject)
209 210 211 212 213 214
{
    if (type == CPUProfileType) {
        ProfilesMap::iterator it = m_profiles.find(uid);
        if (it != m_profiles.end()) {
            *profileObject = createProfileHeader(*it->second);
            (*profileObject)->setObject("head", it->second->buildInspectorObjectForHead());
215 216
            if (it->second->bottomUpHead())
                (*profileObject)->setObject("bottomUpHead", it->second->buildInspectorObjectForBottomUpHead());
217 218 219 220
        }
    } else if (type == HeapProfileType) {
        HeapSnapshotsMap::iterator it = m_snapshots.find(uid);
        if (it != m_snapshots.end()) {
221 222 223 224 225 226
            RefPtr<ScriptHeapSnapshot> snapshot = it->second;
            *profileObject = createSnapshotHeader(*snapshot);
            if (m_frontend) {
                OutputStream stream(m_frontend, uid);
                snapshot->writeJSON(&stream);
            }
227
        }
228 229 230
    }
}

231
void InspectorProfilerAgent::removeProfile(ErrorString*, const String& type, unsigned uid)
232
{
233 234 235 236 237 238 239
    if (type == CPUProfileType) {
        if (m_profiles.contains(uid))
            m_profiles.remove(uid);
    } else if (type == HeapProfileType) {
        if (m_snapshots.contains(uid))
            m_snapshots.remove(uid);
    }
240 241 242 243
}

void InspectorProfilerAgent::resetState()
{
244
    stopUserInitiatedProfiling();
245
    m_profiles.clear();
246
    m_snapshots.clear();
247 248
    m_currentUserInitiatedProfileNumber = 1;
    m_nextUserInitiatedProfileNumber = 1;
249
    m_nextUserInitiatedHeapSnapshotNumber = 1;
250 251 252 253 254
    resetFrontendProfiles();
}

void InspectorProfilerAgent::resetFrontendProfiles()
{
255 256 257
    if (m_frontend
        && m_profiles.begin() == m_profiles.end()
        && m_snapshots.begin() == m_snapshots.end())
258
        m_frontend->resetProfiles();
259 260
}

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
void InspectorProfilerAgent::setFrontend(InspectorFrontend* frontend)
{
    m_frontend = frontend->profiler();
    restoreEnablement();
}

void InspectorProfilerAgent::clearFrontend()
{
    m_frontend = 0;
    stopUserInitiatedProfiling();
}

void InspectorProfilerAgent::restore()
{
    // Need to restore enablement state here as in setFrontend m_inspectorState wasn't loaded yet.
    restoreEnablement();

    // Revisit this.
    resetFrontendProfiles();
    if (m_inspectorState->getBoolean(ProfilerAgentState::userInitiatedProfiling))
        startUserInitiatedProfiling();
}

void InspectorProfilerAgent::restoreEnablement()
{
    if (m_inspectorState->getBoolean(ProfilerAgentState::profilerEnabled)) {
        ErrorString error;
        enable(&error);
    }
}

292 293
void InspectorProfilerAgent::startUserInitiatedProfiling()
{
294 295
    if (m_recordingUserInitiatedProfile)
        return;
296
    if (!enabled()) {
297
        enable(true);
298
        PageScriptDebugServer::shared().recompileAllJSFunctions(0);
299 300 301 302
    }
    m_recordingUserInitiatedProfile = true;
    String title = getCurrentUserInitiatedProfileName(true);
#if USE(JSC)
303
    JSC::ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec();
304 305 306 307 308 309
#else
    ScriptState* scriptState = 0;
#endif
    ScriptProfiler::start(scriptState, title);
    addStartProfilingMessageToConsole(title, 0, String());
    toggleRecordButton(true);
310
    m_inspectorState->setBoolean(ProfilerAgentState::userInitiatedProfiling, true);
311 312
}

313
void InspectorProfilerAgent::stopUserInitiatedProfiling(bool ignoreProfile)
314
{
315 316
    if (!m_recordingUserInitiatedProfile)
        return;
317 318 319
    m_recordingUserInitiatedProfile = false;
    String title = getCurrentUserInitiatedProfileName();
#if USE(JSC)
320
    JSC::ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec();
321 322 323 324 325 326
#else
    // Use null script state to avoid filtering by context security token.
    // All functions from all iframes should be visible from Inspector UI.
    ScriptState* scriptState = 0;
#endif
    RefPtr<ScriptProfile> profile = ScriptProfiler::stop(scriptState, title);
327 328 329 330 331 332
    if (profile) {
        if (!ignoreProfile)
            addProfile(profile, 0, String());
        else
            addProfileFinishedMessageToConsole(profile, 0, String());
    }
333
    toggleRecordButton(false);
334
    m_inspectorState->setBoolean(ProfilerAgentState::userInitiatedProfiling, false);
335 336
}

337 338 339 340
namespace {

class HeapSnapshotProgress: public ScriptProfiler::HeapSnapshotProgress {
public:
341
    explicit HeapSnapshotProgress(InspectorFrontend::Profiler* frontend)
342 343 344 345 346 347 348 349 350 351 352 353 354
        : m_frontend(frontend) { }
    void Start(int totalWork)
    {
        m_totalWork = totalWork;
    }
    void Worked(int workDone)
    {
        if (m_frontend)
            m_frontend->reportHeapSnapshotProgress(workDone, m_totalWork);
    }
    void Done() { }
    bool isCanceled() { return false; }
private:
355
    InspectorFrontend::Profiler* m_frontend;
356 357 358 359 360
    int m_totalWork;
};

};

361
void InspectorProfilerAgent::takeHeapSnapshot(ErrorString*)
362
{
363 364 365
    String title = makeString(UserInitiatedProfileName, '.', String::number(m_nextUserInitiatedHeapSnapshotNumber));
    ++m_nextUserInitiatedHeapSnapshotNumber;

366
    HeapSnapshotProgress progress(m_frontend);
367
    RefPtr<ScriptHeapSnapshot> snapshot = ScriptProfiler::takeHeapSnapshot(title, &progress);
368 369 370 371 372 373 374
    if (snapshot) {
        m_snapshots.add(snapshot->uid(), snapshot);
        if (m_frontend)
            m_frontend->addProfileHeader(createSnapshotHeader(*snapshot));
    }
}

375 376
void InspectorProfilerAgent::toggleRecordButton(bool isProfiling)
{
377 378
    if (m_frontend)
        m_frontend->setRecordingProfile(isProfiling);
379 380 381 382
}

} // namespace WebCore

383
#endif // ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)