[ci] Another Python 3 fix in babysitter
[mono.git] / mcs / class / corlib / System.Diagnostics / StackTrace.cs
1 //
2 // System.Diagnostics.StackTrace.cs
3 //
4 // Author:
5 //      Alexander Klyubin (klyubin@aqris.com)
6 //      Dietmar Maurer (dietmar@ximian.com)
7 //
8 // (C) 2001
9 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System.Collections.Generic;
32 using System.Globalization;
33 using System.Reflection;
34 using System.Runtime.CompilerServices;
35 using System.Runtime.InteropServices;
36 using System.Security;
37 using System.Security.Permissions;
38 using System.Text;
39 using System.Threading;
40 using System.IO;
41
42 namespace System.Diagnostics {
43
44         [Serializable]
45         [ComVisible (true)]
46         [MonoTODO ("Serialized objects are not compatible with .NET")]
47         public class StackTrace {
48
49         // TraceFormat is Used to specify options for how the 
50         // string-representation of a StackTrace should be generated.
51         internal enum TraceFormat 
52         {
53             Normal,
54             TrailingNewLine,        // include a trailing new line character
55             NoResourceLookup    // to prevent infinite resource recusion
56         }
57
58                 public const int METHODS_TO_SKIP = 0;
59
60                 private StackFrame[] frames;
61                 readonly StackTrace[] captured_traces;
62                 private bool debug_info;
63
64                 private static Dictionary<string, Func<StackTrace, string>> metadataHandlers;
65
66                 [MethodImplAttribute (MethodImplOptions.NoInlining)]
67                 public StackTrace ()
68                 {
69                         init_frames (METHODS_TO_SKIP, false);
70                 }
71
72                 [MethodImplAttribute (MethodImplOptions.NoInlining)]
73                 public StackTrace (bool fNeedFileInfo)
74                 {
75                         init_frames (METHODS_TO_SKIP, fNeedFileInfo);
76                 }
77
78                 [MethodImplAttribute (MethodImplOptions.NoInlining)]
79                 public StackTrace (int skipFrames)
80                 {
81                         init_frames (skipFrames, false);
82                 }
83
84                 [MethodImplAttribute (MethodImplOptions.NoInlining)]
85                 public StackTrace (int skipFrames, bool fNeedFileInfo)
86                 {
87                         init_frames (skipFrames, fNeedFileInfo);
88                 }
89
90                 [MethodImplAttribute (MethodImplOptions.NoInlining)]
91                 void init_frames (int skipFrames, bool fNeedFileInfo)
92                 {
93                         if (skipFrames < 0)
94                                 throw new ArgumentOutOfRangeException ("< 0", "skipFrames");
95
96                         StackFrame sf;
97                         var l = new List<StackFrame> ();
98
99                         skipFrames += 2;
100                         
101                         while ((sf = new StackFrame (skipFrames, fNeedFileInfo)) != null &&
102                                sf.GetMethod () != null) {
103                                 
104                                 l.Add (sf);
105                                 skipFrames++;
106                         };
107
108                         debug_info = fNeedFileInfo;
109                         frames = l.ToArray ();
110                 }
111                 
112                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
113                 extern static StackFrame [] get_trace (Exception e, int skipFrames, bool fNeedFileInfo);
114
115                 public StackTrace (Exception e)
116                         : this (e, METHODS_TO_SKIP, false)
117                 {
118                 }
119
120                 public StackTrace (Exception e, bool fNeedFileInfo)
121                         : this (e, METHODS_TO_SKIP, fNeedFileInfo)
122                 {
123                 }
124
125                 public StackTrace (Exception e, int skipFrames)
126                         : this (e, skipFrames, false)
127                 {
128                 }
129
130                 public StackTrace (Exception e, int skipFrames, bool fNeedFileInfo)
131                 {
132                         if (e == null)
133                                 throw new ArgumentNullException ("e");
134                         if (skipFrames < 0)
135                                 throw new ArgumentOutOfRangeException ("< 0", "skipFrames");
136
137                         frames = get_trace (e, skipFrames, fNeedFileInfo);
138
139                         captured_traces = e.captured_traces;
140                 }
141
142                 public StackTrace (StackFrame frame)
143                 {
144                         this.frames = new StackFrame [1];
145                         this.frames [0] = frame;
146                 }
147
148                 [MonoLimitation ("Not possible to create StackTraces from other threads")]
149                 [Obsolete]
150                 public StackTrace (Thread targetThread, bool needFileInfo)
151                 {
152                         if (targetThread == Thread.CurrentThread){
153                                 init_frames (METHODS_TO_SKIP, needFileInfo);
154                                 return;
155                         }
156                         
157                         throw new NotImplementedException ();
158                 }
159
160                 internal StackTrace (StackFrame[] frames) {
161                         this.frames = frames;
162                 }
163
164                 public virtual int FrameCount {
165                         get {
166                                 return (frames == null) ? 0 : frames.Length;
167                         }
168                 }
169
170                 public virtual StackFrame GetFrame (int index)
171                 {
172                         if ((index < 0) || (index >= FrameCount)) {
173                                 return null;
174                         }
175
176                         return frames [index];
177                 }
178
179                 [ComVisibleAttribute (false)]
180                 public virtual StackFrame[] GetFrames ()
181                 {
182                         return frames;
183                 }
184
185                 bool AddFrames (StringBuilder sb)
186                 {
187                         bool printOffset;
188                         string debugInfo, indentation;
189                         string unknown = Locale.GetText ("<unknown method>");
190
191                         indentation = "  ";
192                         debugInfo = Locale.GetText (" in {0}:{1} ");
193
194                         var newline = String.Format ("{0}{1}{2} ", Environment.NewLine, indentation,
195                                         Locale.GetText ("at"));
196
197                         int i;
198                         for (i = 0; i < FrameCount; i++) {
199                                 StackFrame frame = GetFrame (i);
200                                 if (i == 0)
201                                         sb.AppendFormat ("{0}{1} ", indentation, Locale.GetText ("at"));
202                                 else
203                                         sb.Append (newline);
204
205                                 if (frame.GetMethod () == null) {
206                                         string internal_name = frame.GetInternalMethodName ();
207                                         if (internal_name != null)
208                                                 sb.Append (internal_name);
209                                         else
210                                                 sb.AppendFormat ("<0x{0:x5} + 0x{1:x5}> {2}", frame.GetMethodAddress (), frame.GetNativeOffset (), unknown);
211                                 } else {
212                                         GetFullNameForStackTrace (sb, frame.GetMethod ());
213
214                                         if (frame.GetILOffset () == -1) {
215                                                 sb.AppendFormat (" <0x{0:x5} + 0x{1:x5}>", frame.GetMethodAddress (), frame.GetNativeOffset ());
216                                                 if (frame.GetMethodIndex () != 0xffffff)
217                                                         sb.AppendFormat (" {0}", frame.GetMethodIndex ());
218                                         } else {
219                                                 sb.AppendFormat (" [0x{0:x5}]", frame.GetILOffset ());
220                                         }
221
222                                         sb.AppendFormat (debugInfo, frame.GetSecureFileName (),
223                                                          frame.GetFileLineNumber ());
224                                 }
225                         }
226
227                         return i != 0;
228                 }
229
230                 public static void GetFullNameForStackTrace (StringBuilder sb, MethodBase mi)
231                 {
232                         var declaringType = mi.DeclaringType;
233                         if (declaringType.IsGenericType && !declaringType.IsGenericTypeDefinition)
234                                 declaringType = declaringType.GetGenericTypeDefinition ();
235
236                         // Get generic definition
237                         const BindingFlags bindingflags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
238                         foreach (var m in declaringType.GetMethods (bindingflags)) {
239                                 if (m.MetadataToken == mi.MetadataToken) {
240                                         mi = m;
241                                         break;
242                                 }
243                         }
244
245                         sb.Append (declaringType.ToString ());
246
247                         sb.Append (".");
248                         sb.Append (mi.Name);
249
250                         if (mi.IsGenericMethod) {
251                                 Type[] gen_params = mi.GetGenericArguments ();
252                                 sb.Append ("[");
253                                 for (int j = 0; j < gen_params.Length; j++) {
254                                         if (j > 0)
255                                                 sb.Append (",");
256                                         sb.Append (gen_params [j].Name);
257                                 }
258                                 sb.Append ("]");
259                         }
260
261                         ParameterInfo[] p = mi.GetParameters ();
262
263                         sb.Append (" (");
264                         for (int i = 0; i < p.Length; ++i) {
265                                 if (i > 0)
266                                         sb.Append (", ");
267
268                                 Type pt = p[i].ParameterType;
269                                 if (pt.IsGenericType && ! pt.IsGenericTypeDefinition)
270                                         pt = pt.GetGenericTypeDefinition ();
271
272                                 sb.Append (pt.ToString());
273
274                                 if (p [i].Name != null) {
275                                         sb.Append (" ");
276                                         sb.Append (p [i].Name);
277                                 }
278                         }
279                         sb.Append (")");
280                 }               
281
282                 public override string ToString ()
283                 {
284                         StringBuilder sb = new StringBuilder ();
285
286                         //
287                         // Add traces captured using ExceptionDispatchInfo
288                         //
289                         if (captured_traces != null) {
290                                 foreach (var t in captured_traces) {
291                                         if (!t.AddFrames (sb))
292                                                 continue;
293
294                                         t.AddMetadata (sb);
295
296                                         sb.Append (Environment.NewLine);
297                                         sb.Append ("--- End of stack trace from previous location where exception was thrown ---");
298                                         sb.Append (Environment.NewLine);
299                                 }
300                         }
301
302                         AddFrames (sb);
303                         AddMetadata (sb);
304
305                         return sb.ToString ();
306                 }
307
308                 void AddMetadata (StringBuilder sb)
309                 {
310                         if (metadataHandlers == null)
311                                 InitMetadataHandlers ();
312
313                         foreach (var handler in metadataHandlers) {
314                                 var lines = handler.Value (this);
315                                 using (var reader = new StringReader (lines)) {
316                                         string line;
317                                         while ((line = reader.ReadLine()) != null) {
318                                                 sb.AppendLine ();
319                                                 sb.AppendFormat ("[{0}] {1}", handler.Key, line);
320                                         }
321                                 }
322                         }
323                 }
324
325                 internal String ToString (TraceFormat traceFormat)
326                 {
327                         // TODO:
328                         return ToString ();
329                 }
330
331                 static void InitMetadataHandlers ()
332                 {
333                         metadataHandlers = new Dictionary<string, Func<StackTrace, string>> (StringComparer.Ordinal);
334
335                         var aotid = Assembly.GetAotId ();
336                         if (aotid != null)
337                                 AddMetadataHandler ("AOTID", st => { return new Guid (aotid).ToString ("N"); });
338
339                         AddMetadataHandler ("MVID", st => {
340                                 var mvidLines = new Dictionary<Guid, List<int>> ();
341                                 var frames = st.GetFrames ();
342                                 for (var lineNumber = 0; lineNumber < frames.Length; lineNumber++) {
343                                         var method = frames[lineNumber].GetMethod ();
344                                         if (method == null)
345                                                 continue;
346                                         var mvid = method.Module.ModuleVersionId;
347
348                                         List<int> lines = null;
349                                         if (!mvidLines.TryGetValue (mvid, out lines)) {
350                                                 lines = new List<int> ();
351                                                 mvidLines.Add (mvid, lines);
352                                         }
353
354                                         lines.Add (lineNumber);
355                                 }
356
357                                 var mvids = new List<Guid> (mvidLines.Keys);
358                                 mvids.Sort ();
359
360                                 var sb = new StringBuilder ();
361                                 foreach (var mvid in mvids)
362                                         sb.AppendLine (string.Format ("{0} {1}", mvid.ToString ("N"), string.Join (",", mvidLines[mvid])));
363
364                                 return sb.ToString ();
365                         });
366                 }
367
368                 // This method signature should not change, apps can use it with reflection to add custom metadata handlers.
369                 private static void AddMetadataHandler (string id, Func<StackTrace, string> handler)
370                 {
371                         if (metadataHandlers == null)
372                                 InitMetadataHandlers ();
373
374                         metadataHandlers.Add (id, handler);
375                 }
376         }
377 }