This series covers various fixes & improvements when a crash happens in a program which fires up winedbg in auto mode.
Firstly, winedbg is a bit too strict for detecting the startup sequence (and fails to display the backtrace in these stricter cases). Third patch relaxes the startup conditions in that case.
Secondly, if failing programs is a GUI, winedbg is launched in a new console (since GUI are now detached from console), but console is closed after winedbg terminates (which is after printing the crash details). So rather not useful :-(
To work around this, conhost in its configuration dialog has tab 'Configuration' / 'End of program' / 'Close console' checkbox But which is not implemented. Second patch add supports for this in conhost.
Lastly, conhost has the ability to store configuration per application. Hence, one can configure conhost with specific settings for winedbg. Interesting options are: - setting very high buffer zone so that conhost keeps track of all winedbg output, - unchecking the 'Close console' option. To set it for winedbg, - start it with './wine wineconsole winedbg' - use 'Properties' from the menu (right-click in conhost) to set the options for winedbg
Since 68f3a8e699904bd308de0da4a1e1d2c9eb91ea96, conhost doesn't pick the registry entry for winedbg (if it exists) when started with './wine wineconsole winedbg'. Third patch solves that in a somehow hacky way, but couldn't find a better option that doesn't break https://bugs.winehq.org/show_bug.cgi?id=10941
-- v2: winedbg: No longer expect a startup sequence in auto mode. conhost: Support the close-on-exit configuration option. programs/wineconsole: Ensure conhost gets the expected title.
From: Eric Pouech epouech@codeweavers.com
Otherwise, when launching an exec with wineconsole, conhost gets wineconsole.exe and not the exec as title, and messes up the per-application settings.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/wineconsole/wineconsole.c | 34 +++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-)
diff --git a/programs/wineconsole/wineconsole.c b/programs/wineconsole/wineconsole.c index ab3e5cdade8..372f8820812 100644 --- a/programs/wineconsole/wineconsole.c +++ b/programs/wineconsole/wineconsole.c @@ -33,8 +33,23 @@
WINE_DEFAULT_DEBUG_CHANNEL(console);
-static BOOL setup_target_console(void) +static WCHAR *lookup_executable(WCHAR *cmdline) { + WCHAR path[MAX_PATH]; + WCHAR *p; + BOOL status; + + if (!(cmdline = wcsdup(cmdline))) return NULL; + if ((p = wcspbrk(cmdline, L" \t"))) *p = L'\0'; + status = SearchPathW(NULL, cmdline, L".exe", ARRAY_SIZE(path), path, NULL); + free(cmdline); + return status ? wcsdup(path) : NULL; +} + +static BOOL setup_target_console(WCHAR *title) +{ + BOOL ret; + if (!FreeConsole()) return FALSE; /* Zero out std handles so that AllocConsole() sets the newly allocated handles as std, * and will be inherited by child process. @@ -42,7 +57,20 @@ static BOOL setup_target_console(void) SetStdHandle(STD_INPUT_HANDLE, NULL); SetStdHandle(STD_OUTPUT_HANDLE, NULL); SetStdHandle(STD_ERROR_HANDLE, NULL); - return AllocConsole(); + /* HACK: tweak process parameters to set the title to target executable + * so that conhost will take config from that target process (and not wineconsole) + */ + if (title) + { + UNICODE_STRING old = RtlGetCurrentPeb()->ProcessParameters->WindowTitle; + RtlInitUnicodeString(&RtlGetCurrentPeb()->ProcessParameters->WindowTitle, title); + ret = AllocConsole(); + RtlGetCurrentPeb()->ProcessParameters->WindowTitle = old; + free(title); + } + else + ret = AllocConsole(); + return ret; }
int WINAPI wWinMain( HINSTANCE inst, HINSTANCE prev, WCHAR *cmdline, INT show ) @@ -56,7 +84,7 @@ int WINAPI wWinMain( HINSTANCE inst, HINSTANCE prev, WCHAR *cmdline, INT show )
if (!*cmd) cmd = default_cmd;
- if (!setup_target_console()) + if (!setup_target_console(lookup_executable(cmdline))) { ERR( "failed to allocate console: %lu\n", GetLastError() ); return 1;
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/conhost/conhost.c | 2 ++ programs/conhost/conhost.h | 1 + programs/conhost/window.c | 49 ++++++++++++++++++++++++++++++++------ 3 files changed, 45 insertions(+), 7 deletions(-)
diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 6b8c6d4f01d..8298743553f 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -2985,6 +2985,8 @@ static void teardown( struct console *console ) set_tty_attr( console, empty_char_info.attr ); tty_flush( console ); } + if (console->win) + teardown_window( console ); }
int __cdecl wmain(int argc, WCHAR *argv[]) diff --git a/programs/conhost/conhost.h b/programs/conhost/conhost.h index 76d897e8800..d3485156894 100644 --- a/programs/conhost/conhost.h +++ b/programs/conhost/conhost.h @@ -149,6 +149,7 @@ BOOL init_window( struct console *console ); void init_message_window( struct console *console ); void update_window_region( struct console *console, const RECT *update ); void update_window_config( struct console *console, BOOL delay ); +void teardown_window( struct console *console );
static inline void empty_update_rect( struct screen_buffer *screen_buffer, RECT *rect ) { diff --git a/programs/conhost/window.c b/programs/conhost/window.c index dedd48a9fc8..f8fc7016af1 100644 --- a/programs/conhost/window.c +++ b/programs/conhost/window.c @@ -61,6 +61,7 @@ struct console_window unsigned int sb_width; /* active screen buffer width */ unsigned int sb_height; /* active screen buffer height */ COORD cursor_pos; /* cursor position */ + unsigned int close_on_exit; /* whether conhost closes when all children have terminated */
RECT update; /* screen buffer update rect */ enum update_state update_state; /* update state */ @@ -86,6 +87,7 @@ struct console_config unsigned int win_height; COORD win_pos; /* position (in cells) of visible part of screen buffer in window */ unsigned int edition_mode; /* edition mode flavor while line editing */ + unsigned int close_on_exit; /* whether conhost closes when all children have terminated */ unsigned int font_pitch_family; unsigned int font_weight; WCHAR face_name[LF_FACESIZE]; @@ -94,7 +96,8 @@ struct console_config static const char *debugstr_config( const struct console_config *config ) { return wine_dbg_sprintf( "cell=(%u,%u) cursor=(%d,%d) attr=%02x pop-up=%02x font=%s/%u/%u " - "hist=%u/%d flags=%c%c msk=%08x sb=(%u,%u) win=(%u,%u)x(%u,%u) edit=%u", + "hist=%u/%d flags=%c%c msk=%08x sb=(%u,%u) win=(%u,%u)x(%u,%u) edit=%u " + "close_on_exit=%u", config->cell_width, config->cell_height, config->cursor_size, config->cursor_visible, config->attr, config->popup_attr, wine_dbgstr_w(config->face_name), config->font_pitch_family, @@ -104,7 +107,7 @@ static const char *debugstr_config( const struct console_config *config ) config->quick_edit ? 'Q' : 'q', config->menu_mask, config->sb_width, config->sb_height, config->win_pos.X, config->win_pos.Y, config->win_width, - config->win_height, config->edition_mode ); + config->win_height, config->edition_mode, config->close_on_exit ); }
static const char *debugstr_logfont( const LOGFONTW *lf, unsigned int ft ) @@ -141,6 +144,10 @@ static void load_registry_key( HKEY key, struct console_config *config ) DWORD type, count, val, i; WCHAR color_name[13];
+ count = sizeof(val); + if (!RegQueryValueExW( key, L"CloseOnExit", 0, &type, (BYTE *)&val, &count )) + config->close_on_exit = val; + for (i = 0; i < ARRAY_SIZE(config->color_map); i++) { wsprintfW( color_name, L"ColorTable%02d", i ); @@ -264,6 +271,7 @@ static void load_config( const WCHAR *key_name, struct console_config *config ) config->win_pos.X = 0; config->win_pos.Y = 0; config->edition_mode = 0; + config->close_on_exit= 1;
/* Load default console settings */ if (!RegOpenKeyW( HKEY_CURRENT_USER, L"Console", &key )) @@ -293,6 +301,12 @@ static void save_registry_key( HKEY key, const struct console_config *config, BO if (!save_all) load_config( NULL, &default_config );
+ if (save_all || config->close_on_exit != default_config.close_on_exit) + { + val = config->close_on_exit; + RegSetValueExW( key, L"CloseOnExit", 0, REG_DWORD, (BYTE *)&val, sizeof(val) ); + } + for (i = 0; i < ARRAY_SIZE(config->color_map); i++) { if (save_all || config->color_map[i] != default_config.color_map[i]) @@ -1673,7 +1687,7 @@ static INT_PTR WINAPI config_dialog_proc( HWND dialog, UINT msg, WPARAM wparam, SendMessageW( GetDlgItem(dialog, IDC_CNF_SB_HEIGHT_UD), UDM_SETRANGE, 0, MAKELPARAM(max_ud, 0)); SendMessageW( GetDlgItem(dialog, IDC_CNF_SB_WIDTH_UD), UDM_SETRANGE, 0, MAKELPARAM(max_ud, 0));
- SendDlgItemMessageW( dialog, IDC_CNF_CLOSE_EXIT, BM_SETCHECK, BST_CHECKED, 0 ); + SendDlgItemMessageW( dialog, IDC_CNF_CLOSE_EXIT, BM_SETCHECK, di->config.close_on_exit ? BST_CHECKED : BST_UNCHECKED, 0 );
SendDlgItemMessageW( dialog, IDC_CNF_EDITION_MODE, CB_ADDSTRING, 0, (LPARAM)L"Win32" ); SendDlgItemMessageW( dialog, IDC_CNF_EDITION_MODE, CB_ADDSTRING, 0, (LPARAM)L"Emacs" ); @@ -1685,6 +1699,7 @@ static INT_PTR WINAPI config_dialog_proc( HWND dialog, UINT msg, WPARAM wparam, NMHDR *nmhdr = (NMHDR*)lparam; int win_w, win_h, sb_w, sb_h; BOOL st1, st2; + DWORD val;
di = (struct dialog_info *)GetWindowLongPtrW( dialog, DWLP_USER ); switch (nmhdr->code) @@ -1724,6 +1739,9 @@ static INT_PTR WINAPI config_dialog_proc( HWND dialog, UINT msg, WPARAM wparam, di->config.sb_width = sb_w; di->config.sb_height = sb_h;
+ val = (IsDlgButtonChecked( dialog, IDC_CNF_CLOSE_EXIT ) & BST_CHECKED) != 0; + di->config.close_on_exit = val; + di->config.edition_mode = SendDlgItemMessageW( dialog, IDC_CNF_EDITION_MODE, CB_GETCURSEL, 0, 0 ); SetWindowLongPtrW( dialog, DWLP_MSGRESULT, PSNRET_NOERROR ); @@ -1744,8 +1762,9 @@ static void apply_config( struct console *console, const struct console_config * if (console->active->width != config->sb_width || console->active->height != config->sb_height) change_screen_buffer_size( console->active, config->sb_width, config->sb_height );
- console->window->menu_mask = config->menu_mask; - console->window->quick_edit = config->quick_edit; + console->window->menu_mask = config->menu_mask; + console->window->quick_edit = config->quick_edit; + console->window->close_on_exit = config->close_on_exit;
console->edition_mode = config->edition_mode; console->history_mode = config->history_mode; @@ -1813,8 +1832,9 @@ static void current_config( struct console *console, struct console_config *conf { size_t len;
- config->menu_mask = console->window->menu_mask; - config->quick_edit = console->window->quick_edit; + config->menu_mask = console->window->menu_mask; + config->quick_edit = console->window->quick_edit; + config->close_on_exit = console->window->close_on_exit;
config->edition_mode = console->edition_mode; config->history_mode = console->history_mode; @@ -2458,3 +2478,18 @@ void init_message_window( struct console *console ) WS_MAXIMIZEBOX|WS_HSCROLL|WS_VSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, HWND_MESSAGE, 0, wndclass.hInstance, console ); } + +void teardown_window( struct console *console ) +{ + if (console->window && !console->window->close_on_exit) + { + MSG msg; + + while (GetMessageW( &msg, 0, 0, 0 )) + { + if (msg.message == WM_QUIT) return; + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } + } +}
From: Eric Pouech epouech@codeweavers.com
winedbg is too strict about events expected for startup sequence in auto mode.
I've seen exit-thread, output debug string (and likely others could happen). Consequence is that winedbg doesn't display the backtrace (it needs the first exception for that).
So relax the startup conditions, and enter the auto mode after either first exception, exit-process, or timeout.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/winedbg/tgt_active.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-)
diff --git a/programs/winedbg/tgt_active.c b/programs/winedbg/tgt_active.c index a919f70a486..df642d0a717 100644 --- a/programs/winedbg/tgt_active.c +++ b/programs/winedbg/tgt_active.c @@ -607,23 +607,17 @@ void dbg_active_wait_for_first_exception(void) wait_exception(); }
-static BOOL dbg_active_wait_for_startup(DEBUG_EVENT* de) +static BOOL dbg_active_wait_for_startup(DEBUG_EVENT* de, DWORD timeout) { + DWORD64 tc, tc_last = GetTickCount64() + timeout; + dbg_interactiveP = FALSE; - while (dbg_num_processes() && WaitForDebugEvent(de, INFINITE)) + while (dbg_num_processes() && (tc = GetTickCount64()) <= tc_last && WaitForDebugEvent(de, tc_last - tc)) { - switch (de->dwDebugEventCode) - { - case CREATE_PROCESS_DEBUG_EVENT: - case CREATE_THREAD_DEBUG_EVENT: - case LOAD_DLL_DEBUG_EVENT: - case EXCEPTION_DEBUG_EVENT: - if (dbg_handle_debug_event(de)) return TRUE; - break; - default: - return FALSE; - } + if (de->dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) return FALSE; + if (dbg_handle_debug_event(de)) return TRUE; } + de->dwDebugEventCode = 0; return FALSE; }
@@ -932,9 +926,9 @@ enum dbg_start dbg_active_auto(int argc, char* argv[]) if (input == INVALID_HANDLE_VALUE) return start_error_parse;
/* debuggee can terminate before we get the first exception. - * so detect end of attach load sequence, and then print information. + * so wait either until first exception, or process detach, or timeout */ - if (dbg_curr_process->active_debuggee && !(first_exception = dbg_active_wait_for_startup(&de))) + if (dbg_curr_process->active_debuggee && !(first_exception = dbg_active_wait_for_startup(&de, 10000))) { dbg_printf("Couldn't get first exception for process %04lx %ls%s.\n" "No backtrace available\n", @@ -947,8 +941,10 @@ enum dbg_start dbg_active_auto(int argc, char* argv[]) if (!first_exception) { /* continue managing debug events, in case the exception event comes after current debug event */ - ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE); - dbg_active_wait_for_first_exception(); + if (de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) + dbg_handle_debug_event(&de); + else + dbg_active_wait_for_first_exception(); } if (output != INVALID_HANDLE_VALUE) {
Pushed V2: - fixes SAST warning
Gitlab does not highlight 68f3a8e699904bd308de0da4a1e1d2c9eb91ea96, and cannot find it in wine history - is this commit probably on some private branch?
yeah, should have picked the right (aka second) entry from git log it's 0eaebfe5c77af4665f8f60e1cf7e9efd10e6dd6a