collector.h
1 //
2 // Copyright 2018 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 
25 #ifndef PXR_BASE_TRACE_COLLECTOR_H
26 #define PXR_BASE_TRACE_COLLECTOR_H
27 
28 #include "pxr/pxr.h"
29 
30 #include "pxr/base/trace/api.h"
31 #include "pxr/base/trace/concurrentList.h"
32 #include "pxr/base/trace/collection.h"
33 #include "pxr/base/trace/event.h"
34 #include "pxr/base/trace/key.h"
35 #include "pxr/base/trace/threads.h"
36 
38 #include "pxr/base/tf/mallocTag.h"
39 
40 #include "pxr/base/tf/pyTracing.h"
41 
42 #include "pxr/base/tf/singleton.h"
43 #include "pxr/base/tf/refBase.h"
44 #include "pxr/base/tf/refPtr.h"
45 #include "pxr/base/tf/weakBase.h"
46 #include "pxr/base/tf/weakPtr.h"
47 
48 #include <atomic>
49 #include <string>
50 #include <vector>
51 
52 #include <tbb/spin_mutex.h>
53 
54 PXR_NAMESPACE_OPEN_SCOPE
55 
56 class TraceScopeHolder;
57 
60 
61 
69 class TraceCollector : public TfWeakBase {
70 public:
71 
72  TF_MALLOC_TAG_NEW("Trace", "TraceCollector");
73 
74  using This = TraceCollector;
75  using ThisPtr = TraceCollectorPtr;
76 
77  using TimeStamp = TraceEvent::TimeStamp;
78 
79  using Key = TraceDynamicKey;
80 
82  TRACE_API static TraceCollector& GetInstance() {
84  }
85 
86  TRACE_API ~TraceCollector();
87 
89  TRACE_API void SetEnabled(bool isEnabled);
90 
92  static bool IsEnabled() {
93  return (_isEnabled.load(std::memory_order_acquire) == 1);
94  }
95 
98  struct DefaultCategory {
100  static constexpr TraceCategoryId GetId() { return TraceCategory::Default;}
102  static bool IsEnabled() { return TraceCollector::IsEnabled(); }
103  };
104 
105 #ifdef PXR_PYTHON_SUPPORT_ENABLED
106  bool IsPythonTracingEnabled() const {
108  return _isPythonTracingEnabled.load(std::memory_order_acquire) != 0;
109  }
110 
112  TRACE_API void SetPythonTracingEnabled(bool enabled);
113 #endif // PXR_PYTHON_SUPPORT_ENABLED
114 
116  TRACE_API TimeStamp GetScopeOverhead() const;
117 
120  TRACE_API void Clear();
121 
124 
133  template <typename Category = DefaultCategory>
134  TimeStamp BeginEvent(const Key& key) {
135  if (ARCH_LIKELY(!Category::IsEnabled())) {
136  return 0;
137  }
138  return _BeginEvent(key, Category::GetId());
139  }
140 
146  template <typename Category = DefaultCategory>
147  void BeginEventAtTime(const Key& key, double ms) {
148  if (ARCH_LIKELY(!Category::IsEnabled())) {
149  return;
150  }
151  _BeginEventAtTime(key, ms, Category::GetId());
152  }
153 
162  template <typename Category = DefaultCategory>
163  TimeStamp EndEvent(const Key& key) {
164  if (ARCH_LIKELY(!Category::IsEnabled())) {
165  return 0;
166  }
167  return _EndEvent(key, Category::GetId());
168  }
169 
175  template <typename Category = DefaultCategory>
176  void EndEventAtTime(const Key& key, double ms) {
177  if (ARCH_LIKELY(!Category::IsEnabled())) {
178  return;
179  }
180  _EndEventAtTime(key, ms, Category::GetId());
181  }
182 
183 
187 
188  template <typename Category = DefaultCategory>
189  TimeStamp MarkerEvent(const Key& key) {
190  if (ARCH_LIKELY(!Category::IsEnabled())) {
191  return 0;
192  }
193  return _MarkerEvent(key, Category::GetId());
194  }
195 
201  template <typename Category = DefaultCategory>
202  void MarkerEventAtTime(const Key& key, double ms) {
203  if (ARCH_LIKELY(!Category::IsEnabled())) {
204  return;
205  }
206  _MarkerEventAtTime(key, ms, Category::GetId());
207  }
208 
214  template <typename Category = DefaultCategory>
215  void BeginScope(const TraceKey& _key) {
216  if (ARCH_LIKELY(!Category::IsEnabled()))
217  return;
218 
219  _BeginScope(_key, Category::GetId());
220  }
221 
227  template <typename Category, typename... Args>
229  const TraceKey& key, Args&&... args) {
230  static_assert( sizeof...(Args) %2 == 0,
231  "Data arguments must come in pairs");
232 
233  if (ARCH_LIKELY(!Category::IsEnabled()))
234  return;
235 
236  _PerThreadData *threadData = _GetThreadData();
237  threadData->BeginScope(key, Category::GetId());
238  _StoreDataRec(threadData, Category::GetId(), std::forward<Args>(args)...);
239  }
240 
245  template <typename... Args>
246  void BeginScope(const TraceKey& key, Args&&... args) {
247  static_assert( sizeof...(Args) %2 == 0,
248  "Data arguments must come in pairs");
249 
250  // Explicitly cast to TraceCategoryId so overload resolution choose the
251  // version with a category arguement.
252  BeginScope<DefaultCategory>(key,
253  std::forward<Args>(args)...);
254  }
255 
260  template <typename Category = DefaultCategory>
261  void EndScope(const TraceKey& key) {
262  if (ARCH_LIKELY(!Category::IsEnabled()))
263  return;
264 
265  _EndScope(key, Category::GetId());
266  }
267 
274  TRACE_API
275  static void
276  Scope(const TraceKey& key, TimeStamp start, TimeStamp stop) noexcept;
277 
284  template <typename Category = DefaultCategory>
285  void Scope(const TraceKey& key, TimeStamp start, TimeStamp stop) {
286  if (ARCH_LIKELY(!Category::IsEnabled()))
287  return;
288  _PerThreadData *threadData = _GetThreadData();
289  threadData->EmplaceEvent(
290  TraceEvent::Timespan, key, start, stop, Category::GetId());
291  }
292 
296  template <typename Category, typename... Args>
297  void ScopeArgs(Args&&... args) {
298  static_assert( sizeof...(Args) %2 == 0,
299  "Data arguments must come in pairs");
300 
301  if (ARCH_LIKELY(!Category::IsEnabled()))
302  return;
303 
304  _PerThreadData *threadData = _GetThreadData();
305  _StoreDataRec(threadData, Category::GetId(), std::forward<Args>(args)...);
306  }
307 
314  template <typename... Args>
315  void ScopeArgs(Args&&... args) {
316  static_assert( sizeof...(Args) %2 == 0,
317  "Data arguments must come in pairs");
318 
319  ScopeArgs<DefaultCategory>(std::forward<Args>(args)...);
320  }
321 
322 
329  template <typename Category = DefaultCategory>
330  void MarkerEventStatic(const TraceKey& key) {
331  if (ARCH_LIKELY(!Category::IsEnabled()))
332  return;
333 
334  _PerThreadData *threadData = _GetThreadData();
335  threadData->EmplaceEvent(
336  TraceEvent::Marker, key, Category::GetId());
337  }
338 
343  template <typename Category = DefaultCategory, typename T>
344  void StoreData(const TraceKey &key, const T& value) {
345  if (ARCH_UNLIKELY(Category::IsEnabled())) {
346  _StoreData(_GetThreadData(), key, Category::GetId(), value);
347  }
348  }
349 
351  template <typename Category = DefaultCategory>
352  void RecordCounterDelta(const TraceKey &key,
353  double delta) {
354  // Only record counter values if the collector is enabled.
355  if (ARCH_UNLIKELY(Category::IsEnabled())) {
356  _PerThreadData *threadData = _GetThreadData();
357  threadData->EmplaceEvent(
358  TraceEvent::CounterDelta, key, delta, Category::GetId());
359  }
360  }
361 
363  template <typename Category = DefaultCategory>
364  void RecordCounterDelta(const Key &key, double delta) {
365  if (ARCH_UNLIKELY(Category::IsEnabled())) {
366  _PerThreadData *threadData = _GetThreadData();
367  threadData->CounterDelta(key, delta, Category::GetId());
368  }
369  }
370 
372  template <typename Category = DefaultCategory>
373  void RecordCounterValue(const TraceKey &key, double value) {
374  // Only record counter values if the collector is enabled.
375  if (ARCH_UNLIKELY(Category::IsEnabled())) {
376  _PerThreadData *threadData = _GetThreadData();
377  threadData->EmplaceEvent(
378  TraceEvent::CounterValue, key, value, Category::GetId());
379  }
380  }
381 
384  template <typename Category = DefaultCategory>
385  void RecordCounterValue(const Key &key, double value) {
386 
387  if (ARCH_UNLIKELY(Category::IsEnabled())) {
388  _PerThreadData *threadData = _GetThreadData();
389  threadData->CounterValue(key, value, Category::GetId());
390  }
391  }
392 
394 
396  const std::string& GetLabel() {
397  return _label;
398  }
399 
404  TRACE_API void CreateCollection();
405 
406 private:
407 
408  TraceCollector();
409 
410  friend class TfSingleton<TraceCollector>;
411 
412  class _PerThreadData;
413 
414  // Return a pointer to existing per-thread data or create one if none
415  // exists.
416  TRACE_API _PerThreadData* _GetThreadData() noexcept;
417 
418  TRACE_API TimeStamp _BeginEvent(const Key& key, TraceCategoryId cat);
419 
420  TRACE_API void _BeginEventAtTime(
421  const Key& key, double ms, TraceCategoryId cat);
422 
423  TRACE_API TimeStamp _EndEvent(const Key& key, TraceCategoryId cat);
424 
425  TRACE_API void _EndEventAtTime(
426  const Key& key, double ms, TraceCategoryId cat);
427 
428  TRACE_API TimeStamp _MarkerEvent(const Key& key, TraceCategoryId cat);
429 
430  TRACE_API void _MarkerEventAtTime(
431  const Key& key, double ms, TraceCategoryId cat);
432 
433  // This is the fast execution path called from the TRACE_FUNCTION
434  // and TRACE_SCOPE macros
435  void _BeginScope(const TraceKey& key, TraceCategoryId cat)
436  {
437  // Note we're not calling _NewEvent, don't need to cache key
438  _PerThreadData *threadData = _GetThreadData();
439  threadData->BeginScope(key, cat);
440  }
441 
442  // This is the fast execution path called from the TRACE_FUNCTION
443  // and TRACE_SCOPE macros
444  TRACE_API void _EndScope(const TraceKey& key, TraceCategoryId cat);
445 
446  TRACE_API void _MeasureScopeOverhead();
447 
448 #ifdef PXR_PYTHON_SUPPORT_ENABLED
449  // Callback function registered as a python tracing function.
450  void _PyTracingCallback(const TfPyTraceInfo &info);
451 #endif // PXR_PYTHON_SUPPORT_ENABLED
452 
453  // Implementation for small data that can stored inlined with the event.
454  template <typename T,
455  typename std::enable_if<
456  sizeof(T) <= sizeof(uintptr_t)
457  && !std::is_pointer<T>::value , int>::type = 0>
458  void _StoreData(_PerThreadData* threadData, const TraceKey &key,
459  TraceCategoryId cat, const T& value) {
460  threadData->StoreData(key, value, cat);
461  }
462 
463  // Implementation for data that must be stored outside of the events.
464  template <typename T,
465  typename std::enable_if<
466  (sizeof(T) > sizeof(uintptr_t))
467  && !std::is_pointer<T>::value, int>::type = 0>
468  void _StoreData(_PerThreadData* threadData, const TraceKey &key,
469  TraceCategoryId cat, const T& value) {
470  threadData->StoreLargeData(key, value, cat);
471  }
472 
473  // Specialization for c string
474  void _StoreData(
475  _PerThreadData* threadData,
476  const TraceKey &key,
477  TraceCategoryId cat,
478  const char* value) {
479  threadData->StoreLargeData(key, value, cat);
480  }
481 
482  // Specialization for std::string
483  void _StoreData(
484  _PerThreadData* threadData,
485  const TraceKey &key,
486  TraceCategoryId cat,
487  const std::string& value) {
488  threadData->StoreLargeData(key, value.c_str(), cat);
489  }
490 
491  // Variadic version to store multiple data events in one function call.
492  template <typename K, typename T, typename... Args>
493  void _StoreDataRec(
494  _PerThreadData* threadData, TraceCategoryId cat, K&& key,
495  const T& value, Args&&... args) {
496  _StoreData(threadData, std::forward<K>(key), cat, value);
497  _StoreDataRec(threadData, cat, std::forward<Args>(args)...);
498  }
499 
500  // Base case to terminate template recursion
501  void _StoreDataRec(_PerThreadData* threadData, TraceCategoryId cat) {}
502 
503 
504  // Thread-local storage, accessed via _GetThreadData()
505  //
506  class _PerThreadData {
507  public:
508  using EventList = TraceCollection::EventList;
509 
510  _PerThreadData();
511  ~_PerThreadData();
512 
513  const TraceThreadId& GetThreadId() const {
514  return _threadIndex;
515  }
516  TimeStamp BeginEvent(const Key& key, TraceCategoryId cat);
517  TimeStamp EndEvent(const Key& key, TraceCategoryId cat);
518  TimeStamp MarkerEvent(const Key& key, TraceCategoryId cat);
519 
520  // Debug Methods
521  void BeginEventAtTime(
522  const Key& key, double ms, TraceCategoryId cat);
523  void EndEventAtTime(const Key& key, double ms, TraceCategoryId cat);
524  void MarkerEventAtTime(const Key& key, double ms, TraceCategoryId cat);
525 
526  void BeginScope(const TraceKey& key, TraceCategoryId cat) {
527  AtomicRef lock(_writing);
528  _BeginScope(key, cat);
529  }
530 
531  void EndScope(const TraceKey& key, TraceCategoryId cat) {
532  AtomicRef lock(_writing);
533  _EndScope(key, cat);
534  }
535 
536  TRACE_API void CounterDelta(
537  const Key&, double value, TraceCategoryId cat);
538 
539  TRACE_API void CounterValue(
540  const Key&, double value, TraceCategoryId cat);
541 
542  template <typename T>
543  void StoreData(
544  const TraceKey& key, const T& data, TraceCategoryId cat) {
545  AtomicRef lock(_writing);
546  _events.load(std::memory_order_acquire)->EmplaceBack(
547  TraceEvent::Data, key, data, cat);
548  }
549 
550  template <typename T>
551  void StoreLargeData(
552  const TraceKey& key, const T& data, TraceCategoryId cat) {
553  AtomicRef lock(_writing);
554  EventList* events = _events.load(std::memory_order_acquire);
555  const auto* cached = events->StoreData(data);
556  events->EmplaceBack(TraceEvent::Data, key, cached, cat);
557  }
558 
559  template <typename... Args>
560  void EmplaceEvent(Args&&... args) {
561  AtomicRef lock(_writing);
562  _events.load(std::memory_order_acquire)->EmplaceBack(
563  std::forward<Args>(args)...);
564  }
565 
566 #ifdef PXR_PYTHON_SUPPORT_ENABLED
567  void PushPyScope(const Key& key, bool enabled);
568  void PopPyScope(bool enabled);
569 #endif // PXR_PYTHON_SUPPORT_ENABLED
570 
571  // These methods can be called from threads at the same time as the
572  // other methods.
573  std::unique_ptr<EventList> GetCollectionData();
574  void Clear();
575 
576  private:
577  void _BeginScope(const TraceKey& key, TraceCategoryId cat) {
578  _events.load(std::memory_order_acquire)->EmplaceBack(
579  TraceEvent::Begin, key, cat);
580  }
581 
582  void _EndScope(const TraceKey& key, TraceCategoryId cat);
583 
584  // Flag to let other threads know that the list is being written to.
585  mutable std::atomic<bool> _writing;
586  std::atomic<EventList*> _events;
587 
588  class AtomicRef {
589  public:
590  AtomicRef(std::atomic<bool>& b) : _bool(b) {
591  _bool.store(true, std::memory_order_release);
592  }
593  ~AtomicRef() {
594  _bool.store(false, std::memory_order_release);
595  }
596  private:
597  std::atomic<bool>& _bool;
598  };
599 
600  // An integer that is unique for each thread launched by any
601  // threadDispatcher. Each time a thread is Start-ed it get's
602  // a new id.
603  //
604  TraceThreadId _threadIndex;
605 
606  // When auto-tracing python frames, this stores the stack of scopes.
607  struct PyScope {
608  Key key;
609  };
610  std::vector<PyScope> _pyScopes;
611  };
612 
613  TRACE_API static std::atomic<int> _isEnabled;
614 
615  // A list with one _PerThreadData per thread.
616  TraceConcurrentList<_PerThreadData> _allPerThreadData;
617 
618  std::string _label;
619 
620  TimeStamp _measuredScopeOverhead;
621 
622  std::atomic<int> _isPythonTracingEnabled;
623  TfPyTraceFnId _pyTraceFnId;
624 };
625 
626 TRACE_API_TEMPLATE_CLASS(TfSingleton<TraceCollector>);
627 
628 PXR_NAMESPACE_CLOSE_SCOPE
629 
630 #endif // PXR_BASE_TRACE_COLLECTOR_H
const std::string & GetLabel()
Return the label associated with this collector.
Definition: collector.h:396
static bool IsEnabled()
Returns the result of TraceCollector::IsEnabled.
Definition: collector.h:102
void StoreData(const TraceKey &key, const T &value)
Record a data event with the given key and value if Category is enabled.
Definition: collector.h:344
Manage a single instance of an object.
TRACE_API void CreateCollection()
Produces a TraceCollection from all the events that recorded in the collector and issues a TraceColle...
TRACE_API void SetEnabled(bool isEnabled)
Enables or disables collection of events for DefaultCategory.
Manage a single instance of an object (see.
Definition: singleton.h:119
void RecordCounterValue(const Key &key, double value)
Record a counter value for a name key and delta value if Category is enabled.
Definition: collector.h:385
void BeginEventAtTime(const Key &key, double ms)
Record a begin event with key at a specified time if Category is enabled.
Definition: collector.h:147
#define TF_DECLARE_WEAK_PTRS(type)
Define standard weak pointer types.
Definition: declarePtrs.h:62
Standard pointer typedefs.
void MarkerEventStatic(const TraceKey &key)
Record a scope event described by key that started at start if Category is enabled.
Definition: collector.h:330
void MarkerEventAtTime(const Key &key, double ms)
Record a marker event with key at a specified time if Category is enabled.
Definition: collector.h:202
TimeStamp EndEvent(const Key &key)
Record an end event with key if Category is enabled.
Definition: collector.h:163
This class represents an identifier for a thread.
Definition: threads.h:40
uint64_t TimeStamp
Time in "ticks".
Definition: event.h:50
void RecordCounterValue(const TraceKey &key, double value)
Record a counter value for a name key if Category is enabled.
Definition: collector.h:373
bool IsPythonTracingEnabled() const
Returns whether automatic tracing of all python scopes is enabled.
Definition: collector.h:107
Pointer storage with deletion detection.
#define TF_DECLARE_WEAK_AND_REF_PTRS(type)
Define standard weak, ref, and vector pointer types.
Definition: declarePtrs.h:89
static constexpr TraceCategoryId GetId()
Returns TraceCategory::Default.
Definition: collector.h:100
TRACE_API void Clear()
Clear all pending events from the collector.
This class stores data used to create dynamic keys which can be referenced in TraceEvent instances.
Definition: dynamicKey.h:42
TimeStamp BeginEvent(const Key &key)
Record a begin event with key if Category is enabled.
Definition: collector.h:134
void BeginScope(const TraceKey &key, Args &&... args)
Record a begin event for a scope described by key and store data arguments if Category is enabled.
Definition: collector.h:246
void Scope(const TraceKey &key, TimeStamp start, TimeStamp stop)
Record a scope event described by key that started at start if Category is enabled.
Definition: collector.h:285
Default Trace category which corresponds to events stored for TRACE_ macros.
Definition: collector.h:98
void EndEventAtTime(const Key &key, double ms)
Record an end event with key at a specified time if Category is enabled.
Definition: collector.h:176
void BeginScope(const TraceKey &key, Args &&... args)
Record a begin event for a scope described by key and a specified category and store data arguments i...
Definition: collector.h:228
void ScopeArgs(Args &&... args)
Record multiple data events with the default category if collection of events is enabled.
Definition: collector.h:315
TimeStamp MarkerEvent(const Key &key)
Record a marker event with key if Category is enabled.
Definition: collector.h:189
static TRACE_API TraceCollector & GetInstance()
Returns the singleton instance.
Definition: collector.h:82
This is a singleton class that records TraceEvent instances and populates TraceCollection instances.
Definition: collector.h:69
void EndScope(const TraceKey &key)
Record an end event described by key if Category is enabled.
Definition: collector.h:261
void RecordCounterDelta(const Key &key, double delta)
Record a counter delta for a name key if Category is enabled.
Definition: collector.h:364
Structure passed to python trace functions.
Definition: pyTracing.h:44
static T & GetInstance()
Return a reference to an object of type T, creating it if necessary.
Definition: singleton.h:134
void BeginScope(const TraceKey &_key)
Record a begin event for a scope described by key if Category is enabled.
Definition: collector.h:215
static TRACE_API void Scope(const TraceKey &key, TimeStamp start, TimeStamp stop) noexcept
Record a scope event described by key that started at start for the DefaultCategory.
This class represents an ordered collection of TraceEvents and the TraceDynamicKeys and data that the...
Definition: eventList.h:46
Reference counting.
static bool IsEnabled()
Returns whether collection of events is enabled for DefaultCategory.
Definition: collector.h:92
TRACE_API void SetPythonTracingEnabled(bool enabled)
Set whether automatic tracing of all python scopes is enabled.
TRACE_API TimeStamp GetScopeOverhead() const
Return the overhead cost to measure a scope.
uint32_t TraceCategoryId
Categories that a TraceReporter can use to filter events.
Definition: category.h:44
Enable a concrete base class for use with TfWeakPtr.
Definition: weakBase.h:141
void RecordCounterDelta(const TraceKey &key, double delta)
Record a counter delta for a name key if Category is enabled.
Definition: collector.h:352
void ScopeArgs(Args &&... args)
Record multiple data events with category cat if Category is enabled.
Definition: collector.h:297
A wrapper around a TraceStaticKeyData pointer that is stored in TraceEvent instances.
Definition: key.h:40