2011-05-14 8 views
9

he creado un contenedor para Log4net (que yo pueda estar disminuyendo en favor de Nlog; no he decidido todavía), y sangrar los mensajes registrados como resultado de dar una idea de llamar a la estructura. Por ejemplo:Para el registro de C#, ¿cómo obtengo la profundidad de la pila de llamadas con una sobrecarga mínima?

2011-04-03 00:20:30,271 [CT] DEBUG -  Merlinia.ProcessManager.CentralThread.ProcessAdminCommand - ProcStart - User Info Repository 
2011-04-03 00:20:30,271 [CT] DEBUG -  Merlinia.ProcessManager.CentralThread.StartOneProcess - User Info Repository 
2011-04-03 00:20:30,411 [CT] DEBUG -  Merlinia.ProcessManager.CentralThread.SetProcessStatus - Process = User Info Repository, status = ProcStarting 
2011-04-03 00:20:30,411 [CT] DEBUG -  Merlinia.ProcessManager.CentralThread.SendProcessStatusInfo 
2011-04-03 00:20:30,411 [CT] DEBUG -   Merlinia.CommonClasses.MhlAdminLayer.SendToAllAdministrators - ProcessTable 
2011-04-03 00:20:30,411 [CT] DEBUG -   Merlinia.CommonClasses.MReflection.CopyToBinary 
2011-04-03 00:20:30,411 [CT] DEBUG -   Merlinia.CommonClasses.MReflection.CopyToBinary - False 
2011-04-03 00:20:30,411 [CT] DEBUG -   Merlinia.CommonClasses.MhlBasicLayer.SendToAllConnections - 228 - True - False 
2011-04-03 00:20:30,411 [CT] DEBUG -   Merlinia.CommonClasses.MmlNonThreaded.SendObject - 228 
2011-04-03 00:20:30,411 [CT] DEBUG -   Merlinia.CommonClasses.MllTcpSocket.SendMessage - 228 - True 
2011-04-03 00:20:32,174 [10] DEBUG - Merlinia.CommonClasses.MReflection.CreateFromBinary 
2011-04-03 00:20:32,174 [10] DEBUG -  Merlinia.CommonClasses.MReflection.CopyFromBinary - Bytes = 71 
2011-04-03 00:20:32,174 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.MessagingCallback - User Info Repository - ProcessInfoAndRequests 
2011-04-03 00:20:32,174 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.ProcessProcessInfoAndRequests - User Info Repository 

que hacerlo utilizando System.Diagnostics.StackTrace y contando StackFrames. hay alguna forma más eficiente de hacer esto:

Ahora aquí es la pregunta? Solo necesito determinar la profundidad (relativa) de la pila de llamadas, es decir, es la profundidad actual más o menos de lo que fue la última vez que se llamó a mi contenedor de registro. (Tenga en cuenta que en realidad no uso los objetos StackFrame - obtengo los nombres del método de otra manera.)

Estoy esperando alguna manera simple de alto rendimiento de consultar la profundidad de la pila de llamadas o el uso de la pila.

Respuesta

5

Simplemente use la propiedad StackTrace.FrameCount, y compárela con la FrameCount previamente registrada. Para su información, FrameCount es probablemente el método más rápido para recuperar el recuento de fotogramas real, ya que sólo devuelve el campo interno m_iNumOfFrames de nuevo a usted.

+0

Gracias por su respuesta. Puede que esté equivocado, pero supongo que cuando crea un objeto StackTrace, también se crean todos los objetos de StackFrame. ¿Estás diciendo que este no es el caso? – RenniePet

+3

nueva StackFrame() objeto no es tan caro si no lo uso, pero si realmente necesita el máximo rendimiento, sigue leyendo para algunos trenzado cosas Reflection.Emit: http://ayende.com/blog/3879/reducing- the-cost-of-getting-a-stack-trace –

+0

Guau, estoy impresionado. También siento que mis habilidades no están a la altura de lo que has hecho. Trataré de analizarlo y entenderlo más tarde cuando tenga más tiempo. Pero, ¿qué significa "Func "? (Estoy usando .Net 2, y esa construcción está marcada como indefinida). – RenniePet

