Add conditionals where needed.
[mono.git] / mcs / class / referencesource / System / net / System / Net / _LoggingObject.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="_LoggingObject.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 //
8 //  We have function based stack and thread based logging of basic behavior.  We
9 //  also now have the ability to run a "watch thread" which does basic hang detection
10 //  and error-event based logging.   The logging code buffers the callstack/picture
11 //  of all COMNET threads, and upon error from an assert or a hang, it will open a file
12 //  and dump the snapsnot.  Future work will allow this to be configed by registry and
13 //  to use Runtime based logging.  We'd also like to have different levels of logging.
14 //
15
16 namespace System.Net {
17     using System.Collections;
18     using System.Collections.Generic;
19     using System.IO;
20     using System.Threading;
21     using System.Diagnostics;
22     using System.Security.Permissions;
23     using System.Security.Principal;
24     using System.Security;
25     using Microsoft.Win32;
26     using System.Runtime.ConstrainedExecution;
27     using System.Globalization;
28     using System.Configuration;
29
30     //
31     // BaseLoggingObject - used to disable logging,
32     //  this is just a base class that does nothing.
33     //
34
35     internal class BaseLoggingObject {
36
37         internal BaseLoggingObject() {
38         }
39
40         internal virtual void EnterFunc(string funcname) {
41         }
42
43         internal virtual void LeaveFunc(string funcname) {
44         }
45
46         internal virtual void DumpArrayToConsole() {
47         }
48
49         internal virtual void PrintLine(string msg) {
50         }
51
52         internal virtual void DumpArray(bool shouldClose) {
53         }
54
55         internal virtual void DumpArrayToFile(bool shouldClose) {
56         }
57
58         internal virtual void Flush() {
59         }
60
61         internal virtual void Flush(bool close) {
62         }
63
64         internal virtual void LoggingMonitorTick() {
65         }
66
67         internal virtual void Dump(byte[] buffer) {
68         }
69
70         internal virtual void Dump(byte[] buffer, int length) {
71         }
72
73         internal virtual void Dump(byte[] buffer, int offset, int length) {
74         }
75
76         internal virtual void Dump(IntPtr pBuffer, int offset, int length) {
77         }
78
79     } // class BaseLoggingObject
80
81 #if TRAVE
82     /// <internalonly/>
83     /// <devdoc>
84     /// </devdoc>
85     internal class LoggingObject : BaseLoggingObject {
86         public ArrayList _Logarray;
87         private Hashtable _ThreadNesting;
88         private int _AddCount;
89         private StreamWriter _Stream;
90         private int _IamAlive;
91         private int _LastIamAlive;
92         private bool _Finalized = false;
93         private double _NanosecondsPerTick;
94         private int _StartMilliseconds;
95         private long _StartTicks;
96
97         internal LoggingObject() : base() {
98             _Logarray      = new ArrayList();
99             _ThreadNesting = new Hashtable();
100             _AddCount      = 0;
101             _IamAlive      = 0;
102             _LastIamAlive  = -1;
103
104             if (GlobalLog.s_UsePerfCounter) {
105                 long ticksPerSecond;
106                 SafeNativeMethods.QueryPerformanceFrequency(out ticksPerSecond);
107                 _NanosecondsPerTick = 10000000.0/(double)ticksPerSecond;
108                 SafeNativeMethods.QueryPerformanceCounter(out _StartTicks);
109             } else {
110                 _StartMilliseconds = Environment.TickCount;
111             }
112         }
113
114         //
115         // LoggingMonitorTick - this function is run from the monitor thread,
116         //  and used to check to see if there any hangs, ie no logging
117         //  activitity
118         //
119
120         internal override void LoggingMonitorTick() {
121             if ( _LastIamAlive == _IamAlive ) {
122                 PrintLine("================= Error TIMEOUT - HANG DETECTED =================");
123                 DumpArray(true);
124             }
125             _LastIamAlive = _IamAlive;
126         }
127
128         internal override void EnterFunc(string funcname) {
129             if (_Finalized) {
130                 return;
131             }
132             IncNestingCount();
133             ValidatePush(funcname);
134             PrintLine(funcname);
135         }
136
137         internal override void LeaveFunc(string funcname) {
138             if (_Finalized) {
139                 return;
140             }
141             PrintLine(funcname);
142             DecNestingCount();
143             ValidatePop(funcname);
144         }
145
146         internal override void DumpArrayToConsole() {
147             for (int i=0; i < _Logarray.Count; i++) {
148                 Console.WriteLine((string) _Logarray[i]);
149             }
150         }
151
152         internal override void PrintLine(string msg) {
153             if (_Finalized) {
154                 return;
155             }
156             string spc = "";
157
158             _IamAlive++;
159
160             spc = GetNestingString();
161
162             string tickString = "";
163
164             if (GlobalLog.s_UsePerfCounter) {
165                 long nowTicks;
166                 SafeNativeMethods.QueryPerformanceCounter(out nowTicks);
167                 if (_StartTicks>nowTicks) { // counter reset, restart from 0
168                     _StartTicks = nowTicks;
169                 }
170                 nowTicks -= _StartTicks;
171                 if (GlobalLog.s_UseTimeSpan) {
172                     tickString = new TimeSpan((long)(nowTicks*_NanosecondsPerTick)).ToString();
173                     // note: TimeSpan().ToString() doesn't return the uSec part
174                     // if its 0. .ToString() returns [H*]HH:MM:SS:uuuuuuu, hence 16
175                     if (tickString.Length < 16) {
176                         tickString += ".0000000";
177                     }
178                 }
179                 else {
180                     tickString = ((double)nowTicks*_NanosecondsPerTick/10000).ToString("f3");
181                 }
182             }
183             else {
184                 int nowMilliseconds = Environment.TickCount;
185                 if (_StartMilliseconds>nowMilliseconds) {
186                     _StartMilliseconds = nowMilliseconds;
187                 }
188                 nowMilliseconds -= _StartMilliseconds;
189                 if (GlobalLog.s_UseTimeSpan) {
190                     tickString = new TimeSpan(nowMilliseconds*10000).ToString();
191                     // note: TimeSpan().ToString() doesn't return the uSec part
192                     // if its 0. .ToString() returns [H*]HH:MM:SS:uuuuuuu, hence 16
193                     if (tickString.Length < 16) {
194                         tickString += ".0000000";
195                     }
196                 }
197                 else {
198                     tickString = nowMilliseconds.ToString();
199                 }
200             }
201
202             uint threadId = 0;
203
204             if (GlobalLog.s_UseThreadId) {
205                 try {
206                     object threadData = Thread.GetData(GlobalLog.s_ThreadIdSlot);
207                     if (threadData!= null) {
208                         threadId = (uint)threadData;
209                     }
210
211                 }
212                 catch(Exception exception) {
213                     if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {
214                         throw;
215                     }
216                 }
217                 if (threadId == 0) {
218                     threadId = UnsafeNclNativeMethods.GetCurrentThreadId();
219                     Thread.SetData(GlobalLog.s_ThreadIdSlot, threadId);
220                 }
221             }
222             if (threadId == 0) {
223                 threadId = (uint)Thread.CurrentThread.GetHashCode();
224             }
225
226             string str = "[" + threadId.ToString("x8") + "]"  + " (" +tickString+  ") " + spc + msg;
227
228             lock(this) {
229                 _AddCount++;
230                 _Logarray.Add(str);
231                 int MaxLines = GlobalLog.s_DumpToConsole ? 0 : GlobalLog.MaxLinesBeforeSave;
232                 if (_AddCount > MaxLines) {
233                     _AddCount = 0;
234                     DumpArray(false);
235                     _Logarray = new ArrayList();
236                 }
237             }
238         }
239
240         internal override void DumpArray(bool shouldClose) {
241             if ( GlobalLog.s_DumpToConsole ) {
242                 DumpArrayToConsole();
243             } else {
244                 DumpArrayToFile(shouldClose);
245             }
246         }
247
248         internal unsafe override void Dump(byte[] buffer, int offset, int length) {
249             //if (!GlobalLog.s_DumpWebData) {
250             //    return;
251             //}
252             if (buffer==null) {
253                 PrintLine("(null)");
254                 return;
255             }
256             if (offset > buffer.Length) {
257                 PrintLine("(offset out of range)");
258                 return;
259             }
260             if (length > GlobalLog.s_MaxDumpSize) {
261                 PrintLine("(printing " + GlobalLog.s_MaxDumpSize.ToString() + " out of " + length.ToString() + ")");
262                 length = GlobalLog.s_MaxDumpSize;
263             }
264             if ((length < 0) || (length > buffer.Length - offset)) {
265                 length = buffer.Length - offset;
266             }
267             fixed (byte* pBuffer = buffer) {
268                 Dump((IntPtr)pBuffer, offset, length);
269             }
270         }
271
272         internal unsafe override void Dump(IntPtr pBuffer, int offset, int length) {
273             //if (!GlobalLog.s_DumpWebData) {
274             //    return;
275             //}
276             if (pBuffer==IntPtr.Zero || length<0) {
277                 PrintLine("(null)");
278                 return;
279             }
280             if (length > GlobalLog.s_MaxDumpSize) {
281                 PrintLine("(printing " + GlobalLog.s_MaxDumpSize.ToString() + " out of " + length.ToString() + ")");
282                 length = GlobalLog.s_MaxDumpSize;
283             }
284             byte* buffer = (byte*)pBuffer + offset;
285             Dump(buffer, length);
286         }
287
288         unsafe void Dump(byte* buffer, int length) {
289             do {
290                 int offset = 0;
291                 int n = Math.Min(length, 16);
292                 string disp = ((IntPtr)buffer).ToString("X8") + " : " + offset.ToString("X8") + " : ";
293                 byte current;
294                 for (int i = 0; i < n; ++i) {
295                     current = *(buffer + i);
296                     disp += current.ToString("X2") + ((i == 7) ? '-' : ' ');
297                 }
298                 for (int i = n; i < 16; ++i) {
299                     disp += "   ";
300                 }
301                 disp += ": ";
302                 for (int i = 0; i < n; ++i) {
303                     current = *(buffer + i);
304                     disp += ((current < 0x20) || (current > 0x7e)) ? '.' : (char)current;
305                 }
306                 PrintLine(disp);
307                 offset += n;
308                 buffer += n;
309                 length -= n;
310             } while (length > 0);
311         }
312
313         // SECURITY: This is dev-debugging class and we need some permissions
314         // to use it under trust-restricted environment as well.
315         [PermissionSet(SecurityAction.Assert, Name="FullTrust")]
316         internal override void DumpArrayToFile(bool shouldClose) {
317             lock (this) {
318                 if (!shouldClose) {
319                     if (_Stream==null) {
320                         string mainLogFileRoot = GlobalLog.s_RootDirectory + "System.Net";
321                         string mainLogFile = mainLogFileRoot;
322                         for (int k=0; k<20; k++) {
323                             if (k>0) {
324                                 mainLogFile = mainLogFileRoot + "." + k.ToString();
325                             }
326                             string fileName = mainLogFile + ".log";
327                             if (!File.Exists(fileName)) {
328                                 try {
329                                     _Stream = new StreamWriter(fileName);
330                                     break;
331                                 }
332                                 catch (Exception exception) {
333                                     if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {
334                                         throw;
335                                     }
336                                     if (exception is SecurityException || exception is UnauthorizedAccessException) {
337                                         // can't be CAS (we assert) this is an ACL issue
338                                         break;
339                                     }
340                                 }
341                             }
342                         }
343                         if (_Stream==null) {
344                             _Stream = StreamWriter.Null;
345                         }
346                         // write a header with information about the Process and the AppDomain
347                         _Stream.Write("# MachineName: " + Environment.MachineName + "\r\n");
348                         _Stream.Write("# ProcessName: " + Process.GetCurrentProcess().ProcessName + " (pid: " + Process.GetCurrentProcess().Id + ")\r\n");
349                         _Stream.Write("# AppDomainId: " + AppDomain.CurrentDomain.Id + "\r\n");
350                         _Stream.Write("# CurrentIdentity: " + WindowsIdentity.GetCurrent().Name + "\r\n");
351                         _Stream.Write("# CommandLine: " + Environment.CommandLine + "\r\n");
352                         _Stream.Write("# ClrVersion: " + Environment.Version + "\r\n");
353                         _Stream.Write("# CreationDate: " + DateTime.Now.ToString("g") + "\r\n");
354                     }
355                 }
356                 try {
357                     if (_Logarray!=null) {
358                         for (int i=0; i<_Logarray.Count; i++) {
359                             _Stream.Write((string)_Logarray[i]);
360                             _Stream.Write("\r\n");
361                         }
362
363                         if (_Logarray.Count > 0 && _Stream != null)
364                             _Stream.Flush();
365                     }
366                 }
367                 catch (Exception exception) {
368                     if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {
369                         throw;
370                     }
371                 }
372                 if (shouldClose && _Stream!=null) {
373                     try {
374                         _Stream.Close();
375                     }
376                     catch (ObjectDisposedException) { }
377                     _Stream = null;
378                 }
379             }
380         }
381
382         internal override void Flush() {
383             Flush(false);
384         }
385
386         internal override void Flush(bool close) {
387             lock (this) {
388                 if (!GlobalLog.s_DumpToConsole) {
389                     DumpArrayToFile(close);
390                     _AddCount = 0;
391                 }
392             }
393         }
394
395         private class ThreadInfoData {
396             public ThreadInfoData(string indent) {
397                 Indent = indent;
398                 NestingStack = new Stack();
399             }
400             public string Indent;
401             public Stack NestingStack;
402         };
403
404         string IndentString {
405             get {
406                 string indent = " ";
407                 Object obj = _ThreadNesting[Thread.CurrentThread.GetHashCode()];
408                 if (!GlobalLog.s_DebugCallNesting) {
409                     if (obj == null) {
410                         _ThreadNesting[Thread.CurrentThread.GetHashCode()] = indent;
411                     } else {
412                         indent = (String) obj;
413                     }
414                 } else {
415                     ThreadInfoData threadInfo = obj as ThreadInfoData;
416                     if (threadInfo == null) {
417                         threadInfo = new ThreadInfoData(indent);
418                         _ThreadNesting[Thread.CurrentThread.GetHashCode()] = threadInfo;
419                     }
420                     indent = threadInfo.Indent;
421                 }
422                 return indent;
423             }
424             set {
425                 Object obj = _ThreadNesting[Thread.CurrentThread.GetHashCode()];
426                 if (obj == null) {
427                     return;
428                 }
429                 if (!GlobalLog.s_DebugCallNesting) {
430                     _ThreadNesting[Thread.CurrentThread.GetHashCode()] = value;
431                 } else {
432                     ThreadInfoData threadInfo = obj as ThreadInfoData;
433                     if (threadInfo == null) {
434                         threadInfo = new ThreadInfoData(value);
435                         _ThreadNesting[Thread.CurrentThread.GetHashCode()] = threadInfo;
436                     }
437                     threadInfo.Indent = value;
438                 }
439             }
440         }
441
442         [System.Diagnostics.Conditional("TRAVE")]
443         private void IncNestingCount() {
444             IndentString = IndentString + " ";
445         }
446
447         [System.Diagnostics.Conditional("TRAVE")]
448         private void DecNestingCount() {
449             string indent = IndentString;
450             if (indent.Length>1) {
451                 try {
452                     indent = indent.Substring(1);
453                 }
454                 catch {
455                     indent = string.Empty;
456                 }
457             }
458             if (indent.Length==0) {
459                 indent = "< ";
460             }
461             IndentString = indent;
462         }
463
464         private string GetNestingString() {
465             return IndentString;
466         }
467
468         [System.Diagnostics.Conditional("TRAVE")]
469         private void ValidatePush(string name) {
470             if (GlobalLog.s_DebugCallNesting) {
471                 Object obj = _ThreadNesting[Thread.CurrentThread.GetHashCode()];
472                 ThreadInfoData threadInfo = obj as ThreadInfoData;
473                 if (threadInfo == null) {
474                     return;
475                 }
476                 threadInfo.NestingStack.Push(name);
477             }
478         }
479
480         [System.Diagnostics.Conditional("TRAVE")]
481         private void ValidatePop(string name) {
482             if (GlobalLog.s_DebugCallNesting) {
483                 try {
484                     Object obj = _ThreadNesting[Thread.CurrentThread.GetHashCode()];
485                     ThreadInfoData threadInfo = obj as ThreadInfoData;
486                     if (threadInfo == null) {
487                         return;
488                     }
489                     if (threadInfo.NestingStack.Count == 0) {
490                         PrintLine("++++====" + "Poped Empty Stack for :"+name);
491                     }
492                     string popedName = (string) threadInfo.NestingStack.Pop();
493                     string [] parsedList = popedName.Split(new char [] {'(',')',' ','.',':',',','#'});
494                     foreach (string element in parsedList) {
495                         if (element != null && element.Length > 1 && name.IndexOf(element) != -1) {
496                             return;
497                         }
498                     }
499                     PrintLine("++++====" + "Expected:" + popedName + ": got :" + name + ": StackSize:"+threadInfo.NestingStack.Count);
500                     // relevel the stack
501                     while(threadInfo.NestingStack.Count>0) {
502                         string popedName2 = (string) threadInfo.NestingStack.Pop();
503                         string [] parsedList2 = popedName2.Split(new char [] {'(',')',' ','.',':',',','#'});
504                         foreach (string element2 in parsedList2) {
505                             if (element2 != null && element2.Length > 1 && name.IndexOf(element2) != -1) {
506                                 return;
507                             }
508                         }
509                     }
510                 }
511                 catch {
512                     PrintLine("++++====" + "ValidatePop failed for: "+name);
513                 }
514             }
515         }
516
517
518         ~LoggingObject() {
519             if(!_Finalized) {
520                 _Finalized = true;
521                 lock(this) {
522                     DumpArray(true);
523                 }
524             }
525         }
526
527
528     } // class LoggingObject
529
530     internal static class TraveHelper {
531         private static readonly string Hexizer = "0x{0:x}";
532         internal static string ToHex(object value) {
533             return String.Format(Hexizer, value);
534         }
535     }
536 #endif // TRAVE
537
538 #if TRAVE 
539     internal class IntegerSwitch : BooleanSwitch {
540         public IntegerSwitch(string switchName, string switchDescription) : base(switchName, switchDescription) {
541         }
542         public new int Value {
543             get {
544                 return base.SwitchSetting;
545             }
546         }
547     }
548
549 #endif
550
551     [Flags]
552     internal enum ThreadKinds
553     {
554         Unknown        = 0x0000,
555
556         // Mutually exclusive.
557         User           = 0x0001,     // Thread has entered via an API.
558         System         = 0x0002,     // Thread has entered via a system callback (e.g. completion port) or is our own thread.
559
560         // Mutually exclusive.
561         Sync           = 0x0004,     // Thread should block.
562         Async          = 0x0008,     // Thread should not block.
563
564         // Mutually exclusive, not always known for a user thread.  Never changes.
565         Timer          = 0x0010,     // Thread is the timer thread.  (Can't call user code.)
566         CompletionPort = 0x0020,     // Thread is a ThreadPool completion-port thread.
567         Worker         = 0x0040,     // Thread is a ThreadPool worker thread.
568         Finalization   = 0x0080,     // Thread is the finalization thread.
569         Other          = 0x0100,     // Unknown source.
570
571         OwnerMask      = User | System,
572         SyncMask       = Sync | Async,
573         SourceMask     = Timer | CompletionPort | Worker | Finalization | Other,
574
575         // Useful "macros"
576         SafeSources    = SourceMask & ~(Timer | Finalization),  // Methods that "unsafe" sources can call must be explicitly marked.
577         ThreadPool     = CompletionPort | Worker,               // Like Thread.CurrentThread.IsThreadPoolThread
578     }
579
580     /// <internalonly/>
581     /// <devdoc>
582     /// </devdoc>
583     internal static class GlobalLog {
584
585         //
586         // Logging Initalization - I need to disable Logging code, and limit
587         //  the effect it has when it is dissabled, so I use a bool here.
588         //
589         //  This can only be set when the logging code is built and enabled.
590         //  By specifing the "CSC_DEFINES=/D:TRAVE" in the build environment,
591         //  this code will be built and then checks against an enviroment variable
592         //  and a BooleanSwitch to see if any of the two have enabled logging.
593         //
594
595         private static BaseLoggingObject Logobject = GlobalLog.LoggingInitialize();
596 #if TRAVE
597         internal static LocalDataStoreSlot s_ThreadIdSlot;
598         internal static bool s_UseThreadId;
599         internal static bool s_UseTimeSpan;
600         internal static bool s_DumpWebData;
601         internal static bool s_UsePerfCounter;
602         internal static bool s_DebugCallNesting;
603         internal static bool s_DumpToConsole;
604         internal static int s_MaxDumpSize;
605         internal static string s_RootDirectory;
606
607         //
608         // Logging Config Variables -  below are list of consts that can be used to config
609         //  the logging,
610         //
611
612         // Max number of lines written into a buffer, before a save is invoked
613         // s_DumpToConsole disables.
614         public const int MaxLinesBeforeSave = 0;
615
616 #endif
617         [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
618         private static BaseLoggingObject LoggingInitialize() {
619
620 #if MONO_FEATURE_WEB_STACK
621 #if DEBUG
622             if (GetSwitchValue("SystemNetLogging", "System.Net logging module", false) &&
623                 GetSwitchValue("SystemNetLog_ConnectionMonitor", "System.Net connection monitor thread", false)) {
624                 InitConnectionMonitor();
625             }
626 #endif // DEBUG
627 #endif // MONO_FEATURE_WEB_STACK
628 #if TRAVE
629             // by default we'll log to c:\temp\ so that non interactive services (like w3wp.exe) that don't have environment
630             // variables can easily be debugged, note that the ACLs of the directory might need to be adjusted
631             if (!GetSwitchValue("SystemNetLog_OverrideDefaults", "System.Net log override default settings", false)) {
632                 s_ThreadIdSlot = Thread.AllocateDataSlot();
633                 s_UseThreadId = true;
634                 s_UseTimeSpan = true;
635                 s_DumpWebData = true;
636                 s_MaxDumpSize = 256;
637                 s_UsePerfCounter = true;
638                 s_DebugCallNesting = true;
639                 s_DumpToConsole = false;
640                 s_RootDirectory = "C:\\Temp\\";
641                 return new LoggingObject();
642             }
643             if (GetSwitchValue("SystemNetLogging", "System.Net logging module", false)) {
644                 s_ThreadIdSlot = Thread.AllocateDataSlot();
645                 s_UseThreadId = GetSwitchValue("SystemNetLog_UseThreadId", "System.Net log display system thread id", false);
646                 s_UseTimeSpan = GetSwitchValue("SystemNetLog_UseTimeSpan", "System.Net log display ticks as TimeSpan", false);
647                 s_DumpWebData = GetSwitchValue("SystemNetLog_DumpWebData", "System.Net log display HTTP send/receive data", false);
648                 s_MaxDumpSize = GetSwitchValue("SystemNetLog_MaxDumpSize", "System.Net log max size of display data", 256);
649                 s_UsePerfCounter = GetSwitchValue("SystemNetLog_UsePerfCounter", "System.Net log use QueryPerformanceCounter() to get ticks ", false);
650                 s_DebugCallNesting = GetSwitchValue("SystemNetLog_DebugCallNesting", "System.Net used to debug call nesting", false);
651                 s_DumpToConsole = GetSwitchValue("SystemNetLog_DumpToConsole", "System.Net log to console", false);
652                 s_RootDirectory = GetSwitchValue("SystemNetLog_RootDirectory", "System.Net root directory of log file", string.Empty);
653                 return new LoggingObject();
654             }
655 #endif // TRAVE
656             return new BaseLoggingObject();
657         }
658
659
660 #if TRAVE 
661         private static string GetSwitchValue(string switchName, string switchDescription, string defaultValue) {
662             new EnvironmentPermission(PermissionState.Unrestricted).Assert();
663             try {
664                 defaultValue = Environment.GetEnvironmentVariable(switchName);
665             }
666             finally {
667                 EnvironmentPermission.RevertAssert();
668             }
669             return defaultValue;
670         }
671
672         private static int GetSwitchValue(string switchName, string switchDescription, int defaultValue) {
673             IntegerSwitch theSwitch = new IntegerSwitch(switchName, switchDescription);
674             if (theSwitch.Enabled) {
675                 return theSwitch.Value;
676             }
677             new EnvironmentPermission(PermissionState.Unrestricted).Assert();
678             try {
679                 string environmentVar = Environment.GetEnvironmentVariable(switchName);
680                 if (environmentVar!=null) {
681                     defaultValue = Int32.Parse(environmentVar.Trim(), CultureInfo.InvariantCulture);
682                 }
683             }
684             finally {
685                 EnvironmentPermission.RevertAssert();
686             }
687             return defaultValue;
688         }
689
690 #endif
691
692 #if TRAVE || DEBUG
693         private static bool GetSwitchValue(string switchName, string switchDescription, bool defaultValue) {
694             BooleanSwitch theSwitch = new BooleanSwitch(switchName, switchDescription);
695             new EnvironmentPermission(PermissionState.Unrestricted).Assert();
696             try {
697                 if (theSwitch.Enabled) {
698                     return true;
699                 }
700                 string environmentVar = Environment.GetEnvironmentVariable(switchName);
701                 defaultValue = environmentVar!=null && environmentVar.Trim()=="1";
702             }
703             catch (ConfigurationException) { }
704             finally {
705                 EnvironmentPermission.RevertAssert();
706             }
707             return defaultValue;
708         }
709 #endif // TRAVE || DEBUG
710
711
712         // Enables thread tracing, detects mis-use of threads.
713 #if DEBUG
714         [ThreadStatic]
715         private static Stack<ThreadKinds> t_ThreadKindStack;
716
717         private static Stack<ThreadKinds> ThreadKindStack
718         {
719             get
720             {
721                 if (t_ThreadKindStack == null)
722                 {
723                     t_ThreadKindStack = new Stack<ThreadKinds>();
724                 }
725                 return t_ThreadKindStack;
726             }
727         }
728 #endif
729                 
730         internal static ThreadKinds CurrentThreadKind
731         {
732             get
733             {
734 #if DEBUG
735                 return ThreadKindStack.Count > 0 ? ThreadKindStack.Peek() : ThreadKinds.Other;
736 #else
737                 return ThreadKinds.Unknown;
738 #endif
739             }
740         }
741
742 #if DEBUG
743         // ifdef'd instead of conditional since people are forced to handle the return value.
744         // [Conditional("DEBUG")]
745         [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
746         internal static IDisposable SetThreadKind(ThreadKinds kind)
747         {
748             if ((kind & ThreadKinds.SourceMask) != ThreadKinds.Unknown)
749             {
750                 throw new InternalException();
751             }
752
753             // Ignore during shutdown.
754             if (NclUtilities.HasShutdownStarted)
755             {
756                 return null;
757             }
758
759             ThreadKinds threadKind = CurrentThreadKind;
760             ThreadKinds source = threadKind & ThreadKinds.SourceMask;
761
762 #if TRAVE
763             // Special warnings when doing dangerous things on a thread.
764             if ((threadKind & ThreadKinds.User) != 0 && (kind & ThreadKinds.System) != 0)
765             {
766                 Print("WARNING: Thread changed from User to System; user's thread shouldn't be hijacked.");
767             }
768
769             if ((threadKind & ThreadKinds.Async) != 0 && (kind & ThreadKinds.Sync) != 0)
770             {
771                 Print("WARNING: Thread changed from Async to Sync, may block an Async thread.");
772             }
773             else if ((threadKind & (ThreadKinds.Other | ThreadKinds.CompletionPort)) == 0 && (kind & ThreadKinds.Sync) != 0)
774             {
775                 Print("WARNING: Thread from a limited resource changed to Sync, may deadlock or bottleneck.");
776             }
777 #endif
778
779             ThreadKindStack.Push(
780                 (((kind & ThreadKinds.OwnerMask) == 0 ? threadKind : kind) & ThreadKinds.OwnerMask) |
781                 (((kind & ThreadKinds.SyncMask) == 0 ? threadKind : kind) & ThreadKinds.SyncMask) |
782                 (kind & ~(ThreadKinds.OwnerMask | ThreadKinds.SyncMask)) |
783                 source);
784
785 #if TRAVE
786             if (CurrentThreadKind != threadKind)
787             {
788                 Print("Thread becomes:(" + CurrentThreadKind.ToString() + ")");
789             }
790 #endif
791
792             return new ThreadKindFrame();
793         }
794
795         private class ThreadKindFrame : IDisposable
796         {
797             private int m_FrameNumber;
798
799             internal ThreadKindFrame()
800             {
801                 m_FrameNumber = ThreadKindStack.Count;
802             }
803
804             void IDisposable.Dispose()
805             {
806                 // Ignore during shutdown.
807                 if (NclUtilities.HasShutdownStarted)
808                 {
809                     return;
810                 }
811
812                 if (m_FrameNumber != ThreadKindStack.Count)
813                 {
814                     throw new InternalException();
815                 }
816
817                 ThreadKinds previous = ThreadKindStack.Pop();
818
819 #if TRAVE
820                 if (CurrentThreadKind != previous)
821                 {
822                     Print("Thread reverts:(" + CurrentThreadKind.ToString() + ")");
823                 }
824 #endif
825             }
826         }
827 #endif
828
829         [Conditional("DEBUG")]
830         [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
831         internal static void SetThreadSource(ThreadKinds source)
832         {
833 #if DEBUG
834             if ((source & ThreadKinds.SourceMask) != source || source == ThreadKinds.Unknown)
835             {
836                 throw new ArgumentException("Must specify the thread source.", "source");
837             }
838
839             if (ThreadKindStack.Count == 0)
840             {
841                 ThreadKindStack.Push(source);
842                 return;
843             }
844
845             if (ThreadKindStack.Count > 1)
846             {
847                 Print("WARNING: SetThreadSource must be called at the base of the stack, or the stack has been corrupted.");
848                 while (ThreadKindStack.Count > 1)
849                 {
850                     ThreadKindStack.Pop();
851                 }
852             }
853
854             if (ThreadKindStack.Peek() != source)
855             {
856                 // SQL can fail to clean up the stack, leaving the default Other at the bottom.  Replace it.
857                 Print("WARNING: The stack has been corrupted.");
858                 ThreadKinds last = ThreadKindStack.Pop() & ThreadKinds.SourceMask;
859                 Assert(last == source || last == ThreadKinds.Other, "Thread source changed.|Was:({0}) Now:({1})", last, source);
860                 ThreadKindStack.Push(source);
861             }
862 #endif
863         }
864
865         [Conditional("DEBUG")]
866         [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
867         internal static void ThreadContract(ThreadKinds kind, string errorMsg)
868         {
869             ThreadContract(kind, ThreadKinds.SafeSources, errorMsg);
870         }
871
872         [Conditional("DEBUG")]
873         [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
874         internal static void ThreadContract(ThreadKinds kind, ThreadKinds allowedSources, string errorMsg)
875         {
876             if ((kind & ThreadKinds.SourceMask) != ThreadKinds.Unknown || (allowedSources & ThreadKinds.SourceMask) != allowedSources)
877             {
878                 throw new InternalException();
879             }
880
881             ThreadKinds threadKind = CurrentThreadKind;
882             Assert((threadKind & allowedSources) != 0, errorMsg, "Thread Contract Violation.|Expected source:({0}) Actual source:({1})", allowedSources , threadKind & ThreadKinds.SourceMask);
883             Assert((threadKind & kind) == kind, errorMsg, "Thread Contract Violation.|Expected kind:({0}) Actual kind:({1})", kind, threadKind & ~ThreadKinds.SourceMask);
884         }
885
886 #if DEBUG
887         // Enables auto-hang detection, which will "snap" a log on hang
888         internal static bool EnableMonitorThread = false;
889
890         // Default value for hang timer
891 #if FEATURE_PAL // ROTORTODO - after speedups (like real JIT and GC) remove this
892         public const int DefaultTickValue = 1000*60*5; // 5 minutes
893 #else
894         public const int DefaultTickValue = 1000*60; // 60 secs
895 #endif // FEATURE_PAL
896 #endif // DEBUG
897
898         [System.Diagnostics.Conditional("TRAVE")]
899         public static void AddToArray(string msg) {
900 #if TRAVE
901             GlobalLog.Logobject.PrintLine(msg);
902 #endif
903         }
904
905         [System.Diagnostics.Conditional("TRAVE")]
906         public static void Ignore(object msg) {
907         }
908
909         [System.Diagnostics.Conditional("TRAVE")]
910         [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
911         public static void Print(string msg) {
912 #if TRAVE
913             GlobalLog.Logobject.PrintLine(msg);
914 #endif
915         }
916
917         [System.Diagnostics.Conditional("TRAVE")]
918         public static void PrintHex(string msg, object value) {
919 #if TRAVE
920             GlobalLog.Logobject.PrintLine(msg+TraveHelper.ToHex(value));
921 #endif
922         }
923
924         [System.Diagnostics.Conditional("TRAVE")]
925         public static void Enter(string func) {
926 #if TRAVE
927             GlobalLog.Logobject.EnterFunc(func + "(*none*)");
928 #endif
929         }
930
931         [System.Diagnostics.Conditional("TRAVE")]
932         public static void Enter(string func, string parms) {
933 #if TRAVE
934             GlobalLog.Logobject.EnterFunc(func + "(" + parms + ")");
935 #endif
936         }
937
938         [Conditional("DEBUG")]
939         [Conditional("_FORCE_ASSERTS")]
940         [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
941         public static void Assert(bool condition, string messageFormat, params object[] data)
942         {
943             if (!condition)
944             {
945                 string fullMessage = string.Format(CultureInfo.InvariantCulture, messageFormat, data);
946                 int pipeIndex = fullMessage.IndexOf('|');
947                 if (pipeIndex == -1)
948                 {
949                     Assert(fullMessage);
950                 }
951                 else
952                 {
953                     int detailLength = fullMessage.Length - pipeIndex - 1;
954                     Assert(fullMessage.Substring(0, pipeIndex), detailLength > 0 ? fullMessage.Substring(pipeIndex + 1, detailLength) : null);
955                 }
956             }
957         }
958
959         [Conditional("DEBUG")]
960         [Conditional("_FORCE_ASSERTS")]
961         [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
962         public static void Assert(string message)
963         {
964             Assert(message, null);
965         }
966
967         [Conditional("DEBUG")]
968         [Conditional("_FORCE_ASSERTS")]
969         [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
970         public static void Assert(string message, string detailMessage)
971         {
972             try
973             {
974                 Print("Assert: " + message + (!string.IsNullOrEmpty(detailMessage) ? ": " + detailMessage : ""));
975                 Print("*******");
976                 Logobject.DumpArray(false);
977             }
978             finally
979             {
980 #if DEBUG && !STRESS
981                 Debug.Assert(false, message, detailMessage);
982 #else
983 #if MONO_NOT_IMPLEMENTED
984                 UnsafeNclNativeMethods.DebugBreak();
985 #endif
986                 Debugger.Break();
987 #endif
988             }
989         }
990
991
992         [System.Diagnostics.Conditional("TRAVE")]
993         public static void LeaveException(string func, Exception exception) {
994 #if TRAVE
995             GlobalLog.Logobject.LeaveFunc(func + " exception " + ((exception!=null) ? exception.Message : String.Empty));
996 #endif
997         }
998
999         [System.Diagnostics.Conditional("TRAVE")]
1000         public static void Leave(string func) {
1001 #if TRAVE
1002             GlobalLog.Logobject.LeaveFunc(func + " returns ");
1003 #endif
1004         }
1005
1006         [System.Diagnostics.Conditional("TRAVE")]
1007         public static void Leave(string func, string result) {
1008 #if TRAVE
1009             GlobalLog.Logobject.LeaveFunc(func + " returns " + result);
1010 #endif
1011         }
1012
1013         [System.Diagnostics.Conditional("TRAVE")]
1014         public static void Leave(string func, int returnval) {
1015 #if TRAVE
1016             GlobalLog.Logobject.LeaveFunc(func + " returns " + returnval.ToString());
1017 #endif
1018         }
1019
1020         [System.Diagnostics.Conditional("TRAVE")]
1021         public static void Leave(string func, bool returnval) {
1022 #if TRAVE
1023             GlobalLog.Logobject.LeaveFunc(func + " returns " + returnval.ToString());
1024 #endif
1025         }
1026
1027         [System.Diagnostics.Conditional("TRAVE")]
1028         public static void DumpArray() {
1029 #if TRAVE
1030             GlobalLog.Logobject.DumpArray(true);
1031 #endif
1032         }
1033
1034         [System.Diagnostics.Conditional("TRAVE")]
1035         public static void Dump(byte[] buffer) {
1036 #if TRAVE
1037             Logobject.Dump(buffer, 0, buffer!=null ? buffer.Length : -1);
1038 #endif
1039         }
1040
1041         [System.Diagnostics.Conditional("TRAVE")]
1042         public static void Dump(byte[] buffer, int length) {
1043 #if TRAVE
1044             Logobject.Dump(buffer, 0, length);
1045 #endif
1046         }
1047
1048         [System.Diagnostics.Conditional("TRAVE")]
1049         public static void Dump(byte[] buffer, int offset, int length) {
1050 #if TRAVE
1051             Logobject.Dump(buffer, offset, length);
1052 #endif
1053         }
1054
1055         [System.Diagnostics.Conditional("TRAVE")]
1056         public static void Dump(IntPtr buffer, int offset, int length) {
1057 #if TRAVE
1058             Logobject.Dump(buffer, offset, length);
1059 #endif
1060         }
1061
1062 #if MONO_FEATURE_WEB_STACK
1063
1064 #if DEBUG
1065         private class HttpWebRequestComparer : IComparer {
1066             public int Compare(
1067                    object x1,
1068                    object y1
1069                    ) {
1070
1071                 HttpWebRequest x = (HttpWebRequest) x1;
1072                 HttpWebRequest y = (HttpWebRequest) y1;
1073
1074                 if (x.GetHashCode() == y.GetHashCode()) {
1075                     return 0;
1076                 } else if (x.GetHashCode() < y.GetHashCode()) {
1077                     return -1;
1078                 } else if (x.GetHashCode() > y.GetHashCode()) {
1079                     return 1;
1080                 }
1081
1082                 return 0;
1083             }
1084         }
1085
1086         private class ConnectionMonitorEntry {
1087             public HttpWebRequest m_Request;
1088             public int m_Flags;
1089             public DateTime m_TimeAdded;
1090             public Connection m_Connection;
1091
1092             public ConnectionMonitorEntry(HttpWebRequest request, Connection connection, int flags) {
1093                 m_Request = request;
1094                 m_Connection = connection;
1095                 m_Flags = flags;
1096                 m_TimeAdded = DateTime.Now;
1097             }
1098         }
1099
1100         private static volatile ManualResetEvent s_ShutdownEvent;
1101         private static volatile SortedList s_RequestList;
1102
1103         internal const int WaitingForReadDoneFlag = 0x1;
1104 #endif
1105
1106 #if DEBUG
1107         private static void ConnectionMonitor() {
1108             while(! s_ShutdownEvent.WaitOne(DefaultTickValue, false)) {
1109                 if (GlobalLog.EnableMonitorThread) {
1110 #if TRAVE
1111                     GlobalLog.Logobject.LoggingMonitorTick();
1112 #endif
1113                 }
1114
1115                 int hungCount = 0;
1116                 lock (s_RequestList) {
1117                     DateTime dateNow = DateTime.Now;
1118                     DateTime dateExpired = dateNow.AddSeconds(-DefaultTickValue);
1119                     foreach (ConnectionMonitorEntry monitorEntry in s_RequestList.GetValueList() ) {
1120                         if (monitorEntry != null &&
1121                             (dateExpired > monitorEntry.m_TimeAdded))
1122                         {
1123                             hungCount++;
1124 #if TRAVE
1125                             GlobalLog.Print("delay:" + (dateNow - monitorEntry.m_TimeAdded).TotalSeconds +
1126                                 " req#" + monitorEntry.m_Request.GetHashCode() +
1127                                 " cnt#" + monitorEntry.m_Connection.GetHashCode() +
1128                                 " flags:" + monitorEntry.m_Flags);
1129
1130 #endif
1131                             monitorEntry.m_Connection.DebugMembers(monitorEntry.m_Request.GetHashCode());
1132                         }
1133                     }
1134                 }
1135                 Assert(hungCount == 0, "Warning: Hang Detected on Connection(s) of greater than {0} ms.  {1} request(s) hung.|Please Dump System.Net.GlobalLog.s_RequestList for pending requests, make sure your streams are calling Close(), and that your destination server is up.", DefaultTickValue, hungCount);
1136             }
1137         }
1138 #endif // DEBUG
1139
1140 #if DEBUG
1141         [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
1142         internal static void AppDomainUnloadEvent(object sender, EventArgs e) {
1143             s_ShutdownEvent.Set();
1144         }
1145 #endif
1146
1147 #if DEBUG
1148         [System.Diagnostics.Conditional("DEBUG")]
1149         private static void InitConnectionMonitor() {
1150             s_RequestList = new SortedList(new HttpWebRequestComparer(), 10);
1151             s_ShutdownEvent = new ManualResetEvent(false);
1152             AppDomain.CurrentDomain.DomainUnload += new EventHandler(AppDomainUnloadEvent);
1153             AppDomain.CurrentDomain.ProcessExit += new EventHandler(AppDomainUnloadEvent);
1154             Thread threadMonitor = new Thread(new ThreadStart(ConnectionMonitor));
1155             threadMonitor.IsBackground = true;
1156             threadMonitor.Start();
1157         }
1158 #endif
1159
1160         [System.Diagnostics.Conditional("DEBUG")]
1161         internal static void DebugAddRequest(HttpWebRequest request, Connection connection, int flags) {
1162 #if DEBUG
1163             // null if the connection monitor is off
1164             if(s_RequestList == null)
1165                 return;
1166
1167             lock(s_RequestList) {
1168                 Assert(!s_RequestList.ContainsKey(request), "s_RequestList.ContainsKey(request)|A HttpWebRequest should not be submitted twice.");
1169
1170                 ConnectionMonitorEntry requestEntry =
1171                     new ConnectionMonitorEntry(request, connection, flags);
1172
1173                 try {
1174                     s_RequestList.Add(request, requestEntry);
1175                 } catch {
1176                 }
1177             }
1178 #endif
1179         }
1180
1181         [System.Diagnostics.Conditional("DEBUG")]
1182         internal static void DebugRemoveRequest(HttpWebRequest request) {
1183 #if DEBUG
1184             // null if the connection monitor is off
1185             if(s_RequestList == null)
1186                 return;
1187
1188             lock(s_RequestList) {
1189                 Assert(s_RequestList.ContainsKey(request), "!s_RequestList.ContainsKey(request)|A HttpWebRequest should not be removed twice.");
1190
1191                 try {
1192                     s_RequestList.Remove(request);
1193                 } catch {
1194                 }
1195             }
1196 #endif
1197         }
1198
1199         [System.Diagnostics.Conditional("DEBUG")]
1200         internal static void DebugUpdateRequest(HttpWebRequest request, Connection connection, int flags) {
1201 #if DEBUG
1202             // null if the connection monitor is off
1203             if(s_RequestList == null)
1204                 return;
1205
1206             lock(s_RequestList) {
1207                 if(!s_RequestList.ContainsKey(request)) {
1208                     return;
1209                 }
1210
1211                 ConnectionMonitorEntry requestEntry =
1212                     new ConnectionMonitorEntry(request, connection, flags);
1213
1214                 try {
1215                     s_RequestList.Remove(request);
1216                     s_RequestList.Add(request, requestEntry);
1217                 } catch {
1218                 }
1219             }
1220 #endif
1221         }
1222 #endif
1223     } // class GlobalLog
1224 } // namespace System.Net