blob: c6537c1eb33b48a95269f21f957cae8450eec090 [file] [log] [blame]
/*
* 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
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "core/inspector/TimelineTraceEventProcessor.h"
#include "core/inspector/InspectorClient.h"
#include "core/inspector/InspectorInstrumentation.h"
#include "core/inspector/TimelineRecordFactory.h"
#include "wtf/CurrentTime.h"
#include "wtf/MainThread.h"
#include "wtf/Vector.h"
namespace WebCore {
namespace {
class TraceEventDispatcher {
WTF_MAKE_NONCOPYABLE(TraceEventDispatcher);
public:
static TraceEventDispatcher* instance()
{
DEFINE_STATIC_LOCAL(TraceEventDispatcher, instance, ());
return &instance;
}
void addProcessor(TimelineTraceEventProcessor* processor, InspectorClient* client)
{
MutexLocker locker(m_mutex);
m_processors.append(processor);
if (m_processors.size() == 1)
client->setTraceEventCallback(dispatchEventOnAnyThread);
}
void removeProcessor(TimelineTraceEventProcessor* processor, InspectorClient* client)
{
MutexLocker locker(m_mutex);
size_t index = m_processors.find(processor);
if (index == notFound) {
ASSERT_NOT_REACHED();
return;
}
m_processors.remove(index);
if (m_processors.isEmpty())
client->setTraceEventCallback(0);
}
private:
TraceEventDispatcher() { }
static void dispatchEventOnAnyThread(char phase, const unsigned char*, const char* name, unsigned long long id,
int numArgs, const char* const* argNames, const unsigned char* argTypes, const unsigned long long* argValues,
unsigned char flags)
{
TraceEventDispatcher* self = instance();
Vector<RefPtr<TimelineTraceEventProcessor> > processors;
{
MutexLocker locker(self->m_mutex);
processors = self->m_processors;
}
for (int i = 0, size = processors.size(); i < size; ++i) {
processors[i]->processEventOnAnyThread(static_cast<TimelineTraceEventProcessor::TraceEventPhase>(phase),
name, id, numArgs, argNames, argTypes, argValues, flags);
}
}
Mutex m_mutex;
Vector<RefPtr<TimelineTraceEventProcessor> > m_processors;
};
} // namespce
TimelineRecordStack::TimelineRecordStack(WeakPtr<InspectorTimelineAgent> timelineAgent)
: m_timelineAgent(timelineAgent)
{
}
void TimelineRecordStack::addScopedRecord(PassRefPtr<JSONObject> 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<JSONObject> 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<JSONObject> 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_layerTreeId(m_timelineAgent.get()->layerTreeId())
, m_layerId(0)
, m_paintSetupStart(0)
, m_paintSetupEnd(0)
{
registerHandler(InstrumentationEvents::BeginFrame, TracePhaseInstant, &TimelineTraceEventProcessor::onBeginFrame);
registerHandler(InstrumentationEvents::UpdateLayer, TracePhaseBegin, &TimelineTraceEventProcessor::onUpdateLayerBegin);
registerHandler(InstrumentationEvents::UpdateLayer, TracePhaseEnd, &TimelineTraceEventProcessor::onUpdateLayerEnd);
registerHandler(InstrumentationEvents::PaintLayer, TracePhaseBegin, &TimelineTraceEventProcessor::onPaintLayerBegin);
registerHandler(InstrumentationEvents::PaintLayer, TracePhaseEnd, &TimelineTraceEventProcessor::onPaintLayerEnd);
registerHandler(InstrumentationEvents::PaintSetup, TracePhaseBegin, &TimelineTraceEventProcessor::onPaintSetupBegin);
registerHandler(InstrumentationEvents::PaintSetup, TracePhaseEnd, &TimelineTraceEventProcessor::onPaintSetupEnd);
registerHandler(InstrumentationEvents::RasterTask, TracePhaseBegin, &TimelineTraceEventProcessor::onRasterTaskBegin);
registerHandler(InstrumentationEvents::RasterTask, TracePhaseEnd, &TimelineTraceEventProcessor::onRasterTaskEnd);
registerHandler(InstrumentationEvents::ImageDecodeTask, TracePhaseBegin, &TimelineTraceEventProcessor::onImageDecodeTaskBegin);
registerHandler(InstrumentationEvents::ImageDecodeTask, TracePhaseEnd, &TimelineTraceEventProcessor::onImageDecodeTaskEnd);
registerHandler(InstrumentationEvents::Layer, TracePhaseDeleteObject, &TimelineTraceEventProcessor::onLayerDeleted);
registerHandler(InstrumentationEvents::Paint, TracePhaseInstant, &TimelineTraceEventProcessor::onPaint);
registerHandler(PlatformInstrumentation::ImageDecodeEvent, TracePhaseBegin, &TimelineTraceEventProcessor::onImageDecodeBegin);
registerHandler(PlatformInstrumentation::ImageDecodeEvent, TracePhaseEnd, &TimelineTraceEventProcessor::onImageDecodeEnd);
TraceEventDispatcher::instance()->addProcessor(this, m_inspectorClient);
}
TimelineTraceEventProcessor::~TimelineTraceEventProcessor()
{
}
void TimelineTraceEventProcessor::registerHandler(const char* name, TraceEventPhase phase, TraceEventHandler handler)
{
m_handlersByType.set(std::make_pair(name, phase), handler);
}
void TimelineTraceEventProcessor::shutdown()
{
TraceEventDispatcher::instance()->removeProcessor(this, m_inspectorClient);
}
size_t TimelineTraceEventProcessor::TraceEvent::findParameter(const char* name) const
{
for (int i = 0; i < m_argumentCount; ++i) {
if (!strcmp(name, m_argumentNames[i]))
return i;
}
return notFound;
}
const TimelineTraceEventProcessor::TraceValueUnion& TimelineTraceEventProcessor::TraceEvent::parameter(const char* name, TraceValueTypes expectedType) const
{
static TraceValueUnion missingValue;
size_t index = findParameter(name);
if (index == notFound || m_argumentTypes[index] != expectedType) {
ASSERT_NOT_REACHED();
return missingValue;
}
return *reinterpret_cast<const TraceValueUnion*>(m_argumentValues + index);
}
void TimelineTraceEventProcessor::processEventOnAnyThread(TraceEventPhase phase, const char* name, unsigned long long id,
int numArgs, const char* const* argNames, const unsigned char* argTypes, const unsigned long long* argValues,
unsigned char)
{
HandlersMap::iterator it = m_handlersByType.find(std::make_pair(name, phase));
if (it == m_handlersByType.end())
return;
TraceEvent event(WTF::monotonicallyIncreasingTime(), phase, name, id, currentThread(), numArgs, argNames, argTypes, argValues);
if (!isMainThread()) {
MutexLocker locker(m_backgroundEventsMutex);
m_backgroundEvents.append(event);
return;
}
(this->*(it->value))(event);
}
void TimelineTraceEventProcessor::onBeginFrame(const TraceEvent&)
{
processBackgroundEvents();
}
void TimelineTraceEventProcessor::onUpdateLayerBegin(const TraceEvent& event)
{
unsigned long long layerTreeId = event.asUInt(InstrumentationEventArguments::LayerTreeId);
if (layerTreeId != m_layerTreeId)
return;
m_layerId = event.asUInt(InstrumentationEventArguments::LayerId);
// We don't know the node yet. For content layers, the node will be updated
// by paint. For others, let it remain 0 -- we just need the fact that
// the layer belongs to the page (see cookie check).
m_layerToNodeMap.add(m_layerId, 0);
}
void TimelineTraceEventProcessor::onUpdateLayerEnd(const TraceEvent& event)
{
m_layerId = 0;
}
void TimelineTraceEventProcessor::onPaintLayerBegin(const TraceEvent& event)
{
m_layerId = event.asUInt(InstrumentationEventArguments::LayerId);
ASSERT(m_layerId);
ASSERT(!m_paintSetupStart);
}
void TimelineTraceEventProcessor::onPaintLayerEnd(const TraceEvent& event)
{
m_layerId = 0;
ASSERT(m_paintSetupStart);
}
void TimelineTraceEventProcessor::onPaintSetupBegin(const TraceEvent& event)
{
ASSERT(!m_paintSetupStart);
m_paintSetupStart = m_timeConverter.toProtocolTimestamp(event.timestamp());
}
void TimelineTraceEventProcessor::onPaintSetupEnd(const TraceEvent& event)
{
ASSERT(m_paintSetupStart);
m_paintSetupEnd = m_timeConverter.toProtocolTimestamp(event.timestamp());
}
void TimelineTraceEventProcessor::onRasterTaskBegin(const TraceEvent& event)
{
TimelineThreadState& state = threadState(event.threadIdentifier());
if (!maybeEnterLayerTask(event, state))
return;
unsigned long long layerId = event.asUInt(InstrumentationEventArguments::LayerId);
ASSERT(layerId);
RefPtr<JSONObject> record = createRecord(event, TimelineRecordType::Rasterize);
record->setObject("data", TimelineRecordFactory::createLayerData(m_layerToNodeMap.get(layerId)));
state.recordStack.addScopedRecord(record.release());
}
void TimelineTraceEventProcessor::onRasterTaskEnd(const TraceEvent& event)
{
TimelineThreadState& state = threadState(event.threadIdentifier());
if (!state.inKnownLayerTask)
return;
ASSERT(state.recordStack.isOpenRecordOfType(TimelineRecordType::Rasterize));
state.recordStack.closeScopedRecord(m_timeConverter.toProtocolTimestamp(event.timestamp()));
leaveLayerTask(state);
}
void TimelineTraceEventProcessor::onImageDecodeTaskBegin(const TraceEvent& event)
{
maybeEnterLayerTask(event, threadState(event.threadIdentifier()));
}
void TimelineTraceEventProcessor::onImageDecodeTaskEnd(const TraceEvent& event)
{
leaveLayerTask(threadState(event.threadIdentifier()));
}
bool TimelineTraceEventProcessor::maybeEnterLayerTask(const TraceEvent& event, TimelineThreadState& threadState)
{
unsigned long long layerId = event.asUInt(InstrumentationEventArguments::LayerId);
if (!m_layerToNodeMap.contains(layerId))
return false;
ASSERT(!threadState.inKnownLayerTask);
threadState.inKnownLayerTask = true;
return true;
}
void TimelineTraceEventProcessor::leaveLayerTask(TimelineThreadState& threadState)
{
threadState.inKnownLayerTask = false;
}
void TimelineTraceEventProcessor::onImageDecodeBegin(const TraceEvent& event)
{
TimelineThreadState& state = threadState(event.threadIdentifier());
if (!state.inKnownLayerTask)
return;
state.recordStack.addScopedRecord(createRecord(event, TimelineRecordType::DecodeImage));
}
void TimelineTraceEventProcessor::onImageDecodeEnd(const TraceEvent& event)
{
TimelineThreadState& state = threadState(event.threadIdentifier());
if (!state.inKnownLayerTask)
return;
ASSERT(state.recordStack.isOpenRecordOfType(TimelineRecordType::DecodeImage));
state.recordStack.closeScopedRecord(m_timeConverter.toProtocolTimestamp(event.timestamp()));
}
void TimelineTraceEventProcessor::onLayerDeleted(const TraceEvent& event)
{
unsigned long long id = event.id();
ASSERT(id);
processBackgroundEvents();
m_layerToNodeMap.remove(id);
}
void TimelineTraceEventProcessor::onPaint(const TraceEvent& event)
{
double paintSetupStart = m_paintSetupStart;
m_paintSetupStart = 0;
if (!m_layerId)
return;
unsigned long long pageId = event.asUInt(InstrumentationEventArguments::PageId);
if (pageId != m_pageId)
return;
long long nodeId = event.asInt(InstrumentationEventArguments::NodeId);
ASSERT(nodeId);
m_layerToNodeMap.set(m_layerId, nodeId);
InspectorTimelineAgent* timelineAgent = m_timelineAgent.get();
if (timelineAgent && paintSetupStart) {
RefPtr<JSONObject> paintSetupRecord = TimelineRecordFactory::createGenericRecord(paintSetupStart, 0, TimelineRecordType::PaintSetup);
paintSetupRecord->setNumber("endTime", m_paintSetupEnd);
paintSetupRecord->setObject("data", TimelineRecordFactory::createLayerData(nodeId));
timelineAgent->addRecordToTimeline(paintSetupRecord);
}
}
PassRefPtr<JSONObject> TimelineTraceEventProcessor::createRecord(const TraceEvent& event, const String& recordType, PassRefPtr<JSONObject> data)
{
double startTime = m_timeConverter.toProtocolTimestamp(event.timestamp());
RefPtr<JSONObject> record = TimelineRecordFactory::createBackgroundRecord(startTime, String::number(event.threadIdentifier()));
record->setString("type", recordType);
record->setObject("data", data ? data : JSONObject::create());
return record.release();
}
void TimelineTraceEventProcessor::processBackgroundEvents()
{
ASSERT(isMainThread());
Vector<TraceEvent> events;
{
MutexLocker locker(m_backgroundEventsMutex);
events.reserveCapacity(m_backgroundEvents.capacity());
m_backgroundEvents.swap(events);
}
for (size_t i = 0, size = events.size(); i < size; ++i) {
const TraceEvent& event = events[i];
HandlersMap::iterator it = m_handlersByType.find(std::make_pair(event.name(), event.phase()));
ASSERT(it != m_handlersByType.end() && it->value);
(this->*(it->value))(event);
}
}
} // namespace WebCore