mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-07-09 17:24:49 -06:00
core: Make PICA debugging a toggleable feature (#2254)
This commit is contained in:
parent
77cb5cecd8
commit
fcb10fb899
@ -32,6 +32,7 @@ foreach(KEY IN ITEMS
|
|||||||
"physical_device"
|
"physical_device"
|
||||||
"use_gles"
|
"use_gles"
|
||||||
"renderer_debug"
|
"renderer_debug"
|
||||||
|
"pica_debugging"
|
||||||
"dump_command_buffers"
|
"dump_command_buffers"
|
||||||
"spirv_shader_gen"
|
"spirv_shader_gen"
|
||||||
"disable_spirv_optimizer"
|
"disable_spirv_optimizer"
|
||||||
|
|||||||
@ -314,6 +314,7 @@ void Config::ReadValues() {
|
|||||||
Settings::values.record_frame_times =
|
Settings::values.record_frame_times =
|
||||||
android_config->GetBoolean("Debugging", Settings::HKeys::record_frame_times.c_str(), false);
|
android_config->GetBoolean("Debugging", Settings::HKeys::record_frame_times.c_str(), false);
|
||||||
ReadSetting("Debugging", Settings::values.renderer_debug);
|
ReadSetting("Debugging", Settings::values.renderer_debug);
|
||||||
|
ReadSetting("Debugging", Settings::values.pica_debugging);
|
||||||
ReadSetting("Debugging", Settings::values.use_gdbstub);
|
ReadSetting("Debugging", Settings::values.use_gdbstub);
|
||||||
ReadSetting("Debugging", Settings::values.gdbstub_port);
|
ReadSetting("Debugging", Settings::values.gdbstub_port);
|
||||||
ReadSetting("Debugging", Settings::values.instant_debug_log);
|
ReadSetting("Debugging", Settings::values.instant_debug_log);
|
||||||
|
|||||||
@ -546,6 +546,10 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"(
|
|||||||
# 0 (default): Off, 1: On
|
# 0 (default): Off, 1: On
|
||||||
)") DECLARE_KEY(renderer_debug) BOOST_HANA_STRING(R"(
|
)") DECLARE_KEY(renderer_debug) BOOST_HANA_STRING(R"(
|
||||||
|
|
||||||
|
# Whether to enable PICA200 debugging (does nothing on Android)
|
||||||
|
# 0 (default): Off, 1: On
|
||||||
|
)") DECLARE_KEY(pica_debugging) BOOST_HANA_STRING(R"(
|
||||||
|
|
||||||
# Flush log output on every message
|
# Flush log output on every message
|
||||||
# Immediately commits the debug log to file. Use this if Azahar crashes and the log output is being cut.
|
# Immediately commits the debug log to file. Use this if Azahar crashes and the log output is being cut.
|
||||||
)") DECLARE_KEY(instant_debug_log) BOOST_HANA_STRING(R"(
|
)") DECLARE_KEY(instant_debug_log) BOOST_HANA_STRING(R"(
|
||||||
|
|||||||
@ -255,7 +255,13 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
|||||||
// Register microphone permission check
|
// Register microphone permission check
|
||||||
system.RegisterMicPermissionCheck(&CheckMicPermission);
|
system.RegisterMicPermissionCheck(&CheckMicPermission);
|
||||||
|
|
||||||
Pica::g_debug_context = Pica::DebugContext::Construct();
|
// No PICA debugging on Android
|
||||||
|
if (Settings::values.pica_debugging) {
|
||||||
|
Pica::g_debug_context = Pica::DebugContext::Construct();
|
||||||
|
} else {
|
||||||
|
Pica::g_debug_context.reset();
|
||||||
|
}
|
||||||
|
|
||||||
InputManager::Init();
|
InputManager::Init();
|
||||||
|
|
||||||
window->MakeCurrent();
|
window->MakeCurrent();
|
||||||
|
|||||||
@ -414,7 +414,11 @@ GMainWindow::GMainWindow(Core::System& system_)
|
|||||||
|
|
||||||
LoadTranslation();
|
LoadTranslation();
|
||||||
|
|
||||||
Pica::g_debug_context = Pica::DebugContext::Construct();
|
if (Settings::values.pica_debugging) {
|
||||||
|
Pica::g_debug_context = Pica::DebugContext::Construct();
|
||||||
|
} else {
|
||||||
|
Pica::g_debug_context.reset();
|
||||||
|
}
|
||||||
setAcceptDrops(true);
|
setAcceptDrops(true);
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
statusBar()->hide();
|
statusBar()->hide();
|
||||||
@ -706,8 +710,12 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::InitializeDebugWidgets() {
|
void GMainWindow::InitializeDebugWidgets() {
|
||||||
connect(ui->action_Create_Pica_Surface_Viewer, &QAction::triggered, this,
|
if (Pica::g_debug_context) {
|
||||||
&GMainWindow::OnCreateGraphicsSurfaceViewer);
|
connect(ui->action_Create_Pica_Surface_Viewer, &QAction::triggered, this,
|
||||||
|
&GMainWindow::OnCreateGraphicsSurfaceViewer);
|
||||||
|
} else {
|
||||||
|
ui->action_Create_Pica_Surface_Viewer->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
QMenu* debug_menu = ui->menu_View_Debugging;
|
QMenu* debug_menu = ui->menu_View_Debugging;
|
||||||
|
|
||||||
@ -726,35 +734,37 @@ void GMainWindow::InitializeDebugWidgets() {
|
|||||||
connect(this, &GMainWindow::EmulationStopping, registersWidget,
|
connect(this, &GMainWindow::EmulationStopping, registersWidget,
|
||||||
&RegistersWidget::OnEmulationStopping);
|
&RegistersWidget::OnEmulationStopping);
|
||||||
|
|
||||||
graphicsWidget = new GPUCommandStreamWidget(system, this);
|
if (Pica::g_debug_context) {
|
||||||
addDockWidget(Qt::RightDockWidgetArea, graphicsWidget);
|
graphicsWidget = new GPUCommandStreamWidget(system, this);
|
||||||
graphicsWidget->hide();
|
addDockWidget(Qt::RightDockWidgetArea, graphicsWidget);
|
||||||
debug_menu->addAction(graphicsWidget->toggleViewAction());
|
graphicsWidget->hide();
|
||||||
|
debug_menu->addAction(graphicsWidget->toggleViewAction());
|
||||||
|
|
||||||
graphicsCommandsWidget = new GPUCommandListWidget(system, this);
|
graphicsCommandsWidget = new GPUCommandListWidget(system, this);
|
||||||
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
|
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
|
||||||
graphicsCommandsWidget->hide();
|
graphicsCommandsWidget->hide();
|
||||||
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
|
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
|
||||||
|
|
||||||
graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this);
|
graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this);
|
||||||
addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
|
addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
|
||||||
graphicsBreakpointsWidget->hide();
|
graphicsBreakpointsWidget->hide();
|
||||||
debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
|
debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
|
||||||
|
|
||||||
graphicsVertexShaderWidget =
|
graphicsVertexShaderWidget =
|
||||||
new GraphicsVertexShaderWidget(system, Pica::g_debug_context, this);
|
new GraphicsVertexShaderWidget(system, Pica::g_debug_context, this);
|
||||||
addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget);
|
addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget);
|
||||||
graphicsVertexShaderWidget->hide();
|
graphicsVertexShaderWidget->hide();
|
||||||
debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
|
debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
|
||||||
|
|
||||||
graphicsTracingWidget = new GraphicsTracingWidget(system, Pica::g_debug_context, this);
|
graphicsTracingWidget = new GraphicsTracingWidget(system, Pica::g_debug_context, this);
|
||||||
addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget);
|
addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget);
|
||||||
graphicsTracingWidget->hide();
|
graphicsTracingWidget->hide();
|
||||||
debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
|
debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
|
||||||
connect(this, &GMainWindow::EmulationStarting, graphicsTracingWidget,
|
connect(this, &GMainWindow::EmulationStarting, graphicsTracingWidget,
|
||||||
&GraphicsTracingWidget::OnEmulationStarting);
|
&GraphicsTracingWidget::OnEmulationStarting);
|
||||||
connect(this, &GMainWindow::EmulationStopping, graphicsTracingWidget,
|
connect(this, &GMainWindow::EmulationStopping, graphicsTracingWidget,
|
||||||
&GraphicsTracingWidget::OnEmulationStopping);
|
&GraphicsTracingWidget::OnEmulationStopping);
|
||||||
|
}
|
||||||
|
|
||||||
waitTreeWidget = new WaitTreeWidget(system, this);
|
waitTreeWidget = new WaitTreeWidget(system, this);
|
||||||
addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
|
addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
|
||||||
@ -1553,7 +1563,7 @@ void GMainWindow::BootGame(const QString& filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register debug widgets
|
// Register debug widgets
|
||||||
if (graphicsWidget->isVisible()) {
|
if (graphicsWidget && graphicsWidget->isVisible()) {
|
||||||
graphicsWidget->Register();
|
graphicsWidget->Register();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1638,10 +1648,12 @@ void GMainWindow::ShutdownGame() {
|
|||||||
// breakpoint after (or before) RequestStop() is called, the emulation would never be able
|
// breakpoint after (or before) RequestStop() is called, the emulation would never be able
|
||||||
// to continue out to the main loop and terminate. Thus wait() would hang forever.
|
// to continue out to the main loop and terminate. Thus wait() would hang forever.
|
||||||
// TODO(bunnei): This function is not thread safe, but it's being used as if it were
|
// TODO(bunnei): This function is not thread safe, but it's being used as if it were
|
||||||
Pica::g_debug_context->ClearBreakpoints();
|
if (Pica::g_debug_context) {
|
||||||
|
Pica::g_debug_context->ClearBreakpoints();
|
||||||
|
}
|
||||||
|
|
||||||
// Unregister debug widgets
|
// Unregister debug widgets
|
||||||
if (graphicsWidget->isVisible()) {
|
if (graphicsWidget && graphicsWidget->isVisible()) {
|
||||||
graphicsWidget->Unregister();
|
graphicsWidget->Unregister();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -411,19 +411,19 @@ private:
|
|||||||
std::vector<QString> physical_devices;
|
std::vector<QString> physical_devices;
|
||||||
|
|
||||||
// Debugger panes
|
// Debugger panes
|
||||||
ProfilerWidget* profilerWidget;
|
ProfilerWidget* profilerWidget{};
|
||||||
#if MICROPROFILE_ENABLED
|
#if MICROPROFILE_ENABLED
|
||||||
MicroProfileDialog* microProfileDialog;
|
MicroProfileDialog* microProfileDialog{};
|
||||||
#endif
|
#endif
|
||||||
RegistersWidget* registersWidget;
|
RegistersWidget* registersWidget{};
|
||||||
GPUCommandStreamWidget* graphicsWidget;
|
GPUCommandStreamWidget* graphicsWidget{};
|
||||||
GPUCommandListWidget* graphicsCommandsWidget;
|
GPUCommandListWidget* graphicsCommandsWidget{};
|
||||||
GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
|
GraphicsBreakPointsWidget* graphicsBreakpointsWidget{};
|
||||||
GraphicsVertexShaderWidget* graphicsVertexShaderWidget;
|
GraphicsVertexShaderWidget* graphicsVertexShaderWidget{};
|
||||||
GraphicsTracingWidget* graphicsTracingWidget;
|
GraphicsTracingWidget* graphicsTracingWidget{};
|
||||||
IPCRecorderWidget* ipcRecorderWidget;
|
IPCRecorderWidget* ipcRecorderWidget{};
|
||||||
LLEServiceModulesWidget* lleServiceModulesWidget;
|
LLEServiceModulesWidget* lleServiceModulesWidget{};
|
||||||
WaitTreeWidget* waitTreeWidget;
|
WaitTreeWidget* waitTreeWidget{};
|
||||||
|
|
||||||
QAction* actions_recent_files[max_recent_files_item];
|
QAction* actions_recent_files[max_recent_files_item];
|
||||||
std::array<QAction*, Core::SaveStateSlotCount> actions_load_state;
|
std::array<QAction*, Core::SaveStateSlotCount> actions_load_state;
|
||||||
|
|||||||
@ -513,6 +513,7 @@ void QtConfig::ReadDebuggingValues() {
|
|||||||
ReadBasicSetting(Settings::values.use_gdbstub);
|
ReadBasicSetting(Settings::values.use_gdbstub);
|
||||||
ReadBasicSetting(Settings::values.gdbstub_port);
|
ReadBasicSetting(Settings::values.gdbstub_port);
|
||||||
ReadBasicSetting(Settings::values.renderer_debug);
|
ReadBasicSetting(Settings::values.renderer_debug);
|
||||||
|
ReadBasicSetting(Settings::values.pica_debugging);
|
||||||
ReadBasicSetting(Settings::values.dump_command_buffers);
|
ReadBasicSetting(Settings::values.dump_command_buffers);
|
||||||
ReadBasicSetting(Settings::values.instant_debug_log);
|
ReadBasicSetting(Settings::values.instant_debug_log);
|
||||||
ReadBasicSetting(Settings::values.enable_rpc_server);
|
ReadBasicSetting(Settings::values.enable_rpc_server);
|
||||||
@ -1103,6 +1104,7 @@ void QtConfig::SaveDebuggingValues() {
|
|||||||
WriteBasicSetting(Settings::values.use_gdbstub);
|
WriteBasicSetting(Settings::values.use_gdbstub);
|
||||||
WriteBasicSetting(Settings::values.gdbstub_port);
|
WriteBasicSetting(Settings::values.gdbstub_port);
|
||||||
WriteBasicSetting(Settings::values.renderer_debug);
|
WriteBasicSetting(Settings::values.renderer_debug);
|
||||||
|
WriteBasicSetting(Settings::values.pica_debugging);
|
||||||
WriteBasicSetting(Settings::values.instant_debug_log);
|
WriteBasicSetting(Settings::values.instant_debug_log);
|
||||||
WriteBasicSetting(Settings::values.enable_rpc_server);
|
WriteBasicSetting(Settings::values.enable_rpc_server);
|
||||||
WriteBasicSetting(Settings::values.toggle_unique_data_console_type);
|
WriteBasicSetting(Settings::values.toggle_unique_data_console_type);
|
||||||
|
|||||||
@ -82,8 +82,14 @@ ConfigureDebug::ConfigureDebug(bool is_powered_on_, QWidget* parent)
|
|||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
connect(ui->toggle_pica_debugging, &QCheckBox::clicked, this, [this](bool checked) {
|
||||||
|
QMessageBox::information(this, tr("Relaunch Required"),
|
||||||
|
tr("Please relaunch Azahar for this setting to take effect."));
|
||||||
|
});
|
||||||
|
|
||||||
ui->toggle_cpu_jit->setEnabled(!is_powered_on);
|
ui->toggle_cpu_jit->setEnabled(!is_powered_on);
|
||||||
ui->toggle_renderer_debug->setEnabled(!is_powered_on);
|
ui->toggle_renderer_debug->setEnabled(!is_powered_on);
|
||||||
|
ui->toggle_pica_debugging->setEnabled(!is_powered_on);
|
||||||
ui->toggle_dump_command_buffers->setEnabled(!is_powered_on);
|
ui->toggle_dump_command_buffers->setEnabled(!is_powered_on);
|
||||||
ui->enable_rpc_server->setEnabled(!is_powered_on);
|
ui->enable_rpc_server->setEnabled(!is_powered_on);
|
||||||
ui->toggle_unique_data_console_type->setEnabled(!is_powered_on);
|
ui->toggle_unique_data_console_type->setEnabled(!is_powered_on);
|
||||||
@ -135,6 +141,7 @@ void ConfigureDebug::SetConfiguration() {
|
|||||||
Settings::values.break_on_unmapped_memory_access.GetValue());
|
Settings::values.break_on_unmapped_memory_access.GetValue());
|
||||||
|
|
||||||
ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue());
|
ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue());
|
||||||
|
ui->toggle_pica_debugging->setChecked(Settings::values.pica_debugging.GetValue());
|
||||||
ui->toggle_dump_command_buffers->setChecked(Settings::values.dump_command_buffers.GetValue());
|
ui->toggle_dump_command_buffers->setChecked(Settings::values.dump_command_buffers.GetValue());
|
||||||
|
|
||||||
if (!Settings::IsConfiguringGlobal()) {
|
if (!Settings::IsConfiguringGlobal()) {
|
||||||
@ -181,6 +188,7 @@ void ConfigureDebug::ApplyConfiguration() {
|
|||||||
Settings::values.break_on_unmapped_memory_access =
|
Settings::values.break_on_unmapped_memory_access =
|
||||||
ui->break_on_unmapped_memory_access->isChecked();
|
ui->break_on_unmapped_memory_access->isChecked();
|
||||||
Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked();
|
Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked();
|
||||||
|
Settings::values.pica_debugging = ui->toggle_pica_debugging->isChecked();
|
||||||
Settings::values.dump_command_buffers = ui->toggle_dump_command_buffers->isChecked();
|
Settings::values.dump_command_buffers = ui->toggle_dump_command_buffers->isChecked();
|
||||||
Settings::values.instant_debug_log = ui->instant_debug_log->isChecked();
|
Settings::values.instant_debug_log = ui->instant_debug_log->isChecked();
|
||||||
|
|
||||||
|
|||||||
@ -250,7 +250,14 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="5" column="0">
|
||||||
|
<widget class="QCheckBox" name="toggle_pica_debugging">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable PICA200 debugging</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
<widget class="QCheckBox" name="toggle_dump_command_buffers">
|
<widget class="QCheckBox" name="toggle_dump_command_buffers">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Dump command buffers</string>
|
<string>Dump command buffers</string>
|
||||||
|
|||||||
@ -96,6 +96,7 @@ void LogSettings() {
|
|||||||
log_setting("Renderer_SpirvShaderGen", values.spirv_shader_gen.GetValue());
|
log_setting("Renderer_SpirvShaderGen", values.spirv_shader_gen.GetValue());
|
||||||
log_setting("Renderer_DisableSpirvOptimizer", values.disable_spirv_optimizer.GetValue());
|
log_setting("Renderer_DisableSpirvOptimizer", values.disable_spirv_optimizer.GetValue());
|
||||||
log_setting("Renderer_Debug", values.renderer_debug.GetValue());
|
log_setting("Renderer_Debug", values.renderer_debug.GetValue());
|
||||||
|
log_setting("Renderer_PicaDebugging", values.pica_debugging.GetValue());
|
||||||
log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue());
|
log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue());
|
||||||
log_setting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul.GetValue());
|
log_setting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul.GetValue());
|
||||||
log_setting("Renderer_UseShaderJit", values.use_shader_jit.GetValue());
|
log_setting("Renderer_UseShaderJit", values.use_shader_jit.GetValue());
|
||||||
|
|||||||
@ -527,6 +527,7 @@ struct Values {
|
|||||||
SwitchableSetting<u32> physical_device{0, Keys::physical_device};
|
SwitchableSetting<u32> physical_device{0, Keys::physical_device};
|
||||||
Setting<bool> use_gles{false, Keys::use_gles};
|
Setting<bool> use_gles{false, Keys::use_gles};
|
||||||
Setting<bool> renderer_debug{false, Keys::renderer_debug};
|
Setting<bool> renderer_debug{false, Keys::renderer_debug};
|
||||||
|
Setting<bool> pica_debugging{false, Keys::pica_debugging};
|
||||||
Setting<bool> dump_command_buffers{false, Keys::dump_command_buffers};
|
Setting<bool> dump_command_buffers{false, Keys::dump_command_buffers};
|
||||||
SwitchableSetting<bool> spirv_shader_gen{true, Keys::spirv_shader_gen};
|
SwitchableSetting<bool> spirv_shader_gen{true, Keys::spirv_shader_gen};
|
||||||
SwitchableSetting<bool> disable_spirv_optimizer{true, Keys::disable_spirv_optimizer};
|
SwitchableSetting<bool> disable_spirv_optimizer{true, Keys::disable_spirv_optimizer};
|
||||||
|
|||||||
@ -371,10 +371,6 @@ const Pica::PicaCore& GPU::PicaCore() const {
|
|||||||
return impl->pica;
|
return impl->pica;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pica::DebugContext& GPU::DebugContext() {
|
|
||||||
return *Pica::g_debug_context;
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsDebugger& GPU::Debugger() {
|
GraphicsDebugger& GPU::Debugger() {
|
||||||
return impl->gpu_debugger;
|
return impl->gpu_debugger;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,9 +84,6 @@ public:
|
|||||||
/// Returns an immutable reference to the PICA GPU.
|
/// Returns an immutable reference to the PICA GPU.
|
||||||
[[nodiscard]] const Pica::PicaCore& PicaCore() const;
|
[[nodiscard]] const Pica::PicaCore& PicaCore() const;
|
||||||
|
|
||||||
/// Returns a mutable reference to the pica debugging context.
|
|
||||||
[[nodiscard]] Pica::DebugContext& DebugContext();
|
|
||||||
|
|
||||||
/// Returns a mutable reference to the GSP command debugger.
|
/// Returns a mutable reference to the GSP command debugger.
|
||||||
[[nodiscard]] GraphicsDebugger& Debugger();
|
[[nodiscard]] GraphicsDebugger& Debugger();
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user