Web Inspector: do not aggregate non-main thread timeline events, handle them in a...
authorcaseq@chromium.org <caseq@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 Mar 2013 13:59:41 +0000 (13:59 +0000)
committercaseq@chromium.org <caseq@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 Mar 2013 13:59:41 +0000 (13:59 +0000)
https://bugs.webkit.org/show_bug.cgi?id=112172

Reviewed by Vsevolod Vlasov.

- build nested event trees for non-main thread events similar to how it's done on the main thread;
- drop aggregation logic for Rasterize events;
- extract time conversion logic so that it may be reused in TimelineTraceEventProcessor.

* English.lproj/localizedStrings.js: drive-by: drop duplicate line.
* inspector/InspectorTimelineAgent.cpp:
(WebCore::TimelineTimeConverter::reset):
(WebCore):
(WebCore::InspectorTimelineAgent::pushGCEventRecords):
(WebCore::InspectorTimelineAgent::start):
(WebCore::InspectorTimelineAgent::innerAddRecordToTimeline):
(WebCore::InspectorTimelineAgent::InspectorTimelineAgent):
(WebCore::InspectorTimelineAgent::appendRecord):
(WebCore::InspectorTimelineAgent::sendEvent):
(WebCore::InspectorTimelineAgent::timestamp):
* inspector/InspectorTimelineAgent.h:
(TimelineTimeConverter):
(WebCore::TimelineTimeConverter::TimelineTimeConverter):
(WebCore::TimelineTimeConverter::fromMonotonicallyIncreasingTime):
(WebCore):
(InspectorTimelineAgent):
(WebCore::InspectorTimelineAgent::timeConverter):
* inspector/TimelineRecordFactory.cpp:
(WebCore::TimelineRecordFactory::createBackgroundRecord):
(WebCore):
* inspector/TimelineRecordFactory.h:
(TimelineRecordFactory):
* inspector/TimelineTraceEventProcessor.cpp:
(WebCore::TimelineRecordStack::TimelineRecordStack):
(WebCore):
(WebCore::TimelineRecordStack::addScopedRecord):
(WebCore::TimelineRecordStack::closeScopedRecord):
(WebCore::TimelineRecordStack::addInstantRecord):
(WebCore::TimelineRecordStack::isOpenRecordOfType):
(WebCore::TimelineRecordStack::send):
(WebCore::TimelineTraceEventProcessor::TimelineTraceEventProcessor):
(WebCore::TimelineTraceEventProcessor::onBeginFrame):
(WebCore::TimelineTraceEventProcessor::onRasterTaskBegin):
(WebCore::TimelineTraceEventProcessor::onRasterTaskEnd):
(WebCore::TimelineTraceEventProcessor::createRecord):
* inspector/TimelineTraceEventProcessor.h:
(TimelineRecordStack):
(WebCore::TimelineRecordStack::Entry::Entry):
(Entry):
(WebCore::TimelineRecordStack::TimelineRecordStack):
(WebCore):
(WebCore::TimelineTraceEventProcessor::TimelineThreadState::TimelineThreadState):
(TimelineThreadState):
(TimelineTraceEventProcessor):
(WebCore::TimelineTraceEventProcessor::TraceEvent::TraceEvent):
(WebCore::TimelineTraceEventProcessor::TraceEvent::isNull):
(WebCore::TimelineTraceEventProcessor::threadState):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@145809 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/English.lproj/localizedStrings.js
Source/WebCore/inspector/InspectorTimelineAgent.cpp
Source/WebCore/inspector/InspectorTimelineAgent.h
Source/WebCore/inspector/TimelineRecordFactory.cpp
Source/WebCore/inspector/TimelineRecordFactory.h
Source/WebCore/inspector/TimelineTraceEventProcessor.cpp
Source/WebCore/inspector/TimelineTraceEventProcessor.h

