[haiku-development] Re: Emergency keyboard shortcut handler

  • From: Jan Klötzke <jan.kloetzke@xxxxxxxxxx>
  • To: haiku-development@xxxxxxxxxxxxx
  • Date: Mon, 7 Jul 2008 20:45:23 +0200

"Axel Dörfler" <axeld@xxxxxxxxxxxxxxxx> wrote:
> Jan Klötzke <jan.kloetzke@xxxxxxxxxx> wrote:
> > "Axel Dörfler" <axeld@xxxxxxxxxxxxxxxx> wrote:
> > Good point. That's certainly a problem with the chosen approach.
>
> There is a light at the end of the tunnel, though. We do have a minimal
> keyboard interrupt handler in arch_debug_console.c - you could change
> int.c in such a way that it will install that handler again after the
> PS/2 interrupt is unused again.
> This way, it would work even without a running input_server (but then
> not for USB keyboards). I would think that's acceptable, though.

I looked into it but I don't have a good idea yet on how to use and integrate 
it with the emergency module. At least not at the moment. I'll leave it for 
the moment as it is...

> > While I do agree in general I made the halting optional only for the
> > "show
> > backtraces on all CPU's" feature. I thought it would lock up on SMP
> > systems
> > because the "sc" KDL command is broadcasted to all CPU's via ICI
> > (call_all_cpus()) and would then again try to lock all CPU's also via
> > ICI's.
> > So all CPU's try to lock each other. But quite frankly I'm not sure
> > anymore
> > that there is really a problem after digging in the code.
> > Unfortunately I
> > have no SMP system to test...
>
> I'd be glad to check it out for you :-)
> But if all CPUs are in the kernel debugger, anyway, maybe it isn't that
> bad after all. At least I wouldn't mind applying your patch now as is
> if you like.

Great. :-) I removed the optional locking and fixed a bug which caused a 
deadlock when a page fault happened while executing the KDL command. I 
attached the revised patches #1 and #2. Patches #3 and #4 are not affected.

/Jan
From 06230c8023c48853bc6df8b2d1356319e96877a3 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Jan=20Kl=C3=B6tzke?= <jan.kloetzke@xxxxxxxxxx>
Date: Sun, 22 Jun 2008 12:44:20 +0200
Subject: [PATCH 1/4] Allow non-interactive execution of KDL commands

* Add kernel_debugger_exec() which allows to execute a single command
* Route the output of the non-interactive command to syslog too
---
 headers/os/drivers/KernelExport.h             |    1 +
 src/system/kernel/debug/debug.cpp             |  138 ++++++++++++++++++-------
 src/system/kernel/debug/debug_commands.cpp    |    2 +-
 src/system/kernel/debug/debug_output_filter.h |    7 +-
 4 files changed, 110 insertions(+), 38 deletions(-)

diff --git a/headers/os/drivers/KernelExport.h 
b/headers/os/drivers/KernelExport.h
index 3a88928..8b76534 100644
--- a/headers/os/drivers/KernelExport.h
+++ b/headers/os/drivers/KernelExport.h
@@ -164,6 +164,7 @@ extern bool                 set_dprintf_enabled(bool 
new_state);
 extern void                    panic(const char *format, ...) _PRINTFLIKE(1, 
2);
 
 extern void                    kernel_debugger(const char *message);
+extern void                    kernel_debugger_exec(const char *command);
 extern uint64          parse_expression(const char *string);
 
 extern int                     add_debugger_command(char *name, 
debugger_command_hook hook, char *help);
diff --git a/src/system/kernel/debug/debug.cpp 
b/src/system/kernel/debug/debug.cpp
index 08ee466..82e25b7 100644
--- a/src/system/kernel/debug/debug.cpp
+++ b/src/system/kernel/debug/debug.cpp
@@ -70,7 +70,10 @@ static const char* sCurrentKernelDebuggerMessage;
 static char sOutputBuffer[OUTPUT_BUFFER_SIZE];
 static char sLastOutputBuffer[OUTPUT_BUFFER_SIZE];
 static DebugOutputFilter* sDebugOutputFilter = NULL;
