1 /* 2 * Copyright (C) 2013 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 "core/inspector/InspectorTimelineAgent.h" 33 34 #include "InspectorFrontend.h" 35 #include "core/dom/Event.h" 36 #include "core/inspector/IdentifiersFactory.h" 37 #include "core/inspector/InspectorCounters.h" 38 #include "core/inspector/InspectorDOMAgent.h" 39 #include "core/inspector/InspectorInstrumentation.h" 40 #include "core/inspector/InspectorMemoryAgent.h" 41 #include "core/inspector/InspectorPageAgent.h" 42 #include "core/inspector/InspectorState.h" 43 #include "core/inspector/InstrumentingAgents.h" 44 #include "core/inspector/ScriptCallStack.h" 45 #include "core/inspector/TimelineRecordFactory.h" 46 #include "core/inspector/TimelineTraceEventProcessor.h" 47 #include "core/loader/DocumentLoader.h" 48 #include "core/page/DOMWindow.h" 49 #include "core/page/Frame.h" 50 #include "core/page/FrameView.h" 51 #include "core/platform/MemoryUsageSupport.h" 52 #include "core/platform/chromium/TraceEvent.h" 53 #include "core/platform/network/ResourceRequest.h" 54 #include "core/rendering/RenderObject.h" 55 #include "core/rendering/RenderView.h" 56 #include "core/xml/XMLHttpRequest.h" 57 58 #include "wtf/CurrentTime.h" 59 60 namespace WebCore { 61 62 namespace TimelineAgentState { 63 static const char timelineAgentEnabled[] = "timelineAgentEnabled"; 64 static const char timelineMaxCallStackDepth[] = "timelineMaxCallStackDepth"; 65 static const char includeDomCounters[] = "includeDomCounters"; 66 static const char includeNativeMemoryStatistics[] = "includeNativeMemoryStatistics"; 67 } 68 69 // Must be kept in sync with WebInspector.TimelineModel.RecordType in TimelineModel.js 70 namespace TimelineRecordType { 71 static const char Program[] = "Program"; 72 73 static const char EventDispatch[] = "EventDispatch"; 74 static const char BeginFrame[] = "BeginFrame"; 75 static const char ScheduleStyleRecalculation[] = "ScheduleStyleRecalculation"; 76 static const char RecalculateStyles[] = "RecalculateStyles"; 77 static const char InvalidateLayout[] = "InvalidateLayout"; 78 static const char Layout[] = "Layout"; 79 static const char Paint[] = "Paint"; 80 static const char ScrollLayer[] = "ScrollLayer"; 81 static const char ResizeImage[] = "ResizeImage"; 82 static const char CompositeLayers[] = "CompositeLayers"; 83 84 static const char ParseHTML[] = "ParseHTML"; 85 86 static const char TimerInstall[] = "TimerInstall"; 87 static const char TimerRemove[] = "TimerRemove"; 88 static const char TimerFire[] = "TimerFire"; 89 90 static const char EvaluateScript[] = "EvaluateScript"; 91 92 static const char MarkLoad[] = "MarkLoad"; 93 static const char MarkDOMContent[] = "MarkDOMContent"; 94 95 static const char TimeStamp[] = "TimeStamp"; 96 static const char Time[] = "Time"; 97 static const char TimeEnd[] = "TimeEnd"; 98 99 static const char ScheduleResourceRequest[] = "ScheduleResourceRequest"; 100 static const char ResourceSendRequest[] = "ResourceSendRequest"; 101 static const char ResourceReceiveResponse[] = "ResourceReceiveResponse"; 102 static const char ResourceReceivedData[] = "ResourceReceivedData"; 103 static const char ResourceFinish[] = "ResourceFinish"; 104 105 static const char XHRReadyStateChange[] = "XHRReadyStateChange"; 106 static const char XHRLoad[] = "XHRLoad"; 107 108 static const char FunctionCall[] = "FunctionCall"; 109 static const char GCEvent[] = "GCEvent"; 110 111 static const char RequestAnimationFrame[] = "RequestAnimationFrame"; 112 static const char CancelAnimationFrame[] = "CancelAnimationFrame"; 113 static const char FireAnimationFrame[] = "FireAnimationFrame"; 114 115 static const char WebSocketCreate[] = "WebSocketCreate"; 116 static const char WebSocketSendHandshakeRequest[] = "WebSocketSendHandshakeRequest"; 117 static const char WebSocketReceiveHandshakeResponse[] = "WebSocketReceiveHandshakeResponse"; 118 static const char WebSocketDestroy[] = "WebSocketDestroy"; 119 120 // Event names visible to other modules. 121 const char DecodeImage[] = "DecodeImage"; 122 const char Rasterize[] = "Rasterize"; 123 const char PaintSetup[] = "PaintSetup"; 124 } 125 126 namespace { 127 const char BackendNodeIdGroup[] = "timeline"; 128 } 129 130 static Frame* frameForScriptExecutionContext(ScriptExecutionContext* context) 131 { 132 Frame* frame = 0; 133 if (context->isDocument()) 134 frame = toDocument(context)->frame(); 135 return frame; 136 } 137 138 static bool eventHasListeners(const AtomicString& eventType, DOMWindow* window, Node* node, const EventPath& eventPath) 139 { 140 if (window && window->hasEventListeners(eventType)) 141 return true; 142 143 if (node->hasEventListeners(eventType)) 144 return true; 145 146 for (size_t i = 0; i < eventPath.size(); i++) { 147 if (eventPath[i]->node()->hasEventListeners(eventType)) 148 return true; 149 } 150 151 return false; 152 } 153 154 void TimelineTimeConverter::reset() 155 { 156 m_startTimeMs = currentTime() * 1000; 157 m_timestampsBaseMs = monotonicallyIncreasingTime() * 1000; 158 } 159 160 void InspectorTimelineAgent::pushGCEventRecords() 161 { 162 if (!m_gcEvents.size()) 163 return; 164 165 GCEvents events = m_gcEvents; 166 m_gcEvents.clear(); 167 for (GCEvents::iterator i = events.begin(); i != events.end(); ++i) { 168 RefPtr<JSONObject> record = TimelineRecordFactory::createGenericRecord(m_timeConverter.toProtocolTimestamp(i->startTime), m_maxCallStackDepth, TimelineRecordType::GCEvent); 169 record->setObject("data", TimelineRecordFactory::createGCEventData(i->collectedBytes)); 170 record->setNumber("endTime", m_timeConverter.toProtocolTimestamp(i->endTime)); 171 addRecordToTimeline(record.release()); 172 } 173 } 174 175 void InspectorTimelineAgent::didGC(double startTime, double endTime, size_t collectedBytesCount) 176 { 177 m_gcEvents.append(GCEvent(startTime, endTime, collectedBytesCount)); 178 } 179 180 InspectorTimelineAgent::~InspectorTimelineAgent() 181 { 182 } 183 184 void InspectorTimelineAgent::setFrontend(InspectorFrontend* frontend) 185 { 186 m_frontend = frontend->timeline(); 187 } 188 189 void InspectorTimelineAgent::clearFrontend() 190 { 191 ErrorString error; 192 stop(&error); 193 releaseNodeIds(); 194 m_frontend = 0; 195 } 196 197 void InspectorTimelineAgent::restore() 198 { 199 if (m_state->getBoolean(TimelineAgentState::timelineAgentEnabled)) { 200 m_maxCallStackDepth = m_state->getLong(TimelineAgentState::timelineMaxCallStackDepth); 201 ErrorString error; 202 bool includeDomCounters = m_state->getBoolean(TimelineAgentState::includeDomCounters); 203 bool includeNativeMemoryStatistics = m_state->getBoolean(TimelineAgentState::includeNativeMemoryStatistics); 204 start(&error, &m_maxCallStackDepth, &includeDomCounters, &includeNativeMemoryStatistics); 205 } 206 } 207 208 void InspectorTimelineAgent::start(ErrorString*, const int* maxCallStackDepth, const bool* includeDomCounters, const bool* includeNativeMemoryStatistics) 209 { 210 if (!m_frontend) 211 return; 212 213 releaseNodeIds(); 214 if (maxCallStackDepth && *maxCallStackDepth >= 0) 215 m_maxCallStackDepth = *maxCallStackDepth; 216 else 217 m_maxCallStackDepth = 5; 218 m_state->setLong(TimelineAgentState::timelineMaxCallStackDepth, m_maxCallStackDepth); 219 m_state->setBoolean(TimelineAgentState::includeDomCounters, includeDomCounters && *includeDomCounters); 220 m_state->setBoolean(TimelineAgentState::includeNativeMemoryStatistics, includeNativeMemoryStatistics && *includeNativeMemoryStatistics); 221 m_timeConverter.reset(); 222 m_frontend->timelineStarted(m_timeConverter.timestampsBaseMs(), m_timeConverter.startTimeMs()); 223 224 m_instrumentingAgents->setInspectorTimelineAgent(this); 225 ScriptGCEvent::addEventListener(this); 226 m_state->setBoolean(TimelineAgentState::timelineAgentEnabled, true); 227 if (m_client && m_pageAgent) 228 m_traceEventProcessor = adoptRef(new TimelineTraceEventProcessor(m_weakFactory.createWeakPtr(), m_client)); 229 } 230 231 void InspectorTimelineAgent::stop(ErrorString*) 232 { 233 if (!m_state->getBoolean(TimelineAgentState::timelineAgentEnabled)) 234 return; 235 236 if (m_traceEventProcessor) { 237 m_traceEventProcessor->shutdown(); 238 m_traceEventProcessor.clear(); 239 } 240 m_weakFactory.revokeAll(); 241 m_instrumentingAgents->setInspectorTimelineAgent(0); 242 ScriptGCEvent::removeEventListener(this); 243 244 clearRecordStack(); 245 m_gcEvents.clear(); 246 247 m_state->setBoolean(TimelineAgentState::timelineAgentEnabled, false); 248 } 249 250 void InspectorTimelineAgent::didBeginFrame() 251 { 252 TRACE_EVENT_INSTANT0("webkit", InstrumentationEvents::BeginFrame); 253 m_pendingFrameRecord = TimelineRecordFactory::createGenericRecord(timestamp(), 0, TimelineRecordType::BeginFrame); 254 } 255 256 void InspectorTimelineAgent::didCancelFrame() 257 { 258 m_pendingFrameRecord.clear(); 259 } 260 261 bool InspectorTimelineAgent::willCallFunction(ScriptExecutionContext* context, const String& scriptName, int scriptLine) 262 { 263 pushCurrentRecord(TimelineRecordFactory::createFunctionCallData(scriptName, scriptLine), TimelineRecordType::FunctionCall, true, frameForScriptExecutionContext(context)); 264 return true; 265 } 266 267 void InspectorTimelineAgent::didCallFunction() 268 { 269 didCompleteCurrentRecord(TimelineRecordType::FunctionCall); 270 } 271 272 bool InspectorTimelineAgent::willDispatchEvent(Document* document, const Event& event, DOMWindow* window, Node* node, const EventPath& eventPath) 273 { 274 if (!eventHasListeners(event.type(), window, node, eventPath)) 275 return false; 276 277 pushCurrentRecord(TimelineRecordFactory::createEventDispatchData(event), TimelineRecordType::EventDispatch, false, document->frame()); 278 return true; 279 } 280 281 bool InspectorTimelineAgent::willDispatchEventOnWindow(const Event& event, DOMWindow* window) 282 { 283 if (!window->hasEventListeners(event.type())) 284 return false; 285 pushCurrentRecord(TimelineRecordFactory::createEventDispatchData(event), TimelineRecordType::EventDispatch, false, window->frame()); 286 return true; 287 } 288 289 void InspectorTimelineAgent::didDispatchEvent() 290 { 291 didCompleteCurrentRecord(TimelineRecordType::EventDispatch); 292 } 293 294 void InspectorTimelineAgent::didDispatchEventOnWindow() 295 { 296 didDispatchEvent(); 297 } 298 299 void InspectorTimelineAgent::didInvalidateLayout(Frame* frame) 300 { 301 appendRecord(JSONObject::create(), TimelineRecordType::InvalidateLayout, true, frame); 302 } 303 304 bool InspectorTimelineAgent::willLayout(Frame* frame) 305 { 306 RenderObject* root = frame->view()->layoutRoot(); 307 bool partialLayout = !!root; 308 309 if (!partialLayout) 310 root = frame->contentRenderer(); 311 312 unsigned dirtyObjects = 0; 313 unsigned totalObjects = 0; 314 for (RenderObject* o = root; o; o = o->nextInPreOrder(root)) { 315 ++totalObjects; 316 if (o->needsLayout()) 317 ++dirtyObjects; 318 } 319 pushCurrentRecord(TimelineRecordFactory::createLayoutData(dirtyObjects, totalObjects, partialLayout), TimelineRecordType::Layout, true, frame); 320 return true; 321 } 322 323 void InspectorTimelineAgent::didLayout(RenderObject* root) 324 { 325 if (m_recordStack.isEmpty()) 326 return; 327 TimelineRecordEntry& entry = m_recordStack.last(); 328 ASSERT(entry.type == TimelineRecordType::Layout); 329 Vector<FloatQuad> quads; 330 root->absoluteQuads(quads); 331 if (quads.size() >= 1) 332 TimelineRecordFactory::appendLayoutRoot(entry.data.get(), quads[0], idForNode(root->generatingNode())); 333 else 334 ASSERT_NOT_REACHED(); 335 didCompleteCurrentRecord(TimelineRecordType::Layout); 336 } 337 338 void InspectorTimelineAgent::didScheduleStyleRecalculation(Document* document) 339 { 340 appendRecord(JSONObject::create(), TimelineRecordType::ScheduleStyleRecalculation, true, document->frame()); 341 } 342 343 bool InspectorTimelineAgent::willRecalculateStyle(Document* document) 344 { 345 pushCurrentRecord(JSONObject::create(), TimelineRecordType::RecalculateStyles, true, document->frame()); 346 ASSERT(!m_styleRecalcElementCounter); 347 return true; 348 } 349 350 void InspectorTimelineAgent::didRecalculateStyle() 351 { 352 if (m_recordStack.isEmpty()) 353 return; 354 TimelineRecordEntry& entry = m_recordStack.last(); 355 ASSERT(entry.type == TimelineRecordType::RecalculateStyles); 356 TimelineRecordFactory::appendStyleRecalcDetails(entry.data.get(), m_styleRecalcElementCounter); 357 m_styleRecalcElementCounter = 0; 358 didCompleteCurrentRecord(TimelineRecordType::RecalculateStyles); 359 } 360 361 void InspectorTimelineAgent::didRecalculateStyleForElement() 362 { 363 ++m_styleRecalcElementCounter; 364 } 365 366 void InspectorTimelineAgent::willPaint(RenderObject* renderer) 367 { 368 Frame* frame = renderer->frame(); 369 TRACE_EVENT_INSTANT2("instrumentation", InstrumentationEvents::Paint, 370 InstrumentationEventArguments::PageId, reinterpret_cast<unsigned long long>(frame->page()), 371 InstrumentationEventArguments::NodeId, idForNode(renderer->generatingNode())); 372 373 pushCurrentRecord(JSONObject::create(), TimelineRecordType::Paint, true, frame, true); 374 } 375 376 void InspectorTimelineAgent::didPaint(RenderObject* renderer, GraphicsContext*, const LayoutRect& clipRect) 377 { 378 TimelineRecordEntry& entry = m_recordStack.last(); 379 ASSERT(entry.type == TimelineRecordType::Paint); 380 FloatQuad quad; 381 localToPageQuad(*renderer, clipRect, &quad); 382 entry.data = TimelineRecordFactory::createPaintData(quad, idForNode(renderer->generatingNode())); 383 didCompleteCurrentRecord(TimelineRecordType::Paint); 384 } 385 386 void InspectorTimelineAgent::willScrollLayer(RenderObject* renderer) 387 { 388 pushCurrentRecord(TimelineRecordFactory::createLayerData(idForNode(renderer->generatingNode())), TimelineRecordType::ScrollLayer, false, renderer->frame()); 389 } 390 391 void InspectorTimelineAgent::didScrollLayer() 392 { 393 didCompleteCurrentRecord(TimelineRecordType::ScrollLayer); 394 } 395 396 void InspectorTimelineAgent::willDecodeImage(const String& imageType) 397 { 398 pushCurrentRecord(TimelineRecordFactory::createDecodeImageData(imageType), TimelineRecordType::DecodeImage, true, 0); 399 } 400 401 void InspectorTimelineAgent::didDecodeImage() 402 { 403 didCompleteCurrentRecord(TimelineRecordType::DecodeImage); 404 } 405 406 void InspectorTimelineAgent::willResizeImage(bool shouldCache) 407 { 408 pushCurrentRecord(TimelineRecordFactory::createResizeImageData(shouldCache), TimelineRecordType::ResizeImage, true, 0); 409 } 410 411 void InspectorTimelineAgent::didResizeImage() 412 { 413 didCompleteCurrentRecord(TimelineRecordType::ResizeImage); 414 } 415 416 void InspectorTimelineAgent::willComposite() 417 { 418 pushCurrentRecord(JSONObject::create(), TimelineRecordType::CompositeLayers, false, 0); 419 } 420 421 void InspectorTimelineAgent::didComposite() 422 { 423 didCompleteCurrentRecord(TimelineRecordType::CompositeLayers); 424 } 425 426 bool InspectorTimelineAgent::willWriteHTML(Document* document, unsigned startLine) 427 { 428 pushCurrentRecord(TimelineRecordFactory::createParseHTMLData(startLine), TimelineRecordType::ParseHTML, true, document->frame()); 429 return true; 430 } 431 432 void InspectorTimelineAgent::didWriteHTML(unsigned endLine) 433 { 434 if (!m_recordStack.isEmpty()) { 435 TimelineRecordEntry entry = m_recordStack.last(); 436 entry.data->setNumber("endLine", endLine); 437 didCompleteCurrentRecord(TimelineRecordType::ParseHTML); 438 } 439 } 440 441 void InspectorTimelineAgent::didInstallTimer(ScriptExecutionContext* context, int timerId, int timeout, bool singleShot) 442 { 443 appendRecord(TimelineRecordFactory::createTimerInstallData(timerId, timeout, singleShot), TimelineRecordType::TimerInstall, true, frameForScriptExecutionContext(context)); 444 } 445 446 void InspectorTimelineAgent::didRemoveTimer(ScriptExecutionContext* context, int timerId) 447 { 448 appendRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerRemove, true, frameForScriptExecutionContext(context)); 449 } 450 451 bool InspectorTimelineAgent::willFireTimer(ScriptExecutionContext* context, int timerId) 452 { 453 pushCurrentRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerFire, false, frameForScriptExecutionContext(context)); 454 return true; 455 } 456 457 void InspectorTimelineAgent::didFireTimer() 458 { 459 didCompleteCurrentRecord(TimelineRecordType::TimerFire); 460 } 461 462 bool InspectorTimelineAgent::willDispatchXHRReadyStateChangeEvent(ScriptExecutionContext* context, XMLHttpRequest* request) 463 { 464 if (!request->hasEventListeners(eventNames().readystatechangeEvent)) 465 return false; 466 pushCurrentRecord(TimelineRecordFactory::createXHRReadyStateChangeData(request->url().string(), request->readyState()), TimelineRecordType::XHRReadyStateChange, false, frameForScriptExecutionContext(context)); 467 return true; 468 } 469 470 void InspectorTimelineAgent::didDispatchXHRReadyStateChangeEvent() 471 { 472 didCompleteCurrentRecord(TimelineRecordType::XHRReadyStateChange); 473 } 474 475 bool InspectorTimelineAgent::willDispatchXHRLoadEvent(ScriptExecutionContext* context, XMLHttpRequest* request) 476 { 477 if (!request->hasEventListeners(eventNames().loadEvent)) 478 return false; 479 pushCurrentRecord(TimelineRecordFactory::createXHRLoadData(request->url().string()), TimelineRecordType::XHRLoad, true, frameForScriptExecutionContext(context)); 480 return true; 481 } 482 483 void InspectorTimelineAgent::didDispatchXHRLoadEvent() 484 { 485 didCompleteCurrentRecord(TimelineRecordType::XHRLoad); 486 } 487 488 bool InspectorTimelineAgent::willEvaluateScript(Frame* frame, const String& url, int lineNumber) 489 { 490 pushCurrentRecord(TimelineRecordFactory::createEvaluateScriptData(url, lineNumber), TimelineRecordType::EvaluateScript, true, frame); 491 return true; 492 } 493 494 void InspectorTimelineAgent::didEvaluateScript() 495 { 496 didCompleteCurrentRecord(TimelineRecordType::EvaluateScript); 497 } 498 499 void InspectorTimelineAgent::didScheduleResourceRequest(Document* document, const String& url) 500 { 501 appendRecord(TimelineRecordFactory::createScheduleResourceRequestData(url), TimelineRecordType::ScheduleResourceRequest, true, document->frame()); 502 } 503 504 void InspectorTimelineAgent::willSendRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request, const ResourceResponse&, const FetchInitiatorInfo&) 505 { 506 String requestId = IdentifiersFactory::requestId(identifier); 507 appendRecord(TimelineRecordFactory::createResourceSendRequestData(requestId, request), TimelineRecordType::ResourceSendRequest, true, loader->frame()); 508 } 509 510 bool InspectorTimelineAgent::willReceiveResourceData(Frame* frame, unsigned long identifier, int length) 511 { 512 String requestId = IdentifiersFactory::requestId(identifier); 513 pushCurrentRecord(TimelineRecordFactory::createReceiveResourceData(requestId, length), TimelineRecordType::ResourceReceivedData, false, frame); 514 return true; 515 } 516 517 void InspectorTimelineAgent::didReceiveResourceData() 518 { 519 didCompleteCurrentRecord(TimelineRecordType::ResourceReceivedData); 520 } 521 522 bool InspectorTimelineAgent::willReceiveResourceResponse(Frame* frame, unsigned long identifier, const ResourceResponse& response) 523 { 524 String requestId = IdentifiersFactory::requestId(identifier); 525 pushCurrentRecord(TimelineRecordFactory::createResourceReceiveResponseData(requestId, response), TimelineRecordType::ResourceReceiveResponse, false, frame); 526 return true; 527 } 528 529 void InspectorTimelineAgent::didReceiveResourceResponse(unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response, ResourceLoader* resourceLoader) 530 { 531 didCompleteCurrentRecord(TimelineRecordType::ResourceReceiveResponse); 532 } 533 534 void InspectorTimelineAgent::didFinishLoadingResource(unsigned long identifier, bool didFail, double finishTime, Frame* frame) 535 { 536 appendRecord(TimelineRecordFactory::createResourceFinishData(IdentifiersFactory::requestId(identifier), didFail, finishTime * 1000), TimelineRecordType::ResourceFinish, false, frame); 537 } 538 539 void InspectorTimelineAgent::didFinishLoading(unsigned long identifier, DocumentLoader* loader, double monotonicFinishTime) 540 { 541 double finishTime = 0.0; 542 // FIXME: Expose all of the timing details to inspector and have it calculate finishTime. 543 if (monotonicFinishTime) 544 finishTime = loader->timing()->monotonicTimeToPseudoWallTime(monotonicFinishTime); 545 546 didFinishLoadingResource(identifier, false, finishTime, loader->frame()); 547 } 548 549 void InspectorTimelineAgent::didFailLoading(unsigned long identifier, DocumentLoader* loader, const ResourceError& error) 550 { 551 didFinishLoadingResource(identifier, true, 0, loader->frame()); 552 } 553 554 void InspectorTimelineAgent::consoleTimeStamp(Frame* frame, PassRefPtr<ScriptArguments> arguments) 555 { 556 String message; 557 arguments->getFirstArgumentAsString(message); 558 appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeStamp, true, frame); 559 } 560 561 void InspectorTimelineAgent::startConsoleTiming(Frame* frame, const String& message) 562 { 563 appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::Time, true, frame); 564 } 565 566 void InspectorTimelineAgent::stopConsoleTiming(Frame* frame, const String& message, PassRefPtr<ScriptCallStack>) 567 { 568 appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeEnd, true, frame); 569 } 570 571 void InspectorTimelineAgent::domContentLoadedEventFired(Frame* frame) 572 { 573 bool isMainFrame = frame && m_pageAgent && (frame == m_pageAgent->mainFrame()); 574 appendRecord(TimelineRecordFactory::createMarkData(isMainFrame), TimelineRecordType::MarkDOMContent, false, frame); 575 } 576 577 void InspectorTimelineAgent::loadEventFired(Frame* frame) 578 { 579 bool isMainFrame = frame && m_pageAgent && (frame == m_pageAgent->mainFrame()); 580 appendRecord(TimelineRecordFactory::createMarkData(isMainFrame), TimelineRecordType::MarkLoad, false, frame); 581 } 582 583 void InspectorTimelineAgent::didCommitLoad() 584 { 585 clearRecordStack(); 586 } 587 588 void InspectorTimelineAgent::didRequestAnimationFrame(Document* document, int callbackId) 589 { 590 appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::RequestAnimationFrame, true, document->frame()); 591 } 592 593 void InspectorTimelineAgent::didCancelAnimationFrame(Document* document, int callbackId) 594 { 595 appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::CancelAnimationFrame, true, document->frame()); 596 } 597 598 bool InspectorTimelineAgent::willFireAnimationFrame(Document* document, int callbackId) 599 { 600 pushCurrentRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::FireAnimationFrame, false, document->frame()); 601 return true; 602 } 603 604 void InspectorTimelineAgent::didFireAnimationFrame() 605 { 606 didCompleteCurrentRecord(TimelineRecordType::FireAnimationFrame); 607 } 608 609 void InspectorTimelineAgent::willProcessTask() 610 { 611 pushCurrentRecord(JSONObject::create(), TimelineRecordType::Program, false, 0); 612 } 613 614 void InspectorTimelineAgent::didProcessTask() 615 { 616 didCompleteCurrentRecord(TimelineRecordType::Program); 617 } 618 619 void InspectorTimelineAgent::didCreateWebSocket(Document* document, unsigned long identifier, const KURL& url, const String& protocol) 620 { 621 appendRecord(TimelineRecordFactory::createWebSocketCreateData(identifier, url, protocol), TimelineRecordType::WebSocketCreate, true, document->frame()); 622 } 623 624 void InspectorTimelineAgent::willSendWebSocketHandshakeRequest(Document* document, unsigned long identifier, const WebSocketHandshakeRequest&) 625 { 626 appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketSendHandshakeRequest, true, document->frame()); 627 } 628 629 void InspectorTimelineAgent::didReceiveWebSocketHandshakeResponse(Document* document, unsigned long identifier, const WebSocketHandshakeResponse&) 630 { 631 appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketReceiveHandshakeResponse, false, document->frame()); 632 } 633 634 void InspectorTimelineAgent::didCloseWebSocket(Document* document, unsigned long identifier) 635 { 636 appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketDestroy, true, document->frame()); 637 } 638 639 void InspectorTimelineAgent::addRecordToTimeline(PassRefPtr<JSONObject> record) 640 { 641 commitFrameRecord(); 642 innerAddRecordToTimeline(record); 643 } 644 645 void InspectorTimelineAgent::innerAddRecordToTimeline(PassRefPtr<JSONObject> prpRecord) 646 { 647 RefPtr<TypeBuilder::Timeline::TimelineEvent> record = TypeBuilder::Timeline::TimelineEvent::runtimeCast(prpRecord); 648 649 if (m_recordStack.isEmpty()) { 650 sendEvent(record.release()); 651 } else { 652 setDOMCounters(record.get()); 653 TimelineRecordEntry parent = m_recordStack.last(); 654 parent.children->pushObject(record.release()); 655 } 656 } 657 658 static size_t getUsedHeapSize() 659 { 660 HeapInfo info; 661 ScriptGCEvent::getHeapSize(info); 662 return info.usedJSHeapSize; 663 } 664 665 void InspectorTimelineAgent::setDOMCounters(TypeBuilder::Timeline::TimelineEvent* record) 666 { 667 record->setUsedHeapSize(getUsedHeapSize()); 668 669 if (m_state->getBoolean(TimelineAgentState::includeDomCounters)) { 670 int documentCount = 0; 671 int nodeCount = 0; 672 if (m_inspectorType == PageInspector) { 673 documentCount = InspectorCounters::counterValue(InspectorCounters::DocumentCounter); 674 nodeCount = InspectorCounters::counterValue(InspectorCounters::NodeCounter); 675 } 676 int listenerCount = ThreadLocalInspectorCounters::current().counterValue(ThreadLocalInspectorCounters::JSEventListenerCounter); 677 RefPtr<TypeBuilder::Timeline::DOMCounters> counters = TypeBuilder::Timeline::DOMCounters::create() 678 .setDocuments(documentCount) 679 .setNodes(nodeCount) 680 .setJsEventListeners(listenerCount); 681 record->setCounters(counters.release()); 682 } 683 } 684 685 void InspectorTimelineAgent::setFrameIdentifier(JSONObject* record, Frame* frame) 686 { 687 if (!frame || !m_pageAgent) 688 return; 689 String frameId; 690 if (frame && m_pageAgent) 691 frameId = m_pageAgent->frameId(frame); 692 record->setString("frameId", frameId); 693 } 694 695 void InspectorTimelineAgent::didCompleteCurrentRecord(const String& type) 696 { 697 // An empty stack could merely mean that the timeline agent was turned on in the middle of 698 // an event. Don't treat as an error. 699 if (!m_recordStack.isEmpty()) { 700 if (m_platformInstrumentationClientInstalledAtStackDepth == m_recordStack.size()) { 701 m_platformInstrumentationClientInstalledAtStackDepth = 0; 702 PlatformInstrumentation::setClient(0); 703 } 704 705 pushGCEventRecords(); 706 TimelineRecordEntry entry = m_recordStack.last(); 707 m_recordStack.removeLast(); 708 ASSERT(entry.type == type); 709 entry.record->setObject("data", entry.data); 710 entry.record->setArray("children", entry.children); 711 entry.record->setNumber("endTime", timestamp()); 712 ptrdiff_t usedHeapSizeDelta = getUsedHeapSize() - entry.usedHeapSizeAtStart; 713 if (usedHeapSizeDelta) 714 entry.record->setNumber("usedHeapSizeDelta", usedHeapSizeDelta); 715 addRecordToTimeline(entry.record); 716 } 717 } 718 719 InspectorTimelineAgent::InspectorTimelineAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorMemoryAgent* memoryAgent, InspectorDOMAgent* domAgent, InspectorCompositeState* state, InspectorType type, InspectorClient* client) 720 : InspectorBaseAgent<InspectorTimelineAgent>("Timeline", instrumentingAgents, state) 721 , m_pageAgent(pageAgent) 722 , m_memoryAgent(memoryAgent) 723 , m_domAgent(domAgent) 724 , m_frontend(0) 725 , m_id(1) 726 , m_maxCallStackDepth(5) 727 , m_platformInstrumentationClientInstalledAtStackDepth(0) 728 , m_inspectorType(type) 729 , m_client(client) 730 , m_weakFactory(this) 731 , m_styleRecalcElementCounter(0) 732 , m_layerTreeId(0) 733 { 734 } 735 736 void InspectorTimelineAgent::appendRecord(PassRefPtr<JSONObject> data, const String& type, bool captureCallStack, Frame* frame) 737 { 738 pushGCEventRecords(); 739 RefPtr<JSONObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0, type); 740 record->setObject("data", data); 741 setFrameIdentifier(record.get(), frame); 742 addRecordToTimeline(record.release()); 743 } 744 745 void InspectorTimelineAgent::sendEvent(PassRefPtr<JSONObject> event) 746 { 747 // FIXME: runtimeCast is a hack. We do it because we can't build TimelineEvent directly now. 748 RefPtr<TypeBuilder::Timeline::TimelineEvent> recordChecked = TypeBuilder::Timeline::TimelineEvent::runtimeCast(event); 749 m_frontend->eventRecorded(recordChecked.release()); 750 } 751 752 void InspectorTimelineAgent::pushCurrentRecord(PassRefPtr<JSONObject> data, const String& type, bool captureCallStack, Frame* frame, bool hasLowLevelDetails) 753 { 754 pushGCEventRecords(); 755 commitFrameRecord(); 756 RefPtr<JSONObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0, type); 757 setFrameIdentifier(record.get(), frame); 758 m_recordStack.append(TimelineRecordEntry(record.release(), data, JSONArray::create(), type, getUsedHeapSize())); 759 if (hasLowLevelDetails && !m_platformInstrumentationClientInstalledAtStackDepth && !PlatformInstrumentation::hasClient()) { 760 m_platformInstrumentationClientInstalledAtStackDepth = m_recordStack.size(); 761 PlatformInstrumentation::setClient(this); 762 } 763 } 764 765 void InspectorTimelineAgent::commitFrameRecord() 766 { 767 if (!m_pendingFrameRecord) 768 return; 769 770 m_pendingFrameRecord->setObject("data", JSONObject::create()); 771 innerAddRecordToTimeline(m_pendingFrameRecord.release()); 772 } 773 774 void InspectorTimelineAgent::clearRecordStack() 775 { 776 if (m_platformInstrumentationClientInstalledAtStackDepth) { 777 m_platformInstrumentationClientInstalledAtStackDepth = 0; 778 PlatformInstrumentation::setClient(0); 779 } 780 m_pendingFrameRecord.clear(); 781 m_recordStack.clear(); 782 m_id++; 783 } 784 785 void InspectorTimelineAgent::localToPageQuad(const RenderObject& renderer, const LayoutRect& rect, FloatQuad* quad) 786 { 787 Frame* frame = renderer.frame(); 788 FrameView* view = frame->view(); 789 FloatQuad absolute = renderer.localToAbsoluteQuad(FloatQuad(rect)); 790 quad->setP1(view->contentsToRootView(roundedIntPoint(absolute.p1()))); 791 quad->setP2(view->contentsToRootView(roundedIntPoint(absolute.p2()))); 792 quad->setP3(view->contentsToRootView(roundedIntPoint(absolute.p3()))); 793 quad->setP4(view->contentsToRootView(roundedIntPoint(absolute.p4()))); 794 } 795 796 long long InspectorTimelineAgent::idForNode(Node* node) 797 { 798 return m_domAgent && node ? m_domAgent->backendNodeIdForNode(node, BackendNodeIdGroup) : 0; 799 } 800 801 void InspectorTimelineAgent::releaseNodeIds() 802 { 803 ErrorString unused; 804 if (m_domAgent) 805 m_domAgent->releaseBackendNodeIds(&unused, BackendNodeIdGroup); 806 } 807 808 double InspectorTimelineAgent::timestamp() const 809 { 810 return m_timeConverter.toProtocolTimestamp(WTF::monotonicallyIncreasingTime()); 811 } 812 813 Page* InspectorTimelineAgent::page() 814 { 815 return m_pageAgent ? m_pageAgent->page() : 0; 816 } 817 818 } // namespace WebCore 819 820