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