Reduce stack pressure throughout Cygwin

* dcrt0.cc (initial_env): Reduce size of local path buffers to
        PATH_MAX.  Allocate debugger_command from process heap.
        (init_windows_system_directory): Very early initialize new global
        variable global_progname.
        * dll_init.cc (dll_list::alloc): Make path buffer static.  Explain why.
        (dll_list::populate_deps): Use tmp_pathbuf for local path buffer.
        * exceptions.cc (debugger_command): Convert to PWCHAR.
        (error_start_init): Allocate debugger_command and fill with wide char
        strings.  Only allocate if NULL.
        (try_to_debug): Just check if debugger_command is a NULL pointer to
        return.  Drop conversion from char to WCHAR and drop local variable
        dbg_cmd.
        * globals.cc (global_progname): New global variable to store Windows
        application path.
        * pinfo.cc (pinfo_basic::pinfo_basic): Just copy progname over from
        global_progname.
        (pinfo::status_exit): Let path_conv create the POSIX path to
        avoid local buffer.
        * pseudo_reloc.cc (__report_error): Utilize global_progname, drop local
        buffer.
        * smallprint.cc (__small_vsprintf): Just utilize global_progname for
        %P format specifier.
        (__small_vswprintf): Ditto.
        * strace.cc (PROTECT): Change to reflect x being a pointer.  Reformat.
        (CHECK): Ditto.  Reformat.
        (strace::activate): Utilize global_progname, drop local buffer.
        Fix formatting.
        (strace::vsprntf): Reduce size of local progname buffer to NAME_MAX.
        Copy and, if necessary, convert only the last path component to
        progname.
        (strace_buf_guard): New muto.
        (buf): New static pointer.
        (strace::vprntf): Use buf under strace_buf_guard lock only.  Allocate
        buffer space for buf on Windows heap.
        * wow64.cc (wow64_respawn_process): Utilize global_progname, drop
        local path buffer.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2015-07-19 22:38:30 +02:00
parent 93a8435f82
commit 4889f730c1
10 changed files with 141 additions and 85 deletions

View File

@ -1,3 +1,42 @@
2015-07-19 Corinna Vinschen <corinna@vinschen.de>
* dcrt0.cc (initial_env): Reduce size of local path buffers to
PATH_MAX. Allocate debugger_command from process heap.
(init_windows_system_directory): Very early initialize new global
variable global_progname.
* dll_init.cc (dll_list::alloc): Make path buffer static. Explain why.
(dll_list::populate_deps): Use tmp_pathbuf for local path buffer.
* exceptions.cc (debugger_command): Convert to PWCHAR.
(error_start_init): Allocate debugger_command and fill with wide char
strings. Only allocate if NULL.
(try_to_debug): Just check if debugger_command is a NULL pointer to
return. Drop conversion from char to WCHAR and drop local variable
dbg_cmd.
* globals.cc (global_progname): New global variable to store Windows
application path.
* pinfo.cc (pinfo_basic::pinfo_basic): Just copy progname over from
global_progname.
(pinfo::status_exit): Let path_conv create the POSIX path to
avoid local buffer.
* pseudo_reloc.cc (__report_error): Utilize global_progname, drop local
buffer.
* smallprint.cc (__small_vsprintf): Just utilize global_progname for
%P format specifier.
(__small_vswprintf): Ditto.
* strace.cc (PROTECT): Change to reflect x being a pointer. Reformat.
(CHECK): Ditto. Reformat.
(strace::activate): Utilize global_progname, drop local buffer.
Fix formatting.
(strace::vsprntf): Reduce size of local progname buffer to NAME_MAX.
Copy and, if necessary, convert only the last path component to
progname.
(strace_buf_guard): New muto.
(buf): New static pointer.
(strace::vprntf): Use buf under strace_buf_guard lock only. Allocate
buffer space for buf on Windows heap.
* wow64.cc (wow64_respawn_process): Utilize global_progname, drop
local path buffer.
2015-07-18 Corinna Vinschen <corinna@vinschen.de> 2015-07-18 Corinna Vinschen <corinna@vinschen.de>
* gendef: Remove unused 64 bit versions of __sjfault and __ljfault. * gendef: Remove unused 64 bit versions of __sjfault and __ljfault.

