diff options
-rw-r--r-- | winsup/cygwin/environ.cc | 1 | ||||
-rw-r--r-- | winsup/cygwin/exceptions.cc | 104 | ||||
-rw-r--r-- | winsup/cygwin/local_includes/winsup.h | 1 | ||||
-rw-r--r-- | winsup/cygwin/release/3.5.0 | 4 | ||||
-rw-r--r-- | winsup/doc/cygwinenv.xml | 25 | ||||
-rw-r--r-- | winsup/doc/new-features.xml | 6 | ||||
-rw-r--r-- | winsup/doc/utils.xml | 43 |
7 files changed, 143 insertions, 41 deletions
diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc index 008854a07..dca5c5db0 100644 --- a/winsup/cygwin/environ.cc +++ b/winsup/cygwin/environ.cc @@ -832,6 +832,7 @@ environ_init (char **envp, int envc) out: findenv_func = (char * (*)(const char*, int*)) my_findenv; environ = envp; + dumper_init (); if (envp_passed_in) { p = getenv ("CYGWIN"); diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 642afb788..36f6a476a 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -52,6 +52,7 @@ details. */ #define DUMPSTACK_FRAME_LIMIT 32 PWCHAR debugger_command; +PWCHAR dumper_command; extern uint8_t _sigbe; extern uint8_t _sigdelayed_end; @@ -132,6 +133,42 @@ error_start_init (const char *buf) wcscat (cp, L"\""); } +extern "C" void +dumper_init (void) +{ + WCHAR dll_dir[PATH_MAX]; + if (!GetModuleFileNameW (cygwin_hmodule, dll_dir, PATH_MAX)) + return; + + /* Strip off last path component ("\\cygwin1.dll") */ + PWCHAR w = wcsrchr (dll_dir, L'\\'); + if (!w) + return; + + *w = L'\0'; + + /* Calculate the length of the command, allowing for an appended DWORD PID and + terminating null */ + int cmd_len = 1 + wcslen(dll_dir) + 11 + 2 + 1 + wcslen(global_progname) + 1 + 10 + 1; + if (cmd_len > 32767) + { + /* If this comes to more than the 32,767 characters CreateProcess() can + accept, we can't work, so don't set dumper_command */ + return; + } + + dumper_command = (PWCHAR) malloc(cmd_len * sizeof (WCHAR)); + + PWCHAR cp = dumper_command; + cp = wcpcpy (cp, L"\""); + cp = wcpcpy (cp, dll_dir); + cp = wcpcpy (cp, L"\\dumper.exe"); + cp = wcpcpy (cp, L"\" "); + cp = wcpcpy (cp, L"\""); + cp = wcpcpy (cp, global_progname); + wcscat (cp, L"\""); +} + void cygwin_exception::open_stackdumpfile () { @@ -454,20 +491,14 @@ cygwin_stackdump () exc.dumpstack (); } -extern "C" int -try_to_debug () +static +int exec_prepared_command (PWCHAR command) { - if (!debugger_command) + if (!command) return 0; - debug_printf ("debugger_command '%W'", debugger_command); - if (being_debugged ()) - { - extern void break_here (); - break_here (); - return 0; - } + debug_printf ("executing prepared command '%W'", command); - PWCHAR dbg_end = wcschr (debugger_command, L'\0'); + PWCHAR dbg_end = wcschr (command, L'\0'); __small_swprintf (dbg_end, L" %u", GetCurrentProcessId ()); LONG prio = GetThreadPriority (GetCurrentThread ()); @@ -509,11 +540,12 @@ try_to_debug () } FreeEnvironmentStringsW (rawenv); - console_printf ("*** starting debugger for pid %u, tid %u\n", + console_printf ("*** starting '%W' for pid %u, tid %u\r\n", + command, cygwin_pid (GetCurrentProcessId ()), GetCurrentThreadId ()); BOOL dbg; dbg = CreateProcessW (NULL, - debugger_command, + command, NULL, NULL, FALSE, @@ -527,11 +559,15 @@ try_to_debug () we can't wait here for the error_start process to exit, as if it's a debugger, it might want to continue this thread. So we busy wait until a debugger attaches, which stops this process, after which it can decide if - we continue or not. */ + we continue or not. + + Note that this is still racy: if the error_start process does it's work too + fast, we don't notice that it attached and get stuck here. + */ *dbg_end = L'\0'; if (!dbg) - system_printf ("Failed to start debugger, %E"); + system_printf ("Failed to start, %E"); else { SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_IDLE); @@ -540,13 +576,29 @@ try_to_debug () Sleep (2000); } - console_printf ("*** continuing pid %u from debugger call (%d)\n", - cygwin_pid (GetCurrentProcessId ()), dbg); + console_printf ("*** continuing pid %u\r\n", + cygwin_pid (GetCurrentProcessId ())); SetThreadPriority (GetCurrentThread (), prio); return dbg; } +extern "C" int +try_to_debug () +{ + /* If already being debugged, break into the debugger (Note that this function + can be called from places other than an exception) */ + if (being_debugged ()) + { + extern void break_here (); + break_here (); + return 1; + } + + /* Otherwise, invoke the JIT debugger, if set */ + return exec_prepared_command (debugger_command); +} + /* myfault exception handler. */ EXCEPTION_DISPOSITION exception::myfault (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, @@ -1264,7 +1316,6 @@ signal_exit (int sig, siginfo_t *si, void *) debug_printf ("exiting due to signal %d", sig); exit_state = ES_SIGNAL_EXIT; - if (cygheap->rlim_core > 0UL) switch (sig) { case SIGABRT: @@ -1277,9 +1328,24 @@ signal_exit (int sig, siginfo_t *si, void *) case SIGTRAP: case SIGXCPU: case SIGXFSZ: - sig |= 0x80; /* Flag that we've "dumped core" */ if (try_to_debug ()) break; + + if (cygheap->rlim_core == 0Ul) + break; + + sig |= 0x80; /* Set flag in exit status to show that we've "dumped core" */ + + /* If core dump size is >1MB, try to invoke dumper to write a + .core file */ + if (cygheap->rlim_core > 1024*1024) + { + if (exec_prepared_command (dumper_command)) + break; + /* If that failed, fall-through to... */ + } + + /* Otherwise write a .stackdump */ if (si->si_code != SI_USER && si->si_cyg) { cygwin_exception *exc = (cygwin_exception *) si->si_cyg; diff --git a/winsup/cygwin/local_includes/winsup.h b/winsup/cygwin/local_includes/winsup.h index bf0a0bcc3..76957618b 100644 --- a/winsup/cygwin/local_includes/winsup.h +++ b/winsup/cygwin/local_includes/winsup.h @@ -179,6 +179,7 @@ void close_all_files (bool = false); /* debug_on_trap support. see exceptions.cc:try_to_debug() */ extern "C" void error_start_init (const char*); +extern "C" void dumper_init (void); extern "C" int try_to_debug (); void ld_preload (); diff --git a/winsup/cygwin/release/3.5.0 b/winsup/cygwin/release/3.5.0 index 6209064a6..1e62316a0 100644 --- a/winsup/cygwin/release/3.5.0 +++ b/winsup/cygwin/release/3.5.0 @@ -60,3 +60,7 @@ What changed: - Enable automatic sparsifying of files on SSDs, independent of the "sparse" mount mode. + +- When RLIMIT_CORE is more than 1MB, a core dump file which can be loaded by gdb + is now written on a fatal error. Otherwise, if it's greater than zero, a text + format .stackdump file is written, as previously. diff --git a/winsup/doc/cygwinenv.xml b/winsup/doc/cygwinenv.xml index 5e17404a7..d97f2b77d 100644 --- a/winsup/doc/cygwinenv.xml +++ b/winsup/doc/cygwinenv.xml @@ -23,18 +23,29 @@ Defaults to off.</para> <listitem> <para> -<envar>error_start:Win32filepath</envar> - if set, runs -<filename>Win32filepath</filename> when cygwin encounters a fatal error, -which is useful for debugging. <filename>Win32filepath</filename> is -usually set to the path to <command>gdb</command> or -<command>dumper</command>, for example -<filename>C:\cygwin\bin\gdb.exe</filename>. -There is no default set. +<envar>error_start:Win32filepath</envar> - if set, runs +<filename>Win32filepath</filename> when cygwin encounters a fatal error, which +can be useful for debugging. Defaults to not set. +</para> +<para> +<filename>Win32filepath</filename> is typically set to <command>gdb</command> or +<command>dumper</command>. If giving a path in +<filename>Win32filepath</filename>, note that it is a Windows-style path and not +a Cygwin path. </para> <para> The filename of the executing program and it's Windows process id are appended to the command as arguments. </para> +<para> + Note: This takes priority over writing core dump or .stackdump files, if + enabled by <function>setrlimit(RLIMIT_CORE)</function> (e.g. via + <command>ulimit -c</command>). +</para> +<para> + Note: This has no effect if a debugger is already attached when the fatal + error occurs. +</para> </listitem> <listitem> diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml index 0abe1c41c..f8e38f5c4 100644 --- a/winsup/doc/new-features.xml +++ b/winsup/doc/new-features.xml @@ -97,6 +97,12 @@ Enable automatic sparsifying of files on SSDs, independent of the "sparse" mount mode. </para></listitem> +<listitem><para> +When RLIMIT_CORE is more than 1MB, a core dump file which can be loaded by gdb +is now written on a fatal error. Otherwise, if it's greater than zero, a text +format .stackdump file is written, as previously. +</para></listitem> + </itemizedlist> </sect2> diff --git a/winsup/doc/utils.xml b/winsup/doc/utils.xml index 9210c94e2..692dae38f 100644 --- a/winsup/doc/utils.xml +++ b/winsup/doc/utils.xml @@ -721,17 +721,17 @@ explorer $XPATH & <refsect1 id="dumper-desc"> <title>Description</title> <para>The <command>dumper</command> utility can be used to create a core - dump of running Windows process. This core dump can be later loaded to - <command>gdb</command> and analyzed. One common way to use - <command>dumper</command> is to plug it into cygwin's Just-In-Time - debugging facility by adding - <screen> -error_start=x:\path\to\dumper.exe -</screen> to the - <emphasis>CYGWIN</emphasis> environment variable. Please note that - <literal>x:\path\to\dumper.exe</literal> is Windows-style and not cygwin - path. If <literal>error_start</literal> is set this way, then dumper will - be started whenever some program encounters a fatal error. </para> + dump of running Windows process. This core dump can be later loaded into + <command>gdb</command> and analyzed. + </para> + + <para> + If the core file size limit is set to unlimited (e.g. <command>ulimit -c + unlimited</command>) and an <literal>error_start</literal> executable + hasn't been configured in the <literal>CYGWIN</literal> environment + variable, Cygwin will automatically run <command>dumper</command> when a + fatal error occurs. + </para> <para> <command>dumper</command> can be also be started from the command line to create a core dump of any running process.</para> @@ -742,14 +742,25 @@ error_start=x:\path\to\dumper.exe terminated.</para> <para> To save space in the core dump, <command>dumper</command> doesn't - write those portions of the target process's memory space that are loaded + write those portions of the target process's memory space that were loaded from executable and dll files and are unchanged (e.g. program code). Instead, <command>dumper</command> saves paths to the files which contain that data. When a core dump is loaded into gdb, it uses these paths to load the appropriate files. That means that if you create a core dump on one machine and try to debug it on another, you'll need to place identical copies of the executable and dlls in the same directories as on - the machine where the core dump was created. </para> + the machine where the core dump was created. + </para> + </refsect1> + + <refsect1 id="dumper-notes"> + <title>Notes</title> + <para> + A Cygwin "core dump file" is an ELF file containing the mutable parts of + the process memory and special note sections which capture the process, + thread and loaded module context needed to recreate the process image in a + debugger. + </para> </refsect1> </refentry> @@ -1497,8 +1508,10 @@ bash$ locale noexpr <para> <command>minidumper</command> can be used with cygwin's Just-In-Time - debugging facility in exactly the same way as <command>dumper</command> - (See <xref linkend="dumper"></xref>). + debugging facility by adding <code>error_start=minidumper</code> to the + <literal>CYGWIN</literal> environment variable. If <literal>CYGWIN</literal> + is set this way, then <command>minidumper</command> will be started whenever + a program encounters a fatal exception. </para> <para> |