summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt8
-rw-r--r--MultiMC.cpp25
-rw-r--r--MultiMC.h8
-rw-r--r--gui/MainWindow.cpp56
-rw-r--r--gui/MainWindow.h8
-rw-r--r--gui/MainWindow.ui12
-rw-r--r--gui/dialogs/SettingsDialog.cpp85
-rw-r--r--gui/dialogs/SettingsDialog.h5
-rw-r--r--gui/dialogs/SettingsDialog.ui112
-rw-r--r--logic/OneSixInstance.cpp1
-rw-r--r--logic/profiler/BaseProfiler.cpp19
-rw-r--r--logic/profiler/BaseProfiler.h39
-rw-r--r--logic/profiler/JProfiler.cpp56
-rw-r--r--logic/profiler/JProfiler.h21
-rw-r--r--logic/profiler/JVisualVM.cpp46
-rw-r--r--logic/profiler/JVisualVM.h21
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
diff --git a/MultiMC.h b/MultiMC.h
index 638a442f..69bfb4a4 100644
--- a/MultiMC.h
+++ b/MultiMC.h
@@ -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;
+};