Files
itgmania212121/src/RageLog.cpp
T
Kyzentun 8b09da8e64 Various changes to speed up start up time:
Added delay_save_cache to SongCacheIndex so that it doesn't write the entire cache index file after every song is loaded when loading songs.
Added m_SongsByDir to SongManager so that GetSongFromDir doesn't have to walk the entire list of songs.
Minor changes to when LoadEnabledSongsFromPref occurs and how SanityCheckGroupDir works to speed up loading.
Song::ReloadFromSongDir removes cache file to force an actual reload from the song dir instead of reloading from the cache.  ReloadFromSongDir exposed to lua.
Reordered Actor::LoadFromNode to put Command first because that case is more common.
Course::GetTrailUnsorted reserves entries before starting to save time reallocating.
join in RageUtil calculates the final size of the concatenated strings reserves it to save time reallocating.
Added time log file to RageLog for profiling.
2015-03-30 17:45:52 -06:00

471 lines
12 KiB
C++

#include "global.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "RageTimer.h"
#include "RageFile.h"
#include "RageThreads.h"
#include "Foreach.h"
#include <ctime>
#if defined(_WINDOWS)
#include <windows.h>
#endif
#include <map>
RageLog* LOG; // global and accessible from anywhere in the program
/*
* We have a couple log types and a couple logs.
*
* Traces are for very verbose debug information. Use them as much as you want.
*
* Warnings are for things that shouldn't happen, but can be dealt with. (If
* they can't be dealt with, use RageException::Throw, which will also send a
* warning.)
*
* Info is for important bits of information. These should be used selectively.
* Try to keep Info text dense; lots of information is fine, but try to keep
* it to a reasonable total length.
*
* log.txt receives all logs. This file can get rather large.
*
* info.txt receives warnings and infos. This file should be fairly small; small
* enough to be mailed without having to be edited or zipped, and small enough
* to be very easily read.
*/
/* Map data names to logged data.
*
* This lets us keep individual bits of changing information to be present
* in crash logs. For example, we want to know which file was being processed
* if we crash during a texture load. However, we don't want to log every
* load, since there are a huge number, even for log.txt. We only want to
* know the current one, if any.
*
* So, when a texture begins loading, we do:
* LOG->MapLog("TextureManager::Load", "Loading foo.png");
* and when it finishes loading without crashing,
* LOG->UnmapLog("TextureManager::Load");
*
* Each time a mapped log changes, we update a block of static text to be put
* in info.txt, so we see "Loading foo.png".
*
* The identifier is never displayed, so we can use a simple local object to
* map/unmap, using any mechanism to generate unique IDs. */
static map<RString, RString> LogMaps;
#define LOG_PATH "/Logs/log.txt"
#define INFO_PATH "/Logs/info.txt"
#define TIME_PATH "/Logs/timelog.txt"
#define USER_PATH "/Logs/userlog.txt"
static RageFile *g_fileLog, *g_fileInfo, *g_fileUserLog, *g_fileTimeLog;
/* Mutex writes to the files. Writing to files is not thread-aware, and this is the
* only place we write to the same file from multiple threads. */
static RageMutex *g_Mutex;
/* staticlog gets info.txt
* crashlog gets log.txt */
enum
{
/* If this is set, the message will also be written to info.txt. (info and warnings) */
WRITE_TO_INFO = 0x01,
/* If this is set, the message will also be written to userlog.txt. (user warnings only) */
WRITE_TO_USER_LOG = 0x02,
/* Whether this line should be loud when written to log.txt (warnings). */
WRITE_LOUD = 0x04,
WRITE_TO_TIME= 0x08
};
RageLog::RageLog(): m_bLogToDisk(false), m_bInfoToDisk(false),
m_bUserLogToDisk(false), m_bFlush(false), m_bShowLogOutput(false)
{
g_fileLog = new RageFile;
g_fileInfo = new RageFile;
g_fileUserLog = new RageFile;
g_fileTimeLog = new RageFile;
if(!g_fileTimeLog->Open(TIME_PATH, RageFile::WRITE|RageFile::STREAMED))
{ fprintf(stderr, "Couldn't open %s: %s\n", TIME_PATH, g_fileTimeLog->GetError().c_str()); }
g_Mutex = new RageMutex( "Log" );
}
RageLog::~RageLog()
{
/* Add the mapped log data to info.txt. */
const RString AdditionalLog = GetAdditionalLog();
vector<RString> AdditionalLogLines;
split( AdditionalLog, "\n", AdditionalLogLines );
for( unsigned i = 0; i < AdditionalLogLines.size(); ++i )
{
Trim( AdditionalLogLines[i] );
this->Info( "%s", AdditionalLogLines[i].c_str() );
}
Flush();
SetShowLogOutput( false );
g_fileLog->Close();
g_fileInfo->Close();
g_fileUserLog->Close();
g_fileTimeLog->Close();
SAFE_DELETE( g_Mutex );
SAFE_DELETE( g_fileLog );
SAFE_DELETE( g_fileInfo );
SAFE_DELETE( g_fileUserLog );
}
void RageLog::SetLogToDisk( bool b )
{
if( m_bLogToDisk == b )
return;
m_bLogToDisk = b;
if( !m_bLogToDisk )
{
if( g_fileLog->IsOpen() )
g_fileLog->Close();
return;
}
if( !g_fileLog->Open( LOG_PATH, RageFile::WRITE|RageFile::STREAMED ) )
fprintf( stderr, "Couldn't open %s: %s\n", LOG_PATH, g_fileLog->GetError().c_str() );
}
void RageLog::SetInfoToDisk( bool b )
{
if( m_bInfoToDisk == b )
return;
m_bInfoToDisk = b;
if( !m_bInfoToDisk )
{
if( g_fileInfo->IsOpen() )
g_fileInfo->Close();
return;
}
if( !g_fileInfo->Open( INFO_PATH, RageFile::WRITE|RageFile::STREAMED ) )
fprintf( stderr, "Couldn't open %s: %s\n", INFO_PATH, g_fileInfo->GetError().c_str() );
}
void RageLog::SetUserLogToDisk( bool b )
{
if( m_bUserLogToDisk == b )
return;
m_bUserLogToDisk = b;
if( !m_bUserLogToDisk )
{
if( g_fileUserLog->IsOpen() )
g_fileUserLog->Close();
return;
}
if( !g_fileUserLog->Open(USER_PATH, RageFile::WRITE|RageFile::STREAMED) )
fprintf( stderr, "Couldn't open %s: %s\n", USER_PATH, g_fileUserLog->GetError().c_str() );
}
void RageLog::SetFlushing( bool b )
{
m_bFlush = b;
}
/* Enable or disable display of output to stdout, or a console window in Windows. */
void RageLog::SetShowLogOutput( bool show )
{
m_bShowLogOutput = show;
#if defined(WIN32)
if( m_bShowLogOutput )
{
// create a new console window and attach standard handles
AllocConsole();
freopen( "CONOUT$","wb", stdout );
freopen( "CONOUT$","wb", stderr );
}
else
{
FreeConsole();
}
#endif
}
void RageLog::Trace( const char *fmt, ... )
{
va_list va;
va_start( va, fmt );
RString sBuff = vssprintf( fmt, va );
va_end( va );
Write( 0, sBuff );
}
/* Use this for more important information; it'll always be included
* in crash dumps. */
void RageLog::Info( const char *fmt, ... )
{
va_list va;
va_start( va, fmt );
RString sBuff = vssprintf( fmt, va );
va_end( va );
Write( WRITE_TO_INFO, sBuff );
}
void RageLog::Warn( const char *fmt, ... )
{
va_list va;
va_start( va, fmt );
RString sBuff = vssprintf( fmt, va );
va_end( va );
Write( WRITE_TO_INFO | WRITE_LOUD, sBuff );
}
void RageLog::Time(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
RString sBuff = vssprintf(fmt, va);
va_end(va);
Write(WRITE_TO_TIME, sBuff);
}
void RageLog::UserLog( const RString &sType, const RString &sElement, const char *fmt, ... )
{
va_list va;
va_start( va, fmt );
RString sBuf = vssprintf( fmt, va );
va_end( va );
if( !sType.empty() )
sBuf = ssprintf( "%s \"%s\" %s", sType.c_str(), sElement.c_str(), sBuf.c_str() );
Write( WRITE_TO_USER_LOG, sBuf );
}
void RageLog::Write( int where, const RString &sLine )
{
LockMut( *g_Mutex );
const char *const sWarningSeparator = "/////////////////////////////////////////";
vector<RString> asLines;
split( sLine, "\n", asLines, false );
if( where & WRITE_LOUD )
{
if( m_bLogToDisk && g_fileLog->IsOpen() )
g_fileLog->PutLine( sWarningSeparator );
puts( sWarningSeparator );
}
RString sTimestamp = SecondsToMMSSMsMsMs( RageTimer::GetTimeSinceStart() ) + ": ";
RString sWarning;
if( where & WRITE_LOUD )
sWarning = "WARNING: ";
for( unsigned i = 0; i < asLines.size(); ++i )
{
RString &sStr = asLines[i];
if( sWarning.size() )
sStr.insert( 0, sWarning );
if( m_bShowLogOutput || (where&WRITE_TO_INFO) )
puts(sStr); //fputws( (const wchar_t *)sStr.c_str(), stdout );
if( where & WRITE_TO_INFO )
AddToInfo( sStr );
if( m_bLogToDisk && (where&WRITE_TO_INFO) && g_fileInfo->IsOpen() )
g_fileInfo->PutLine( sStr );
if( m_bUserLogToDisk && (where&WRITE_TO_USER_LOG) && g_fileUserLog->IsOpen() )
g_fileUserLog->PutLine( sStr );
/* Add a timestamp to log.txt and RecentLogs, but not the rest of info.txt
* and stdout. */
sStr.insert( 0, sTimestamp );
if(where & WRITE_TO_TIME)
g_fileTimeLog->PutLine(sStr);
AddToRecentLogs( sStr );
if( m_bLogToDisk && g_fileLog->IsOpen() )
g_fileLog->PutLine( sStr );
}
if( where & WRITE_LOUD )
{
if( m_bLogToDisk && g_fileLog->IsOpen() && (where & WRITE_LOUD) )
g_fileLog->PutLine( sWarningSeparator );
puts( sWarningSeparator );
}
if( m_bFlush || (where & WRITE_TO_INFO) )
Flush();
}
void RageLog::Flush()
{
g_fileLog->Flush();
g_fileInfo->Flush();
g_fileTimeLog->Flush();
g_fileUserLog->Flush();
}
#define NEWLINE "\n"
static char staticlog[1024*32]="";
static unsigned staticlog_size = 0;
void RageLog::AddToInfo( const RString &str )
{
static bool limit_reached = false;
if( limit_reached )
return;
unsigned len = str.size() + strlen( NEWLINE );
if( staticlog_size + len > sizeof(staticlog) )
{
const RString txt( NEWLINE "Staticlog limit reached" NEWLINE );
const unsigned pos = min( staticlog_size, sizeof(staticlog) - txt.size() );
memcpy( staticlog+pos, txt.data(), txt.size() );
limit_reached = true;
return;
}
memcpy( staticlog+staticlog_size, str.data(), str.size() );
staticlog_size += str.size();
memcpy( staticlog+staticlog_size, NEWLINE, strlen(NEWLINE) );
staticlog_size += strlen( NEWLINE );
}
const char *RageLog::GetInfo()
{
staticlog[ sizeof(staticlog)-1 ] = 0;
return staticlog;
}
static const int BACKLOG_LINES = 10;
static char backlog[BACKLOG_LINES][1024];
static int backlog_start=0, backlog_cnt=0;
void RageLog::AddToRecentLogs( const RString &str )
{
unsigned len = str.size();
if( len > sizeof(backlog[backlog_start])-1 )
len = sizeof(backlog[backlog_start])-1;
strncpy( backlog[backlog_start], str, len );
backlog[backlog_start] [ len ] = 0;
backlog_start++;
if( backlog_start > backlog_cnt )
backlog_cnt=backlog_start;
backlog_start %= BACKLOG_LINES;
}
const char *RageLog::GetRecentLog( int n )
{
if( n >= BACKLOG_LINES || n >= backlog_cnt )
return NULL;
if( backlog_cnt == BACKLOG_LINES )
{
n += backlog_start;
n %= BACKLOG_LINES;
}
/* Make sure it's terminated: */
backlog[n][ sizeof(backlog[n])-1 ] = 0;
return backlog[n];
}
static char g_AdditionalLogStr[10240] = "";
static int g_AdditionalLogSize = 0;
void RageLog::UpdateMappedLog()
{
RString str;
FOREACHM_CONST( RString, RString, LogMaps, i )
str += ssprintf( "%s" NEWLINE, i->second.c_str() );
g_AdditionalLogSize = min( sizeof(g_AdditionalLogStr), str.size()+1 );
memcpy( g_AdditionalLogStr, str.c_str(), g_AdditionalLogSize );
g_AdditionalLogStr[ sizeof(g_AdditionalLogStr)-1 ] = 0;
}
const char *RageLog::GetAdditionalLog()
{
int size = min( g_AdditionalLogSize, (int) sizeof(g_AdditionalLogStr)-1 );
g_AdditionalLogStr[size] = 0;
return g_AdditionalLogStr;
}
void RageLog::MapLog( const RString &key, const char *fmt, ... )
{
RString s;
va_list va;
va_start( va, fmt );
s += vssprintf( fmt, va );
va_end( va );
LogMaps[key] = s;
UpdateMappedLog();
}
void RageLog::UnmapLog( const RString &key )
{
LogMaps.erase( key );
UpdateMappedLog();
}
void ShowWarningOrTrace( const char *file, int line, const char *message, bool bWarning )
{
/* Ignore everything up to and including the first "src/". */
const char *temp = strstr( file, "src/" );
if( temp )
file = temp + 4;
void (RageLog::*method)(const char *fmt, ...) = bWarning ? &RageLog::Warn : &RageLog::Trace;
if( LOG )
(LOG->*method)( "%s:%i: %s", file, line, message );
else
fprintf( stderr, "%s:%i: %s\n", file, line, message );
}
/*
* Copyright (c) 2001-2004 Chris Danford, Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/