void QmlProfilerStatisticsModel::loadEvent(const QmlEvent &event, const QmlEventType &type) { if (!d->acceptedTypes.contains(type.rangeType())) return; switch (event.rangeStage()) { case RangeStart: // binding loop detection: check whether event is already in stack for (int ii = 1; ii < d->callStack.size(); ++ii) { if (d->callStack.at(ii).typeIndex() == event.typeIndex() && type.rangeType() != Javascript) { d->eventsInBindingLoop.insert(event.typeIndex()); break; } } d->callStack.push(event); break; case RangeEnd: { // update stats QmlEventStats *stats = &d->data[event.typeIndex()]; qint64 duration = event.timestamp() - d->callStack.top().timestamp(); stats->duration += duration; stats->durationSelf += duration; if (duration < stats->minTime) stats->minTime = duration; if (duration > stats->maxTime) stats->maxTime = duration; stats->calls++; // for median computing d->durations[event.typeIndex()].append(duration); // qml time computation if (event.timestamp() > d->lastEndTime) { // assume parent event if starts before last end d->qmlTime += duration; d->lastEndTime = event.timestamp(); } d->callStack.pop(); if (d->callStack.count() > 1) d->data[d->callStack.top().typeIndex()].durationSelf -= duration; break; } default: break; } if (!d->childrenModel.isNull()) d->childrenModel->loadEvent(event); if (!d->parentsModel.isNull()) d->parentsModel->loadEvent(event); }
void MemoryUsageModel::loadEvent(const QmlEvent &event, const QmlEventType &type) { if (type.message() != MemoryAllocation) { if (type.rangeType() != MaximumRangeType) { if (event.rangeStage() == RangeStart) m_rangeStack.push(RangeStackFrame(event.typeIndex(), event.timestamp())); else if (event.rangeStage() == RangeEnd) m_rangeStack.pop(); m_continuation = ContinueNothing; } return; } auto canContinue = [&](EventContinuation continuation) { QTC_ASSERT(continuation != ContinueNothing, return false); if ((m_continuation & continuation) == 0) return false; int currentIndex = (continuation == ContinueAllocation ? m_currentJSHeapIndex : m_currentUsageIndex); if (m_rangeStack.isEmpty()) { qint64 amount = event.number<qint64>(0); // outside of ranges show monotonous allocation or deallocation return (amount >= 0 && m_data[currentIndex].allocated >= 0) || (amount < 0 && m_data[currentIndex].deallocated > 0); } else { return m_data[currentIndex].typeId == m_rangeStack.top().originTypeIndex && m_rangeStack.top().startTime < startTime(currentIndex); } };
void FlameGraphModel::loadEvent(const QmlEvent &event, const QmlEventType &type) { Q_UNUSED(type); if (m_stackBottom.children.isEmpty()) beginResetModel(); const bool isCompiling = (type.rangeType() == Compiling); QStack<QmlEvent> &stack = isCompiling ? m_compileStack : m_callStack; FlameGraphData *&stackTop = isCompiling ? m_compileStackTop : m_callStackTop; const QmlEvent *potentialParent = &(stack.top()); if (type.message() == MemoryAllocation) { if (type.detailType() == HeapPage) return; // We're only interested in actual allocations, not heap pages being mmap'd qint64 amount = event.number<qint64>(0); if (amount < 0) return; // We're not interested in GC runs here for (FlameGraphData *data = stackTop; data; data = data->parent) { ++data->allocations; data->memory += amount; } } else if (event.rangeStage() == RangeEnd) { stackTop->duration += event.timestamp() - potentialParent->timestamp(); stack.pop(); stackTop = stackTop->parent; potentialParent = &(stack.top()); } else { QTC_ASSERT(event.rangeStage() == RangeStart, return); stack.push(event); stackTop = pushChild(stackTop, event); } }
bool MemoryUsageModel::accepted(const QmlEventType &type) const { return QmlProfilerTimelineModel::accepted(type) || type.rangeType() != MaximumRangeType; }
bool QmlProfilerTimelineModel::accepted(const QmlEventType &type) const { return (type.rangeType() == m_rangeType && type.message() == m_message); }