index bdd3068..57a1f49 100644 (file)
@@ -1,3 +1,63 @@
+2013-03-12  Andrey Kosyakov  <caseq@chromium.org>
+
+        Web Inspector: do not aggregate non-main thread timeline events, handle them in a generic fashion
+        https://bugs.webkit.org/show_bug.cgi?id=112172
+
+        Reviewed by Vsevolod Vlasov.
+
+        - build nested event trees for non-main thread events similar to how it's done on the main thread;
+        - drop aggregation logic for Rasterize events;
+        - extract time conversion logic so that it may be reused in TimelineTraceEventProcessor.
+
+        * English.lproj/localizedStrings.js: drive-by: drop duplicate line.
+        * inspector/InspectorTimelineAgent.cpp:
+        (WebCore::TimelineTimeConverter::reset):
+        (WebCore):
+        (WebCore::InspectorTimelineAgent::pushGCEventRecords):
+        (WebCore::InspectorTimelineAgent::start):
+        (WebCore::InspectorTimelineAgent::innerAddRecordToTimeline):
+        (WebCore::InspectorTimelineAgent::InspectorTimelineAgent):
+        (WebCore::InspectorTimelineAgent::appendRecord):
+        (WebCore::InspectorTimelineAgent::sendEvent):
+        (WebCore::InspectorTimelineAgent::timestamp):
+        * inspector/InspectorTimelineAgent.h:
+        (TimelineTimeConverter):
+        (WebCore::TimelineTimeConverter::TimelineTimeConverter):
+        (WebCore::TimelineTimeConverter::fromMonotonicallyIncreasingTime):
+        (WebCore):
+        (InspectorTimelineAgent):
+        (WebCore::InspectorTimelineAgent::timeConverter):
+        * inspector/TimelineRecordFactory.cpp:
+        (WebCore::TimelineRecordFactory::createBackgroundRecord):
+        (WebCore):
+        * inspector/TimelineRecordFactory.h:
+        (TimelineRecordFactory):
+        * inspector/TimelineTraceEventProcessor.cpp:
+        (WebCore::TimelineRecordStack::TimelineRecordStack):
+        (WebCore):
+        (WebCore::TimelineRecordStack::addScopedRecord):
+        (WebCore::TimelineRecordStack::closeScopedRecord):
+        (WebCore::TimelineRecordStack::addInstantRecord):
+        (WebCore::TimelineRecordStack::isOpenRecordOfType):
+        (WebCore::TimelineRecordStack::send):
+        (WebCore::TimelineTraceEventProcessor::TimelineTraceEventProcessor):
+        (WebCore::TimelineTraceEventProcessor::onBeginFrame):
+        (WebCore::TimelineTraceEventProcessor::onRasterTaskBegin):
+        (WebCore::TimelineTraceEventProcessor::onRasterTaskEnd):
+        (WebCore::TimelineTraceEventProcessor::createRecord):
+        * inspector/TimelineTraceEventProcessor.h:
+        (TimelineRecordStack):
+        (WebCore::TimelineRecordStack::Entry::Entry):
+        (Entry):
+        (WebCore::TimelineRecordStack::TimelineRecordStack):
+        (WebCore):
+        (WebCore::TimelineTraceEventProcessor::TimelineThreadState::TimelineThreadState):
+        (TimelineThreadState):
+        (TimelineTraceEventProcessor):
+        (WebCore::TimelineTraceEventProcessor::TraceEvent::TraceEvent):
+        (WebCore::TimelineTraceEventProcessor::TraceEvent::isNull):
+        (WebCore::TimelineTraceEventProcessor::threadState):
+
 2013-03-14  Xan Lopez  <xlopez@igalia.com>
 
         [GTK] Wrong ASSERT in AudioDestinationGstreamer::stop
index bd306f1..d980776 100644 (file)
@@ -773,7 +773,6 @@ localizedStrings["Schedule Style Recalculation"] = "Schedule Style Recalculation
 localizedStrings["Invalidate Layout"] = "Invalidate Layout";
 localizedStrings["Composite Layers"] = "Composite Layers";
 localizedStrings["Interval Duration"] = "Interval Duration";
-localizedStrings["CPU time"] = "CPU time";
 localizedStrings["Replay XHR"] = "Replay XHR";
 localizedStrings["[ %d - %d ]"] = "[ %d - %d ]";
 localizedStrings["Search sources"] = "Search sources";
index 0288b04..33e1754 100644 (file)
@@ -122,6 +122,11 @@ static const char WebSocketDestroy[] = "WebSocketDestroy";
 const char Rasterize[] = "Rasterize";
 }
 
