Remove obsolete platforms ifdefs from PAL (#8971)
[platform/upstream/coreclr.git] / src / pal / src / debug / debug.cpp
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.
4
5 /*++
6
7
8
9 Module Name:
10
11     debug.c
12
13 Abstract:
14
15     Implementation of Win32 debugging API functions.
16
17 Revision History:
18
19
20
21 --*/
22
23 #ifndef BIT64
24 #undef _LARGEFILE64_SOURCE
25 #undef _FILE_OFFSET_BITS
26 #endif
27
28 #include "pal/dbgmsg.h"
29 SET_DEFAULT_DEBUG_CHANNEL(DEBUG); // some headers have code with asserts, so do this first
30
31 #include "pal/thread.hpp"
32 #include "pal/procobj.hpp"
33 #include "pal/file.hpp"
34
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"
44
45 #include <signal.h>
46 #include <unistd.h>
47 #if HAVE_PROCFS_CTL
48 #include <unistd.h>
49 #elif HAVE_TTRACE // HAVE_PROCFS_CTL
50 #include <sys/ttrace.h>
51 #else // HAVE_TTRACE
52 #include <sys/ptrace.h>
53 #endif  // HAVE_PROCFS_CTL
54 #if HAVE_VM_READ
55 #include <mach/mach.h>
56 #endif  // HAVE_VM_READ
57 #include <errno.h>
58 #include <sys/types.h>
59 #include <sys/wait.h>
60
61 #if HAVE_PROCFS_H
62 #include <procfs.h>
63 #endif // HAVE_PROCFS_H
64
65 #if HAVE_MACH_EXCEPTIONS
66 #include "../exception/machexception.h"
67 #endif // HAVE_MACH_EXCEPTIONS
68
69 using namespace CorUnix;
70
71 extern "C" void DBG_DebugBreak_End();
72
73 #if HAVE_PROCFS_CTL
74 #define CTL_ATTACH      "attach"
75 #define CTL_DETACH      "detach"
76 #define CTL_WAIT        "wait"
77 #endif   // HAVE_PROCFS_CTL
78
79 /* ------------------- Constant definitions ----------------------------------*/
80
81 #if !HAVE_VM_READ && !HAVE_PROCFS_CTL
82 const BOOL DBG_ATTACH       = TRUE;
83 const BOOL DBG_DETACH       = FALSE;
84 #endif
85 static const char PAL_OUTPUTDEBUGSTRING[]    = "PAL_OUTPUTDEBUGSTRING";
86
87 #ifdef _DEBUG
88 #define ENABLE_RUN_ON_DEBUG_BREAK 1
89 #endif // _DEBUG
90
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
94
95 extern "C" {
96
97 /*++
98 Function:
99   FlushInstructionCache
100
101 The FlushInstructionCache function flushes the instruction cache for
102 the specified process.
103
104 Remarks
105
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.
109
110 --*/
111 BOOL
112 PALAPI
113 FlushInstructionCache(
114         IN HANDLE hProcess,
115         IN LPCVOID lpBaseAddress,
116         IN SIZE_T dwSize)
117 {
118     BOOL Ret;
119
120     PERF_ENTRY(FlushInstructionCache);
121     ENTRY("FlushInstructionCache (hProcess=%p, lpBaseAddress=%p dwSize=%d)\
122           \n", hProcess, lpBaseAddress, dwSize);
123
124     if (lpBaseAddress != NULL)
125     {
126         Ret = DBG_FlushInstructionCache(lpBaseAddress, dwSize);
127     }
128     else
129     {
130         Ret = TRUE;
131     }
132
133     LOGEXIT("FlushInstructionCache returns BOOL %d\n", Ret);
134     PERF_EXIT(FlushInstructionCache);
135     return Ret;
136 }
137
138
139 /*++
140 Function:
141   OutputDebugStringA
142
143 See MSDN doc.
144 --*/
145 VOID
146 PALAPI
147 OutputDebugStringA(
148         IN LPCSTR lpOutputString)
149 {
150     PERF_ENTRY(OutputDebugStringA);
151     ENTRY("OutputDebugStringA (lpOutputString=%p (%s))\n",
152           lpOutputString ? lpOutputString : "NULL",
153           lpOutputString ? lpOutputString : "NULL");
154
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)))
161     {
162         fprintf(stderr, "%s", lpOutputString);
163     }
164
165     LOGEXIT("OutputDebugStringA returns\n");
166     PERF_EXIT(OutputDebugStringA);
167 }
168
169 /*++
170 Function:
171   OutputDebugStringW
172
173 See MSDN doc.
174 --*/
175 VOID
176 PALAPI
177 OutputDebugStringW(
178         IN LPCWSTR lpOutputString)
179 {
180     CHAR *lpOutputStringA;
181     int strLen;
182
183     PERF_ENTRY(OutputDebugStringW);
184     ENTRY("OutputDebugStringW (lpOutputString=%p (%S))\n",
185           lpOutputString ? lpOutputString: W16_NULLSTRING,
186           lpOutputString ? lpOutputString: W16_NULLSTRING);
187     
188     if (lpOutputString == NULL) 
189     {
190         OutputDebugStringA("");
191         goto EXIT;
192     }
193
194     if ((strLen = WideCharToMultiByte(CP_ACP, 0, lpOutputString, -1, NULL, 0, 
195                                       NULL, NULL)) 
196         == 0)
197     {
198         ASSERT("failed to get wide chars length\n");
199         SetLastError(ERROR_INTERNAL_ERROR);
200         goto EXIT;
201     }
202
203     /* strLen includes the null terminator */
204     if ((lpOutputStringA = (LPSTR) InternalMalloc((strLen * sizeof(CHAR)))) == NULL)
205     {
206         ERROR("Insufficient memory available !\n");
207         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
208         goto EXIT;
209     }
210
211     if(! WideCharToMultiByte(CP_ACP, 0, lpOutputString, -1, 
212                              lpOutputStringA, strLen, NULL, NULL)) 
213     {
214         ASSERT("failed to convert wide chars to multibytes\n");
215         SetLastError(ERROR_INTERNAL_ERROR);
216         free(lpOutputStringA);
217         goto EXIT;
218     }
219     
220     OutputDebugStringA(lpOutputStringA);
221     free(lpOutputStringA);
222
223 EXIT:
224     LOGEXIT("OutputDebugStringW returns\n");
225     PERF_EXIT(OutputDebugStringW);
226 }
227
228 #ifdef ENABLE_RUN_ON_DEBUG_BREAK
229 /*
230    When DebugBreak() is called, if PAL_RUN_ON_DEBUG_BREAK is set,
231    DebugBreak() will execute whatever command is in there.
232
233    PAL_RUN_ON_DEBUG_BREAK must be no longer than 255 characters.
234
235    This command string inherits the current process's environment,
236    with two additions:
237       PAL_EXE_PID  - the process ID of the current process
238       PAL_EXE_NAME - the name of the executable of the current process
239
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
244    continue.
245
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'
249
250      to print out the stack trace:
251        'pstack $PAL_EXE_PID'
252
253      to invoke the gdb debugger on the process:
254        'set -x; gdb $PAL_EXE_NAME $PAL_EXE_PID'
255
256      to invoke the ddd debugger on the process (requires X11):
257        'set -x; ddd $PAL_EXE_NAME $PAL_EXE_PID'
258 */
259
260 static
261 int
262 run_debug_command (const char *command)
263 {
264     int pid;
265     Volatile<int> spin = 1;
266
267     if (!command) {
268         return 1;
269     }
270
271     printf("Spawning command: %s\n", command);
272     
273     pid = fork();
274     if (pid == -1) {
275         return -1;
276     }
277     if (pid == 0) {
278         const char *argv[4] = { "sh", "-c", command, 0 };
279         execv("/bin/sh", (char **)argv);
280         exit(127);
281     }
282
283     /* We continue either when the spawned process has stopped, or when
284        an attached debugger sets spin to 0 */
285     while (spin != 0) {
286         int status = 0;
287         int ret = waitpid(pid, &status, WNOHANG);
288         if (ret == 0) {
289             int i;
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++)
294                 ;
295         }
296         else if (ret == -1) {
297             if (errno != EINTR) {
298                 return -1;
299             }
300         }
301         else if (WIFEXITED(status)) {
302             return WEXITSTATUS(status);
303         }
304         else {
305             fprintf (stderr, "unexpected return from waitpid\n");
306             return -1;
307         }
308     };
309     return 0;
310 }
311 #endif // ENABLE_RUN_ON_DEBUG_BREAK
312
313 #define PID_TEXT "PAL_EXE_PID="
314 #define EXE_TEXT "PAL_EXE_NAME="
315
316 static
317 int
318 DebugBreakCommand()
319 {
320 #ifdef ENABLE_RUN_ON_DEBUG_BREAK
321     extern MODSTRUCT exe_module;
322
323     char *command_string = EnvironGetenv(PAL_RUN_ON_DEBUG_BREAK);
324     if (command_string)
325     {
326         char pid_buf[sizeof (PID_TEXT) + 32];
327         PathCharString exe_bufString;
328         int libNameLength = 10;
329
330         if (exe_module.lib_name != NULL)
331         {
332             libNameLength = PAL_wcslen(exe_module.lib_name);
333         }
334         
335         SIZE_T dwexe_buf = strlen(EXE_TEXT) + libNameLength + 1;
336         CHAR * exe_buf = exe_bufString.OpenStringBuffer(dwexe_buf);
337         
338         if (NULL == exe_buf)
339         {
340             goto FAILED;
341         }
342
343         if (snprintf (pid_buf, sizeof (pid_buf), PID_TEXT "%d", getpid()) <= 0)
344         {
345             goto FAILED;
346         }
347
348         if (snprintf (exe_buf, sizeof (CHAR) * (dwexe_buf + 1), EXE_TEXT "%ls", (wchar_t *)exe_module.lib_name) <= 0)
349         {
350             goto FAILED;
351         }
352
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 */
357
358         if (!EnvironPutenv (pid_buf, FALSE) || !EnvironPutenv (exe_buf, FALSE))
359         {
360             goto FAILED;
361         }
362
363         if (run_debug_command (command_string))
364         {
365             goto FAILED;
366         }
367
368         free(command_string);
369         return 1;
370     }
371
372     return 0;
373
374 FAILED:
375     if (command_string)
376     {
377         free(command_string);
378     }
379
380     fprintf (stderr, "Failed to execute command: '%s'\n", command_string);
381     return -1;
382 #else // ENABLE_RUN_ON_DEBUG_BREAK
383     return 0;
384 #endif // ENABLE_RUN_ON_DEBUG_BREAK
385 }
386
387 /*++
388 Function:
389   DebugBreak
390
391 See MSDN doc.
392 --*/
393 VOID
394 PALAPI
395 DebugBreak(
396        VOID)
397 {
398     PERF_ENTRY(DebugBreak);
399     ENTRY("DebugBreak()\n");
400
401     if (DebugBreakCommand() <= 0) {
402         // either didn't do anything, or failed
403         TRACE("Calling DBG_DebugBreak\n");
404         DBG_DebugBreak();
405     }
406     
407     LOGEXIT("DebugBreak returns\n");
408     PERF_EXIT(DebugBreak);
409 }
410
411 /*++
412 Function:
413   IsInDebugBreak(addr)
414
415   Returns true if the address is in DBG_DebugBreak.
416
417 --*/
418 BOOL
419 IsInDebugBreak(void *addr)
420 {
421     return (addr >= (void *)DBG_DebugBreak) && (addr <= (void *)DBG_DebugBreak_End);
422 }
423
424 /*++
425 Function:
426   GetThreadContext
427
428 See MSDN doc.
429 --*/
430 BOOL
431 PALAPI
432 GetThreadContext(
433            IN HANDLE hThread,
434            IN OUT LPCONTEXT lpContext)
435 {
436     PAL_ERROR palError;
437     CPalThread *pThread;
438     CPalThread *pTargetThread;
439     IPalObject *pobjThread = NULL;
440     BOOL ret = FALSE;
441     
442     PERF_ENTRY(GetThreadContext);
443     ENTRY("GetThreadContext (hThread=%p, lpContext=%p)\n",hThread,lpContext);
444
445     pThread = InternalGetCurrentThread();
446
447     palError = InternalGetThreadDataFromHandle(
448         pThread,
449         hThread,
450         0, // THREAD_GET_CONTEXT
451         &pTargetThread,
452         &pobjThread
453         );
454
455     if (NO_ERROR == palError)
456     {
457         if (!pTargetThread->IsDummy())
458         {
459             ret = CONTEXT_GetThreadContext(
460                 GetCurrentProcessId(),
461                 pTargetThread->GetPThreadSelf(),
462                 lpContext
463                 );
464         }
465         else
466         {
467             ASSERT("Dummy thread handle passed to GetThreadContext\n");
468             pThread->SetLastError(ERROR_INVALID_HANDLE);
469         }
470     }
471     else
472     {
473         pThread->SetLastError(palError);
474     }
475
476     if (NULL != pobjThread)
477     {
478         pobjThread->ReleaseReference(pThread);
479     }
480     
481     LOGEXIT("GetThreadContext returns ret:%d\n", ret);
482     PERF_EXIT(GetThreadContext);
483     return ret;
484 }
485
486 /*++
487 Function:
488   SetThreadContext
489
490 See MSDN doc.
491 --*/
492 BOOL
493 PALAPI
494 SetThreadContext(
495            IN HANDLE hThread,
496            IN CONST CONTEXT *lpContext)
497 {
498     PAL_ERROR palError;
499     CPalThread *pThread;
500     CPalThread *pTargetThread;
501     IPalObject *pobjThread = NULL;
502     BOOL ret = FALSE;
503     
504     PERF_ENTRY(SetThreadContext);
505     ENTRY("SetThreadContext (hThread=%p, lpContext=%p)\n",hThread,lpContext);
506
507     pThread = InternalGetCurrentThread();
508
509     palError = InternalGetThreadDataFromHandle(
510         pThread,
511         hThread,
512         0, // THREAD_SET_CONTEXT
513         &pTargetThread,
514         &pobjThread
515         );
516
517     if (NO_ERROR == palError)
518     {
519         if (!pTargetThread->IsDummy())
520         {
521             ret = CONTEXT_SetThreadContext(
522                 GetCurrentProcessId(),
523                 pTargetThread->GetPThreadSelf(),
524                 lpContext
525                 );
526         }
527         else
528         {
529             ASSERT("Dummy thread handle passed to SetThreadContext\n");
530             pThread->SetLastError(ERROR_INVALID_HANDLE);
531         }
532     }
533     else
534     {
535         pThread->SetLastError(palError);
536     }
537
538     if (NULL != pobjThread)
539     {
540         pobjThread->ReleaseReference(pThread);
541     }
542         
543     return ret;
544 }
545
546 __attribute__((noinline))
547 __attribute__((optnone))
548 void 
549 ProbeMemory(volatile PBYTE pbBuffer, DWORD cbBuffer, bool fWriteAccess)
550 {
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)
554     {
555         throw PAL_SEHException();
556     }
557
558     // Simple one byte at a time probing
559     while (cbBuffer > 0)
560     {
561         volatile BYTE read = *pbBuffer;
562         if (fWriteAccess)
563         {
564             *pbBuffer = read;
565         }
566         ++pbBuffer;
567         --cbBuffer;
568     }
569 }
570
571 /*++
572 Function:
573   PAL_ProbeMemory
574
575 Abstract
576
577 Parameter
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.
581
582 Return
583   true if memory is valid, false if not.
584 --*/
585 BOOL
586 PALAPI
587 PAL_ProbeMemory(
588     PVOID pBuffer,
589     DWORD cbBuffer,
590     BOOL fWriteAccess)
591 {
592     try
593     {
594         // Need to explicit h/w exception holder so to catch them in ProbeMemory
595         CatchHardwareExceptionHolder __catchHardwareException;
596
597         ProbeMemory((PBYTE)pBuffer, cbBuffer, fWriteAccess);
598     }
599     catch(...)
600     {
601         return FALSE;
602     }
603     return TRUE;
604 }
605
606 } // extern "C"