From 2d477a09cf89b6f2cbee26883c605b9531e69ee0 Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Sat, 17 Dec 2005 02:33:57 +0000 Subject: [PATCH] Profiler: Shift+F11 to save profile data into logs/profile.txt This was SVN commit r3263. --- binaries/data/config/system.cfg | 4 + source/ps/CStr.cpp | 39 +++++++++ source/ps/CStr.h | 6 ++ source/ps/Hotkey.cpp | 1 + source/ps/Hotkey.h | 1 + source/ps/ProfileViewer.cpp | 137 +++++++++++++++++++++++++++++++- source/ps/ProfileViewer.h | 6 ++ 7 files changed, 193 insertions(+), 1 deletion(-) diff --git a/binaries/data/config/system.cfg b/binaries/data/config/system.cfg index 8c420e63f4..7ea9de2ffb 100644 --- a/binaries/data/config/system.cfg +++ b/binaries/data/config/system.cfg @@ -202,6 +202,10 @@ hotkey.jukebox = "Ctrl+M" ; Enable/disable tracklist viewer. hotkey.next.song = "M" ; Start another music track. 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: ; LMB ; select unit diff --git a/source/ps/CStr.cpp b/source/ps/CStr.cpp index b340973950..346f066366 100755 --- a/source/ps/CStr.cpp +++ b/source/ps/CStr.cpp @@ -185,6 +185,14 @@ using namespace std; #define _totupper toupper #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: #define NUM_TYPE(T) \ @@ -497,6 +505,37 @@ CStr CStr::Trim(PS_TRIM_MODE Mode) const 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: CStr CStr::operator+(const CStr& Str) diff --git a/source/ps/CStr.h b/source/ps/CStr.h index b69008f87b..40b35372f1 100755 --- a/source/ps/CStr.h +++ b/source/ps/CStr.h @@ -88,6 +88,9 @@ public: : std::tstring(String, Length) {} CStr(const tchar Char) : std::tstring(1, Char) {} // std::string's constructor is (repeats, chr) 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 // CStrW === utf16string @@ -190,6 +193,9 @@ public: // Returns a trimmed string, removes whitespace from the left/right/both 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 CStr& operator=(int Number); diff --git a/source/ps/Hotkey.cpp b/source/ps/Hotkey.cpp index 91b92b3619..9c8f93855a 100755 --- a/source/ps/Hotkey.cpp +++ b/source/ps/Hotkey.cpp @@ -131,6 +131,7 @@ static SHotkeyInfo hotkeyInfo[] = { HOTKEY_CONTEXTORDER_PREVIOUS, "contextorder.previous", SDLK_LEFTBRACKET, 0 }, { HOTKEY_HIGHLIGHTALL, "highlightall", SDLK_o, 0 }, { HOTKEY_PROFILE_TOGGLE, "profile.toggle", SDLK_F11, 0 }, + { HOTKEY_PROFILE_SAVE, "profile.save", 0, 0 }, { HOTKEY_PLAYMUSIC, "playmusic", SDLK_p, 0 }, { HOTKEY_WATER_TOGGLE, "water.toggle", SDLK_q, 0 }, { HOTKEY_WATER_RAISE, "water.toggle", SDLK_a, 0 }, diff --git a/source/ps/Hotkey.h b/source/ps/Hotkey.h index d8051b805d..6866f1d023 100755 --- a/source/ps/Hotkey.h +++ b/source/ps/Hotkey.h @@ -94,6 +94,7 @@ enum HOTKEY_CONTEXTORDER_PREVIOUS, HOTKEY_HIGHLIGHTALL, HOTKEY_PROFILE_TOGGLE, + HOTKEY_PROFILE_SAVE, HOTKEY_PLAYMUSIC, HOTKEY_WATER_TOGGLE, HOTKEY_WATER_RAISE, diff --git a/source/ps/ProfileViewer.cpp b/source/ps/ProfileViewer.cpp index e5f1117c09..6943a2c34c 100644 --- a/source/ps/ProfileViewer.cpp +++ b/source/ps/ProfileViewer.cpp @@ -15,13 +15,16 @@ #include "Profile.h" #include "Renderer.h" #include "lib/res/graphics/unifont.h" +#include "lib/res/file/file.h" #include "Hotkey.h" - +#include "ps/CLogger.h" extern int g_xres, g_yres; struct CProfileViewerInternals { + CProfileViewerInternals() {} + /// Whether the profiling display is currently visible bool profileVisible; @@ -34,6 +37,14 @@ struct CProfileViewerInternals /// Helper functions void TableIsDeleted(AbstractProfileTable* table); 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 ); } + else if( ev->user.code == HOTKEY_PROFILE_SAVE ) + { + SaveToFile(); + return( IN_HANDLED ); + } break; } return( IN_PASS ); @@ -301,3 +317,122 @@ void CProfileViewer::AddRootTable(AbstractProfileTable* table) m->rootTables.push_back(table); } +namespace +{ + struct WriteTable + { + std::ofstream& f; + WriteTable(std::ofstream& f) : f(f) {} + + void operator() (AbstractProfileTable* table) + { + std::vector data; // 2d array of (rows+head)*columns elements + + // Add column headers to 'data' + for (std::vector::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 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& 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 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(); +} \ No newline at end of file diff --git a/source/ps/ProfileViewer.h b/source/ps/ProfileViewer.h index 5abaecc30f..8fb9c2e4ff 100644 --- a/source/ps/ProfileViewer.h +++ b/source/ps/ProfileViewer.h @@ -162,6 +162,12 @@ public: * like a normal, global function input handler. */ 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: CProfileViewerInternals* m;