+DebugOutputFilter *gCurrentDebugOutputFilter = NULL;
 DefaultDebugOutputFilter gDefaultDebugOutputFilter;
+ExecDebugOutputFilter gExecDebugOutputFilter;
+static vint32 sInDebugger = 0;
 
 static void flush_pending_repeats(void);
 static void check_pending_repeats(void *data, int iter);
@@ -92,6 +95,7 @@ static int32 sCurrentLine = 0;
 
 #define distance(a, b) ((a) < (b) ? (b) - (a) : (a) - (b))
 
+static void syslog_write(const char *text, int32 length);
 
 // #pragma mark - DebugOutputFilter
 
@@ -142,6 +146,18 @@ DefaultDebugOutputFilter::Print(const char* format, 
va_list args)
 }
 
 
+void
+ExecDebugOutputFilter::PrintString(const char* string)
+{
+       if (sSerialDebugEnabled)
+               arch_debug_serial_puts(string);
+       if (sBlueScreenEnabled || sDebugScreenEnabled)
+               blue_screen_puts(string);
+       if (sSyslogOutputEnabled)
+               syslog_write(string, strlen(string));
+}
+
+
 // #pragma mark -
 
 
@@ -178,7 +194,8 @@ kputs(const char *s)
 void
 kputs_unfiltered(const char *s)
 {
-       gDefaultDebugOutputFilter.PrintString(s);
+       if (gCurrentDebugOutputFilter)
+               gCurrentDebugOutputFilter->PrintString(s);
 }
 
 
@@ -646,9 +663,6 @@ kgets(char *buffer, int length)
 static void
 kernel_debugger_loop(void)
 {
-       int32 previousCPU = sDebuggerOnCPU;
-       sDebuggerOnCPU = smp_get_current_cpu();
-
        // set a few temporary debug variables
        if (struct thread* thread = thread_get_current_thread()) {
                set_debug_variable("_thread", (uint64)(addr_t)thread);
@@ -700,8 +714,6 @@ kernel_debugger_loop(void)
                if (++sCurrentLine >= HISTORY_SIZE)
                        sCurrentLine = 0;
        }
-
-       sDebuggerOnCPU = previousCPU;
 }
 
 
@@ -907,6 +919,50 @@ call_modules_hook(bool enter)
 }
 
 
+static cpu_status
+debugger_halt_cpus(void)
+{
+       cpu_status state;
+
+       state = disable_interrupts();
+       while (atomic_add(&sInDebugger, 1) > 0) {
+               // The debugger is already running, find out where...
+               if (sDebuggerOnCPU != smp_get_current_cpu()) {
+                       // Some other CPU must have entered the debugger and 
tried to halt
+                       // us. Process ICIs to ensure we get the halt request. 
Then we are
+                       // blocking there until everyone leaves the debugger 
and we can
+                       // try to enter it again.
+                       atomic_add(&sInDebugger, -1);
+                       smp_intercpu_int_handler();
+               } else {
+                       // We are re-entering the debugger on the same CPU.
+                       break;
+               }
+       }
+
+       if (sDebuggerOnCPU != smp_get_current_cpu() && smp_get_num_cpus() > 1) {
+               // First entry on a MP system, send a halt request to all of 
the other
+               // CPUs. Should they try to enter the debugger they will be 
cought in
+               // the loop above.
+               smp_send_broadcast_ici(SMP_MSG_CPU_HALT, 0, 0, 0,
+                       (void *)&sInDebugger, SMP_MSG_FLAG_SYNC);
+       }
+
+       sDebuggerOnCPU = smp_get_current_cpu();
+
+       return state;
+}
+
+
+static void
+debugger_resume_cpus(cpu_status state)
+{
+       if (atomic_add(&sInDebugger, -1) == 1)
+               sDebuggerOnCPU = -1;
+       restore_interrupts(state);
+}
+
+
 //     #pragma mark - private kernel API
 
 
@@ -927,7 +983,7 @@ debug_stop_screen_debug_output(void)
 bool
 debug_debugger_running(void)
 {
-       return sDebuggerOnCPU != -1;
+       return sInDebugger > 0;
 }
 
 
