//// Debug.h// libraries/shared/src//// Created by Zach Pomerantz on 4/12/16.// Copyright 2016 High Fidelity, Inc.//// Debug-specific classes.// To use without drastically increasing compilation time, #include <Debug.h> in your cpp files only.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #ifndef hifi_Debug_h#define hifi_Debug_h #ifdef DEBUG #include <atomic>#include <mutex>#include <QDebug> #include "SharedLogging.h" // Implement the streaming operator for std::string for ease-of-use.static QDebug operator<<(QDebug debug, const std::string& s) { QDebugStateSaver saver(debug); debug.nospace() << s.c_str(); return debug;} // A counter. Prints out counts for each key on destruction.template <class K>class Counter { using Map = std::map<K, size_t>; public: enum LogLevel { // Only print counts for each count (e.g. "5 34 entries", meaning 34 keys have a count of 5). SUMMARY = 0, // Print SUMMARY and every key and its count. DETAILED, }; Counter(const std::string& name, LogLevel logLevel = LogLevel::DETAILED) : _logLevel{ logLevel } { _name = name; } ~Counter() { log(); } // Increase the count for key (by inc). void add(const K& key, size_t inc = 1); // Decrease the count for key (by dec). void sub(const K& key, size_t dec = 1); // Log current counts (called on destruction). void log(); private: std::string _name; std::vector<std::string> _legend; LogLevel _logLevel; std::mutex _mutex; Map _map;}; // A tracker. Tracks keys state (as a size_t, mapped to std::string).// Prints out counts for each state on destruction. Optionally prints out counts for duplicates and each key's state.//// K must have an implemented streaming operator for QDebug operator<<. (Done for std::string in this header.)//// Useful cases may be tracking pointers' ctor/dtor (K = size_t or K = uintptr_t), or tracking resource names (K = std::string).template <class K>class Tracker { using Map = std::map<K, size_t>; using DuplicateValueMap = std::map<size_t, size_t>; using DuplicateMap = std::map<K, DuplicateValueMap>; public: enum LogLevel { // Only print counts for each state. SUMMARY = 0, // Print SUMMARY and a count of duplicate states (when a key is set to its current state). DUPLICATES, // Print DUPLICATES and the current state for each key not in its final state (the last state of the legend). DETAILED, }; // Create a new tracker. // 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. // allowReuse: if true, keys in the last state may be set to a different state. Tracker(const std::string& name, const std::vector<std::string>& legend, LogLevel logLevel = SUMMARY, bool allowReuse = true) : _logLevel{ logLevel }, _allowReuse{ allowReuse }, _max{ legend.size() - 1 } { _name = name; _legend = legend; } ~Tracker() { log(); } // Set key to state. // Fails (triggers an assertion) if (state < current state) (i.e. states are ordered), // unless allowReuse is true and the current state is the final state. void set(const K& key, const size_t& state); // Log current states (called on destruction). void log(); private: std::string _name; std::vector<std::string> _legend; bool _allowReuse; size_t _max; LogLevel _logLevel; std::mutex _mutex; Map _map; DuplicateMap _duplicates;}; template<class K>void Counter<K>::add(const K& k, size_t inc) { std::lock_guard<std::mutex> lock(_mutex); auto& it = _map.find(k); if (it == _map.end()) { // No entry for k; add it _map.insert(std::pair<K, size_t>(k, inc)); } else { // Entry for k; update it it->second += inc; }} template<class K>void Counter<K>::sub(const K& k, size_t dec) { add(k, -dec);} template <class K>void Counter<K>::log() { // Avoid logging nothing if (!_map.size()) return; std::map<size_t, size_t> results; for (const auto& entry : _map) { if (!results.insert(std::pair<size_t, size_t>(entry.second, 1)).second) { results.find(entry.second)->second++; } } qCDebug(shared) << "Counts for" << _name; for (const auto& entry : results) { qCDebug(shared) << entry.first << '\t' << entry.second << "entries"; } if (_logLevel == LogLevel::SUMMARY) return; qCDebug(shared) << "Entries"; for (const auto& entry : _map) { qCDebug(shared) << entry.first << '\t' << entry.second; } if (_logLevel == LogLevel::DETAILED) return;} template <class K>void Tracker<K>::set(const K& k, const size_t& v) { std::lock_guard<std::mutex> lock(_mutex); auto& it = _map.find(k); if (it == _map.end()) { // No entry for k; add it _map.insert(std::pair<K, size_t>(k, v)); } else if (v > it->second) { // Ordered entry for k; update it it->second = v; } else if (v == it->second) { // Duplicate entry for k; log it auto& dit = _duplicates.find(k); if (dit == _duplicates.end()) { // No duplicate value map for k; add it DuplicateValueMap duplicateValueMap{ std::pair<size_t, size_t>(v, 1) }; _duplicates.insert(std::pair<K, DuplicateValueMap>(k, duplicateValueMap)); } else { // Duplicate value map for k; update it auto& dvit = dit->second.find(v); if (dvit == dit->second.end()) { // No duplicate entry for (k, v); add it dit->second.insert(std::pair<size_t, size_t>(v, 1)); } else { // Duplicate entry for (k, v); update it dvit->second++; } } } else { // if (v < it.second) if (_allowReuse && it->second == _max) { // Reusing an entry; update it it->second = v; } else { // Unordered entry for k; dump log and fail qCDebug(shared) << "Badly ordered entry detected:" << k << _legend.at(it->second).c_str() << "->" << _legend.at(v).c_str(); log(); assert(false); } }} template <class K>void Tracker<K>::log() { // Avoid logging nothing if (!_map.size()) return; std::vector<std::vector<K>> results(_max + 1); for (const auto& entry : _map) { results.at(entry.second).push_back(entry.first); } qCDebug(shared) << "Summary of" << _name; for (auto i = 0; i < results.size(); ++i) { qCDebug(shared) << _legend.at(i) << '\t' << results[i].size() << "entries"; } if (_logLevel == LogLevel::SUMMARY) return; size_t size = 0; for (const auto& entry : _duplicates) { size += entry.second.size(); } qCDebug(shared) << "Duplicates" << size << "entries"; // TODO: Add more detail to duplicate logging if (_logLevel <= LogLevel::DUPLICATES) return; qCDebug(shared) << "Entries"; // Don't log the terminal case for (auto i = 0; i < _max; ++i) { qCDebug(shared) << "----" << _legend.at(i) << "----"; for (const auto& entry : results[i]) { qCDebug(shared) << "\t" << entry; } } if (_logLevel <= LogLevel::DETAILED) return;} #endif // DEBUG #endif // hifi_Debug_h
//
// Debug.h
// libraries/shared/src
//
// Created by Zach Pomerantz on 4/12/16.
// Copyright 2016 High Fidelity, Inc.
//
// Debug-specific classes.
// To use without drastically increasing compilation time, #include <Debug.h> in your cpp files only.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Debug_h
#define hifi_Debug_h
#ifdef DEBUG
#include <atomic>
#include <mutex>
#include <QDebug>
#include "SharedLogging.h"
// Implement the streaming operator for std::string for ease-of-use.
static QDebug operator<<(QDebug debug, const std::string& s) {
QDebugStateSaver saver(debug);
debug.nospace() << s.c_str();
return debug;
}
// A counter. Prints out counts for each key on destruction.
template <class K>
class Counter {
using Map = std::map<K, size_t>;
public:
enum LogLevel {
// Only print counts for each count (e.g. "5 34 entries", meaning 34 keys have a count of 5).
SUMMARY = 0,
// Print SUMMARY and every key and its count.
DETAILED,
};
Counter(const std::string& name, LogLevel logLevel = LogLevel::DETAILED) :
_logLevel{ logLevel } {
_name = name;
}
~Counter() { log(); }
// Increase the count for key (by inc).
void add(const K& key, size_t inc = 1);
// Decrease the count for key (by dec).
void sub(const K& key, size_t dec = 1);
// Log current counts (called on destruction).
void log();
private:
std::string _name;
std::vector<std::string> _legend;
LogLevel _logLevel;
std::mutex _mutex;
Map _map;
};
// A tracker. Tracks keys state (as a size_t, mapped to std::string).
// Prints out counts for each state on destruction. Optionally prints out counts for duplicates and each key's state.
//
// K must have an implemented streaming operator for QDebug operator<<. (Done for std::string in this header.)
//
// Useful cases may be tracking pointers' ctor/dtor (K = size_t or K = uintptr_t), or tracking resource names (K = std::string).
template <class K>
class Tracker {
using Map = std::map<K, size_t>;
using DuplicateValueMap = std::map<size_t, size_t>;
using DuplicateMap = std::map<K, DuplicateValueMap>;
public:
enum LogLevel {
// Only print counts for each state.
SUMMARY = 0,
// Print SUMMARY and a count of duplicate states (when a key is set to its current state).
DUPLICATES,
// Print DUPLICATES and the current state for each key not in its final state (the last state of the legend).
DETAILED,
};
// Create a new tracker.
// 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.
// allowReuse: if true, keys in the last state may be set to a different state.
Tracker(const std::string& name, const std::vector<std::string>& legend, LogLevel logLevel = SUMMARY, bool allowReuse = true) :
_logLevel{ logLevel }, _allowReuse{ allowReuse }, _max{ legend.size() - 1 } {
_name = name;
_legend = legend;
}
~Tracker() { log(); }
// Set key to state.
// Fails (triggers an assertion) if (state < current state) (i.e. states are ordered),
// unless allowReuse is true and the current state is the final state.
void set(const K& key, const size_t& state);
// Log current states (called on destruction).
void log();
private:
std::string _name;
std::vector<std::string> _legend;
bool _allowReuse;
size_t _max;
LogLevel _logLevel;
std::mutex _mutex;
Map _map;
DuplicateMap _duplicates;
};
template<class K>
void Counter<K>::add(const K& k, size_t inc) {
std::lock_guard<std::mutex> lock(_mutex);
auto& it = _map.find(k);
if (it == _map.end()) {
// No entry for k; add it
_map.insert(std::pair<K, size_t>(k, inc));
} else {
// Entry for k; update it
it->second += inc;
}
}
template<class K>
void Counter<K>::sub(const K& k, size_t dec) {
add(k, -dec);
}
template <class K>
void Counter<K>::log() {
// Avoid logging nothing
if (!_map.size()) return;
std::map<size_t, size_t> results;
for (const auto& entry : _map) {
if (!results.insert(std::pair<size_t, size_t>(entry.second, 1)).second) {
results.find(entry.second)->second++;
}
}
qCDebug(shared) << "Counts for" << _name;
for (const auto& entry : results) {
qCDebug(shared) << entry.first << '\t' << entry.second << "entries";
}
if (_logLevel == LogLevel::SUMMARY) return;
qCDebug(shared) << "Entries";
for (const auto& entry : _map) {
qCDebug(shared) << entry.first << '\t' << entry.second;
}
if (_logLevel == LogLevel::DETAILED) return;
}
template <class K>
void Tracker<K>::set(const K& k, const size_t& v) {
std::lock_guard<std::mutex> lock(_mutex);
auto& it = _map.find(k);
if (it == _map.end()) {
// No entry for k; add it
_map.insert(std::pair<K, size_t>(k, v));
} else if (v > it->second) {
// Ordered entry for k; update it
it->second = v;
} else if (v == it->second) {
// Duplicate entry for k; log it
auto& dit = _duplicates.find(k);
if (dit == _duplicates.end()) {
// No duplicate value map for k; add it
DuplicateValueMap duplicateValueMap{ std::pair<size_t, size_t>(v, 1) };
_duplicates.insert(std::pair<K, DuplicateValueMap>(k, duplicateValueMap));
} else {
// Duplicate value map for k; update it
auto& dvit = dit->second.find(v);
if (dvit == dit->second.end()) {
// No duplicate entry for (k, v); add it
dit->second.insert(std::pair<size_t, size_t>(v, 1));
} else {
// Duplicate entry for (k, v); update it
dvit->second++;
}
}
} else { // if (v < it.second)
if (_allowReuse && it->second == _max) {
// Reusing an entry; update it
it->second = v;
} else {
// Unordered entry for k; dump log and fail
qCDebug(shared) << "Badly ordered entry detected:" <<
k << _legend.at(it->second).c_str() << "->" << _legend.at(v).c_str();
log();
assert(false);
}
}
}
template <class K>
void Tracker<K>::log() {
// Avoid logging nothing
if (!_map.size()) return;
std::vector<std::vector<K>> results(_max + 1);
for (const auto& entry : _map) {
results.at(entry.second).push_back(entry.first);
}
qCDebug(shared) << "Summary of" << _name;
for (auto i = 0; i < results.size(); ++i) {
qCDebug(shared) << _legend.at(i) << '\t' << results[i].size() << "entries";
}
if (_logLevel == LogLevel::SUMMARY) return;
size_t size = 0;
for (const auto& entry : _duplicates) {
size += entry.second.size();
}
qCDebug(shared) << "Duplicates" << size << "entries";
// TODO: Add more detail to duplicate logging
if (_logLevel <= LogLevel::DUPLICATES) return;
qCDebug(shared) << "Entries";
// Don't log the terminal case
for (auto i = 0; i < _max; ++i) {
qCDebug(shared) << "----" << _legend.at(i) << "----";
for (const auto& entry : results[i]) {
qCDebug(shared) << "\t" << entry;
}
}
if (_logLevel <= LogLevel::DETAILED) return;
}
#endif // DEBUG
#endif // hifi_Debug_h