/// <summary> /// Adds or updates a performance counter categories information. /// </summary> /// <param name="categoryName">Name of the category.</param> /// <param name="typeReference">The type reference.</param> /// <param name="assembly">The assembly.</param> /// <param name="categoryHelp">The category help.</param> public static void Set( [NotNull] string categoryName, [NotNull] TypeReference typeReference, [NotNull] string assembly, string categoryHelp) { if (categoryName == null) { throw new ArgumentNullException("categoryName"); } if (typeReference == null) { throw new ArgumentNullException("typeReference"); } if (assembly == null) { throw new ArgumentNullException("assembly"); } try { PerformanceType performanceType = PerformanceType.Get(typeReference); PerfCategory category = _categories.GetOrAdd( categoryName, // ReSharper disable once AssignNullToNotNullAttribute n => new PerfCategory(assembly, performanceType, n)); Debug.Assert(category != null); // Type cannot change. if (category.PerformanceType != performanceType) { Logger.Add( Level.Error, "The '{0}' performance counter category was declared more than once with different types ('{1}' and '{2}') in assembly '{3}'.", categoryName, category.PerformanceType, performanceType, assembly); return; } // Only update category help if not already set. if (!string.IsNullOrWhiteSpace(categoryHelp) && string.IsNullOrWhiteSpace(category.CategoryHelp)) { category.CategoryHelp = categoryHelp; } // Add assembly if not seen before if (!category._assemblies.Contains(assembly)) { category._assemblies.Add(assembly); } } catch (Exception e) { Logger.Add( Level.Error, "Failed to set performance category information for category '{0}' in assembly '{1}'. {2}", categoryName, assembly, e.Message); } }
/// <summary> /// Loads the specified assembly and checks for performance counter use. /// </summary> /// <param name="assemblyPath">The assembly path.</param> private static void Load(string assemblyPath) { AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyPath, ReaderParameters); Debug.Assert(assembly != null); Debug.Assert(assembly.Name != null); if (assembly.Name.Name == "WebApplications.Utilities.Performance") { return; } // Find any modules that reference logging. // ReSharper disable once AssignNullToNotNullAttribute ModuleDefinition[] referencingModules = assembly.Modules .Where( // ReSharper disable PossibleNullReferenceException, AssignNullToNotNullAttribute m => m.AssemblyReferences .FirstOrDefault( ar => ar.Name == "WebApplications.Utilities.Performance") != null) // ReSharper restore PossibleNullReferenceException, AssignNullToNotNullAttribute .ToArray(); if (referencingModules.Length < 1) { return; } Queue <string> lastStrings = new Queue <string>(2); foreach (ModuleDefinition module in referencingModules) { Debug.Assert(module != null); Debug.Assert(module.Types != null); foreach (TypeDefinition type in module.Types) { Debug.Assert(type != null); Debug.Assert(type.Methods != null); foreach (MethodDefinition method in type.Methods) { Debug.Assert(method != null); if (!method.HasBody) { continue; } lastStrings.Clear(); Debug.Assert(method.Body != null); Debug.Assert(method.Body.Instructions != null); foreach (Instruction instr in method.Body.Instructions) { Debug.Assert(instr != null); // Detect string literals loaded onto evaluation stack if (instr.OpCode.Code == Code.Ldstr) { // We track last two load strings. if (lastStrings.Count > 1) { lastStrings.Dequeue(); } lastStrings.Enqueue(instr.Operand as string); continue; } // Detect nulls being loaded onto evaluation stack if (instr.OpCode.Code == Code.Ldnull) { // We track last two load strings. if (lastStrings.Count > 1) { lastStrings.Dequeue(); } lastStrings.Enqueue(null); continue; } if (instr.OpCode.Code != Code.Call) { // If we have any ops other than NewObj after our loads then the loads aren't for us. lastStrings.Clear(); continue; } // Make sure we have the right method signature. GenericInstanceMethod methodReference = instr.Operand as GenericInstanceMethod; if ((methodReference == null) || (methodReference.Name != "GetOrAdd") || !methodReference.HasGenericArguments || !methodReference.IsGenericInstance || // ReSharper disable PossibleNullReferenceException (methodReference.GenericArguments.Count != 1) || (methodReference.Parameters.Count != 2) || (methodReference.Parameters[0].ParameterType.FullName != "System.String") || (methodReference.Parameters[1].ParameterType.FullName != "System.String")) { // ReSharper restore PossibleNullReferenceException continue; } // Make sure it's on the right type. TypeReference typeReference = methodReference.DeclaringType; if ((typeReference == null) || (typeReference.FullName != "WebApplications.Utilities.Performance.PerfCategory")) { continue; } TypeReference perfCategoryType = methodReference.GenericArguments.First(); Debug.Assert(perfCategoryType != null); if (lastStrings.Count > 1) { int pCount = methodReference.Parameters.Count(); bool isTimer = pCount == 4; Debug.Assert(isTimer || (pCount == 2)); string categoryName = lastStrings.Dequeue(); string categoryHelp = lastStrings.Dequeue(); // We have a constructor set performance information. if (!String.IsNullOrWhiteSpace(categoryName)) { Logger.Add( Level.Low, "The '{0}' assembly calls PerfCategory.GetOrAdd<{1}>(\"{2}\", {3}).", assemblyPath, perfCategoryType.Name, categoryName, categoryHelp == null ? "null" : "\"" + categoryHelp + "\""); PerfCategory.Set( categoryName, perfCategoryType, // ReSharper disable once AssignNullToNotNullAttribute assembly.Name.Name, categoryHelp); } else { Logger.Add( Level.Error, "Performance counter creation found in '{0}' but category name was null or empty - make sure you use inline strings as constructor parameters. Press any key to continue", assemblyPath); } } else { Logger.Add( Level.Error, "Performance counter creation found in '{0}' but could not find category name and or category help - make sure you use inline strings as constructor parameters. Press any key to continue", assemblyPath); } lastStrings.Clear(); } } } } }