Merge pull request #8697 from sivarv/lockAddFix
[platform/upstream/coreclr.git] / src / debug / shared / utils.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 // Type-safe helper wrapper to get an EXCEPTION_RECORD slot as a CORDB_ADDRESS
6 // 
7 // Arguments:
8 //    pRecord - exception record
9 //    idxSlot - slot to retrieve from.
10 //    
11 // Returns:
12 //    contents of slot as a CordbAddress.
13 CORDB_ADDRESS GetExceptionInfoAsAddress(const EXCEPTION_RECORD * pRecord, int idxSlot)
14 {
15     _ASSERTE((idxSlot >= 0) && (idxSlot < EXCEPTION_MAXIMUM_PARAMETERS));
16
17     // ExceptionInformation is an array of ULONG_PTR.  CORDB_ADDRESS is a 0-extended ULONG64.
18     // So the implicit cast will work here on x86.  On 64-bit, it's basically a nop.
19     return pRecord->ExceptionInformation[idxSlot];
20 }
21
22
23 // Determine if an exception event is a Debug event for this flavor of the CLR.
24 // 
25 // Arguments:
26 //    pRecord - exception record
27 //    pClrBaseAddress - clr Instance ID for which CLR in the target we're checking against.
28 //    
29 // Returns:
30 //    NULL if the exception is not a CLR managed debug event for the given Clr instance.
31 //    Else, address in target process of managed debug event described by the exception (the payload).
32 //    
33 // Notes:
34 //    This decodes events raised by code:Debugger.SendRawEvent
35 //    Anybody can spoof our exception, so this is not a reliably safe method.
36 //    With multiple CLRs in the same process, it's essential to use the proper pClrBaseAddress.
37 CORDB_ADDRESS IsEventDebuggerNotification(
38     const EXCEPTION_RECORD * pRecord,
39     CORDB_ADDRESS pClrBaseAddress
40     )
41 {
42     _ASSERTE(pRecord != NULL);
43
44     // Must specify a CLR instance.
45     _ASSERTE(pClrBaseAddress != NULL);
46
47     // If it's not even our exception code, then it's not ours.
48     if (pRecord->ExceptionCode != CLRDBG_NOTIFICATION_EXCEPTION_CODE)
49     {
50         return NULL;
51     }
52
53     //
54     // Format of an ExceptionInformation parameter is:
55     //  0: cookie (CLRDBG_EXCEPTION_DATA_CHECKSUM)
56     //  1: Base address of mscorwks. This identifies the instance of the CLR.
57     //  2: Target Address of DebuggerIPCEvent, which contains the "real" event.
58     //
59     if (pRecord->NumberParameters != 3)
60     {
61         return NULL;
62     }
63
64     // 1st argument should always be the cookie.
65     // If cookie doesn't match, very likely it's a stray exception that happens to be using
66     // our code.
67     DWORD cookie = (DWORD) pRecord->ExceptionInformation[0];
68     if (cookie != CLRDBG_EXCEPTION_DATA_CHECKSUM)
69     {
70         return NULL;
71     }
72
73     // TODO: We don't do this check in case of non-windows debugging now, because we don't support
74     // multi-instance debugging.
75 #if !defined(FEATURE_DBGIPC_TRANSPORT_VM) && !defined(FEATURE_DBGIPC_TRANSPORT_DI)
76     // If base-address doesn't match, then it's likely an event from another version of the CLR
77     // in the target.
78     // We need to be careful here.  CORDB_ADDRESS is a ULONG64, whereas ExceptionInformation[1] 
79     // is ULONG_PTR.  So on 32-bit, their sizes don't match.
80     CORDB_ADDRESS pTargetBase = GetExceptionInfoAsAddress(pRecord, 1);
81     if (pTargetBase != pClrBaseAddress)
82     {
83         return NULL;        
84     }
85 #endif
86
87     // It passes all the format checks. So now get the payload.
88     CORDB_ADDRESS ptrRemoteManagedEvent = GetExceptionInfoAsAddress(pRecord, 2);
89     
90     return ptrRemoteManagedEvent;
91 }
92
93 #if defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI)
94 void InitEventForDebuggerNotification(DEBUG_EVENT *      pDebugEvent,
95                                       CORDB_ADDRESS      pClrBaseAddress,
96                                       DebuggerIPCEvent * pIPCEvent)
97 {
98     pDebugEvent->dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
99
100     pDebugEvent->u.Exception.dwFirstChance = TRUE;
101     pDebugEvent->u.Exception.ExceptionRecord.ExceptionCode    = CLRDBG_NOTIFICATION_EXCEPTION_CODE;
102     pDebugEvent->u.Exception.ExceptionRecord.ExceptionFlags   = 0;
103     pDebugEvent->u.Exception.ExceptionRecord.ExceptionRecord  = NULL;
104     pDebugEvent->u.Exception.ExceptionRecord.ExceptionAddress = 0;
105
106     //
107     // Format of an ExceptionInformation parameter is:
108     //  0: cookie (CLRDBG_EXCEPTION_DATA_CHECKSUM)
109     //  1: Base address of mscorwks. This identifies the instance of the CLR.
110     //  2: Target Address of DebuggerIPCEvent, which contains the "real" event.
111     //
112     pDebugEvent->u.Exception.ExceptionRecord.NumberParameters = 3;
113     pDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0] = CLRDBG_EXCEPTION_DATA_CHECKSUM;
114     pDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[1] = (ULONG_PTR)CORDB_ADDRESS_TO_PTR(pClrBaseAddress);
115     pDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[2] = (ULONG_PTR)pIPCEvent;
116
117     _ASSERTE(IsEventDebuggerNotification(&(pDebugEvent->u.Exception.ExceptionRecord), pClrBaseAddress) == 
118              PTR_TO_CORDB_ADDRESS(pIPCEvent));
119 }
120 #endif // defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI)
121
122 //-----------------------------------------------------------------------------
123 // Helper to get the proper decorated name
124 // Caller ensures that pBufSize is large enough. We'll assert just to check,
125 // but no runtime failure.
126 // pBuf - the output buffer to write the decorated name in
127 // cBufSizeInChars - the size of the buffer in characters, including the null.
128 // pPrefx - The undecorated name of the event.
129 //-----------------------------------------------------------------------------
130 void GetPidDecoratedName(__out_z __out_ecount(cBufSizeInChars) WCHAR * pBuf, int cBufSizeInChars, const WCHAR * pPrefix, DWORD pid)
131 {
132     const WCHAR szGlobal[] = W("Global\\");
133     int szGlobalLen;
134     szGlobalLen = NumItems(szGlobal) - 1;
135
136     // Caller should always give us a big enough buffer.
137     _ASSERTE(cBufSizeInChars > (int) wcslen(pPrefix) + szGlobalLen);
138
139     // PERF: We are no longer calling GetSystemMetrics in an effort to prevent
140     //       superfluous DLL loading on startup.  Instead, we're prepending
141     //       "Global\" to named kernel objects if we are on NT5 or above.  The
142     //       only bad thing that results from this is that you can't debug
143     //       cross-session on NT4.  Big bloody deal.
144     wcscpy_s(pBuf, cBufSizeInChars, szGlobal);
145     pBuf += szGlobalLen;
146     cBufSizeInChars -= szGlobalLen;
147
148     int ret;
149     ret = _snwprintf_s(pBuf, cBufSizeInChars, _TRUNCATE, pPrefix, pid);
150
151     // Since this is all determined at compile time, we know we should have enough buffer.
152     _ASSERTE (ret != STRUNCATE);
153 }
154
155 //-----------------------------------------------------------------------------
156 // The 'internal' version of our IL to Native map (the DebuggerILToNativeMap struct)
157 // has an extra field - ICorDebugInfo::SourceTypes source.  The 'external/user-visible'
158 // version (COR_DEBUG_IL_TO_NATIVE_MAP) lacks that field, so we need to translate our
159 // internal version to the external version.
160 // "Export" seemed more succinct than "CopyInternalToExternalILToNativeMap" :)
161 //-----------------------------------------------------------------------------
162 void ExportILToNativeMap(ULONG32 cMap,             // [in] Min size of mapExt, mapInt
163              COR_DEBUG_IL_TO_NATIVE_MAP mapExt[],  // [in] Filled in here
164              struct DebuggerILToNativeMap mapInt[],// [in] Source of info
165              SIZE_T sizeOfCode)                    // [in] Total size of method (bytes)
166 {
167     ULONG32 iMap;
168     _ASSERTE(mapExt != NULL);
169     _ASSERTE(mapInt != NULL);
170
171     for(iMap=0; iMap < cMap; iMap++)
172     {
173         mapExt[iMap].ilOffset = mapInt[iMap].ilOffset ;
174         mapExt[iMap].nativeStartOffset = mapInt[iMap].nativeStartOffset ;
175         mapExt[iMap].nativeEndOffset = mapInt[iMap].nativeEndOffset ;
176
177         // An element that has an end offset of zero, means "till the end of
178         // the method".  Pretty this up so that customers don't have to care about
179         // this.
180         if ((DWORD)mapInt[iMap].source & (DWORD)ICorDebugInfo::NATIVE_END_OFFSET_UNKNOWN)
181         {
182             mapExt[iMap].nativeEndOffset = (ULONG32)sizeOfCode;
183         }
184
185 #if defined(_DEBUG)
186         {
187             // UnsafeGetConfigDWORD
188             SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
189             static int fReturnSourceTypeForTesting = -1;
190             if (fReturnSourceTypeForTesting == -1)
191                 fReturnSourceTypeForTesting = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ReturnSourceTypeForTesting);
192
193             if (fReturnSourceTypeForTesting)
194             {
195                 // Steal the most significant four bits from the native end offset for the source type.
196                 _ASSERTE( (mapExt[iMap].nativeEndOffset >> 28) == 0x0 );
197                 _ASSERTE( (ULONG32)(mapInt[iMap].source) < 0xF );
198                 mapExt[iMap].nativeEndOffset |= ((ULONG32)(mapInt[iMap].source) << 28);
199             }
200         }
201 #endif // _DEBUG
202     }
203 }