mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
Fix memory corruption in PerformanceTimer when Stats are active
PerformanceTimer was neither thread safe nor re-entrant. Because it was being used increasingly on render and worker threads it has increased the likelihood of heap corruption. I was able to identify this by enabling full page heap verification using the gflags app. https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/enable-page-heap
This commit is contained in:
parent
e9f23a43f6
commit
c4c3581285
3 changed files with 25 additions and 9 deletions
|
@ -431,7 +431,7 @@ void Stats::updateStats(bool force) {
|
||||||
// a new Map sorted by average time...
|
// a new Map sorted by average time...
|
||||||
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
|
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
|
||||||
QMap<float, QString> sortedRecords;
|
QMap<float, QString> sortedRecords;
|
||||||
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
|
auto allRecords = PerformanceTimer::getAllTimerRecords();
|
||||||
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
|
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
|
||||||
|
|
||||||
while (i.hasNext()) {
|
while (i.hasNext()) {
|
||||||
|
@ -479,7 +479,7 @@ void Stats::updateStats(bool force) {
|
||||||
bool operator<(const SortableStat& other) const { return priority < other.priority; }
|
bool operator<(const SortableStat& other) const { return priority < other.priority; }
|
||||||
};
|
};
|
||||||
|
|
||||||
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
|
auto allRecords = PerformanceTimer::getAllTimerRecords();
|
||||||
std::priority_queue<SortableStat> idleUpdateStats;
|
std::priority_queue<SortableStat> idleUpdateStats;
|
||||||
auto itr = allRecords.find("/idle/update");
|
auto itr = allRecords.find("/idle/update");
|
||||||
if (itr != allRecords.end()) {
|
if (itr != allRecords.end()) {
|
||||||
|
@ -496,7 +496,7 @@ void Stats::updateStats(bool force) {
|
||||||
};
|
};
|
||||||
for (int32_t j = 0; j < categories.size(); ++j) {
|
for (int32_t j = 0; j < categories.size(); ++j) {
|
||||||
QString recordKey = "/idle/update/" + categories[j];
|
QString recordKey = "/idle/update/" + categories[j];
|
||||||
auto record = PerformanceTimer::getTimerRecord(recordKey);
|
auto& record = allRecords[recordKey];
|
||||||
if (record.getCount()) {
|
if (record.getCount()) {
|
||||||
float dt = (float) record.getMovingAverage() / (float)USECS_PER_MSEC;
|
float dt = (float) record.getMovingAverage() / (float)USECS_PER_MSEC;
|
||||||
QString message = QString("\n %1 = %2").arg(categories[j]).arg(dt);
|
QString message = QString("\n %1 = %2").arg(categories[j]).arg(dt);
|
||||||
|
|
|
@ -80,16 +80,19 @@ void PerformanceTimerRecord::tallyResult(const quint64& now) {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
std::atomic<bool> PerformanceTimer::_isActive(false);
|
std::atomic<bool> PerformanceTimer::_isActive(false);
|
||||||
|
std::mutex PerformanceTimer::_mutex;
|
||||||
QHash<QThread*, QString> PerformanceTimer::_fullNames;
|
QHash<QThread*, QString> PerformanceTimer::_fullNames;
|
||||||
QMap<QString, PerformanceTimerRecord> PerformanceTimer::_records;
|
QMap<QString, PerformanceTimerRecord> PerformanceTimer::_records;
|
||||||
|
|
||||||
|
|
||||||
PerformanceTimer::PerformanceTimer(const QString& name) {
|
PerformanceTimer::PerformanceTimer(const QString& name) {
|
||||||
if (_isActive) {
|
if (_isActive) {
|
||||||
_name = name;
|
_name = name;
|
||||||
QString& fullName = _fullNames[QThread::currentThread()];
|
{
|
||||||
fullName.append("/");
|
std::lock_guard<std::mutex> guard(_mutex);
|
||||||
fullName.append(_name);
|
QString& fullName = _fullNames[QThread::currentThread()];
|
||||||
|
fullName.append("/");
|
||||||
|
fullName.append(_name);
|
||||||
|
}
|
||||||
_start = usecTimestampNow();
|
_start = usecTimestampNow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +100,7 @@ PerformanceTimer::PerformanceTimer(const QString& name) {
|
||||||
PerformanceTimer::~PerformanceTimer() {
|
PerformanceTimer::~PerformanceTimer() {
|
||||||
if (_isActive && _start != 0) {
|
if (_isActive && _start != 0) {
|
||||||
quint64 elapsedUsec = (usecTimestampNow() - _start);
|
quint64 elapsedUsec = (usecTimestampNow() - _start);
|
||||||
|
std::lock_guard<std::mutex> guard(_mutex);
|
||||||
QString& fullName = _fullNames[QThread::currentThread()];
|
QString& fullName = _fullNames[QThread::currentThread()];
|
||||||
PerformanceTimerRecord& namedRecord = _records[fullName];
|
PerformanceTimerRecord& namedRecord = _records[fullName];
|
||||||
namedRecord.accumulateResult(elapsedUsec);
|
namedRecord.accumulateResult(elapsedUsec);
|
||||||
|
@ -111,11 +115,13 @@ bool PerformanceTimer::isActive() {
|
||||||
|
|
||||||
// static
|
// static
|
||||||
QString PerformanceTimer::getContextName() {
|
QString PerformanceTimer::getContextName() {
|
||||||
|
std::lock_guard<std::mutex> guard(_mutex);
|
||||||
return _fullNames[QThread::currentThread()];
|
return _fullNames[QThread::currentThread()];
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void PerformanceTimer::addTimerRecord(const QString& fullName, quint64 elapsedUsec) {
|
void PerformanceTimer::addTimerRecord(const QString& fullName, quint64 elapsedUsec) {
|
||||||
|
std::lock_guard<std::mutex> guard(_mutex);
|
||||||
PerformanceTimerRecord& namedRecord = _records[fullName];
|
PerformanceTimerRecord& namedRecord = _records[fullName];
|
||||||
namedRecord.accumulateResult(elapsedUsec);
|
namedRecord.accumulateResult(elapsedUsec);
|
||||||
}
|
}
|
||||||
|
@ -125,6 +131,7 @@ void PerformanceTimer::setActive(bool active) {
|
||||||
if (active != _isActive) {
|
if (active != _isActive) {
|
||||||
_isActive.store(active);
|
_isActive.store(active);
|
||||||
if (!active) {
|
if (!active) {
|
||||||
|
std::lock_guard<std::mutex> guard(_mutex);
|
||||||
_fullNames.clear();
|
_fullNames.clear();
|
||||||
_records.clear();
|
_records.clear();
|
||||||
}
|
}
|
||||||
|
@ -133,8 +140,15 @@ void PerformanceTimer::setActive(bool active) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
QMap<QString, PerformanceTimerRecord> PerformanceTimer::getAllTimerRecords() {
|
||||||
|
std::lock_guard<std::mutex> guard(_mutex);
|
||||||
|
return _records;
|
||||||
|
};
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void PerformanceTimer::tallyAllTimerRecords() {
|
void PerformanceTimer::tallyAllTimerRecords() {
|
||||||
|
std::lock_guard<std::mutex> guard(_mutex);
|
||||||
QMap<QString, PerformanceTimerRecord>::iterator recordsItr = _records.begin();
|
QMap<QString, PerformanceTimerRecord>::iterator recordsItr = _records.begin();
|
||||||
QMap<QString, PerformanceTimerRecord>::const_iterator recordsEnd = _records.end();
|
QMap<QString, PerformanceTimerRecord>::const_iterator recordsEnd = _records.end();
|
||||||
quint64 now = usecTimestampNow();
|
quint64 now = usecTimestampNow();
|
||||||
|
@ -150,6 +164,7 @@ void PerformanceTimer::tallyAllTimerRecords() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PerformanceTimer::dumpAllTimerRecords() {
|
void PerformanceTimer::dumpAllTimerRecords() {
|
||||||
|
std::lock_guard<std::mutex> guard(_mutex);
|
||||||
QMapIterator<QString, PerformanceTimerRecord> i(_records);
|
QMapIterator<QString, PerformanceTimerRecord> i(_records);
|
||||||
while (i.hasNext()) {
|
while (i.hasNext()) {
|
||||||
i.next();
|
i.next();
|
||||||
|
|
|
@ -84,8 +84,7 @@ public:
|
||||||
|
|
||||||
static QString getContextName();
|
static QString getContextName();
|
||||||
static void addTimerRecord(const QString& fullName, quint64 elapsedUsec);
|
static void addTimerRecord(const QString& fullName, quint64 elapsedUsec);
|
||||||
static const PerformanceTimerRecord& getTimerRecord(const QString& name) { return _records[name]; };
|
static QMap<QString, PerformanceTimerRecord> getAllTimerRecords();
|
||||||
static const QMap<QString, PerformanceTimerRecord>& getAllTimerRecords() { return _records; };
|
|
||||||
static void tallyAllTimerRecords();
|
static void tallyAllTimerRecords();
|
||||||
static void dumpAllTimerRecords();
|
static void dumpAllTimerRecords();
|
||||||
|
|
||||||
|
@ -93,6 +92,8 @@ private:
|
||||||
quint64 _start = 0;
|
quint64 _start = 0;
|
||||||
QString _name;
|
QString _name;
|
||||||
static std::atomic<bool> _isActive;
|
static std::atomic<bool> _isActive;
|
||||||
|
|
||||||
|
static std::mutex _mutex; // used to guard multi-threaded access to _fullNames and _records
|
||||||
static QHash<QThread*, QString> _fullNames;
|
static QHash<QThread*, QString> _fullNames;
|
||||||
static QMap<QString, PerformanceTimerRecord> _records;
|
static QMap<QString, PerformanceTimerRecord> _records;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue