Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsruntimeprofiler.cpp
3 : : ---------------------
4 : : begin : June 2016
5 : : copyright : (C) 2016 by Nathan Woodrow
6 : : email : woodrow dot nathan at gmail dot com
7 : : ***************************************************************************
8 : : * *
9 : : * This program is free software; you can redistribute it and/or modify *
10 : : * it under the terms of the GNU General Public License as published by *
11 : : * the Free Software Foundation; either version 2 of the License, or *
12 : : * (at your option) any later version. *
13 : : * *
14 : : ***************************************************************************/
15 : : #include "qgsruntimeprofiler.h"
16 : : #include "qgslogger.h"
17 : : #include "qgis.h"
18 : : #include "qgsapplication.h"
19 : : #include <QSet>
20 : : #include <QThreadStorage>
21 : :
22 : : QgsRuntimeProfiler *QgsRuntimeProfiler::sMainProfiler = nullptr;
23 : :
24 : :
25 : : //
26 : : // QgsRuntimeProfilerNode
27 : : //
28 : :
29 : 981 : QgsRuntimeProfilerNode::QgsRuntimeProfilerNode( const QString &group, const QString &name )
30 : 981 : : mName( name )
31 : 981 : , mGroup( group )
32 : : {
33 : :
34 : 981 : }
35 : :
36 : 981 : QgsRuntimeProfilerNode::~QgsRuntimeProfilerNode() = default;
37 : :
38 : 6956 : QStringList QgsRuntimeProfilerNode::fullParentPath() const
39 : : {
40 : 6956 : QStringList res;
41 : 6956 : if ( mParent )
42 : : {
43 : 5004 : res = mParent->fullParentPath();
44 : 5004 : const QString parentName = mParent->data( Name ).toString();
45 : 5004 : if ( !parentName.isEmpty() )
46 : 3052 : res << parentName;
47 : 5004 : }
48 : 6956 : return res;
49 : 6956 : }
50 : :
51 : 6956 : QVariant QgsRuntimeProfilerNode::data( int role ) const
52 : : {
53 : 6956 : switch ( role )
54 : : {
55 : : case Qt::DisplayRole:
56 : : case Qt::ToolTipRole:
57 : : case Name:
58 : 5980 : return mName;
59 : :
60 : : case Group:
61 : 0 : return mGroup;
62 : :
63 : : case Elapsed:
64 : 976 : return mElapsed;
65 : :
66 : : case ParentElapsed:
67 : 0 : return mParent ? ( mParent->elapsed() > 0 ? mParent->elapsed() : mParent->totalElapsedTimeForChildren( mGroup ) ) : 0;
68 : : }
69 : 0 : return QVariant();
70 : 6956 : }
71 : :
72 : 976 : void QgsRuntimeProfilerNode::addChild( std::unique_ptr<QgsRuntimeProfilerNode> child )
73 : : {
74 : 976 : if ( !child )
75 : 0 : return;
76 : :
77 : : Q_ASSERT( !child->mParent );
78 : 976 : child->mParent = this;
79 : :
80 : 976 : mChildren.emplace_back( std::move( child ) );
81 : 976 : }
82 : :
83 : 7080 : int QgsRuntimeProfilerNode::indexOf( QgsRuntimeProfilerNode *child ) const
84 : : {
85 : : Q_ASSERT( child->mParent == this );
86 : 158719 : auto it = std::find_if( mChildren.begin(), mChildren.end(), [&]( const std::unique_ptr<QgsRuntimeProfilerNode> &p )
87 : : {
88 : 151639 : return p.get() == child;
89 : : } );
90 : 7080 : if ( it != mChildren.end() )
91 : 7080 : return std::distance( mChildren.begin(), it );
92 : 0 : return -1;
93 : 7080 : }
94 : :
95 : 0 : QgsRuntimeProfilerNode *QgsRuntimeProfilerNode::child( const QString &group, const QString &name )
96 : : {
97 : 0 : for ( auto &it : mChildren )
98 : : {
99 : 0 : if ( it->data( Group ).toString() == group && it->data( Name ).toString() == name )
100 : 0 : return it.get();
101 : : }
102 : 0 : return nullptr;
103 : 0 : }
104 : :
105 : 6530 : QgsRuntimeProfilerNode *QgsRuntimeProfilerNode::childAt( int index )
106 : : {
107 : : Q_ASSERT( static_cast< std::size_t >( index ) < mChildren.size() );
108 : 6530 : return mChildren[ index ].get();
109 : : }
110 : :
111 : 0 : void QgsRuntimeProfilerNode::clear()
112 : : {
113 : 0 : mChildren.clear();
114 : 0 : }
115 : :
116 : 0 : void QgsRuntimeProfilerNode::removeChildAt( int index )
117 : : {
118 : : Q_ASSERT( static_cast< std::size_t >( index ) < mChildren.size() );
119 : 0 : mChildren.erase( mChildren.begin() + index );
120 : 0 : }
121 : :
122 : 976 : void QgsRuntimeProfilerNode::start()
123 : : {
124 : 976 : mProfileTime.restart();
125 : 976 : }
126 : :
127 : 976 : void QgsRuntimeProfilerNode::stop()
128 : : {
129 : 976 : mElapsed = mProfileTime.elapsed() / 1000.0;
130 : 976 : }
131 : :
132 : 0 : void QgsRuntimeProfilerNode::setElapsed( double time )
133 : : {
134 : 0 : mElapsed = time;
135 : 0 : }
136 : :
137 : 0 : double QgsRuntimeProfilerNode::elapsed() const
138 : : {
139 : 0 : return mElapsed;
140 : : }
141 : :
142 : 0 : double QgsRuntimeProfilerNode::totalElapsedTimeForChildren( const QString &group ) const
143 : : {
144 : 0 : double total = 0;
145 : 0 : for ( auto &it : mChildren )
146 : : {
147 : 0 : if ( it->data( Group ).toString() == group )
148 : 0 : total += it->elapsed();
149 : : }
150 : 981 : return total;
151 : 0 : }
152 : 981 :
153 : : //
154 : : // QgsRuntimeProfiler
155 : : //
156 : :
157 : 5 : QgsRuntimeProfiler::QgsRuntimeProfiler()
158 : 5 : : mRootNode( std::make_unique< QgsRuntimeProfilerNode >( QString(), QString() ) )
159 : 10 : {
160 : :
161 : 5 : }
162 : :
163 : 10 : QgsRuntimeProfiler::~QgsRuntimeProfiler() = default;
164 : :
165 : 1803 : QgsRuntimeProfiler *QgsRuntimeProfiler::threadLocalInstance()
166 : : {
167 : 1803 : static QThreadStorage<QgsRuntimeProfiler> sInstances;
168 : 1803 : QgsRuntimeProfiler *profiler = &sInstances.localData();
169 : :
170 : 1803 : if ( !qApp || profiler->thread() == qApp->thread() )
171 : 1803 : sMainProfiler = profiler;
172 : :
173 : 1803 : if ( !profiler->mInitialized )
174 : 5 : profiler->setupConnections();
175 : :
176 : 1803 : return profiler;
177 : 0 : }
178 : :
179 : 0 : void QgsRuntimeProfiler::beginGroup( const QString &name )
180 : : {
181 : 0 : start( name );
182 : 0 : }
183 : :
184 : 0 : void QgsRuntimeProfiler::endGroup()
185 : : {
186 : 0 : end();
187 : 0 : }
188 : :
189 : 0 : QStringList QgsRuntimeProfiler::childGroups( const QString &parent, const QString &group ) const
190 : : {
191 : 0 : QgsRuntimeProfilerNode *parentNode = pathToNode( group, parent );
192 : 0 : if ( !parentNode )
193 : 0 : return QStringList();
194 : :
195 : 0 : QStringList res;
196 : 0 : res.reserve( parentNode->childCount() );
197 : 0 : for ( int i = 0; i < parentNode->childCount(); ++i )
198 : : {
199 : 0 : QgsRuntimeProfilerNode *child = parentNode->childAt( i );
200 : 0 : if ( child->data( QgsRuntimeProfilerNode::Group ).toString() == group )
201 : 0 : res << child->data( QgsRuntimeProfilerNode::Name ).toString();
202 : 0 : }
203 : 0 : return res;
204 : 0 : }
205 : :
206 : 976 : void QgsRuntimeProfiler::start( const QString &name, const QString &group )
207 : : {
208 : 976 : std::unique_ptr< QgsRuntimeProfilerNode > node = std::make_unique< QgsRuntimeProfilerNode >( group, name );
209 : 976 : node->start();
210 : :
211 : 976 : QgsRuntimeProfilerNode *child = node.get();
212 : 976 : if ( !mCurrentStack[ group ].empty() )
213 : : {
214 : 796 : QgsRuntimeProfilerNode *parent = mCurrentStack[group ].top();
215 : :
216 : 796 : const QModelIndex parentIndex = node2index( parent );
217 : 796 : beginInsertRows( parentIndex, parent->childCount(), parent->childCount() );
218 : 796 : parent->addChild( std::move( node ) );
219 : 796 : endInsertRows();
220 : 796 : }
221 : : else
222 : : {
223 : 180 : beginInsertRows( QModelIndex(), mRootNode->childCount(), mRootNode->childCount() );
224 : 180 : mRootNode->addChild( std::move( node ) );
225 : 180 : endInsertRows();
226 : : }
227 : :
228 : 976 : mCurrentStack[group].push( child );
229 : 976 : emit started( group, child->fullParentPath(), name );
230 : :
231 : 976 : if ( !mGroups.contains( group ) )
232 : : {
233 : 5 : mGroups.insert( group );
234 : 5 : emit groupAdded( group );
235 : 5 : }
236 : 976 : }
237 : :
238 : 976 : void QgsRuntimeProfiler::end( const QString &group )
239 : : {
240 : 976 : if ( mCurrentStack[group].empty() )
241 : 0 : return;
242 : :
243 : 976 : QgsRuntimeProfilerNode *node = mCurrentStack[group].top();
244 : 976 : mCurrentStack[group].pop();
245 : 976 : node->stop();
246 : :
247 : 976 : const QModelIndex nodeIndex = node2index( node );
248 : 976 : const QModelIndex col2Index = index( nodeIndex.row(), 1, nodeIndex.parent() );
249 : 976 : emit dataChanged( nodeIndex, nodeIndex );
250 : 976 : emit dataChanged( col2Index, col2Index );
251 : : // parent item has data changed too, cos the overall time elapsed will have changed!
252 : 976 : QModelIndex parentIndex = nodeIndex.parent();
253 : 2502 : while ( parentIndex.isValid() )
254 : : {
255 : 1526 : const QModelIndex parentCol2Index = index( parentIndex.row(), 1, parentIndex.parent() );
256 : 1526 : emit dataChanged( parentIndex, parentIndex );
257 : 1526 : emit dataChanged( parentCol2Index, parentCol2Index );
258 : 1526 : parentIndex = parentIndex.parent();
259 : : }
260 : :
261 : 976 : emit ended( group, node->fullParentPath(), node->data( QgsRuntimeProfilerNode::Name ).toString(), node->data( QgsRuntimeProfilerNode::Elapsed ).toDouble() );
262 : 976 : }
263 : :
264 : 0 : double QgsRuntimeProfiler::profileTime( const QString &name, const QString &group ) const
265 : : {
266 : 0 : QgsRuntimeProfilerNode *node = pathToNode( group, name );
267 : 0 : if ( !node )
268 : 0 : return 0;
269 : :
270 : 0 : return node->data( QgsRuntimeProfilerNode::Elapsed ).toDouble();
271 : 0 : }
272 : :
273 : 0 : void QgsRuntimeProfiler::clear( const QString &group )
274 : : {
275 : 0 : for ( int row = mRootNode->childCount() - 1; row >= 0; row-- )
276 : : {
277 : 0 : if ( mRootNode->childAt( row )->data( QgsRuntimeProfilerNode::Group ).toString() == group )
278 : : {
279 : 0 : beginRemoveRows( QModelIndex(), row, row );
280 : 0 : mRootNode->removeChildAt( row );
281 : 0 : endRemoveRows();
282 : 0 : }
283 : 0 : }
284 : 0 : }
285 : :
286 : 0 : double QgsRuntimeProfiler::totalTime( const QString &group )
287 : : {
288 : 0 : if ( QgsRuntimeProfilerNode *node = pathToNode( group, QString() ) )
289 : 5 : return node->elapsed();
290 : :
291 : 0 : return 0;
292 : 0 : }
293 : :
294 : 156 : bool QgsRuntimeProfiler::groupIsActive( const QString &group ) const
295 : : {
296 : 156 : return !mCurrentStack.value( group ).empty();
297 : 0 : }
298 : :
299 : 0 : QString QgsRuntimeProfiler::translateGroupName( const QString &group )
300 : : {
301 : 0 : if ( group == QLatin1String( "startup" ) )
302 : 0 : return tr( "Startup" );
303 : 0 : else if ( group == QLatin1String( "projectload" ) )
304 : 0 : return tr( "Project Load" );
305 : 0 : else if ( group == QLatin1String( "render" ) )
306 : 0 : return tr( "Map Render" );
307 : 0 : return QString();
308 : 0 : }
309 : :
310 : 7506 : int QgsRuntimeProfiler::rowCount( const QModelIndex &parent ) const
311 : : {
312 : 7506 : QgsRuntimeProfilerNode *n = index2node( parent );
313 : 7506 : if ( !n )
314 : 0 : return 0;
315 : :
316 : 7506 : return n->childCount();
317 : 7506 : }
318 : :
319 : 6530 : int QgsRuntimeProfiler::columnCount( const QModelIndex &parent ) const
320 : : {
321 : 6530 : Q_UNUSED( parent )
322 : 6530 : return 2;
323 : : }
324 : :
325 : 6530 : QModelIndex QgsRuntimeProfiler::index( int row, int column, const QModelIndex &parent ) const
326 : : {
327 : 13060 : if ( column < 0 || column >= columnCount( parent ) ||
328 : 6530 : row < 0 || row >= rowCount( parent ) )
329 : 0 : return QModelIndex();
330 : :
331 : 6530 : QgsRuntimeProfilerNode *n = index2node( parent );
332 : 6530 : if ( !n )
333 : 0 : return QModelIndex(); // have no children
334 : :
335 : 6530 : return createIndex( row, column, n->childAt( row ) );
336 : 6530 : }
337 : :
338 : 5004 : QModelIndex QgsRuntimeProfiler::parent( const QModelIndex &child ) const
339 : : {
340 : 5004 : if ( !child.isValid() )
341 : 0 : return QModelIndex();
342 : :
343 : 5004 : if ( QgsRuntimeProfilerNode *n = index2node( child ) )
344 : : {
345 : 5004 : return indexOfParentNode( n->parent() ); // must not be null
346 : : }
347 : : else
348 : : {
349 : : Q_ASSERT( false );
350 : 0 : return QModelIndex();
351 : : }
352 : 5004 : }
353 : :
354 : 0 : QVariant QgsRuntimeProfiler::data( const QModelIndex &index, int role ) const
355 : : {
356 : 0 : if ( !index.isValid() || index.column() > 2 )
357 : 0 : return QVariant();
358 : :
359 : 0 : QgsRuntimeProfilerNode *node = index2node( index );
360 : 0 : if ( !node )
361 : 0 : return QVariant();
362 : :
363 : 0 : switch ( index.column() )
364 : : {
365 : : case 0:
366 : 0 : return node->data( role );
367 : :
368 : : case 1:
369 : : {
370 : 0 : switch ( role )
371 : : {
372 : : case Qt::DisplayRole:
373 : : case Qt::InitialSortOrderRole:
374 : 0 : return node->data( QgsRuntimeProfilerNode::Elapsed );
375 : :
376 : : default:
377 : 0 : break;
378 : : }
379 : 0 : return node->data( role );
380 : : }
381 : : }
382 : 0 : return QVariant();
383 : 0 : }
384 : :
385 : 0 : QVariant QgsRuntimeProfiler::headerData( int section, Qt::Orientation orientation, int role ) const
386 : : {
387 : 0 : switch ( role )
388 : : {
389 : : case Qt::DisplayRole:
390 : : {
391 : 0 : if ( orientation == Qt::Horizontal )
392 : : {
393 : 0 : switch ( section )
394 : : {
395 : : case 0:
396 : 0 : return tr( "Task" );
397 : : case 1:
398 : 0 : return tr( "Time (seconds)" );
399 : : default:
400 : 0 : return QVariant();
401 : : }
402 : : }
403 : : else
404 : : {
405 : 0 : return QVariant();
406 : : }
407 : : }
408 : :
409 : : default:
410 : 0 : return QAbstractItemModel::headerData( section, orientation, role );
411 : : }
412 : 0 : }
413 : :
414 : 0 : void QgsRuntimeProfiler::otherProfilerStarted( const QString &group, const QStringList &path, const QString &name )
415 : : {
416 : 0 : QgsRuntimeProfilerNode *parentNode = mRootNode.get();
417 : 0 : for ( const QString &part : path )
418 : : {
419 : 0 : QgsRuntimeProfilerNode *child = parentNode->child( group, part );
420 : 0 : if ( !child )
421 : : {
422 : 0 : std::unique_ptr< QgsRuntimeProfilerNode > newChild = std::make_unique< QgsRuntimeProfilerNode >( group, part );
423 : :
424 : 0 : const QModelIndex parentIndex = node2index( parentNode );
425 : 0 : beginInsertRows( parentIndex, parentNode->childCount(), parentNode->childCount() );
426 : 0 : QgsRuntimeProfilerNode *next = newChild.get();
427 : 0 : parentNode->addChild( std::move( newChild ) );
428 : 0 : endInsertRows();
429 : 0 : parentNode = next;
430 : 0 : }
431 : : else
432 : : {
433 : 0 : parentNode = child;
434 : : }
435 : : }
436 : :
437 : 0 : if ( parentNode->child( group, name ) )
438 : 0 : return;
439 : :
440 : 0 : const QModelIndex parentIndex = node2index( parentNode );
441 : 0 : beginInsertRows( parentIndex, parentNode->childCount(), parentNode->childCount() );
442 : 0 : parentNode->addChild( std::make_unique< QgsRuntimeProfilerNode >( group, name ) );
443 : 0 : endInsertRows();
444 : :
445 : 0 : if ( !mGroups.contains( group ) )
446 : : {
447 : 0 : mGroups.insert( group );
448 : 0 : emit groupAdded( group );
449 : 0 : }
450 : 0 : }
451 : :
452 : 0 : void QgsRuntimeProfiler::otherProfilerEnded( const QString &group, const QStringList &path, const QString &name, double elapsed )
453 : : {
454 : 0 : QgsRuntimeProfilerNode *parentNode = mRootNode.get();
455 : 0 : for ( const QString &part : path )
456 : : {
457 : 0 : QgsRuntimeProfilerNode *child = parentNode->child( group, part );
458 : 0 : if ( !child )
459 : : {
460 : 0 : std::unique_ptr< QgsRuntimeProfilerNode > newChild = std::make_unique< QgsRuntimeProfilerNode >( group, part );
461 : :
462 : 0 : const QModelIndex parentIndex = node2index( parentNode );
463 : 0 : beginInsertRows( parentIndex, parentNode->childCount(), parentNode->childCount() );
464 : 0 : QgsRuntimeProfilerNode *next = newChild.get();
465 : 0 : parentNode->addChild( std::move( newChild ) );
466 : 0 : endInsertRows();
467 : 0 : parentNode = next;
468 : 0 : }
469 : : else
470 : : {
471 : 0 : parentNode = child;
472 : : }
473 : : }
474 : :
475 : 0 : QgsRuntimeProfilerNode *destNode = parentNode->child( group, name );
476 : 0 : if ( !destNode )
477 : : {
478 : 0 : std::unique_ptr< QgsRuntimeProfilerNode > node = std::make_unique< QgsRuntimeProfilerNode >( group, name );
479 : 0 : destNode = node.get();
480 : 0 : const QModelIndex parentIndex = node2index( parentNode );
481 : 0 : beginInsertRows( parentIndex, parentNode->childCount(), parentNode->childCount() );
482 : 0 : parentNode->addChild( std::move( node ) );
483 : 0 : endInsertRows();
484 : 0 : }
485 : :
486 : 0 : destNode->setElapsed( elapsed );
487 : :
488 : 0 : const QModelIndex nodeIndex = node2index( destNode );
489 : 0 : const QModelIndex col2Index = index( nodeIndex.row(), 1, nodeIndex.parent() );
490 : 0 : emit dataChanged( nodeIndex, nodeIndex );
491 : 0 : emit dataChanged( col2Index, col2Index );
492 : : // parent item has data changed too, cos the overall time elapsed will have changed!
493 : 0 : QModelIndex parentIndex = nodeIndex.parent();
494 : 0 : while ( parentIndex.isValid() )
495 : : {
496 : 0 : const QModelIndex parentCol2Index = index( parentIndex.row(), 1, parentIndex.parent() );
497 : 0 : emit dataChanged( parentIndex, parentIndex );
498 : 0 : emit dataChanged( parentCol2Index, parentCol2Index );
499 : 0 : parentIndex = parentIndex.parent();
500 : : }
501 : 0 : }
502 : :
503 : 5 : void QgsRuntimeProfiler::setupConnections()
504 : : {
505 : 5 : mInitialized = true;
506 : :
507 : : Q_ASSERT( sMainProfiler );
508 : :
509 : 5 : if ( sMainProfiler != this )
510 : : {
511 : 0 : connect( this, &QgsRuntimeProfiler::started, sMainProfiler, &QgsRuntimeProfiler::otherProfilerStarted );
512 : 0 : connect( this, &QgsRuntimeProfiler::ended, sMainProfiler, &QgsRuntimeProfiler::otherProfilerEnded );
513 : 0 : }
514 : 5 : }
515 : :
516 : 0 : QgsRuntimeProfilerNode *QgsRuntimeProfiler::pathToNode( const QString &group, const QString &path ) const
517 : : {
518 : 0 : QStringList parts = path.split( '/' );
519 : 0 : QgsRuntimeProfilerNode *res = mRootNode.get();
520 : 0 : for ( const QString &part : parts )
521 : : {
522 : 0 : if ( part.isEmpty() )
523 : 0 : continue;
524 : :
525 : 0 : res = res->child( group, part );
526 : 0 : if ( !res )
527 : 0 : break;
528 : : }
529 : 0 : return res;
530 : 0 : }
531 : :
532 : 0 : QgsRuntimeProfilerNode *QgsRuntimeProfiler::pathToNode( const QString &group, const QStringList &path ) const
533 : : {
534 : 0 : QgsRuntimeProfilerNode *res = mRootNode.get();
535 : 0 : for ( const QString &part : path )
536 : : {
537 : 0 : res = res->child( group, part );
538 : 0 : if ( !res )
539 : 0 : break;
540 : : }
541 : 0 : return res;
542 : : }
543 : :
544 : 5800 : QModelIndex QgsRuntimeProfiler::node2index( QgsRuntimeProfilerNode *node ) const
545 : : {
546 : 5800 : if ( !node || !node->parent() )
547 : 1772 : return QModelIndex(); // this is the only root item -> invalid index
548 : :
549 : 4028 : QModelIndex parentIndex = node2index( node->parent() );
550 : :
551 : 4028 : int row = node->parent()->indexOf( node );
552 : : Q_ASSERT( row >= 0 );
553 : 4028 : return index( row, 0, parentIndex );
554 : 5800 : }
555 : :
556 : 5004 : QModelIndex QgsRuntimeProfiler::indexOfParentNode( QgsRuntimeProfilerNode *parentNode ) const
557 : : {
558 : : Q_ASSERT( parentNode );
559 : :
560 : 5004 : QgsRuntimeProfilerNode *grandParentNode = parentNode->parent();
561 : 5004 : if ( !grandParentNode )
562 : 1952 : return QModelIndex(); // root node -> invalid index
563 : :
564 : 3052 : int row = grandParentNode->indexOf( parentNode );
565 : : Q_ASSERT( row >= 0 );
566 : :
567 : 3052 : return createIndex( row, 0, parentNode );
568 : 5004 : }
569 : :
570 : 19040 : QgsRuntimeProfilerNode *QgsRuntimeProfiler::index2node( const QModelIndex &index ) const
571 : : {
572 : 19040 : if ( !index.isValid() )
573 : 5676 : return mRootNode.get();
574 : :
575 : 13364 : return reinterpret_cast<QgsRuntimeProfilerNode *>( index.internalPointer() );
576 : 19040 : }
577 : :
578 : :
579 : : //
580 : : // QgsScopedRuntimeProfile
581 : : //
582 : :
583 : 821 : QgsScopedRuntimeProfile::QgsScopedRuntimeProfile( const QString &name, const QString &group )
584 : 821 : : mGroup( group )
585 : : {
586 : 821 : QgsApplication::profiler()->start( name, mGroup );
587 : 821 : }
588 : :
589 : 821 : QgsScopedRuntimeProfile::~QgsScopedRuntimeProfile()
590 : : {
591 : 821 : QgsApplication::profiler()->end( mGroup );
592 : 821 : }
593 : :
594 : 0 : void QgsScopedRuntimeProfile::switchTask( const QString &name )
595 : : {
596 : 0 : QgsApplication::profiler()->end( mGroup );
597 : 0 : QgsApplication::profiler()->start( name, mGroup );
598 : 0 : }
|