|
//------------------------------------------------------------------------------
// <copyright file="Debug.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.Util {
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Reflection;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using System.Runtime.Versioning;
internal static class Debug {
internal const string TAG_INTERNAL = "Internal";
internal const string TAG_EXTERNAL = "External";
internal const string TAG_ALL = "*";
internal const string DATE_FORMAT = @"yyyy/MM/dd HH:mm:ss.ffff";
internal const string TIME_FORMAT = @"HH:mm:ss:ffff";
// Some of these APIs must be always available, not #ifdefed away.
[SuppressUnmanagedCodeSecurity]
private static partial class NativeMethods {
[DllImport("kernel32.dll")]
internal extern static bool IsDebuggerPresent();
}
#if DBG
private static partial class NativeMethods {
[DllImport("kernel32.dll")]
internal extern static void DebugBreak();
[DllImport("kernel32.dll")]
internal extern static int GetCurrentProcessId();
[DllImport("kernel32.dll")]
internal extern static int GetCurrentThreadId();
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal extern static IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", SetLastError=true)]
internal extern static bool TerminateProcess(HandleRef processHandle, int exitCode);
[DllImport("kernel32.dll", CharSet=CharSet.Auto, BestFitMapping=false)]
internal extern static void OutputDebugString(string message);
internal const int PM_NOREMOVE = 0x0000;
internal const int PM_REMOVE = 0x0001;
[StructLayout(LayoutKind.Sequential)]
internal struct MSG {
internal IntPtr hwnd;
internal int message;
internal IntPtr wParam;
internal IntPtr lParam;
internal int time;
internal int pt_x;
internal int pt_y;
}
[DllImport("user32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)]
internal extern static bool PeekMessage([In, Out] ref MSG msg, HandleRef hwnd, int msgMin, int msgMax, int remove);
internal const int
MB_OK = 0x00000000,
MB_OKCANCEL = 0x00000001,
MB_ABORTRETRYIGNORE = 0x00000002,
MB_YESNOCANCEL = 0x00000003,
MB_YESNO = 0x00000004,
MB_RETRYCANCEL = 0x00000005,
MB_ICONHAND = 0x00000010,
MB_ICONQUESTION = 0x00000020,
MB_ICONEXCLAMATION = 0x00000030,
MB_ICONASTERISK = 0x00000040,
MB_USERICON = 0x00000080,
MB_ICONWARNING = 0x00000030,
MB_ICONERROR = 0x00000010,
MB_ICONINFORMATION = 0x00000040,
MB_DEFBUTTON1 = 0x00000000,
MB_DEFBUTTON2 = 0x00000100,
MB_DEFBUTTON3 = 0x00000200,
MB_DEFBUTTON4 = 0x00000300,
MB_APPLMODAL = 0x00000000,
MB_SYSTEMMODAL = 0x00001000,
MB_TASKMODAL = 0x00002000,
MB_HELP = 0x00004000,
MB_NOFOCUS = 0x00008000,
MB_SETFOREGROUND = 0x00010000,
MB_DEFAULT_DESKTOP_ONLY = 0x00020000,
MB_TOPMOST = 0x00040000,
MB_RIGHT = 0x00080000,
MB_RTLREADING = 0x00100000,
MB_SERVICE_NOTIFICATION = 0x00200000,
MB_SERVICE_NOTIFICATION_NT3X = 0x00040000,
MB_TYPEMASK = 0x0000000F,
MB_ICONMASK = 0x000000F0,
MB_DEFMASK = 0x00000F00,
MB_MODEMASK = 0x00003000,
MB_MISCMASK = 0x0000C000;
internal const int
IDOK = 1,
IDCANCEL = 2,
IDABORT = 3,
IDRETRY = 4,
IDIGNORE = 5,
IDYES = 6,
IDNO = 7,
IDCLOSE = 8,
IDHELP = 9;
[DllImport("user32.dll", CharSet=CharSet.Auto, BestFitMapping=false)]
internal extern static int MessageBox(HandleRef hWnd, string text, string caption, int type);
internal static readonly IntPtr HKEY_LOCAL_MACHINE = unchecked((IntPtr)(int)0x80000002);
internal const int READ_CONTROL = 0x00020000;
internal const int STANDARD_RIGHTS_READ = READ_CONTROL;
internal const int SYNCHRONIZE = 0x00100000;
internal const int KEY_QUERY_VALUE = 0x0001;
internal const int KEY_ENUMERATE_SUB_KEYS = 0x0008;
internal const int KEY_NOTIFY = 0x0010;
internal const int KEY_READ = ((STANDARD_RIGHTS_READ |
KEY_QUERY_VALUE |
KEY_ENUMERATE_SUB_KEYS |
KEY_NOTIFY)
&
(~SYNCHRONIZE));
internal const int REG_NOTIFY_CHANGE_NAME = 1;
internal const int REG_NOTIFY_CHANGE_LAST_SET = 4;
[DllImport("advapi32.dll", CharSet=CharSet.Auto, BestFitMapping=false, SetLastError=true)]
internal extern static int RegOpenKeyEx(IntPtr hKey, string lpSubKey, int ulOptions, int samDesired, out SafeRegistryHandle hkResult);
[DllImport("advapi32.dll", ExactSpelling=true, SetLastError=true)]
internal extern static int RegNotifyChangeKeyValue(SafeRegistryHandle hKey, bool watchSubTree, uint notifyFilter, SafeWaitHandle regEvent, bool async);
}
private class SafeRegistryHandle : SafeHandleZeroOrMinusOneIsInvalid {
// Note: Officially -1 is the recommended invalid handle value for
// registry keys, but we'll also get back 0 as an invalid handle from
// RegOpenKeyEx.
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
[ResourceExposure(ResourceScope.Machine)]
internal SafeRegistryHandle() : base(true) {}
[DllImport("advapi32.dll"),
SuppressUnmanagedCodeSecurity,
ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
private static extern int RegCloseKey(IntPtr hKey);
override protected bool ReleaseHandle()
{
// Returns a Win32 error code, 0 for success
int r = RegCloseKey(handle);
return r == 0;
}
}
private enum TagValue {
Disabled = 0,
Enabled = 1,
Break = 2,
Min = Disabled,
Max = Break,
}
private const string TAG_ASSERT = "Assert";
private const string TAG_ASSERT_BREAK = "AssertBreak";
private const string TAG_DEBUG_VERBOSE = "DebugVerbose";
private const string TAG_DEBUG_MONITOR = "DebugMonitor";
private const string TAG_DEBUG_PREFIX = "DebugPrefix";
private const string TAG_DEBUG_THREAD_PREFIX = "DebugThreadPrefix";
private const string PRODUCT = "Microsoft .NET Framework";
private const string COMPONENT = "System.Web";
private static string s_regKeyName = @"Software\Microsoft\ASP.NET\Debug";
private static string s_listenKeyName = @"Software\Microsoft";
private static bool s_assert;
private static bool s_assertBreak;
private static bool s_includePrefix;
private static bool s_includeThreadPrefix;
private static bool s_monitor;
private static object s_lock;
private static volatile bool s_inited;
private static ReadOnlyCollection<Tag> s_tagDefaults;
private static List<Tag> s_tags;
private static AutoResetEvent s_notifyEvent;
private static RegisteredWaitHandle s_waitHandle;
private static SafeRegistryHandle s_regHandle;
private static bool s_stopMonitoring;
private static Hashtable s_tableAlwaysValidate;
private static Type[] s_DumpArgs;
private static Type[] s_ValidateArgs;
private class Tag {
string _name;
TagValue _value;
int _prefixLength;
internal Tag(string name, TagValue value) {
_name = name;
_value = value;
if (_name[_name.Length - 1] == '*') {
_prefixLength = _name.Length - 1;
}
else {
_prefixLength = -1;
}
}
internal string Name {
get {return _name;}
}
internal TagValue Value {
get {return _value;}
}
internal int PrefixLength {
get {return _prefixLength;}
}
}
static Debug() {
s_lock = new object();
}
private static void EnsureInit() {
bool continueInit = false;
if (!s_inited) {
lock (s_lock) {
if (!s_inited) {
s_tableAlwaysValidate = new Hashtable();
s_DumpArgs = new Type[1] {typeof(string)};
s_ValidateArgs = new Type[0];
List<Tag> tagDefaults = new List<Tag>();
tagDefaults.Add(new Tag(TAG_ALL, TagValue.Disabled));
tagDefaults.Add(new Tag(TAG_INTERNAL, TagValue.Enabled));
tagDefaults.Add(new Tag(TAG_EXTERNAL, TagValue.Enabled));
tagDefaults.Add(new Tag(TAG_ASSERT, TagValue.Break));
tagDefaults.Add(new Tag(TAG_ASSERT_BREAK, TagValue.Disabled));
tagDefaults.Add(new Tag(TAG_DEBUG_VERBOSE, TagValue.Enabled));
tagDefaults.Add(new Tag(TAG_DEBUG_MONITOR, TagValue.Enabled));
tagDefaults.Add(new Tag(TAG_DEBUG_PREFIX, TagValue.Enabled));
tagDefaults.Add(new Tag(TAG_DEBUG_THREAD_PREFIX, TagValue.Enabled));
s_tagDefaults = tagDefaults.AsReadOnly();
s_tags = new List<Tag>(s_tagDefaults);
GetBuiltinTagValues();
continueInit = true;
s_inited = true;
}
}
}
// Work to do outside the init lock.
if (continueInit) {
ReadTagsFromRegistry();
Trace(TAG_DEBUG_VERBOSE, "Debugging package initialized");
// Need to read tags before starting to monitor in order to get TAG_DEBUG_MONITOR
StartRegistryMonitor();
}
}
private static bool StringEqualsIgnoreCase(string s1, string s2) {
return StringComparer.OrdinalIgnoreCase.Equals(s1, s2);
}
[RegistryPermission(SecurityAction.Assert, Unrestricted=true)]
private static void WriteTagsToRegistry() {
try {
using (RegistryKey key = Registry.LocalMachine.CreateSubKey(s_regKeyName)) {
List<Tag> tags = s_tags;
foreach (Tag tag in tags) {
key.SetValue(tag.Name, tag.Value, RegistryValueKind.DWord);
}
}
}
catch {
}
}
private static void GetBuiltinTagValues() {
// Use GetTagValue instead of IsTagEnabled because it does not call EnsureInit
// and potentially recurse.
s_assert = (GetTagValue(TAG_ASSERT) != TagValue.Disabled);
s_assertBreak = (GetTagValue(TAG_ASSERT_BREAK) != TagValue.Disabled);
s_includePrefix = (GetTagValue(TAG_DEBUG_PREFIX) != TagValue.Disabled);
s_includeThreadPrefix = (GetTagValue(TAG_DEBUG_THREAD_PREFIX) != TagValue.Disabled);
s_monitor = (GetTagValue(TAG_DEBUG_MONITOR) != TagValue.Disabled);
}
[RegistryPermission(SecurityAction.Assert, Unrestricted=true)]
private static void ReadTagsFromRegistry() {
lock (s_lock) {
try {
List<Tag> tags = new List<Tag>(s_tagDefaults);
string[] names = null;
bool writeTags = false;
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(s_regKeyName, false)) {
if (key != null) {
names = key.GetValueNames();
foreach (string name in names) {
TagValue value = TagValue.Disabled;
try {
TagValue keyvalue = (TagValue) key.GetValue(name);
if (TagValue.Min <= keyvalue && keyvalue <= TagValue.Max) {
value = keyvalue;
}
else {
writeTags = true;
}
}
catch {
writeTags = true;
}
// Add tag to list, making sure it is unique.
Tag tag = new Tag(name, (TagValue) value);
bool found = false;
for (int i = 0; i < s_tagDefaults.Count; i++) {
if (StringEqualsIgnoreCase(name, tags[i].Name)) {
found = true;
tags[i] = tag;
break;
}
}
if (!found) {
tags.Add(tag);
}
}
}
}
s_tags = tags;
GetBuiltinTagValues();
// Write tags out if there was an invalid value or
// not all default tags are present.
if (writeTags || (names != null && names.Length < tags.Count)) {
WriteTagsToRegistry();
}
}
catch {
s_tags = new List<Tag>(s_tagDefaults);
}
}
}
private static void StartRegistryMonitor() {
if (!s_monitor) {
Trace(TAG_DEBUG_VERBOSE, "WARNING: Registry monitoring disabled, changes during process execution will not be recognized.");
return;
}
Trace(TAG_DEBUG_VERBOSE, "Monitoring registry key " + s_listenKeyName + " for changes.");
// Event used to notify of changes.
s_notifyEvent = new AutoResetEvent(false);
// Register a wait on the event.
s_waitHandle = ThreadPool.RegisterWaitForSingleObject(s_notifyEvent, OnRegChangeKeyValue, null, -1, false);
// Monitor the registry.
MonitorRegistryForOneChange();
}
private static void StopRegistryMonitor() {
// Cleanup allocated handles
s_stopMonitoring = true;
if (s_regHandle != null) {
s_regHandle.Close();
s_regHandle = null;
}
if (s_waitHandle != null) {
s_waitHandle.Unregister(s_notifyEvent);
s_waitHandle = null;
}
if (s_notifyEvent != null) {
s_notifyEvent.Close();
s_notifyEvent = null;
}
Trace(TAG_DEBUG_VERBOSE, "Registry monitoring stopped.");
}
public static void OnRegChangeKeyValue(object state, bool timedOut) {
if (!s_stopMonitoring) {
if (timedOut) {
StopRegistryMonitor();
}
else {
// Monitor again
MonitorRegistryForOneChange();
// Once we're monitoring, read the changes to the registry.
// We have to do this after we start monitoring in order
// to catch all changes to the registry.
ReadTagsFromRegistry();
}
}
}
private static void MonitorRegistryForOneChange() {
// Close the open reg handle
if (s_regHandle != null) {
s_regHandle.Close();
s_regHandle = null;
}
// Open the reg key
int result = NativeMethods.RegOpenKeyEx(NativeMethods.HKEY_LOCAL_MACHINE, s_listenKeyName, 0, NativeMethods.KEY_READ, out s_regHandle);
if (result != 0) {
StopRegistryMonitor();
return;
}
// Listen for changes.
result = NativeMethods.RegNotifyChangeKeyValue(
s_regHandle,
true,
NativeMethods.REG_NOTIFY_CHANGE_NAME | NativeMethods.REG_NOTIFY_CHANGE_LAST_SET,
s_notifyEvent.SafeWaitHandle,
true);
if (result != 0) {
StopRegistryMonitor();
}
}
private static Tag FindMatchingTag(string name, bool exact) {
List<Tag> tags = s_tags;
// Look for exact match first
foreach (Tag tag in tags) {
if (StringEqualsIgnoreCase(name, tag.Name)) {
return tag;
}
}
if (exact) {
return null;
}
Tag longestTag = null;
int longestPrefix = -1;
foreach (Tag tag in tags) {
if ( tag.PrefixLength > longestPrefix &&
0 == string.Compare(name, 0, tag.Name, 0, tag.PrefixLength, StringComparison.OrdinalIgnoreCase)) {
longestTag = tag;
longestPrefix = tag.PrefixLength;
}
}
return longestTag;
}
private static TagValue GetTagValue(string name) {
Tag tag = FindMatchingTag(name, false);
if (tag != null) {
return tag.Value;
}
else {
return TagValue.Disabled;
}
}
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
private static bool TraceBreak(string tagName, string message, Exception e, bool includePrefix) {
EnsureInit();
TagValue tagValue = GetTagValue(tagName);
if (tagValue == TagValue.Disabled) {
return false;
}
bool isAssert = object.ReferenceEquals(tagName, TAG_ASSERT);
if (isAssert) {
tagName = "";
}
string exceptionMessage = null;
if (e != null) {
string httpCode = null;
string errorCode = null;
if (e is HttpException) {
httpCode = " _httpCode=" + ((HttpException)e).GetHttpCode().ToString(CultureInfo.InvariantCulture) + " ";
}
if (e is ExternalException) {
// note that HttpExceptions are ExternalExceptions
errorCode = "_hr=0x" + ((ExternalException)e).ErrorCode.ToString("x", CultureInfo.InvariantCulture);
}
// Use e.ToString() in order to get inner exception
if (errorCode != null) {
exceptionMessage = "Exception " + e.ToString() + "\n" + httpCode + errorCode;
}
else {
exceptionMessage = "Exception " + e.ToString();
}
}
if (string.IsNullOrEmpty(message) & exceptionMessage != null) {
message = exceptionMessage;
exceptionMessage = null;
}
string traceFormat;
int idThread = 0;
int idProcess = 0;
if (!includePrefix || !s_includePrefix) {
traceFormat = "{4}\n{5}";
}
else {
if (s_includeThreadPrefix) {
idThread = NativeMethods.GetCurrentThreadId();
idProcess = NativeMethods.GetCurrentProcessId();
traceFormat = "[0x{0:x}.{1:x} {2} {3}] {4}\n{5}";
}
else {
traceFormat = "[{2} {3}] {4}\n{5}";
}
}
string suffix = "";
if (exceptionMessage != null) {
suffix += exceptionMessage + "\n";
}
bool doBreak = (tagValue == TagValue.Break);
if (doBreak && !isAssert) {
suffix += "Breaking into debugger...\n";
}
string traceMessage = string.Format(CultureInfo.InvariantCulture, traceFormat, idProcess, idThread, COMPONENT, tagName, message, suffix);
NativeMethods.OutputDebugString(traceMessage);
return doBreak;
}
private class MBResult {
internal int Result;
}
[ResourceExposure(ResourceScope.None)]
static bool DoAssert(string message) {
if (!s_assert) {
return false;
}
// Skip 2 frames - one for this function, one for
// the public Assert function that called this function.
System.Diagnostics.StackFrame frame = new System.Diagnostics.StackFrame(2, true);
System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(2, true);
string fileName = frame.GetFileName();
int lineNumber = frame.GetFileLineNumber();
string traceFormat;
if (!string.IsNullOrEmpty(fileName)) {
traceFormat = "ASSERTION FAILED: {0}\nFile: {1}:{2}\nStack trace:\n{3}";
}
else {
traceFormat = "ASSERTION FAILED: {0}\nStack trace:\n{3}";
}
string traceMessage = string.Format(CultureInfo.InvariantCulture, traceFormat, message, fileName, lineNumber, trace.ToString());
if (!TraceBreak(TAG_ASSERT, traceMessage, null, true)) {
// If the value of "Assert" is not TagValue.Break, then don't even ask user.
return false;
}
if (s_assertBreak) {
// If "AssertBreak" is enabled, then always break.
return true;
}
string dialogFormat;
if (!string.IsNullOrEmpty(fileName)) {
dialogFormat =
@"Failed expression: {0}
File: {1}:{2}
Component: {3}
PID={4} TID={5}
Stack trace:
{6}
A=Exit process R=Debug I=Continue";
}
else {
dialogFormat =
@"Failed expression: {0}
(no file information available)
Component: {3}
PID={4} TID={5}
Stack trace:
{6}
A=Exit process R=Debug I=Continue";
}
string dialogMessage = string.Format(
CultureInfo.InvariantCulture,
dialogFormat,
message,
fileName, lineNumber,
COMPONENT,
NativeMethods.GetCurrentProcessId(), NativeMethods.GetCurrentThreadId(),
trace.ToString());
MBResult mbResult = new MBResult();
Thread thread = new Thread(
delegate() {
for (int i = 0; i < 100; i++) {
NativeMethods.MSG msg = new NativeMethods.MSG();
NativeMethods.PeekMessage(ref msg, new HandleRef(mbResult, IntPtr.Zero), 0, 0, NativeMethods.PM_REMOVE);
}
mbResult.Result = NativeMethods.MessageBox(new HandleRef(mbResult, IntPtr.Zero), dialogMessage, PRODUCT + " Assertion",
NativeMethods.MB_SERVICE_NOTIFICATION |
NativeMethods.MB_TOPMOST |
NativeMethods.MB_ABORTRETRYIGNORE |
NativeMethods.MB_ICONEXCLAMATION);
}
);
thread.Start();
thread.Join();
if (mbResult.Result == NativeMethods.IDABORT) {
IntPtr currentProcess = NativeMethods.GetCurrentProcess();
NativeMethods.TerminateProcess(new HandleRef(mbResult, currentProcess), 1);
}
return mbResult.Result == NativeMethods.IDRETRY;
}
#endif
//
// Sends the message to the debugger if the tag is enabled.
// Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
//
[System.Diagnostics.Conditional("DBG")]
internal static void Trace(string tagName, string message) {
#if DBG
if (TraceBreak(tagName, message, null, true)) {
Break();
}
#endif
}
//
// Sends the message to the debugger if the tag is enabled.
// Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
//
[System.Diagnostics.Conditional("DBG")]
internal static void Trace(string tagName, string message, bool includePrefix) {
#if DBG
if (TraceBreak(tagName, message, null, includePrefix)) {
Break();
}
#endif
}
//
// Sends the message to the debugger if the tag is enabled.
// Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
//
[System.Diagnostics.Conditional("DBG")]
internal static void Trace(string tagName, string message, Exception e) {
#if DBG
if (TraceBreak(tagName, message, e, true)) {
Break();
}
#endif
}
//
// Sends the message to the debugger if the tag is enabled.
// Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
//
[System.Diagnostics.Conditional("DBG")]
internal static void Trace(string tagName, Exception e) {
#if DBG
if (TraceBreak(tagName, null, e, true)) {
Break();
}
#endif
}
//
// Sends the message to the debugger if the tag is enabled.
// Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
//
[System.Diagnostics.Conditional("DBG")]
internal static void Trace(string tagName, string message, Exception e, bool includePrefix) {
#if DBG
if (TraceBreak(tagName, message, e, includePrefix)) {
Break();
}
#endif
}
#if DBG
#endif
[System.Diagnostics.Conditional("DBG")]
public static void TraceException(String tagName, Exception e) {
#if DBG
if (TraceBreak(tagName, null, e, true)) {
Break();
}
#endif
}
//
// If the assertion is false and the 'Assert' tag is enabled:
// * Send a message to the debugger.
// * If the 'AssertBreak' tag is enabled, immediately break into the debugger
// * Else display a dialog box asking the user to Abort, Retry (break), or Ignore
//
[System.Diagnostics.Conditional("DBG")]
internal static void Assert(bool assertion, string message) {
#if DBG
EnsureInit();
if (assertion == false) {
if (DoAssert(message)) {
Break();
}
}
#endif
}
//
// If the assertion is false and the 'Assert' tag is enabled:
// * Send a message to the debugger.
// * If the 'AssertBreak' tag is enabled, immediately break into the debugger
// * Else display a dialog box asking the user to Abort, Retry (break), or Ignore
//
[System.Diagnostics.Conditional("DBG")]
[ResourceExposure(ResourceScope.None)]
internal static void Assert(bool assertion) {
#if DBG
EnsureInit();
if (assertion == false) {
if (DoAssert(null)) {
Break();
}
}
#endif
}
//
// Like Assert, but the assertion is always considered to be false.
//
[System.Diagnostics.Conditional("DBG")]
[ResourceExposure(ResourceScope.None)]
internal static void Fail(string message) {
#if DBG
Assert(false, message);
#endif
}
//
// Returns true if the tag is enabled, false otherwise.
// Note that the tag needn't be an exact match.
//
[ResourceExposure(ResourceScope.None)]
internal static bool IsTagEnabled(string tagName) {
#if DBG
EnsureInit();
return GetTagValue(tagName) != TagValue.Disabled;
#else
return false;
#endif
}
//
// Returns true if the tag present.
// This function chekcs for an exact match.
//
[ResourceExposure(ResourceScope.None)]
internal static bool IsTagPresent(string tagName) {
#if DBG
EnsureInit();
return FindMatchingTag(tagName, true) != null;
#else
return false;
#endif
}
// Returns a value indicating whether a debugger is attached to the process.
// We don't #ifdef this away since we might need to change behavior based
// on whether one is attached.
internal static bool IsDebuggerPresent() {
return (NativeMethods.IsDebuggerPresent() || System.Diagnostics.Debugger.IsAttached);
}
//
// Breaks into the debugger, or launches one if not yet attached.
//
[System.Diagnostics.Conditional("DBG")]
[ResourceExposure(ResourceScope.None)]
internal static void Break() {
#if DBG
if (NativeMethods.IsDebuggerPresent()) {
NativeMethods.DebugBreak();
}
else if (!System.Diagnostics.Debugger.IsAttached) {
System.Diagnostics.Debugger.Launch();
}
else {
System.Diagnostics.Debugger.Break();
}
#endif
}
//
// Tells the debug system to always validate calls for a
// particular tag. This is useful for temporarily enabling
// validation in stress tests or other situations where you
// may not have control over the debug tags that are enabled
// on a particular machine.
//
[System.Diagnostics.Conditional("DBG")]
internal static void AlwaysValidate(string tagName) {
#if DBG
EnsureInit();
s_tableAlwaysValidate[tagName] = tagName;
#endif
}
//
// Throws an exception if the assertion is not valid.
// Use this function from a DebugValidate method where
// you would otherwise use Assert.
//
[System.Diagnostics.Conditional("DBG")]
internal static void CheckValid(bool assertion, string message) {
#if DBG
if (!assertion) {
throw new Exception(message);
}
#endif
}
//
// Calls DebugValidate on an object if such a method exists.
//
// This method should be used from implementations of DebugValidate
// where it is unknown whether an object has a DebugValidate method.
// For example, the DoubleLink class uses it to validate the
// item of type Object which it points to.
//
// This method should NOT be used when code wants to conditionally
// validate an object and have a failed validation caught in an assert.
// Use Debug.Validate(tagName, obj) for that purpose.
//
[System.Diagnostics.Conditional("DBG")]
[ResourceExposure(ResourceScope.None)]
internal static void Validate(Object obj) {
#if DBG
Type type;
MethodInfo mi;
if (obj != null) {
type = obj.GetType();
mi = type.GetMethod(
"DebugValidate",
BindingFlags.NonPublic | BindingFlags.Instance,
null,
s_ValidateArgs,
null);
if (mi != null) {
object[] tempIndex = null;
mi.Invoke(obj, tempIndex);
}
}
#endif
}
// Ensures that the bounds provided to an array-consuming method are correct.
// Because this method is on the Debug type, it should only be used in debugging
// code and should not be depended on for user input sanitization (since the
// compiler won't emit calls to it.)
[System.Diagnostics.Conditional("DBG")]
internal static void ValidateArrayBounds<T>(T[] array, int offset, int count) {
#if DBG
// these are the same checks as in the ArraySegment<T> ctor
Assert(array != null, "Array cannot be null.");
Assert(offset >= 0, "Offset must be non-negative.");
Assert(count >= 0, "Count must be non-negative.");
Assert(array.Length - offset >= count, "Invalid offset.");
#endif
}
//
// Validates an object is the "Validate" tag is enabled, or when
// the "Validate" tag is not disabled and the given 'tag' is enabled.
// An Assertion is made if the validation fails.
//
[System.Diagnostics.Conditional("DBG")]
[ResourceExposure(ResourceScope.None)]
internal static void Validate(string tagName, Object obj) {
#if DBG
EnsureInit();
if ( obj != null
&& ( IsTagEnabled("Validate")
|| ( !IsTagPresent("Validate")
&& ( s_tableAlwaysValidate[tagName] != null
|| IsTagEnabled(tagName))))) {
try {
Debug.Validate(obj);
}
catch (Exception e) {
Debug.Assert(false, "Validate failed: " + e.InnerException.Message);
}
#pragma warning disable 1058
catch {
Debug.Assert(false, "Validate failed. Non-CLS compliant exception caught.");
}
#pragma warning restore 1058
}
#endif
}
#if DBG
//
// Calls DebugDescription on an object to get its description.
//
// This method should only be used in implementations of DebugDescription
// where it is not known whether a nested objects has an implementation
// of DebugDescription. For example, the double linked list class uses
// GetDescription to get the description of the item it points to.
//
// This method should NOT be used when you want to conditionally
// dump an object. Use Debug.Dump instead.
//
// @param obj The object to call DebugDescription on. May be null.
// @param indent A prefix for each line in the description. This is used
// to allow the nested display of objects within other objects.
// The indent is usually a multiple of four spaces.
//
// @return The description.
//
[ReflectionPermission(SecurityAction.Assert, Unrestricted=true)]
internal static string GetDescription(Object obj, string indent) {
string description;
Type type;
MethodInfo mi;
Object[] parameters;
if (obj == null) {
description = "\n";
}
else {
type = obj.GetType();
mi = type.GetMethod(
"DebugDescription",
BindingFlags.NonPublic | BindingFlags.Instance,
null,
s_DumpArgs,
null);
if (mi == null || mi.ReturnType != typeof(string)) {
description = indent + obj.ToString();
}
else {
parameters = new Object[1] {(Object) indent};
description = (string) mi.Invoke(obj, parameters);
}
}
return description;
}
#endif
//
// Dumps an object to the debugger if the "Dump" tag is enabled,
// or if the "Dump" tag is not present and the 'tag' is enabled.
//
// @param tagName The tag to Dump with.
// @param obj The object to dump.
//
[System.Diagnostics.Conditional("DBG")]
internal static void Dump(string tagName, Object obj) {
#if DBG
EnsureInit();
string description;
string traceTag = null;
bool dumpEnabled, dumpPresent;
if (obj != null) {
dumpEnabled = IsTagEnabled("Dump");
dumpPresent = IsTagPresent("Dump");
if (dumpEnabled || !dumpPresent) {
if (IsTagEnabled(tagName)) {
traceTag = tagName;
}
else if (dumpEnabled) {
traceTag = "Dump";
}
if (traceTag != null) {
description = GetDescription(obj, string.Empty);
Debug.Trace(traceTag, "Dump\n" + description);
}
}
}
#endif
}
#if DBG
static internal string ToStringMaybeNull(object o) {
if (o != null) {
return o.ToString();
}
return "<null>";
}
#endif
static internal string FormatUtcDate(DateTime utcTime) {
#if DBG
DateTime localTime = DateTimeUtil.ConvertToLocalTime(utcTime);
return localTime.ToString(DATE_FORMAT, CultureInfo.InvariantCulture);
#else
return string.Empty;
#endif
}
static internal string FormatLocalDate(DateTime localTime) {
#if DBG
return localTime.ToString(DATE_FORMAT, CultureInfo.InvariantCulture);
#else
return string.Empty;
#endif
}
}
}
|