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