Instanciated the profiler + added a way to dump the export the profilin data in the UI

This commit is contained in:
emeric
2024-03-09 18:55:54 +01:00
parent f36f176a8b
commit aca6c00414
12 changed files with 187 additions and 1 deletions
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<messages xmlns:if="Wt.WTemplate.conditions">
<message id="Lms.Admin.ProfilerController.template">
<form>
<legend>${tr:Lms.Admin.ProfilerController.profiler}</legend>
<div class="row g-3">
<div class="col-12">
${export-btn class="btn btn-primary"}
</div>
</div>
</form>
</message>
</messages>
+1
View File
@@ -49,6 +49,7 @@
<li>${scan-settings class="dropdown-item"}</li>
<li>${scanner class="dropdown-item"}</li>
<li>${users class="dropdown-item"}</li>
<li>${profiler class="dropdown-item"}</li>
</ul>
</li>
${</if-is-admin>}
+5
View File
@@ -45,6 +45,7 @@
<!--Administration-->
<message id="Lms.Admin.menu-media-libraries"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Libraries</message>
<message id="Lms.Admin.menu-profiler"><i class="fa fa-fw fa-bar-chart" aria-hidden="true"></i> Profiler</message>
<message id="Lms.Admin.menu-scan-settings"><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> Scan settings</message>
<message id="Lms.Admin.menu-scanner"><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> Scanner</message>
<message id="Lms.Admin.menu-users"><i class="fa fa-fw fa-users" aria-hidden="true"></i> Users</message>
@@ -63,6 +64,10 @@
<message id="Lms.Admin.MediaLibrary.path-must-be-existing-directory">Path must be an existing directory</message>
<message id="Lms.Admin.MediaLibrary.root-path">Root directory</message>
<!--Profiler Controller-->
<message id="Lms.Admin.ProfilerController.export-current-buffer">Export profiling data</message>
<message id="Lms.Admin.ProfilerController.profiler">Profiler</message>
<!--Scan settings-->
<message id="Lms.Admin.Database.artist-tag-delimiter">Delimiter to be used for splitting artist tags</message>
<message id="Lms.Admin.Database.daily">Daily</message>
+5
View File
@@ -45,6 +45,7 @@
<!--Administration-->
<message id="Lms.Admin.menu-media-libraries"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Bibliothèques</message>
<message id="Lms.Admin.menu-profiler"><i class="fa fa-fw fa-bar-chart" aria-hidden="true"></i> Profileur</message>
<message id="Lms.Admin.menu-scan-settings"><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> Paramètres du scan</message>
<message id="Lms.Admin.menu-scanner"><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> Scanner</message>
<message id="Lms.Admin.menu-users"><i class="fa fa-fw fa-users" aria-hidden="true"></i> Utilisateurs</message>
@@ -63,6 +64,10 @@
<message id="Lms.Admin.MediaLibrary.path-must-be-existing-directory">Le chemin doit référencer un répertoire existant</message>
<message id="Lms.Admin.MediaLibrary.root-path">Répertoire racine</message>
<!--Profiler Controller-->
<message id="Lms.Admin.ProfilerController.export-current-buffer">Exporter les données de profilage</message>
<message id="Lms.Admin.ProfilerController.profiler">Profileur</message>
<!--Scan settings-->
<message id="Lms.Admin.Database.artist-tag-delimiter">Délimiteur à utiliser pour séparer les tags d'artistes</message>
<message id="Lms.Admin.Database.daily">Tous les jours</message>
+5
View File
@@ -45,6 +45,7 @@
<!--Administration-->
<message id="Lms.Admin.menu-media-libraries"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Librerie</message>
<message id="Lms.Admin.menu-profiler"><i class="fa fa-fw fa-bar-chart" aria-hidden="true"></i> Profilatore</message>
<message id="Lms.Admin.menu-scan-settings"><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> Impostazioni di scansione</message>
<message id="Lms.Admin.menu-scanner"><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> Scanner</message>
<message id="Lms.Admin.menu-users"><i class="fa fa-fw fa-users" aria-hidden="true"></i> Utenti</message>
@@ -63,6 +64,10 @@
<message id="Lms.Admin.MediaLibrary.path-must-be-existing-directory">Il percorso deve essere una directory esistente</message>
<message id="Lms.Admin.MediaLibrary.root-path">Cartella principale</message>
<!--Profiler Controller-->
<message id="Lms.Admin.ProfilerController.export-current-buffer">Esporta dati di profilazione</message>
<message id="Lms.Admin.ProfilerController.profiler">Profilatore</message>
<!--Scan settings-->
<message id="Lms.Admin.Database.artist-tag-delimiter">Delimitatore da utilizzare per separare i tag degli artisti</message>
<message id="Lms.Admin.Database.daily">Giornaliera</message>
+6
View File
@@ -49,6 +49,7 @@
<!--MediaLibraries-->
@@ -63,6 +64,10 @@
<!--Scan settings-->
<message id="Lms.Admin.Database.daily">每日</message>
@@ -83,6 +88,7 @@
<message id="Lms.Admin.Database.update-start-time">更新开始时间</message>
<message id="Lms.Admin.Database.weekly">每周</message>
<!--Scanner Controller-->
<message id="Lms.Admin.ScannerController.bad-duration">无法获得音轨时间</message>
<message id="Lms.Admin.ScannerController.cannot-parse-file">无法解析文件</message>
+7
View File
@@ -93,6 +93,13 @@ artist-image-file-names = ("artist");
# Playqueue max entry count
playqueue-max-entry-count = 1000;
# Internal profiling, enable only if necessary as it brings some runtime overhead!
# Possible values are "disabled", "overview" or "detailed".
# If enabled, profiling information has to be dumped in the profiler view located in the admin menu
profiling-level = "disabled";
# The profiling buffer size, in MBytes (min is 16)
profiling-buffer-size = 16;
# Set to true if you want to hide duplicate tracks
scanner-skip-duplicate-mbid = false;
+1
View File
@@ -14,6 +14,7 @@ add_executable(lms
ui/admin/InitWizardView.cpp
ui/admin/MediaLibrariesView.cpp
ui/admin/MediaLibraryModal.cpp
ui/admin/ProfilerController.cpp
ui/admin/ScannerController.cpp
ui/admin/ScanSettingsView.cpp
ui/admin/UserView.cpp
+19
View File
@@ -43,6 +43,7 @@
#include "utils/IChildProcessManager.hpp"
#include "utils/IConfig.hpp"
#include "utils/IOContextRunner.hpp"
#include "utils/IProfiler.hpp"
#include "utils/Service.hpp"
#include "utils/String.hpp"
#include "utils/WtLogger.hpp"
@@ -75,6 +76,20 @@ namespace
throw LmsException{ "Invalid config value for 'log-min-severity'" };
}
std::optional<profiling::Level> getProfilingLevel()
{
std::string_view profilingLevel{ Service<IConfig>::get()->getString("profiling-level", "disabled") };
if (profilingLevel == "disabled")
return std::nullopt;
else if (profilingLevel == "overview")
return profiling::Level::Overview;
else if (profilingLevel == "detailed")
return profiling::Level::Detailed;
throw LmsException{ "Invalid config value for 'profiling-level'" };
}
std::vector<std::string> generateWtConfig(std::string execPath, Severity minSeverity)
{
std::vector<std::string> args;
@@ -233,6 +248,9 @@ int main(int argc, char* argv[])
Service<IConfig> config{ createConfig(configFilePath) };
const Severity minLogSeverity{getLogMinSeverity()};
Service<ILogger> logger{ std::make_unique<WtLogger>(minLogSeverity) };
std::optional<Service<profiling::IProfiler>> profiler;
if (const auto level{ getProfilingLevel() })
profiler.emplace(profiling::createProfiler(level.value(), config->getULong("profiling-buffer-size", profiling::MinBufferSizeInMBytes)));
// use system locale. libarchive relies on this to write filenames
if (char* locale{ ::setlocale(LC_ALL, "") })
@@ -243,6 +261,7 @@ int main(int argc, char* argv[])
// Make sure the working directory exists
std::filesystem::create_directories(config->getPath("working-dir"));
std::filesystem::create_directories(config->getPath("working-dir") / "cache");
std::filesystem::create_directories(config->getPath("working-dir") / "profiling");
// Construct WT configuration and get the argc/argv back
const std::vector<std::string> wtServerArgs{ generateWtConfig(argv[0], minLogSeverity) };
+17 -1
View File
@@ -38,15 +38,17 @@
#include "database/User.hpp"
#include "services/scrobbling/IScrobblingService.hpp"
#include "utils/ILogger.hpp"
#include "utils/IProfiler.hpp"
#include "utils/Service.hpp"
#include "utils/String.hpp"
#include "admin/InitWizardView.hpp"
#include "admin/MediaLibrariesView.hpp"
#include "admin/ProfilerController.hpp"
#include "admin/ScannerController.hpp"
#include "admin/ScanSettingsView.hpp"
#include "admin/UserView.hpp"
#include "admin/UsersView.hpp"
#include "admin/ScannerController.hpp"
#include "common/Template.hpp"
#include "explore/Explore.hpp"
#include "explore/Filters.hpp"
@@ -77,6 +79,7 @@ namespace UserInterface
res->use(appRoot + "admin-initwizard");
res->use(appRoot + "admin-medialibraries");
res->use(appRoot + "admin-medialibrary");
res->use(appRoot + "admin-profilercontroller");
res->use(appRoot + "admin-scannercontroller");
res->use(appRoot + "admin-scansettings");
res->use(appRoot + "admin-user");
@@ -119,6 +122,7 @@ namespace UserInterface
IdxAdminScanner,
IdxAdminUsers,
IdxAdminUser,
IdxAdminProfiler,
};
void handlePathChange(Wt::WStackedWidget& stack, bool isAdmin)
@@ -146,6 +150,7 @@ namespace UserInterface
{ "/admin/scanner", IdxAdminScanner, true, Wt::WString::tr("Lms.Admin.ScannerController.scanner") },
{ "/admin/users", IdxAdminUsers, true, Wt::WString::tr("Lms.Admin.Users.users") },
{ "/admin/user", IdxAdminUser, true, std::nullopt },
{ "/admin/profiler", IdxAdminProfiler, true, Wt::WString::tr("Lms.Admin.ProfilerController.profiler") },
};
LMS_LOG(UI, DEBUG, "Internal path changed to '" << wApp->internalPath() << "'");
@@ -264,6 +269,8 @@ namespace UserInterface
void LmsApplication::init()
{
LMS_SCOPED_PROFILE_OVERVIEW("UI", "ApplicationInit");
setTheme(std::make_shared<LmsTheme>());
useStyleSheet("resources/font-awesome/css/font-awesome.min.css");
@@ -381,6 +388,8 @@ namespace UserInterface
void LmsApplication::createHome()
{
LMS_SCOPED_PROFILE_OVERVIEW("UI", "ApplicationCreateHome");
_coverResource = std::make_shared<CoverResource>();
declareJavaScriptFunction("onLoadCover", "function(id) { id.className += \" Lms-cover-loaded\"}");
@@ -436,6 +445,11 @@ namespace UserInterface
navbar->bindNew<Wt::WAnchor>("scan-settings", Wt::WLink{ Wt::LinkType::InternalPath, "/admin/scan-settings" }, Wt::WString::tr("Lms.Admin.menu-scan-settings"));
navbar->bindNew<Wt::WAnchor>("scanner", Wt::WLink{ Wt::LinkType::InternalPath, "/admin/scanner" }, Wt::WString::tr("Lms.Admin.menu-scanner"));
navbar->bindNew<Wt::WAnchor>("users", Wt::WLink{ Wt::LinkType::InternalPath, "/admin/users" }, Wt::WString::tr("Lms.Admin.menu-users"));
// Hide the entry if the profiler is not enabled
if (Service<::profiling::IProfiler>::get())
navbar->bindNew<Wt::WAnchor>("profiler", Wt::WLink{ Wt::LinkType::InternalPath, "/admin/profiler" }, Wt::WString::tr("Lms.Admin.menu-profiler"));
else
navbar->bindEmpty("profiler");
}
// Contents
@@ -467,6 +481,7 @@ namespace UserInterface
mainStack->addNew<ScannerController>();
mainStack->addNew<UsersView>();
mainStack->addNew<UserView>();
mainStack->addNew<ProfilerController>();
}
explore->getPlayQueueController().setMaxTrackCountToEnqueue(_playQueue->getCapacity());
@@ -544,6 +559,7 @@ namespace UserInterface
{
try
{
LMS_SCOPED_PROFILE_OVERVIEW("UI", "ProcessEvent");
WApplication::notify(event);
}
catch (LmsApplicationException& e)
+75
View File
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2024 Emeric Poupon
*
* This file is part of LMS.
*
* LMS 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, either version 3 of the License, or
* (at your option) any later version.
*
* LMS 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ProfilerController.hpp"
#include <Wt/Http/Response.h>
#include <Wt/WDateTime.h>
#include <Wt/WPushButton.h>
#include <Wt/WResource.h>
#include "utils/IProfiler.hpp"
#include "utils/String.hpp"
namespace UserInterface
{
namespace
{
class ReportResource : public Wt::WResource
{
public:
ReportResource(::profiling::IProfiler& profiler)
: _profiler{ profiler }
{
}
~ReportResource()
{
beingDeleted();
}
void handleRequest(const Wt::Http::Request&, Wt::Http::Response& response)
{
suggestFileName(StringUtils::toISO8601String(Wt::WDateTime::currentDateTime()) + "-profiling.json");
response.setMimeType("application/json");
_profiler.dumpCurrentBuffer(response.out());
}
private:
profiling::IProfiler& _profiler;
};
}
ProfilerController::ProfilerController()
: Wt::WTemplate{ Wt::WString::tr("Lms.Admin.ProfilerController.template") }
{
addFunction("tr", &Wt::WTemplate::Functions::tr);
Wt::WPushButton* dumpBtn{ bindNew<Wt::WPushButton>("export-btn", Wt::WString::tr("Lms.Admin.ProfilerController.export-current-buffer")) };
if (auto profiler{ Service<profiling::IProfiler>::get() })
{
Wt::WLink link{ std::make_shared<ReportResource>(*profiler) };
link.setTarget(Wt::LinkTarget::NewWindow);
dumpBtn->setLink(link);
}
else
dumpBtn->setEnabled(false);
}
} // namespace UserInterface
+31
View File
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2024 Emeric Poupon
*
* This file is part of LMS.
*
* LMS 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, either version 3 of the License, or
* (at your option) any later version.
*
* LMS 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <Wt/WTemplate.h>
namespace UserInterface
{
class ProfilerController : public Wt::WTemplate
{
public:
ProfilerController();
};
} // namespace UserInterface