1 //------------------------------------------------------------------------------
2 // <copyright file="Debug.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace System.Web.Util {
9 using Microsoft.Win32.SafeHandles;
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.Collections.ObjectModel;
14 using System.Globalization;
15 using System.Reflection;
16 using System.Runtime.ConstrainedExecution;
17 using System.Runtime.InteropServices;
18 using System.Security;
19 using System.Security.Permissions;
20 using System.Threading;
21 using System.Runtime.Versioning;
23 internal static class Debug {
25 internal const string TAG_INTERNAL = "Internal";
26 internal const string TAG_EXTERNAL = "External";
27 internal const string TAG_ALL = "*";
29 internal const string DATE_FORMAT = @"yyyy/MM/dd HH:mm:ss.ffff";
30 internal const string TIME_FORMAT = @"HH:mm:ss:ffff";
32 // Some of these APIs must be always available, not #ifdefed away.
33 [SuppressUnmanagedCodeSecurity]
34 private static partial class NativeMethods {
35 [DllImport("kernel32.dll")]
36 internal extern static bool IsDebuggerPresent();
40 private static partial class NativeMethods {
41 [DllImport("kernel32.dll")]
42 internal extern static void DebugBreak();
44 [DllImport("kernel32.dll")]
45 internal extern static int GetCurrentProcessId();
47 [DllImport("kernel32.dll")]
48 internal extern static int GetCurrentThreadId();
50 [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
51 internal extern static IntPtr GetCurrentProcess();
53 [DllImport("kernel32.dll", SetLastError=true)]
54 internal extern static bool TerminateProcess(HandleRef processHandle, int exitCode);
56 [DllImport("kernel32.dll", CharSet=CharSet.Auto, BestFitMapping=false)]
57 internal extern static void OutputDebugString(string message);
59 internal const int PM_NOREMOVE = 0x0000;
60 internal const int PM_REMOVE = 0x0001;
62 [StructLayout(LayoutKind.Sequential)]
66 internal IntPtr wParam;
67 internal IntPtr lParam;
73 [DllImport("user32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)]
74 internal extern static bool PeekMessage([In, Out] ref MSG msg, HandleRef hwnd, int msgMin, int msgMax, int remove);
78 MB_OKCANCEL = 0x00000001,
79 MB_ABORTRETRYIGNORE = 0x00000002,
80 MB_YESNOCANCEL = 0x00000003,
81 MB_YESNO = 0x00000004,
82 MB_RETRYCANCEL = 0x00000005,
83 MB_ICONHAND = 0x00000010,
84 MB_ICONQUESTION = 0x00000020,
85 MB_ICONEXCLAMATION = 0x00000030,
86 MB_ICONASTERISK = 0x00000040,
87 MB_USERICON = 0x00000080,
88 MB_ICONWARNING = 0x00000030,
89 MB_ICONERROR = 0x00000010,
90 MB_ICONINFORMATION = 0x00000040,
91 MB_DEFBUTTON1 = 0x00000000,
92 MB_DEFBUTTON2 = 0x00000100,
93 MB_DEFBUTTON3 = 0x00000200,
94 MB_DEFBUTTON4 = 0x00000300,
95 MB_APPLMODAL = 0x00000000,
96 MB_SYSTEMMODAL = 0x00001000,
97 MB_TASKMODAL = 0x00002000,
99 MB_NOFOCUS = 0x00008000,
100 MB_SETFOREGROUND = 0x00010000,
101 MB_DEFAULT_DESKTOP_ONLY = 0x00020000,
102 MB_TOPMOST = 0x00040000,
103 MB_RIGHT = 0x00080000,
104 MB_RTLREADING = 0x00100000,
105 MB_SERVICE_NOTIFICATION = 0x00200000,
106 MB_SERVICE_NOTIFICATION_NT3X = 0x00040000,
107 MB_TYPEMASK = 0x0000000F,
108 MB_ICONMASK = 0x000000F0,
109 MB_DEFMASK = 0x00000F00,
110 MB_MODEMASK = 0x00003000,
111 MB_MISCMASK = 0x0000C000;
125 [DllImport("user32.dll", CharSet=CharSet.Auto, BestFitMapping=false)]
126 internal extern static int MessageBox(HandleRef hWnd, string text, string caption, int type);
128 internal static readonly IntPtr HKEY_LOCAL_MACHINE = unchecked((IntPtr)(int)0x80000002);
130 internal const int READ_CONTROL = 0x00020000;
131 internal const int STANDARD_RIGHTS_READ = READ_CONTROL;
133 internal const int SYNCHRONIZE = 0x00100000;
135 internal const int KEY_QUERY_VALUE = 0x0001;
136 internal const int KEY_ENUMERATE_SUB_KEYS = 0x0008;
137 internal const int KEY_NOTIFY = 0x0010;
140 internal const int KEY_READ = ((STANDARD_RIGHTS_READ |
142 KEY_ENUMERATE_SUB_KEYS |
147 internal const int REG_NOTIFY_CHANGE_NAME = 1;
148 internal const int REG_NOTIFY_CHANGE_LAST_SET = 4;
151 [DllImport("advapi32.dll", CharSet=CharSet.Auto, BestFitMapping=false, SetLastError=true)]
152 internal extern static int RegOpenKeyEx(IntPtr hKey, string lpSubKey, int ulOptions, int samDesired, out SafeRegistryHandle hkResult);
154 [DllImport("advapi32.dll", ExactSpelling=true, SetLastError=true)]
155 internal extern static int RegNotifyChangeKeyValue(SafeRegistryHandle hKey, bool watchSubTree, uint notifyFilter, SafeWaitHandle regEvent, bool async);
158 private class SafeRegistryHandle : SafeHandleZeroOrMinusOneIsInvalid {
160 // Note: Officially -1 is the recommended invalid handle value for
161 // registry keys, but we'll also get back 0 as an invalid handle from
164 [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
165 [ResourceExposure(ResourceScope.Machine)]
166 internal SafeRegistryHandle() : base(true) {}
168 [DllImport("advapi32.dll"),
169 SuppressUnmanagedCodeSecurity,
170 ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
171 private static extern int RegCloseKey(IntPtr hKey);
173 override protected bool ReleaseHandle()
175 // Returns a Win32 error code, 0 for success
176 int r = RegCloseKey(handle);
181 private enum TagValue {
190 private const string TAG_ASSERT = "Assert";
191 private const string TAG_ASSERT_BREAK = "AssertBreak";
193 private const string TAG_DEBUG_VERBOSE = "DebugVerbose";
194 private const string TAG_DEBUG_MONITOR = "DebugMonitor";
195 private const string TAG_DEBUG_PREFIX = "DebugPrefix";
196 private const string TAG_DEBUG_THREAD_PREFIX = "DebugThreadPrefix";
198 private const string PRODUCT = "Microsoft .NET Framework";
199 private const string COMPONENT = "System.Web";
201 private static string s_regKeyName = @"Software\Microsoft\ASP.NET\Debug";
202 private static string s_listenKeyName = @"Software\Microsoft";
204 private static bool s_assert;
205 private static bool s_assertBreak;
207 private static bool s_includePrefix;
208 private static bool s_includeThreadPrefix;
209 private static bool s_monitor;
211 private static object s_lock;
212 private static volatile bool s_inited;
213 private static ReadOnlyCollection<Tag> s_tagDefaults;
214 private static List<Tag> s_tags;
216 private static AutoResetEvent s_notifyEvent;
217 private static RegisteredWaitHandle s_waitHandle;
218 private static SafeRegistryHandle s_regHandle;
219 private static bool s_stopMonitoring;
221 private static Hashtable s_tableAlwaysValidate;
222 private static Type[] s_DumpArgs;
223 private static Type[] s_ValidateArgs;
230 internal Tag(string name, TagValue value) {
234 if (_name[_name.Length - 1] == '*') {
235 _prefixLength = _name.Length - 1;
242 internal string Name {
246 internal TagValue Value {
250 internal int PrefixLength {
251 get {return _prefixLength;}
256 s_lock = new object();
259 private static void EnsureInit() {
260 bool continueInit = false;
265 s_tableAlwaysValidate = new Hashtable();
266 s_DumpArgs = new Type[1] {typeof(string)};
267 s_ValidateArgs = new Type[0];
269 List<Tag> tagDefaults = new List<Tag>();
270 tagDefaults.Add(new Tag(TAG_ALL, TagValue.Disabled));
271 tagDefaults.Add(new Tag(TAG_INTERNAL, TagValue.Enabled));
272 tagDefaults.Add(new Tag(TAG_EXTERNAL, TagValue.Enabled));
273 tagDefaults.Add(new Tag(TAG_ASSERT, TagValue.Break));
274 tagDefaults.Add(new Tag(TAG_ASSERT_BREAK, TagValue.Disabled));
275 tagDefaults.Add(new Tag(TAG_DEBUG_VERBOSE, TagValue.Enabled));
276 tagDefaults.Add(new Tag(TAG_DEBUG_MONITOR, TagValue.Enabled));
277 tagDefaults.Add(new Tag(TAG_DEBUG_PREFIX, TagValue.Enabled));
278 tagDefaults.Add(new Tag(TAG_DEBUG_THREAD_PREFIX, TagValue.Enabled));
280 s_tagDefaults = tagDefaults.AsReadOnly();
281 s_tags = new List<Tag>(s_tagDefaults);
282 GetBuiltinTagValues();
290 // Work to do outside the init lock.
292 ReadTagsFromRegistry();
293 Trace(TAG_DEBUG_VERBOSE, "Debugging package initialized");
295 // Need to read tags before starting to monitor in order to get TAG_DEBUG_MONITOR
296 StartRegistryMonitor();
300 private static bool StringEqualsIgnoreCase(string s1, string s2) {
301 return StringComparer.OrdinalIgnoreCase.Equals(s1, s2);
304 [RegistryPermission(SecurityAction.Assert, Unrestricted=true)]
305 private static void WriteTagsToRegistry() {
307 using (RegistryKey key = Registry.LocalMachine.CreateSubKey(s_regKeyName)) {
308 List<Tag> tags = s_tags;
309 foreach (Tag tag in tags) {
310 key.SetValue(tag.Name, tag.Value, RegistryValueKind.DWord);
318 private static void GetBuiltinTagValues() {
319 // Use GetTagValue instead of IsTagEnabled because it does not call EnsureInit
320 // and potentially recurse.
321 s_assert = (GetTagValue(TAG_ASSERT) != TagValue.Disabled);
322 s_assertBreak = (GetTagValue(TAG_ASSERT_BREAK) != TagValue.Disabled);
323 s_includePrefix = (GetTagValue(TAG_DEBUG_PREFIX) != TagValue.Disabled);
324 s_includeThreadPrefix = (GetTagValue(TAG_DEBUG_THREAD_PREFIX) != TagValue.Disabled);
325 s_monitor = (GetTagValue(TAG_DEBUG_MONITOR) != TagValue.Disabled);
328 [RegistryPermission(SecurityAction.Assert, Unrestricted=true)]
329 private static void ReadTagsFromRegistry() {
332 List<Tag> tags = new List<Tag>(s_tagDefaults);
333 string[] names = null;
335 bool writeTags = false;
336 using (RegistryKey key = Registry.LocalMachine.OpenSubKey(s_regKeyName, false)) {
338 names = key.GetValueNames();
339 foreach (string name in names) {
340 TagValue value = TagValue.Disabled;
342 TagValue keyvalue = (TagValue) key.GetValue(name);
343 if (TagValue.Min <= keyvalue && keyvalue <= TagValue.Max) {
354 // Add tag to list, making sure it is unique.
355 Tag tag = new Tag(name, (TagValue) value);
357 for (int i = 0; i < s_tagDefaults.Count; i++) {
358 if (StringEqualsIgnoreCase(name, tags[i].Name)) {
373 GetBuiltinTagValues();
375 // Write tags out if there was an invalid value or
376 // not all default tags are present.
377 if (writeTags || (names != null && names.Length < tags.Count)) {
378 WriteTagsToRegistry();
382 s_tags = new List<Tag>(s_tagDefaults);
387 private static void StartRegistryMonitor() {
389 Trace(TAG_DEBUG_VERBOSE, "WARNING: Registry monitoring disabled, changes during process execution will not be recognized.");
393 Trace(TAG_DEBUG_VERBOSE, "Monitoring registry key " + s_listenKeyName + " for changes.");
395 // Event used to notify of changes.
396 s_notifyEvent = new AutoResetEvent(false);
398 // Register a wait on the event.
399 s_waitHandle = ThreadPool.RegisterWaitForSingleObject(s_notifyEvent, OnRegChangeKeyValue, null, -1, false);
401 // Monitor the registry.
402 MonitorRegistryForOneChange();
405 private static void StopRegistryMonitor() {
406 // Cleanup allocated handles
407 s_stopMonitoring = true;
409 if (s_regHandle != null) {
414 if (s_waitHandle != null) {
415 s_waitHandle.Unregister(s_notifyEvent);
419 if (s_notifyEvent != null) {
420 s_notifyEvent.Close();
421 s_notifyEvent = null;
424 Trace(TAG_DEBUG_VERBOSE, "Registry monitoring stopped.");
427 public static void OnRegChangeKeyValue(object state, bool timedOut) {
428 if (!s_stopMonitoring) {
430 StopRegistryMonitor();
434 MonitorRegistryForOneChange();
436 // Once we're monitoring, read the changes to the registry.
437 // We have to do this after we start monitoring in order
438 // to catch all changes to the registry.
439 ReadTagsFromRegistry();
444 private static void MonitorRegistryForOneChange() {
445 // Close the open reg handle
446 if (s_regHandle != null) {
452 int result = NativeMethods.RegOpenKeyEx(NativeMethods.HKEY_LOCAL_MACHINE, s_listenKeyName, 0, NativeMethods.KEY_READ, out s_regHandle);
454 StopRegistryMonitor();
458 // Listen for changes.
459 result = NativeMethods.RegNotifyChangeKeyValue(
462 NativeMethods.REG_NOTIFY_CHANGE_NAME | NativeMethods.REG_NOTIFY_CHANGE_LAST_SET,
463 s_notifyEvent.SafeWaitHandle,
467 StopRegistryMonitor();
471 private static Tag FindMatchingTag(string name, bool exact) {
472 List<Tag> tags = s_tags;
474 // Look for exact match first
475 foreach (Tag tag in tags) {
476 if (StringEqualsIgnoreCase(name, tag.Name)) {
485 Tag longestTag = null;
486 int longestPrefix = -1;
487 foreach (Tag tag in tags) {
488 if ( tag.PrefixLength > longestPrefix &&
489 0 == string.Compare(name, 0, tag.Name, 0, tag.PrefixLength, StringComparison.OrdinalIgnoreCase)) {
492 longestPrefix = tag.PrefixLength;
499 private static TagValue GetTagValue(string name) {
500 Tag tag = FindMatchingTag(name, false);
505 return TagValue.Disabled;
509 [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
510 private static bool TraceBreak(string tagName, string message, Exception e, bool includePrefix) {
513 TagValue tagValue = GetTagValue(tagName);
514 if (tagValue == TagValue.Disabled) {
518 bool isAssert = object.ReferenceEquals(tagName, TAG_ASSERT);
523 string exceptionMessage = null;
525 string httpCode = null;
526 string errorCode = null;
528 if (e is HttpException) {
529 httpCode = " _httpCode=" + ((HttpException)e).GetHttpCode().ToString(CultureInfo.InvariantCulture) + " ";
532 if (e is ExternalException) {
533 // note that HttpExceptions are ExternalExceptions
534 errorCode = "_hr=0x" + ((ExternalException)e).ErrorCode.ToString("x", CultureInfo.InvariantCulture);
537 // Use e.ToString() in order to get inner exception
538 if (errorCode != null) {
539 exceptionMessage = "Exception " + e.ToString() + "\n" + httpCode + errorCode;
542 exceptionMessage = "Exception " + e.ToString();
546 if (string.IsNullOrEmpty(message) & exceptionMessage != null) {
547 message = exceptionMessage;
548 exceptionMessage = null;
555 if (!includePrefix || !s_includePrefix) {
556 traceFormat = "{4}\n{5}";
559 if (s_includeThreadPrefix) {
560 idThread = NativeMethods.GetCurrentThreadId();
561 idProcess = NativeMethods.GetCurrentProcessId();
562 traceFormat = "[0x{0:x}.{1:x} {2} {3}] {4}\n{5}";
565 traceFormat = "[{2} {3}] {4}\n{5}";
570 if (exceptionMessage != null) {
571 suffix += exceptionMessage + "\n";
574 bool doBreak = (tagValue == TagValue.Break);
575 if (doBreak && !isAssert) {
576 suffix += "Breaking into debugger...\n";
579 string traceMessage = string.Format(CultureInfo.InvariantCulture, traceFormat, idProcess, idThread, COMPONENT, tagName, message, suffix);
581 NativeMethods.OutputDebugString(traceMessage);
586 private class MBResult {
590 [ResourceExposure(ResourceScope.None)]
591 static bool DoAssert(string message) {
596 // Skip 2 frames - one for this function, one for
597 // the public Assert function that called this function.
598 System.Diagnostics.StackFrame frame = new System.Diagnostics.StackFrame(2, true);
599 System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(2, true);
601 string fileName = frame.GetFileName();
602 int lineNumber = frame.GetFileLineNumber();
605 if (!string.IsNullOrEmpty(fileName)) {
606 traceFormat = "ASSERTION FAILED: {0}\nFile: {1}:{2}\nStack trace:\n{3}";
609 traceFormat = "ASSERTION FAILED: {0}\nStack trace:\n{3}";
612 string traceMessage = string.Format(CultureInfo.InvariantCulture, traceFormat, message, fileName, lineNumber, trace.ToString());
614 if (!TraceBreak(TAG_ASSERT, traceMessage, null, true)) {
615 // If the value of "Assert" is not TagValue.Break, then don't even ask user.
620 // If "AssertBreak" is enabled, then always break.
625 if (!string.IsNullOrEmpty(fileName)) {
627 @"Failed expression: {0}
634 A=Exit process R=Debug I=Continue";
638 @"Failed expression: {0}
639 (no file information available)
645 A=Exit process R=Debug I=Continue";
648 string dialogMessage = string.Format(
649 CultureInfo.InvariantCulture,
652 fileName, lineNumber,
654 NativeMethods.GetCurrentProcessId(), NativeMethods.GetCurrentThreadId(),
657 MBResult mbResult = new MBResult();
659 Thread thread = new Thread(
661 for (int i = 0; i < 100; i++) {
662 NativeMethods.MSG msg = new NativeMethods.MSG();
663 NativeMethods.PeekMessage(ref msg, new HandleRef(mbResult, IntPtr.Zero), 0, 0, NativeMethods.PM_REMOVE);
666 mbResult.Result = NativeMethods.MessageBox(new HandleRef(mbResult, IntPtr.Zero), dialogMessage, PRODUCT + " Assertion",
667 NativeMethods.MB_SERVICE_NOTIFICATION |
668 NativeMethods.MB_TOPMOST |
669 NativeMethods.MB_ABORTRETRYIGNORE |
670 NativeMethods.MB_ICONEXCLAMATION);
677 if (mbResult.Result == NativeMethods.IDABORT) {
678 IntPtr currentProcess = NativeMethods.GetCurrentProcess();
679 NativeMethods.TerminateProcess(new HandleRef(mbResult, currentProcess), 1);
682 return mbResult.Result == NativeMethods.IDRETRY;
687 // Sends the message to the debugger if the tag is enabled.
688 // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
690 [System.Diagnostics.Conditional("DBG")]
691 internal static void Trace(string tagName, string message) {
693 if (TraceBreak(tagName, message, null, true)) {
700 // Sends the message to the debugger if the tag is enabled.
701 // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
703 [System.Diagnostics.Conditional("DBG")]
704 internal static void Trace(string tagName, string message, bool includePrefix) {
706 if (TraceBreak(tagName, message, null, includePrefix)) {
713 // Sends the message to the debugger if the tag is enabled.
714 // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
716 [System.Diagnostics.Conditional("DBG")]
717 internal static void Trace(string tagName, string message, Exception e) {
719 if (TraceBreak(tagName, message, e, true)) {
726 // Sends the message to the debugger if the tag is enabled.
727 // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
729 [System.Diagnostics.Conditional("DBG")]
730 internal static void Trace(string tagName, Exception e) {
732 if (TraceBreak(tagName, null, e, true)) {
739 // Sends the message to the debugger if the tag is enabled.
740 // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
742 [System.Diagnostics.Conditional("DBG")]
743 internal static void Trace(string tagName, string message, Exception e, bool includePrefix) {
745 if (TraceBreak(tagName, message, e, includePrefix)) {
754 [System.Diagnostics.Conditional("DBG")]
755 public static void TraceException(String tagName, Exception e) {
757 if (TraceBreak(tagName, null, e, true)) {
765 // If the assertion is false and the 'Assert' tag is enabled:
766 // * Send a message to the debugger.
767 // * If the 'AssertBreak' tag is enabled, immediately break into the debugger
768 // * Else display a dialog box asking the user to Abort, Retry (break), or Ignore
770 [System.Diagnostics.Conditional("DBG")]
771 internal static void Assert(bool assertion, string message) {
774 if (assertion == false) {
775 if (DoAssert(message)) {
784 // If the assertion is false and the 'Assert' tag is enabled:
785 // * Send a message to the debugger.
786 // * If the 'AssertBreak' tag is enabled, immediately break into the debugger
787 // * Else display a dialog box asking the user to Abort, Retry (break), or Ignore
789 [System.Diagnostics.Conditional("DBG")]
790 [ResourceExposure(ResourceScope.None)]
791 internal static void Assert(bool assertion) {
794 if (assertion == false) {
795 if (DoAssert(null)) {
803 // Like Assert, but the assertion is always considered to be false.
805 [System.Diagnostics.Conditional("DBG")]
806 [ResourceExposure(ResourceScope.None)]
807 internal static void Fail(string message) {
809 Assert(false, message);
814 // Returns true if the tag is enabled, false otherwise.
815 // Note that the tag needn't be an exact match.
817 [ResourceExposure(ResourceScope.None)]
818 internal static bool IsTagEnabled(string tagName) {
821 return GetTagValue(tagName) != TagValue.Disabled;
828 // Returns true if the tag present.
829 // This function chekcs for an exact match.
831 [ResourceExposure(ResourceScope.None)]
832 internal static bool IsTagPresent(string tagName) {
835 return FindMatchingTag(tagName, true) != null;
841 // Returns a value indicating whether a debugger is attached to the process.
842 // We don't #ifdef this away since we might need to change behavior based
843 // on whether one is attached.
844 internal static bool IsDebuggerPresent() {
845 return (NativeMethods.IsDebuggerPresent() || System.Diagnostics.Debugger.IsAttached);
849 // Breaks into the debugger, or launches one if not yet attached.
851 [System.Diagnostics.Conditional("DBG")]
852 [ResourceExposure(ResourceScope.None)]
853 internal static void Break() {
855 if (NativeMethods.IsDebuggerPresent()) {
856 NativeMethods.DebugBreak();
858 else if (!System.Diagnostics.Debugger.IsAttached) {
859 System.Diagnostics.Debugger.Launch();
862 System.Diagnostics.Debugger.Break();
869 // Tells the debug system to always validate calls for a
870 // particular tag. This is useful for temporarily enabling
871 // validation in stress tests or other situations where you
872 // may not have control over the debug tags that are enabled
873 // on a particular machine.
875 [System.Diagnostics.Conditional("DBG")]
876 internal static void AlwaysValidate(string tagName) {
879 s_tableAlwaysValidate[tagName] = tagName;
885 // Throws an exception if the assertion is not valid.
886 // Use this function from a DebugValidate method where
887 // you would otherwise use Assert.
889 [System.Diagnostics.Conditional("DBG")]
890 internal static void CheckValid(bool assertion, string message) {
893 throw new Exception(message);
899 // Calls DebugValidate on an object if such a method exists.
901 // This method should be used from implementations of DebugValidate
902 // where it is unknown whether an object has a DebugValidate method.
903 // For example, the DoubleLink class uses it to validate the
904 // item of type Object which it points to.
906 // This method should NOT be used when code wants to conditionally
907 // validate an object and have a failed validation caught in an assert.
908 // Use Debug.Validate(tagName, obj) for that purpose.
910 [System.Diagnostics.Conditional("DBG")]
911 [ResourceExposure(ResourceScope.None)]
912 internal static void Validate(Object obj) {
918 type = obj.GetType();
922 BindingFlags.NonPublic | BindingFlags.Instance,
928 object[] tempIndex = null;
929 mi.Invoke(obj, tempIndex);
935 // Ensures that the bounds provided to an array-consuming method are correct.
936 // Because this method is on the Debug type, it should only be used in debugging
937 // code and should not be depended on for user input sanitization (since the
938 // compiler won't emit calls to it.)
939 [System.Diagnostics.Conditional("DBG")]
940 internal static void ValidateArrayBounds<T>(T[] array, int offset, int count) {
942 // these are the same checks as in the ArraySegment<T> ctor
943 Assert(array != null, "Array cannot be null.");
944 Assert(offset >= 0, "Offset must be non-negative.");
945 Assert(count >= 0, "Count must be non-negative.");
946 Assert(array.Length - offset >= count, "Invalid offset.");
951 // Validates an object is the "Validate" tag is enabled, or when
952 // the "Validate" tag is not disabled and the given 'tag' is enabled.
953 // An Assertion is made if the validation fails.
955 [System.Diagnostics.Conditional("DBG")]
956 [ResourceExposure(ResourceScope.None)]
957 internal static void Validate(string tagName, Object obj) {
962 && ( IsTagEnabled("Validate")
963 || ( !IsTagPresent("Validate")
964 && ( s_tableAlwaysValidate[tagName] != null
965 || IsTagEnabled(tagName))))) {
969 catch (Exception e) {
970 Debug.Assert(false, "Validate failed: " + e.InnerException.Message);
972 #pragma warning disable 1058
974 Debug.Assert(false, "Validate failed. Non-CLS compliant exception caught.");
976 #pragma warning restore 1058
984 // Calls DebugDescription on an object to get its description.
986 // This method should only be used in implementations of DebugDescription
987 // where it is not known whether a nested objects has an implementation
988 // of DebugDescription. For example, the double linked list class uses
989 // GetDescription to get the description of the item it points to.
991 // This method should NOT be used when you want to conditionally
992 // dump an object. Use Debug.Dump instead.
994 // @param obj The object to call DebugDescription on. May be null.
995 // @param indent A prefix for each line in the description. This is used
996 // to allow the nested display of objects within other objects.
997 // The indent is usually a multiple of four spaces.
999 // @return The description.
1001 [ReflectionPermission(SecurityAction.Assert, Unrestricted=true)]
1002 internal static string GetDescription(Object obj, string indent) {
1006 Object[] parameters;
1012 type = obj.GetType();
1013 mi = type.GetMethod(
1015 BindingFlags.NonPublic | BindingFlags.Instance,
1020 if (mi == null || mi.ReturnType != typeof(string)) {
1021 description = indent + obj.ToString();
1024 parameters = new Object[1] {(Object) indent};
1025 description = (string) mi.Invoke(obj, parameters);
1035 // Dumps an object to the debugger if the "Dump" tag is enabled,
1036 // or if the "Dump" tag is not present and the 'tag' is enabled.
1038 // @param tagName The tag to Dump with.
1039 // @param obj The object to dump.
1041 [System.Diagnostics.Conditional("DBG")]
1042 internal static void Dump(string tagName, Object obj) {
1047 string traceTag = null;
1048 bool dumpEnabled, dumpPresent;
1051 dumpEnabled = IsTagEnabled("Dump");
1052 dumpPresent = IsTagPresent("Dump");
1053 if (dumpEnabled || !dumpPresent) {
1054 if (IsTagEnabled(tagName)) {
1057 else if (dumpEnabled) {
1061 if (traceTag != null) {
1062 description = GetDescription(obj, string.Empty);
1063 Debug.Trace(traceTag, "Dump\n" + description);
1071 static internal string ToStringMaybeNull(object o) {
1073 return o.ToString();
1080 static internal string FormatUtcDate(DateTime utcTime) {
1082 DateTime localTime = DateTimeUtil.ConvertToLocalTime(utcTime);
1083 return localTime.ToString(DATE_FORMAT, CultureInfo.InvariantCulture);
1085 return string.Empty;
1089 static internal string FormatLocalDate(DateTime localTime) {
1091 return localTime.ToString(DATE_FORMAT, CultureInfo.InvariantCulture);
1093 return string.Empty;