[corlib] Added MVID metadata to StackTrace
[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                 static StackTrace ()
67                 {
68                         metadataHandlers = new Dictionary<string, Func<StackTrace, string>> ();
69
70                         InitMetadataHandlers ();
71                 }
72
73                 [MethodImplAttribute (MethodImplOptions.NoInlining)]
74                 public StackTrace ()
75                 {
76                         init_frames (METHODS_TO_SKIP, false);
77                 }
78
79                 [MethodImplAttribute (MethodImplOptions.NoInlining)]
80                 public StackTrace (bool fNeedFileInfo)
81                 {
82                         init_frames (METHODS_TO_SKIP, fNeedFileInfo);
83                 }
84
85                 [MethodImplAttribute (MethodImplOptions.NoInlining)]
86                 public StackTrace (int skipFrames)
87                 {
88                         init_frames (skipFrames, false);
89                 }
90
91                 [MethodImplAttribute (MethodImplOptions.NoInlining)]
92                 public StackTrace (int skipFrames, bool fNeedFileInfo)
93                 {
94                         init_frames (skipFrames, fNeedFileInfo);
95                 }
96
97                 [MethodImplAttribute (MethodImplOptions.NoInlining)]
98                 void init_frames (int skipFrames, bool fNeedFileInfo)
99                 {
100                         if (skipFrames < 0)
101                                 throw new ArgumentOutOfRangeException ("< 0", "skipFrames");
102
103                         StackFrame sf;
104                         var l = new List<StackFrame> ();
105
106                         skipFrames += 2;
107                         
108                         while ((sf = new StackFrame (skipFrames, fNeedFileInfo)) != null &&
109                                sf.GetMethod () != null) {
110                                 
111                                 l.Add (sf);
112                                 skipFrames++;
113                         };
114
115                         debug_info = fNeedFileInfo;
116                         frames = l.ToArray ();
117                 }
118                 
119                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
120                 extern static StackFrame [] get_trace (Exception e, int skipFrames, bool fNeedFileInfo);
121
122                 public StackTrace (Exception e)
123                         : this (e, METHODS_TO_SKIP, false)
124                 {
125                 }
126
127                 public StackTrace (Exception e, bool fNeedFileInfo)
128                         : this (e, METHODS_TO_SKIP, fNeedFileInfo)
129                 {
130                 }
131
132                 public StackTrace (Exception e, int skipFrames)
133                         : this (e, skipFrames, false)
134                 {
135                 }
136
137                 public StackTrace (Exception e, int skipFrames, bool fNeedFileInfo)
138                 {
139                         if (e == null)
140                                 throw new ArgumentNullException ("e");
141                         if (skipFrames < 0)
142                                 throw new ArgumentOutOfRangeException ("< 0", "skipFrames");
143
144                         frames = get_trace (e, skipFrames, fNeedFileInfo);
145
146                         captured_traces = e.captured_traces;
147                 }
148
149                 public StackTrace (StackFrame frame)
150                 {
151                         this.frames = new StackFrame [1];
152                         this.frames [0] = frame;
153                 }
154
155                 [MonoLimitation ("Not possible to create StackTraces from other threads")]
156                 [Obsolete]
157                 public StackTrace (Thread targetThread, bool needFileInfo)
158                 {
159                         if (targetThread == Thread.CurrentThread){
160                                 init_frames (METHODS_TO_SKIP, needFileInfo);
161                                 return;
162                         }
163                         
164                         throw new NotImplementedException ();
165                 }
166
167                 internal StackTrace (StackFrame[] frames) {
168                         this.frames = frames;
169                 }
170
171                 public virtual int FrameCount {
172                         get {
173                                 return (frames == null) ? 0 : frames.Length;
174                         }
175                 }
176
177                 public virtual StackFrame GetFrame (int index)
178                 {
179                         if ((index < 0) || (index >= FrameCount)) {
180                                 return null;
181                         }
182
183                         return frames [index];
184                 }
185
186                 [ComVisibleAttribute (false)]
187                 public virtual StackFrame[] GetFrames ()
188                 {
189                         return frames;
190                 }
191
192                 bool AddFrames (StringBuilder sb)
193                 {
194                         bool printOffset;
195                         string debugInfo, indentation;
196                         string unknown = Locale.GetText ("<unknown method>");
197
198                         indentation = "  ";
199                         debugInfo = Locale.GetText (" in {0}:{1} ");
200
201                         var newline = String.Format ("{0}{1}{2} ", Environment.NewLine, indentation,
202                                         Locale.GetText ("at"));
203
204                         int i;
205                         for (i = 0; i < FrameCount; i++) {
206                                 StackFrame frame = GetFrame (i);
207                                 if (i == 0)
208                                         sb.AppendFormat ("{0}{1} ", indentation, Locale.GetText ("at"));
209                                 else
210                                         sb.Append (newline);
211
212                                 if (frame.GetMethod () == null) {
213                                         string internal_name = frame.GetInternalMethodName ();
214                                         if (internal_name != null)
215                                                 sb.Append (internal_name);
216                                         else
217                                                 sb.AppendFormat ("<0x{0:x5} + 0x{1:x5}> {2}", frame.GetMethodAddress (), frame.GetNativeOffset (), unknown);
218                                 } else {
219                                         GetFullNameForStackTrace (sb, frame.GetMethod ());
220
221                                         if (frame.GetILOffset () == -1) {
222                                                 sb.AppendFormat (" <0x{0:x5} + 0x{1:x5}>", frame.GetMethodAddress (), frame.GetNativeOffset ());
223                                                 if (frame.GetMethodIndex () != 0xffffff)
224                                                         sb.AppendFormat (" {0}", frame.GetMethodIndex ());
225                                         } else {
226                                                 sb.AppendFormat (" [0x{0:x5}]", frame.GetILOffset ());
227                                         }
228
229                                         sb.AppendFormat (debugInfo, frame.GetSecureFileName (),
230                                                          frame.GetFileLineNumber ());
231                                 }
232                         }
233
234                         return i != 0;
235                 }
236
237                 public static void GetFullNameForStackTrace (StringBuilder sb, MethodBase mi)
238                 {
239                         var declaringType = mi.DeclaringType;
240                         if (declaringType.IsGenericType && !declaringType.IsGenericTypeDefinition)
241                                 declaringType = declaringType.GetGenericTypeDefinition ();
242
243                         // Get generic definition
244                         const BindingFlags bindingflags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
245                         foreach (var m in declaringType.GetMethods (bindingflags)) {
246                                 if (m.MetadataToken == mi.MetadataToken) {
247                                         mi = m;
248                                         break;
249                                 }
250                         }
251
252                         sb.Append (declaringType.ToString ());
253
254                         sb.Append (".");
255                         sb.Append (mi.Name);
256
257                         if (mi.IsGenericMethod) {
258                                 Type[] gen_params = mi.GetGenericArguments ();
259                                 sb.Append ("[");
260                                 for (int j = 0; j < gen_params.Length; j++) {
261                                         if (j > 0)
262                                                 sb.Append (",");
263                                         sb.Append (gen_params [j].Name);
264                                 }
265                                 sb.Append ("]");
266                         }
267
268                         ParameterInfo[] p = mi.GetParameters ();
269
270                         sb.Append (" (");
271                         for (int i = 0; i < p.Length; ++i) {
272                                 if (i > 0)
273                                         sb.Append (", ");
274
275                                 Type pt = p[i].ParameterType;
276                                 if (pt.IsGenericType && ! pt.IsGenericTypeDefinition)
277                                         pt = pt.GetGenericTypeDefinition ();
278
279                                 sb.Append (pt.ToString());
280
281                                 if (p [i].Name != null) {
282                                         sb.Append (" ");
283                                         sb.Append (p [i].Name);
284                                 }
285                         }
286                         sb.Append (")");
287                 }               
288
289                 public override string ToString ()
290                 {
291                         StringBuilder sb = new StringBuilder ();
292
293                         //
294                         // Add traces captured using ExceptionDispatchInfo
295                         //
296                         if (captured_traces != null) {
297                                 foreach (var t in captured_traces) {
298                                         if (!t.AddFrames (sb))
299                                                 continue;
300
301                                         sb.Append (Environment.NewLine);
302                                         sb.Append ("--- End of stack trace from previous location where exception was thrown ---");
303                                         sb.Append (Environment.NewLine);
304                                 }
305                         }
306
307                         AddFrames (sb);
308
309                         foreach (var handler in metadataHandlers) {
310                                 var lines = handler.Value (this);
311                                 using (var reader = new StringReader (lines)) {
312                                         string line;
313                                         while ((line = reader.ReadLine()) != null) {
314                                                 sb.AppendLine ();
315                                                 sb.Append (string.Format ("[{0}] {1}", handler.Key, line));
316                                         }
317                                 }
318                         }
319
320                         return sb.ToString ();
321                 }
322
323                 
324                 internal String ToString (TraceFormat traceFormat)
325                 {
326                         // TODO:
327                         return ToString ();
328                 }
329
330                 static void InitMetadataHandlers ()
331                 {
332                         string aotid = Assembly.GetAotId ();
333                         if (aotid != "00000000-0000-0000-0000-000000000000")
334                                 AddMetadataHandler ("AOTID", st => { return aotid; });
335
336                         AddMetadataHandler ("MVID", st => {
337                                 var mvidLines = new Dictionary<Guid, List<int>> ();
338                                 var frames = st.GetFrames ();
339                                 for (var lineNumber = 0; lineNumber < frames.Length; lineNumber++) {
340                                         var mvid = frames[lineNumber].GetMethod ().Module.ModuleVersionId;
341                                         if (!mvidLines.ContainsKey (mvid))
342                                                 mvidLines.Add (mvid, new List<int> ());
343
344                                         mvidLines[mvid].Add (lineNumber);
345                                 }
346
347                                 var sb = new StringBuilder ();
348                                 foreach (var kv in mvidLines) {
349                                         var mvid = kv.Key.ToString ().ToUpper ();
350                                         sb.AppendLine (string.Format ("{0} {1}", mvid, string.Join (",", kv.Value)));
351                                 }
352
353                                 return sb.ToString ();
354                         });
355                 }
356
357                 private static void AddMetadataHandler (string id, Func<StackTrace, string> handler)
358                 {
359                         metadataHandlers.Add (id, handler);
360                 }
361         }
362 }