OpenTTD Source  1.11.2
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 "date_func.h"
12 #include "fileio_func.h"
13 #include "string_func.h"
14 #include "console_func.h"
15 #include "spritecache.h"
16 
17 #include <chrono>
18 #include <time.h>
19 
20 
21 std::vector<NewGRFProfiler> _newgrf_profilers;
22 Date _newgrf_profile_end_date;
23 
24 
30 NewGRFProfiler::NewGRFProfiler(const GRFFile *grffile) : grffile{ grffile }, active{ false }, cur_call{}
31 {
32 }
33 
38 {
39 }
40 
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)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
51  this->cur_call.tick = _tick_counter;
52  this->cur_call.cb = resolver.callback;
53  this->cur_call.feat = resolver.GetFeature();
54  this->cur_call.item = resolver.GetDebugID();
55 }
56 
61 {
62  using namespace std::chrono;
63  this->cur_call.time = (uint32)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 
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 = _tick_counter;
91 }
92 
93 uint32 NewGRFProfiler::Finish()
94 {
95  if (!this->active) return 0;
96 
97  if (this->calls.empty()) {
98  IConsolePrintF(CC_DEBUG, "Finished profile of NewGRF [%08X], no events collected, not writing a file", BSWAP32(this->grffile->grfid));
99  return 0;
100  }
101 
102  std::string filename = this->GetOutputFilename();
103  IConsolePrintF(CC_DEBUG, "Finished profile of NewGRF [%08X], writing %u events to %s", BSWAP32(this->grffile->grfid), (uint)this->calls.size(), filename.c_str());
104 
105  FILE *f = FioFOpenFile(filename, "wt", Subdirectory::NO_DIRECTORY);
106  FileCloser fcloser(f);
107 
108  uint32 total_microseconds = 0;
109 
110  fputs("Tick,Sprite,Feature,Item,CallbackID,Microseconds,Depth,Result\n", f);
111  for (const Call &c : this->calls) {
112  fprintf(f, "%u,%u,0x%X,%u,0x%X,%u,%u,%u\n", c.tick, c.root_sprite, c.feat, c.item, (uint)c.cb, c.time, c.subs, c.result);
113  total_microseconds += c.time;
114  }
115 
116  this->Abort();
117 
118  return total_microseconds;
119 }
120 
121 void NewGRFProfiler::Abort()
122 {
123  this->active = false;
124  this->calls.clear();
125 }
126 
132 {
133  time_t write_time = time(nullptr);
134 
135  char timestamp[16] = {};
136  strftime(timestamp, lengthof(timestamp), "%Y%m%d-%H%M", localtime(&write_time));
137 
138  char filepath[MAX_PATH] = {};
139  seprintf(filepath, lastof(filepath), "%sgrfprofile-%s-%08X.csv", FiosGetScreenshotDir(), timestamp, BSWAP32(this->grffile->grfid));
140 
141  return std::string(filepath);
142 }
143 
144 uint32 NewGRFProfiler::FinishAll()
145 {
146  int max_ticks = 0;
147  uint32 total_microseconds = 0;
148  for (NewGRFProfiler &pr : _newgrf_profilers) {
149  if (pr.active) {
150  total_microseconds += pr.Finish();
151  max_ticks = std::max(max_ticks, _tick_counter - pr.start_tick);
152  }
153  }
154 
155  if (total_microseconds > 0 && max_ticks > 0) {
156  IConsolePrintF(CC_DEBUG, "Total NewGRF callback processing: %u microseconds over %d ticks", total_microseconds, max_ticks);
157  }
158 
159  _newgrf_profile_end_date = MAX_DAY;
160 
161  return total_microseconds;
162 }
NewGRFProfiler::calls
std::vector< Call > calls
All calls collected so far.
Definition: newgrf_profiling.h:57
NewGRFProfiler::Call::tick
uint16 tick
Game tick.
Definition: newgrf_profiling.h:48
NewGRFProfiler::grffile
const GRFFile * grffile
Which GRF is being profiled.
Definition: newgrf_profiling.h:53
ResolverObject
Interface for SpriteGroup-s to access the gamestate.
Definition: newgrf_spritegroup.h:315
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
MAX_DAY
#define MAX_DAY
The number of days till the last day.
Definition: date_type.h:97
ResolverObject::GetFeature
virtual GrfSpecFeature GetFeature() const
Get the feature number being resolved for.
Definition: newgrf_spritegroup.h:407
ResolverObject::GetDebugID
virtual uint32 GetDebugID() const
Get an identifier for the item being resolved.
Definition: newgrf_spritegroup.h:413
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:140
NewGRFProfiler::BeginResolve
void BeginResolve(const ResolverObject &resolver)
Capture the start of a sprite group resolution.
Definition: newgrf_profiling.cpp:45
CallbackResultSpriteGroup
Definition: newgrf_spritegroup.h:218
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:406
ResolverObject::root_spritegroup
const SpriteGroup * root_spritegroup
Root SpriteGroup to use for resolving.
Definition: newgrf_spritegroup.h:344
NewGRFProfiler::~NewGRFProfiler
~NewGRFProfiler()
Complete profiling session and write data to file.
Definition: newgrf_profiling.cpp:37
Date
int32 Date
The type to store our dates in.
Definition: date_type.h:14
NewGRFProfiler::Call::feat
GrfSpecFeature feat
GRF feature being resolved for.
Definition: newgrf_profiling.h:50
NewGRFProfiler::Call::result
uint32 result
Result of callback.
Definition: newgrf_profiling.h:45
_tick_counter
uint16 _tick_counter
Ever incrementing (and sometimes wrapping) tick counter for setting off various events.
Definition: date.cpp:30
NewGRFProfiler::Call::item
uint32 item
Local ID of item being resolved for.
Definition: newgrf_profiling.h:44
NewGRFProfiler::GetOutputFilename
std::string GetOutputFilename() const
Get name of the file that will be written.
Definition: newgrf_profiling.cpp:131
CC_DEBUG
static const TextColour CC_DEBUG
Colour for debug output.
Definition: console_type.h:27
NewGRFProfiler::Call::subs
uint32 subs
Sub-calls to other sprite groups.
Definition: newgrf_profiling.h:46
NewGRFProfiler::active
bool active
Is this profiler collecting data.
Definition: newgrf_profiling.h:54
date_func.h
BSWAP32
static uint32 BSWAP32(uint32 x)
Perform a 32 bits endianness bitswap on x.
Definition: bitmath_func.hpp:380
NewGRFProfiler::cur_call
Call cur_call
Data for current call in progress.
Definition: newgrf_profiling.h:56
string_func.h
spritecache.h
NewGRFProfiler
Callback profiler for NewGRF development.
Definition: newgrf_profiling.h:26
NO_DIRECTORY
@ NO_DIRECTORY
A path without any base directory.
Definition: fileio_type.h:125
NewGRFProfiler::start_tick
uint16 start_tick
Tick number this profiler was started on.
Definition: newgrf_profiling.h:55
GetSpriteLocalID
uint32 GetSpriteLocalID(SpriteID sprite)
Get the GRF-local sprite id of a given sprite.
Definition: spritecache.cpp:154
ResolverObject::callback
CallbackID callback
Callback being resolved.
Definition: newgrf_spritegroup.h:333
seprintf
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:460
NewGRFProfiler::Call::root_sprite
uint32 root_sprite
Pseudo-sprite index in GRF file.
Definition: newgrf_profiling.h:43
lengthof
#define lengthof(x)
Return the length of an fixed size array.
Definition: stdafx.h:369
console_func.h
NewGRFProfiler::Call::time
uint32 time
Time taken for resolution (microseconds)
Definition: newgrf_profiling.h:47
newgrf_profiling.h
ResultSpriteGroup
Definition: newgrf_spritegroup.h:244
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:385
SpriteGroup
Definition: newgrf_spritegroup.h:57
NewGRFProfiler::Call::cb
CallbackID cb
Callback ID.
Definition: newgrf_profiling.h:49
IConsolePrintF
void CDECL IConsolePrintF(TextColour colour_code, const char *format,...)
Handle the printing of text entered into the console or redirected there by any other means.
Definition: console.cpp:125
GRFFile
Dynamic data of a loaded NewGRF.
Definition: newgrf.h:105
FiosGetScreenshotDir
const char * FiosGetScreenshotDir()
Get the directory for screenshots.
Definition: fios.cpp:628