yuzu: Use a debugger to generate minidumps
yuzu: Move mini_dump out of core startup_checks: Better exception handling
This commit is contained in:
		@@ -529,6 +529,7 @@ struct Values {
 | 
			
		||||
    Setting<bool> use_debug_asserts{false, "use_debug_asserts"};
 | 
			
		||||
    Setting<bool> use_auto_stub{false, "use_auto_stub"};
 | 
			
		||||
    Setting<bool> enable_all_controllers{false, "enable_all_controllers"};
 | 
			
		||||
    Setting<bool> create_crash_dumps{false, "create_crash_dumps"};
 | 
			
		||||
 | 
			
		||||
    // Miscellaneous
 | 
			
		||||
    Setting<std::string> log_filter{"*:Info", "log_filter"};
 | 
			
		||||
 
 | 
			
		||||
@@ -208,6 +208,16 @@ add_executable(yuzu
 | 
			
		||||
    yuzu.rc
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
if (WIN32 AND NOT ("${DBGHELP_LIBRARY}" STREQUAL "DBGHELP_LIBRARY-NOTFOUND"))
 | 
			
		||||
    target_sources(yuzu PRIVATE
 | 
			
		||||
        mini_dump.cpp
 | 
			
		||||
        mini_dump.h
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    target_link_libraries(yuzu PUBLIC ${DBGHELP_LIBRARY})
 | 
			
		||||
    target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
file(GLOB COMPAT_LIST
 | 
			
		||||
     ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
 | 
			
		||||
     ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
 | 
			
		||||
    InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
 | 
			
		||||
    : QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()),
 | 
			
		||||
      parameters(std::move(parameters_)), input_subsystem{input_subsystem_},
 | 
			
		||||
      input_profiles(std::make_unique<InputProfiles>(system_)), system{system_} {
 | 
			
		||||
      input_profiles(std::make_unique<InputProfiles>()), system{system_} {
 | 
			
		||||
    ui->setupUi(this);
 | 
			
		||||
 | 
			
		||||
    player_widgets = {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,8 +15,7 @@
 | 
			
		||||
 | 
			
		||||
namespace FS = Common::FS;
 | 
			
		||||
 | 
			
		||||
Config::Config(Core::System& system_, const std::string& config_name, ConfigType config_type)
 | 
			
		||||
    : type(config_type), system{system_} {
 | 
			
		||||
Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) {
 | 
			
		||||
    global = config_type == ConfigType::GlobalConfig;
 | 
			
		||||
 | 
			
		||||
    Initialize(config_name);
 | 
			
		||||
@@ -546,6 +545,7 @@ void Config::ReadDebuggingValues() {
 | 
			
		||||
    ReadBasicSetting(Settings::values.use_debug_asserts);
 | 
			
		||||
    ReadBasicSetting(Settings::values.use_auto_stub);
 | 
			
		||||
    ReadBasicSetting(Settings::values.enable_all_controllers);
 | 
			
		||||
    ReadBasicSetting(Settings::values.create_crash_dumps);
 | 
			
		||||
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
}
 | 
			
		||||
@@ -1160,6 +1160,7 @@ void Config::SaveDebuggingValues() {
 | 
			
		||||
    WriteBasicSetting(Settings::values.use_debug_asserts);
 | 
			
		||||
    WriteBasicSetting(Settings::values.disable_macro_jit);
 | 
			
		||||
    WriteBasicSetting(Settings::values.enable_all_controllers);
 | 
			
		||||
    WriteBasicSetting(Settings::values.create_crash_dumps);
 | 
			
		||||
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
}
 | 
			
		||||
@@ -1545,7 +1546,6 @@ void Config::Reload() {
 | 
			
		||||
    ReadValues();
 | 
			
		||||
    // To apply default value changes
 | 
			
		||||
    SaveValues();
 | 
			
		||||
    system.ApplySettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Config::Save() {
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ public:
 | 
			
		||||
        InputProfile,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    explicit Config(Core::System& system_, const std::string& config_name = "qt-config",
 | 
			
		||||
    explicit Config(const std::string& config_name = "qt-config",
 | 
			
		||||
                    ConfigType config_type = ConfigType::GlobalConfig);
 | 
			
		||||
    ~Config();
 | 
			
		||||
 | 
			
		||||
@@ -194,8 +194,6 @@ private:
 | 
			
		||||
    std::unique_ptr<QSettings> qt_config;
 | 
			
		||||
    std::string qt_config_loc;
 | 
			
		||||
    bool global;
 | 
			
		||||
 | 
			
		||||
    Core::System& system;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// These metatype declarations cannot be in common/settings.h because core is devoid of QT
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
#include <QDesktopServices>
 | 
			
		||||
#include <QMessageBox>
 | 
			
		||||
#include <QUrl>
 | 
			
		||||
#include "common/fs/path_util.h"
 | 
			
		||||
#include "common/logging/backend.h"
 | 
			
		||||
@@ -26,6 +27,16 @@ ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent)
 | 
			
		||||
 | 
			
		||||
    connect(ui->toggle_gdbstub, &QCheckBox::toggled,
 | 
			
		||||
            [&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); });
 | 
			
		||||
 | 
			
		||||
    connect(ui->create_crash_dumps, &QCheckBox::stateChanged, [&](int) {
 | 
			
		||||
        if (crash_dump_warning_shown) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        QMessageBox::warning(this, tr("Restart Required"),
 | 
			
		||||
                             tr("yuzu is required to restart in order to apply this setting."),
 | 
			
		||||
                             QMessageBox::Ok, QMessageBox::Ok);
 | 
			
		||||
        crash_dump_warning_shown = true;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConfigureDebug::~ConfigureDebug() = default;
 | 
			
		||||
@@ -71,7 +82,14 @@ void ConfigureDebug::SetConfiguration() {
 | 
			
		||||
    ui->disable_web_applet->setChecked(UISettings::values.disable_web_applet.GetValue());
 | 
			
		||||
#else
 | 
			
		||||
    ui->disable_web_applet->setEnabled(false);
 | 
			
		||||
    ui->disable_web_applet->setText(QString::fromUtf8("Web applet not compiled"));
 | 
			
		||||
    ui->disable_web_applet->setText(tr("Web applet not compiled"));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef YUZU_DBGHELP
 | 
			
		||||
    ui->create_crash_dumps->setChecked(Settings::values.create_crash_dumps.GetValue());
 | 
			
		||||
#else
 | 
			
		||||
    ui->create_crash_dumps->setEnabled(false);
 | 
			
		||||
    ui->create_crash_dumps->setText(tr("MiniDump creation not compiled"));
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -84,6 +102,7 @@ void ConfigureDebug::ApplyConfiguration() {
 | 
			
		||||
    Settings::values.enable_fs_access_log = ui->fs_access_log->isChecked();
 | 
			
		||||
    Settings::values.reporting_services = ui->reporting_services->isChecked();
 | 
			
		||||
    Settings::values.dump_audio_commands = ui->dump_audio_commands->isChecked();
 | 
			
		||||
    Settings::values.create_crash_dumps = ui->create_crash_dumps->isChecked();
 | 
			
		||||
    Settings::values.quest_flag = ui->quest_flag->isChecked();
 | 
			
		||||
    Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
 | 
			
		||||
    Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
 | 
			
		||||
 
 | 
			
		||||
@@ -32,4 +32,6 @@ private:
 | 
			
		||||
    std::unique_ptr<Ui::ConfigureDebug> ui;
 | 
			
		||||
 | 
			
		||||
    const Core::System& system;
 | 
			
		||||
 | 
			
		||||
    bool crash_dump_warning_shown{false};
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -7,60 +7,60 @@
 | 
			
		||||
  </property>
 | 
			
		||||
 <widget class="QWidget">
 | 
			
		||||
  <layout class="QVBoxLayout" name="verticalLayout_1">
 | 
			
		||||
    <item>
 | 
			
		||||
      <layout class="QVBoxLayout" name="verticalLayout_2">
 | 
			
		||||
       <item>
 | 
			
		||||
        <widget class="QGroupBox" name="groupBox">
 | 
			
		||||
         <property name="title">
 | 
			
		||||
          <string>Debugger</string>
 | 
			
		||||
         </property>
 | 
			
		||||
         <layout class="QVBoxLayout" name="verticalLayout_3">
 | 
			
		||||
   <item>
 | 
			
		||||
    <layout class="QVBoxLayout" name="verticalLayout_2">
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QGroupBox" name="groupBox">
 | 
			
		||||
       <property name="title">
 | 
			
		||||
        <string>Debugger</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <layout class="QVBoxLayout" name="verticalLayout_3">
 | 
			
		||||
        <item>
 | 
			
		||||
         <layout class="QHBoxLayout" name="horizontalLayout_11">
 | 
			
		||||
          <item>
 | 
			
		||||
           <layout class="QHBoxLayout" name="horizontalLayout_11">
 | 
			
		||||
            <item>
 | 
			
		||||
             <widget class="QCheckBox" name="toggle_gdbstub">
 | 
			
		||||
              <property name="text">
 | 
			
		||||
               <string>Enable GDB Stub</string>
 | 
			
		||||
              </property>
 | 
			
		||||
             </widget>
 | 
			
		||||
            </item>
 | 
			
		||||
            <item>
 | 
			
		||||
             <spacer name="horizontalSpacer">
 | 
			
		||||
              <property name="orientation">
 | 
			
		||||
               <enum>Qt::Horizontal</enum>
 | 
			
		||||
              </property>
 | 
			
		||||
              <property name="sizeHint" stdset="0">
 | 
			
		||||
               <size>
 | 
			
		||||
                <width>40</width>
 | 
			
		||||
                <height>20</height>
 | 
			
		||||
               </size>
 | 
			
		||||
              </property>
 | 
			
		||||
             </spacer>
 | 
			
		||||
            </item>
 | 
			
		||||
            <item>
 | 
			
		||||
             <widget class="QLabel" name="label_11">
 | 
			
		||||
              <property name="text">
 | 
			
		||||
               <string>Port:</string>
 | 
			
		||||
              </property>
 | 
			
		||||
             </widget>
 | 
			
		||||
            </item>
 | 
			
		||||
            <item>
 | 
			
		||||
             <widget class="QSpinBox" name="gdbport_spinbox">
 | 
			
		||||
              <property name="minimum">
 | 
			
		||||
                <number>1024</number>
 | 
			
		||||
              </property>
 | 
			
		||||
              <property name="maximum">
 | 
			
		||||
               <number>65535</number>
 | 
			
		||||
              </property>
 | 
			
		||||
             </widget>
 | 
			
		||||
            </item>
 | 
			
		||||
           </layout>
 | 
			
		||||
           <widget class="QCheckBox" name="toggle_gdbstub">
 | 
			
		||||
            <property name="text">
 | 
			
		||||
             <string>Enable GDB Stub</string>
 | 
			
		||||
            </property>
 | 
			
		||||
           </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
          <item>
 | 
			
		||||
           <spacer name="horizontalSpacer">
 | 
			
		||||
            <property name="orientation">
 | 
			
		||||
             <enum>Qt::Horizontal</enum>
 | 
			
		||||
            </property>
 | 
			
		||||
            <property name="sizeHint" stdset="0">
 | 
			
		||||
             <size>
 | 
			
		||||
              <width>40</width>
 | 
			
		||||
              <height>20</height>
 | 
			
		||||
             </size>
 | 
			
		||||
            </property>
 | 
			
		||||
           </spacer>
 | 
			
		||||
          </item>
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QLabel" name="label_11">
 | 
			
		||||
            <property name="text">
 | 
			
		||||
             <string>Port:</string>
 | 
			
		||||
            </property>
 | 
			
		||||
           </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QSpinBox" name="gdbport_spinbox">
 | 
			
		||||
            <property name="minimum">
 | 
			
		||||
             <number>1024</number>
 | 
			
		||||
            </property>
 | 
			
		||||
            <property name="maximum">
 | 
			
		||||
             <number>65535</number>
 | 
			
		||||
            </property>
 | 
			
		||||
           </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
         </layout>
 | 
			
		||||
        </widget>
 | 
			
		||||
       </item>
 | 
			
		||||
      </layout>
 | 
			
		||||
        </item>
 | 
			
		||||
       </layout>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
    </layout>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item>
 | 
			
		||||
    <widget class="QGroupBox" name="groupBox_2">
 | 
			
		||||
     <property name="title">
 | 
			
		||||
@@ -231,6 +231,13 @@
 | 
			
		||||
      <string>Debugging</string>
 | 
			
		||||
     </property>
 | 
			
		||||
     <layout class="QGridLayout" name="gridLayout_3">
 | 
			
		||||
      <item row="2" column="0">
 | 
			
		||||
       <widget class="QCheckBox" name="reporting_services">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Enable Verbose Reporting Services**</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item row="0" column="0">
 | 
			
		||||
       <widget class="QCheckBox" name="fs_access_log">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
@@ -238,20 +245,20 @@
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item row="1" column="0">
 | 
			
		||||
      <item row="0" column="1">
 | 
			
		||||
       <widget class="QCheckBox" name="dump_audio_commands">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Dump Audio Commands To Console**</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer.</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Dump Audio Commands To Console**</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item row="2" column="0">
 | 
			
		||||
       <widget class="QCheckBox" name="reporting_services">
 | 
			
		||||
      <item row="2" column="1">
 | 
			
		||||
       <widget class="QCheckBox" name="create_crash_dumps">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Enable Verbose Reporting Services**</string>
 | 
			
		||||
         <string>Create Minidump After Crash</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
@@ -340,7 +347,6 @@
 | 
			
		||||
  <tabstop>disable_loop_safety_checks</tabstop>
 | 
			
		||||
  <tabstop>fs_access_log</tabstop>
 | 
			
		||||
  <tabstop>reporting_services</tabstop>
 | 
			
		||||
  <tabstop>dump_audio_commands</tabstop>
 | 
			
		||||
  <tabstop>quest_flag</tabstop>
 | 
			
		||||
  <tabstop>enable_cpu_debugging</tabstop>
 | 
			
		||||
  <tabstop>use_debug_asserts</tabstop>
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,7 @@ void OnDockedModeChanged(bool last_state, bool new_state, Core::System& system)
 | 
			
		||||
 | 
			
		||||
ConfigureInput::ConfigureInput(Core::System& system_, QWidget* parent)
 | 
			
		||||
    : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
 | 
			
		||||
      profiles(std::make_unique<InputProfiles>(system_)), system{system_} {
 | 
			
		||||
      profiles(std::make_unique<InputProfiles>()), system{system_} {
 | 
			
		||||
    ui->setupUi(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -42,8 +42,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
 | 
			
		||||
    const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
 | 
			
		||||
    const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
 | 
			
		||||
                                                : fmt::format("{:016X}", title_id);
 | 
			
		||||
    game_config =
 | 
			
		||||
        std::make_unique<Config>(system, config_file_name, Config::ConfigType::PerGameConfig);
 | 
			
		||||
    game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig);
 | 
			
		||||
 | 
			
		||||
    addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this);
 | 
			
		||||
    audio_tab = std::make_unique<ConfigureAudio>(system_, this);
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) {
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
InputProfiles::InputProfiles(Core::System& system_) : system{system_} {
 | 
			
		||||
InputProfiles::InputProfiles() {
 | 
			
		||||
    const auto input_profile_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "input";
 | 
			
		||||
 | 
			
		||||
    if (!FS::IsDir(input_profile_loc)) {
 | 
			
		||||
@@ -43,8 +43,8 @@ InputProfiles::InputProfiles(Core::System& system_) : system{system_} {
 | 
			
		||||
 | 
			
		||||
            if (IsINI(filename) && IsProfileNameValid(name_without_ext)) {
 | 
			
		||||
                map_profiles.insert_or_assign(
 | 
			
		||||
                    name_without_ext, std::make_unique<Config>(system, name_without_ext,
 | 
			
		||||
                                                               Config::ConfigType::InputProfile));
 | 
			
		||||
                    name_without_ext,
 | 
			
		||||
                    std::make_unique<Config>(name_without_ext, Config::ConfigType::InputProfile));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
@@ -80,8 +80,7 @@ bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t p
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    map_profiles.insert_or_assign(
 | 
			
		||||
        profile_name,
 | 
			
		||||
        std::make_unique<Config>(system, profile_name, Config::ConfigType::InputProfile));
 | 
			
		||||
        profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile));
 | 
			
		||||
 | 
			
		||||
    return SaveProfile(profile_name, player_index);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ class Config;
 | 
			
		||||
class InputProfiles {
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    explicit InputProfiles(Core::System& system_);
 | 
			
		||||
    explicit InputProfiles();
 | 
			
		||||
    virtual ~InputProfiles();
 | 
			
		||||
 | 
			
		||||
    std::vector<std::string> GetInputProfileNames();
 | 
			
		||||
@@ -31,6 +31,4 @@ private:
 | 
			
		||||
    bool ProfileExistsInMap(const std::string& profile_name) const;
 | 
			
		||||
 | 
			
		||||
    std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles;
 | 
			
		||||
 | 
			
		||||
    Core::System& system;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -138,6 +138,10 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
 | 
			
		||||
#include "yuzu/uisettings.h"
 | 
			
		||||
#include "yuzu/util/clickable_label.h"
 | 
			
		||||
 | 
			
		||||
#ifdef YUZU_DBGHELP
 | 
			
		||||
#include "yuzu/mini_dump.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using namespace Common::Literals;
 | 
			
		||||
 | 
			
		||||
#ifdef USE_DISCORD_PRESENCE
 | 
			
		||||
@@ -269,10 +273,9 @@ bool GMainWindow::CheckDarkMode() {
 | 
			
		||||
#endif // __linux__
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GMainWindow::GMainWindow(bool has_broken_vulkan)
 | 
			
		||||
GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan)
 | 
			
		||||
    : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
 | 
			
		||||
      input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
 | 
			
		||||
      config{std::make_unique<Config>(*system)},
 | 
			
		||||
      input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)},
 | 
			
		||||
      vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
 | 
			
		||||
      provider{std::make_unique<FileSys::ManualContentProvider>()} {
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
@@ -1637,7 +1640,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
 | 
			
		||||
        const auto config_file_name = title_id == 0
 | 
			
		||||
                                          ? Common::FS::PathToUTF8String(file_path.filename())
 | 
			
		||||
                                          : fmt::format("{:016X}", title_id);
 | 
			
		||||
        Config per_game_config(*system, config_file_name, Config::ConfigType::PerGameConfig);
 | 
			
		||||
        Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
 | 
			
		||||
        system->ApplySettings();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Save configurations
 | 
			
		||||
@@ -2981,7 +2985,7 @@ void GMainWindow::OnConfigure() {
 | 
			
		||||
 | 
			
		||||
        Settings::values.disabled_addons.clear();
 | 
			
		||||
 | 
			
		||||
        config = std::make_unique<Config>(*system);
 | 
			
		||||
        config = std::make_unique<Config>();
 | 
			
		||||
        UISettings::values.reset_to_defaults = false;
 | 
			
		||||
 | 
			
		||||
        UISettings::values.game_dirs = std::move(old_game_dirs);
 | 
			
		||||
@@ -3042,6 +3046,7 @@ void GMainWindow::OnConfigure() {
 | 
			
		||||
 | 
			
		||||
    UpdateStatusButtons();
 | 
			
		||||
    controller_dialog->refreshConfiguration();
 | 
			
		||||
    system->ApplySettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GMainWindow::OnConfigureTas() {
 | 
			
		||||
@@ -4082,7 +4087,22 @@ void GMainWindow::changeEvent(QEvent* event) {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[]) {
 | 
			
		||||
    std::unique_ptr<Config> config = std::make_unique<Config>();
 | 
			
		||||
    bool has_broken_vulkan = false;
 | 
			
		||||
    bool is_child = false;
 | 
			
		||||
    if (CheckEnvVars(&is_child)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef YUZU_DBGHELP
 | 
			
		||||
    PROCESS_INFORMATION pi;
 | 
			
		||||
    if (!is_child && Settings::values.create_crash_dumps.GetValue() && SpawnDebuggee(argv[0], pi)) {
 | 
			
		||||
        config.reset(nullptr);
 | 
			
		||||
        DebugDebuggee(pi);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (StartupChecks(argv[0], &has_broken_vulkan)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
@@ -4135,7 +4155,7 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
    // generating shaders
 | 
			
		||||
    setlocale(LC_ALL, "C");
 | 
			
		||||
 | 
			
		||||
    GMainWindow main_window{has_broken_vulkan};
 | 
			
		||||
    GMainWindow main_window{std::move(config), has_broken_vulkan};
 | 
			
		||||
    // After settings have been loaded by GMainWindow, apply the filter
 | 
			
		||||
    main_window.show();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -120,7 +120,7 @@ class GMainWindow : public QMainWindow {
 | 
			
		||||
public:
 | 
			
		||||
    void filterBarSetChecked(bool state);
 | 
			
		||||
    void UpdateUITheme();
 | 
			
		||||
    explicit GMainWindow(bool has_broken_vulkan);
 | 
			
		||||
    explicit GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan);
 | 
			
		||||
    ~GMainWindow() override;
 | 
			
		||||
 | 
			
		||||
    bool DropAction(QDropEvent* event);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										185
									
								
								src/yuzu/mini_dump.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								src/yuzu/mini_dump.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,185 @@
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <ctime>
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "yuzu/mini_dump.h"
 | 
			
		||||
#include "yuzu/startup_checks.h"
 | 
			
		||||
 | 
			
		||||
// dbghelp.h must be included after windows.h
 | 
			
		||||
#include <dbghelp.h>
 | 
			
		||||
 | 
			
		||||
void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
 | 
			
		||||
                    EXCEPTION_POINTERS* pep) {
 | 
			
		||||
    LOG_INFO(Core, "called");
 | 
			
		||||
 | 
			
		||||
    char file_name[255];
 | 
			
		||||
    const std::time_t the_time = std::time(nullptr);
 | 
			
		||||
    std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time));
 | 
			
		||||
 | 
			
		||||
    // Open the file
 | 
			
		||||
    HANDLE file_handle = CreateFile(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr,
 | 
			
		||||
                                    CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
 | 
			
		||||
 | 
			
		||||
    if ((file_handle != nullptr) && (file_handle != INVALID_HANDLE_VALUE)) {
 | 
			
		||||
        // Create the minidump
 | 
			
		||||
        const MINIDUMP_TYPE dump_type = MiniDumpNormal;
 | 
			
		||||
 | 
			
		||||
        const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle,
 | 
			
		||||
                                                         dump_type, (pep != 0) ? info : 0, 0, 0);
 | 
			
		||||
 | 
			
		||||
        if (!write_dump_status) {
 | 
			
		||||
            LOG_ERROR(Core, "MiniDumpWriteDump failed. Error: {}", GetLastError());
 | 
			
		||||
        } else {
 | 
			
		||||
            LOG_INFO(Core, "Minidump created.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Close the file
 | 
			
		||||
        CloseHandle(file_handle);
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_ERROR(Core, "CreateFile failed. Error: {}", GetLastError());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) {
 | 
			
		||||
    std::memset(&pi, 0, sizeof(pi));
 | 
			
		||||
 | 
			
		||||
    if (!SpawnChild(arg0, &pi, 0)) {
 | 
			
		||||
        std::fprintf(stderr, "warning: continuing without crash dumps\n");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Don't debug if we are already being debugged
 | 
			
		||||
    if (IsDebuggerPresent()) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const bool can_debug = DebugActiveProcess(pi.dwProcessId);
 | 
			
		||||
    if (!can_debug) {
 | 
			
		||||
        std::fprintf(stderr,
 | 
			
		||||
                     "warning: DebugActiveProcess failed (%d), continuing without crash dumps\n",
 | 
			
		||||
                     GetLastError());
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DebugDebuggee(PROCESS_INFORMATION& pi) {
 | 
			
		||||
    DEBUG_EVENT deb_ev;
 | 
			
		||||
 | 
			
		||||
    while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) {
 | 
			
		||||
        const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE);
 | 
			
		||||
        if (!wait_success) {
 | 
			
		||||
            std::fprintf(stderr, "error: WaitForDebugEvent failed (%d)\n", GetLastError());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch (deb_ev.dwDebugEventCode) {
 | 
			
		||||
        case OUTPUT_DEBUG_STRING_EVENT:
 | 
			
		||||
        case CREATE_PROCESS_DEBUG_EVENT:
 | 
			
		||||
        case CREATE_THREAD_DEBUG_EVENT:
 | 
			
		||||
        case EXIT_PROCESS_DEBUG_EVENT:
 | 
			
		||||
        case EXIT_THREAD_DEBUG_EVENT:
 | 
			
		||||
        case LOAD_DLL_DEBUG_EVENT:
 | 
			
		||||
        case RIP_EVENT:
 | 
			
		||||
        case UNLOAD_DLL_DEBUG_EVENT:
 | 
			
		||||
            ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE);
 | 
			
		||||
            break;
 | 
			
		||||
        case EXCEPTION_DEBUG_EVENT:
 | 
			
		||||
            EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord;
 | 
			
		||||
 | 
			
		||||
            std::fprintf(stderr, "ExceptionCode: 0x%08x %s\n", record.ExceptionCode,
 | 
			
		||||
                         ExceptionName(record.ExceptionCode));
 | 
			
		||||
            if (!deb_ev.u.Exception.dwFirstChance) {
 | 
			
		||||
                HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, false, deb_ev.dwThreadId);
 | 
			
		||||
                if (thread_handle == nullptr) {
 | 
			
		||||
                    std::fprintf(stderr, "OpenThread failed (%d)\n", GetLastError());
 | 
			
		||||
                }
 | 
			
		||||
                if (SuspendThread(thread_handle) == (DWORD)-1) {
 | 
			
		||||
                    std::fprintf(stderr, "SuspendThread failed (%d)\n", GetLastError());
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                CONTEXT context;
 | 
			
		||||
                std::memset(&context, 0, sizeof(context));
 | 
			
		||||
                context.ContextFlags = CONTEXT_ALL;
 | 
			
		||||
                if (!GetThreadContext(thread_handle, &context)) {
 | 
			
		||||
                    std::fprintf(stderr, "GetThreadContext failed (%d)\n", GetLastError());
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                EXCEPTION_POINTERS ep;
 | 
			
		||||
                ep.ExceptionRecord = &record;
 | 
			
		||||
                ep.ContextRecord = &context;
 | 
			
		||||
 | 
			
		||||
                MINIDUMP_EXCEPTION_INFORMATION info;
 | 
			
		||||
                info.ThreadId = deb_ev.dwThreadId;
 | 
			
		||||
                info.ExceptionPointers = &ep;
 | 
			
		||||
                info.ClientPointers = false;
 | 
			
		||||
 | 
			
		||||
                CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep);
 | 
			
		||||
 | 
			
		||||
                std::fprintf(stderr, "previous thread suspend count: %d\n",
 | 
			
		||||
                             ResumeThread(thread_handle));
 | 
			
		||||
                if (CloseHandle(thread_handle) == 0) {
 | 
			
		||||
                    std::fprintf(stderr, "error: CloseHandle(thread_handle) failed (%d)\n",
 | 
			
		||||
                                 GetLastError());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* ExceptionName(DWORD exception) {
 | 
			
		||||
    switch (exception) {
 | 
			
		||||
    case EXCEPTION_ACCESS_VIOLATION:
 | 
			
		||||
        return "EXCEPTION_ACCESS_VIOLATION";
 | 
			
		||||
    case EXCEPTION_DATATYPE_MISALIGNMENT:
 | 
			
		||||
        return "EXCEPTION_DATATYPE_MISALIGNMENT";
 | 
			
		||||
    case EXCEPTION_BREAKPOINT:
 | 
			
		||||
        return "EXCEPTION_BREAKPOINT";
 | 
			
		||||
    case EXCEPTION_SINGLE_STEP:
 | 
			
		||||
        return "EXCEPTION_SINGLE_STEP";
 | 
			
		||||
    case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
 | 
			
		||||
        return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
 | 
			
		||||
    case EXCEPTION_FLT_DENORMAL_OPERAND:
 | 
			
		||||
        return "EXCEPTION_FLT_DENORMAL_OPERAND";
 | 
			
		||||
    case EXCEPTION_FLT_DIVIDE_BY_ZERO:
 | 
			
		||||
        return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
 | 
			
		||||
    case EXCEPTION_FLT_INEXACT_RESULT:
 | 
			
		||||
        return "EXCEPTION_FLT_INEXACT_RESULT";
 | 
			
		||||
    case EXCEPTION_FLT_INVALID_OPERATION:
 | 
			
		||||
        return "EXCEPTION_FLT_INVALID_OPERATION";
 | 
			
		||||
    case EXCEPTION_FLT_OVERFLOW:
 | 
			
		||||
        return "EXCEPTION_FLT_OVERFLOW";
 | 
			
		||||
    case EXCEPTION_FLT_STACK_CHECK:
 | 
			
		||||
        return "EXCEPTION_FLT_STACK_CHECK";
 | 
			
		||||
    case EXCEPTION_FLT_UNDERFLOW:
 | 
			
		||||
        return "EXCEPTION_FLT_UNDERFLOW";
 | 
			
		||||
    case EXCEPTION_INT_DIVIDE_BY_ZERO:
 | 
			
		||||
        return "EXCEPTION_INT_DIVIDE_BY_ZERO";
 | 
			
		||||
    case EXCEPTION_INT_OVERFLOW:
 | 
			
		||||
        return "EXCEPTION_INT_OVERFLOW";
 | 
			
		||||
    case EXCEPTION_PRIV_INSTRUCTION:
 | 
			
		||||
        return "EXCEPTION_PRIV_INSTRUCTION";
 | 
			
		||||
    case EXCEPTION_IN_PAGE_ERROR:
 | 
			
		||||
        return "EXCEPTION_IN_PAGE_ERROR";
 | 
			
		||||
    case EXCEPTION_ILLEGAL_INSTRUCTION:
 | 
			
		||||
        return "EXCEPTION_ILLEGAL_INSTRUCTION";
 | 
			
		||||
    case EXCEPTION_NONCONTINUABLE_EXCEPTION:
 | 
			
		||||
        return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
 | 
			
		||||
    case EXCEPTION_STACK_OVERFLOW:
 | 
			
		||||
        return "EXCEPTION_STACK_OVERFLOW";
 | 
			
		||||
    case EXCEPTION_INVALID_DISPOSITION:
 | 
			
		||||
        return "EXCEPTION_INVALID_DISPOSITION";
 | 
			
		||||
    case EXCEPTION_GUARD_PAGE:
 | 
			
		||||
        return "EXCEPTION_GUARD_PAGE";
 | 
			
		||||
    case EXCEPTION_INVALID_HANDLE:
 | 
			
		||||
        return "EXCEPTION_INVALID_HANDLE";
 | 
			
		||||
    default:
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/yuzu/mini_dump.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/yuzu/mini_dump.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
 | 
			
		||||
#include <dbghelp.h>
 | 
			
		||||
 | 
			
		||||
void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
 | 
			
		||||
                    EXCEPTION_POINTERS* pep);
 | 
			
		||||
 | 
			
		||||
bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi);
 | 
			
		||||
void DebugDebuggee(PROCESS_INFORMATION& pi);
 | 
			
		||||
const char* ExceptionName(DWORD exception);
 | 
			
		||||
@@ -31,19 +31,36 @@ void CheckVulkan() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
 | 
			
		||||
bool CheckEnvVars(bool* is_child) {
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    // Check environment variable to see if we are the child
 | 
			
		||||
    char variable_contents[8];
 | 
			
		||||
    const DWORD startup_check_var =
 | 
			
		||||
        GetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, variable_contents, 8);
 | 
			
		||||
    if (startup_check_var > 0 && std::strncmp(variable_contents, "ON", 8) == 0) {
 | 
			
		||||
    if (startup_check_var > 0 && std::strncmp(variable_contents, ENV_VAR_ENABLED_TEXT, 8) == 0) {
 | 
			
		||||
        CheckVulkan();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Don't perform startup checks if we are a child process
 | 
			
		||||
    char is_child_s[8];
 | 
			
		||||
    const DWORD is_child_len = GetEnvironmentVariableA(IS_CHILD_ENV_VAR, is_child_s, 8);
 | 
			
		||||
    if (is_child_len > 0 && std::strncmp(is_child_s, ENV_VAR_ENABLED_TEXT, 8) == 0) {
 | 
			
		||||
        *is_child = true;
 | 
			
		||||
        return false;
 | 
			
		||||
    } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) {
 | 
			
		||||
        std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n",
 | 
			
		||||
                     IS_CHILD_ENV_VAR, GetLastError());
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    // Set the startup variable for child processes
 | 
			
		||||
    const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON");
 | 
			
		||||
    const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT);
 | 
			
		||||
    if (!env_var_set) {
 | 
			
		||||
        std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n",
 | 
			
		||||
                     STARTUP_CHECK_ENV_VAR, GetLastError());
 | 
			
		||||
@@ -53,7 +70,7 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
 | 
			
		||||
    PROCESS_INFORMATION process_info;
 | 
			
		||||
    std::memset(&process_info, '\0', sizeof(process_info));
 | 
			
		||||
 | 
			
		||||
    if (!SpawnChild(arg0, &process_info)) {
 | 
			
		||||
    if (!SpawnChild(arg0, &process_info, 0)) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -106,7 +123,7 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
 | 
			
		||||
bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) {
 | 
			
		||||
    STARTUPINFOA startup_info;
 | 
			
		||||
 | 
			
		||||
    std::memset(&startup_info, '\0', sizeof(startup_info));
 | 
			
		||||
@@ -120,7 +137,7 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
 | 
			
		||||
                                                nullptr,       // lpProcessAttributes
 | 
			
		||||
                                                nullptr,       // lpThreadAttributes
 | 
			
		||||
                                                false,         // bInheritHandles
 | 
			
		||||
                                                0,             // dwCreationFlags
 | 
			
		||||
                                                flags,         // dwCreationFlags
 | 
			
		||||
                                                nullptr,       // lpEnvironment
 | 
			
		||||
                                                nullptr,       // lpCurrentDirectory
 | 
			
		||||
                                                &startup_info, // lpStartupInfo
 | 
			
		||||
 
 | 
			
		||||
@@ -7,11 +7,14 @@
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
constexpr char IS_CHILD_ENV_VAR[] = "YUZU_IS_CHILD";
 | 
			
		||||
constexpr char STARTUP_CHECK_ENV_VAR[] = "YUZU_DO_STARTUP_CHECKS";
 | 
			
		||||
constexpr char ENV_VAR_ENABLED_TEXT[] = "ON";
 | 
			
		||||
 | 
			
		||||
void CheckVulkan();
 | 
			
		||||
bool CheckEnvVars(bool* is_child);
 | 
			
		||||
bool StartupChecks(const char* arg0, bool* has_broken_vulkan);
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi);
 | 
			
		||||
bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags);
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user