OpenTTD Source: src/newgrf_profiling.cpp Source File

OpenTTD Source 14.0-beta3

newgrf_profiling.cpp
Go to the documentation of this file.
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD 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, version 2.
4 * OpenTTD 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.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10 #include "newgrf_profiling.h"
11 #include "fileio_func.h"
12 #include "string_func.h"
13 #include "console_func.h"
14 #include "spritecache.h"
15 #include "3rdparty/fmt/chrono.h"
16 #include "timer/timer.h"
17 #include "timer/timer_game_tick.h"
18
19 #include <chrono>
20
21
22 std::vector<NewGRFProfiler> _newgrf_profilers;
23
24
30 NewGRFProfiler::NewGRFProfiler(const GRFFile *grffile) : grffile{ grffile }, active{ false }, cur_call{}
31 {
32 }
33
37 NewGRFProfiler::~NewGRFProfiler()
38 {
39 }
40
45 void NewGRFProfiler::BeginResolve(const ResolverObject &resolver)
46 {
47 using namespace std::chrono;
48 this->cur_call.root_sprite = resolver.root_spritegroup->nfo_line;
49 this->cur_call.subs = 0;
50 this->cur_call.time = (uint32_t)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
51 this->cur_call.tick = TimerGameTick::counter;
52 this->cur_call.cb = resolver.callback;
53 this->cur_call.feat = resolver.GetFeature();
54 this->cur_call.item = resolver.GetDebugID();
55 }
56
60 void NewGRFProfiler::EndResolve(const SpriteGroup *result)
61 {
62 using namespace std::chrono;
63 this->cur_call.time = (uint32_t)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count() - this->cur_call.time;
64
65 if (result == nullptr) {
66 this->cur_call.result = 0;
67 } else if (result->type == SGT_CALLBACK) {
68 this->cur_call.result = static_cast<const CallbackResultSpriteGroup *>(result)->result;
69 } else if (result->type == SGT_RESULT) {
70 this->cur_call.result = GetSpriteLocalID(static_cast<const ResultSpriteGroup *>(result)->sprite);
71 } else {
72 this->cur_call.result = result->nfo_line;
73 }
74
75 this->calls.push_back(this->cur_call);
76 }
77
81 void NewGRFProfiler::RecursiveResolve()
82 {
83 this->cur_call.subs += 1;
84 }
85
86 void NewGRFProfiler::Start()
87 {
88 this->Abort();
89 this->active = true;
90 this->start_tick = TimerGameTick::counter;
91 }
92
93 uint32_t NewGRFProfiler::Finish()
94 {
95 if (!this->active) return 0;
96
97 if (this->calls.empty()) {
98 IConsolePrint(CC_DEBUG, "Finished profile of NewGRF [{:08X}], no events collected, not writing a file.", BSWAP32(this->grffile->grfid));
99
100 this->Abort();
101 return 0;
102 }
103
104 std::string filename = this->GetOutputFilename();
105 IConsolePrint(CC_DEBUG, "Finished profile of NewGRF [{:08X}], writing {} events to '{}'.", BSWAP32(this->grffile->grfid), this->calls.size(), filename);
106
107 FILE *f = FioFOpenFile(filename, "wt", Subdirectory::NO_DIRECTORY);
108 FileCloser fcloser(f);
109
110 uint32_t total_microseconds = 0;
111
112 fmt::print(f, "Tick,Sprite,Feature,Item,CallbackID,Microseconds,Depth,Result\n");
113 for (const Call &c : this->calls) {
114 fmt::print(f, "{},{},{:#X},{},{:#X},{},{},{}\n", c.tick, c.root_sprite, c.feat, c.item, (uint)c.cb, c.time, c.subs, c.result);
115 total_microseconds += c.time;
116 }
117
118 this->Abort();
119 return total_microseconds;
120 }
121
122 void NewGRFProfiler::Abort()
123 {
124 this->active = false;
125 this->calls.clear();
126 }
127
132 std::string NewGRFProfiler::GetOutputFilename() const
133 {
134 return fmt::format("{}grfprofile-{:%Y%m%d-%H%M}-{:08X}.csv", FiosGetScreenshotDir(), fmt::localtime(time(nullptr)), BSWAP32(this->grffile->grfid));
135 }
136
137 /* static */ uint32_t NewGRFProfiler::FinishAll()
138 {
139 NewGRFProfiler::AbortTimer();
140
141 uint64_t max_ticks = 0;
142 uint32_t total_microseconds = 0;
143 for (NewGRFProfiler &pr : _newgrf_profilers) {
144 if (pr.active) {
145 total_microseconds += pr.Finish();
146 max_ticks = std::max(max_ticks, TimerGameTick::counter - pr.start_tick);
147 }
148 }
149
150 if (total_microseconds > 0 && max_ticks > 0) {
151 IConsolePrint(CC_DEBUG, "Total NewGRF callback processing: {} microseconds over {} ticks.", total_microseconds, max_ticks);
152 }
153
154 return total_microseconds;
155 }
156
160 static TimeoutTimer<TimerGameTick> _profiling_finish_timeout(0, []()
161 {
162 NewGRFProfiler::FinishAll();
163 });
164
168 /* static */ void NewGRFProfiler::StartTimer(uint64_t ticks)
169 {
170 _profiling_finish_timeout.Reset(ticks);
171 }
172
176 /* static */ void NewGRFProfiler::AbortTimer()
177 {
178 _profiling_finish_timeout.Abort();
179 }
_profiling_finish_timeout
static TimeoutTimer< TimerGameTick > _profiling_finish_timeout(0, []() { NewGRFProfiler::FinishAll();})
Check whether profiling is active and should be finished.
TimerGameTick::counter
static TickCounter counter
Monotonic counter, in ticks, since start of game.
Definition: timer_game_tick.h:33
NewGRFProfiler::calls
std::vector< Call > calls
All calls collected so far.
Definition: newgrf_profiling.h:56
NewGRFProfiler::Call::tick
uint64_t tick
Game tick.
Definition: newgrf_profiling.h:47
NewGRFProfiler::Call::item
uint32_t item
Local ID of item being resolved for.
Definition: newgrf_profiling.h:43
NewGRFProfiler::start_tick
uint64_t start_tick
Tick number this profiler was started on.
Definition: newgrf_profiling.h:54
NewGRFProfiler::grffile
const GRFFile * grffile
Which GRF is being profiled.
Definition: newgrf_profiling.h:52
NewGRFProfiler::AbortTimer
static void AbortTimer()
Abort the timeout timer, so the timer callback is never called.
Definition: newgrf_profiling.cpp:176
ResolverObject
Interface for SpriteGroup-s to access the gamestate.
Definition: newgrf_spritegroup.h:307
NewGRFProfiler::RecursiveResolve
void RecursiveResolve()
Capture a recursive sprite group resolution.
Definition: newgrf_profiling.cpp:81
fileio_func.h
NewGRFProfiler::NewGRFProfiler
NewGRFProfiler(const GRFFile *grffile)
Create profiler object and begin profiling session.
Definition: newgrf_profiling.cpp:30
ResolverObject::GetFeature
virtual GrfSpecFeature GetFeature() const
Get the feature number being resolved for.
Definition: newgrf_spritegroup.h:399
GetSpriteLocalID
uint32_t GetSpriteLocalID(SpriteID sprite)
Get the GRF-local sprite id of a given sprite.
Definition: spritecache.cpp:172
NewGRFProfiler::EndResolve
void EndResolve(const SpriteGroup *result)
Capture the completion of a sprite group resolution.
Definition: newgrf_profiling.cpp:60
FileCloser
Auto-close a file upon scope exit.
Definition: fileio_func.h:118
BSWAP32
static uint32_t BSWAP32(uint32_t x)
Perform a 32 bits endianness bitswap on x.
Definition: bitmath_func.hpp:345
NewGRFProfiler::BeginResolve
void BeginResolve(const ResolverObject &resolver)
Capture the start of a sprite group resolution.
Definition: newgrf_profiling.cpp:45
NewGRFProfiler::StartTimer
static void StartTimer(uint64_t ticks)
Start the timeout timer that will finish all profiling sessions.
Definition: newgrf_profiling.cpp:168
CallbackResultSpriteGroup
Definition: newgrf_spritegroup.h:210
FioFOpenFile
FILE * FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition: fileio.cpp:263
ResolverObject::root_spritegroup
const SpriteGroup * root_spritegroup
Root SpriteGroup to use for resolving.
Definition: newgrf_spritegroup.h:336
NewGRFProfiler::~NewGRFProfiler
~NewGRFProfiler()
Complete profiling session and write data to file.
Definition: newgrf_profiling.cpp:37
NewGRFProfiler::Call::feat
GrfSpecFeature feat
GRF feature being resolved for.
Definition: newgrf_profiling.h:49
timer_game_tick.h
timer.h
ResolverObject::GetDebugID
virtual uint32_t GetDebugID() const
Get an identifier for the item being resolved.
Definition: newgrf_spritegroup.h:405
NewGRFProfiler::GetOutputFilename
std::string GetOutputFilename() const
Get name of the file that will be written.
Definition: newgrf_profiling.cpp:132
CC_DEBUG
static const TextColour CC_DEBUG
Colour for debug output.
Definition: console_type.h:28
NewGRFProfiler::active
bool active
Is this profiler collecting data.
Definition: newgrf_profiling.h:53
NewGRFProfiler::cur_call
Call cur_call
Data for current call in progress.
Definition: newgrf_profiling.h:55
string_func.h
spritecache.h
NewGRFProfiler::Call::time
uint32_t time
Time taken for resolution (microseconds)
Definition: newgrf_profiling.h:46
TimeoutTimer
A timeout timer will fire once after the interval.
Definition: timer.h:116
NewGRFProfiler
Callback profiler for NewGRF development.
Definition: newgrf_profiling.h:23
NO_DIRECTORY
@ NO_DIRECTORY
A path without any base directory.
Definition: fileio_type.h:126
ResolverObject::callback
CallbackID callback
Callback being resolved.
Definition: newgrf_spritegroup.h:325
console_func.h
newgrf_profiling.h
ResultSpriteGroup
Definition: newgrf_spritegroup.h:236
NewGRFProfiler::Call::result
uint32_t result
Result of callback.
Definition: newgrf_profiling.h:44
SpriteGroup
Definition: newgrf_spritegroup.h:57
NewGRFProfiler::Call::subs
uint32_t subs
Sub-calls to other sprite groups.
Definition: newgrf_profiling.h:45
NewGRFProfiler::Call::cb
CallbackID cb
Callback ID.
Definition: newgrf_profiling.h:48
GRFFile
Dynamic data of a loaded NewGRF.
Definition: newgrf.h:107
NewGRFProfiler::Call::root_sprite
uint32_t root_sprite
Pseudo-sprite index in GRF file.
Definition: newgrf_profiling.h:42
FiosGetScreenshotDir
const char * FiosGetScreenshotDir()
Get the directory for screenshots.
Definition: fios.cpp:597
IConsolePrint
void IConsolePrint(TextColour colour_code, const std::string &string)
Handle the printing of text entered into the console or redirected there by any other means.
Definition: console.cpp:91