X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fwin32-nat.c;h=641f0949f818f0ba321c64e63014981b0d5d6f60;hb=6c890568ff69b3a6966b4619515cf27e2177bd74;hp=e295a663c9e5b01d94cab684864a13423c10cc0f;hpb=1750a5ef21ab77772e1aa396d9483afb8177fc8c;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/win32-nat.c b/gdb/win32-nat.c index e295a663c9..641f0949f8 100644 --- a/gdb/win32-nat.c +++ b/gdb/win32-nat.c @@ -1,8 +1,7 @@ /* Target-vector operations for controlling win32 child processes, for GDB. - Copyright 1995 - Free Software Foundation, Inc. - + Copyright 1995, 1996 Free Software Foundation, Inc. Contributed by Cygnus Support. + This file is part of GDB. This program is free software; you can redistribute it and/or modify @@ -21,6 +20,8 @@ /* by Steve Chamberlain, sac@cygnus.com */ +/* We assume we're being built with and will be used for cygwin32. */ + #include "defs.h" #include "frame.h" /* required by inferior.h */ #include "inferior.h" @@ -33,13 +34,19 @@ #include #include #include "buildsym.h" +#include "symfile.h" +#include "objfiles.h" #include "gdb_string.h" -#include "thread.h" +#include "gdbthread.h" #include "gdbcmd.h" #include -#define CHECK(x) check (x, __FILE__,__LINE__) -#define DEBUG(x) if (remote_debug) printf x +#include +#define CHECK(x) check (x, __FILE__,__LINE__) +#define DEBUG_EXEC(x) if (debug_exec) printf x +#define DEBUG_EVENTS(x) if (debug_events) printf x +#define DEBUG_MEM(x) if (debug_memory) printf x +#define DEBUG_EXCEPT(x) if (debug_exceptions) printf x /* Forward declaration */ extern struct target_ops child_ops; @@ -63,6 +70,10 @@ static int event_count = 0; /* User options. */ static int new_console = 0; static int new_group = 0; +static int debug_exec = 0; /* show execution */ +static int debug_events = 0; /* show events from kernel */ +static int debug_memory = 0; /* show target memory accesses */ +static int debug_exceptions = 0; /* show target exceptions */ /* This vector maps GDB's idea of a register's number into an address in the win32 exception context vector. @@ -83,9 +94,92 @@ struct regmappings int mask; }; -static const struct regmappings - mappings[] = + +static const struct regmappings mappings[] = { +#ifdef __PPC__ + {(char *) &context.Gpr0, CONTEXT_INTEGER}, + {(char *) &context.Gpr1, CONTEXT_INTEGER}, + {(char *) &context.Gpr2, CONTEXT_INTEGER}, + {(char *) &context.Gpr3, CONTEXT_INTEGER}, + {(char *) &context.Gpr4, CONTEXT_INTEGER}, + {(char *) &context.Gpr5, CONTEXT_INTEGER}, + {(char *) &context.Gpr6, CONTEXT_INTEGER}, + {(char *) &context.Gpr7, CONTEXT_INTEGER}, + + {(char *) &context.Gpr8, CONTEXT_INTEGER}, + {(char *) &context.Gpr9, CONTEXT_INTEGER}, + {(char *) &context.Gpr10, CONTEXT_INTEGER}, + {(char *) &context.Gpr11, CONTEXT_INTEGER}, + {(char *) &context.Gpr12, CONTEXT_INTEGER}, + {(char *) &context.Gpr13, CONTEXT_INTEGER}, + {(char *) &context.Gpr14, CONTEXT_INTEGER}, + {(char *) &context.Gpr15, CONTEXT_INTEGER}, + + {(char *) &context.Gpr16, CONTEXT_INTEGER}, + {(char *) &context.Gpr17, CONTEXT_INTEGER}, + {(char *) &context.Gpr18, CONTEXT_INTEGER}, + {(char *) &context.Gpr19, CONTEXT_INTEGER}, + {(char *) &context.Gpr20, CONTEXT_INTEGER}, + {(char *) &context.Gpr21, CONTEXT_INTEGER}, + {(char *) &context.Gpr22, CONTEXT_INTEGER}, + {(char *) &context.Gpr23, CONTEXT_INTEGER}, + + {(char *) &context.Gpr24, CONTEXT_INTEGER}, + {(char *) &context.Gpr25, CONTEXT_INTEGER}, + {(char *) &context.Gpr26, CONTEXT_INTEGER}, + {(char *) &context.Gpr27, CONTEXT_INTEGER}, + {(char *) &context.Gpr28, CONTEXT_INTEGER}, + {(char *) &context.Gpr29, CONTEXT_INTEGER}, + {(char *) &context.Gpr30, CONTEXT_INTEGER}, + {(char *) &context.Gpr31, CONTEXT_INTEGER}, + + {(char *) &context.Fpr0, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr1, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr2, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr3, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr4, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr5, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr6, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr7, CONTEXT_FLOATING_POINT}, + + {(char *) &context.Fpr8, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr9, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr10, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr11, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr12, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr13, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr14, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr15, CONTEXT_FLOATING_POINT}, + + {(char *) &context.Fpr16, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr17, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr18, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr19, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr20, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr21, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr22, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr23, CONTEXT_FLOATING_POINT}, + + {(char *) &context.Fpr24, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr25, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr26, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr27, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr28, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr29, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr30, CONTEXT_FLOATING_POINT}, + {(char *) &context.Fpr31, CONTEXT_FLOATING_POINT}, + + + {(char *) &context.Iar, CONTEXT_CONTROL}, + {(char *) &context.Msr, CONTEXT_CONTROL}, + {(char *) &context.Cr, CONTEXT_INTEGER}, + {(char *) &context.Lr, CONTEXT_CONTROL}, + {(char *) &context.Ctr, CONTEXT_CONTROL}, + + {(char *) &context.Xer, CONTEXT_INTEGER}, + {0,0}, /* MQ, but there isn't one */ +#else {(char *) &context.Eax, CONTEXT_INTEGER}, {(char *) &context.Ecx, CONTEXT_INTEGER}, {(char *) &context.Edx, CONTEXT_INTEGER}, @@ -110,6 +204,7 @@ static const struct regmappings {&context.FloatSave.RegisterArea[5 * 10], CONTEXT_FLOATING_POINT}, {&context.FloatSave.RegisterArea[6 * 10], CONTEXT_FLOATING_POINT}, {&context.FloatSave.RegisterArea[7 * 10], CONTEXT_FLOATING_POINT}, +#endif }; @@ -191,7 +286,9 @@ handle_load_dll (char *eventp) if (done == sizeof (dll_name_ptr) && dll_name_ptr) { - char *dll_name; + char *dll_name, *dll_basename; + struct objfile *objfile; + char unix_dll_name[MAX_PATH]; int size = event->u.LoadDll.fUnicode ? sizeof (WCHAR) : sizeof (char); int len = 0; char b[2]; @@ -206,7 +303,6 @@ handle_load_dll (char *eventp) } while ((b[0] != 0 || b[size - 1] != 0) && done == size); - dll_name = alloca (len); if (event->u.LoadDll.fUnicode) @@ -231,10 +327,32 @@ handle_load_dll (char *eventp) &done); } + + dos_path_to_unix_path (dll_name, unix_dll_name); + /* FIXME!! It would be nice to define one symbol which pointed to the front of the dll if we can't find any symbols. */ - context.ContextFlags = CONTEXT_FULL; + if (!(dll_basename = strrchr(dll_name, '\\'))) + dll_basename = strrchr(dll_name, '/'); + + ALL_OBJFILES(objfile) + { + char *objfile_basename; + if (!(objfile_basename = strrchr(objfile->name, '\\'))) + objfile_basename = strrchr(objfile->name, '/'); + + if (dll_basename && objfile_basename && + strcmp(dll_basename+1, objfile_basename+1) == 0) + { + printf_unfiltered ("%s (symbols previously loaded)\n", + dll_basename + 1); + return 1; + } + } + + + context.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT; GetThreadContext (current_thread, &context); /* The symbols in a dll are offset by 0x1000, which is the @@ -244,14 +362,11 @@ handle_load_dll (char *eventp) FIXME: Is this the real reason that we need the 0x1000 ? */ - symbol_file_add (dll_name, 0, + symbol_file_add (unix_dll_name, 0, (int) event->u.LoadDll.lpBaseOfDll + 0x1000, 0, 0, 0); - /* We strip off the path of the dll for tidiness. */ - if (strrchr (dll_name, '\\')) - dll_name = strrchr (dll_name, '\\') + 1; - - printf_unfiltered ("%x:%s\n", event->u.LoadDll.lpBaseOfDll, dll_name); + printf_unfiltered ("%x:%s\n", event->u.LoadDll.lpBaseOfDll, + unix_dll_name); } return 1; } @@ -264,23 +379,42 @@ handle_exception (DEBUG_EVENT * event, struct target_waitstatus *ourstatus) int done = 0; ourstatus->kind = TARGET_WAITKIND_STOPPED; - for (i = 0; !done && xlate[i].us > 0; i++) - { - if (xlate[i].them == event->u.Exception.ExceptionRecord.ExceptionCode) - { - ourstatus->value.sig = xlate[i].us; - done = 1; - break; - } - } - if (!done) + switch (event->u.Exception.ExceptionRecord.ExceptionCode) { - printf_unfiltered ("Want to know about exception code %08x\n", - event->u.Exception.ExceptionRecord.ExceptionCode); + case EXCEPTION_ACCESS_VIOLATION: + DEBUG_EXCEPT (("gdb: Target exception ACCESS_VIOLATION at 0x%08x\n", + event->u.Exception.ExceptionRecord.ExceptionAddress)); + ourstatus->value.sig = TARGET_SIGNAL_SEGV; + break; + case STATUS_STACK_OVERFLOW: + DEBUG_EXCEPT (("gdb: Target exception STACK_OVERFLOW at 0x%08x\n", + event->u.Exception.ExceptionRecord.ExceptionAddress)); + ourstatus->value.sig = TARGET_SIGNAL_SEGV; + break; + case EXCEPTION_BREAKPOINT: + DEBUG_EXCEPT (("gdb: Target exception BREAKPOINT at 0x%08x\n", + event->u.Exception.ExceptionRecord.ExceptionAddress)); + ourstatus->value.sig = TARGET_SIGNAL_TRAP; + break; + case DBG_CONTROL_C: + DEBUG_EXCEPT (("gdb: Target exception CONTROL_C at 0x%08x\n", + event->u.Exception.ExceptionRecord.ExceptionAddress)); + ourstatus->value.sig = TARGET_SIGNAL_INT; + break; + case EXCEPTION_SINGLE_STEP: + DEBUG_EXCEPT (("gdb: Target exception SINGLE_STEP at 0x%08x\n", + event->u.Exception.ExceptionRecord.ExceptionAddress)); + ourstatus->value.sig = TARGET_SIGNAL_TRAP; + break; + default: + printf_unfiltered ("gdb: unknown target exception 0x%08x at 0x%08x\n", + event->u.Exception.ExceptionRecord.ExceptionCode, + event->u.Exception.ExceptionRecord.ExceptionAddress); ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; + break; } - context.ContextFlags = CONTEXT_FULL; + context.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT; GetThreadContext (current_thread, &context); exception_count++; } @@ -298,12 +432,7 @@ child_wait (int pid, struct target_waitstatus *ourstatus) { DEBUG_EVENT event; BOOL t = WaitForDebugEvent (&event, INFINITE); - - DEBUG (("%d = WaitForDebugEvent() code=%d pid=%d tid=%d)\n", - t, - event.dwDebugEventCode, - event.dwProcessId, - event.dwThreadId)); + char *p; event_count++; @@ -313,11 +442,25 @@ child_wait (int pid, struct target_waitstatus *ourstatus) switch (event.dwDebugEventCode) { case CREATE_THREAD_DEBUG_EVENT: + DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", + event.dwProcessId, event.dwThreadId, + "CREATE_THREAD_DEBUG_EVENT")); + break; case EXIT_THREAD_DEBUG_EVENT: + DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", + event.dwProcessId, event.dwThreadId, + "EXIT_THREAD_DEBUG_EVENT")); + break; case CREATE_PROCESS_DEBUG_EVENT: + DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", + event.dwProcessId, event.dwThreadId, + "CREATE_PROCESS_DEBUG_EVENT")); break; case EXIT_PROCESS_DEBUG_EVENT: + DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", + event.dwProcessId, event.dwThreadId, + "EXIT_PROCESS_DEBUG_EVENT")); ourstatus->kind = TARGET_WAITKIND_EXITED; ourstatus->value.integer = event.u.ExitProcess.dwExitCode; CloseHandle (current_process); @@ -326,22 +469,48 @@ child_wait (int pid, struct target_waitstatus *ourstatus) break; case LOAD_DLL_DEBUG_EVENT: - catch_errors (handle_load_dll, - (char*) &event, - "\n[failed reading symbols from DLL]\n", - RETURN_MASK_ALL); - registers_changed(); /* mark all regs invalid */ + DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", + event.dwProcessId, event.dwThreadId, + "LOAD_DLL_DEBUG_EVENT")); + catch_errors (handle_load_dll, + (char*) &event, + "\n[failed reading symbols from DLL]\n", + RETURN_MASK_ALL); + registers_changed(); /* mark all regs invalid */ break; - case EXCEPTION_DEBUG_EVENT: + case UNLOAD_DLL_DEBUG_EVENT: + DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", + event.dwProcessId, event.dwThreadId, + "UNLOAD_DLL_DEBUG_EVENT")); + break; /* FIXME: don't know what to do here */ + case EXCEPTION_DEBUG_EVENT: + DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", + event.dwProcessId, event.dwThreadId, + "EXCEPTION_DEBUG_EVENT")); handle_exception (&event, ourstatus); return current_process_id; + + case OUTPUT_DEBUG_STRING_EVENT: /* message from the kernel */ + DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", + event.dwProcessId, event.dwThreadId, + "OUTPUT_DEBUG_STRING_EVENT")); + if (target_read_string + ((CORE_ADDR) event.u.DebugString.lpDebugStringData, + &p, 1024, 0) && p && *p) + { + warning(p); + free(p); + } + break; default: - printf_unfiltered ("waitfor it %d %d %d %d\n", t, - event.dwDebugEventCode, - event.dwProcessId, - event.dwThreadId); + printf_unfiltered ("gdb: kernel event for pid=%d tid=%d\n", + event.dwProcessId, event.dwThreadId); + printf_unfiltered (" unknown event code %d\n", + event.dwDebugEventCode); break; } + DEBUG_EVENTS (("ContinueDebugEvent (cpid=%d, ctid=%d, DBG_CONTINUE);\n", + current_process_id, current_thread_id)); CHECK (ContinueDebugEvent (current_process_id, current_thread_id, DBG_CONTINUE)); @@ -349,8 +518,6 @@ child_wait (int pid, struct target_waitstatus *ourstatus) } - - /* Attach to process PID, then initialize for debugging it. */ static void @@ -431,13 +598,11 @@ child_open (arg, from_tty) error ("Use the \"run\" command to start a Unix child process."); } - /* Start an inferior win32 child process and sets inferior_pid to its pid. EXEC_FILE is the file to run. ALLARGS is a string containing the arguments to the program. ENV is the environment vector to pass. Errors reported with error(). */ - static void child_create_inferior (exec_file, allargs, env) char *exec_file; @@ -467,7 +632,7 @@ child_create_inferior (exec_file, allargs, env) unix_path_to_dos_path (exec_file, real_path); - flags = DEBUG_ONLY_THIS_PROCESS | DEBUG_PROCESS; + flags = DEBUG_ONLY_THIS_PROCESS; if (new_group) flags |= CREATE_NEW_PROCESS_GROUP; @@ -475,32 +640,96 @@ child_create_inferior (exec_file, allargs, env) if (new_console) flags |= CREATE_NEW_CONSOLE; - args = alloca (strlen (exec_file) + strlen (allargs) + 2); + args = alloca (strlen (real_path) + strlen (allargs) + 2); + + strcpy (args, real_path); - strcpy (args, exec_file); strcat (args, " "); strcat (args, allargs); - - /* get total size for env strings */ - for (envlen = 0, i = 0; env[i] && *env[i]; i++) - envlen += strlen(env[i]) + 1; - - winenv = alloca(envlen + 1); /* allocate new buffer */ - - /* copy env strings into new buffer */ - for (temp = winenv, i = 0; env[i] && *env[i]; i++) - { - strcpy(temp, env[i]); - temp += strlen(temp) + 1; - } - *temp = 0; /* final nil string to terminate new env */ - - strcat (real_path, " "); - strcat (real_path, args); + /* Prepare the environment vars for CreateProcess. */ + { + /* This code use to assume all env vars were file names and would + translate them all to win32 style. That obviously doesn't work in the + general case. The current rule is that the user either works solely + with win32 style path names or with posix style path names and that + all env vars are already set up appropriately. At any rate it is + wrong for us to willy-nilly change them. + + However, we need to handle PATH because we're about to call + CreateProcess and it uses PATH to find DLL's. Fortunately PATH + has a well-defined value in both posix and win32 environments. + cygwin.dll will change it back to posix style if necessary. If we're + working with win32 style path names, we don't need to do anything at + all. */ + + static const char *conv_path_names[] = + { + "PATH=", + 0 + }; + int posix_rules_p = sysconf (_SC_PATH_RULES) == _PATH_RULES_POSIX; + + /* CreateProcess takes the environment list as a null terminated set of + strings (i.e. two nulls terminate the list). */ + + /* Get total size for env strings. */ + for (envlen = 0, i = 0; env[i] && *env[i]; i++) + { + if (posix_rules_p) + { + int j, len; + + for (j = 0; conv_path_names[j]; j++) + { + len = strlen (conv_path_names[j]); + if (strncmp (conv_path_names[j], env[i], len) == 0) + { + envlen += len + + cygwin32_posix_to_win32_path_list_buf_size (env[i] + len); + break; + } + } + if (conv_path_names[j] == NULL) + envlen += strlen (env[i]) + 1; + } + else + envlen += strlen (env[i]) + 1; + } + + winenv = alloca (envlen + 1); + + /* Copy env strings into new buffer. */ + for (temp = winenv, i = 0; env[i] && *env[i]; i++) + { + if (posix_rules_p) + { + int j, len; + + for (j = 0; conv_path_names[j]; j++) + { + len = strlen (conv_path_names[j]); + if (strncmp (conv_path_names[j], env[i], len) == 0) + { + memcpy (temp, env[i], len); + cygwin32_posix_to_win32_path_list (env[i] + len, temp + len); + break; + } + } + if (conv_path_names[j] == NULL) + strcpy (temp, env[i]); + } + else + strcpy (temp, env[i]); + temp += strlen (temp) + 1; + } + + /* Final nil string to terminate new env. */ + *temp = 0; + } ret = CreateProcess (0, - real_path, + args, /* command line */ NULL, /* Security */ NULL, /* thread */ TRUE, /* inherit handles */ @@ -547,7 +776,9 @@ child_mourn_inferior () void child_stop () { + DEBUG_EVENTS (("gdb: GenerateConsoleCtrlEvent (CTRLC_EVENT, 0)\n")); CHECK (GenerateConsoleCtrlEvent (CTRL_C_EVENT, 0)); + registers_changed(); /* refresh register state */ } int @@ -557,11 +788,15 @@ child_xfer_memory (CORE_ADDR memaddr, char *our, int len, DWORD done; if (write) { + DEBUG_MEM (("gdb: write target memory, %d bytes at 0x%08x\n", + len, memaddr)); WriteProcessMemory (current_process, memaddr, our, len, &done); FlushInstructionCache (current_process, memaddr, len); } else { + DEBUG_MEM (("gdb: read target memory, %d bytes at 0x%08x\n", + len, memaddr)); ReadProcessMemory (current_process, memaddr, our, len, &done); } return done; @@ -573,18 +808,25 @@ child_kill_inferior (void) CHECK (TerminateProcess (current_process, 0)); CHECK (CloseHandle (current_process)); CHECK (CloseHandle (current_thread)); + target_mourn_inferior(); /* or just child_mourn_inferior? */ } void child_resume (int pid, int step, enum target_signal signal) { - DEBUG (("child_resume (%d, %d, %d);\n", pid, step, signal)); + DEBUG_EXEC (("gdb: child_resume (pid=%d, step=%d, signal=%d);\n", + pid, step, signal)); if (step) { +#ifdef __PPC__ + warning ("Single stepping not done.\n"); +#endif +#ifdef i386 /* Single step by setting t bit */ child_fetch_inferior_registers (PS_REGNUM); context.EFlags |= FLAG_TRACE_BIT; +#endif } if (context.ContextFlags) @@ -598,6 +840,8 @@ child_resume (int pid, int step, enum target_signal signal) fprintf_unfiltered (gdb_stderr, "Can't send signals to the child.\n"); } + DEBUG_EVENTS (("gdb: ContinueDebugEvent (cpid=%d, ctid=%d, DBG_CONTINUE);\n", + current_process_id, current_thread_id)); CHECK (ContinueDebugEvent (current_process_id, current_thread_id, DBG_CONTINUE)); @@ -618,8 +862,9 @@ child_can_run () static void child_close () { - + DEBUG_EVENTS (("gdb: child_close, inferior_pid=%d\n", inferior_pid)); } + struct target_ops child_ops = { "child", /* to_shortname */ @@ -667,6 +912,8 @@ struct target_ops child_ops = void _initialize_inftarg () { + struct cmd_list_element *c; + add_show_from_set (add_set_cmd ("new-console", class_support, var_boolean, (char *) &new_console, @@ -681,5 +928,33 @@ _initialize_inftarg () &setlist), &showlist); + add_show_from_set + (add_set_cmd ("debugexec", class_support, var_boolean, + (char *) &debug_exec, + "Set whether to display execution in child process.", + &setlist), + &showlist); + + add_show_from_set + (add_set_cmd ("debugevents", class_support, var_boolean, + (char *) &debug_events, + "Set whether to display kernel events in child process.", + &setlist), + &showlist); + + add_show_from_set + (add_set_cmd ("debugmemory", class_support, var_boolean, + (char *) &debug_memory, + "Set whether to display memory accesses in child process.", + &setlist), + &showlist); + + add_show_from_set + (add_set_cmd ("debugexceptions", class_support, var_boolean, + (char *) &debug_exceptions, + "Set whether to display kernel exceptions in child process.", + &setlist), + &showlist); + add_target (&child_ops); }