Overte C++ Documentation
Debug.h
1 //
2 // Debug.h
3 // libraries/shared/src
4 //
5 // Created by Zach Pomerantz on 4/12/16.
6 // Copyright 2016 High Fidelity, Inc.
7 //
8 // Debug-specific classes.
9 // To use without drastically increasing compilation time, #include <Debug.h> in your cpp files only.
10 //
11 // Distributed under the Apache License, Version 2.0.
12 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
13 //
14 
15 #ifndef hifi_Debug_h
16 #define hifi_Debug_h
17 
18 #ifdef DEBUG
19 
20 #include <atomic>
21 #include <mutex>
22 #include <QDebug>
23 
24 #include "SharedLogging.h"
25 
26 // Implement the streaming operator for std::string for ease-of-use.
27 static QDebug operator<<(QDebug debug, const std::string& s) {
28  QDebugStateSaver saver(debug);
29  debug.nospace() << s.c_str();
30  return debug;
31 }
32 
33 // A counter. Prints out counts for each key on destruction.
34 template <class K>
35 class Counter {
36  using Map = std::map<K, size_t>;
37 
38 public:
39  enum LogLevel {
40  // Only print counts for each count (e.g. "5 34 entries", meaning 34 keys have a count of 5).
41  SUMMARY = 0,
42  // Print SUMMARY and every key and its count.
43  DETAILED,
44  };
45 
46  Counter(const std::string& name, LogLevel logLevel = LogLevel::DETAILED) :
47  _logLevel{ logLevel } {
48  _name = name;
49  }
50  ~Counter() { log(); }
51 
52  // Increase the count for key (by inc).
53  void add(const K& key, size_t inc = 1);
54 
55  // Decrease the count for key (by dec).
56  void sub(const K& key, size_t dec = 1);
57 
58  // Log current counts (called on destruction).
59  void log();
60 
61 private:
62  std::string _name;
63  std::vector<std::string> _legend;
64  LogLevel _logLevel;
65 
66  std::mutex _mutex;
67  Map _map;
68 };
69 
70 // A tracker. Tracks keys state (as a size_t, mapped to std::string).
71 // Prints out counts for each state on destruction. Optionally prints out counts for duplicates and each key's state.
72 //
73 // K must have an implemented streaming operator for QDebug operator<<. (Done for std::string in this header.)
74 //
75 // Useful cases may be tracking pointers' ctor/dtor (K = size_t or K = uintptr_t), or tracking resource names (K = std::string).
76 template <class K>
77 class Tracker {
78  using Map = std::map<K, size_t>;
79  using DuplicateValueMap = std::map<size_t, size_t>;
80  using DuplicateMap = std::map<K, DuplicateValueMap>;
81 
82 public:
83  enum LogLevel {
84  // Only print counts for each state.
85  SUMMARY = 0,
86  // Print SUMMARY and a count of duplicate states (when a key is set to its current state).
87  DUPLICATES,
88  // Print DUPLICATES and the current state for each key not in its final state (the last state of the legend).
89  DETAILED,
90  };
91 
92  // Create a new tracker.
93  // legend: defines a mapping from state (size_t) to that state's name. The final state is assumed to be the last state in the legend.
94  // allowReuse: if true, keys in the last state may be set to a different state.
95  Tracker(const std::string& name, const std::vector<std::string>& legend, LogLevel logLevel = SUMMARY, bool allowReuse = true) :
96  _logLevel{ logLevel }, _allowReuse{ allowReuse }, _max{ legend.size() - 1 } {
97  _name = name;
98  _legend = legend;
99  }
100  ~Tracker() { log(); }
101 
102  // Set key to state.
103  // Fails (triggers an assertion) if (state < current state) (i.e. states are ordered),
104  // unless allowReuse is true and the current state is the final state.
105  void set(const K& key, const size_t& state);
106 
107  // Log current states (called on destruction).
108  void log();
109 
110 private:
111  std::string _name;
112  std::vector<std::string> _legend;
113  bool _allowReuse;
114  size_t _max;
115  LogLevel _logLevel;
116 
117  std::mutex _mutex;
118  Map _map;
119  DuplicateMap _duplicates;
120 };
121 
122 template<class K>
123 void Counter<K>::add(const K& k, size_t inc) {
124  std::lock_guard<std::mutex> lock(_mutex);
125 
126  auto& it = _map.find(k);
127 
128  if (it == _map.end()) {
129  // No entry for k; add it
130  _map.insert(std::pair<K, size_t>(k, inc));
131  } else {
132  // Entry for k; update it
133  it->second += inc;
134  }
135 }
136 
137 template<class K>
138 void Counter<K>::sub(const K& k, size_t dec) {
139  add(k, -dec);
140 }
141 
142 template <class K>
143 void Counter<K>::log() {
144  // Avoid logging nothing
145  if (!_map.size()) return;
146 
147  std::map<size_t, size_t> results;
148  for (const auto& entry : _map) {
149  if (!results.insert(std::pair<size_t, size_t>(entry.second, 1)).second) {
150  results.find(entry.second)->second++;
151  }
152  }
153 
154  qCDebug(shared) << "Counts for" << _name;
155  for (const auto& entry : results) {
156  qCDebug(shared) << entry.first << '\t' << entry.second << "entries";
157  }
158  if (_logLevel == LogLevel::SUMMARY) return;
159 
160  qCDebug(shared) << "Entries";
161  for (const auto& entry : _map) {
162  qCDebug(shared) << entry.first << '\t' << entry.second;
163  }
164  if (_logLevel == LogLevel::DETAILED) return;
165 }
166 
167 template <class K>
168 void Tracker<K>::set(const K& k, const size_t& v) {
169  std::lock_guard<std::mutex> lock(_mutex);
170 
171  auto& it = _map.find(k);
172 
173  if (it == _map.end()) {
174  // No entry for k; add it
175  _map.insert(std::pair<K, size_t>(k, v));
176  } else if (v > it->second) {
177  // Ordered entry for k; update it
178  it->second = v;
179  } else if (v == it->second) {
180  // Duplicate entry for k; log it
181  auto& dit = _duplicates.find(k);
182 
183  if (dit == _duplicates.end()) {
184  // No duplicate value map for k; add it
185  DuplicateValueMap duplicateValueMap{ std::pair<size_t, size_t>(v, 1) };
186  _duplicates.insert(std::pair<K, DuplicateValueMap>(k, duplicateValueMap));
187  } else {
188  // Duplicate value map for k; update it
189  auto& dvit = dit->second.find(v);
190 
191  if (dvit == dit->second.end()) {
192  // No duplicate entry for (k, v); add it
193  dit->second.insert(std::pair<size_t, size_t>(v, 1));
194  } else {
195  // Duplicate entry for (k, v); update it
196  dvit->second++;
197  }
198  }
199  } else { // if (v < it.second)
200  if (_allowReuse && it->second == _max) {
201  // Reusing an entry; update it
202  it->second = v;
203  } else {
204  // Unordered entry for k; dump log and fail
205  qCDebug(shared) << "Badly ordered entry detected:" <<
206  k << _legend.at(it->second).c_str() << "->" << _legend.at(v).c_str();
207  log();
208  assert(false);
209  }
210  }
211 }
212 
213 template <class K>
214 void Tracker<K>::log() {
215  // Avoid logging nothing
216  if (!_map.size()) return;
217 
218  std::vector<std::vector<K>> results(_max + 1);
219  for (const auto& entry : _map) {
220  results.at(entry.second).push_back(entry.first);
221  }
222 
223  qCDebug(shared) << "Summary of" << _name;
224  for (auto i = 0; i < results.size(); ++i) {
225  qCDebug(shared) << _legend.at(i) << '\t' << results[i].size() << "entries";
226  }
227  if (_logLevel == LogLevel::SUMMARY) return;
228 
229  size_t size = 0;
230  for (const auto& entry : _duplicates) {
231  size += entry.second.size();
232  }
233  qCDebug(shared) << "Duplicates" << size << "entries";
234  // TODO: Add more detail to duplicate logging
235  if (_logLevel <= LogLevel::DUPLICATES) return;
236 
237  qCDebug(shared) << "Entries";
238  // Don't log the terminal case
239  for (auto i = 0; i < _max; ++i) {
240  qCDebug(shared) << "----" << _legend.at(i) << "----";
241  for (const auto& entry : results[i]) {
242  qCDebug(shared) << "\t" << entry;
243  }
244  }
245  if (_logLevel <= LogLevel::DETAILED) return;
246 }
247 
248 #endif // DEBUG
249 
250 #endif // hifi_Debug_h
251