2

Gracias a Teoman Soygul y especialmente a Oren Eini, cuyo blog Teoman proporcionado un enlace a.

El siguiente es código "prueba de concepto" que creo que es la solución que va a utilizar - aunque he de reconocer que no he hecho ninguna prueba de sincronización.

class TestProgram 
    { 
     static void Main(string[] args) 
     { 
     OneTimeSetup(); 

     int i = GetCallStackDepth(); // i = 10 on my test machine 
     i = AddOneToNesting();   // Now i = 11 
     } 


     private delegate object DGetStackFrameHelper(); 

     private static DGetStackFrameHelper _getStackFrameHelper; 

     private static FieldInfo _frameCount; 


     private static void OneTimeSetup() 
     { 
     Type stackFrameHelperType = 
      typeof(object).Assembly.GetType("System.Diagnostics.StackFrameHelper"); 


     MethodInfo getStackFramesInternal = 
      Type.GetType("System.Diagnostics.StackTrace, mscorlib").GetMethod(
          "GetStackFramesInternal", BindingFlags.Static | BindingFlags.NonPublic); 


     DynamicMethod dynamicMethod = new DynamicMethod(
         "GetStackFrameHelper", typeof(object), new Type[0], typeof(StackTrace), true); 

     ILGenerator generator = dynamicMethod.GetILGenerator(); 
     generator.DeclareLocal(stackFrameHelperType); 
     generator.Emit(OpCodes.Ldc_I4_0); 
     generator.Emit(OpCodes.Ldnull); 
     generator.Emit(OpCodes.Newobj, 
        stackFrameHelperType.GetConstructor(new Type[] { typeof(bool), typeof(Thread) })); 
     generator.Emit(OpCodes.Stloc_0); 
     generator.Emit(OpCodes.Ldloc_0); 
     generator.Emit(OpCodes.Ldc_I4_0); 
     generator.Emit(OpCodes.Ldnull); 
     generator.Emit(OpCodes.Call, getStackFramesInternal); 
     generator.Emit(OpCodes.Ldloc_0); 
     generator.Emit(OpCodes.Ret); 


     _getStackFrameHelper = 
        (DGetStackFrameHelper)dynamicMethod.CreateDelegate(typeof(DGetStackFrameHelper)); 


     _frameCount = stackFrameHelperType.GetField(
            "iFrameCount", BindingFlags.NonPublic | BindingFlags.Instance); 
     } 


     private static int GetCallStackDepth() 
     { 
     return (int)_frameCount.GetValue(_getStackFrameHelper()); 
     } 


     private static int AddOneToNesting() 
     { 
     return GetCallStackDepth(); 
     } 
    } 

EDIT: Esta versión no funciona para .Net Framework 4.5 después de una actualización de mscorlib.dll por Microsoft a finales de 2017. Véase otra respuesta que acabo de publicar una versión más reciente. (Dejo esta respuesta por el bien de la posteridad, y todavía funciona para .Net Framework 2.0 y 3.5.)

+0

+1, buen trabajo allí. –

+0

En realidad, no es casi 100% el código de Oren Eini, simplemente retrocedió a .Net 2. – RenniePet

2

Después de seis años y medio de servicio de confianza, de repente experimenté que muchos de mis programas eran fallando a fines de 2017, después de haber aplicado una actualización de Microsoft que incluía cambios en .Net Framework 4.5. Eso es lo que obtiene al escribir código que depende de las estructuras internas de datos no documentados en mscorlib.dll.

Esta versión del código funciona de nuevo, y también está diseñada para ser un poco más robusta ante posibles actualizaciones futuras de mscorlib.dll - con suerte simplemente falla con gracia y siempre devuelve cero. Pero todavía no hay garantías contra futuros cambios en mscorlib.dll que resulten en fallas futuras en este código.

/// <summary> 
    /// This test program demonstrates a faster way of getting call stack depth by avoiding getting a 
    /// StackTrace object. But you can't get the calling method names this way. 
    /// 
    /// See http://stackoverflow.com/questions/5999177/for-c-logging-how-to-obtain-call-stack-depth-with-minimal-overhead 
    /// and http://ayende.com/blog/3879/reducing-the-cost-of-getting-a-stack-trace 
    /// 
    /// Update, late 2017, .Net mscorlib.dll has been changed for .Net 4.5. In the code below the two 
    /// possibilities are called "old .Net" and "new .Net". The two versions can be tested by setting 
    /// the target for this project to either .Net Framework 2.0 or .Net Framework 4.5. 
    /// </summary> 
    class TestProgram 
    { 
     static void Main() 
     { 
     OneTimeSetup(); 

     int i = GetCallStackDepth(); // i = 10 on my test machine for old .Net, 12 for new .Net 
     int j = AddOneToNesting(); 
     Console.WriteLine(j == i + 1 ? "Test succeeded!" : "Test failed!!!!!!!!"); 
     Console.ReadKey(); 
     } 


     private delegate object DGetStackFrameHelper(); 

     private static DGetStackFrameHelper _getStackFrameHelper = null; 

     private static FieldInfo _frameCount = null; 


     private static void OneTimeSetup() 
     { 
     try 
     { 
      Type stackFrameHelperType = 
          typeof(object).Assembly.GetType("System.Diagnostics.StackFrameHelper"); 

      // ReSharper disable once PossibleNullReferenceException 
      MethodInfo getStackFramesInternal = 
       Type.GetType("System.Diagnostics.StackTrace, mscorlib").GetMethod(
          "GetStackFramesInternal", BindingFlags.Static | BindingFlags.NonPublic); 
      if (getStackFramesInternal == null) 
       return; // Unknown mscorlib implementation 

      DynamicMethod dynamicMethod = new DynamicMethod(
         "GetStackFrameHelper", typeof(object), new Type[0], typeof(StackTrace), true); 

      ILGenerator generator = dynamicMethod.GetILGenerator(); 
      generator.DeclareLocal(stackFrameHelperType); 

      bool newDotNet = false; 

      ConstructorInfo constructorInfo = 
        stackFrameHelperType.GetConstructor(new Type[] {typeof(bool), typeof(Thread)}); 
      if (constructorInfo != null) 
       generator.Emit(OpCodes.Ldc_I4_0); 
      else 
      { 
       constructorInfo = stackFrameHelperType.GetConstructor(new Type[] {typeof(Thread)}); 
       if (constructorInfo == null) 
        return; // Unknown mscorlib implementation 
       newDotNet = true; 
      } 

      generator.Emit(OpCodes.Ldnull); 
      generator.Emit(OpCodes.Newobj, constructorInfo); 
      generator.Emit(OpCodes.Stloc_0); 
      generator.Emit(OpCodes.Ldloc_0); 
      generator.Emit(OpCodes.Ldc_I4_0); 

      if (newDotNet) 
       generator.Emit(OpCodes.Ldc_I4_0); // Extra parameter 

      generator.Emit(OpCodes.Ldnull); 
      generator.Emit(OpCodes.Call, getStackFramesInternal); 
      generator.Emit(OpCodes.Ldloc_0); 
      generator.Emit(OpCodes.Ret); 

      _getStackFrameHelper = 
        (DGetStackFrameHelper) dynamicMethod.CreateDelegate(typeof(DGetStackFrameHelper)); 

      _frameCount = stackFrameHelperType.GetField("iFrameCount", 
                BindingFlags.NonPublic | BindingFlags.Instance); 
     } 
     catch 
     {} // _frameCount remains null, indicating unknown mscorlib implementation 
     } 


     private static int GetCallStackDepth() 
     { 
     if (_frameCount == null) 
      return 0; // Unknown mscorlib implementation 
     return (int)_frameCount.GetValue(_getStackFrameHelper()); 
     } 


     private static int AddOneToNesting() 
     { 
     return GetCallStackDepth(); 
     } 
    } 
Cuestiones relacionadas