OpenTTD Source: src/debug.cpp Source File

OpenTTD Source 15.0-RC2

debug.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#include "core/string_consumer.hpp"
12#include "console_func.h"
13#include "debug.h"
14#include "string_func.h"
15#include "fileio_func.h"
16#include "settings_type.h"
17#include <mutex>
18
19#if defined(_WIN32)
20#include "os/windows/win32.h"
21#endif
22
23#include "3rdparty/fmt/chrono.h"
24
25#include "network/network_admin.h"
26
27#include "safeguards.h"
28
30 struct QueuedDebugItem {
31 std::string_view level;
32 std::string message;
33};
34std::atomic<bool> _debug_remote_console;
35std::mutex _debug_remote_console_mutex;
36std::vector<QueuedDebugItem> _debug_remote_console_queue;
37std::vector<QueuedDebugItem> _debug_remote_console_queue_spare;
38
39int _debug_driver_level;
40int _debug_grf_level;
41int _debug_map_level;
42int _debug_misc_level;
43int _debug_net_level;
44int _debug_sprite_level;
45int _debug_oldloader_level;
46int _debug_yapf_level;
47int _debug_fontcache_level;
48int _debug_script_level;
49int _debug_sl_level;
50int _debug_gamelog_level;
51int _debug_desync_level;
52int _debug_console_level;
53#ifdef RANDOM_DEBUG
54int _debug_random_level;
55#endif
56
57 struct DebugLevel {
58 std::string_view name;
59 int *level;
60};
61
62#define DEBUG_LEVEL(x) { #x, &_debug_##x##_level }
63static const std::initializer_list<DebugLevel> _debug_levels{
64 DEBUG_LEVEL(driver),
65 DEBUG_LEVEL(grf),
66 DEBUG_LEVEL(map),
67 DEBUG_LEVEL(misc),
68 DEBUG_LEVEL(net),
69 DEBUG_LEVEL(sprite),
70 DEBUG_LEVEL(oldloader),
71 DEBUG_LEVEL(yapf),
72 DEBUG_LEVEL(fontcache),
73 DEBUG_LEVEL(script),
74 DEBUG_LEVEL(sl),
75 DEBUG_LEVEL(gamelog),
76 DEBUG_LEVEL(desync),
77 DEBUG_LEVEL(console),
78#ifdef RANDOM_DEBUG
79 DEBUG_LEVEL(random),
80#endif
81};
82#undef DEBUG_LEVEL
83
88 void DumpDebugFacilityNames(std::back_insert_iterator<std::string> &output_iterator)
89{
90 bool written = false;
91 for (const auto &debug_level : _debug_levels) {
92 if (!written) {
93 fmt::format_to(output_iterator, "List of debug facility names:\n");
94 } else {
95 fmt::format_to(output_iterator, ", ");
96 }
97 fmt::format_to(output_iterator, "{}", debug_level.name);
98 written = true;
99 }
100 if (written) {
101 fmt::format_to(output_iterator, "\n\n");
102 }
103}
104
110 void DebugPrint(std::string_view category, int level, std::string &&message)
111{
112 if (category == "desync" && level != 0) {
113 static auto f = FioFOpenFile("commands-out.log", "wb", AUTOSAVE_DIR);
114 if (!f.has_value()) return;
115
116 fmt::print(*f, "{}{}\n", GetLogPrefix(true), message);
117 fflush(*f);
118#ifdef RANDOM_DEBUG
119 } else if (category == "random") {
120 static auto f = FioFOpenFile("random-out.log", "wb", AUTOSAVE_DIR);
121 if (!f.has_value()) return;
122
123 fmt::print(*f, "{}\n", message);
124 fflush(*f);
125#endif
126 } else {
127 fmt::print(stderr, "{}dbg: [{}:{}] {}\n", GetLogPrefix(true), category, level, message);
128
129 if (_debug_remote_console.load()) {
130 /* Only add to the queue when there is at least one consumer of the data. */
131 std::lock_guard<std::mutex> lock(_debug_remote_console_mutex);
132 _debug_remote_console_queue.emplace_back(category, std::move(message));
133 }
134 }
135}
136
144 void SetDebugString(std::string_view s, SetDebugStringErrorFunc error_func)
145{
146 StringConsumer consumer{s};
147
148 /* Store planned changes into map during parse */
149 std::map<std::string_view, int> new_levels;
150
151 /* Global debugging level? */
152 auto level = consumer.TryReadIntegerBase<int>(10);
153 if (level.has_value()) {
154 for (const auto &debug_level : _debug_levels) {
155 new_levels[debug_level.name] = *level;
156 }
157 }
158
159 static const std::string_view lowercase_letters{"abcdefghijklmnopqrstuvwxyz"};
160 static const std::string_view lowercase_letters_and_digits{"abcdefghijklmnopqrstuvwxyz0123456789"};
161
162 /* Individual levels */
163 while (consumer.AnyBytesLeft()) {
164 consumer.SkipUntilCharIn(lowercase_letters);
165 if (!consumer.AnyBytesLeft()) break;
166
167 /* Find the level by name. */
168 std::string_view key = consumer.ReadUntilCharNotIn(lowercase_letters);
169 auto it = std::ranges::find(_debug_levels, key, &DebugLevel::name);
170 if (it == std::end(_debug_levels)) {
171 error_func(fmt::format("Unknown debug level '{}'", key));
172 return;
173 }
174
175 /* Do not skip lowercase letters, so 'net misc=2' won't be resolved
176 * to setting 'net=2' and leaving misc untouched. */
177 consumer.SkipUntilCharIn(lowercase_letters_and_digits);
178 level = consumer.TryReadIntegerBase<int>(10);
179 if (!level.has_value()) {
180 error_func(fmt::format("Level for '{}' must be a valid integer.", key));
181 return;
182 }
183
184 new_levels[it->name] = *level;
185 }
186
187 /* Apply the changes after parse is successful */
188 for (const auto &debug_level : _debug_levels) {
189 const auto &nl = new_levels.find(debug_level.name);
190 if (nl != new_levels.end()) {
191 *debug_level.level = nl->second;
192 }
193 }
194}
195
201std::string GetDebugString()
202{
203 std::string result;
204 for (const auto &debug_level : _debug_levels) {
205 if (!result.empty()) result += ", ";
206 format_append(result, "{}={}", debug_level.name, *debug_level.level);
207 }
208 return result;
209}
210
219std::string GetLogPrefix(bool force)
220{
221 std::string log_prefix;
222 if (force || _settings_client.gui.show_date_in_logs) {
223 log_prefix = fmt::format("[{:%Y-%m-%d %H:%M:%S}] ", fmt::localtime(time(nullptr)));
224 }
225 return log_prefix;
226}
227
235 void DebugSendRemoteMessages()
236{
237 if (!_debug_remote_console.load()) return;
238
239 {
240 std::lock_guard<std::mutex> lock(_debug_remote_console_mutex);
241 std::swap(_debug_remote_console_queue, _debug_remote_console_queue_spare);
242 }
243
244 for (auto &item : _debug_remote_console_queue_spare) {
245 NetworkAdminConsole(item.level, item.message);
246 if (_settings_client.gui.developer >= 2) IConsolePrint(CC_DEBUG, "dbg: [{}] {}", item.level, item.message);
247 }
248
249 _debug_remote_console_queue_spare.clear();
250}
251
259 void DebugReconsiderSendRemoteMessages()
260{
261 bool enable = _settings_client.gui.developer >= 2;
262
263 for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) {
264 if (as->update_frequency[ADMIN_UPDATE_CONSOLE].Test(AdminUpdateFrequency::Automatic)) {
265 enable = true;
266 break;
267 }
268 }
269
270 _debug_remote_console.store(enable);
271}
ServerNetworkAdminSocketHandler
Class for handling the server side of the game connection.
Definition network_admin.h:25
ServerNetworkAdminSocketHandler::IterateActive
static Pool::IterateWrapperFiltered< ServerNetworkAdminSocketHandler, ServerNetworkAdminSocketHandlerFilter > IterateActive(size_t from=0)
Returns an iterable ensemble of all active admin sockets.
Definition network_admin.h:102
StringConsumer
Parse data from a string / buffer.
Definition string_consumer.hpp:27
StringConsumer::TryReadIntegerBase
std::optional< T > TryReadIntegerBase(int base, bool clamp=false)
Try to read and parse an integer in number 'base', and then advance the reader.
Definition string_consumer.hpp:874
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
GetLogPrefix
std::string GetLogPrefix(bool force)
Get the prefix for logs.
Definition debug.cpp:219
_debug_remote_console_queue
std::vector< QueuedDebugItem > _debug_remote_console_queue
Queue for debug messages to be passed to NetworkAdminConsole or IConsolePrint.
Definition debug.cpp:36
_debug_remote_console_mutex
std::mutex _debug_remote_console_mutex
Mutex to guard the queue of debug messages for either NetworkAdminConsole or IConsolePrint.
Definition debug.cpp:35
SetDebugString
void SetDebugString(std::string_view s, SetDebugStringErrorFunc error_func)
Set debugging levels by parsing the text in s.
Definition debug.cpp:144
DebugReconsiderSendRemoteMessages
void DebugReconsiderSendRemoteMessages()
Reconsider whether we need to send debug messages to either NetworkAdminConsole or IConsolePrint.
Definition debug.cpp:259
_debug_remote_console
std::atomic< bool > _debug_remote_console
Whether we need to send data to either NetworkAdminConsole or IConsolePrint.
Definition debug.cpp:34
DebugPrint
void DebugPrint(std::string_view category, int level, std::string &&message)
Internal function for outputting the debug line.
Definition debug.cpp:110
DumpDebugFacilityNames
void DumpDebugFacilityNames(std::back_insert_iterator< std::string > &output_iterator)
Dump the available debug facility names in the help text.
Definition debug.cpp:88
_debug_remote_console_queue_spare
std::vector< QueuedDebugItem > _debug_remote_console_queue_spare
Spare queue to swap with _debug_remote_console_queue.
Definition debug.cpp:37
GetDebugString
std::string GetDebugString()
Print out the current debug-level.
Definition debug.cpp:201
DebugSendRemoteMessages
void DebugSendRemoteMessages()
Send the queued Debug messages to either NetworkAdminConsole or IConsolePrint from the GameLoop threa...
Definition debug.cpp:235
debug.h
Functions related to debugging.
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.
AUTOSAVE_DIR
@ AUTOSAVE_DIR
Subdirectory of save for autosaves.
Definition fileio_type.h:91
NetworkAdminConsole
void NetworkAdminConsole(std::string_view origin, std::string_view string)
Send console to the admin network (if they did opt in for the respective update).
Definition network_admin.cpp:1009
network_admin.h
Server part of the admin network protocol.
safeguards.h
A number of safeguards to prevent using unsafe methods.
_settings_client
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
settings_type.h
Types related to global configuration settings.
stdafx.h
Definition of base types and functions in a cross-platform compatible way.
string_consumer.hpp
Parse strings.
string_func.h
Functions related to low-level strings.
ClientSettings::gui
GUISettings gui
settings related to the GUI
Definition settings_type.h:650
DebugLevel
Definition debug.cpp:57
GUISettings::show_date_in_logs
bool show_date_in_logs
whether to show dates in console logs
Definition settings_type.h:250
GUISettings::developer
uint8_t developer
print non-fatal warnings in console (>= 1), copy debug output to console (== 2)
Definition settings_type.h:249
QueuedDebugItem
Element in the queue of debug messages that have to be passed to either NetworkAdminConsole or IConso...
Definition debug.cpp:30
QueuedDebugItem::level
std::string_view level
The used debug level.
Definition debug.cpp:31
QueuedDebugItem::message
std::string message
The actual formatted message.
Definition debug.cpp:32
ADMIN_UPDATE_CONSOLE
@ ADMIN_UPDATE_CONSOLE
The admin would like to have console messages.
Definition tcp_admin.h:87
AdminUpdateFrequency::Automatic
@ Automatic
The admin gets information about this when it changes.
win32.h
declarations of functions for MS windows systems
lock
std::mutex lock
synchronization for playback status fields
Definition win32_m.cpp:35