2011-09-18 Ilya Tikhonovsky <loislo@chromium.org>
[WebKit-https.git] / Source / WebCore / inspector / InspectorTimelineAgent.cpp
1 /*
2 * Copyright (C) 2009 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 "InspectorTimelineAgent.h"
33
34 #if ENABLE(INSPECTOR)
35
36 #include "Event.h"
37 #include "IdentifiersFactory.h"
38 #include "InspectorFrontend.h"
39 #include "InspectorState.h"
40 #include "InstrumentingAgents.h"
41 #include "IntRect.h"
42 #include "ResourceRequest.h"
43 #include "ResourceResponse.h"
44 #include "TimelineRecordFactory.h"
45
46 #include <wtf/CurrentTime.h>
47
48 namespace WebCore {
49
50 namespace TimelineAgentState {
51 static const char timelineAgentEnabled[] = "timelineAgentEnabled";
52 static const char timelineMaxCallStackDepth[] = "timelineMaxCallStackDepth";
53 }
54
55 // Must be kept in sync with TimelineAgent.js
56 namespace TimelineRecordType {
57 static const char EventDispatch[] = "EventDispatch";
58 static const char Layout[] = "Layout";
59 static const char RecalculateStyles[] = "RecalculateStyles";
60 static const char Paint[] = "Paint";
61 static const char ParseHTML[] = "ParseHTML";
62
63 static const char TimerInstall[] = "TimerInstall";
64 static const char TimerRemove[] = "TimerRemove";
65 static const char TimerFire[] = "TimerFire";
66
67 static const char EvaluateScript[] = "EvaluateScript";
68
69 static const char MarkLoad[] = "MarkLoad";
70 static const char MarkDOMContent[] = "MarkDOMContent";
71
72 static const char TimeStamp[] = "TimeStamp";
73
74 static const char ScheduleResourceRequest[] = "ScheduleResourceRequest";
75 static const char ResourceSendRequest[] = "ResourceSendRequest";
76 static const char ResourceReceiveResponse[] = "ResourceReceiveResponse";
77 static const char ResourceReceivedData[] = "ResourceReceivedData";
78 static const char ResourceFinish[] = "ResourceFinish";
79
80 static const char XHRReadyStateChange[] = "XHRReadyStateChange";
81 static const char XHRLoad[] = "XHRLoad";
82
83 static const char FunctionCall[] = "FunctionCall";
84 static const char GCEvent[] = "GCEvent";
85
86 static const char RegisterAnimationFrameCallback[] = "RegisterAnimationFrameCallback";
87 static const char CancelAnimationFrameCallback[] = "CancelAnimationFrameCallback";
88 static const char FireAnimationFrameEvent[] = "FireAnimationFrameEvent";
89 }
90
91 void InspectorTimelineAgent::pushGCEventRecords()
92 {
93     if (!m_gcEvents.size())
94         return;
95
96     GCEvents events = m_gcEvents;
97     m_gcEvents.clear();
98     for (GCEvents::iterator i = events.begin(); i != events.end(); ++i) {
99         RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(i->startTime, m_maxCallStackDepth);
100         record->setObject("data", TimelineRecordFactory::createGCEventData(i->collectedBytes));
101         record->setNumber("endTime", i->endTime);
102         addRecordToTimeline(record.release(), TimelineRecordType::GCEvent);
103     }
104 }
105
106 void InspectorTimelineAgent::didGC(double startTime, double endTime, size_t collectedBytesCount)
107 {
108     m_gcEvents.append(GCEvent(startTime, endTime, collectedBytesCount));
109 }
110
111 InspectorTimelineAgent::~InspectorTimelineAgent()
112 {
113     clearFrontend();
114 }
115
116 void InspectorTimelineAgent::setFrontend(InspectorFrontend* frontend)
117 {
118     m_frontend = frontend->timeline();
119 }
120
121 void InspectorTimelineAgent::clearFrontend()
122 {
123     ErrorString error;
124     stop(&error);
125     m_frontend = 0;
126 }
127
128 void InspectorTimelineAgent::restore()
129 {
130     if (m_state->getBoolean(TimelineAgentState::timelineAgentEnabled)) {
131         m_maxCallStackDepth = m_state->getLong(TimelineAgentState::timelineMaxCallStackDepth);
132         ErrorString error;
133         start(&error, &m_maxCallStackDepth);
134     }
135 }
136
137 void InspectorTimelineAgent::start(ErrorString*, int* maxCallStackDepth)
138 {
139     if (!m_frontend)
140         return;
141
142     if (maxCallStackDepth && *maxCallStackDepth > 0)
143         m_maxCallStackDepth = *maxCallStackDepth;
144     else
145         m_maxCallStackDepth = 5;
146     m_state->setLong(TimelineAgentState::timelineMaxCallStackDepth, m_maxCallStackDepth);
147
148     m_instrumentingAgents->setInspectorTimelineAgent(this);
149     ScriptGCEvent::addEventListener(this);
150     m_frontend->started();
151     m_state->setBoolean(TimelineAgentState::timelineAgentEnabled, true);
152 }
153
154 void InspectorTimelineAgent::stop(ErrorString*)
155 {
156     if (!started())
157         return;
158     m_instrumentingAgents->setInspectorTimelineAgent(0);
159     if (m_frontend)
160         m_frontend->stopped();
161     ScriptGCEvent::removeEventListener(this);
162
163     clearRecordStack();
164     m_gcEvents.clear();
165
166     m_state->setBoolean(TimelineAgentState::timelineAgentEnabled, false);
167 }
168
169 bool InspectorTimelineAgent::started() const
170 {
171     return m_state->getBoolean(TimelineAgentState::timelineAgentEnabled);
172 }
173
174 void InspectorTimelineAgent::willCallFunction(const String& scriptName, int scriptLine)
175 {
176     pushCurrentRecord(TimelineRecordFactory::createFunctionCallData(scriptName, scriptLine), TimelineRecordType::FunctionCall);
177 }
178
179 void InspectorTimelineAgent::didCallFunction()
180 {
181     didCompleteCurrentRecord(TimelineRecordType::FunctionCall);
182 }
183
184 void InspectorTimelineAgent::willDispatchEvent(const Event& event)
185 {
186     pushCurrentRecord(TimelineRecordFactory::createEventDispatchData(event),
187         TimelineRecordType::EventDispatch);
188 }
189
190 void InspectorTimelineAgent::didDispatchEvent()
191 {
192     didCompleteCurrentRecord(TimelineRecordType::EventDispatch);
193 }
194
195 void InspectorTimelineAgent::willLayout()
196 {
197     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Layout);
198 }
199
200 void InspectorTimelineAgent::didLayout()
201 {
202     didCompleteCurrentRecord(TimelineRecordType::Layout);
203 }
204
205 void InspectorTimelineAgent::willRecalculateStyle()
206 {
207     pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RecalculateStyles);
208 }
209
210 void InspectorTimelineAgent::didRecalculateStyle()
211 {
212     didCompleteCurrentRecord(TimelineRecordType::RecalculateStyles);
213 }
214
215 void InspectorTimelineAgent::willPaint(const LayoutRect& rect)
216 {
217     pushCurrentRecord(TimelineRecordFactory::createPaintData(rect), TimelineRecordType::Paint);
218 }
219
220 void InspectorTimelineAgent::didPaint()
221 {
222     didCompleteCurrentRecord(TimelineRecordType::Paint);
223 }
224
225 void InspectorTimelineAgent::willWriteHTML(unsigned int length, unsigned int startLine)
226 {
227     pushCurrentRecord(TimelineRecordFactory::createParseHTMLData(length, startLine), TimelineRecordType::ParseHTML);
228 }
229
230 void InspectorTimelineAgent::didWriteHTML(unsigned int endLine)
231 {
232     if (!m_recordStack.isEmpty()) {
233         TimelineRecordEntry entry = m_recordStack.last();
234         entry.data->setNumber("endLine", endLine);
235         didCompleteCurrentRecord(TimelineRecordType::ParseHTML);
236     }
237 }
238
239 void InspectorTimelineAgent::didInstallTimer(int timerId, int timeout, bool singleShot)
240 {
241     appendRecord(TimelineRecordFactory::createTimerInstallData(timerId, timeout, singleShot), TimelineRecordType::TimerInstall);
242 }
243
244 void InspectorTimelineAgent::didRemoveTimer(int timerId)
245 {
246     appendRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerRemove);
247 }
248
249 void InspectorTimelineAgent::willFireTimer(int timerId)
250 {
251     pushCurrentRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerFire);
252 }
253
254 void InspectorTimelineAgent::didFireTimer()
255 {
256     didCompleteCurrentRecord(TimelineRecordType::TimerFire);
257 }
258
259 void InspectorTimelineAgent::willChangeXHRReadyState(const String& url, int readyState)
260 {
261     pushCurrentRecord(TimelineRecordFactory::createXHRReadyStateChangeData(url, readyState), TimelineRecordType::XHRReadyStateChange);
262 }
263
264 void InspectorTimelineAgent::didChangeXHRReadyState()
265 {
266     didCompleteCurrentRecord(TimelineRecordType::XHRReadyStateChange);
267 }
268
269 void InspectorTimelineAgent::willLoadXHR(const String& url) 
270 {
271     pushCurrentRecord(TimelineRecordFactory::createXHRLoadData(url), TimelineRecordType::XHRLoad);
272 }
273
274 void InspectorTimelineAgent::didLoadXHR()
275 {
276     didCompleteCurrentRecord(TimelineRecordType::XHRLoad);
277 }
278
279 void InspectorTimelineAgent::willEvaluateScript(const String& url, int lineNumber)
280 {
281     pushCurrentRecord(TimelineRecordFactory::createEvaluateScriptData(url, lineNumber), TimelineRecordType::EvaluateScript);
282 }
283     
284 void InspectorTimelineAgent::didEvaluateScript()
285 {
286     didCompleteCurrentRecord(TimelineRecordType::EvaluateScript);
287 }
288
289 void InspectorTimelineAgent::didScheduleResourceRequest(const String& url)
290 {
291     appendRecord(TimelineRecordFactory::createScheduleResourceRequestData(url), TimelineRecordType::ScheduleResourceRequest);
292 }
293
294 void InspectorTimelineAgent::willSendResourceRequest(unsigned long identifier, const ResourceRequest& request)
295 {
296     pushGCEventRecords();
297     RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(WTF::currentTimeMS(), m_maxCallStackDepth);
298     String requestId = IdentifiersFactory::requestId(identifier);
299     record->setObject("data", TimelineRecordFactory::createResourceSendRequestData(requestId, request));
300     record->setString("type", TimelineRecordType::ResourceSendRequest);
301     setHeapSizeStatistic(record.get());
302     m_frontend->eventRecorded(record.release());
303 }
304
305 void InspectorTimelineAgent::willReceiveResourceData(unsigned long identifier)
306 {
307     String requestId = IdentifiersFactory::requestId(identifier);
308     pushCurrentRecord(TimelineRecordFactory::createReceiveResourceData(requestId), TimelineRecordType::ResourceReceivedData);
309 }
310
311 void InspectorTimelineAgent::didReceiveResourceData()
312 {
313     didCompleteCurrentRecord(TimelineRecordType::ResourceReceivedData);
314 }
315     
316 void InspectorTimelineAgent::willReceiveResourceResponse(unsigned long identifier, const ResourceResponse& response)
317 {
318     String requestId = IdentifiersFactory::requestId(identifier);
319     pushCurrentRecord(TimelineRecordFactory::createResourceReceiveResponseData(requestId, response), TimelineRecordType::ResourceReceiveResponse);
320 }
321
322 void InspectorTimelineAgent::didReceiveResourceResponse()
323 {
324     didCompleteCurrentRecord(TimelineRecordType::ResourceReceiveResponse);
325 }
326
327 void InspectorTimelineAgent::didFinishLoadingResource(unsigned long identifier, bool didFail, double finishTime)
328 {
329     appendRecord(TimelineRecordFactory::createResourceFinishData(IdentifiersFactory::requestId(identifier), didFail, finishTime * 1000), TimelineRecordType::ResourceFinish);
330 }
331
332 void InspectorTimelineAgent::didTimeStamp(const String& message)
333 {
334     appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeStamp);
335 }
336
337 void InspectorTimelineAgent::didMarkDOMContentEvent()
338 {
339     appendRecord(InspectorObject::create(), TimelineRecordType::TimeStamp);
340 }
341
342 void InspectorTimelineAgent::didMarkLoadEvent()
343 {
344     appendRecord(InspectorObject::create(), TimelineRecordType::MarkLoad);
345 }
346
347 void InspectorTimelineAgent::didCommitLoad()
348 {
349     clearRecordStack();
350 }
351
352 void InspectorTimelineAgent::didRegisterAnimationFrameCallback(int callbackId)
353 {
354     appendRecord(TimelineRecordFactory::createAnimationFrameCallbackData(callbackId), TimelineRecordType::RegisterAnimationFrameCallback);
355 }
356
357 void InspectorTimelineAgent::didCancelAnimationFrameCallback(int callbackId)
358 {
359     appendRecord(TimelineRecordFactory::createAnimationFrameCallbackData(callbackId), TimelineRecordType::CancelAnimationFrameCallback);
360 }
361
362 void InspectorTimelineAgent::willFireAnimationFrameEvent(int callbackId)
363 {
364     pushCurrentRecord(TimelineRecordFactory::createAnimationFrameCallbackData(callbackId), TimelineRecordType::FireAnimationFrameEvent);
365 }
366
367 void InspectorTimelineAgent::didFireAnimationFrameEvent()
368 {
369     didCompleteCurrentRecord(TimelineRecordType::FireAnimationFrameEvent);
370 }
371
372 void InspectorTimelineAgent::addRecordToTimeline(PassRefPtr<InspectorObject> prpRecord, const String& type)
373 {
374     RefPtr<InspectorObject> record(prpRecord);
375     record->setString("type", type);
376     setHeapSizeStatistic(record.get());
377     if (m_recordStack.isEmpty())
378         m_frontend->eventRecorded(record.release());
379     else {
380         TimelineRecordEntry parent = m_recordStack.last();
381         parent.children->pushObject(record.release());
382     }
383 }
384
385 void InspectorTimelineAgent::setHeapSizeStatistic(InspectorObject* record)
386 {
387     size_t usedHeapSize = 0;
388     size_t totalHeapSize = 0;
389     size_t heapSizeLimit = 0;
390     ScriptGCEvent::getHeapSize(usedHeapSize, totalHeapSize, heapSizeLimit);
391     record->setNumber("usedHeapSize", usedHeapSize);
392     record->setNumber("totalHeapSize", totalHeapSize);
393 }
394
395 void InspectorTimelineAgent::didCompleteCurrentRecord(const String& type)
396 {
397     // An empty stack could merely mean that the timeline agent was turned on in the middle of
398     // an event.  Don't treat as an error.
399     if (!m_recordStack.isEmpty()) {
400         pushGCEventRecords();
401         TimelineRecordEntry entry = m_recordStack.last();
402         m_recordStack.removeLast();
403         ASSERT(entry.type == type);
404         entry.record->setObject("data", entry.data);
405         entry.record->setArray("children", entry.children);
406         entry.record->setNumber("endTime", WTF::currentTimeMS());
407         addRecordToTimeline(entry.record, type);
408     }
409 }
410
411 InspectorTimelineAgent::InspectorTimelineAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state)
412     : m_instrumentingAgents(instrumentingAgents)
413     , m_state(state)
414     , m_frontend(0)
415     , m_id(1)
416     , m_maxCallStackDepth(5)
417 {
418 }
419
420 void InspectorTimelineAgent::appendRecord(PassRefPtr<InspectorObject> data, const String& type)
421 {
422     pushGCEventRecords();
423     RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(WTF::currentTimeMS(), m_maxCallStackDepth);
424     record->setObject("data", data);
425     record->setString("type", type);
426     addRecordToTimeline(record.release(), type);
427 }
428
429 void InspectorTimelineAgent::pushCurrentRecord(PassRefPtr<InspectorObject> data, const String& type)
430 {
431     pushGCEventRecords();
432     RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(WTF::currentTimeMS(), m_maxCallStackDepth);
433     m_recordStack.append(TimelineRecordEntry(record.release(), data, InspectorArray::create(), type));
434 }
435
436 void InspectorTimelineAgent::clearRecordStack()
437 {
438     m_recordStack.clear();
439     m_id++;
440 }
441
442 } // namespace WebCore
443
444 #endif // ENABLE(INSPECTOR)