+void TimelineTimeConverter::reset()
+{
+    m_startOffset = monotonicallyIncreasingTime() - currentTime();
+}
+
 void InspectorTimelineAgent::pushGCEventRecords()
 {
     if (!m_gcEvents.size())
@@ -130,9 +135,9 @@ void InspectorTimelineAgent::pushGCEventRecords()
     GCEvents events = m_gcEvents;
     m_gcEvents.clear();
     for (GCEvents::iterator i = events.begin(); i != events.end(); ++i) {
-        RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestampFromMicroseconds(i->startTime), m_maxCallStackDepth);
+        RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(m_timeConverter.fromMonotonicallyIncreasingTime(i->startTime), m_maxCallStackDepth);
         record->setObject("data", TimelineRecordFactory::createGCEventData(i->collectedBytes));
-        record->setNumber("endTime", timestampFromMicroseconds(i->endTime));
+        record->setNumber("endTime", m_timeConverter.fromMonotonicallyIncreasingTime(i->endTime));
         addRecordToTimeline(record.release(), TimelineRecordType::GCEvent);
     }
 }
@@ -178,7 +183,7 @@ void InspectorTimelineAgent::start(ErrorString*, const int* maxCallStackDepth)
     else
         m_maxCallStackDepth = 5;
     m_state->setLong(TimelineAgentState::timelineMaxCallStackDepth, m_maxCallStackDepth);
-    m_timestampOffset = currentTime() - monotonicallyIncreasingTime();
+    m_timeConverter.reset();
 
     m_instrumentingAgents->setInspectorTimelineAgent(this);
     ScriptGCEvent::addEventListener(this);
