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 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 98 void InspectorTimelineAgent::didGC(double startTime, double endTime, size_t collectedBytesCount) 99 { 100 m_gcEvents.append(GCEvent(startTime, endTime, collectedBytesCount)); 101 } 102 103 InspectorTimelineAgent::~InspectorTimelineAgent() 104 { 105 clearFrontend(); 106 } 107 108 void InspectorTimelineAgent::setFrontend(InspectorFrontend* frontend) 109 { 110 m_frontend = frontend->timeline(); 111 } 112 113 void InspectorTimelineAgent::clearFrontend() 114 { 115 ErrorString error; 116 stop(&error); 117 m_frontend = 0; 118 } 119 120 void InspectorTimelineAgent::restore() 121 { 122 if (m_state->getBoolean(TimelineAgentState::timelineAgentEnabled)) { 123 ErrorString error; 124 start(&error); 125 } 126 } 127 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 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 153 bool InspectorTimelineAgent::started() const 154 { 155 return m_state->getBoolean(TimelineAgentState::timelineAgentEnabled); 156 } 157 158 void InspectorTimelineAgent::willCallFunction(const String& scriptName, int scriptLine) 159 { 160 pushCurrentRecord(TimelineRecordFactory::createFunctionCallData(scriptName, scriptLine), TimelineRecordType::FunctionCall); 161 } 162 163 void InspectorTimelineAgent::didCallFunction() 164 { 165 didCompleteCurrentRecord(TimelineRecordType::FunctionCall); 166 } 167 168 void InspectorTimelineAgent::willDispatchEvent(const Event& event) 169 { 170 pushCurrentRecord(TimelineRecordFactory::createEventDispatchData(event), 171 TimelineRecordType::EventDispatch); 172 } 173 174 void InspectorTimelineAgent::didDispatchEvent() 175 { 176 didCompleteCurrentRecord(TimelineRecordType::EventDispatch); 177 } 178 179 void InspectorTimelineAgent::willLayout() 180 { 181 pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Layout); 182 } 183 184 void InspectorTimelineAgent::didLayout() 185 { 186 didCompleteCurrentRecord(TimelineRecordType::Layout); 187 } 188 189 void InspectorTimelineAgent::willRecalculateStyle() 190 { 191 pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RecalculateStyles); 192 } 193 194 void InspectorTimelineAgent::didRecalculateStyle() 195 { 196 didCompleteCurrentRecord(TimelineRecordType::RecalculateStyles); 197 } 198 199 void InspectorTimelineAgent::willPaint(const IntRect& rect) 200 { 201 pushCurrentRecord(TimelineRecordFactory::createPaintData(rect), TimelineRecordType::Paint); 202 } 203 204 void InspectorTimelineAgent::didPaint() 205 { 206 didCompleteCurrentRecord(TimelineRecordType::Paint); 207 } 208 209 void InspectorTimelineAgent::willWriteHTML(unsigned int length, unsigned int startLine) 210 { 211 pushCurrentRecord(TimelineRecordFactory::createParseHTMLData(length, startLine), TimelineRecordType::ParseHTML); 212 } 213 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 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 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 239 void InspectorTimelineAgent::willFireTimer(int timerId) 240 { 241 pushCurrentRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerFire); 242 } 243 244 void InspectorTimelineAgent::didFireTimer() 245 { 246 didCompleteCurrentRecord(TimelineRecordType::TimerFire); 247 } 248 249 void InspectorTimelineAgent::willChangeXHRReadyState(const String& url, int readyState) 250 { 251 pushCurrentRecord(TimelineRecordFactory::createXHRReadyStateChangeData(url, readyState), TimelineRecordType::XHRReadyStateChange); 252 } 253 254 void InspectorTimelineAgent::didChangeXHRReadyState() 255 { 256 didCompleteCurrentRecord(TimelineRecordType::XHRReadyStateChange); 257 } 258 259 void InspectorTimelineAgent::willLoadXHR(const String& url) 260 { 261 pushCurrentRecord(TimelineRecordFactory::createXHRLoadData(url), TimelineRecordType::XHRLoad); 262 } 263 264 void InspectorTimelineAgent::didLoadXHR() 265 { 266 didCompleteCurrentRecord(TimelineRecordType::XHRLoad); 267 } 268 269 void InspectorTimelineAgent::willEvaluateScript(const String& url, int lineNumber) 270 { 271 pushCurrentRecord(TimelineRecordFactory::createEvaluateScriptData(url, lineNumber), TimelineRecordType::EvaluateScript); 272 } 273 274 void InspectorTimelineAgent::didEvaluateScript() 275 { 276 didCompleteCurrentRecord(TimelineRecordType::EvaluateScript); 277 } 278 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 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 298 void InspectorTimelineAgent::willReceiveResourceData(unsigned long identifier) 299 { 300 pushCurrentRecord(TimelineRecordFactory::createReceiveResourceData(identifier), TimelineRecordType::ResourceReceivedData); 301 } 302 303 void InspectorTimelineAgent::didReceiveResourceData() 304 { 305 didCompleteCurrentRecord(TimelineRecordType::ResourceReceivedData); 306 } 307 308 void InspectorTimelineAgent::willReceiveResourceResponse(unsigned long identifier, const ResourceResponse& response) 309 { 310 pushCurrentRecord(TimelineRecordFactory::createResourceReceiveResponseData(identifier, response), TimelineRecordType::ResourceReceiveResponse); 311 } 312 313 void InspectorTimelineAgent::didReceiveResourceResponse() 314 { 315 didCompleteCurrentRecord(TimelineRecordType::ResourceReceiveResponse); 316 } 317 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 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 337 void InspectorTimelineAgent::didMarkDOMContentEvent() 338 { 339 pushGCEventRecords(); 340 RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(WTF::currentTimeMS()); 341 addRecordToTimeline(record.release(), TimelineRecordType::MarkDOMContent); 342 } 343 344 void InspectorTimelineAgent::didMarkLoadEvent() 345 { 346 pushGCEventRecords(); 347 RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(WTF::currentTimeMS()); 348 addRecordToTimeline(record.release(), TimelineRecordType::MarkLoad); 349 } 350 351 void InspectorTimelineAgent::didCommitLoad() 352 { 353 clearRecordStack(); 354 } 355 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 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 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 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 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 410 void InspectorTimelineAgent::clearRecordStack() 411 { 412 m_recordStack.clear(); 413 m_id++; 414 } 415 416 } // namespace WebCore 417 418 #endif // ENABLE(INSPECTOR) 419