23#include <QThreadStorage>
25#include "moc_qgsruntimeprofiler.cpp"
27using namespace Qt::StringLiterals;
49 res = mParent->fullParentPath();
50 const QString parentName = mParent->data(
static_cast< int >(
CustomRole::Name ) ).toString();
51 const QString parentId = mParent->data(
static_cast< int >(
CustomRole::Id ) ).toString();
52 if ( !parentId.isEmpty() )
54 else if ( !parentName.isEmpty() )
79 return mParent ? ( mParent->elapsed() > 0 ? mParent->elapsed() : mParent->totalElapsedTimeForChildren( mGroup ) ) : 0;
89 Q_ASSERT( !
child->mParent );
90 child->mParent =
this;
92 mChildren.emplace_back( std::move(
child ) );
97 Q_ASSERT(
child->mParent ==
this );
98 const auto it = std::find_if( mChildren.begin(), mChildren.end(), [&](
const std::unique_ptr<QgsRuntimeProfilerNode> &p ) { return p.get() == child; } );
99 if ( it != mChildren.end() )
100 return std::distance( mChildren.begin(), it );
106 for (
auto &it : mChildren )
109 && ( ( !
id.isEmpty() && it->data(
static_cast< int >(
CustomRole::Id ) ) ==
id ) || (
id.isEmpty() && !name.isEmpty() && it->data(
static_cast< int >(
CustomRole::Name ) ).toString() == name ) ) )
117 Q_ASSERT(
static_cast< std::size_t
>( index ) < mChildren.size() );
118 return mChildren[index].get();
128 Q_ASSERT(
static_cast< std::size_t
>( index ) < mChildren.size() );
129 mChildren.erase( mChildren.begin() + index );
134 mProfileTime.restart();
139 mElapsed = mProfileTime.elapsed() / 1000.0;
155 for (
auto &it : mChildren )
158 total += it->elapsed();
175 static QThreadStorage<QgsRuntimeProfiler> sInstances;
178 if ( !qApp || profiler->thread() == qApp->thread() )
179 sMainProfiler = profiler;
181 if ( !profiler->mInitialized )
182 profiler->setupConnections();
201 return QStringList();
205 for (
int i = 0; i < parentNode->
childCount(); ++i )
216 auto node = std::make_unique< QgsRuntimeProfilerNode >( group, name,
id );
220 if ( !mCurrentStack[group].empty() )
224 const QModelIndex parentIndex = node2index(
parent );
225 beginInsertRows( parentIndex,
parent->childCount(),
parent->childCount() );
226 parent->addChild( std::move( node ) );
231 beginInsertRows( QModelIndex(), mRootNode->childCount(), mRootNode->childCount() );
232 mRootNode->addChild( std::move( node ) );
236 mCurrentStack[group].push( child );
239 if ( !mGroups.contains( group ) )
241 mGroups.insert( group );
248 if ( mCurrentStack[group].empty() )
252 mCurrentStack[group].pop();
255 const QModelIndex nodeIndex = node2index( node );
256 const QModelIndex col2Index =
index( nodeIndex.row(), 1, nodeIndex.parent() );
257 emit dataChanged( nodeIndex, nodeIndex );
258 emit dataChanged( col2Index, col2Index );
260 QModelIndex parentIndex = nodeIndex.parent();
261 while ( parentIndex.isValid() )
263 const QModelIndex parentCol2Index =
index( parentIndex.row(), 1, parentIndex.parent() );
264 emit dataChanged( parentIndex, parentIndex );
265 emit dataChanged( parentCol2Index, parentCol2Index );
266 parentIndex = parentIndex.parent();
280 auto node = std::make_unique< QgsRuntimeProfilerNode >( group, name,
id );
283 if ( !mCurrentStack[group].empty() )
287 const QModelIndex parentIndex = node2index(
parent );
288 beginInsertRows( parentIndex,
parent->childCount(),
parent->childCount() );
289 parent->addChild( std::move( node ) );
294 beginInsertRows( QModelIndex(), mRootNode->childCount(), mRootNode->childCount() );
295 mRootNode->addChild( std::move( node ) );
309 if ( !mGroups.contains( group ) )
311 mGroups.insert( group );
327 for (
int row = mRootNode->childCount() - 1; row >= 0; row-- )
331 beginRemoveRows( QModelIndex(), row, row );
332 mRootNode->removeChildAt( row );
341 return node->elapsed();
348 return !mCurrentStack.value( group ).empty();
353 if ( group ==
"startup"_L1 )
354 return tr(
"Startup" );
355 else if ( group ==
"projectload"_L1 )
356 return tr(
"Project Load" );
357 else if ( group ==
"rendering"_L1 )
358 return tr(
"Map Render" );
380 return QModelIndex();
384 return QModelIndex();
386 return createIndex( row, column, n->
childAt( row ) );
391 if ( !child.isValid() )
392 return QModelIndex();
396 return indexOfParentNode( n->parent() );
401 return QModelIndex();
414 switch (
index.column() )
417 return node->
data( role );
423 case Qt::DisplayRole:
424 case Qt::InitialSortOrderRole:
430 return node->
data( role );
440 case Qt::DisplayRole:
442 if ( orientation == Qt::Horizontal )
449 return tr(
"Time (seconds)" );
461 return QAbstractItemModel::headerData( section, orientation, role );
465void QgsRuntimeProfiler::otherProfilerStarted(
const QString &group,
const QStringList &path,
const QString &name,
const QString &
id )
468 for (
const QString &part : path )
473 child = parentNode->
child( group, part );
477 auto newChild = std::make_unique< QgsRuntimeProfilerNode >( group, part );
479 const QModelIndex parentIndex = node2index( parentNode );
482 parentNode->
addChild( std::move( newChild ) );
492 if ( parentNode->
child( group, name,
id ) )
495 const QModelIndex parentIndex = node2index( parentNode );
497 parentNode->
addChild( std::make_unique< QgsRuntimeProfilerNode >( group, name,
id ) );
500 if ( !mGroups.contains( group ) )
502 mGroups.insert( group );
507void QgsRuntimeProfiler::otherProfilerEnded(
const QString &group,
const QStringList &path,
const QString &name,
const QString &
id,
double elapsed )
509 QgsRuntimeProfilerNode *parentNode = mRootNode.get();
510 for (
const QString &part : path )
513 QgsRuntimeProfilerNode *child = parentNode->
child( group, QString(), part );
515 child = parentNode->
child( group, part );
519 auto newChild = std::make_unique< QgsRuntimeProfilerNode >( group, part );
521 const QModelIndex parentIndex = node2index( parentNode );
523 QgsRuntimeProfilerNode *next = newChild.get();
524 parentNode->
addChild( std::move( newChild ) );
534 QgsRuntimeProfilerNode *destNode = parentNode->
child( group, name,
id );
537 auto node = std::make_unique< QgsRuntimeProfilerNode >( group, name,
id );
538 destNode = node.get();
539 const QModelIndex parentIndex = node2index( parentNode );
541 parentNode->
addChild( std::move( node ) );
547 const QModelIndex nodeIndex = node2index( destNode );
548 const QModelIndex col2Index =
index( nodeIndex.row(), 1, nodeIndex.parent() );
549 emit dataChanged( nodeIndex, nodeIndex );
550 emit dataChanged( col2Index, col2Index );
552 QModelIndex parentIndex = nodeIndex.parent();
553 while ( parentIndex.isValid() )
555 const QModelIndex parentCol2Index =
index( parentIndex.row(), 1, parentIndex.parent() );
556 emit dataChanged( parentIndex, parentIndex );
557 emit dataChanged( parentCol2Index, parentCol2Index );
558 parentIndex = parentIndex.parent();
562void QgsRuntimeProfiler::setupConnections()
566 Q_ASSERT( sMainProfiler );
568 if ( sMainProfiler !=
this )
570 connect(
this, &QgsRuntimeProfiler::started, sMainProfiler, &QgsRuntimeProfiler::otherProfilerStarted );
571 connect(
this, &QgsRuntimeProfiler::ended, sMainProfiler, &QgsRuntimeProfiler::otherProfilerEnded );
577 const QStringList parts = path.split(
'/' );
578 QgsRuntimeProfilerNode *res = mRootNode.get();
579 for (
const QString &part : parts )
581 if ( part.isEmpty() )
585 QgsRuntimeProfilerNode *child = res->
child( group, QString(), part );
587 child = res->
child( group, part );
596QgsRuntimeProfilerNode *QgsRuntimeProfiler::pathToNode(
const QString &group,
const QStringList &path )
const
598 QgsRuntimeProfilerNode *res = mRootNode.get();
599 for (
const QString &part : path )
602 QgsRuntimeProfilerNode *child = res->
child( group, QString(), part );
604 child = res->
child( group, part );
615 if ( !node || !node->
parent() )
616 return QModelIndex();
618 const QModelIndex parentIndex = node2index( node->
parent() );
621 Q_ASSERT( row >= 0 );
622 return index( row, 0, parentIndex );
627 Q_ASSERT( parentNode );
629 QgsRuntimeProfilerNode *grandParentNode = parentNode->
parent();
630 if ( !grandParentNode )
631 return QModelIndex();
633 const int row = grandParentNode->
indexOf( parentNode );
634 Q_ASSERT( row >= 0 );
636 return createIndex( row, 0, parentNode );
641 if ( !
index.isValid() )
642 return mRootNode.get();
644 return reinterpret_cast<QgsRuntimeProfilerNode *
>(
index.internalPointer() );
647void QgsRuntimeProfiler::extractModelAsText( QStringList &lines,
const QString &group,
const QModelIndex &parent,
int level )
651 for (
int r = 0; r < rc; r++ )
658 for (
int c = 0;
c < cc;
c++ )
661 cells <<
data( cellIndex ).toString();
663 lines << u
"%1 %2"_s.arg( u
"-"_s.repeated( level + 1 ), cells.join(
": "_L1 ) );
664 extractModelAsText( lines, group, rowIndex, level + 1 );
671 for (
const QString &g : std::as_const( mGroups ) )
673 if ( !group.isEmpty() && g != group )
677 lines << ( !groupName.isEmpty() ? groupName : g );
678 extractModelAsText( lines, g );
680 return lines.join(
"\r\n"_L1 );
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