1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
15 Implementation of Win32 debugging API functions.
24 #undef _LARGEFILE64_SOURCE
25 #undef _FILE_OFFSET_BITS
28 #include "pal/dbgmsg.h"
29 SET_DEFAULT_DEBUG_CHANNEL(DEBUG); // some headers have code with asserts, so do this first
31 #include "pal/thread.hpp"
32 #include "pal/procobj.hpp"
33 #include "pal/file.hpp"
35 #include "pal/palinternal.h"
36 #include "pal/process.h"
37 #include "pal/context.h"
38 #include "pal/debug.h"
39 #include "pal/environ.h"
40 #include "pal/malloc.hpp"
41 #include "pal/module.h"
42 #include "pal/stackstring.hpp"
43 #include "pal/virtual.h"
49 #elif HAVE_TTRACE // HAVE_PROCFS_CTL
50 #include <sys/ttrace.h>
52 #include <sys/ptrace.h>
53 #endif // HAVE_PROCFS_CTL
55 #include <mach/mach.h>
56 #endif // HAVE_VM_READ
58 #include <sys/types.h>
63 #endif // HAVE_PROCFS_H
65 #if HAVE_MACH_EXCEPTIONS
66 #include "../exception/machexception.h"
67 #endif // HAVE_MACH_EXCEPTIONS
69 using namespace CorUnix;
71 extern "C" void DBG_DebugBreak_End();
74 #define CTL_ATTACH "attach"
75 #define CTL_DETACH "detach"
76 #define CTL_WAIT "wait"
77 #endif // HAVE_PROCFS_CTL
79 /* ------------------- Constant definitions ----------------------------------*/
81 #if !HAVE_VM_READ && !HAVE_PROCFS_CTL
82 const BOOL DBG_ATTACH = TRUE;
83 const BOOL DBG_DETACH = FALSE;
85 static const char PAL_OUTPUTDEBUGSTRING[] = "PAL_OUTPUTDEBUGSTRING";
88 #define ENABLE_RUN_ON_DEBUG_BREAK 1
91 #ifdef ENABLE_RUN_ON_DEBUG_BREAK
92 static const char PAL_RUN_ON_DEBUG_BREAK[] = "PAL_RUN_ON_DEBUG_BREAK";
93 #endif // ENABLE_RUN_ON_DEBUG_BREAK
101 The FlushInstructionCache function flushes the instruction cache for
102 the specified process.
106 This is a no-op for x86 architectures where the instruction and data
107 caches are coherent in hardware. For non-X86 architectures, this call
108 usually maps to a kernel API to flush the D-caches on all processors.
113 FlushInstructionCache(
115 IN LPCVOID lpBaseAddress,
120 PERF_ENTRY(FlushInstructionCache);
121 ENTRY("FlushInstructionCache (hProcess=%p, lpBaseAddress=%p dwSize=%d)\
122 \n", hProcess, lpBaseAddress, dwSize);
124 if (lpBaseAddress != NULL)
126 Ret = DBG_FlushInstructionCache(lpBaseAddress, dwSize);
133 LOGEXIT("FlushInstructionCache returns BOOL %d\n", Ret);
134 PERF_EXIT(FlushInstructionCache);
148 IN LPCSTR lpOutputString)
150 PERF_ENTRY(OutputDebugStringA);
151 ENTRY("OutputDebugStringA (lpOutputString=%p (%s))\n",
152 lpOutputString ? lpOutputString : "NULL",
153 lpOutputString ? lpOutputString : "NULL");
155 // As we don't support debug events, we are going to output the debug string
156 // to stderr instead of generating OUT_DEBUG_STRING_EVENT. It's safe to tell
157 // EnvironGetenv not to make a copy of the value here since we only want to
158 // check whether it exists, not actually use it.
159 if ((lpOutputString != NULL) &&
160 (NULL != EnvironGetenv(PAL_OUTPUTDEBUGSTRING, /* copyValue */ FALSE)))
162 fprintf(stderr, "%s", lpOutputString);
165 LOGEXIT("OutputDebugStringA returns\n");
166 PERF_EXIT(OutputDebugStringA);
178 IN LPCWSTR lpOutputString)
180 CHAR *lpOutputStringA;
183 PERF_ENTRY(OutputDebugStringW);
184 ENTRY("OutputDebugStringW (lpOutputString=%p (%S))\n",
185 lpOutputString ? lpOutputString: W16_NULLSTRING,
186 lpOutputString ? lpOutputString: W16_NULLSTRING);
188 if (lpOutputString == NULL)
190 OutputDebugStringA("");
194 if ((strLen = WideCharToMultiByte(CP_ACP, 0, lpOutputString, -1, NULL, 0,
198 ASSERT("failed to get wide chars length\n");
199 SetLastError(ERROR_INTERNAL_ERROR);
203 /* strLen includes the null terminator */
204 if ((lpOutputStringA = (LPSTR) InternalMalloc((strLen * sizeof(CHAR)))) == NULL)
206 ERROR("Insufficient memory available !\n");
207 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
211 if(! WideCharToMultiByte(CP_ACP, 0, lpOutputString, -1,
212 lpOutputStringA, strLen, NULL, NULL))
214 ASSERT("failed to convert wide chars to multibytes\n");
215 SetLastError(ERROR_INTERNAL_ERROR);
216 free(lpOutputStringA);
220 OutputDebugStringA(lpOutputStringA);
221 free(lpOutputStringA);
224 LOGEXIT("OutputDebugStringW returns\n");
225 PERF_EXIT(OutputDebugStringW);
228 #ifdef ENABLE_RUN_ON_DEBUG_BREAK
230 When DebugBreak() is called, if PAL_RUN_ON_DEBUG_BREAK is set,
231 DebugBreak() will execute whatever command is in there.
233 PAL_RUN_ON_DEBUG_BREAK must be no longer than 255 characters.
235 This command string inherits the current process's environment,
237 PAL_EXE_PID - the process ID of the current process
238 PAL_EXE_NAME - the name of the executable of the current process
240 When DebugBreak() runs this string, it periodically polls the child process
241 and blocks until it finishes. If you use this mechanism to start a
242 debugger, you can break this poll loop by setting the "spin" variable in
243 run_debug_command()'s frame to 0, and then the parent process can
246 suggested values for PAL_RUN_ON_DEBUG_BREAK:
247 to halt the process for later inspection:
248 'echo stopping $PAL_EXE_PID; kill -STOP $PAL_EXE_PID; sleep 10'
250 to print out the stack trace:
251 'pstack $PAL_EXE_PID'
253 to invoke the gdb debugger on the process:
254 'set -x; gdb $PAL_EXE_NAME $PAL_EXE_PID'
256 to invoke the ddd debugger on the process (requires X11):
257 'set -x; ddd $PAL_EXE_NAME $PAL_EXE_PID'
262 run_debug_command (const char *command)
265 Volatile<int> spin = 1;
271 printf("Spawning command: %s\n", command);
278 const char *argv[4] = { "sh", "-c", command, 0 };
279 execv("/bin/sh", (char **)argv);
283 /* We continue either when the spawned process has stopped, or when
284 an attached debugger sets spin to 0 */
287 int ret = waitpid(pid, &status, WNOHANG);
290 /* I tried to use sleep for this, and that works everywhere except
291 FreeBSD. The problem on FreeBSD is that if the process gets a
292 signal while blocked in sleep(), gdb is confused by the stack */
293 for (i = 0; i < 1000000; i++)
296 else if (ret == -1) {
297 if (errno != EINTR) {
301 else if (WIFEXITED(status)) {
302 return WEXITSTATUS(status);
305 fprintf (stderr, "unexpected return from waitpid\n");
311 #endif // ENABLE_RUN_ON_DEBUG_BREAK
313 #define PID_TEXT "PAL_EXE_PID="
314 #define EXE_TEXT "PAL_EXE_NAME="
320 #ifdef ENABLE_RUN_ON_DEBUG_BREAK
321 extern MODSTRUCT exe_module;
323 char *command_string = EnvironGetenv(PAL_RUN_ON_DEBUG_BREAK);
326 char pid_buf[sizeof (PID_TEXT) + 32];
327 PathCharString exe_bufString;
328 int libNameLength = 10;
330 if (exe_module.lib_name != NULL)
332 libNameLength = PAL_wcslen(exe_module.lib_name);
335 SIZE_T dwexe_buf = strlen(EXE_TEXT) + libNameLength + 1;
336 CHAR * exe_buf = exe_bufString.OpenStringBuffer(dwexe_buf);
343 if (snprintf (pid_buf, sizeof (pid_buf), PID_TEXT "%d", getpid()) <= 0)
348 if (snprintf (exe_buf, sizeof (CHAR) * (dwexe_buf + 1), EXE_TEXT "%ls", (wchar_t *)exe_module.lib_name) <= 0)
353 exe_bufString.CloseBuffer(dwexe_buf);
354 /* strictly speaking, we might want to only set these environment
355 variables in the child process, but if we do that we can't check
356 for errors. putenv/setenv can fail when out of memory */
358 if (!EnvironPutenv (pid_buf, FALSE) || !EnvironPutenv (exe_buf, FALSE))
363 if (run_debug_command (command_string))
368 free(command_string);
377 free(command_string);
380 fprintf (stderr, "Failed to execute command: '%s'\n", command_string);
382 #else // ENABLE_RUN_ON_DEBUG_BREAK
384 #endif // ENABLE_RUN_ON_DEBUG_BREAK
398 PERF_ENTRY(DebugBreak);
399 ENTRY("DebugBreak()\n");
401 if (DebugBreakCommand() <= 0) {
402 // either didn't do anything, or failed
403 TRACE("Calling DBG_DebugBreak\n");
407 LOGEXIT("DebugBreak returns\n");
408 PERF_EXIT(DebugBreak);
415 Returns true if the address is in DBG_DebugBreak.
419 IsInDebugBreak(void *addr)
421 return (addr >= (void *)DBG_DebugBreak) && (addr <= (void *)DBG_DebugBreak_End);
434 IN OUT LPCONTEXT lpContext)
438 CPalThread *pTargetThread;
439 IPalObject *pobjThread = NULL;
442 PERF_ENTRY(GetThreadContext);
443 ENTRY("GetThreadContext (hThread=%p, lpContext=%p)\n",hThread,lpContext);
445 pThread = InternalGetCurrentThread();
447 palError = InternalGetThreadDataFromHandle(
450 0, // THREAD_GET_CONTEXT
455 if (NO_ERROR == palError)
457 if (!pTargetThread->IsDummy())
459 ret = CONTEXT_GetThreadContext(
460 GetCurrentProcessId(),
461 pTargetThread->GetPThreadSelf(),
467 ASSERT("Dummy thread handle passed to GetThreadContext\n");
468 pThread->SetLastError(ERROR_INVALID_HANDLE);
473 pThread->SetLastError(palError);
476 if (NULL != pobjThread)
478 pobjThread->ReleaseReference(pThread);
481 LOGEXIT("GetThreadContext returns ret:%d\n", ret);
482 PERF_EXIT(GetThreadContext);
496 IN CONST CONTEXT *lpContext)
500 CPalThread *pTargetThread;
501 IPalObject *pobjThread = NULL;
504 PERF_ENTRY(SetThreadContext);
505 ENTRY("SetThreadContext (hThread=%p, lpContext=%p)\n",hThread,lpContext);
507 pThread = InternalGetCurrentThread();
509 palError = InternalGetThreadDataFromHandle(
512 0, // THREAD_SET_CONTEXT
517 if (NO_ERROR == palError)
519 if (!pTargetThread->IsDummy())
521 ret = CONTEXT_SetThreadContext(
522 GetCurrentProcessId(),
523 pTargetThread->GetPThreadSelf(),
529 ASSERT("Dummy thread handle passed to SetThreadContext\n");
530 pThread->SetLastError(ERROR_INVALID_HANDLE);
535 pThread->SetLastError(palError);
538 if (NULL != pobjThread)
540 pobjThread->ReleaseReference(pThread);
546 __attribute__((noinline))
547 __attribute__((optnone))
549 ProbeMemory(volatile PBYTE pbBuffer, DWORD cbBuffer, bool fWriteAccess)
551 // Need an throw in this function to fool the C++ runtime into handling the
552 // possible h/w exception below.
553 if (pbBuffer == NULL)
555 throw PAL_SEHException();
558 // Simple one byte at a time probing
561 volatile BYTE read = *pbBuffer;
578 pBuffer : address of memory to validate
579 cbBuffer : size of memory region to validate
580 fWriteAccess : if true, validate writable access, else just readable.
583 true if memory is valid, false if not.
594 // Need to explicit h/w exception holder so to catch them in ProbeMemory
595 CatchHardwareExceptionHolder __catchHardwareException;
597 ProbeMemory((PBYTE)pBuffer, cbBuffer, fWriteAccess);