diff options
-rw-r--r-- | CMakeLists.txt | 8 | ||||
-rw-r--r-- | MultiMC.cpp | 25 | ||||
-rw-r--r-- | MultiMC.h | 8 | ||||
-rw-r--r-- | gui/MainWindow.cpp | 56 | ||||
-rw-r--r-- | gui/MainWindow.h | 8 | ||||
-rw-r--r-- | gui/MainWindow.ui | 12 | ||||
-rw-r--r-- | gui/dialogs/SettingsDialog.cpp | 85 | ||||
-rw-r--r-- | gui/dialogs/SettingsDialog.h | 5 | ||||
-rw-r--r-- | gui/dialogs/SettingsDialog.ui | 112 | ||||
-rw-r--r-- | logic/OneSixInstance.cpp | 1 | ||||
-rw-r--r-- | logic/profiler/BaseProfiler.cpp | 19 | ||||
-rw-r--r-- | logic/profiler/BaseProfiler.h | 39 | ||||
-rw-r--r-- | logic/profiler/JProfiler.cpp | 56 | ||||
-rw-r--r-- | logic/profiler/JProfiler.h | 21 | ||||
-rw-r--r-- | logic/profiler/JVisualVM.cpp | 46 | ||||
-rw-r--r-- | logic/profiler/JVisualVM.h | 21 |
16 files changed, 505 insertions, 17 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e7dea67..2dd5dde5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -498,6 +498,14 @@ logic/assets/AssetsMigrateTask.h logic/assets/AssetsMigrateTask.cpp logic/assets/AssetsUtils.h logic/assets/AssetsUtils.cpp + +# Profiling +logic/profiler/BaseProfiler.h +logic/profiler/BaseProfiler.cpp +logic/profiler/JProfiler.h +logic/profiler/JProfiler.cpp +logic/profiler/JVisualVM.h +logic/profiler/JVisualVM.cpp ) diff --git a/MultiMC.cpp b/MultiMC.cpp index 598a3a80..b17afdc6 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -31,6 +31,9 @@ #include "logic/updater/UpdateChecker.h" #include "logic/updater/NotificationChecker.h" +#include "logic/profiler/JProfiler.h" +#include "logic/profiler/JVisualVM.h" + #include "pathutils.h" #include "cmdutils.h" #include <inisettingsobject.h> @@ -41,8 +44,9 @@ using namespace Util::Commandline; MultiMC::MultiMC(int &argc, char **argv, bool root_override) - : QApplication(argc, argv), m_version{VERSION_MAJOR, VERSION_MINOR, VERSION_HOTFIX, - VERSION_BUILD, MultiMCVersion::VERSION_TYPE, VERSION_CHANNEL, BUILD_PLATFORM} + : QApplication(argc, argv), + m_version{VERSION_MAJOR, VERSION_MINOR, VERSION_HOTFIX, VERSION_BUILD, + MultiMCVersion::VERSION_TYPE, VERSION_CHANNEL, BUILD_PLATFORM} { setOrganizationName("MultiMC"); setApplicationName("MultiMC5"); @@ -211,6 +215,15 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override) // init proxy settings updateProxySettings(); + m_profilers.insert("jprofiler", + std::shared_ptr<BaseProfilerFactory>(new JProfilerFactory())); + m_profilers.insert("jvisualvm", + std::shared_ptr<BaseProfilerFactory>(new JVisualVMFactory())); + for (auto profiler : m_profilers.values()) + { + profiler->registerSettings(m_settings.get()); + } + // launch instance, if that's what should be done // WARNING: disabled until further notice /* @@ -426,6 +439,9 @@ void MultiMC::initGlobalSettings() m_settings->registerSetting("ConsoleWindowGeometry", ""); m_settings->registerSetting("SettingsGeometry", ""); + + // Profilers + m_settings->registerSetting("CurrentProfiler"); } void MultiMC::initHttpMetaCache() @@ -554,6 +570,11 @@ std::shared_ptr<JavaVersionList> MultiMC::javalist() return m_javalist; } +std::shared_ptr<BaseProfilerFactory> MultiMC::currentProfiler() +{ + return m_profilers.value(m_settings->get("CurrentProfiler").toString()); +} + void MultiMC::installUpdates(const QString updateFilesDir, UpdateFlags flags) { // if we are going to update on exit, save the params now @@ -22,6 +22,7 @@ class UpdateChecker; class NotificationChecker; class NewsChecker; class StatusChecker; +class BaseProfilerFactory; #if defined(MMC) #undef MMC @@ -127,6 +128,12 @@ public: std::shared_ptr<JavaVersionList> javalist(); + QMap<QString, std::shared_ptr<BaseProfilerFactory>> profilers() + { + return m_profilers; + } + std::shared_ptr<BaseProfilerFactory> currentProfiler(); + void installUpdates(const QString updateFilesDir, UpdateFlags flags = None); /*! @@ -198,6 +205,7 @@ private: std::shared_ptr<ForgeVersionList> m_forgelist; std::shared_ptr<MinecraftVersionList> m_minecraftlist; std::shared_ptr<JavaVersionList> m_javalist; + QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers; QsLogging::DestinationPtr m_fileDestination; QsLogging::DestinationPtr m_debugDestination; diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 29f7c8e8..ddeb9947 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -33,6 +33,7 @@ #include <QLabel> #include <QToolButton> #include <QWidgetAction> +#include <QProgressDialog> #include "osutils.h" #include "userutils.h" @@ -97,6 +98,9 @@ #include <logic/updater/NotificationChecker.h> #include <logic/tasks/ThreadTask.h> +#include "logic/profiler/BaseProfiler.h" +#include "logic/OneSixInstance.h" + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { MultiMCPlatform::fixWM_CLASS(this); @@ -1078,7 +1082,7 @@ void MainWindow::on_actionLaunchInstanceOffline_triggered() } } -void MainWindow::doLaunch(bool online) +void MainWindow::doLaunch(bool online, bool profile) { if (!m_selectedInstance) return; @@ -1194,11 +1198,11 @@ void MainWindow::doLaunch(bool online) // update first if the server actually responded if (session->auth_server_online) { - updateInstance(m_selectedInstance, session); + updateInstance(m_selectedInstance, session, profile); } else { - launchInstance(m_selectedInstance, session); + launchInstance(m_selectedInstance, session, profile); } tryagain = false; } @@ -1206,22 +1210,22 @@ void MainWindow::doLaunch(bool online) } } -void MainWindow::updateInstance(BaseInstance *instance, AuthSessionPtr session) +void MainWindow::updateInstance(BaseInstance *instance, AuthSessionPtr session, bool profile) { auto updateTask = instance->doUpdate(); if (!updateTask) { - launchInstance(instance, session); + launchInstance(instance, session, profile); return; } ProgressDialog tDialog(this); - connect(updateTask.get(), &Task::succeeded, [this, instance, session] - { launchInstance(instance, session); }); + connect(updateTask.get(), &Task::succeeded, [this, instance, session, profile] + { launchInstance(instance, session, profile); }); connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); tDialog.exec(updateTask.get()); } -void MainWindow::launchInstance(BaseInstance *instance, AuthSessionPtr session) +void MainWindow::launchInstance(BaseInstance *instance, AuthSessionPtr session, bool profile) { Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL"); Q_ASSERT_X(session.get() != nullptr, "launchInstance", "session is NULL"); @@ -1237,6 +1241,33 @@ void MainWindow::launchInstance(BaseInstance *instance, AuthSessionPtr session) proc->setLogin(session); proc->launch(); + + if (profile && qobject_cast<OneSixInstance *>(instance)) + { + BaseProfiler *profiler = MMC->currentProfiler()->createProfiler( + qobject_cast<OneSixInstance *>(instance), this); + QProgressDialog dialog; + dialog.setMinimum(0); + dialog.setMaximum(0); + dialog.setValue(0); + dialog.setLabelText(tr("Waiting for profiler...")); + dialog.show(); + connect(profiler, &BaseProfiler::readyToLaunch, [&dialog, this](const QString &message) + { + dialog.accept(); + QMessageBox msg; + msg.setText(tr("The launch of Minecraft itself is delayed until you press the " + "button. This is the right time to setup the profiler, as the " + "profiler server is running now.\n\n%1").arg(message)); + msg.setWindowTitle(tr("Waiting")); + msg.setIcon(QMessageBox::Information); + msg.addButton(tr("Launch"), QMessageBox::AcceptRole); + msg.exec(); + proc->write("launch onesix\n"); + }); + profiler->beginProfiling(proc); + dialog.exec(); + } } void MainWindow::onGameUpdateError(QString error) @@ -1416,6 +1447,15 @@ void MainWindow::on_actionEditInstNotes_triggered() } } +void MainWindow::on_actionProfileInstance_triggered() +{ + if (m_selectedInstance) + { + NagUtils::checkJVMArgs(m_selectedInstance->settings().get("JvmArgs").toString(), this); + doLaunch(true, true); + } +} + void MainWindow::instanceEnded() { this->show(); diff --git a/gui/MainWindow.h b/gui/MainWindow.h index 4d9e165d..66d98bc5 100644 --- a/gui/MainWindow.h +++ b/gui/MainWindow.h @@ -107,22 +107,24 @@ slots: void on_actionEditInstNotes_triggered(); + void on_actionProfileInstance_triggered(); + /*! * Launches the currently selected instance with the default account. * If no default account is selected, prompts the user to pick an account. */ - void doLaunch(bool online = true); + void doLaunch(bool online = true, bool profile = false); /*! * Launches the given instance with the given account. * This function assumes that the given account has a valid, usable access token. */ - void launchInstance(BaseInstance *instance, AuthSessionPtr session); + void launchInstance(BaseInstance *instance, AuthSessionPtr session, bool profile = false); /*! * Prepares the given instance for launch with the given account. */ - void updateInstance(BaseInstance *instance, AuthSessionPtr account); + void updateInstance(BaseInstance *instance, AuthSessionPtr account, bool profile = false); void onGameUpdateError(QString error); diff --git a/gui/MainWindow.ui b/gui/MainWindow.ui index 8cf26d18..81f66136 100644 --- a/gui/MainWindow.ui +++ b/gui/MainWindow.ui @@ -108,6 +108,7 @@ <addaction name="actionChangeInstIcon"/> <addaction name="actionLaunchInstance"/> <addaction name="actionLaunchInstanceOffline"/> + <addaction name="actionProfileInstance"/> <addaction name="separator"/> <addaction name="actionEditInstNotes"/> <addaction name="actionChangeInstGroup"/> @@ -528,12 +529,19 @@ <string>Launch the selected instance.</string> </property> </action> + <action name="actionProfileInstance"> + <property name="text"> + <string>Profile</string> + </property> + <property name="toolTip"> + <string>Starts a profiling session with the current instance</string> + </property> + </action> </widget> <layoutdefault spacing="6" margin="11"/> <resources> - <include location="../resources/instances/instances.qrc"/> <include location="../resources/multimc/multimc.qrc"/> - <include location="../resources/backgrounds/backgrounds.qrc"/> + <include location="../resources/instances/instances.qrc"/> </resources> <connections/> </ui> diff --git a/gui/dialogs/SettingsDialog.cpp b/gui/dialogs/SettingsDialog.cpp index ef363f02..3039acc1 100644 --- a/gui/dialogs/SettingsDialog.cpp +++ b/gui/dialogs/SettingsDialog.cpp @@ -29,6 +29,8 @@ #include "logic/updater/UpdateChecker.h" +#include "logic/profiler/BaseProfiler.h" + #include <settingsobject.h> #include <pathutils.h> #include <QFileDialog> @@ -368,6 +370,22 @@ void SettingsDialog::applySettings(SettingsObject *s) } s->set("PostExitCommand", ui->postExitCmdTextBox->text()); + + // Profilers + s->set("JProfilerPath", ui->jprofilerPathEdit->text()); + s->set("JVisualVMPath", ui->jvisualvmPathEdit->text()); + if (ui->profilerNoneBtn->isChecked()) + { + s->set("CurrentProfiler", QString()); + } + else if (ui->jprofilerBtn->isChecked()) + { + s->set("CurrentProfiler", "jprofiler"); + } + else if (ui->jvisualvmBtn->isChecked()) + { + s->set("CurrentProfiler", "jvisualvm"); + } } void SettingsDialog::loadSettings(SettingsObject *s) @@ -447,6 +465,23 @@ void SettingsDialog::loadSettings(SettingsObject *s) // Custom Commands ui->preLaunchCmdTextBox->setText(s->get("PreLaunchCommand").toString()); ui->postExitCmdTextBox->setText(s->get("PostExitCommand").toString()); + + // Profilers + ui->jprofilerPathEdit->setText(s->get("JProfilerPath").toString()); + ui->jvisualvmPathEdit->setText(s->get("JVisualVMPath").toString()); + const QString currentProfiler = s->get("CurrentProfiler").toString(); + if (currentProfiler.isEmpty()) + { + ui->profilerNoneBtn->setChecked(true); + } + else if (currentProfiler == "jprofiler") + { + ui->jprofilerBtn->setChecked(true); + } + else if (currentProfiler == "jvisualvm") + { + ui->jvisualvmBtn->setChecked(true); + } } void SettingsDialog::on_javaDetectBtn_clicked() @@ -503,3 +538,53 @@ void SettingsDialog::checkFinished(JavaCheckResult result) "or set the path to the java executable.")); } } + +void SettingsDialog::on_jprofilerPathBtn_clicked() +{ + QString raw_dir = QFileDialog::getExistingDirectory(this, tr("JProfiler Directory"), + ui->jprofilerPathEdit->text()); + QString cooked_dir = NormalizePath(raw_dir); + + // do not allow current dir - it's dirty. Do not allow dirs that don't exist + if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists()) + { + ui->jprofilerPathEdit->setText(cooked_dir); + } +} +void SettingsDialog::on_jprofilerCheckBtn_clicked() +{ + if (!ui->jprofilerPathEdit->text().isEmpty()) + { + QString error; + if (!MMC->profilers()["jprofiler"]->check(ui->jprofilerPathEdit->text(), &error)) + { + QMessageBox::critical(this, tr("Error"), + tr("Error while checking JProfiler install:\n%1").arg(error)); + } + } +} + +void SettingsDialog::on_jvisualvmPathBtn_clicked() +{ + QString raw_dir = QFileDialog::getOpenFileName(this, tr("JVisualVM Executable"), + ui->jvisualvmPathEdit->text()); + QString cooked_dir = NormalizePath(raw_dir); + + // do not allow current dir - it's dirty. Do not allow dirs that don't exist + if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists()) + { + ui->jvisualvmPathEdit->setText(cooked_dir); + } +} +void SettingsDialog::on_jvisualvmCheckBtn_clicked() +{ + if (!ui->jvisualvmPathEdit->text().isEmpty()) + { + QString error; + if (!MMC->profilers()["jvisualvm"]->check(ui->jvisualvmPathEdit->text(), &error)) + { + QMessageBox::critical(this, tr("Error"), + tr("Error while checking JVisualVM install:\n%1").arg(error)); + } + } +} diff --git a/gui/dialogs/SettingsDialog.h b/gui/dialogs/SettingsDialog.h index d7bbbeb3..60d569f9 100644 --- a/gui/dialogs/SettingsDialog.h +++ b/gui/dialogs/SettingsDialog.h @@ -75,6 +75,11 @@ slots: void checkFinished(JavaCheckResult result); + void on_jprofilerPathBtn_clicked(); + void on_jprofilerCheckBtn_clicked(); + void on_jvisualvmPathBtn_clicked(); + void on_jvisualvmCheckBtn_clicked(); + /*! * Updates the list of update channels in the combo box. */ diff --git a/gui/dialogs/SettingsDialog.ui b/gui/dialogs/SettingsDialog.ui index 54e7db7a..29cbbcba 100644 --- a/gui/dialogs/SettingsDialog.ui +++ b/gui/dialogs/SettingsDialog.ui @@ -863,6 +863,116 @@ </item> </layout> </widget> + <widget class="QWidget" name="profilingTab"> + <attribute name="title"> + <string>Profiling</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_13"> + <item> + <widget class="QGroupBox" name="groupBox_4"> + <property name="title"> + <string>Active profiler</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_12"> + <item> + <widget class="QRadioButton" name="profilerNoneBtn"> + <property name="text"> + <string>None</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="jprofilerBtn"> + <property name="text"> + <string>JProfiler</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="jvisualvmBtn"> + <property name="text"> + <string>JVisualVM</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>JProfiler</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_10"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLineEdit" name="jprofilerPathEdit"/> + </item> + <item> + <widget class="QPushButton" name="jprofilerPathBtn"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QPushButton" name="jprofilerCheckBtn"> + <property name="text"> + <string>Check</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>JVisualVM</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_11"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="QLineEdit" name="jvisualvmPathEdit"/> + </item> + <item> + <widget class="QPushButton" name="jvisualvmPathBtn"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QPushButton" name="jvisualvmCheckBtn"> + <property name="text"> + <string>Check</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> </widget> </item> <item> @@ -938,7 +1048,7 @@ </connection> </connections> <buttongroups> - <buttongroup name="proxyGroup"/> <buttongroup name="sortingModeGroup"/> + <buttongroup name="proxyGroup"/> </buttongroups> </ui> diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index ae172f21..d987b693 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -228,7 +228,6 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(AuthSessionPtr session) launchScript += "ext " + finfo.absoluteFilePath() + "\n"; } launchScript += "natives " + natives_dir.absolutePath() + "\n"; - launchScript += "launch onesix\n"; // create the process and set its parameters MinecraftProcess *proc = new MinecraftProcess(this); diff --git a/logic/profiler/BaseProfiler.cpp b/logic/profiler/BaseProfiler.cpp new file mode 100644 index 00000000..788e9614 --- /dev/null +++ b/logic/profiler/BaseProfiler.cpp @@ -0,0 +1,19 @@ +#include "BaseProfiler.h" + +BaseProfiler::BaseProfiler(OneSixInstance *instance, QObject *parent) + : QObject(parent), m_instance(instance) +{ +} + +BaseProfiler::~BaseProfiler() +{ +} + +void BaseProfiler::beginProfiling(MinecraftProcess *process) +{ + beginProfilingImpl(process); +} + +BaseProfilerFactory::~BaseProfilerFactory() +{ +} diff --git a/logic/profiler/BaseProfiler.h b/logic/profiler/BaseProfiler.h new file mode 100644 index 00000000..2986c8e1 --- /dev/null +++ b/logic/profiler/BaseProfiler.h @@ -0,0 +1,39 @@ +#pragma once + +#include <QObject> + +class OneSixInstance; +class SettingsObject; +class MinecraftProcess; + +class BaseProfiler : public QObject +{ + Q_OBJECT +public: + explicit BaseProfiler(OneSixInstance *instance, QObject *parent = 0); + virtual ~BaseProfiler(); + +public +slots: + void beginProfiling(MinecraftProcess *process); + +protected: + OneSixInstance *m_instance; + + virtual void beginProfilingImpl(MinecraftProcess *process) = 0; + +signals: + void readyToLaunch(const QString &message); +}; + +class BaseProfilerFactory +{ +public: + virtual ~BaseProfilerFactory(); + + virtual void registerSettings(SettingsObject *settings) = 0; + + virtual BaseProfiler *createProfiler(OneSixInstance *instance, QObject *parent = 0) = 0; + + virtual bool check(const QString &path, QString *error) = 0; +}; diff --git a/logic/profiler/JProfiler.cpp b/logic/profiler/JProfiler.cpp new file mode 100644 index 00000000..b9cf19a0 --- /dev/null +++ b/logic/profiler/JProfiler.cpp @@ -0,0 +1,56 @@ +#include "JProfiler.h" + +#include <QDir> +#include <QMessageBox> + +#include "settingsobject.h" +#include "logic/MinecraftProcess.h" +#include "logic/OneSixInstance.h" +#include "MultiMC.h" + +JProfiler::JProfiler(OneSixInstance *instance, QObject *parent) : BaseProfiler(instance, parent) +{ +} + +void JProfiler::beginProfilingImpl(MinecraftProcess *process) +{ + int port = MMC->settings()->get("JProfilerPort").toInt(); + QProcess *profiler = new QProcess(this); + profiler->setArguments(QStringList() << "-d" << QString::number(process->pid()) << "--gui" + << "-p" << QString::number(port)); + profiler->setProgram(QDir(MMC->settings()->get("JProfilerPath").toString()) + .absoluteFilePath("bin/jpenable")); + connect(profiler, &QProcess::started, [this, port]() + { emit readyToLaunch(tr("Listening on port: %1").arg(port)); }); + connect(profiler, SIGNAL(finished(int)), profiler, SLOT(deleteLater())); + profiler->start(); + QMessageBox::information(0, tr("JProfiler"), + tr("JProfiler started and listening on port %1").arg(port)); +} + +void JProfilerFactory::registerSettings(SettingsObject *settings) +{ + settings->registerSetting("JProfilerPath"); + settings->registerSetting("JProfilerPort", 42042); +} + +BaseProfiler *JProfilerFactory::createProfiler(OneSixInstance *instance, QObject *parent) +{ + return new JProfiler(instance, parent); +} + +bool JProfilerFactory::check(const QString &path, QString *error) +{ + QDir dir(path); + if (!dir.exists()) + { + *error = QObject::tr("Path does not exist"); + return false; + } + if (!dir.exists("bin") || !dir.exists("bin/jprofiler") || !dir.exists("bin/agent.jar")) + { + *error = QObject::tr("Invalid JProfiler install"); + return false; + } + return true; +} diff --git a/logic/profiler/JProfiler.h b/logic/profiler/JProfiler.h new file mode 100644 index 00000000..9fa3591a --- /dev/null +++ b/logic/profiler/JProfiler.h @@ -0,0 +1,21 @@ +#pragma once + +#include "BaseProfiler.h" + +class JProfiler : public BaseProfiler +{ + Q_OBJECT +public: + JProfiler(OneSixInstance *instance, QObject *parent = 0); + +protected: + void beginProfilingImpl(MinecraftProcess *process); +}; + +class JProfilerFactory : public BaseProfilerFactory +{ +public: + void registerSettings(SettingsObject *settings) override; + BaseProfiler *createProfiler(OneSixInstance *instance, QObject *parent = 0) override; + bool check(const QString &path, QString *error) override; +}; diff --git a/logic/profiler/JVisualVM.cpp b/logic/profiler/JVisualVM.cpp new file mode 100644 index 00000000..31cc51b1 --- /dev/null +++ b/logic/profiler/JVisualVM.cpp @@ -0,0 +1,46 @@ +#include "JVisualVM.h" + +#include <QDir> +#include <QStandardPaths> + +#include "settingsobject.h" +#include "logic/MinecraftProcess.h" +#include "logic/OneSixInstance.h" + +JVisualVM::JVisualVM(OneSixInstance *instance, QObject *parent) : BaseProfiler(instance, parent) +{ +} + +void JVisualVM::beginProfilingImpl(MinecraftProcess *process) +{ + QProcess *profiler = new QProcess(this); + profiler->setArguments(QStringList() << "--jdkhome" + << m_instance->settings().get("JavaPath").toString() + << "--openpid" << QString::number(process->pid())); + profiler->setProgram("jvisualvm"); + connect(profiler, &QProcess::started, [this]() + { emit readyToLaunch(tr("JVisualVM started")); }); + connect(profiler, SIGNAL(finished(int)), profiler, SLOT(deleteLater())); + profiler->start(); +} + +void JVisualVMFactory::registerSettings(SettingsObject *settings) +{ + settings->registerSetting("JVisualVMPath"); +} + +BaseProfiler *JVisualVMFactory::createProfiler(OneSixInstance *instance, QObject *parent) +{ + return new JVisualVM(instance, parent); +} + +bool JVisualVMFactory::check(const QString &path, QString *error) +{ + QString resolved = QStandardPaths::findExecutable(path); + if (resolved.isEmpty() && !QDir::isAbsolutePath(path)) + { + *error = QObject::tr("Invalid path to JVisualVM"); + return false; + } + return true; +} diff --git a/logic/profiler/JVisualVM.h b/logic/profiler/JVisualVM.h new file mode 100644 index 00000000..e72b75d9 --- /dev/null +++ b/logic/profiler/JVisualVM.h @@ -0,0 +1,21 @@ +#pragma once + +#include "BaseProfiler.h" + +class JVisualVM : public BaseProfiler +{ + Q_OBJECT +public: + JVisualVM(OneSixInstance *instance, QObject *parent = 0); + +protected: + void beginProfilingImpl(MinecraftProcess *process); +}; + +class JVisualVMFactory : public BaseProfilerFactory +{ +public: + void registerSettings(SettingsObject *settings) override; + BaseProfiler *createProfiler(OneSixInstance *instance, QObject *parent = 0) override; + bool check(const QString &path, QString *error) override; +}; |