View File

@ -509,11 +509,11 @@ initial_env ()
_cygwin_testing = 1; _cygwin_testing = 1;
#ifdef DEBUGGING #ifdef DEBUGGING
char buf[NT_MAX_PATH]; char buf[PATH_MAX];
if (GetEnvironmentVariableA ("CYGWIN_DEBUG", buf, sizeof (buf) - 1)) if (GetEnvironmentVariableA ("CYGWIN_DEBUG", buf, sizeof (buf) - 1))
{ {
char buf1[NT_MAX_PATH]; char buf1[PATH_MAX];
GetModuleFileName (NULL, buf1, NT_MAX_PATH); GetModuleFileName (NULL, buf1, PATH_MAX);
char *p = strpbrk (buf, ":="); char *p = strpbrk (buf, ":=");
if (!p) if (!p)
p = (char *) "gdb.exe -nw"; p = (char *) "gdb.exe -nw";
@ -521,6 +521,13 @@ initial_env ()
*p++ = '\0'; *p++ = '\0';
if (strcasestr (buf1, buf)) if (strcasestr (buf1, buf))
{ {
extern PWCHAR debugger_command;
debugger_command = (PWCHAR) HeapAlloc (GetProcessHeap (), 0,
(2 * NT_MAX_PATH + 20)
* sizeof (WCHAR));
if (!debugger_command)
return;
error_start_init (p); error_start_init (p);
jit_debug = true; jit_debug = true;
try_to_debug (); try_to_debug ();
@ -736,6 +743,7 @@ void
dll_crt0_0 () dll_crt0_0 ()
{ {
wincap.init (); wincap.init ();
GetModuleFileNameW (NULL, global_progname, NT_MAX_PATH);
child_proc_info = get_cygwin_startup_info (); child_proc_info = get_cygwin_startup_info ();
init_windows_system_directory (); init_windows_system_directory ();
initial_env (); initial_env ();

View File

@ -25,6 +25,7 @@ details. */
#include <wchar.h> #include <wchar.h>
#include <sys/reent.h> #include <sys/reent.h>
#include <assert.h> #include <assert.h>
#include <tls_pbuf.h>
extern void __stdcall check_sanity_and_sync (per_process *); extern void __stdcall check_sanity_and_sync (per_process *);
@ -178,7 +179,9 @@ dll_list::find_by_modname (const PWCHAR modname)
dll * dll *
dll_list::alloc (HINSTANCE h, per_process *p, dll_type type) dll_list::alloc (HINSTANCE h, per_process *p, dll_type type)
{ {
WCHAR buf[NT_MAX_PATH]; /* Called under loader lock conditions so this function can't be called
multiple times in parallel. A static buffer is safe. */
static WCHAR buf[NT_MAX_PATH];
GetModuleFileNameW (h, buf, NT_MAX_PATH); GetModuleFileNameW (h, buf, NT_MAX_PATH);
PWCHAR name = buf; PWCHAR name = buf;
if (!wcsncmp (name, L"\\\\?\\", 4)) if (!wcsncmp (name, L"\\\\?\\", 4))
@ -264,7 +267,9 @@ dll_list::append (dll* d)
void dll_list::populate_deps (dll* d) void dll_list::populate_deps (dll* d)
{ {
WCHAR wmodname[NT_MAX_PATH]; tmp_pathbuf tp;
PWCHAR wmodname = tp.w_get ();
pefile* pef = (pefile*) d->handle; pefile* pef = (pefile*) d->handle;
PIMAGE_DATA_DIRECTORY dd = pef->idata_dir (IMAGE_DIRECTORY_ENTRY_IMPORT); PIMAGE_DATA_DIRECTORY dd = pef->idata_dir (IMAGE_DIRECTORY_ENTRY_IMPORT);
/* Annoyance: calling crealloc with a NULL pointer will use the /* Annoyance: calling crealloc with a NULL pointer will use the

View File

@ -46,7 +46,7 @@ details. */
#define CALL_HANDLER_RETRY_OUTER 10 #define CALL_HANDLER_RETRY_OUTER 10
#define CALL_HANDLER_RETRY_INNER 10 #define CALL_HANDLER_RETRY_INNER 10
char debugger_command[2 * NT_MAX_PATH + 20]; PWCHAR debugger_command;
extern u_char _sigbe; extern u_char _sigbe;
extern u_char _sigdelayed_end; extern u_char _sigdelayed_end;
@ -112,18 +112,19 @@ extern "C" void
error_start_init (const char *buf) error_start_init (const char *buf)
{ {
if (!buf || !*buf) if (!buf || !*buf)
{
debugger_command[0] = '\0';
return; return;
} if (!debugger_command &&
!(debugger_command = (PWCHAR) malloc ((2 * NT_MAX_PATH + 20)
* sizeof (WCHAR))))
return;
char pgm[NT_MAX_PATH]; PWCHAR cp = debugger_command
if (!GetModuleFileName (NULL, pgm, NT_MAX_PATH)) + sys_mbstowcs (debugger_command, NT_MAX_PATH, buf) - 1;
strcpy (pgm, "cygwin1.dll"); cp = wcpcpy (cp, L" \"");
for (char *p = strchr (pgm, '\\'); p; p = strchr (p, '\\')) wcpcpy (cp, global_progname);
*p = '/'; for (PWCHAR p = wcschr (cp, L'\\'); p; p = wcschr (p, L'\\'))
*p = L'/';
__small_sprintf (debugger_command, "%s \"%s\"", buf, pgm); wcscat (cp, L"\"");
} }
void void
@ -474,9 +475,9 @@ cygwin_stackdump ()
extern "C" int extern "C" int
try_to_debug (bool waitloop) try_to_debug (bool waitloop)
{ {
debug_printf ("debugger_command '%s'", debugger_command); if (!debugger_command)
if (*debugger_command == '\0')
return 0; return 0;
debug_printf ("debugger_command '%W'", debugger_command);
if (being_debugged ()) if (being_debugged ())
{ {
extern void break_here (); extern void break_here ();
@ -484,8 +485,8 @@ try_to_debug (bool waitloop)
return 0; return 0;
} }
__small_sprintf (strchr (debugger_command, '\0'), " %u", PWCHAR dbg_end = wcschr (debugger_command, L'\0');
GetCurrentProcessId ()); __small_swprintf (dbg_end, L" %u", GetCurrentProcessId ());
LONG prio = GetThreadPriority (GetCurrentThread ()); LONG prio = GetThreadPriority (GetCurrentThread ());
SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST); SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
@ -529,10 +530,8 @@ try_to_debug (bool waitloop)
console_printf ("*** starting debugger for pid %u, tid %u\n", console_printf ("*** starting debugger for pid %u, tid %u\n",
cygwin_pid (GetCurrentProcessId ()), GetCurrentThreadId ()); cygwin_pid (GetCurrentProcessId ()), GetCurrentThreadId ());
BOOL dbg; BOOL dbg;
WCHAR dbg_cmd[strlen(debugger_command) + 1];
sys_mbstowcs (dbg_cmd, strlen(debugger_command) + 1, debugger_command);
dbg = CreateProcessW (NULL, dbg = CreateProcessW (NULL,
dbg_cmd, debugger_command,
NULL, NULL,
NULL, NULL,
FALSE, FALSE,
@ -542,6 +541,7 @@ try_to_debug (bool waitloop)
&si, &si,
&pi); &pi);
*dbg_end = L'\0';
if (!dbg) if (!dbg)
system_printf ("Failed to start debugger, %E"); system_printf ("Failed to start debugger, %E");
else else

View File

@ -31,6 +31,7 @@ UINT windows_system_directory_length;
WCHAR system_wow64_directory[MAX_PATH]; WCHAR system_wow64_directory[MAX_PATH];
UINT system_wow64_directory_length; UINT system_wow64_directory_length;
#endif /* !__x86_64__ */ #endif /* !__x86_64__ */
WCHAR global_progname[NT_MAX_PATH];
/* program exit the program */ /* program exit the program */

View File

@ -38,7 +38,9 @@ public:
pinfo_basic::pinfo_basic () pinfo_basic::pinfo_basic ()
{ {
pid = dwProcessId = GetCurrentProcessId (); pid = dwProcessId = GetCurrentProcessId ();
GetModuleFileNameW (NULL, progname, sizeof (progname) / sizeof (WCHAR)); PWCHAR pend = wcpncpy (progname, global_progname,
sizeof (progname) / sizeof (WCHAR) - 1);
*pend = L'\0';
/* Default uid/gid are needed very early to initialize shared user info. */ /* Default uid/gid are needed very early to initialize shared user info. */
uid = ILLEGAL_UID; uid = ILLEGAL_UID;
gid = ILLEGAL_GID; gid = ILLEGAL_GID;
@ -120,20 +122,18 @@ pinfo::status_exit (DWORD x)
{ {
case STATUS_DLL_NOT_FOUND: case STATUS_DLL_NOT_FOUND:
{ {
char posix_prog[NT_MAX_PATH];
path_conv pc; path_conv pc;
if (!procinfo) if (!procinfo)
pc.check ("/dev/null"); pc.check ("/dev/null", PC_NOWARN | PC_POSIX);
else else
{ {
UNICODE_STRING uc; UNICODE_STRING uc;
RtlInitUnicodeString(&uc, procinfo->progname); RtlInitUnicodeString(&uc, procinfo->progname);
pc.check (&uc, PC_NOWARN); pc.check (&uc, PC_NOWARN | PC_POSIX);
} }
mount_table->conv_to_posix_path (pc.get_win32 (), posix_prog, 1);
small_printf ("%s: error while loading shared libraries: %s: cannot " small_printf ("%s: error while loading shared libraries: %s: cannot "
"open shared object file: No such file or directory\n", "open shared object file: No such file or directory\n",
posix_prog, find_first_notloaded_dll (pc)); pc.get_posix (), find_first_notloaded_dll (pc));
x = 127 << 8; x = 127 << 8;
} }
break; break;

View File

@ -85,20 +85,18 @@ __report_error (const char *msg, ...)
* cygwin ptys. * cygwin ptys.
*/ */
char buf[128]; char buf[128];
WCHAR module[PATH_MAX];
char *posix_module = NULL; char *posix_module = NULL;
static const char UNKNOWN_MODULE[] = "<unknown module>: "; static const char UNKNOWN_MODULE[] = "<unknown module>: ";
static const char CYGWIN_FAILURE_MSG[] = "Cygwin runtime failure: "; static const char CYGWIN_FAILURE_MSG[] = "Cygwin runtime failure: ";
HANDLE errh = GetStdHandle (STD_ERROR_HANDLE); HANDLE errh = GetStdHandle (STD_ERROR_HANDLE);
ssize_t modulelen = GetModuleFileNameW (NULL, module, PATH_MAX);
va_list args; va_list args;
/* FIXME: cleanup further to avoid old use of cygwin_internal */ /* FIXME: cleanup further to avoid old use of cygwin_internal */
if (errh == INVALID_HANDLE_VALUE) if (errh == INVALID_HANDLE_VALUE)
cygwin_internal (CW_EXIT_PROCESS, STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION, 1); cygwin_internal (CW_EXIT_PROCESS, STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION, 1);
if (modulelen > 0) posix_module = (char *) cygwin_create_path (CCP_WIN_W_TO_POSIX,
posix_module = (char *) cygwin_create_path (CCP_WIN_W_TO_POSIX, module); global_progname);
va_start (args, msg); va_start (args, msg);
vsnprintf (buf, sizeof (buf), msg, args); vsnprintf (buf, sizeof (buf), msg, args);

View File

@ -284,12 +284,6 @@ gen_decimalLL:
dst = rnarg (dst, 16, 0, len, pad); dst = rnarg (dst, 16, 0, len, pad);
#endif #endif
break; break;
case 'P':
if (!GetModuleFileName (NULL, tmp, NT_MAX_PATH))
s = "cygwin program";
else
s = tmp;
goto fillin;
case '.': case '.':
n = strtol (fmt, (char **) &fmt, 10); n = strtol (fmt, (char **) &fmt, 10);
if (*fmt++ != 's') if (*fmt++ != 's')
@ -311,6 +305,9 @@ gen_decimalLL:
else else
*dst++ = *s++; *dst++ = *s++;
break; break;
case 'P':
RtlInitUnicodeString (us = &uw, global_progname);
goto wfillin;
case 'W': case 'W':
w = va_arg (ap, PWCHAR); w = va_arg (ap, PWCHAR);
RtlInitUnicodeString (us = &uw, w ?: L"(null)"); RtlInitUnicodeString (us = &uw, w ?: L"(null)");
@ -613,10 +610,7 @@ gen_decimalLL:
#endif #endif
break; break;
case L'P': case L'P':
if (!GetModuleFileNameW (NULL, tmp, NT_MAX_PATH)) RtlInitUnicodeString (us = &uw, global_progname);
RtlInitUnicodeString (us = &uw, L"cygwin program");
else
RtlInitUnicodeString (us = &uw, tmp);
goto fillin; goto fillin;
case L'.': case L'.':
n = wcstoul (fmt, (wchar_t **) &fmt, 10); n = wcstoul (fmt, (wchar_t **) &fmt, 10);

View File

@ -21,9 +21,13 @@ details. */
#include "dtable.h" #include "dtable.h"
#include "cygheap.h" #include "cygheap.h"
#include "child_info.h" #include "child_info.h"
#include "sync.h"
#define PROTECT(x) x[sizeof (x)-1] = 0 #define PROTECT(x) {x[NT_MAX_PATH - 1] = '\0';}
#define CHECK(x) if (x[sizeof (x)-1] != 0) { small_printf ("array bound exceeded %d\n", __LINE__); ExitProcess (1); } #define CHECK(x) if (x[NT_MAX_PATH - 1] != '\0') \
{ small_printf ("array bound exceeded %d\n", __LINE__); \
ExitProcess (1); \
}
class strace NO_COPY strace; class strace NO_COPY strace;
@ -35,25 +39,25 @@ strace::activate (bool isfork)
if (!_active && being_debugged ()) if (!_active && being_debugged ())
{ {
char buf[30]; char buf[30];
__small_sprintf (buf, "cYg%8x %lx %d", _STRACE_INTERFACE_ACTIVATE_ADDR, &_active, isfork); __small_sprintf (buf, "cYg%8x %lx %d",
_STRACE_INTERFACE_ACTIVATE_ADDR, &_active, isfork);
OutputDebugString (buf); OutputDebugString (buf);
if (_active) if (_active)
{ {
char pidbuf[80]; char pidbuf[80];
WCHAR progname_buf[NT_MAX_PATH - 512]; PWCHAR progname;
WCHAR *progname;
if (myself) if (myself)
{ {
__small_sprintf (pidbuf, "(pid %d, ppid %d, windows pid %u)", myself->pid, __small_sprintf (pidbuf, "(pid %d, ppid %d, windows pid %u)",
myself->ppid ?: 1, GetCurrentProcessId ()); myself->pid, myself->ppid ?: 1,
GetCurrentProcessId ());
progname = myself->progname; progname = myself->progname;
} }
else else
{ {
GetModuleFileNameW (NULL, progname_buf, __small_sprintf (pidbuf, "(windows pid %u)",
sizeof progname_buf / sizeof (WCHAR)); GetCurrentProcessId ());
__small_sprintf (pidbuf, "(windows pid %u)", GetCurrentProcessId ()); progname = global_progname;
progname = progname_buf;
} }
prntf (1, NULL, "**********************************************"); prntf (1, NULL, "**********************************************");
prntf (1, NULL, "Program name: %W %s", progname, pidbuf); prntf (1, NULL, "Program name: %W %s", progname, pidbuf);
@ -151,32 +155,32 @@ strace::vsprntf (char *buf, const char *func, const char *infmt, va_list ap)
else else
{ {
PWCHAR pn = NULL; PWCHAR pn = NULL;
WCHAR progname[NT_MAX_PATH]; WCHAR progname[NAME_MAX];
if (!cygwin_finished_initializing) if (cygwin_finished_initializing && __progname)
pn = (myself) ? myself->progname : NULL; {
else if (__progname) char *p = strrchr (__progname, '/');
sys_mbstowcs(pn = progname, NT_MAX_PATH, __progname); if (p)
++p;
WCHAR empty[1] = {};
PWCHAR p;
if (!pn)
GetModuleFileNameW (NULL, pn = progname, sizeof (progname));
if (!pn)
p = empty;
else if ((p = wcsrchr (pn, L'\\')) != NULL)
p++;
else if ((p = wcsrchr (pn, L'/')) != NULL)
p++;
else else
p = pn; p = __progname;
if (p != progname) char *pe = strrchr (p, '.');
wcscpy (progname, p); if (!pe || !ascii_strcasematch (pe, ".exe"))
if ((p = wcsrchr (progname, '.')) != NULL pe = strrchr (p, '\0');
&& !wcscasecmp (p, L".exe")) sys_mbstowcs (pn = progname, NAME_MAX, p, pe - p);
*p = '\000'; }
p = progname; else
{
PWCHAR p = wcsrchr (global_progname, L'\\');
++p;
PWCHAR pe = wcsrchr (p, '.');
if (!pe || wcscasecmp (pe, L".exe"))
pe = wcsrchr (p, L'\0');
pe = wcpncpy (progname, p, pe - p);
*pe = L'\0';
pn = progname;
}
char tmpbuf[20]; char tmpbuf[20];
count = __small_sprintf (buf, fmt, *p ? p : L"?", mypid (tmpbuf), count = __small_sprintf (buf, fmt, pn, mypid (tmpbuf),
execing ? "!" : ""); execing ? "!" : "");
if (func) if (func)
count += getfunc (buf + count, func); count += getfunc (buf + count, func);
@ -235,14 +239,22 @@ strace::write_childpid (pid_t pid)
/* Printf function used when tracing system calls. /* Printf function used when tracing system calls.
Warning: DO NOT SET ERRNO HERE! */ Warning: DO NOT SET ERRNO HERE! */
static NO_COPY muto strace_buf_guard;
static NO_COPY char *buf;
void void
strace::vprntf (unsigned category, const char *func, const char *fmt, va_list ap) strace::vprntf (unsigned category, const char *func, const char *fmt, va_list ap)
{ {
DWORD err = GetLastError (); DWORD err = GetLastError ();
int len; int len;
char buf[NT_MAX_PATH];
strace_buf_guard.init ("smallprint_buf")->acquire ();
/* Creating buffer on Windows process heap to drop stack pressure and
keeping our .bss small. */
if (!buf)
buf = (char *) HeapAlloc (GetProcessHeap (), 0, NT_MAX_PATH);
if (!buf)
return;
PROTECT (buf); PROTECT (buf);
SetLastError (err); SetLastError (err);
@ -272,6 +284,7 @@ strace::vprntf (unsigned category, const char *func, const char *fmt, va_list ap
if (active ()) if (active ())
write (category, buf, len); write (category, buf, len);
#endif #endif
strace_buf_guard.release ();
SetLastError (err); SetLastError (err);
} }

View File

@ -192,19 +192,17 @@ wow64_revert_to_original_stack (PVOID &allocationbase)
void void
wow64_respawn_process () wow64_respawn_process ()
{ {
WCHAR path[PATH_MAX];
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
STARTUPINFOW si; STARTUPINFOW si;
DWORD ret = 0; DWORD ret = 0;
GetModuleFileNameW (NULL, path, PATH_MAX);
GetStartupInfoW (&si); GetStartupInfoW (&si);
if (!CreateProcessW (path, GetCommandLineW (), NULL, NULL, TRUE, if (!CreateProcessW (global_progname, GetCommandLineW (), NULL, NULL, TRUE,
CREATE_DEFAULT_ERROR_MODE CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ()), | GetPriorityClass (GetCurrentProcess ()),
NULL, NULL, &si, &pi)) NULL, NULL, &si, &pi))
api_fatal ("Failed to create process <%W> <%W>, %E", api_fatal ("Failed to create process <%W> <%W>, %E",
path, GetCommandLineW ()); global_progname, GetCommandLineW ());
CloseHandle (pi.hThread); CloseHandle (pi.hThread);
if (WaitForSingleObject (pi.hProcess, INFINITE) == WAIT_FAILED) if (WaitForSingleObject (pi.hProcess, INFINITE) == WAIT_FAILED)
api_fatal ("Waiting for process %u failed, %E", pi.dwProcessId); api_fatal ("Waiting for process %u failed, %E", pi.dwProcessId);