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