summaryrefslogtreecommitdiff
path: root/src/debug/shared/utils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/shared/utils.cpp')
-rw-r--r--src/debug/shared/utils.cpp203
1 files changed, 203 insertions, 0 deletions
diff --git a/src/debug/shared/utils.cpp b/src/debug/shared/utils.cpp
new file mode 100644
index 0000000000..cc6727396d
--- /dev/null
+++ b/src/debug/shared/utils.cpp
@@ -0,0 +1,203 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// Type-safe helper wrapper to get an EXCEPTION_RECORD slot as a CORDB_ADDRESS
+//
+// Arguments:
+// pRecord - exception record
+// idxSlot - slot to retrieve from.
+//
+// Returns:
+// contents of slot as a CordbAddress.
+CORDB_ADDRESS GetExceptionInfoAsAddress(const EXCEPTION_RECORD * pRecord, int idxSlot)
+{
+ _ASSERTE((idxSlot >= 0) && (idxSlot < EXCEPTION_MAXIMUM_PARAMETERS));
+
+ // ExceptionInformation is an array of ULONG_PTR. CORDB_ADDRESS is a 0-extended ULONG64.
+ // So the implicit cast will work here on x86. On 64-bit, it's basically a nop.
+ return pRecord->ExceptionInformation[idxSlot];
+}
+
+
+// Determine if an exception event is a Debug event for this flavor of the CLR.
+//
+// Arguments:
+// pRecord - exception record
+// pClrBaseAddress - clr Instance ID for which CLR in the target we're checking against.
+//
+// Returns:
+// NULL if the exception is not a CLR managed debug event for the given Clr instance.
+// Else, address in target process of managed debug event described by the exception (the payload).
+//
+// Notes:
+// This decodes events raised by code:Debugger.SendRawEvent
+// Anybody can spoof our exception, so this is not a reliably safe method.
+// With multiple CLRs in the same process, it's essential to use the proper pClrBaseAddress.
+CORDB_ADDRESS IsEventDebuggerNotification(
+ const EXCEPTION_RECORD * pRecord,
+ CORDB_ADDRESS pClrBaseAddress
+ )
+{
+ _ASSERTE(pRecord != NULL);
+
+ // Must specify a CLR instance.
+ _ASSERTE(pClrBaseAddress != NULL);
+
+ // If it's not even our exception code, then it's not ours.
+ if (pRecord->ExceptionCode != CLRDBG_NOTIFICATION_EXCEPTION_CODE)
+ {
+ return NULL;
+ }
+
+ //
+ // Format of an ExceptionInformation parameter is:
+ // 0: cookie (CLRDBG_EXCEPTION_DATA_CHECKSUM)
+ // 1: Base address of mscorwks. This identifies the instance of the CLR.
+ // 2: Target Address of DebuggerIPCEvent, which contains the "real" event.
+ //
+ if (pRecord->NumberParameters != 3)
+ {
+ return NULL;
+ }
+
+ // 1st argument should always be the cookie.
+ // If cookie doesn't match, very likely it's a stray exception that happens to be using
+ // our code.
+ DWORD cookie = (DWORD) pRecord->ExceptionInformation[0];
+ if (cookie != CLRDBG_EXCEPTION_DATA_CHECKSUM)
+ {
+ return NULL;
+ }
+
+ // TODO: We don't do this check in case of non-windows debugging now, because we don't support
+ // multi-instance debugging.
+#if !defined(FEATURE_DBGIPC_TRANSPORT_VM) && !defined(FEATURE_DBGIPC_TRANSPORT_DI)
+ // If base-address doesn't match, then it's likely an event from another version of the CLR
+ // in the target.
+ // We need to be careful here. CORDB_ADDRESS is a ULONG64, whereas ExceptionInformation[1]
+ // is ULONG_PTR. So on 32-bit, their sizes don't match.
+ CORDB_ADDRESS pTargetBase = GetExceptionInfoAsAddress(pRecord, 1);
+ if (pTargetBase != pClrBaseAddress)
+ {
+ return NULL;
+ }
+#endif
+
+ // It passes all the format checks. So now get the payload.
+ CORDB_ADDRESS ptrRemoteManagedEvent = GetExceptionInfoAsAddress(pRecord, 2);
+
+ return ptrRemoteManagedEvent;
+}
+
+#if defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI)
+void InitEventForDebuggerNotification(DEBUG_EVENT * pDebugEvent,
+ CORDB_ADDRESS pClrBaseAddress,
+ DebuggerIPCEvent * pIPCEvent)
+{
+ pDebugEvent->dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
+
+ pDebugEvent->u.Exception.dwFirstChance = TRUE;
+ pDebugEvent->u.Exception.ExceptionRecord.ExceptionCode = CLRDBG_NOTIFICATION_EXCEPTION_CODE;
+ pDebugEvent->u.Exception.ExceptionRecord.ExceptionFlags = 0;
+ pDebugEvent->u.Exception.ExceptionRecord.ExceptionRecord = NULL;
+ pDebugEvent->u.Exception.ExceptionRecord.ExceptionAddress = 0;
+
+ //
+ // Format of an ExceptionInformation parameter is:
+ // 0: cookie (CLRDBG_EXCEPTION_DATA_CHECKSUM)
+ // 1: Base address of mscorwks. This identifies the instance of the CLR.
+ // 2: Target Address of DebuggerIPCEvent, which contains the "real" event.
+ //
+ pDebugEvent->u.Exception.ExceptionRecord.NumberParameters = 3;
+ pDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0] = CLRDBG_EXCEPTION_DATA_CHECKSUM;
+ pDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[1] = (ULONG_PTR)CORDB_ADDRESS_TO_PTR(pClrBaseAddress);
+ pDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[2] = (ULONG_PTR)pIPCEvent;
+
+ _ASSERTE(IsEventDebuggerNotification(&(pDebugEvent->u.Exception.ExceptionRecord), pClrBaseAddress) ==
+ PTR_TO_CORDB_ADDRESS(pIPCEvent));
+}
+#endif // defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI)
+
+//-----------------------------------------------------------------------------
+// Helper to get the proper decorated name
+// Caller ensures that pBufSize is large enough. We'll assert just to check,
+// but no runtime failure.
+// pBuf - the output buffer to write the decorated name in
+// cBufSizeInChars - the size of the buffer in characters, including the null.
+// pPrefx - The undecorated name of the event.
+//-----------------------------------------------------------------------------
+void GetPidDecoratedName(__out_z __out_ecount(cBufSizeInChars) WCHAR * pBuf, int cBufSizeInChars, const WCHAR * pPrefix, DWORD pid)
+{
+ const WCHAR szGlobal[] = W("Global\\");
+ int szGlobalLen;
+ szGlobalLen = NumItems(szGlobal) - 1;
+
+ // Caller should always give us a big enough buffer.
+ _ASSERTE(cBufSizeInChars > (int) wcslen(pPrefix) + szGlobalLen);
+
+ // PERF: We are no longer calling GetSystemMetrics in an effort to prevent
+ // superfluous DLL loading on startup. Instead, we're prepending
+ // "Global\" to named kernel objects if we are on NT5 or above. The
+ // only bad thing that results from this is that you can't debug
+ // cross-session on NT4. Big bloody deal.
+ wcscpy_s(pBuf, cBufSizeInChars, szGlobal);
+ pBuf += szGlobalLen;
+ cBufSizeInChars -= szGlobalLen;
+
+ int ret;
+ ret = _snwprintf_s(pBuf, cBufSizeInChars, _TRUNCATE, pPrefix, pid);
+
+ // Since this is all determined at compile time, we know we should have enough buffer.
+ _ASSERTE (ret != STRUNCATE);
+}
+
+//-----------------------------------------------------------------------------
+// The 'internal' version of our IL to Native map (the DebuggerILToNativeMap struct)
+// has an extra field - ICorDebugInfo::SourceTypes source. The 'external/user-visible'
+// version (COR_DEBUG_IL_TO_NATIVE_MAP) lacks that field, so we need to translate our
+// internal version to the external version.
+// "Export" seemed more succinct than "CopyInternalToExternalILToNativeMap" :)
+//-----------------------------------------------------------------------------
+void ExportILToNativeMap(ULONG32 cMap, // [in] Min size of mapExt, mapInt
+ COR_DEBUG_IL_TO_NATIVE_MAP mapExt[], // [in] Filled in here
+ struct DebuggerILToNativeMap mapInt[],// [in] Source of info
+ SIZE_T sizeOfCode) // [in] Total size of method (bytes)
+{
+ ULONG32 iMap;
+ _ASSERTE(mapExt != NULL);
+ _ASSERTE(mapInt != NULL);
+
+ for(iMap=0; iMap < cMap; iMap++)
+ {
+ mapExt[iMap].ilOffset = mapInt[iMap].ilOffset ;
+ mapExt[iMap].nativeStartOffset = mapInt[iMap].nativeStartOffset ;
+ mapExt[iMap].nativeEndOffset = mapInt[iMap].nativeEndOffset ;
+
+ // An element that has an end offset of zero, means "till the end of
+ // the method". Pretty this up so that customers don't have to care about
+ // this.
+ if ((DWORD)mapInt[iMap].source & (DWORD)ICorDebugInfo::NATIVE_END_OFFSET_UNKNOWN)
+ {
+ mapExt[iMap].nativeEndOffset = (ULONG32)sizeOfCode;
+ }
+
+#if defined(_DEBUG)
+ {
+ // UnsafeGetConfigDWORD
+ SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
+ static int fReturnSourceTypeForTesting = -1;
+ if (fReturnSourceTypeForTesting == -1)
+ fReturnSourceTypeForTesting = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ReturnSourceTypeForTesting);
+
+ if (fReturnSourceTypeForTesting)
+ {
+ // Steal the most significant four bits from the native end offset for the source type.
+ _ASSERTE( (mapExt[iMap].nativeEndOffset >> 28) == 0x0 );
+ _ASSERTE( (ULONG32)(mapInt[iMap].source) < 0xF );
+ mapExt[iMap].nativeEndOffset |= ((ULONG32)(mapInt[iMap].source) << 28);
+ }
+ }
+#endif // _DEBUG
+ }
+}