OpenTTD Source: src/newgrf_profiling.cpp Source File

OpenTTD Source 15.3

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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#include "stdafx.h"
11
12#include "newgrf_profiling.h"
13#include "fileio_func.h"
14#include "string_func.h"
15#include "console_func.h"
16#include "spritecache.h"
17#include "3rdparty/fmt/chrono.h"
18#include "timer/timer.h"
19#include "timer/timer_game_tick.h"
20
21#include <chrono>
22
23#include "safeguards.h"
24
25std::vector<NewGRFProfiler> _newgrf_profilers;
26
27
33NewGRFProfiler::NewGRFProfiler(const GRFFile *grffile) : grffile(grffile)
34{
35}
36
40NewGRFProfiler::~NewGRFProfiler()
41{
42}
43
48 void NewGRFProfiler::BeginResolve(const ResolverObject &resolver)
49{
50 using namespace std::chrono;
51 this->cur_call.root_sprite = resolver.root_spritegroup->nfo_line;
52 this->cur_call.subs = 0;
53 this->cur_call.time = (uint32_t)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
54 this->cur_call.tick = TimerGameTick::counter;
55 this->cur_call.cb = resolver.callback;
56 this->cur_call.feat = resolver.GetFeature();
57 this->cur_call.item = resolver.GetDebugID();
58}
59
63 void NewGRFProfiler::EndResolve(const ResolverResult &result)
64{
65 using namespace std::chrono;
66 this->cur_call.time = (uint32_t)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count() - this->cur_call.time;
67
68 struct visitor {
69 uint32_t operator()(std::monostate)
70 {
71 return 0;
72 }
73 uint32_t operator()(CallbackResult cb_result)
74 {
75 return cb_result;
76 }
77 uint32_t operator()(const ResultSpriteGroup *group)
78 {
79 return group == nullptr ? 0 : GetSpriteLocalID(group->sprite);
80 }
81 uint32_t operator()(const TileLayoutSpriteGroup *group)
82 {
83 return group == nullptr ? 0 : group->nfo_line;
84 }
85 uint32_t operator()(const IndustryProductionSpriteGroup *group)
86 {
87 return group == nullptr ? 0 : group->nfo_line;
88 }
89 };
90 this->cur_call.result = std::visit(visitor{}, result);
91
92 this->calls.push_back(this->cur_call);
93}
94
98 void NewGRFProfiler::RecursiveResolve()
99{
100 this->cur_call.subs += 1;
101}
102
103void NewGRFProfiler::Start()
104{
105 this->Abort();
106 this->active = true;
107 this->start_tick = TimerGameTick::counter;
108}
109
110uint32_t NewGRFProfiler::Finish()
111{
112 if (!this->active) return 0;
113
114 if (this->calls.empty()) {
115 IConsolePrint(CC_DEBUG, "Finished profile of NewGRF [{:08X}], no events collected, not writing a file.", std::byteswap(this->grffile->grfid));
116
117 this->Abort();
118 return 0;
119 }
120
121 std::string filename = this->GetOutputFilename();
122 IConsolePrint(CC_DEBUG, "Finished profile of NewGRF [{:08X}], writing {} events to '{}'.", std::byteswap(this->grffile->grfid), this->calls.size(), filename);
123
124 uint32_t total_microseconds = 0;
125
126 auto f = FioFOpenFile(filename, "wt", Subdirectory::NO_DIRECTORY);
127
128 if (!f.has_value()) {
129 IConsolePrint(CC_ERROR, "Failed to open '{}' for writing.", filename);
130 } else {
131 fmt::print(*f, "Tick,Sprite,Feature,Item,CallbackID,Microseconds,Depth,Result\n");
132 for (const Call &c : this->calls) {
133 fmt::print(*f, "{},{},0x{:X},{},0x{:X},{},{},{}\n", c.tick, c.root_sprite, c.feat, c.item, (uint)c.cb, c.time, c.subs, c.result);
134 total_microseconds += c.time;
135 }
136 }
137
138 this->Abort();
139 return total_microseconds;
140}
141
142void NewGRFProfiler::Abort()
143{
144 this->active = false;
145 this->calls.clear();
146}
147
152std::string NewGRFProfiler::GetOutputFilename() const
153{
154 return fmt::format("{}grfprofile-{:%Y%m%d-%H%M}-{:08X}.csv", FiosGetScreenshotDir(), fmt::localtime(time(nullptr)), std::byteswap(this->grffile->grfid));
155}
156
157/* static */ uint32_t NewGRFProfiler::FinishAll()
158{
159 NewGRFProfiler::AbortTimer();
160
161 uint64_t max_ticks = 0;
162 uint32_t total_microseconds = 0;
163 for (NewGRFProfiler &pr : _newgrf_profilers) {
164 if (pr.active) {
165 total_microseconds += pr.Finish();
166 max_ticks = std::max(max_ticks, TimerGameTick::counter - pr.start_tick);
167 }
168 }
169
170 if (total_microseconds > 0 && max_ticks > 0) {
171 IConsolePrint(CC_DEBUG, "Total NewGRF callback processing: {} microseconds over {} ticks.", total_microseconds, max_ticks);
172 }
173
174 return total_microseconds;
175}
176
180 static TimeoutTimer<TimerGameTick> _profiling_finish_timeout({ TimerGameTick::Priority::NONE, 0 }, []()
181{
182 NewGRFProfiler::FinishAll();
183});
184
188/* static */ void NewGRFProfiler::StartTimer(uint64_t ticks)
189{
190 _profiling_finish_timeout.Reset({ TimerGameTick::Priority::NONE, static_cast<uint>(ticks) });
191}
192
196/* static */ void NewGRFProfiler::AbortTimer()
197{
198 _profiling_finish_timeout.Abort();
199}
TimeoutTimer
A timeout timer will fire once after the interval.
Definition timer.h:116
TimeoutTimer::Reset
void Reset()
Reset the timer, so it will fire again after the timeout.
Definition timer.h:140
TimerGameTick::counter
static TickCounter counter
Monotonic counter, in ticks, since start of game.
Definition timer_game_tick.h:60
TimerGameTick::NONE
@ NONE
These timers can be executed in any order; the order is not relevant.
Definition timer_game_tick.h:28
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:90
console_func.h
Console functions used outside of the console code.
CC_DEBUG
static const TextColour CC_DEBUG
Colour for debug output.
Definition console_type.h:28
CC_ERROR
static const TextColour CC_ERROR
Colour for error lines.
Definition console_type.h:24
FioFOpenFile
std::optional< FileHandle > FioFOpenFile(std::string_view filename, std::string_view mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition fileio.cpp:242
fileio_func.h
Functions for Standard In/Out file operations.
FiosGetScreenshotDir
std::string_view FiosGetScreenshotDir()
Get the directory for screenshots.
Definition fios.cpp:581
NO_DIRECTORY
@ NO_DIRECTORY
A path without any base directory.
Definition fileio_type.h:107
_profiling_finish_timeout
static TimeoutTimer< TimerGameTick > _profiling_finish_timeout({ TimerGameTick::Priority::NONE, 0 }, []() { NewGRFProfiler::FinishAll();})
Check whether profiling is active and should be finished.
newgrf_profiling.h
Profiling of NewGRF action 2 handling.
ResolverResult
std::variant< std::monostate, CallbackResult, const ResultSpriteGroup *, const TileLayoutSpriteGroup *, const IndustryProductionSpriteGroup * > ResolverResult
Result of resolving sprite groups:
Definition newgrf_spritegroup.h:37
safeguards.h
A number of safeguards to prevent using unsafe methods.
GetSpriteLocalID
uint32_t GetSpriteLocalID(SpriteID sprite)
Get the GRF-local sprite id of a given sprite.
Definition spritecache.cpp:163
spritecache.h
Functions to cache sprites in memory.
stdafx.h
Definition of base types and functions in a cross-platform compatible way.
string_func.h
Functions related to low-level strings.
GRFFile
Dynamic data of a loaded NewGRF.
Definition newgrf.h:114
IndustryProductionSpriteGroup
Definition newgrf_spritegroup.h:247
NewGRFProfiler::Call::feat
GrfSpecFeature feat
GRF feature being resolved for.
Definition newgrf_profiling.h:48
NewGRFProfiler::Call::time
uint32_t time
Time taken for resolution (microseconds)
Definition newgrf_profiling.h:45
NewGRFProfiler::Call::subs
uint32_t subs
Sub-calls to other sprite groups.
Definition newgrf_profiling.h:44
NewGRFProfiler::Call::tick
uint64_t tick
Game tick.
Definition newgrf_profiling.h:46
NewGRFProfiler::Call::root_sprite
uint32_t root_sprite
Pseudo-sprite index in GRF file.
Definition newgrf_profiling.h:41
NewGRFProfiler::Call::cb
CallbackID cb
Callback ID.
Definition newgrf_profiling.h:47
NewGRFProfiler::Call::item
uint32_t item
Local ID of item being resolved for.
Definition newgrf_profiling.h:42
NewGRFProfiler::Call::result
uint32_t result
Result of callback.
Definition newgrf_profiling.h:43
NewGRFProfiler
Callback profiler for NewGRF development.
Definition newgrf_profiling.h:22
NewGRFProfiler::RecursiveResolve
void RecursiveResolve()
Capture a recursive sprite group resolution.
Definition newgrf_profiling.cpp:98
NewGRFProfiler::StartTimer
static void StartTimer(uint64_t ticks)
Start the timeout timer that will finish all profiling sessions.
Definition newgrf_profiling.cpp:188
NewGRFProfiler::EndResolve
void EndResolve(const ResolverResult &result)
Capture the completion of a sprite group resolution.
Definition newgrf_profiling.cpp:63
NewGRFProfiler::~NewGRFProfiler
~NewGRFProfiler()
Complete profiling session and write data to file.
Definition newgrf_profiling.cpp:40
NewGRFProfiler::cur_call
Call cur_call
Data for current call in progress.
Definition newgrf_profiling.h:54
NewGRFProfiler::active
bool active
Is this profiler collecting data.
Definition newgrf_profiling.h:52
NewGRFProfiler::grffile
const GRFFile * grffile
Which GRF is being profiled.
Definition newgrf_profiling.h:51
NewGRFProfiler::start_tick
uint64_t start_tick
Tick number this profiler was started on.
Definition newgrf_profiling.h:53
NewGRFProfiler::AbortTimer
static void AbortTimer()
Abort the timeout timer, so the timer callback is never called.
Definition newgrf_profiling.cpp:196
NewGRFProfiler::BeginResolve
void BeginResolve(const ResolverObject &resolver)
Capture the start of a sprite group resolution.
Definition newgrf_profiling.cpp:48
NewGRFProfiler::NewGRFProfiler
NewGRFProfiler(const GRFFile *grffile)
Create profiler object and begin profiling session.
Definition newgrf_profiling.cpp:33
NewGRFProfiler::calls
std::vector< Call > calls
All calls collected so far.
Definition newgrf_profiling.h:55
NewGRFProfiler::GetOutputFilename
std::string GetOutputFilename() const
Get name of the file that will be written.
Definition newgrf_profiling.cpp:152
ResolverObject
Interface for SpriteGroup-s to access the gamestate.
Definition newgrf_spritegroup.h:288
ResolverObject::GetDebugID
virtual uint32_t GetDebugID() const
Get an identifier for the item being resolved.
Definition newgrf_spritegroup.h:443
ResolverObject::callback
CallbackID callback
Callback being resolved.
Definition newgrf_spritegroup.h:340
ResolverObject::GetFeature
virtual GrfSpecFeature GetFeature() const
Get the feature number being resolved for.
Definition newgrf_spritegroup.h:437
ResolverObject::root_spritegroup
const SpriteGroup * root_spritegroup
Root SpriteGroup to use for resolving.
Definition newgrf_spritegroup.h:353
ResultSpriteGroup
Definition newgrf_spritegroup.h:217
TileLayoutSpriteGroup
Action 2 sprite layout for houses, industry tiles, objects and airport tiles.
Definition newgrf_spritegroup.h:236
timer.h
Definition of Interval and OneShot timers.
timer_game_tick.h
Definition of the tick-based game-timer.