22#include <QThreadStorage>
24#include "moc_qgsruntimeprofiler.cpp"
48 res = mParent->fullParentPath();
49 const QString parentName = mParent->data(
static_cast< int >(
CustomRole::Name ) ).toString();
50 const QString parentId = mParent->data(
static_cast< int >(
CustomRole::Id ) ).toString();
51 if ( !parentId.isEmpty() )
53 else if ( !parentName.isEmpty() )
78 return mParent ? ( mParent->elapsed() > 0 ? mParent->elapsed() : mParent->totalElapsedTimeForChildren( mGroup ) ) : 0;
88 Q_ASSERT( !
child->mParent );
89 child->mParent =
this;
91 mChildren.emplace_back( std::move(
child ) );
96 Q_ASSERT(
child->mParent ==
this );
97 const auto it = std::find_if( mChildren.begin(), mChildren.end(), [&](
const std::unique_ptr<QgsRuntimeProfilerNode> &p )
99 return p.get() == child;
101 if ( it != mChildren.end() )
102 return std::distance( mChildren.begin(), it );
108 for (
auto &it : mChildren )
111 && ( ( !
id.isEmpty() && it->data(
static_cast< int >(
CustomRole::Id ) ) ==
id )
112 || (
id.isEmpty() && !name.isEmpty() && it->data(
static_cast< int >(
CustomRole::Name ) ).toString() == name ) ) )
120 Q_ASSERT(
static_cast< std::size_t
>( index ) < mChildren.size() );
121 return mChildren[ index ].get();
131 Q_ASSERT(
static_cast< std::size_t
>( index ) < mChildren.size() );
132 mChildren.erase( mChildren.begin() + index );
137 mProfileTime.restart();
142 mElapsed = mProfileTime.elapsed() / 1000.0;
158 for (
auto &it : mChildren )
161 total += it->elapsed();
180 static QThreadStorage<QgsRuntimeProfiler> sInstances;
183 if ( !qApp || profiler->thread() == qApp->thread() )
184 sMainProfiler = profiler;
186 if ( !profiler->mInitialized )
187 profiler->setupConnections();
206 return QStringList();
210 for (
int i = 0; i < parentNode->
childCount(); ++i )
221 auto node = std::make_unique< QgsRuntimeProfilerNode >( group, name,
id );
225 if ( !mCurrentStack[ group ].empty() )
229 const QModelIndex parentIndex = node2index(
parent );
230 beginInsertRows( parentIndex,
parent->childCount(),
parent->childCount() );
231 parent->addChild( std::move( node ) );
236 beginInsertRows( QModelIndex(), mRootNode->childCount(), mRootNode->childCount() );
237 mRootNode->addChild( std::move( node ) );
241 mCurrentStack[group].push( child );
244 if ( !mGroups.contains( group ) )
246 mGroups.insert( group );
253 if ( mCurrentStack[group].empty() )
257 mCurrentStack[group].pop();
260 const QModelIndex nodeIndex = node2index( node );
261 const QModelIndex col2Index =
index( nodeIndex.row(), 1, nodeIndex.parent() );
262 emit dataChanged( nodeIndex, nodeIndex );
263 emit dataChanged( col2Index, col2Index );
265 QModelIndex parentIndex = nodeIndex.parent();
266 while ( parentIndex.isValid() )
268 const QModelIndex parentCol2Index =
index( parentIndex.row(), 1, parentIndex.parent() );
269 emit dataChanged( parentIndex, parentIndex );
270 emit dataChanged( parentCol2Index, parentCol2Index );
271 parentIndex = parentIndex.parent();
279 auto node = std::make_unique< QgsRuntimeProfilerNode >( group, name,
id );
282 if ( !mCurrentStack[ group ].empty() )
286 const QModelIndex parentIndex = node2index(
parent );
287 beginInsertRows( parentIndex,
parent->childCount(),
parent->childCount() );
288 parent->addChild( std::move( node ) );
293 beginInsertRows( QModelIndex(), mRootNode->childCount(), mRootNode->childCount() );
294 mRootNode->addChild( std::move( node ) );
302 if ( !mGroups.contains( group ) )
304 mGroups.insert( group );
320 for (
int row = mRootNode->childCount() - 1; row >= 0; row-- )
324 beginRemoveRows( QModelIndex(), row, row );
325 mRootNode->removeChildAt( row );
334 return node->elapsed();
341 return !mCurrentStack.value( group ).empty();
346 if ( group == QLatin1String(
"startup" ) )
347 return tr(
"Startup" );
348 else if ( group == QLatin1String(
"projectload" ) )
349 return tr(
"Project Load" );
350 else if ( group == QLatin1String(
"rendering" ) )
351 return tr(
"Map Render" );
374 return QModelIndex();
378 return QModelIndex();
380 return createIndex( row, column, n->
childAt( row ) );
385 if ( !child.isValid() )
386 return QModelIndex();
390 return indexOfParentNode( n->parent() );
395 return QModelIndex();
408 switch (
index.column() )
411 return node->
data( role );
417 case Qt::DisplayRole:
418 case Qt::InitialSortOrderRole:
424 return node->
data( role );
434 case Qt::DisplayRole:
436 if ( orientation == Qt::Horizontal )
443 return tr(
"Time (seconds)" );
455 return QAbstractItemModel::headerData( section, orientation, role );
459void QgsRuntimeProfiler::otherProfilerStarted(
const QString &group,
const QStringList &path,
const QString &name,
const QString &
id )
462 for (
const QString &part : path )
467 child = parentNode->
child( group, part );
471 auto newChild = std::make_unique< QgsRuntimeProfilerNode >( group, part );
473 const QModelIndex parentIndex = node2index( parentNode );
476 parentNode->
addChild( std::move( newChild ) );
486 if ( parentNode->
child( group, name,
id ) )
489 const QModelIndex parentIndex = node2index( parentNode );
491 parentNode->
addChild( std::make_unique< QgsRuntimeProfilerNode >( group, name,
id ) );
494 if ( !mGroups.contains( group ) )
496 mGroups.insert( group );
501void QgsRuntimeProfiler::otherProfilerEnded(
const QString &group,
const QStringList &path,
const QString &name,
const QString &
id,
double elapsed )
503 QgsRuntimeProfilerNode *parentNode = mRootNode.get();
504 for (
const QString &part : path )
507 QgsRuntimeProfilerNode *child = parentNode->
child( group, QString(), part );
509 child = parentNode->
child( group, part );
513 auto newChild = std::make_unique< QgsRuntimeProfilerNode >( group, part );
515 const QModelIndex parentIndex = node2index( parentNode );
517 QgsRuntimeProfilerNode *next = newChild.get();
518 parentNode->
addChild( std::move( newChild ) );
528 QgsRuntimeProfilerNode *destNode = parentNode->
child( group, name,
id );
531 auto node = std::make_unique< QgsRuntimeProfilerNode >( group, name,
id );
532 destNode = node.get();
533 const QModelIndex parentIndex = node2index( parentNode );
535 parentNode->
addChild( std::move( node ) );
541 const QModelIndex nodeIndex = node2index( destNode );
542 const QModelIndex col2Index =
index( nodeIndex.row(), 1, nodeIndex.parent() );
543 emit dataChanged( nodeIndex, nodeIndex );
544 emit dataChanged( col2Index, col2Index );
546 QModelIndex parentIndex = nodeIndex.parent();
547 while ( parentIndex.isValid() )
549 const QModelIndex parentCol2Index =
index( parentIndex.row(), 1, parentIndex.parent() );
550 emit dataChanged( parentIndex, parentIndex );
551 emit dataChanged( parentCol2Index, parentCol2Index );
552 parentIndex = parentIndex.parent();
556void QgsRuntimeProfiler::setupConnections()
560 Q_ASSERT( sMainProfiler );
562 if ( sMainProfiler !=
this )
564 connect(
this, &QgsRuntimeProfiler::started, sMainProfiler, &QgsRuntimeProfiler::otherProfilerStarted );
565 connect(
this, &QgsRuntimeProfiler::ended, sMainProfiler, &QgsRuntimeProfiler::otherProfilerEnded );
571 const QStringList parts = path.split(
'/' );
572 QgsRuntimeProfilerNode *res = mRootNode.get();
573 for (
const QString &part : parts )
575 if ( part.isEmpty() )
579 QgsRuntimeProfilerNode *child = res->
child( group, QString(), part );
581 child = res->
child( group, part );
590QgsRuntimeProfilerNode *QgsRuntimeProfiler::pathToNode(
const QString &group,
const QStringList &path )
const
592 QgsRuntimeProfilerNode *res = mRootNode.get();
593 for (
const QString &part : path )
596 QgsRuntimeProfilerNode *child = res->
child( group, QString(), part );
598 child = res->
child( group, part );
609 if ( !node || !node->
parent() )
610 return QModelIndex();
612 const QModelIndex parentIndex = node2index( node->
parent() );
615 Q_ASSERT( row >= 0 );
616 return index( row, 0, parentIndex );
621 Q_ASSERT( parentNode );
623 QgsRuntimeProfilerNode *grandParentNode = parentNode->
parent();
624 if ( !grandParentNode )
625 return QModelIndex();
627 const int row = grandParentNode->
indexOf( parentNode );
628 Q_ASSERT( row >= 0 );
630 return createIndex( row, 0, parentNode );
635 if ( !
index.isValid() )
636 return mRootNode.get();
638 return reinterpret_cast<QgsRuntimeProfilerNode *
>(
index.internalPointer() );
641void QgsRuntimeProfiler::extractModelAsText( QStringList &lines,
const QString &group,
const QModelIndex &parent,
int level )
645 for (
int r = 0; r < rc; r++ )
652 for (
int c = 0;
c < cc;
c++ )
655 cells <<
data( cellIndex ).toString();
657 lines << QStringLiteral(
"%1 %2" ).arg( QStringLiteral(
"-" ).repeated( level + 1 ), cells.join( QLatin1String(
": " ) ) );
658 extractModelAsText( lines, group, rowIndex, level + 1 );
665 for (
const QString &g : std::as_const( mGroups ) )
667 if ( !group.isEmpty() && g != group )
671 lines << ( !groupName.isEmpty() ? groupName : g );
672 extractModelAsText( lines, g );
674 return lines.join( QLatin1String(
"\r\n" ) );
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
A node representing an entry in a QgsRuntimeProfiler.
void stop()
Stops the node's timer, recording the elapsed time automatically.
QgsRuntimeProfilerNode * child(const QString &group, const QString &name, const QString &id=QString())
Finds the child with matching group, name and id (since QGIS 3.34).
int indexOf(QgsRuntimeProfilerNode *child) const
Returns the index of the specified child node.
void clear()
Clears the node, removing all its children.
QgsRuntimeProfilerNode * childAt(int index)
Returns the child at the specified index.
double elapsed() const
Returns the node's elapsed time, in seconds.
void addChild(std::unique_ptr< QgsRuntimeProfilerNode > child)
Adds a child node to this node.
void removeChildAt(int index)
Removes and deletes the child at the specified index.
double totalElapsedTimeForChildren(const QString &group) const
Returns the total elapsed time in seconds for all children of this node with matching group.
@ Elapsed
Node elapsed time.
@ ParentElapsed
Total elapsed time for node's parent.
QgsRuntimeProfilerNode(const QString &group, const QString &name, const QString &id=QString())
Constructor for QgsRuntimeProfilerNode, with the specified group and name.
QgsRuntimeProfilerNode * parent()
Returns the node's parent node.
int childCount() const
Returns the number of child nodes owned by this node.
QStringList fullParentPath() const
Returns the full path to the node's parent.
void setElapsed(double time)
Manually sets the node's elapsed time, in seconds.
void start()
Starts the node timer.
QVariant data(int role=Qt::DisplayRole) const
Returns the node's data for the specified model role.
~QgsRuntimeProfilerNode()
Provides a method of recording run time profiles of operations, allowing easy recording of their over...
void groupAdded(const QString &group)
Emitted when a new group has started being profiled.
QModelIndex parent(const QModelIndex &child) const override
QString asText(const QString &group=QString())
Returns the model as a multi-line text string.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
double profileTime(const QString &name, const QString &group="startup") const
Returns the profile time for the specified name.
QgsRuntimeProfiler()
Constructor to create a new runtime profiler.
void start(const QString &name, const QString &group="startup", const QString &id=QString())
Start a profile event with the given name.
QStringList childGroups(const QString &parent=QString(), const QString &group="startup") const
Returns a list of all child groups with the specified parent.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
void end(const QString &group="startup")
End the current profile event.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
double totalTime(const QString &group="startup")
The current total time collected in the profiler.
bool groupIsActive(const QString &group) const
Returns true if the specified group is currently being logged, i.e.
void clear(const QString &group="startup")
clear Clear all profile data.
Q_DECL_DEPRECATED void beginGroup(const QString &name)
Begin the group for the profiler.
~QgsRuntimeProfiler() override
Q_DECL_DEPRECATED void endGroup()
End the current active group.
void record(const QString &name, double time, const QString &group="startup", const QString &id=QString())
Manually adds a profile event with the given name and total time (in seconds).
static QString translateGroupName(const QString &group)
Returns the translated name of a standard profile group.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
int columnCount(const QModelIndex &parent=QModelIndex()) const override
void switchTask(const QString &name)
Switches the current task managed by the scoped profile to a new task with the given name.
~QgsScopedRuntimeProfile()
Records the final runtime of the operation in the profiler instance.
QgsScopedRuntimeProfile(const QString &name, const QString &group="startup", const QString &id=QString())
Constructor for QgsScopedRuntimeProfile.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c