@@ -980,6 +1036,7 @@ status_t
 debug_init(kernel_args *args)
 {
        new(&gDefaultDebugOutputFilter) DefaultDebugOutputFilter;
+       new(&gExecDebugOutputFilter) ExecDebugOutputFilter;
 
        debug_paranoia_init();
        return arch_debug_console_init(args);
@@ -1095,43 +1152,21 @@ panic(const char *format, ...)
 void
 kernel_debugger(const char *message)
 {
-       static vint32 inDebugger = 0;
        cpu_status state;
        bool dprintfState;
 
-       state = disable_interrupts();
-       while (atomic_add(&inDebugger, 1) > 0) {
-               // The debugger is already running, find out where...
-               if (sDebuggerOnCPU != smp_get_current_cpu()) {
-                       // Some other CPU must have entered the debugger and 
tried to halt
-                       // us. Process ICIs to ensure we get the halt request. 
Then we are
-                       // blocking there until everyone leaves the debugger 
and we can
-                       // try to enter it again.
-                       atomic_add(&inDebugger, -1);
-                       smp_intercpu_int_handler();
-               } else {
-                       // We are re-entering the debugger on the same CPU.
-                       break;
-               }
-       }
+       state = debugger_halt_cpus();
 
        arch_debug_save_registers(&dbg_register_file[smp_get_current_cpu()][0]);
        dprintfState = set_dprintf_enabled(true);
 
-       if (sDebuggerOnCPU != smp_get_current_cpu() && smp_get_num_cpus() > 1) {
-               // First entry on a MP system, send a halt request to all of 
the other
-               // CPUs. Should they try to enter the debugger they will be 
cought in
-               // the loop above.
-               smp_send_broadcast_ici(SMP_MSG_CPU_HALT, 0, 0, 0,
-                       (void *)&inDebugger, SMP_MSG_FLAG_SYNC);
-       }
-
        if (sBlueScreenOutput) {
                if (blue_screen_enter(false) == B_OK)
                        sBlueScreenEnabled = true;
        }
 
        sDebugOutputFilter = &gDefaultDebugOutputFilter;
+       gCurrentDebugOutputFilter = sDebugOutputFilter;
 
        if (message)
                kprintf("PANIC: %s\n", message);
@@ -1149,15 +1184,43 @@ kernel_debugger(const char *message)
        set_dprintf_enabled(dprintfState);
 
        sDebugOutputFilter = NULL;
+       gCurrentDebugOutputFilter = NULL;
 
        sBlueScreenEnabled = false;
-       atomic_add(&inDebugger, -1);
-       restore_interrupts(state);
+
+       debugger_resume_cpus(state);
 
        // ToDo: in case we change dbg_register_file - don't we want to restore 
it?
 }
 
 
+void
+kernel_debugger_exec(const char *command)
+{
+       cpu_status state;
+       bool dprintfState;
+
+       state = debugger_halt_cpus();
+
+       dprintfState = set_dprintf_enabled(true);
+       arch_debug_save_registers(&dbg_register_file[smp_get_current_cpu()][0]);
+       sDebugOutputFilter = &gExecDebugOutputFilter;
+       gCurrentDebugOutputFilter = sDebugOutputFilter;
+       sCurrentKernelDebuggerMessage = NULL;
+
+       // sort the commands
+       sort_debugger_commands();
+
+       evaluate_debug_command(command);
+
+       set_dprintf_enabled(dprintfState);
+       sDebugOutputFilter = NULL;
+       gCurrentDebugOutputFilter = NULL;
+
+       debugger_resume_cpus(state);
+}
+
+
 bool
 set_dprintf_enabled(bool newState)
 {
@@ -1321,9 +1384,12 @@ void
 kprintf_unfiltered(const char *format, ...)
 {
        va_list args;
-       va_start(args, format);
-       gDefaultDebugOutputFilter.Print(format, args);
-       va_end(args);
+
+       if (gCurrentDebugOutputFilter) {
+               va_start(args, format);
+               gCurrentDebugOutputFilter->Print(format, args);
+               va_end(args);
+       }
 }
 
 
diff --git a/src/system/kernel/debug/debug_commands.cpp 
b/src/system/kernel/debug/debug_commands.cpp
index ea42af7..111a5a5 100644
--- a/src/system/kernel/debug/debug_commands.cpp
+++ b/src/system/kernel/debug/debug_commands.cpp
@@ -155,7 +155,7 @@ invoke_pipe_segment(debugger_command_pipe* pipe, int32 
index, char* argument)
        // set debug output
        DebugOutputFilter* oldFilter = set_debug_output_filter(
                index == pipe->segment_count - 1
-                       ? &gDefaultDebugOutputFilter
+                       ? gCurrentDebugOutputFilter
                        : (DebugOutputFilter*)&sPipeOutputFilters[index]);
 
        // set last command argument
diff --git a/src/system/kernel/debug/debug_output_filter.h 
b/src/system/kernel/debug/debug_output_filter.h
index 7a7ede2..e0dbc39 100644
--- a/src/system/kernel/debug/debug_output_filter.h
+++ b/src/system/kernel/debug/debug_output_filter.h
@@ -25,8 +25,13 @@ public:
        virtual void Print(const char* format, va_list args);
 };
 
+class ExecDebugOutputFilter : public DefaultDebugOutputFilter {
+public:
+       virtual void PrintString(const char* string);
+};
+
 
-extern DefaultDebugOutputFilter gDefaultDebugOutputFilter;
+extern DebugOutputFilter *gCurrentDebugOutputFilter;
 
 
 DebugOutputFilter* set_debug_output_filter(DebugOutputFilter* filter);
-- 
1.5.4.2

From 27f2f83f2ad3ed84fd737e8bb6b8a32ae64b0c20 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Jan=20Kl=C3=B6tzke?= <jan.kloetzke@xxxxxxxxxx>
Date: Wed, 18 Jun 2008 22:33:01 +0200
Subject: [PATCH 2/4] Emergency functions module

Simple module which provides a "emergency_key" method. This method should be
called with the last pressed character of a combination by keyboard drivers,
such as "Shift+F12+s". Depending on the passed character the following
functions are available
  * s = Sync disks
  * b = reBoot
  * o = Off (shutdown)
  * t = dump Threads
  * l = dump Locks
  * f = print stack Frames from all CPU's
---
 build/jam/HaikuImage                             |    2 +-
 headers/os/drivers/emergency.h                   |   28 +++++++
 src/add-ons/kernel/generic/Jamfile               |    1 +
 src/add-ons/kernel/generic/emergency/Jamfile     |    6 ++
 src/add-ons/kernel/generic/emergency/emergency.c |   93 ++++++++++++++++++++++
 5 files changed, 129 insertions(+), 1 deletions(-)
 create mode 100644 headers/os/drivers/emergency.h
 create mode 100644 src/add-ons/kernel/generic/emergency/Jamfile
 create mode 100644 src/add-ons/kernel/generic/emergency/emergency.c

diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage
index e59f113..aea4299 100644
--- a/build/jam/HaikuImage
+++ b/build/jam/HaikuImage
@@ -156,7 +156,7 @@ AddFilesToHaikuImage beos system add-ons kernel debugger
 AddFilesToHaikuImage beos system add-ons kernel file_systems
        : $(BEOS_ADD_ONS_FILE_SYSTEMS) ;
 AddFilesToHaikuImage beos system add-ons kernel generic
-       : block_io dpc ide_adapter locked_pool mpu401 scsi_periph ;
+       : block_io dpc emergency ide_adapter locked_pool mpu401 scsi_periph ;
 AddFilesToHaikuImage beos system add-ons kernel partitioning_systems
        : intel session ;
 AddFilesToHaikuImage beos system add-ons kernel interrupt_controllers
diff --git a/headers/os/drivers/emergency.h b/headers/os/drivers/emergency.h
new file mode 100644
index 0000000..0c42f49
--- /dev/null
+++ b/headers/os/drivers/emergency.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2008, Haiku Inc. All Rights Reserved.
+ * Distributed under the terms of the MIT License.
+ */
+#ifndef _EMERGENCY_H_
+#define _EMERGENCY_H_
+
+#include <OS.h>
+#include <module.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define B_EMERGENCY_MODULE_NAME "generic/emergency/v1"
+
+typedef struct emergency_module_info {
+       module_info     info;
+
+       void (*emergency_key)(char key);
+} emergency_module_info;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/add-ons/kernel/generic/Jamfile 
b/src/add-ons/kernel/generic/Jamfile
index 617d177..6200f0b 100644
--- a/src/add-ons/kernel/generic/Jamfile
+++ b/src/add-ons/kernel/generic/Jamfile
@@ -3,6 +3,7 @@ SubDir HAIKU_TOP src add-ons kernel generic ;
 SubInclude HAIKU_TOP src add-ons kernel generic atomizer ;
 SubInclude HAIKU_TOP src add-ons kernel generic block_io ;
 SubInclude HAIKU_TOP src add-ons kernel generic dpc ;
+SubInclude HAIKU_TOP src add-ons kernel generic emergency ;
 SubInclude HAIKU_TOP src add-ons kernel generic ide_adapter ;
 SubInclude HAIKU_TOP src add-ons kernel generic locked_pool ;
 SubInclude HAIKU_TOP src add-ons kernel generic mpu401 ;
diff --git a/src/add-ons/kernel/generic/emergency/Jamfile 
b/src/add-ons/kernel/generic/emergency/Jamfile
new file mode 100644
index 0000000..abf115c
--- /dev/null
+++ b/src/add-ons/kernel/generic/emergency/Jamfile
@@ -0,0 +1,6 @@
+SubDir HAIKU_TOP src add-ons kernel generic emergency ;
+
+KernelAddon emergency :
+       emergency.c
+;
+
diff --git a/src/add-ons/kernel/generic/emergency/emergency.c 
b/src/add-ons/kernel/generic/emergency/emergency.c
new file mode 100644
index 0000000..81a8bb4
--- /dev/null
+++ b/src/add-ons/kernel/generic/emergency/emergency.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2008, Jan Kloetzke. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ */
+
+#include <KernelExport.h>
+#include <dpc.h>
+#include <stdlib.h>
+
+#include <emergency.h>
+
+static void *sQueue;
+static dpc_module_info *sDpc;
+
+
+static void
+em_sync(void *arg)
+{
+       sync();
+       dprintf("Emergency sync completed\n");
+}
+
+
+static void
+em_stack_frames(void *cookie, int cpu)
+{
+       kernel_debugger_exec("sc");
+}
+
+
+static void
+emergency_key(char key)
+{
+       switch (key) {
+               case 's': /* Sync */
+                       sDpc->queue_dpc(sQueue, em_sync, NULL);
+                       break;
+               case 'b': /* reBoot */
+                       kernel_debugger_exec("reboot");
+                       break;
+               case 'o': /* Off */
+                       kernel_debugger_exec("shutdown");
+                       break;
+               case 't': /* Threads */
+                       kernel_debugger_exec("threads");
+                       break;
+               case 'l': /* Locks */
+                       kernel_debugger_exec("sems");
+                       break;
+               case 'f': /* stack Frames */
+                       call_all_cpus(em_stack_frames, NULL);
+                       break;
+       }
+}
+
+
+static status_t
+std_ops(int32 op, ...)
+{
+       switch (op) {
+               case B_MODULE_INIT:
+                       return sDpc->new_dpc_queue(&sQueue, "emergency", 
B_MAX_PRIORITY);
+               case B_MODULE_UNINIT:
+                       return sDpc->delete_dpc_queue(sQueue);
+
+               default:
+                       return B_ERROR;
+       }
+}
+
+
+static emergency_module_info sEmergencyModule = {
+       {
+               B_EMERGENCY_MODULE_NAME,
+               0,
+               std_ops
+       },
+
+       emergency_key
+};
+
+
+module_dependency module_dependencies[] = {
+       { B_DPC_MODULE_NAME, (module_info **)&sDpc },
+       {}
+};
+
+
+module_info *modules[] = {
+       (module_info *) &sEmergencyModule,
+       NULL
+};
+
-- 
1.5.4.2

Other related posts: