23#include <QThreadStorage>
25#include "moc_qgsruntimeprofiler.cpp"
27using namespace Qt::StringLiterals;
51 res = mParent->fullParentPath();
52 const QString parentName = mParent->data(
static_cast< int >(
CustomRole::Name ) ).toString();
53 const QString parentId = mParent->data(
static_cast< int >(
CustomRole::Id ) ).toString();
54 if ( !parentId.isEmpty() )
56 else if ( !parentName.isEmpty() )
81 return mParent ? ( mParent->elapsed() > 0 ? mParent->elapsed() : mParent->totalElapsedTimeForChildren( mGroup ) ) : 0;
91 Q_ASSERT( !
child->mParent );
92 child->mParent =
this;
94 mChildren.emplace_back( std::move(
child ) );
99 Q_ASSERT(
child->mParent ==
this );
100 const auto it = std::find_if( mChildren.begin(), mChildren.end(), [&](
const std::unique_ptr<QgsRuntimeProfilerNode> &p )
102 return p.get() == child;
104 if ( it != mChildren.end() )
105 return std::distance( mChildren.begin(), it );
111 for (
auto &it : mChildren )
114 && ( ( !
id.isEmpty() && it->data(
static_cast< int >(
CustomRole::Id ) ) ==
id )
115 || (
id.isEmpty() && !name.isEmpty() && it->data(
static_cast< int >(
CustomRole::Name ) ).toString() == name ) ) )
123 Q_ASSERT(
static_cast< std::size_t
>( index ) < mChildren.size() );
124 return mChildren[ index ].get();
134 Q_ASSERT(
static_cast< std::size_t
>( index ) < mChildren.size() );
135 mChildren.erase( mChildren.begin() + index );
140 mProfileTime.restart();
145 mElapsed = mProfileTime.elapsed() / 1000.0;
161 for (
auto &it : mChildren )
164 total += it->elapsed();
183 static QThreadStorage<QgsRuntimeProfiler> sInstances;
186 if ( !qApp || profiler->thread() == qApp->thread() )
187 sMainProfiler = profiler;
189 if ( !profiler->mInitialized )
190 profiler->setupConnections();
209 return QStringList();
213 for (
int i = 0; i < parentNode->
childCount(); ++i )
224 auto node = std::make_unique< QgsRuntimeProfilerNode >( group, name,
id );
228 if ( !mCurrentStack[ group ].empty() )
232 const QModelIndex parentIndex = node2index(
parent );
233 beginInsertRows( parentIndex,
parent->childCount(),
parent->childCount() );
234 parent->addChild( std::move( node ) );
239 beginInsertRows( QModelIndex(), mRootNode->childCount(), mRootNode->childCount() );
240 mRootNode->addChild( std::move( node ) );
244 mCurrentStack[group].push( child );
247 if ( !mGroups.contains( group ) )
249 mGroups.insert( group );
256 if ( mCurrentStack[group].empty() )
260 mCurrentStack[group].pop();
263 const QModelIndex nodeIndex = node2index( node );
264 const QModelIndex col2Index =
index( nodeIndex.row(), 1, nodeIndex.parent() );
265 emit dataChanged( nodeIndex, nodeIndex );
266 emit dataChanged( col2Index, col2Index );
268 QModelIndex parentIndex = nodeIndex.parent();
269 while ( parentIndex.isValid() )
271 const QModelIndex parentCol2Index =
index( parentIndex.row(), 1, parentIndex.parent() );
272 emit dataChanged( parentIndex, parentIndex );
273 emit dataChanged( parentCol2Index, parentCol2Index );
274 parentIndex = parentIndex.parent();
282 auto node = std::make_unique< QgsRuntimeProfilerNode >( group, name,
id );
285 if ( !mCurrentStack[ group ].empty() )
289 const QModelIndex parentIndex = node2index(
parent );
290 beginInsertRows( parentIndex,
parent->childCount(),
parent->childCount() );
291 parent->addChild( std::move( node ) );
296 beginInsertRows( QModelIndex(), mRootNode->childCount(), mRootNode->childCount() );
297 mRootNode->addChild( std::move( node ) );
305 if ( !mGroups.contains( group ) )
307 mGroups.insert( group );
323 for (
int row = mRootNode->childCount() - 1; row >= 0; row-- )
327 beginRemoveRows( QModelIndex(), row, row );
328 mRootNode->removeChildAt( row );
337 return node->elapsed();
344 return !mCurrentStack.value( group ).empty();
349 if ( group ==
"startup"_L1 )
350 return tr(
"Startup" );
351 else if ( group ==
"projectload"_L1 )
352 return tr(
"Project Load" );
353 else if ( group ==
"rendering"_L1 )
354 return tr(
"Map Render" );
377 return QModelIndex();
381 return QModelIndex();
383 return createIndex( row, column, n->
childAt( row ) );
388 if ( !child.isValid() )
389 return QModelIndex();
393 return indexOfParentNode( n->parent() );
398 return QModelIndex();
411 switch (
index.column() )
414 return node->
data( role );
420 case Qt::DisplayRole:
421 case Qt::InitialSortOrderRole:
427 return node->
data( role );
437 case Qt::DisplayRole:
439 if ( orientation == Qt::Horizontal )
446 return tr(
"Time (seconds)" );
458 return QAbstractItemModel::headerData( section, orientation, role );
462void QgsRuntimeProfiler::otherProfilerStarted(
const QString &group,
const QStringList &path,
const QString &name,
const QString &
id )
465 for (
const QString &part : path )
470 child = parentNode->
child( group, part );
474 auto newChild = std::make_unique< QgsRuntimeProfilerNode >( group, part );
476 const QModelIndex parentIndex = node2index( parentNode );
479 parentNode->
addChild( std::move( newChild ) );
489 if ( parentNode->
child( group, name,
id ) )
492 const QModelIndex parentIndex = node2index( parentNode );
494 parentNode->
addChild( std::make_unique< QgsRuntimeProfilerNode >( group, name,
id ) );
497 if ( !mGroups.contains( group ) )
499 mGroups.insert( group );
504void QgsRuntimeProfiler::otherProfilerEnded(
const QString &group,
const QStringList &path,
const QString &name,
const QString &
id,
double elapsed )
506 QgsRuntimeProfilerNode *parentNode = mRootNode.get();
507 for (
const QString &part : path )
510 QgsRuntimeProfilerNode *child = parentNode->
child( group, QString(), part );
512 child = parentNode->
child( group, part );
516 auto newChild = std::make_unique< QgsRuntimeProfilerNode >( group, part );
518 const QModelIndex parentIndex = node2index( parentNode );
520 QgsRuntimeProfilerNode *next = newChild.get();
521 parentNode->
addChild( std::move( newChild ) );
531 QgsRuntimeProfilerNode *destNode = parentNode->
child( group, name,
id );
534 auto node = std::make_unique< QgsRuntimeProfilerNode >( group, name,
id );
535 destNode = node.get();
536 const QModelIndex parentIndex = node2index( parentNode );
538 parentNode->
addChild( std::move( node ) );
544 const QModelIndex nodeIndex = node2index( destNode );
545 const QModelIndex col2Index =
index( nodeIndex.row(), 1, nodeIndex.parent() );
546 emit dataChanged( nodeIndex, nodeIndex );
547 emit dataChanged( col2Index, col2Index );
549 QModelIndex parentIndex = nodeIndex.parent();
550 while ( parentIndex.isValid() )
552 const QModelIndex parentCol2Index =
index( parentIndex.row(), 1, parentIndex.parent() );
553 emit dataChanged( parentIndex, parentIndex );
554 emit dataChanged( parentCol2Index, parentCol2Index );
555 parentIndex = parentIndex.parent();
559void QgsRuntimeProfiler::setupConnections()
563 Q_ASSERT( sMainProfiler );
565 if ( sMainProfiler !=
this )
567 connect(
this, &QgsRuntimeProfiler::started, sMainProfiler, &QgsRuntimeProfiler::otherProfilerStarted );
568 connect(
this, &QgsRuntimeProfiler::ended, sMainProfiler, &QgsRuntimeProfiler::otherProfilerEnded );
574 const QStringList parts = path.split(
'/' );
575 QgsRuntimeProfilerNode *res = mRootNode.get();
576 for (
const QString &part : parts )
578 if ( part.isEmpty() )
582 QgsRuntimeProfilerNode *child = res->
child( group, QString(), part );
584 child = res->
child( group, part );
593QgsRuntimeProfilerNode *QgsRuntimeProfiler::pathToNode(
const QString &group,
const QStringList &path )
const
595 QgsRuntimeProfilerNode *res = mRootNode.get();
596 for (
const QString &part : path )
599 QgsRuntimeProfilerNode *child = res->
child( group, QString(), part );
601 child = res->
child( group, part );
612 if ( !node || !node->
parent() )
613 return QModelIndex();
615 const QModelIndex parentIndex = node2index( node->
parent() );
618 Q_ASSERT( row >= 0 );
619 return index( row, 0, parentIndex );
624 Q_ASSERT( parentNode );
626 QgsRuntimeProfilerNode *grandParentNode = parentNode->
parent();
627 if ( !grandParentNode )
628 return QModelIndex();
630 const int row = grandParentNode->
indexOf( parentNode );
631 Q_ASSERT( row >= 0 );
633 return createIndex( row, 0, parentNode );
638 if ( !
index.isValid() )
639 return mRootNode.get();
641 return reinterpret_cast<QgsRuntimeProfilerNode *
>(
index.internalPointer() );
644void QgsRuntimeProfiler::extractModelAsText( QStringList &lines,
const QString &group,
const QModelIndex &parent,
int level )
648 for (
int r = 0; r < rc; r++ )
655 for (
int c = 0;
c < cc;
c++ )
658 cells <<
data( cellIndex ).toString();
660 lines << u
"%1 %2"_s.arg( u
"-"_s.repeated( level + 1 ), cells.join(
": "_L1 ) );
661 extractModelAsText( lines, group, rowIndex, level + 1 );
668 for (
const QString &g : std::as_const( mGroups ) )
670 if ( !group.isEmpty() && g != group )
674 lines << ( !groupName.isEmpty() ? groupName : g );
675 extractModelAsText( lines, g );
677 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