1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Collections.Generic; 6 using System.Diagnostics; 7 using System.IO; 8 9 using Internal.TypeSystem; 10 11 namespace ILCompiler 12 { 13 public class UtcStackTraceEmissionPolicy : StackTraceEmissionPolicy 14 { 15 /// <summary> 16 /// List of exception files to load. 17 /// </summary> 18 List<string> _stackTraceExceptionFiles = new List<string>(); 19 20 /// <summary> 21 /// Explicitly blacklisted namespaces. 22 /// </summary> 23 HashSet<string> _namespaceBlacklist = new HashSet<string>(); 24 25 /// <summary> 26 /// Explicitly whitelisted namespaces. 27 /// </summary> 28 HashSet<string> _namespaceWhitelist = new HashSet<string>(); 29 30 /// <summary> 31 /// Explicitly blacklisted types. 32 /// </summary> 33 HashSet<string> _typeBlacklist = new HashSet<string>(); 34 35 /// <summary> 36 /// Explicitly whitelisted types. 37 /// </summary> 38 HashSet<string> _typeWhitelist = new HashSet<string>(); 39 40 /// <summary> 41 /// Cache of explicitly enabled / disabled types after their eligibility has been resolved. 42 /// </summary> 43 Dictionary<TypeDesc, bool> _cachedTypeEligibility = new Dictionary<TypeDesc, bool>(); 44 UtcStackTraceEmissionPolicy()45 public UtcStackTraceEmissionPolicy() 46 { 47 // load the default exception file next to the NUTC app 48 LoadExceptionFile(Path.Combine(GetAppExeDirectory(), "StackTraceExceptions.txt")); 49 } 50 ShouldIncludeMethod(MethodDesc method)51 public override bool ShouldIncludeMethod(MethodDesc method) 52 { 53 DefType type = method.GetTypicalMethodDefinition().OwningType as DefType; 54 if (type != null) 55 return !IsTypeExplicitlyDisabled(type); 56 return false; 57 } 58 59 /// <summary> 60 /// Check explicit type / namespace blacklist and update the cache. 61 /// </summary> IsTypeExplicitlyDisabled(DefType type)62 bool IsTypeExplicitlyDisabled(DefType type) 63 { 64 bool result; 65 if (_cachedTypeEligibility.TryGetValue(type, out result)) 66 { 67 return result; 68 } 69 70 string typeName; 71 result = IsTypeExplicitlyDisabledInner(type, out typeName); 72 _cachedTypeEligibility.Add(type, result); 73 return result; 74 } 75 76 /// <summary> 77 /// Check explicit type / namespace blacklist ignoring cache management. Optionally output the type name. 78 /// </summary> IsTypeExplicitlyDisabledInner(DefType type, out string outputTypeName)79 bool IsTypeExplicitlyDisabledInner(DefType type, out string outputTypeName) 80 { 81 string typeName; 82 bool isTypeDisabled = false; 83 MetadataType metaDataType = type as MetadataType; 84 85 if (metaDataType != null && metaDataType.ContainingType != null) 86 { 87 // Nested type 88 isTypeDisabled = IsTypeExplicitlyDisabledInner(metaDataType.ContainingType, out typeName); 89 typeName = typeName + '+' + metaDataType.Name; 90 } 91 else 92 { 93 // Namespace type 94 typeName = type.Name; 95 int lastPeriod = typeName.LastIndexOf('.'); 96 string namespaceName = null; 97 98 if (lastPeriod != -1) 99 { 100 namespaceName = typeName.Substring(0, lastPeriod); 101 } 102 103 isTypeDisabled = _namespaceBlacklist.Contains(namespaceName) && _namespaceWhitelist.Contains(namespaceName); 104 } 105 106 if (_typeBlacklist.Contains(typeName)) 107 { 108 isTypeDisabled = true; 109 } 110 111 if (_typeWhitelist.Contains(typeName)) 112 { 113 isTypeDisabled = false; 114 } 115 116 outputTypeName = typeName; 117 return isTypeDisabled; 118 } 119 120 /// <summary> 121 /// Identify the directory where the main executable resides. 122 /// </summary> GetAppExeDirectory()123 string GetAppExeDirectory() 124 { 125 #if PROJECTN 126 var process = Process.GetCurrentProcess(); 127 string fullPath = process.MainModule.FileName; 128 return Path.GetDirectoryName(fullPath); 129 #else 130 Debug.Assert(false); 131 return null; 132 #endif 133 } 134 135 /// <summary> 136 /// Load the default exception file and possibly additional custom stack trace exception files. 137 /// </summary> LoadExceptionFile(string exceptionFileName)138 void LoadExceptionFile(string exceptionFileName) 139 { 140 #if PROJECTN 141 if (!File.Exists(exceptionFileName)) 142 return; 143 144 const int LeaderCharacterCount = 2; 145 146 using (TextReader tr = File.OpenText(exceptionFileName)) 147 { 148 string line = tr.ReadLine(); 149 while (line != null) 150 { 151 // Currently supported leader character sequences are: 152 // N+nnn .. explicitly whitelist namespace nnn 153 // N-nnn .. explicitly blacklist namespace nnn 154 // T+ttt .. explicitly whitelist namespace-qualified type ttt (nested types separated by +) 155 // T-ttt .. explicitly blacklist namespace-qualified type ttt 156 // Type specifications override namespace specifications. 157 // Whenever both whitelist and blacklist is specified for an element, whitelist wins. 158 159 // Reserve empty lines and lines starting with # for comments 160 if (line.Length > LeaderCharacterCount && line[0] != '#') 161 { 162 char char1 = line[0]; 163 char char2 = line[1]; 164 string arg = line.Substring(2); 165 166 if (char1 == 'N' && char2 == '+') 167 { 168 _namespaceWhitelist.Add(arg); 169 } 170 else if (char1 == 'N' && char2 == '-') 171 { 172 _namespaceBlacklist.Add(arg); 173 } 174 else if (char1 == 'T' && char2 == '+') 175 { 176 _typeWhitelist.Add(arg); 177 } 178 else if (char1 == 'T' && char2 == '-') 179 { 180 _typeBlacklist.Add(arg); 181 } 182 else 183 { 184 // unexpected pattern 185 Debug.Assert(false); 186 } 187 } 188 189 line = tr.ReadLine(); 190 } 191 } 192 #endif 193 } 194 } 195 } 196