1 /* Copyright (C) 2010 Wildfire Games.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * Win32 debug support code.
25  */
26 
27 #include "precompiled.h"
28 #include "lib/debug.h"
29 
30 #include "lib/bits.h"
31 #include "lib/sysdep/os/win/win.h"
32 #include "lib/sysdep/os/win/wutil.h"
33 
34 
35 // return 1 if the pointer appears to be totally bogus, otherwise 0.
36 // this check is not authoritative (the pointer may be "valid" but incorrect)
37 // but can be used to filter out obviously wrong values in a portable manner.
debug_IsPointerBogus(const void * p)38 int debug_IsPointerBogus(const void* p)
39 {
40 	if(p < (void*)0x10000)
41 		return true;
42 #if ARCH_AMD64
43 	if(p == (const void*)(uintptr_t)0xCCCCCCCCCCCCCCCCull)
44 		return true;
45 #elif ARCH_IA32
46 	if(p == (const void*)(uintptr_t)0xCCCCCCCCul)
47 		return true;
48 #endif
49 
50 	// notes:
51 	// - we don't check alignment because nothing can be assumed about a
52 	//   string pointer and we mustn't reject any actually valid pointers.
53 	// - nor do we bother checking the address against known stack/heap areas
54 	//   because that doesn't cover everything (e.g. DLLs, VirtualAlloc).
55 	// - cannot use IsBadReadPtr because it accesses the mem
56 	//   (false alarm for reserved address space).
57 
58 	return false;
59 }
60 
61 
debug_IsCodePointer(void * p)62 bool debug_IsCodePointer(void* p)
63 {
64 	uintptr_t addr = (uintptr_t)p;
65 	// totally invalid pointer
66 	if(debug_IsPointerBogus(p))
67 		return false;
68 	// comes before load address
69 	static const HMODULE base = GetModuleHandle(0);
70 	if(addr < (uintptr_t)base)
71 		return false;
72 
73 	return true;
74 }
75 
76 
debug_IsStackPointer(void * p)77 bool debug_IsStackPointer(void* p)
78 {
79 	uintptr_t addr = (uintptr_t)p;
80 	// totally invalid pointer
81 	if(debug_IsPointerBogus(p))
82 		return false;
83 	// not aligned
84 	if(addr % sizeof(void*))
85 		return false;
86 	// out of bounds (note: IA-32 stack grows downwards)
87 	NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
88 	if(!(tib->StackLimit < p && p < tib->StackBase))
89 		return false;
90 
91 	return true;
92 }
93 
94 
debug_puts(const char * text)95 void debug_puts(const char* text)
96 {
97 	OutputDebugStringW(wstring_from_utf8(text).c_str());
98 }
99 
100 
wdbg_printf(const wchar_t * fmt,...)101 void wdbg_printf(const wchar_t* fmt, ...)
102 {
103 	wchar_t buf[1024+1];	// wvsprintfW will truncate to this size
104 	va_list ap;
105 	va_start(ap, fmt);
106 	wvsprintfW(buf, fmt, ap);	// (return value doesn't indicate whether truncation occurred)
107 	va_end(ap);
108 
109 	OutputDebugStringW(buf);
110 }
111 
112 
113 // inform the debugger of the current thread's description, which it then
114 // displays instead of just the thread handle.
115 //
116 // see "Setting a Thread Name (Unmanaged)": http://msdn2.microsoft.com/en-us/library/xcb2z8hs(vs.71).aspx
debug_SetThreadName(const char * name)117 void debug_SetThreadName(const char* name)
118 {
119 	// we pass information to the debugger via a special exception it
120 	// swallows. if not running under one, bail now to avoid
121 	// "first chance exception" warnings.
122 	if(!IsDebuggerPresent())
123 		return;
124 
125 	// presented by Jay Bazuzi (from the VC debugger team) at TechEd 1999.
126 	const struct ThreadNameInfo
127 	{
128 		DWORD type;
129 		const char* name;
130 		DWORD thread_id;	// any valid ID or -1 for current thread
131 		DWORD flags;
132 	}
133 	info = { 0x1000, name, (DWORD)-1, 0 };
134 	__try
135 	{
136 		RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info);
137 	}
138 	__except(EXCEPTION_EXECUTE_HANDLER)
139 	{
140 		// if we get here, the debugger didn't handle the exception.
141 		// this happens if profiling with Dependency Walker; ENSURE
142 		// must not be called because we may be in critical init.
143 	}
144 }
145