Profiler: Shift+F11 to save profile data into logs/profile.txt
This was SVN commit r3263.
This commit is contained in:
parent
a40ee4bcbc
commit
2d477a09cf
@ -202,6 +202,10 @@ hotkey.jukebox = "Ctrl+M" ; Enable/disable tracklist viewer.
|
|||||||
hotkey.next.song = "M" ; Start another music track.
|
hotkey.next.song = "M" ; Start another music track.
|
||||||
hotkey.audio.toggle = "Ctrl+A" ; Enable/disable sound.
|
hotkey.audio.toggle = "Ctrl+A" ; Enable/disable sound.
|
||||||
|
|
||||||
|
; > PROFILER
|
||||||
|
hotkey.profile.toggle = "F11" ; Enable/disable real-time profiler
|
||||||
|
hotkey.profile.save = "Shift+F11" ; Save current profiler data to "logs/profile.txt"
|
||||||
|
|
||||||
; UNMAPPED KEY/MOUSE REFERENCE:
|
; UNMAPPED KEY/MOUSE REFERENCE:
|
||||||
|
|
||||||
; LMB ; select unit
|
; LMB ; select unit
|
||||||
|
@ -185,6 +185,14 @@ using namespace std;
|
|||||||
#define _totupper toupper
|
#define _totupper toupper
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
CStr CStr::Repeat(CStr String, size_t Reps)
|
||||||
|
{
|
||||||
|
CStr ret;
|
||||||
|
ret.reserve(String.Length() * Reps);
|
||||||
|
while (Reps--) ret += String;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// Construction and assignment from numbers:
|
// Construction and assignment from numbers:
|
||||||
|
|
||||||
#define NUM_TYPE(T) \
|
#define NUM_TYPE(T) \
|
||||||
@ -497,6 +505,37 @@ CStr CStr::Trim(PS_TRIM_MODE Mode) const
|
|||||||
return substr(Left, Right-Left+1);
|
return substr(Left, Right-Left+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CStr CStr::Pad(PS_TRIM_MODE Mode, size_t Length) const
|
||||||
|
{
|
||||||
|
size_t Left = 0, Right = 0;
|
||||||
|
|
||||||
|
if (Length <= length())
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
// From here: Length-length() >= 1
|
||||||
|
|
||||||
|
switch (Mode)
|
||||||
|
{
|
||||||
|
case PS_TRIM_LEFT:
|
||||||
|
Left = Length - length();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PS_TRIM_RIGHT:
|
||||||
|
Right = Length - length();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PS_TRIM_BOTH:
|
||||||
|
Left = (Length - length() + 1)/2;
|
||||||
|
Right = (Length - length() - 1)/2; // cannot be negative
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
debug_warn("CStr::Trim: invalid Mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::tstring(Left, _T(' ')) + *this + std::tstring(Right, _T(' '));
|
||||||
|
}
|
||||||
|
|
||||||
// Concatenation:
|
// Concatenation:
|
||||||
|
|
||||||
CStr CStr::operator+(const CStr& Str)
|
CStr CStr::operator+(const CStr& Str)
|
||||||
|
@ -89,6 +89,9 @@ public:
|
|||||||
CStr(const tchar Char) : std::tstring(1, Char) {} // std::string's constructor is (repeats, chr)
|
CStr(const tchar Char) : std::tstring(1, Char) {} // std::string's constructor is (repeats, chr)
|
||||||
CStr(std::tstring String) : std::tstring(String) {}
|
CStr(std::tstring String) : std::tstring(String) {}
|
||||||
|
|
||||||
|
// Named constructor, to avoid overload overload.
|
||||||
|
static CStr Repeat(CStr String, size_t Reps);
|
||||||
|
|
||||||
// CStr(8|W) construction from utf16strings, except on MSVC CStrW where
|
// CStr(8|W) construction from utf16strings, except on MSVC CStrW where
|
||||||
// CStrW === utf16string
|
// CStrW === utf16string
|
||||||
#if !(MSC_VERSION && defined(_UNICODE))
|
#if !(MSC_VERSION && defined(_UNICODE))
|
||||||
@ -190,6 +193,9 @@ public:
|
|||||||
// Returns a trimmed string, removes whitespace from the left/right/both
|
// Returns a trimmed string, removes whitespace from the left/right/both
|
||||||
CStr Trim(PS_TRIM_MODE Mode) const;
|
CStr Trim(PS_TRIM_MODE Mode) const;
|
||||||
|
|
||||||
|
// Returns a padded string, adding spaces to the left/right/both
|
||||||
|
CStr Pad(PS_TRIM_MODE Mode, size_t Length) const;
|
||||||
|
|
||||||
// Overloaded operations
|
// Overloaded operations
|
||||||
|
|
||||||
CStr& operator=(int Number);
|
CStr& operator=(int Number);
|
||||||
|
@ -131,6 +131,7 @@ static SHotkeyInfo hotkeyInfo[] =
|
|||||||
{ HOTKEY_CONTEXTORDER_PREVIOUS, "contextorder.previous", SDLK_LEFTBRACKET, 0 },
|
{ HOTKEY_CONTEXTORDER_PREVIOUS, "contextorder.previous", SDLK_LEFTBRACKET, 0 },
|
||||||
{ HOTKEY_HIGHLIGHTALL, "highlightall", SDLK_o, 0 },
|
{ HOTKEY_HIGHLIGHTALL, "highlightall", SDLK_o, 0 },
|
||||||
{ HOTKEY_PROFILE_TOGGLE, "profile.toggle", SDLK_F11, 0 },
|
{ HOTKEY_PROFILE_TOGGLE, "profile.toggle", SDLK_F11, 0 },
|
||||||
|
{ HOTKEY_PROFILE_SAVE, "profile.save", 0, 0 },
|
||||||
{ HOTKEY_PLAYMUSIC, "playmusic", SDLK_p, 0 },
|
{ HOTKEY_PLAYMUSIC, "playmusic", SDLK_p, 0 },
|
||||||
{ HOTKEY_WATER_TOGGLE, "water.toggle", SDLK_q, 0 },
|
{ HOTKEY_WATER_TOGGLE, "water.toggle", SDLK_q, 0 },
|
||||||
{ HOTKEY_WATER_RAISE, "water.toggle", SDLK_a, 0 },
|
{ HOTKEY_WATER_RAISE, "water.toggle", SDLK_a, 0 },
|
||||||
|
@ -94,6 +94,7 @@ enum
|
|||||||
HOTKEY_CONTEXTORDER_PREVIOUS,
|
HOTKEY_CONTEXTORDER_PREVIOUS,
|
||||||
HOTKEY_HIGHLIGHTALL,
|
HOTKEY_HIGHLIGHTALL,
|
||||||
HOTKEY_PROFILE_TOGGLE,
|
HOTKEY_PROFILE_TOGGLE,
|
||||||
|
HOTKEY_PROFILE_SAVE,
|
||||||
HOTKEY_PLAYMUSIC,
|
HOTKEY_PLAYMUSIC,
|
||||||
HOTKEY_WATER_TOGGLE,
|
HOTKEY_WATER_TOGGLE,
|
||||||
HOTKEY_WATER_RAISE,
|
HOTKEY_WATER_RAISE,
|
||||||
|
@ -15,13 +15,16 @@
|
|||||||
#include "Profile.h"
|
#include "Profile.h"
|
||||||
#include "Renderer.h"
|
#include "Renderer.h"
|
||||||
#include "lib/res/graphics/unifont.h"
|
#include "lib/res/graphics/unifont.h"
|
||||||
|
#include "lib/res/file/file.h"
|
||||||
#include "Hotkey.h"
|
#include "Hotkey.h"
|
||||||
|
#include "ps/CLogger.h"
|
||||||
|
|
||||||
extern int g_xres, g_yres;
|
extern int g_xres, g_yres;
|
||||||
|
|
||||||
struct CProfileViewerInternals
|
struct CProfileViewerInternals
|
||||||
{
|
{
|
||||||
|
CProfileViewerInternals() {}
|
||||||
|
|
||||||
/// Whether the profiling display is currently visible
|
/// Whether the profiling display is currently visible
|
||||||
bool profileVisible;
|
bool profileVisible;
|
||||||
|
|
||||||
@ -34,6 +37,14 @@ struct CProfileViewerInternals
|
|||||||
/// Helper functions
|
/// Helper functions
|
||||||
void TableIsDeleted(AbstractProfileTable* table);
|
void TableIsDeleted(AbstractProfileTable* table);
|
||||||
void NavigateTree(int id);
|
void NavigateTree(int id);
|
||||||
|
|
||||||
|
/// File for saved profile output (reset when the game is restarted)
|
||||||
|
std::ofstream outputStream;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Cannot be copied/assigned, because of the ofstream
|
||||||
|
CProfileViewerInternals(const CProfileViewerInternals& rhs);
|
||||||
|
const CProfileViewerInternals& operator=(const CProfileViewerInternals& rhs);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -281,6 +292,11 @@ InReaction CProfileViewer::Input(const SDL_Event* ev)
|
|||||||
}
|
}
|
||||||
return( IN_HANDLED );
|
return( IN_HANDLED );
|
||||||
}
|
}
|
||||||
|
else if( ev->user.code == HOTKEY_PROFILE_SAVE )
|
||||||
|
{
|
||||||
|
SaveToFile();
|
||||||
|
return( IN_HANDLED );
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return( IN_PASS );
|
return( IN_PASS );
|
||||||
@ -301,3 +317,122 @@ void CProfileViewer::AddRootTable(AbstractProfileTable* table)
|
|||||||
m->rootTables.push_back(table);
|
m->rootTables.push_back(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct WriteTable
|
||||||
|
{
|
||||||
|
std::ofstream& f;
|
||||||
|
WriteTable(std::ofstream& f) : f(f) {}
|
||||||
|
|
||||||
|
void operator() (AbstractProfileTable* table)
|
||||||
|
{
|
||||||
|
std::vector<CStr> data; // 2d array of (rows+head)*columns elements
|
||||||
|
|
||||||
|
// Add column headers to 'data'
|
||||||
|
for (std::vector<ProfileColumn>::const_iterator col_it = table->GetColumns().begin();
|
||||||
|
col_it != table->GetColumns().end(); ++col_it)
|
||||||
|
data.push_back(col_it->title);
|
||||||
|
|
||||||
|
// Recursively add all profile data to 'data'
|
||||||
|
WriteRows(1, table, data);
|
||||||
|
|
||||||
|
// Calculate the width of each column ( = the maximum width of
|
||||||
|
// any value in that column)
|
||||||
|
std::vector<size_t> columnWidths;
|
||||||
|
size_t cols = table->GetColumns().size();
|
||||||
|
for (size_t c = 0; c < cols; ++c)
|
||||||
|
{
|
||||||
|
size_t max = 0;
|
||||||
|
for (size_t i = c; i < data.size(); i += cols)
|
||||||
|
max = std::max(max, data[i].length());
|
||||||
|
columnWidths.push_back(max);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output data as a formatted table:
|
||||||
|
|
||||||
|
f << "\n\n" << table->GetTitle() << "\n";
|
||||||
|
|
||||||
|
for (size_t r = 0; r < data.size()/cols; ++r)
|
||||||
|
{
|
||||||
|
for (size_t c = 0; c < cols; ++c)
|
||||||
|
f << (c ? " | " : "\n")
|
||||||
|
<< data[r*cols + c].Pad(PS_TRIM_RIGHT, columnWidths[c]);
|
||||||
|
|
||||||
|
// Add dividers under some rows. (Currently only the first, since
|
||||||
|
// that contains the column headers.)
|
||||||
|
if (r == 0)
|
||||||
|
for (size_t c = 0; c < cols; ++c)
|
||||||
|
f << (c ? "-|-" : "\n")
|
||||||
|
<< CStr::Repeat("-", columnWidths[c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteRows(int indent, AbstractProfileTable* table, std::vector<CStr>& data)
|
||||||
|
{
|
||||||
|
for (size_t r = 0; r < table->GetNumberRows(); ++r)
|
||||||
|
{
|
||||||
|
// Do pretty tree-structure indenting
|
||||||
|
CStr indentation = CStr::Repeat("| ", indent-1);
|
||||||
|
if (r+1 == table->GetNumberRows())
|
||||||
|
indentation += "'-";
|
||||||
|
else
|
||||||
|
indentation += "|-";
|
||||||
|
|
||||||
|
for (size_t c = 0; c < table->GetColumns().size(); ++c)
|
||||||
|
if (c == 0)
|
||||||
|
data.push_back(indentation + table->GetCellText(r, c));
|
||||||
|
else
|
||||||
|
data.push_back(table->GetCellText(r, c));
|
||||||
|
|
||||||
|
if (table->GetChild(r))
|
||||||
|
WriteRows(indent+1, table->GetChild(r), data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const WriteTable& operator=(const WriteTable&);
|
||||||
|
};
|
||||||
|
|
||||||
|
bool SortByName(AbstractProfileTable* a, AbstractProfileTable* b)
|
||||||
|
{
|
||||||
|
return (a->GetName() < b->GetName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CProfileViewer::SaveToFile()
|
||||||
|
{
|
||||||
|
// Open the file, if necessary. If this method is called several times,
|
||||||
|
// the profile results will be appended to the previous ones from the same
|
||||||
|
// run.
|
||||||
|
if (! m->outputStream.is_open())
|
||||||
|
{
|
||||||
|
// Work out the filename
|
||||||
|
char N_path[PATH_MAX];
|
||||||
|
(void)file_make_full_native_path("../logs", N_path);
|
||||||
|
PathPackage pp;
|
||||||
|
(void)pp_set_dir(&pp, N_path);
|
||||||
|
(void)pp_append_file(&pp, "profile.txt");
|
||||||
|
|
||||||
|
// Open the file. (It will be closed when the CProfileViewer
|
||||||
|
// destructor is called.)
|
||||||
|
m->outputStream.open(pp.path, std::ofstream::out | std::ofstream::trunc);
|
||||||
|
|
||||||
|
if (m->outputStream.fail())
|
||||||
|
{
|
||||||
|
LOG(ERROR, "profiler", "Failed to open profile log file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t t;
|
||||||
|
time(&t);
|
||||||
|
m->outputStream << "================================================================\n\n";
|
||||||
|
m->outputStream << "PS profiler snapshot - " << asctime(localtime(&t));
|
||||||
|
|
||||||
|
std::vector<AbstractProfileTable*> tables = m->rootTables;
|
||||||
|
std::sort(tables.begin(), tables.end(), SortByName);
|
||||||
|
std::for_each(tables.begin(), tables.end(), WriteTable(m->outputStream));
|
||||||
|
|
||||||
|
m->outputStream << "\n\n================================================================\n";
|
||||||
|
m->outputStream.flush();
|
||||||
|
}
|
@ -163,6 +163,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
static InReaction InputThunk(const SDL_Event* ev);
|
static InReaction InputThunk(const SDL_Event* ev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SaveToFile: Saves the current profiler data (for all profile tables)
|
||||||
|
* to a file in the 'logs' directory.
|
||||||
|
*/
|
||||||
|
void SaveToFile();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CProfileViewerInternals* m;
|
CProfileViewerInternals* m;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user