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/TimelineTraceEventProcessor.h" 33 34#include "core/inspector/InspectorClient.h" 35#include "core/inspector/InspectorInstrumentation.h" 36#include "core/inspector/TimelineRecordFactory.h" 37 38#include "wtf/CurrentTime.h" 39#include "wtf/MainThread.h" 40#include "wtf/Vector.h" 41 42namespace WebCore { 43 44namespace { 45 46class TraceEventDispatcher { 47 WTF_MAKE_NONCOPYABLE(TraceEventDispatcher); 48public: 49 static TraceEventDispatcher* instance() 50 { 51 DEFINE_STATIC_LOCAL(TraceEventDispatcher, instance, ()); 52 return &instance; 53 } 54 55 void addProcessor(TimelineTraceEventProcessor* processor, InspectorClient* client) 56 { 57 MutexLocker locker(m_mutex); 58 59 m_processors.append(processor); 60 if (m_processors.size() == 1) 61 client->setTraceEventCallback(dispatchEventOnAnyThread); 62 } 63 64 void removeProcessor(TimelineTraceEventProcessor* processor, InspectorClient* client) 65 { 66 MutexLocker locker(m_mutex); 67 68 size_t index = m_processors.find(processor); 69 if (index == notFound) { 70 ASSERT_NOT_REACHED(); 71 return; 72 } 73 m_processors.remove(index); 74 if (m_processors.isEmpty()) 75 client->setTraceEventCallback(0); 76 } 77 78private: 79 TraceEventDispatcher() { } 80 81 static void dispatchEventOnAnyThread(char phase, const unsigned char*, const char* name, unsigned long long id, 82 int numArgs, const char* const* argNames, const unsigned char* argTypes, const unsigned long long* argValues, 83 unsigned char flags) 84 { 85 TraceEventDispatcher* self = instance(); 86 Vector<RefPtr<TimelineTraceEventProcessor> > processors; 87 { 88 MutexLocker locker(self->m_mutex); 89 processors = self->m_processors; 90 } 91 for (int i = 0, size = processors.size(); i < size; ++i) { 92 processors[i]->processEventOnAnyThread(static_cast<TimelineTraceEventProcessor::TraceEventPhase>(phase), 93 name, id, numArgs, argNames, argTypes, argValues, flags); 94 } 95 } 96 97 Mutex m_mutex; 98 Vector<RefPtr<TimelineTraceEventProcessor> > m_processors; 99}; 100 101} // namespce 102 103 104TimelineRecordStack::TimelineRecordStack(WeakPtr<InspectorTimelineAgent> timelineAgent) 105 : m_timelineAgent(timelineAgent) 106{ 107} 108 109void TimelineRecordStack::addScopedRecord(PassRefPtr<JSONObject> record) 110{ 111 m_stack.append(Entry(record)); 112} 113 114void TimelineRecordStack::closeScopedRecord(double endTime) 115{ 116 if (m_stack.isEmpty()) 117 return; 118 Entry last = m_stack.last(); 119 m_stack.removeLast(); 120 last.record->setNumber("endTime", endTime); 121 if (last.children->length()) 122 last.record->setArray("children", last.children); 123 addInstantRecord(last.record); 124} 125 126void TimelineRecordStack::addInstantRecord(PassRefPtr<JSONObject> record) 127{ 128 if (m_stack.isEmpty()) 129 send(record); 130 else 131 m_stack.last().children->pushObject(record); 132} 133 134#ifndef NDEBUG 135bool TimelineRecordStack::isOpenRecordOfType(const String& type) 136{ 137 String lastRecordType; 138 return m_stack.isEmpty() || (m_stack.last().record->getString("type", &lastRecordType) && type == lastRecordType); 139} 140#endif 141 142void TimelineRecordStack::send(PassRefPtr<JSONObject> record) 143{ 144 InspectorTimelineAgent* timelineAgent = m_timelineAgent.get(); 145 if (!timelineAgent) 146 return; 147 timelineAgent->sendEvent(record); 148} 149 150TimelineTraceEventProcessor::TimelineTraceEventProcessor(WeakPtr<InspectorTimelineAgent> timelineAgent, InspectorClient *client) 151 : m_timelineAgent(timelineAgent) 152 , m_timeConverter(timelineAgent.get()->timeConverter()) 153 , m_inspectorClient(client) 154 , m_pageId(reinterpret_cast<unsigned long long>(m_timelineAgent.get()->page())) 155 , m_layerTreeId(m_timelineAgent.get()->layerTreeId()) 156 , m_layerId(0) 157 , m_paintSetupStart(0) 158 , m_paintSetupEnd(0) 159{ 160 registerHandler(InstrumentationEvents::BeginFrame, TracePhaseInstant, &TimelineTraceEventProcessor::onBeginFrame); 161 registerHandler(InstrumentationEvents::UpdateLayer, TracePhaseBegin, &TimelineTraceEventProcessor::onUpdateLayerBegin); 162 registerHandler(InstrumentationEvents::UpdateLayer, TracePhaseEnd, &TimelineTraceEventProcessor::onUpdateLayerEnd); 163 registerHandler(InstrumentationEvents::PaintLayer, TracePhaseBegin, &TimelineTraceEventProcessor::onPaintLayerBegin); 164 registerHandler(InstrumentationEvents::PaintLayer, TracePhaseEnd, &TimelineTraceEventProcessor::onPaintLayerEnd); 165 registerHandler(InstrumentationEvents::PaintSetup, TracePhaseBegin, &TimelineTraceEventProcessor::onPaintSetupBegin); 166 registerHandler(InstrumentationEvents::PaintSetup, TracePhaseEnd, &TimelineTraceEventProcessor::onPaintSetupEnd); 167 registerHandler(InstrumentationEvents::RasterTask, TracePhaseBegin, &TimelineTraceEventProcessor::onRasterTaskBegin); 168 registerHandler(InstrumentationEvents::RasterTask, TracePhaseEnd, &TimelineTraceEventProcessor::onRasterTaskEnd); 169 registerHandler(InstrumentationEvents::ImageDecodeTask, TracePhaseBegin, &TimelineTraceEventProcessor::onImageDecodeTaskBegin); 170 registerHandler(InstrumentationEvents::ImageDecodeTask, TracePhaseEnd, &TimelineTraceEventProcessor::onImageDecodeTaskEnd); 171 registerHandler(InstrumentationEvents::Layer, TracePhaseDeleteObject, &TimelineTraceEventProcessor::onLayerDeleted); 172 registerHandler(InstrumentationEvents::Paint, TracePhaseInstant, &TimelineTraceEventProcessor::onPaint); 173 registerHandler(PlatformInstrumentation::ImageDecodeEvent, TracePhaseBegin, &TimelineTraceEventProcessor::onImageDecodeBegin); 174 registerHandler(PlatformInstrumentation::ImageDecodeEvent, TracePhaseEnd, &TimelineTraceEventProcessor::onImageDecodeEnd); 175 176 TraceEventDispatcher::instance()->addProcessor(this, m_inspectorClient); 177} 178 179TimelineTraceEventProcessor::~TimelineTraceEventProcessor() 180{ 181} 182 183void TimelineTraceEventProcessor::registerHandler(const char* name, TraceEventPhase phase, TraceEventHandler handler) 184{ 185 m_handlersByType.set(std::make_pair(name, phase), handler); 186} 187 188void TimelineTraceEventProcessor::shutdown() 189{ 190 TraceEventDispatcher::instance()->removeProcessor(this, m_inspectorClient); 191} 192 193size_t TimelineTraceEventProcessor::TraceEvent::findParameter(const char* name) const 194{ 195 for (int i = 0; i < m_argumentCount; ++i) { 196 if (!strcmp(name, m_argumentNames[i])) 197 return i; 198 } 199 return notFound; 200} 201 202const TimelineTraceEventProcessor::TraceValueUnion& TimelineTraceEventProcessor::TraceEvent::parameter(const char* name, TraceValueTypes expectedType) const 203{ 204 static TraceValueUnion missingValue; 205 size_t index = findParameter(name); 206 if (index == notFound || m_argumentTypes[index] != expectedType) { 207 ASSERT_NOT_REACHED(); 208 return missingValue; 209 } 210 return *reinterpret_cast<const TraceValueUnion*>(m_argumentValues + index); 211} 212 213void TimelineTraceEventProcessor::processEventOnAnyThread(TraceEventPhase phase, const char* name, unsigned long long id, 214 int numArgs, const char* const* argNames, const unsigned char* argTypes, const unsigned long long* argValues, 215 unsigned char) 216{ 217 HandlersMap::iterator it = m_handlersByType.find(std::make_pair(name, phase)); 218 if (it == m_handlersByType.end()) 219 return; 220 221 TraceEvent event(WTF::monotonicallyIncreasingTime(), phase, name, id, currentThread(), numArgs, argNames, argTypes, argValues); 222 223 if (!isMainThread()) { 224 MutexLocker locker(m_backgroundEventsMutex); 225 m_backgroundEvents.append(event); 226 return; 227 } 228 (this->*(it->value))(event); 229} 230 231void TimelineTraceEventProcessor::onBeginFrame(const TraceEvent&) 232{ 233 processBackgroundEvents(); 234} 235 236void TimelineTraceEventProcessor::onUpdateLayerBegin(const TraceEvent& event) 237{ 238 unsigned long long layerTreeId = event.asUInt(InstrumentationEventArguments::LayerTreeId); 239 if (layerTreeId != m_layerTreeId) 240 return; 241 m_layerId = event.asUInt(InstrumentationEventArguments::LayerId); 242 // We don't know the node yet. For content layers, the node will be updated 243 // by paint. For others, let it remain 0 -- we just need the fact that 244 // the layer belongs to the page (see cookie check). 245 m_layerToNodeMap.add(m_layerId, 0); 246} 247 248void TimelineTraceEventProcessor::onUpdateLayerEnd(const TraceEvent& event) 249{ 250 m_layerId = 0; 251} 252 253void TimelineTraceEventProcessor::onPaintLayerBegin(const TraceEvent& event) 254{ 255 m_layerId = event.asUInt(InstrumentationEventArguments::LayerId); 256 ASSERT(m_layerId); 257 ASSERT(!m_paintSetupStart); 258} 259 260void TimelineTraceEventProcessor::onPaintLayerEnd(const TraceEvent& event) 261{ 262 m_layerId = 0; 263 ASSERT(m_paintSetupStart); 264} 265 266void TimelineTraceEventProcessor::onPaintSetupBegin(const TraceEvent& event) 267{ 268 ASSERT(!m_paintSetupStart); 269 m_paintSetupStart = m_timeConverter.toProtocolTimestamp(event.timestamp()); 270} 271 272void TimelineTraceEventProcessor::onPaintSetupEnd(const TraceEvent& event) 273{ 274 ASSERT(m_paintSetupStart); 275 m_paintSetupEnd = m_timeConverter.toProtocolTimestamp(event.timestamp()); 276} 277 278void TimelineTraceEventProcessor::onRasterTaskBegin(const TraceEvent& event) 279{ 280 TimelineThreadState& state = threadState(event.threadIdentifier()); 281 if (!maybeEnterLayerTask(event, state)) 282 return; 283 unsigned long long layerId = event.asUInt(InstrumentationEventArguments::LayerId); 284 ASSERT(layerId); 285 RefPtr<JSONObject> record = createRecord(event, TimelineRecordType::Rasterize); 286 record->setObject("data", TimelineRecordFactory::createLayerData(m_layerToNodeMap.get(layerId))); 287 state.recordStack.addScopedRecord(record.release()); 288} 289 290void TimelineTraceEventProcessor::onRasterTaskEnd(const TraceEvent& event) 291{ 292 TimelineThreadState& state = threadState(event.threadIdentifier()); 293 if (!state.inKnownLayerTask) 294 return; 295 ASSERT(state.recordStack.isOpenRecordOfType(TimelineRecordType::Rasterize)); 296 state.recordStack.closeScopedRecord(m_timeConverter.toProtocolTimestamp(event.timestamp())); 297 leaveLayerTask(state); 298} 299 300void TimelineTraceEventProcessor::onImageDecodeTaskBegin(const TraceEvent& event) 301{ 302 maybeEnterLayerTask(event, threadState(event.threadIdentifier())); 303} 304 305void TimelineTraceEventProcessor::onImageDecodeTaskEnd(const TraceEvent& event) 306{ 307 leaveLayerTask(threadState(event.threadIdentifier())); 308} 309 310bool TimelineTraceEventProcessor::maybeEnterLayerTask(const TraceEvent& event, TimelineThreadState& threadState) 311{ 312 unsigned long long layerId = event.asUInt(InstrumentationEventArguments::LayerId); 313 if (!m_layerToNodeMap.contains(layerId)) 314 return false; 315 ASSERT(!threadState.inKnownLayerTask); 316 threadState.inKnownLayerTask = true; 317 return true; 318} 319 320void TimelineTraceEventProcessor::leaveLayerTask(TimelineThreadState& threadState) 321{ 322 threadState.inKnownLayerTask = false; 323} 324 325void TimelineTraceEventProcessor::onImageDecodeBegin(const TraceEvent& event) 326{ 327 TimelineThreadState& state = threadState(event.threadIdentifier()); 328 if (!state.inKnownLayerTask) 329 return; 330 state.recordStack.addScopedRecord(createRecord(event, TimelineRecordType::DecodeImage)); 331} 332 333void TimelineTraceEventProcessor::onImageDecodeEnd(const TraceEvent& event) 334{ 335 TimelineThreadState& state = threadState(event.threadIdentifier()); 336 if (!state.inKnownLayerTask) 337 return; 338 ASSERT(state.recordStack.isOpenRecordOfType(TimelineRecordType::DecodeImage)); 339 state.recordStack.closeScopedRecord(m_timeConverter.toProtocolTimestamp(event.timestamp())); 340} 341 342void TimelineTraceEventProcessor::onLayerDeleted(const TraceEvent& event) 343{ 344 unsigned long long id = event.id(); 345 ASSERT(id); 346 processBackgroundEvents(); 347 m_layerToNodeMap.remove(id); 348} 349 350void TimelineTraceEventProcessor::onPaint(const TraceEvent& event) 351{ 352 double paintSetupStart = m_paintSetupStart; 353 m_paintSetupStart = 0; 354 if (!m_layerId) 355 return; 356 unsigned long long pageId = event.asUInt(InstrumentationEventArguments::PageId); 357 if (pageId != m_pageId) 358 return; 359 long long nodeId = event.asInt(InstrumentationEventArguments::NodeId); 360 ASSERT(nodeId); 361 m_layerToNodeMap.set(m_layerId, nodeId); 362 InspectorTimelineAgent* timelineAgent = m_timelineAgent.get(); 363 if (timelineAgent && paintSetupStart) { 364 RefPtr<JSONObject> paintSetupRecord = TimelineRecordFactory::createGenericRecord(paintSetupStart, 0, TimelineRecordType::PaintSetup); 365 paintSetupRecord->setNumber("endTime", m_paintSetupEnd); 366 paintSetupRecord->setObject("data", TimelineRecordFactory::createLayerData(nodeId)); 367 timelineAgent->addRecordToTimeline(paintSetupRecord); 368 } 369} 370 371PassRefPtr<JSONObject> TimelineTraceEventProcessor::createRecord(const TraceEvent& event, const String& recordType, PassRefPtr<JSONObject> data) 372{ 373 double startTime = m_timeConverter.toProtocolTimestamp(event.timestamp()); 374 RefPtr<JSONObject> record = TimelineRecordFactory::createBackgroundRecord(startTime, String::number(event.threadIdentifier())); 375 record->setString("type", recordType); 376 record->setObject("data", data ? data : JSONObject::create()); 377 return record.release(); 378} 379 380void TimelineTraceEventProcessor::processBackgroundEvents() 381{ 382 ASSERT(isMainThread()); 383 Vector<TraceEvent> events; 384 { 385 MutexLocker locker(m_backgroundEventsMutex); 386 events.reserveCapacity(m_backgroundEvents.capacity()); 387 m_backgroundEvents.swap(events); 388 } 389 for (size_t i = 0, size = events.size(); i < size; ++i) { 390 const TraceEvent& event = events[i]; 391 HandlersMap::iterator it = m_handlersByType.find(std::make_pair(event.name(), event.phase())); 392 ASSERT(it != m_handlersByType.end() && it->value); 393 (this->*(it->value))(event); 394 } 395} 396 397} // namespace WebCore 398 399