diff --git a/cmake/modules/CanaryLib.cmake b/cmake/modules/CanaryLib.cmake index 4b7d69d2b80..40263db4e68 100644 --- a/cmake/modules/CanaryLib.cmake +++ b/cmake/modules/CanaryLib.cmake @@ -57,6 +57,14 @@ if(SPEED_UP_BUILD_UNITY) log_option_enabled("Build unity for speed up compilation") endif() +# === OTS Statistics === +if(NOT DEFINED DISABLE_STATS OR NOT DISABLE_STATS) + message(STATUS "OTS stats enabled. Run 'cmake -DDISABLE_STATS=1 ..' to disable") + add_definitions(-DSTATS_ENABLED) +else() + message(STATUS "OTS stats disabled. Run 'cmake -DDISABLE_STATS=0 ..' to enable") +endif() + # ***************************************************************************** # Target include directories - to allow #include # ***************************************************************************** diff --git a/config.lua.dist b/config.lua.dist index a00bb061f81..cff62015573 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -468,3 +468,9 @@ vipFamiliarTimeCooldownReduction = 0 -- NOTE set rewardChestMaxCollectItems max items per collect action rewardChestCollectEnabled = true rewardChestMaxCollectItems = 200 + +-- OTS Statistics (Get more info in: https://github.com/opentibiabr/canary/pull/1063) +-- NOTE: time in seconds: 30 = 30 seconds, 0 = disabled +statsDumpInterval = 30 +statsSlowLogTime = 10 +statsVerySlowLogTime = 50 diff --git a/src/canary_server.cpp b/src/canary_server.cpp index 204797b4a94..7ebdf0c0502 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -28,6 +28,7 @@ #include "server/network/webhook/webhook.hpp" #include "io/ioprey.hpp" #include "io/io_bosstiary.hpp" +#include "utils\stats.hpp" #include "core.hpp" @@ -65,6 +66,10 @@ int CanaryServer::run() { setWorldType(); loadMaps(); +#ifdef STATS_ENABLED + g_stats.start(); +#endif + logger.info("Initializing gamestate..."); g_game().setGameState(GAME_STATE_INIT); @@ -111,6 +116,9 @@ int CanaryServer::run() { if (loadFailed || !serviceManager.is_running()) { logger.error("No services running. The server is NOT online!"); +#ifdef STATS_ENABLED + g_stats.shutdown(); +#endif shutdown(); return EXIT_FAILURE; } @@ -119,6 +127,10 @@ int CanaryServer::run() { serviceManager.run(); +#ifdef STATS_ENABLED + g_stats.join(); +#endif + shutdown(); return EXIT_SUCCESS; } diff --git a/src/config/config_definitions.hpp b/src/config/config_definitions.hpp index d3bf0b87eec..1fceebb7771 100644 --- a/src/config/config_definitions.hpp +++ b/src/config/config_definitions.hpp @@ -259,6 +259,10 @@ enum integerConfig_t { REWARD_CHEST_MAX_COLLECT_ITEMS, DISCORD_WEBHOOK_DELAY_MS, + STATS_DUMP_INTERVAL, + STATS_SLOW_LOG_TIME, + STATS_VERY_SLOW_LOG_TIME, + LAST_INTEGER_CONFIG }; diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index d0abcc9352b..bdef89efbd2 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -394,6 +394,10 @@ bool ConfigManager::load() { boolean[TOGGLE_RECEIVE_REWARD] = getGlobalBoolean(L, "toggleReceiveReward", false); + integer[STATS_DUMP_INTERVAL] = getGlobalNumber(L, "statsDumpInterval", 30000); + integer[STATS_SLOW_LOG_TIME] = getGlobalNumber(L, "statsSlowLogTime", 10); + integer[STATS_VERY_SLOW_LOG_TIME] = getGlobalNumber(L, "statsVerySlowLogTime", 50); + loaded = true; lua_close(L); return true; diff --git a/src/database/database.cpp b/src/database/database.cpp index 4f6eaf91851..b75531f2816 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -12,6 +12,7 @@ #include "config/configmanager.hpp" #include "database/database.hpp" #include "lib/di/container.hpp" +#include "utils\stats.hpp" Database::~Database() { if (handle != nullptr) { @@ -123,8 +124,17 @@ bool Database::executeQuery(const std::string_view &query) { std::scoped_lock lock { databaseLock }; +#ifdef STATS_ENABLED + std::chrono::high_resolution_clock::time_point time_point = std::chrono::high_resolution_clock::now(); +#endif + bool success = retryQuery(query, 10); +#ifdef STATS_ENABLED + uint64_t ns = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - time_point).count(); + g_stats.addSqlStats(new Stat(ns, query.substr(0, 100), query.substr(0, 256))); +#endif + mysql_free_result(mysql_store_result(handle)); return success; } @@ -138,6 +148,10 @@ DBResult_ptr Database::storeQuery(const std::string_view &query) { std::scoped_lock lock { databaseLock }; +#ifdef STATS_ENABLED + std::chrono::high_resolution_clock::time_point time_point = std::chrono::high_resolution_clock::now(); +#endif + retry: if (mysql_query(handle, query.data()) != 0) { g_logger().error("Query: {}", query); @@ -149,6 +163,11 @@ DBResult_ptr Database::storeQuery(const std::string_view &query) { goto retry; } +#ifdef STATS_ENABLED + uint64_t ns = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - time_point).count(); + g_stats.addSqlStats(new Stat(ns, query.substr(0, 100), query.substr(0, 256))); +#endif + // Retrieving results of query MYSQL_RES* res = mysql_store_result(handle); if (res != nullptr) { diff --git a/src/game/game.cpp b/src/game/game.cpp index 9f853ab6b22..79ea932c4e8 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -365,6 +365,10 @@ void Game::setGameState(GameState_t newState) { g_dispatcher().addTask(std::bind(&Game::shutdown, this), "Game::shutdown"); + #ifdef STATS_ENABLED + g_stats.stop(); + #endif + break; } @@ -5701,6 +5705,9 @@ void Game::checkCreatures(size_t index) { } } cleanup(); +#ifdef STATS_ENABLED + g_stats.playersOnline = getPlayersOnline(); +#endif } void Game::changeSpeed(std::shared_ptr creature, int32_t varSpeedDelta) { @@ -7287,7 +7294,9 @@ void Game::shutdown() { map.spawnsMonster.clear(); map.spawnsNpc.clear(); raids.clear(); - +#ifdef STATS_ENABLED + g_stats.shutdown(); +#endif cleanup(); if (serviceManager) { diff --git a/src/lua/scripts/luascript.cpp b/src/lua/scripts/luascript.cpp index d7d12dfe238..49b09787ae2 100644 --- a/src/lua/scripts/luascript.cpp +++ b/src/lua/scripts/luascript.cpp @@ -165,6 +165,17 @@ const std::string &LuaScriptInterface::getFileById(int32_t scriptId) { return it->second; } +const std::string &LuaScriptInterface::getFileByIdForStats(int32_t scriptId) +{ + auto it = cacheFiles.find(scriptId); + if (it == cacheFiles.end()) { + static const std::string& unk = "(Unknown scriptfile)"; + return unk; + } + return it->second; +} + + std::string LuaScriptInterface::getStackTrace(const std::string &error_desc) { lua_getglobal(luaState, "debug"); if (!isTable(luaState, -1)) { @@ -227,6 +238,15 @@ bool LuaScriptInterface::closeState() { } bool LuaScriptInterface::callFunction(int params) { +#ifdef STATS_ENABLED + int32_t scriptId; + int32_t callbackId; + bool timerEvent; + LuaScriptInterface* scriptInterface; + getScriptEnv()->getEventInfo(scriptId, scriptInterface, callbackId, timerEvent); + std::chrono::high_resolution_clock::time_point time_point = std::chrono::high_resolution_clock::now(); +#endif + bool result = false; int size = lua_gettop(luaState); if (protectedCall(luaState, params, 1) != 0) { @@ -240,6 +260,11 @@ bool LuaScriptInterface::callFunction(int params) { LuaScriptInterface::reportError(nullptr, "Stack size changed!"); } +#ifdef STATS_ENABLED + uint64_t ns = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - time_point).count(); + g_stats.addLuaStats(new Stat(ns, getFileByIdForStats(scriptId), "")); +#endif + resetScriptEnv(); return result; } diff --git a/src/lua/scripts/luascript.hpp b/src/lua/scripts/luascript.hpp index 8f0b3b36c19..be291c2e94d 100644 --- a/src/lua/scripts/luascript.hpp +++ b/src/lua/scripts/luascript.hpp @@ -28,6 +28,7 @@ class LuaScriptInterface : public LuaFunctionsLoader { int32_t loadFile(const std::string &file, const std::string &scriptName); const std::string &getFileById(int32_t scriptId); + const std::string &getFileByIdForStats(int32_t scriptId); int32_t getEvent(const std::string &eventName); int32_t getEvent(); int32_t getMetaEvent(const std::string &globalName, const std::string &eventName); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 59c2867e519..9fd7b678e6c 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -240,13 +240,13 @@ ProtocolGame::ProtocolGame(Connection_ptr initConnection) : } template -void ProtocolGame::addGameTask(Callable function, Args &&... args) { - g_dispatcher().addTask(std::bind(function, &g_game(), std::forward(args)...), "ProtocolGame::addGameTask"); +void ProtocolGame::addGameTaskWithStats(Callable function, const std::string& function_str, const std::string& extra_info, Args &&... args) { + g_dispatcher().addTaskWithStats(std::bind(function, &g_game(), std::forward(args)...), "ProtocolGame::addGameTaskWithStats", function_str, extra_info); } template -void ProtocolGame::addGameTaskTimed(uint32_t delay, std::string context, Callable function, Args &&... args) { - g_dispatcher().addTask(std::bind(function, &g_game(), std::forward(args)...), context, delay); +void ProtocolGame::addGameTaskTimedWithStats(uint32_t delay, std::string context, Callable function, const std::string& function_str, const std::string& extra_info, Args &&... args) { + g_dispatcher().addTaskWithStats(std::bind(function, &g_game(), std::forward(args)...), context, delay, function_str, extra_info); } void ProtocolGame::AddItem(NetworkMessage &msg, uint16_t id, uint8_t count, uint8_t tier) { diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 60eac6f1821..bde932adc5c 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -70,9 +70,9 @@ class ProtocolGame final : public Protocol { private: // Helpers so we don't need to bind every time template - void addGameTask(Callable function, Args &&... args); + void addGameTaskWithStats(Callable function, const std::string& function_str, const std::string& extra_info, Args &&... args); template - void addGameTaskTimed(uint32_t delay, std::string context, Callable function, Args &&... args); + void addGameTaskTimedWithStats(uint32_t delay, std::string context, Callable function, const std::string& function_str, const std::string& extra_info, Args &&... args); ProtocolGame_ptr getThis() { return std::static_pointer_cast(shared_from_this()); diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 1b2ba9da7f4..e1e239e7bc0 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -1,4 +1,5 @@ target_sources(${PROJECT_NAME}_lib PRIVATE + stats.cpp pugicast.cpp tools.cpp wildcardtree.cpp diff --git a/vcproj/canary.vcxproj b/vcproj/canary.vcxproj index f3a4530b260..ca122cd31f9 100644 --- a/vcproj/canary.vcxproj +++ b/vcproj/canary.vcxproj @@ -218,6 +218,7 @@ + @@ -398,6 +399,7 @@ +