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