20#include <QThreadStorage>
46 const QString parentId = mParent->
data(
static_cast< int >(
CustomRole::Id ) ).toString();
47 if ( !parentId.isEmpty() )
49 else if ( !parentName.isEmpty() )
84 Q_ASSERT( !
child->mParent );
85 child->mParent =
this;
87 mChildren.emplace_back( std::move(
child ) );
92 Q_ASSERT(
child->mParent ==
this );
93 const auto it = std::find_if( mChildren.begin(), mChildren.end(), [&](
const std::unique_ptr<QgsRuntimeProfilerNode> &p )
95 return p.get() == child;
97 if ( it != mChildren.end() )
98 return std::distance( mChildren.begin(), it );
104 for (
auto &it : mChildren )
107 && ( ( !
id.isEmpty() && it->data(
static_cast< int >(
CustomRole::Id ) ) == id )
108 || (
id.isEmpty() && !name.isEmpty() && it->data(
static_cast< int >(
CustomRole::Name ) ).toString() == name ) ) )
116 Q_ASSERT(
static_cast< std::size_t
>( index ) < mChildren.size() );
117 return mChildren[ index ].get();
127 Q_ASSERT(
static_cast< std::size_t
>( index ) < mChildren.size() );
128 mChildren.erase( mChildren.begin() + index );
133 mProfileTime.restart();
138 mElapsed = mProfileTime.elapsed() / 1000.0;
154 for (
auto &it : mChildren )
157 total += it->elapsed();
176 static QThreadStorage<QgsRuntimeProfiler> sInstances;
179 if ( !qApp || profiler->thread() == qApp->thread() )
180 sMainProfiler = profiler;
182 if ( !profiler->mInitialized )
183 profiler->setupConnections();
202 return QStringList();
206 for (
int i = 0; i < parentNode->
childCount(); ++i )
217 std::unique_ptr< QgsRuntimeProfilerNode > node = std::make_unique< QgsRuntimeProfilerNode >( group, name,
id );
221 if ( !mCurrentStack[ group ].empty() )
225 const QModelIndex parentIndex = node2index(
parent );
226 beginInsertRows( parentIndex,
parent->childCount(),
parent->childCount() );
227 parent->addChild( std::move( node ) );
232 beginInsertRows( QModelIndex(), mRootNode->childCount(), mRootNode->childCount() );
233 mRootNode->addChild( std::move( node ) );
237 mCurrentStack[group].push( child );
240 if ( !mGroups.contains( group ) )
242 mGroups.insert( group );
249 if ( mCurrentStack[group].empty() )
253 mCurrentStack[group].pop();
256 const QModelIndex nodeIndex = node2index( node );
257 const QModelIndex col2Index =
index( nodeIndex.row(), 1, nodeIndex.parent() );
258 emit dataChanged( nodeIndex, nodeIndex );
259 emit dataChanged( col2Index, col2Index );
261 QModelIndex parentIndex = nodeIndex.parent();
262 while ( parentIndex.isValid() )
264 const QModelIndex parentCol2Index =
index( parentIndex.row(), 1, parentIndex.parent() );
265 emit dataChanged( parentIndex, parentIndex );
266 emit dataChanged( parentCol2Index, parentCol2Index );
267 parentIndex = parentIndex.parent();
275 std::unique_ptr< QgsRuntimeProfilerNode > node = std::make_unique< QgsRuntimeProfilerNode >( group, name,
id );
278 if ( !mCurrentStack[ group ].empty() )
282 const QModelIndex parentIndex = node2index(
parent );
283 beginInsertRows( parentIndex,
parent->childCount(),
parent->childCount() );
284 parent->addChild( std::move( node ) );
289 beginInsertRows( QModelIndex(), mRootNode->childCount(), mRootNode->childCount() );
290 mRootNode->addChild( std::move( node ) );
298 if ( !mGroups.contains( group ) )
300 mGroups.insert( group );
316 for (
int row = mRootNode->childCount() - 1; row >= 0; row-- )
320 beginRemoveRows( QModelIndex(), row, row );
321 mRootNode->removeChildAt( row );
330 return node->elapsed();
337 return !mCurrentStack.value( group ).empty();
342 if ( group == QLatin1String(
"startup" ) )
343 return tr(
"Startup" );
344 else if ( group == QLatin1String(
"projectload" ) )
345 return tr(
"Project Load" );
346 else if ( group == QLatin1String(
"rendering" ) )
347 return tr(
"Map Render" );
370 return QModelIndex();
374 return QModelIndex();
376 return createIndex( row, column, n->
childAt( row ) );
381 if ( !child.isValid() )
382 return QModelIndex();
386 return indexOfParentNode( n->parent() );
391 return QModelIndex();
404 switch (
index.column() )
407 return node->
data( role );
413 case Qt::DisplayRole:
414 case Qt::InitialSortOrderRole:
420 return node->
data( role );
430 case Qt::DisplayRole:
432 if ( orientation == Qt::Horizontal )
439 return tr(
"Time (seconds)" );
451 return QAbstractItemModel::headerData( section, orientation, role );
455void QgsRuntimeProfiler::otherProfilerStarted(
const QString &group,
const QStringList &path,
const QString &name,
const QString &
id )
458 for (
const QString &part : path )
463 child = parentNode->
child( group, part );
467 std::unique_ptr< QgsRuntimeProfilerNode > newChild = std::make_unique< QgsRuntimeProfilerNode >( group, part );
469 const QModelIndex parentIndex = node2index( parentNode );
472 parentNode->
addChild( std::move( newChild ) );
482 if ( parentNode->
child( group, name,
id ) )
485 const QModelIndex parentIndex = node2index( parentNode );
487 parentNode->
addChild( std::make_unique< QgsRuntimeProfilerNode >( group, name,
id ) );
490 if ( !mGroups.contains( group ) )
492 mGroups.insert( group );
497void QgsRuntimeProfiler::otherProfilerEnded(
const QString &group,
const QStringList &path,
const QString &name,
const QString &
id,
double elapsed )
500 for (
const QString &part : path )
505 child = parentNode->
child( group, part );
509 std::unique_ptr< QgsRuntimeProfilerNode > newChild = std::make_unique< QgsRuntimeProfilerNode >( group, part );
511 const QModelIndex parentIndex = node2index( parentNode );
514 parentNode->
addChild( std::move( newChild ) );
527 std::unique_ptr< QgsRuntimeProfilerNode > node = std::make_unique< QgsRuntimeProfilerNode >( group, name,
id );
528 destNode = node.get();
529 const QModelIndex parentIndex = node2index( parentNode );
531 parentNode->
addChild( std::move( node ) );
537 const QModelIndex nodeIndex = node2index( destNode );
538 const QModelIndex col2Index =
index( nodeIndex.row(), 1, nodeIndex.parent() );
539 emit dataChanged( nodeIndex, nodeIndex );
540 emit dataChanged( col2Index, col2Index );
542 QModelIndex parentIndex = nodeIndex.parent();
543 while ( parentIndex.isValid() )
545 const QModelIndex parentCol2Index =
index( parentIndex.row(), 1, parentIndex.parent() );
546 emit dataChanged( parentIndex, parentIndex );
547 emit dataChanged( parentCol2Index, parentCol2Index );
548 parentIndex = parentIndex.parent();
552void QgsRuntimeProfiler::setupConnections()
556 Q_ASSERT( sMainProfiler );
558 if ( sMainProfiler !=
this )
560 connect(
this, &QgsRuntimeProfiler::started, sMainProfiler, &QgsRuntimeProfiler::otherProfilerStarted );
561 connect(
this, &QgsRuntimeProfiler::ended, sMainProfiler, &QgsRuntimeProfiler::otherProfilerEnded );
567 const QStringList parts = path.split(
'/' );
569 for (
const QString &part : parts )
571 if ( part.isEmpty() )
577 child = res->
child( group, part );
586QgsRuntimeProfilerNode *QgsRuntimeProfiler::pathToNode(
const QString &group,
const QStringList &path )
const
589 for (
const QString &part : path )
594 child = res->
child( group, part );
605 if ( !node || !node->
parent() )
606 return QModelIndex();
608 const QModelIndex parentIndex = node2index( node->
parent() );
611 Q_ASSERT( row >= 0 );
612 return index( row, 0, parentIndex );
617 Q_ASSERT( parentNode );
620 if ( !grandParentNode )
621 return QModelIndex();
623 const int row = grandParentNode->
indexOf( parentNode );
624 Q_ASSERT( row >= 0 );
626 return createIndex( row, 0, parentNode );
631 if ( !
index.isValid() )
632 return mRootNode.get();
637void QgsRuntimeProfiler::extractModelAsText( QStringList &lines,
const QString &group,
const QModelIndex &parent,
int level )
641 for (
int r = 0; r < rc; r++ )
648 for (
int c = 0;
c < cc;
c++ )
651 cells <<
data( cellIndex ).toString();
653 lines << QStringLiteral(
"%1 %2" ).arg( QStringLiteral(
"-" ).repeated( level + 1 ), cells.join( QLatin1String(
": " ) ) );
654 extractModelAsText( lines, group, rowIndex, level + 1 );
661 for (
const QString &g : std::as_const( mGroups ) )
663 if ( !group.isEmpty() && g != group )
667 lines << ( !groupName.isEmpty() ? groupName : g );
668 extractModelAsText( lines, g );
670 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