2012-11-01 15:19:01 +00:00
|
|
|
// Copyright (C) 2003 Dolphin Project.
|
|
|
|
|
|
|
|
// 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
|
2012-11-04 22:01:49 +00:00
|
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2012-12-06 18:03:12 +00:00
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2012-11-01 15:19:01 +00:00
|
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
// Official SVN repository and contact information can be found at
|
|
|
|
// http://code.google.com/p/dolphin-emu/
|
|
|
|
|
|
|
|
#include <algorithm>
|
2013-08-27 18:58:27 +00:00
|
|
|
#include "base/logging.h"
|
2013-09-07 10:34:19 +00:00
|
|
|
#include "util/text/utf8.h"
|
2012-11-01 15:19:01 +00:00
|
|
|
#include "LogManager.h"
|
|
|
|
#include "ConsoleListener.h"
|
|
|
|
#include "Timer.h"
|
|
|
|
#include "FileUtil.h"
|
2013-07-27 23:39:49 +00:00
|
|
|
#include "../Core/Config.h"
|
2012-12-13 03:15:20 +00:00
|
|
|
#ifdef __SYMBIAN32__
|
|
|
|
#include <e32debug.h>
|
|
|
|
#endif
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-03-11 05:25:03 +00:00
|
|
|
// Don't need to savestate this.
|
|
|
|
const char *hleCurrentThreadName = NULL;
|
|
|
|
|
2013-01-30 06:33:14 +00:00
|
|
|
// Unfortunately this is quite slow.
|
2013-02-27 16:52:51 +00:00
|
|
|
#define LOG_MSC_OUTPUTDEBUG false
|
2013-01-30 06:33:14 +00:00
|
|
|
// #define LOG_MSC_OUTPUTDEBUG true
|
|
|
|
|
2012-11-01 15:19:01 +00:00
|
|
|
void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
|
2013-09-07 10:34:19 +00:00
|
|
|
const char *file, int line, const char* fmt, ...) {
|
|
|
|
if (!g_Config.bEnableLogging) return;
|
2013-07-27 23:39:49 +00:00
|
|
|
|
2012-11-01 15:19:01 +00:00
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
if (LogManager::GetInstance())
|
|
|
|
LogManager::GetInstance()->Log(level, type,
|
|
|
|
file, line, fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
2013-09-07 11:38:37 +00:00
|
|
|
LogManager *LogManager::logManager_ = NULL;
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-09-07 10:34:19 +00:00
|
|
|
struct LogNameTableEntry {
|
|
|
|
LogTypes::LOG_TYPE logType;
|
|
|
|
const char *name;
|
|
|
|
const char *longName;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const LogNameTableEntry logTable[] = {
|
|
|
|
{LogTypes::MASTER_LOG, "*", "Master Log"},
|
2013-09-07 19:19:21 +00:00
|
|
|
|
|
|
|
{LogTypes::SCEAUDIO ,"AUDIO", "sceAudio"},
|
|
|
|
{LogTypes::SCECTRL ,"CTRL", "sceCtrl"},
|
|
|
|
{LogTypes::SCEDISPLAY ,"DISP", "sceDisplay"},
|
2013-09-07 20:02:55 +00:00
|
|
|
{LogTypes::SCEFONT ,"FONT", "sceFont"},
|
|
|
|
{LogTypes::SCEGE ,"SCEGE", "sceGe"},
|
2013-09-07 19:19:21 +00:00
|
|
|
{LogTypes::SCEINTC ,"INTC", "sceKernelInterrupt"},
|
|
|
|
{LogTypes::SCEIO ,"IO", "sceIo"},
|
2013-09-07 20:02:55 +00:00
|
|
|
{LogTypes::SCEKERNEL ,"KERNEL", "sceKernel*"},
|
|
|
|
{LogTypes::SCEMODULE ,"MODULE", "sceKernelModule"},
|
2013-09-07 19:19:21 +00:00
|
|
|
{LogTypes::SCENET ,"NET", "sceNet*"},
|
2013-09-07 20:31:14 +00:00
|
|
|
{LogTypes::SCERTC ,"SCERTC", "sceRtc"},
|
2013-09-07 19:19:21 +00:00
|
|
|
{LogTypes::SCESAS ,"SCESAS", "sceSas"},
|
|
|
|
{LogTypes::SCEUTILITY ,"UTIL", "sceUtility"},
|
|
|
|
|
2013-09-07 10:34:19 +00:00
|
|
|
{LogTypes::BOOT ,"BOOT", "Boot"},
|
|
|
|
{LogTypes::COMMON ,"COMMON", "Common"},
|
|
|
|
{LogTypes::CPU ,"CPU", "CPU"},
|
|
|
|
{LogTypes::FILESYS ,"FileSys", "File System"},
|
|
|
|
{LogTypes::G3D ,"G3D", "3D Graphics"},
|
|
|
|
{LogTypes::HLE ,"HLE", "HLE"},
|
2013-09-07 19:19:21 +00:00
|
|
|
{LogTypes::JIT ,"JIT", "JIT compiler"},
|
|
|
|
{LogTypes::LOADER ,"LOAD", "Loader"},
|
2013-09-07 10:34:19 +00:00
|
|
|
{LogTypes::ME ,"ME", "Media Engine"},
|
2013-09-07 19:19:21 +00:00
|
|
|
{LogTypes::MEMMAP ,"MM", "Memory Map"},
|
2013-09-07 20:02:55 +00:00
|
|
|
{LogTypes::TIME ,"TIME", "CoreTiming"},
|
2013-09-07 19:19:21 +00:00
|
|
|
{LogTypes::SASMIX ,"SASMIX", "Sound Mixer (Sas)"},
|
2013-09-07 10:34:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
LogManager::LogManager() {
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(logTable); i++) {
|
2013-09-07 11:38:37 +00:00
|
|
|
if (i != logTable[i].logType) {
|
2013-10-19 21:16:07 +00:00
|
|
|
FLOG("Bad logtable at %i", (int)i);
|
2013-09-07 11:38:37 +00:00
|
|
|
}
|
|
|
|
log_[logTable[i].logType] = new LogChannel(logTable[i].name, logTable[i].longName);
|
2013-09-07 10:34:19 +00:00
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2012-12-06 18:03:12 +00:00
|
|
|
// Remove file logging on small devices
|
2013-12-03 17:09:16 +00:00
|
|
|
#if !(defined(MOBILE_DEVICE) || defined(_XBOX)) || defined(_DEBUG)
|
2013-10-17 22:06:45 +00:00
|
|
|
fileLog_ = new FileLogListener("");
|
2013-09-07 11:38:37 +00:00
|
|
|
consoleLog_ = new ConsoleListener();
|
|
|
|
debuggerLog_ = new DebuggerLogListener();
|
2012-12-22 17:49:29 +00:00
|
|
|
#else
|
2013-09-07 11:38:37 +00:00
|
|
|
fileLog_ = NULL;
|
|
|
|
consoleLog_ = NULL;
|
|
|
|
debuggerLog_ = NULL;
|
2012-11-01 15:19:01 +00:00
|
|
|
#endif
|
2015-01-05 00:23:03 +00:00
|
|
|
ringLog_ = new RingbufferLogListener();
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-09-07 10:34:19 +00:00
|
|
|
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) {
|
2013-09-07 11:38:37 +00:00
|
|
|
log_[i]->SetEnable(true);
|
2013-12-03 17:09:16 +00:00
|
|
|
#if !(defined(MOBILE_DEVICE) || defined(_XBOX)) || defined(_DEBUG)
|
2013-09-07 11:38:37 +00:00
|
|
|
log_[i]->AddListener(fileLog_);
|
|
|
|
log_[i]->AddListener(consoleLog_);
|
2013-12-03 17:09:16 +00:00
|
|
|
#if defined(_MSC_VER) && !defined(_XBOX)
|
2013-09-07 11:38:37 +00:00
|
|
|
if (IsDebuggerPresent() && debuggerLog_ != NULL && LOG_MSC_OUTPUTDEBUG)
|
|
|
|
log_[i]->AddListener(debuggerLog_);
|
2012-11-01 15:19:01 +00:00
|
|
|
#endif
|
2015-01-05 00:23:03 +00:00
|
|
|
log_[i]->AddListener(ringLog_);
|
2012-11-01 15:19:01 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-07 11:38:37 +00:00
|
|
|
LogManager::~LogManager() {
|
|
|
|
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) {
|
2014-02-08 18:29:22 +00:00
|
|
|
#if !defined(MOBILE_DEVICE) || defined(_DEBUG)
|
2013-09-07 11:38:37 +00:00
|
|
|
if (fileLog_ != NULL)
|
|
|
|
logManager_->RemoveListener((LogTypes::LOG_TYPE)i, fileLog_);
|
|
|
|
logManager_->RemoveListener((LogTypes::LOG_TYPE)i, consoleLog_);
|
2013-02-20 03:12:03 +00:00
|
|
|
#ifdef _MSC_VER
|
2013-09-07 11:38:37 +00:00
|
|
|
logManager_->RemoveListener((LogTypes::LOG_TYPE)i, debuggerLog_);
|
2013-02-20 03:12:03 +00:00
|
|
|
#endif
|
2012-11-01 15:19:01 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
|
2013-09-07 11:38:37 +00:00
|
|
|
delete log_[i];
|
|
|
|
if (fileLog_ != NULL)
|
|
|
|
delete fileLog_;
|
2014-02-08 18:29:22 +00:00
|
|
|
#if !defined(MOBILE_DEVICE) || defined(_DEBUG)
|
2013-09-07 11:38:37 +00:00
|
|
|
delete consoleLog_;
|
|
|
|
delete debuggerLog_;
|
2012-11-01 15:19:01 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-09-07 10:34:19 +00:00
|
|
|
void LogManager::ChangeFileLog(const char *filename) {
|
2013-09-07 11:38:37 +00:00
|
|
|
if (fileLog_ != NULL) {
|
2012-12-22 17:49:29 +00:00
|
|
|
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
|
2013-09-07 11:38:37 +00:00
|
|
|
logManager_->RemoveListener((LogTypes::LOG_TYPE)i, fileLog_);
|
|
|
|
delete fileLog_;
|
2012-12-22 17:49:29 +00:00
|
|
|
}
|
|
|
|
|
2013-09-07 10:34:19 +00:00
|
|
|
if (filename != NULL) {
|
2013-09-07 11:38:37 +00:00
|
|
|
fileLog_ = new FileLogListener(filename);
|
2012-12-22 17:49:29 +00:00
|
|
|
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
|
2013-09-07 11:38:37 +00:00
|
|
|
log_[i]->AddListener(fileLog_);
|
2012-12-22 17:49:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-07 10:34:19 +00:00
|
|
|
void LogManager::SaveConfig(IniFile::Section *section) {
|
|
|
|
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) {
|
2013-09-07 11:38:37 +00:00
|
|
|
section->Set((std::string(log_[i]->GetShortName()) + "Enabled").c_str(), log_[i]->IsEnabled());
|
|
|
|
section->Set((std::string(log_[i]->GetShortName()) + "Level").c_str(), (int)log_[i]->GetLevel());
|
2012-12-06 18:03:12 +00:00
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2013-09-07 10:34:19 +00:00
|
|
|
void LogManager::LoadConfig(IniFile::Section *section) {
|
|
|
|
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) {
|
2012-12-06 18:03:12 +00:00
|
|
|
bool enabled;
|
|
|
|
int level;
|
2013-09-07 11:38:37 +00:00
|
|
|
section->Get((std::string(log_[i]->GetShortName()) + "Enabled").c_str(), &enabled, true);
|
|
|
|
section->Get((std::string(log_[i]->GetShortName()) + "Level").c_str(), &level, 0);
|
|
|
|
log_[i]->SetEnable(enabled);
|
|
|
|
log_[i]->SetLevel((LogTypes::LOG_LEVELS)level);
|
2012-12-06 18:03:12 +00:00
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2013-09-07 10:34:19 +00:00
|
|
|
void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char *file, int line, const char *format, va_list args) {
|
2013-09-07 11:38:37 +00:00
|
|
|
LogChannel *log = log_[type];
|
2014-03-03 06:03:02 +00:00
|
|
|
if (level > log->GetLevel() || !log->IsEnabled() || !log->HasListeners())
|
2012-11-01 15:19:01 +00:00
|
|
|
return;
|
|
|
|
|
2014-03-03 06:03:02 +00:00
|
|
|
std::lock_guard<std::mutex> lk(log_lock_);
|
2013-03-11 05:31:47 +00:00
|
|
|
static const char level_to_char[8] = "-NEWIDV";
|
2012-12-06 18:03:12 +00:00
|
|
|
char formattedTime[13];
|
|
|
|
Common::Timer::GetTimeFormatted(formattedTime);
|
2013-02-02 23:40:48 +00:00
|
|
|
|
2013-03-11 05:25:03 +00:00
|
|
|
#ifdef _WIN32
|
2013-03-11 09:27:28 +00:00
|
|
|
static const char sep = '\\';
|
2013-03-11 05:25:03 +00:00
|
|
|
#else
|
2013-03-11 09:27:28 +00:00
|
|
|
static const char sep = '/';
|
|
|
|
#endif
|
|
|
|
const char *fileshort = strrchr(file, sep);
|
|
|
|
if (fileshort != NULL) {
|
|
|
|
do
|
|
|
|
--fileshort;
|
|
|
|
while (fileshort > file && *fileshort != sep);
|
|
|
|
if (fileshort != file)
|
|
|
|
file = fileshort + 1;
|
|
|
|
}
|
2014-03-03 06:03:02 +00:00
|
|
|
|
|
|
|
char msg[MAX_MSGLEN * 2];
|
2013-02-02 23:40:48 +00:00
|
|
|
char *msgPos = msg;
|
2013-09-07 11:38:37 +00:00
|
|
|
if (hleCurrentThreadName != NULL) {
|
2013-03-11 05:25:03 +00:00
|
|
|
msgPos += sprintf(msgPos, "%s %-12.12s %c[%s]: %s:%d ",
|
|
|
|
formattedTime,
|
|
|
|
hleCurrentThreadName, level_to_char[(int)level],
|
|
|
|
log->GetShortName(),
|
|
|
|
file, line);
|
2013-09-07 11:38:37 +00:00
|
|
|
} else {
|
2013-03-11 05:25:03 +00:00
|
|
|
msgPos += sprintf(msgPos, "%s %s:%d %c[%s]: ",
|
|
|
|
formattedTime,
|
|
|
|
file, line, level_to_char[(int)level],
|
|
|
|
log->GetShortName());
|
|
|
|
}
|
2013-02-02 23:40:48 +00:00
|
|
|
|
|
|
|
msgPos += vsnprintf(msgPos, MAX_MSGLEN, format, args);
|
|
|
|
// This will include the null terminator.
|
|
|
|
memcpy(msgPos, "\n", sizeof("\n"));
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
log->Trigger(level, msg);
|
|
|
|
}
|
|
|
|
|
2013-09-07 10:34:19 +00:00
|
|
|
void LogManager::Init() {
|
2013-09-07 11:38:37 +00:00
|
|
|
logManager_ = new LogManager();
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2013-09-07 10:34:19 +00:00
|
|
|
void LogManager::Shutdown() {
|
2013-09-07 11:38:37 +00:00
|
|
|
delete logManager_;
|
|
|
|
logManager_ = NULL;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2013-09-07 11:38:37 +00:00
|
|
|
LogChannel::LogChannel(const char* shortName, const char* fullName, bool enable)
|
2014-03-03 06:03:02 +00:00
|
|
|
: enable_(enable), m_hasListeners(false) {
|
2012-11-01 15:19:01 +00:00
|
|
|
strncpy(m_fullName, fullName, 128);
|
|
|
|
strncpy(m_shortName, shortName, 32);
|
2014-02-16 04:09:59 +00:00
|
|
|
#if defined(_DEBUG)
|
|
|
|
level_ = LogTypes::LDEBUG;
|
|
|
|
#else
|
2014-02-16 04:04:31 +00:00
|
|
|
level_ = LogTypes::LINFO;
|
2014-02-16 04:09:59 +00:00
|
|
|
#endif
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LogContainer
|
2013-09-07 11:38:37 +00:00
|
|
|
void LogChannel::AddListener(LogListener *listener) {
|
2012-11-01 15:19:01 +00:00
|
|
|
std::lock_guard<std::mutex> lk(m_listeners_lock);
|
|
|
|
m_listeners.insert(listener);
|
2014-03-03 06:03:02 +00:00
|
|
|
m_hasListeners = true;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2013-09-07 11:38:37 +00:00
|
|
|
void LogChannel::RemoveListener(LogListener *listener) {
|
2012-11-01 15:19:01 +00:00
|
|
|
std::lock_guard<std::mutex> lk(m_listeners_lock);
|
|
|
|
m_listeners.erase(listener);
|
2014-03-03 06:03:02 +00:00
|
|
|
m_hasListeners = !m_listeners.empty();
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2013-09-07 11:38:37 +00:00
|
|
|
void LogChannel::Trigger(LogTypes::LOG_LEVELS level, const char *msg) {
|
2013-02-20 03:12:03 +00:00
|
|
|
#ifdef __SYMBIAN32__
|
|
|
|
RDebug::Printf("%s",msg);
|
|
|
|
#else
|
2012-11-01 15:19:01 +00:00
|
|
|
std::lock_guard<std::mutex> lk(m_listeners_lock);
|
|
|
|
|
|
|
|
std::set<LogListener*>::const_iterator i;
|
2013-09-07 10:34:19 +00:00
|
|
|
for (i = m_listeners.begin(); i != m_listeners.end(); ++i) {
|
2012-11-01 15:19:01 +00:00
|
|
|
(*i)->Log(level, msg);
|
|
|
|
}
|
2013-02-20 03:12:03 +00:00
|
|
|
#endif
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2013-09-07 10:34:19 +00:00
|
|
|
FileLogListener::FileLogListener(const char *filename) {
|
|
|
|
#ifdef _WIN32
|
|
|
|
m_logfile.open(ConvertUTF8ToWString(filename).c_str(), std::ios::app);
|
|
|
|
#else
|
2012-11-01 15:19:01 +00:00
|
|
|
m_logfile.open(filename, std::ios::app);
|
2013-09-07 10:34:19 +00:00
|
|
|
#endif
|
2012-11-01 15:19:01 +00:00
|
|
|
SetEnable(true);
|
|
|
|
}
|
|
|
|
|
2013-09-07 10:34:19 +00:00
|
|
|
void FileLogListener::Log(LogTypes::LOG_LEVELS, const char *msg) {
|
2012-11-01 15:19:01 +00:00
|
|
|
if (!IsEnabled() || !IsValid())
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lk(m_log_lock);
|
|
|
|
m_logfile << msg << std::flush;
|
|
|
|
}
|
|
|
|
|
2013-09-07 10:34:19 +00:00
|
|
|
void DebuggerLogListener::Log(LogTypes::LOG_LEVELS, const char *msg) {
|
2012-11-01 15:19:01 +00:00
|
|
|
#if _MSC_VER
|
2013-08-27 18:58:27 +00:00
|
|
|
OutputDebugStringUTF8(msg);
|
2012-11-01 15:19:01 +00:00
|
|
|
#endif
|
|
|
|
}
|
2015-01-05 00:23:03 +00:00
|
|
|
|
|
|
|
void RingbufferLogListener::Log(LogTypes::LOG_LEVELS level, const char *msg) {
|
|
|
|
if (!enabled_)
|
|
|
|
return;
|
|
|
|
levels_[curMessage_] = (u8)level;
|
2015-01-11 22:23:03 +00:00
|
|
|
size_t len = (int)strlen(msg);
|
2015-01-05 00:23:03 +00:00
|
|
|
if (len >= sizeof(messages_[0]))
|
|
|
|
len = sizeof(messages_[0]) - 1;
|
|
|
|
memcpy(messages_[curMessage_], msg, len);
|
|
|
|
messages_[curMessage_][len] = 0;
|
|
|
|
curMessage_++;
|
|
|
|
if (curMessage_ >= MAX_LOGS)
|
|
|
|
curMessage_ -= MAX_LOGS;
|
|
|
|
count_++;
|
|
|
|
}
|