368 lines
8.9 KiB
C++
368 lines
8.9 KiB
C++
/* FCE Ultra - NES/Famicom Emulator
|
|
*
|
|
* Copyright notice for this file:
|
|
* Copyright (C) 2002 Xodnizel
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
// profiler.cpp
|
|
//
|
|
#ifdef __FCEU_PROFILER_ENABLE__
|
|
|
|
#include <stdio.h>
|
|
|
|
#ifdef __QT_DRIVER__
|
|
#include <QThread>
|
|
#endif
|
|
|
|
#include "utils/mutex.h"
|
|
#include "fceu.h"
|
|
#include "profiler.h"
|
|
|
|
namespace FCEU
|
|
{
|
|
static thread_local profileExecVector execList;
|
|
static thread_local profilerFuncMap threadProfileMap;
|
|
|
|
FILE *profilerManager::pLog = nullptr;
|
|
|
|
static profilerManager pMgr;
|
|
|
|
//-------------------------------------------------------------------------
|
|
//---- Function Profile Record
|
|
//-------------------------------------------------------------------------
|
|
funcProfileRecord::funcProfileRecord(const char *fileNameStringLiteral,
|
|
const int fileLineNumber,
|
|
const char *funcNameStringLiteral,
|
|
const char *commentStringLiteral)
|
|
|
|
: fileLineNum(fileLineNumber), fileName(fileNameStringLiteral),
|
|
funcName(funcNameStringLiteral), comment(commentStringLiteral)
|
|
{
|
|
min.fromSeconds(9);
|
|
max.zero();
|
|
sum.zero();
|
|
numCalls = 0;
|
|
recursionCount = 0;
|
|
|
|
threadProfileMap.addRecord( fileNameStringLiteral, fileLineNumber,
|
|
funcNameStringLiteral, commentStringLiteral, this);
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
void funcProfileRecord::reset(void)
|
|
{
|
|
min.fromSeconds(9);
|
|
max.zero();
|
|
sum.zero();
|
|
numCalls = 0;
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
double funcProfileRecord::average(void)
|
|
{
|
|
double avg = 0.0;
|
|
|
|
if (numCalls)
|
|
{
|
|
avg = sum.toSeconds() / static_cast<double>(numCalls);
|
|
}
|
|
return avg;
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
//---- Profile Scoped Function Class
|
|
//-------------------------------------------------------------------------
|
|
profileFuncScoped::profileFuncScoped( funcProfileRecord *recordIn )
|
|
{
|
|
rec = recordIn;
|
|
|
|
if (rec)
|
|
{
|
|
threadProfileMap.pushStack(rec);
|
|
start.readNew();
|
|
rec->numCalls++;
|
|
rec->recursionCount++;
|
|
}
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
profileFuncScoped::~profileFuncScoped(void)
|
|
{
|
|
if (rec)
|
|
{
|
|
timeStampRecord ts, dt;
|
|
ts.readNew();
|
|
dt = ts - start;
|
|
|
|
rec->last = dt;
|
|
rec->sum += dt;
|
|
if (dt < rec->min) rec->min = dt;
|
|
if (dt > rec->max) rec->max = dt;
|
|
|
|
rec->recursionCount--;
|
|
|
|
execList._vec.push_back(*rec);
|
|
|
|
threadProfileMap.popStack(rec);
|
|
}
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
//---- Profile Execution Vector
|
|
//-------------------------------------------------------------------------
|
|
profileExecVector::profileExecVector(void)
|
|
{
|
|
_vec.reserve( 10000 );
|
|
|
|
char threadName[128];
|
|
char fileName[256];
|
|
|
|
strcpy( threadName, "MainThread");
|
|
|
|
#ifdef __QT_DRIVER__
|
|
QThread *thread = QThread::currentThread();
|
|
|
|
if (thread)
|
|
{
|
|
//printf("Thread: %s\n", thread->objectName().toStdString().c_str());
|
|
strcpy( threadName, thread->objectName().toStdString().c_str());
|
|
}
|
|
#endif
|
|
sprintf( fileName, "fceux-profile-%s.log", threadName);
|
|
|
|
logFp = ::fopen(fileName, "w");
|
|
|
|
if (logFp == nullptr)
|
|
{
|
|
printf("Error: Failed to create profiler logfile: %s\n", fileName);
|
|
}
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
profileExecVector::~profileExecVector(void)
|
|
{
|
|
if (logFp)
|
|
{
|
|
::fclose(logFp);
|
|
}
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
void profileExecVector::update(void)
|
|
{
|
|
size_t n = _vec.size();
|
|
|
|
for (size_t i=0; i<n; i++)
|
|
{
|
|
funcProfileRecord &rec = _vec[i];
|
|
|
|
fprintf( logFp, "%s: %u %f %f %f %f\n", rec.funcName, rec.numCalls, rec.last.toSeconds(), rec.average(), rec.min.toSeconds(), rec.max.toSeconds());
|
|
}
|
|
_vec.clear();
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
//---- Profile Function Record Map
|
|
//-------------------------------------------------------------------------
|
|
profilerFuncMap::profilerFuncMap(void)
|
|
{
|
|
//printf("profilerFuncMap Constructor: %p\n", this);
|
|
pMgr.addThreadProfiler(this);
|
|
|
|
_map_it = _map.begin();
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
profilerFuncMap::~profilerFuncMap(void)
|
|
{
|
|
//printf("profilerFuncMap Destructor: %p\n", this);
|
|
pMgr.removeThreadProfiler(this);
|
|
|
|
//{
|
|
// autoScopedLock aLock(_mapMtx);
|
|
|
|
// for (auto it = _map.begin(); it != _map.end(); it++)
|
|
// {
|
|
// delete it->second;
|
|
// }
|
|
// _map.clear();
|
|
//}
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
void profilerFuncMap::pushStack(funcProfileRecord *rec)
|
|
{
|
|
stack.push_back(rec);
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
void profilerFuncMap::popStack(funcProfileRecord *rec)
|
|
{
|
|
stack.pop_back();
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
int profilerFuncMap::addRecord(const char *fileNameStringLiteral,
|
|
const int fileLineNumber,
|
|
const char *funcNameStringLiteral,
|
|
const char *commentStringLiteral,
|
|
funcProfileRecord *rec )
|
|
{
|
|
autoScopedLock aLock(_mapMtx);
|
|
char lineString[64];
|
|
|
|
sprintf( lineString, ":%i", fileLineNumber);
|
|
|
|
std::string fname(fileNameStringLiteral);
|
|
|
|
fname.append( lineString );
|
|
|
|
_map[fname] = rec;
|
|
|
|
return 0;
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
funcProfileRecord *profilerFuncMap::findRecord(const char *fileNameStringLiteral,
|
|
const int fileLineNumber,
|
|
const char *funcNameStringLiteral,
|
|
const char *commentStringLiteral,
|
|
bool create)
|
|
{
|
|
autoScopedLock aLock(_mapMtx);
|
|
char lineString[64];
|
|
funcProfileRecord *rec = nullptr;
|
|
|
|
sprintf( lineString, ":%i", fileLineNumber);
|
|
|
|
std::string fname(fileNameStringLiteral);
|
|
|
|
fname.append( lineString );
|
|
|
|
auto it = _map.find(fname);
|
|
|
|
if (it != _map.end())
|
|
{
|
|
rec = it->second;
|
|
}
|
|
else if (create)
|
|
{
|
|
fprintf( pMgr.pLog, "Creating Function Profile Record: %s %s\n", fname.c_str(), funcNameStringLiteral);
|
|
|
|
rec = new funcProfileRecord( fileNameStringLiteral, fileLineNumber,
|
|
funcNameStringLiteral, commentStringLiteral);
|
|
|
|
_map[fname] = rec;
|
|
}
|
|
return rec;
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
funcProfileRecord *profilerFuncMap::iterateBegin(void)
|
|
{
|
|
autoScopedLock aLock(_mapMtx);
|
|
funcProfileRecord *rec = nullptr;
|
|
|
|
_map_it = _map.begin();
|
|
|
|
if (_map_it != _map.end())
|
|
{
|
|
rec = _map_it->second;
|
|
}
|
|
return rec;
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
funcProfileRecord *profilerFuncMap::iterateNext(void)
|
|
{
|
|
autoScopedLock aLock(_mapMtx);
|
|
funcProfileRecord *rec = nullptr;
|
|
|
|
if (_map_it != _map.end())
|
|
{
|
|
_map_it++;
|
|
}
|
|
if (_map_it != _map.end())
|
|
{
|
|
rec = _map_it->second;
|
|
}
|
|
return rec;
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
//----- profilerManager class
|
|
//-------------------------------------------------------------------------
|
|
profilerManager* profilerManager::instance = nullptr;
|
|
|
|
profilerManager* profilerManager::getInstance(void)
|
|
{
|
|
return instance;
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
profilerManager::profilerManager(void)
|
|
{
|
|
//printf("profilerManager Constructor\n");
|
|
if (pLog == nullptr)
|
|
{
|
|
pLog = stdout;
|
|
}
|
|
|
|
if (instance == nullptr)
|
|
{
|
|
instance = this;
|
|
}
|
|
}
|
|
|
|
profilerManager::~profilerManager(void)
|
|
{
|
|
//printf("profilerManager Destructor\n");
|
|
{
|
|
autoScopedLock aLock(threadListMtx);
|
|
threadList.clear();
|
|
}
|
|
|
|
if (pLog && (pLog != stdout))
|
|
{
|
|
fclose(pLog); pLog = nullptr;
|
|
}
|
|
if (instance == this)
|
|
{
|
|
instance = nullptr;
|
|
}
|
|
}
|
|
|
|
int profilerManager::addThreadProfiler( profilerFuncMap *m )
|
|
{
|
|
autoScopedLock aLock(threadListMtx);
|
|
threadList.push_back(m);
|
|
return 0;
|
|
}
|
|
|
|
int profilerManager::removeThreadProfiler( profilerFuncMap *m, bool shouldDestroy )
|
|
{
|
|
int result = -1;
|
|
autoScopedLock aLock(threadListMtx);
|
|
|
|
for (auto it = threadList.begin(); it != threadList.end(); it++)
|
|
{
|
|
if (*it == m )
|
|
{
|
|
threadList.erase(it);
|
|
if (shouldDestroy)
|
|
{
|
|
delete m;
|
|
}
|
|
result = 0;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
} // namespace FCEU
|
|
|
|
//-------------------------------------------------------------------------
|
|
int FCEU_profiler_log_thread_activity(void)
|
|
{
|
|
FCEU::execList.update();
|
|
return 0;
|
|
}
|
|
#endif // __FCEU_PROFILER_ENABLE__
|