@@ -547,11 +552,9 @@ void InspectorTimelineAgent::innerAddRecordToTimeline(PassRefPtr<InspectorObject
     else
         setDOMCounters(record.get());
 
-    if (m_recordStack.isEmpty()) {
-        // FIXME: runtimeCast is a hack. We do it because we can't build TimelineEvent directly now.
-        RefPtr<TypeBuilder::Timeline::TimelineEvent> recordChecked = TypeBuilder::Timeline::TimelineEvent::runtimeCast(record.release());
-        m_frontend->eventRecorded(recordChecked.release());
-    } else {
+    if (m_recordStack.isEmpty())
+        sendEvent(record.release());
+    else {
         TimelineRecordEntry parent = m_recordStack.last();
         parent.children->pushObject(record.release());
     }
@@ -634,7 +637,6 @@ InspectorTimelineAgent::InspectorTimelineAgent(InstrumentingAgents* instrumentin
     , m_pageAgent(pageAgent)
     , m_memoryAgent(memoryAgent)
     , m_frontend(0)
-    , m_timestampOffset(0)
     , m_id(1)
     , m_maxCallStackDepth(5)
     , m_platformInstrumentationClientInstalledAtStackDepth(0)
@@ -649,19 +651,14 @@ void InspectorTimelineAgent::appendRecord(PassRefPtr<InspectorObject> data, cons
     pushGCEventRecords();
     RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0);
     record->setObject("data", data);
-    record->setString("type", type);
     setFrameIdentifier(record.get(), frame);
     addRecordToTimeline(record.release(), type);
 }
 
-void InspectorTimelineAgent::appendBackgroundThreadRecord(PassRefPtr<InspectorObject> data, const String& type, double startTime, double endTime, const String& threadName)
+void InspectorTimelineAgent::sendEvent(PassRefPtr<InspectorObject> event)
 {
-    RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestampFromMicroseconds(startTime), 0);
-    record->setObject("data", data);
-    record->setString("type", type);
-    record->setString("thread", threadName);
-    record->setNumber("endTime", timestampFromMicroseconds(endTime));
-    RefPtr<TypeBuilder::Timeline::TimelineEvent> recordChecked = TypeBuilder::Timeline::TimelineEvent::runtimeCast(record.release());
+    // FIXME: runtimeCast is a hack. We do it because we can't build TimelineEvent directly now.
+    RefPtr<TypeBuilder::Timeline::TimelineEvent> recordChecked = TypeBuilder::Timeline::TimelineEvent::runtimeCast(event);
     m_frontend->eventRecorded(recordChecked.release());
 }
 
@@ -700,12 +697,7 @@ void InspectorTimelineAgent::clearRecordStack()
 
 double InspectorTimelineAgent::timestamp()
 {
-    return timestampFromMicroseconds(WTF::monotonicallyIncreasingTime());
-}
-
-double InspectorTimelineAgent::timestampFromMicroseconds(double microseconds)
-{
-    return (microseconds + m_timestampOffset) * 1000.0;
+    return m_timeConverter.fromMonotonicallyIncreasingTime(WTF::monotonicallyIncreasingTime());
 }
 
 Page* InspectorTimelineAgent::page()
index e1ccc26..9847e1d 100644 (file)
@@ -67,6 +67,19 @@ namespace TimelineRecordType {
 extern const char Rasterize[];
 };
 
+class TimelineTimeConverter {
+public:
+    TimelineTimeConverter()
+        : m_startOffset(0)
+    {
+    }
+    double fromMonotonicallyIncreasingTime(double time) const  { return (time - m_startOffset) * 1000.0; }
+    void reset();
+
+private:
+    double m_startOffset;
+};
+
 class InspectorTimelineAgent
     : public InspectorBaseAgent<InspectorTimelineAgent>,
       public ScriptGCEventListener,
@@ -181,6 +194,7 @@ public:
     virtual void didResizeImage() OVERRIDE;
 
 private:
+    friend class TimelineRecordStack;
     friend class TimelineTraceEventProcessor;
 
     struct TimelineRecordEntry {
@@ -197,7 +211,7 @@ private:
         
     InspectorTimelineAgent(InstrumentingAgents*, InspectorPageAgent*, InspectorMemoryAgent*, InspectorCompositeState*, InspectorType, InspectorClient*);
 
-    void appendBackgroundThreadRecord(PassRefPtr<InspectorObject> data, const String& type, double startTime, double endTime, const String& threadName);
+    void sendEvent(PassRefPtr<InspectorObject>);
     void appendRecord(PassRefPtr<InspectorObject> data, const String& type, bool captureCallStack, Frame*);
     void pushCurrentRecord(PassRefPtr<InspectorObject>, const String& type, bool captureCallStack, Frame*, bool hasLowLevelDetails = false);
 
@@ -215,12 +229,13 @@ private:
     void innerAddRecordToTimeline(PassRefPtr<InspectorObject>, const String& type);
     void clearRecordStack();
 
+    const TimelineTimeConverter& timeConverter() const { return m_timeConverter; }
     double timestamp();
-    double timestampFromMicroseconds(double microseconds);
     Page* page();
 
     InspectorPageAgent* m_pageAgent;
     InspectorMemoryAgent* m_memoryAgent;
+    TimelineTimeConverter m_timeConverter;
 
     InspectorFrontend::Timeline* m_frontend;
     double m_timestampOffset;
index e47cba5..a2d555e 100644 (file)
@@ -59,6 +59,14 @@ PassRefPtr<InspectorObject> TimelineRecordFactory::createGenericRecord(double st
     return record.release();
 }
 
+PassRefPtr<InspectorObject> TimelineRecordFactory::createBackgroundRecord(double startTime, const String& threadName)
+{
+    RefPtr<InspectorObject> record = InspectorObject::create();
+    record->setNumber("startTime", startTime);
+    record->setString("thread", threadName);
+    return record.release();
+}
+
 PassRefPtr<InspectorObject> TimelineRecordFactory::createGCEventData(const size_t usedHeapSizeDelta)
 {
     RefPtr<InspectorObject> data = InspectorObject::create();
@@ -213,14 +221,6 @@ void TimelineRecordFactory::addRectData(InspectorObject* data, const LayoutRect&
     data->setNumber("height", rect.height());
 }
 
-PassRefPtr<InspectorObject> TimelineRecordFactory::createRasterData(double totalCPUTime, int threadsUsed)
-{
-    RefPtr<InspectorObject> data = InspectorObject::create();
-    data->setNumber("totalCPUTime", totalCPUTime);
-    data->setNumber("threadsUsed", threadsUsed);
-    return data.release();
-}
-
 } // namespace WebCore
 
 #endif // ENABLE(INSPECTOR)
index 0925649..68cba6d 100644 (file)
@@ -49,6 +49,7 @@ namespace WebCore {
     class TimelineRecordFactory {
     public:
         static PassRefPtr<InspectorObject> createGenericRecord(double startTime, int maxCallStackDepth);
+        static PassRefPtr<InspectorObject> createBackgroundRecord(double startTime, const String& thread);
 
         static PassRefPtr<InspectorObject> createGCEventData(const size_t usedHeapSizeDelta);
 
@@ -108,8 +109,6 @@ namespace WebCore {
             return data.release();
         }
 #endif
-        static PassRefPtr<InspectorObject> createRasterData(double totalCPUTime, int threadsUsed);
-
     private:
         TimelineRecordFactory() { }
     };
index 9210caa..3e42aac 100644 (file)
@@ -1,5 +1,5 @@
 /*
-* Copyright (C) 2012 Google Inc. All rights reserved.
+* Copyright (C) 2013 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
@@ -36,7 +36,6 @@
 
 #include "InspectorClient.h"
 #include "InspectorInstrumentation.h"
-#include "InspectorTimelineAgent.h"
 #include "TimelineRecordFactory.h"
 
 #include <wtf/CurrentTime.h>
@@ -104,13 +103,59 @@ private:
 };
 
 } // namespce
+
+
+TimelineRecordStack::TimelineRecordStack(WeakPtr<InspectorTimelineAgent> timelineAgent)
+    : m_timelineAgent(timelineAgent)
+{
+}
+
+void TimelineRecordStack::addScopedRecord(PassRefPtr<InspectorObject> record)
+{
+    m_stack.append(Entry(record));
+}
+
+void TimelineRecordStack::closeScopedRecord(double endTime)
+{
+    if (m_stack.isEmpty())
+        return;
+    Entry last = m_stack.last();
+    m_stack.removeLast();
+    last.record->setNumber("endTime", endTime);
+    if (last.children->length())
+        last.record->setArray("children", last.children);
+    addInstantRecord(last.record);
+}
+
+void TimelineRecordStack::addInstantRecord(PassRefPtr<InspectorObject> record)
+{
+    if (m_stack.isEmpty())
+        send(record);
+    else
+        m_stack.last().children->pushObject(record);
+}
+
+#ifndef NDEBUG
+bool TimelineRecordStack::isOpenRecordOfType(const String& type)
+{
+    String lastRecordType;
+    return m_stack.isEmpty() || (m_stack.last().record->getString("type", &lastRecordType) && type == lastRecordType);
+}
+#endif
+
+void TimelineRecordStack::send(PassRefPtr<InspectorObject> record)
+{
+    InspectorTimelineAgent* timelineAgent = m_timelineAgent.get();
+    if (!timelineAgent)
+        return;
+    timelineAgent->sendEvent(record);
+}
+
 TimelineTraceEventProcessor::TimelineTraceEventProcessor(WeakPtr<InspectorTimelineAgent> timelineAgent, InspectorClient *client)
     : m_timelineAgent(timelineAgent)
+    , m_timeConverter(timelineAgent.get()->timeConverter())
     , m_inspectorClient(client)
     , m_pageId(reinterpret_cast<unsigned long long>(m_timelineAgent.get()->page()))
-    , m_firstRasterStartTime(0)
-    , m_lastRasterEndTime(0)
-    , m_frameRasterTime(0)
     , m_layerId(0)
 {
     registerHandler(InstrumentationEvents::BeginFrame, TracePhaseInstant, &TimelineTraceEventProcessor::onBeginFrame);
@@ -178,7 +223,7 @@ void TimelineTraceEventProcessor::processEventOnAnyThread(TraceEventPhase phase,
 
 void TimelineTraceEventProcessor::onBeginFrame(const TraceEvent&)
 {
-    flushRasterizerStatistics();
+    processBackgroundEvents();
 }
 
 void TimelineTraceEventProcessor::onPaintLayerBegin(const TraceEvent& event)
@@ -195,26 +240,23 @@ void TimelineTraceEventProcessor::onPaintLayerEnd(const TraceEvent&)
 void TimelineTraceEventProcessor::onRasterTaskBegin(const TraceEvent& event)
 {
     unsigned long long layerId = event.asUInt(InstrumentationEventArguments::LayerId);
-    ThreadIdentifier threadIdentifier = event.threadIdentifier();
-    ASSERT(m_rasterStartTimeByThread.get(threadIdentifier) == HashTraits<double>::emptyValue());
-    double timestamp = m_knownLayers.contains(layerId) ? event.timestamp() : 0;
-    m_rasterStartTimeByThread.set(threadIdentifier, timestamp);
+    if (!m_knownLayers.contains(layerId))
+        return;
+    TimelineThreadState& state = threadState(event.threadIdentifier());
+    ASSERT(!state.inRasterizeEvent);
+    state.inRasterizeEvent = true;
+    RefPtr<InspectorObject> record = createRecord(event, TimelineRecordType::Rasterize);
+    state.recordStack.addScopedRecord(record.release());
 }
 
 void TimelineTraceEventProcessor::onRasterTaskEnd(const TraceEvent& event)
 {
-    HashMap<ThreadIdentifier, double>::iterator it = m_rasterStartTimeByThread.find(event.threadIdentifier());
-    if (it == m_rasterStartTimeByThread.end())
-        return;
-    double startTime = it->value;
-    double endTime = event.timestamp();
-    if (startTime == HashTraits<double>::emptyValue()) // Rasterizing unknown layer.
+    TimelineThreadState& state = threadState(event.threadIdentifier());
+    if (!state.inRasterizeEvent)
         return;
-    m_frameRasterTime += endTime - startTime;
-    it->value = HashTraits<double>::emptyValue();
-    if (!m_firstRasterStartTime || m_firstRasterStartTime > startTime)
-        m_firstRasterStartTime = startTime;
-    m_lastRasterEndTime = endTime;
+    ASSERT(state.recordStack.isOpenRecordOfType(TimelineRecordType::Rasterize));
+    state.recordStack.closeScopedRecord(m_timeConverter.fromMonotonicallyIncreasingTime(event.timestamp()));
+    state.inRasterizeEvent = false;
 }
 
 void TimelineTraceEventProcessor::onLayerDeleted(const TraceEvent& event)
@@ -235,25 +277,13 @@ void TimelineTraceEventProcessor::onPaint(const TraceEvent& event)
         m_knownLayers.add(m_layerId);
 }
 
-void TimelineTraceEventProcessor::flushRasterizerStatistics()
+PassRefPtr<InspectorObject> TimelineTraceEventProcessor::createRecord(const TraceEvent& event, const String& recordType, PassRefPtr<InspectorObject> data)
 {
-    processBackgroundEvents();
-    if (m_lastRasterEndTime) {
-        RefPtr<InspectorObject> data = TimelineRecordFactory::createRasterData(m_frameRasterTime, m_rasterStartTimeByThread.size());
-        sendTimelineRecord(data, TimelineRecordType::Rasterize, m_firstRasterStartTime, m_lastRasterEndTime, "multiple");
-    }
-    m_firstRasterStartTime = 0;
-    m_lastRasterEndTime = 0;
-    m_frameRasterTime = 0;
-}
-
-void TimelineTraceEventProcessor::sendTimelineRecord(PassRefPtr<InspectorObject> data, const String& recordType, double startTime, double endTime, const String& thread)
-{
-    ASSERT(isMainThread());
-    InspectorTimelineAgent* timelineAgent = m_timelineAgent.get();
-    if (!timelineAgent)
-        return;
-    timelineAgent->appendBackgroundThreadRecord(data, recordType, startTime, endTime, thread);
+    double startTime = m_timeConverter.fromMonotonicallyIncreasingTime(event.timestamp());
+    RefPtr<InspectorObject> record = TimelineRecordFactory::createBackgroundRecord(startTime, String::number(event.threadIdentifier()));
+    record->setString("type", recordType);
+    record->setObject("data", data ? data : InspectorObject::create());
+    return record.release();
 }
 
 void TimelineTraceEventProcessor::processBackgroundEvents()
index 861ab90..c8e0c51 100644 (file)
@@ -33,6 +33,7 @@
 
 #if ENABLE(INSPECTOR)
 
+#include "InspectorTimelineAgent.h"
 #include "InspectorValues.h"
 
 #include <wtf/HashMap.h>
@@ -49,6 +50,38 @@ class InspectorClient;
 class InspectorTimelineAgent;
 class Page;
 
+class TimelineRecordStack {
+private:
+    struct Entry {
+        Entry(PassRefPtr<InspectorObject> record)
+            : record(record)
+            , children(InspectorArray::create())
+        {
+        }
+
+        RefPtr<InspectorObject> record;
+        RefPtr<InspectorArray> children;
+    };
+
+public:
+    TimelineRecordStack() { }
+    TimelineRecordStack(WeakPtr<InspectorTimelineAgent>);
+
+    void addScopedRecord(PassRefPtr<InspectorObject> record);
+    void closeScopedRecord(double endTime);
+    void addInstantRecord(PassRefPtr<InspectorObject> record);
+
+#ifndef NDEBUG
+    bool isOpenRecordOfType(const String& type);
+#endif
+
+private:
+    void send(PassRefPtr<InspectorObject>);
+
+    WeakPtr<InspectorTimelineAgent> m_timelineAgent;
+    Vector<Entry> m_stack;
+};
+
 class TimelineTraceEventProcessor : public ThreadSafeRefCounted<TimelineTraceEventProcessor> {
 public:
     // FIXME: re-use definitions in TraceEvent.h once it's promoted to all platforms.
@@ -89,10 +122,24 @@ private:
         TypeCopyString = 7
     };
 
+    struct TimelineThreadState {
+        TimelineThreadState() { }
+
+        TimelineThreadState(WeakPtr<InspectorTimelineAgent> timelineAgent)
+            : recordStack(timelineAgent)
+            , inRasterizeEvent(false)
+        {
+        }
+
+        TimelineRecordStack recordStack;
+        bool inRasterizeEvent;
+    };
+
     class TraceEvent {
     public:
         TraceEvent()
-            : m_argumentCount(0)
+            : m_name(0)
+            , m_argumentCount(0)
         {
         }
 
@@ -122,6 +169,7 @@ private:
         unsigned long long id() const { return m_id; }
         ThreadIdentifier threadIdentifier() const { return m_threadIdentifier; }
         int argumentCount() const { return m_argumentCount; }
+        bool isNull() const { return !m_name; }
 
         bool asBool(const char* name) const
         {
@@ -168,8 +216,18 @@ private:
 
     typedef void (TimelineTraceEventProcessor::*TraceEventHandler)(const TraceEvent&);
 
+    TimelineThreadState& threadState(ThreadIdentifier thread)
+    {
+        ThreadStateMap::iterator it = m_threadStates.find(thread);
+        if (it != m_threadStates.end())
+            return it->value;
+        return m_threadStates.add(thread, TimelineThreadState(m_timelineAgent)).iterator->value;
+    }
+
     void processBackgroundEvents();
-    void sendTimelineRecord(PassRefPtr<InspectorObject> data, const String& recordType, double startTime, double endTime, const String& Thread);
+    PassRefPtr<InspectorObject> createRecord(const TraceEvent&, const String& recordType, PassRefPtr<InspectorObject> data = 0);
+
+    void registerHandler(const char* name, TraceEventPhase, TraceEventHandler);
 
     void onBeginFrame(const TraceEvent&);
     void onPaintLayerBegin(const TraceEvent&);
@@ -179,25 +237,21 @@ private:
     void onLayerDeleted(const TraceEvent&);
     void onPaint(const TraceEvent&);
 
-    void flushRasterizerStatistics();
-
-    void registerHandler(const char* name, TraceEventPhase, TraceEventHandler);
 
     WeakPtr<InspectorTimelineAgent> m_timelineAgent;
+    TimelineTimeConverter m_timeConverter;
     InspectorClient* m_inspectorClient;
+    unsigned long long m_pageId;
 
     typedef HashMap<std::pair<String, int>, TraceEventHandler> HandlersMap;
     HandlersMap m_handlersByType;
     Mutex m_backgroundEventsMutex;
     Vector<TraceEvent> m_backgroundEvents;
-    unsigned long long m_pageId;
 
-    HashSet<unsigned long long> m_knownLayers;
-    HashMap<ThreadIdentifier, double> m_rasterStartTimeByThread;
-    double m_firstRasterStartTime;
-    double m_lastRasterEndTime;
-    double m_frameRasterTime;
+    typedef HashMap<ThreadIdentifier, TimelineThreadState> ThreadStateMap;
+    ThreadStateMap m_threadStates;
 
+    HashSet<unsigned long long> m_knownLayers;
     unsigned long long m_layerId;
 };