| 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 | } | 
| 204 |  |