Home | History | Annotate | Download | only in inspector
      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