Cómo WCF se dispara a sí mismo en el pie con TraceSource

No es frecuente que consiga escribir algo interesante sobre los problemas asociados con la programación paralela. Esta vez fue "suerte". Debido a la implementación del método TraceEvent estándar, se produjo un error con el bloqueo de varios subprocesos. Me gustaría advertir sobre el matiz existente y contar un caso interesante desde el soporte de nuestros usuarios. ¿Qué tiene que ver el soporte con esto? Aprenderá esto del artículo. Disfruta leyendo.





Fondo

PVS-Studio CLMonitor.exe, . "" PVS-Studio C C++ . , PVS-Studio. : gcc, clang, cl, ..





Windows , 3 :





  1. 'CLMonitor.exe monitor';





  2. ;





  3. 'CLMonitor.exe analyze'.





'', , . – , . , . . 'CLMonitor.exe analyze' '', : ", , ". , – . , , .





, - . , , , . timeout'. – . . ... . , , .





. Windows CLMonitor.exe. Windows.





CLMonitor.exe

, . , .





, , , . , . .





, , , . . , C++ — PVS-Studio_Cmd. , , – Visual Studio. , , , – . , :





  • ;





  • ;





  • .





PVS-Studio_Cmd (*.vcxproj). "" MSBuild Visual Studio. NMake , . , NMake .vcxproj. . . , Unreal Engine *Unreal Build Tool *– , " ". .





, PVS-Studio , , CLMonitor.exe. . . , .





WCF (Windows Communication Foundation). , .





*ServiceHost * , . :





static ErrorLevels PerformMonitoring(....) 
{
  using (ServiceHost host = new ServiceHost(
                       typeof(CLMonitoringContract),   
                       new Uri[]{new Uri(PipeCredentials.PipeRoot)})) 
  {
    ....
    host.AddServiceEndpoint(typeof(ICLMonitoringContract), 
                            pipe, 
                            PipeCredentials.PipeName);
    host.Open();     
    ....
  }
}

      
      



: *CLMonitoringContract * ICLMonitoringContract.





*ICLMonitoringContract *– . *CLMonitoringContract *– . :





[ServiceContract(SessionMode = SessionMode.Required, 
                 CallbackContract = typeof(ICLMonitoringContractCallback))]
interface ICLMonitoringContract
{
  [OperationContract]
  void StopMonitoring(string dumpPath = null);
} 

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
class CLMonitoringContract : ICLMonitoringContract
{
  public void StopMonitoring(string dumpPath = null)
  {
    ....
    CLMonitoringServer.CompilerMonitor.StopMonitoring(dumpPath);
  } 
}

      
      



, . . :





public void FinishMonitor()
{
  CLMonitoringContractCallback allback = new CLMonitoringContractCallback();
  var pipeFactory = new DuplexChannelFactory<ICLMonitoringContract>(
           allback, 
           pipe, 
           new EndpointAddress(....));

  ICLMonitoringContract pipeProxy = pipeFactory.CreateChannel();
  ((IContextChannel)pipeProxy).OperationTimeout = new TimeSpan(24, 0, 0);
  ((IContextChannel)pipeProxy).Faulted += CLMonitoringServer_Faulted;

  pipeProxy.StopMonitoring(dumpPath);
}

      
      



StopMonitoring, . .





, , , CLMonitor.exe.





. , . , – , . 10 :





** . ** 10 ? , , - 24 , , . , . .





( ) 5 , , .





. , . , :) .





''

, , :





. , :





public void FinishMonitor()
{
  ....
  ICLMonitoringContract pipeProxy = pipeFactory.CreateChannel();
  ((IContextChannel)pipeProxy).OperationTimeout = new TimeSpan(24, 0, 0);
  ((IContextChannel)pipeProxy).Faulted += CLMonitoringServer_Faulted;

  pipeProxy.StopMonitoring(dumpPath);            // <=
  ....
}

      
      



, , . , . , 5 . , , .





''

:





-, TraceEvent'? , , 50. . , . , , , - .. , , . , , , , .





, , , , ''. , . . ? , , . , - - . ReferenceSource TraceEvent.





TraceEvent lock:





, - TraceEvent, TraceInternal.critSec. , . , . , DiagnosticsConfiguration.Initialize:





NegotiateStream.AuthenticateAsServer, -:





- WCF. , . , DiagnosticsConfiguration.Initialize . ... - :





, , . , critSec, :





, , .





. TraceEvent GitHub. , Microsoft:





"Also one of the locks, TraceInternal.critSec, is only present if the TraceListener asks for it. Generally speaking such 'global' locks are not a good idea for a high performance logging system (indeed we don't recommend TraceSource for high performance logging at all, it is really there only for compatibility reasons)".





, Microsoft , IPC , , , ...





, :





  1. WCF.





  2. . 10 -.





  3. TraceEvent - Initialize.





  4. , .





  5. , TraceEvent, - lock . lock.





  6. Initialize lock.





, . , Initialize. - , TraceEvent, . TraceEvent' , '' . , , TraceEvent. !





, * * . - , . , , WCF – , - .





.





. , , , - . CrazyLogging, :





private void CrazyLogging()
{
  for (var i = 0; i < 30; i++)
  {
    var j = i;
    new Thread(new ThreadStart(() =>
    {
      while (!Program.isStopMonitor)
        Logger.TraceEvent(TraceEventType.Error, 0, j.ToString());
    })).Start();
  }
}

      
      



Trace, . , :





public void Trace()
{
  ListenersInitialization();
  CrazyLogging();
  ....
}

      
      



. ( Visual Studio 2019), 5 :





! (TestTraceSource.exe analyze), .





, , . Visual Studio . – , DiagnosticsConfiguration.Initialize. .





? , TraceSource – , , . , , . Event' TraceSource.TraceEvent.





"" . , Console.WriteLine. , - , , . - . enableLogger.





,

, .





, .exe trace. , analyze.





**: ** CrazyLogging . , . Visual Studio .





:





using System.Linq;

namespace TestTraceSource
{
  class Program
  {
    public static bool isStopMonitor = false;

    static void Main(string[] args)
    {
      if (!args.Any())
        return;

      if (args[0] == "trace")
      {
        Server server = new Server();
        server.Trace();
      }
      if (args[0] == "analyze")
      {
        Client client = new Client();
        client.FinishMonitor();
      }
    }  
  }
}

      
      



:





using System;
using System.Diagnostics;
using System.ServiceModel;
using System.Threading;

namespace TestTraceSource
{
  class Server
  {
    private static TraceSource Logger;

    public void Trace()
    {
      ListenersInitialization();
      CrazyLogging();
      using (ServiceHost host = new ServiceHost(
                          typeof(TestTraceContract), 
                          new Uri[]{new Uri(PipeCredentials.PipeRoot)}))
      {
        host.AddServiceEndpoint(typeof(IContract), 
                                new NetNamedPipeBinding(), 
                                PipeCredentials.PipeName);
        host.Open();

        while (!Program.isStopMonitor)
        {
          // We catch all processes, process them, and so on
        }

        host.Close();
      }

      Console.WriteLine("Complited.");
    }

    private void ListenersInitialization()
    {
      Logger = new TraceSource("PVS-Studio CLMonitoring");
      Logger.Switch.Level = SourceLevels.Verbose;
      Logger.Listeners.Add(new ConsoleTraceListener());

      String EventSourceName = "PVS-Studio CL Monitoring";

      EventLog log = new EventLog();
      log.Source = EventSourceName;
      Logger.Listeners.Add(new EventLogTraceListener(log));
    }

    private void CrazyLogging()
    {
      for (var i = 0; i < 30; i++)
      {
        var j = i;
        new Thread(new ThreadStart(() =>
        {
          var start = DateTime.Now;
          while (!Program.isStopMonitor)
            Logger.TraceEvent(TraceEventType.Error, 0, j.ToString());
        })).Start();
      }
    } 
  }
}

      
      



:





using System;
using System.ServiceModel;

namespace TestTraceSource
{
  class Client
  {
    public void FinishMonitor()
    {
      TestTraceContractCallback allback = new TestTraceContractCallback();
      var pipeFactory = new DuplexChannelFactory<IContract>(
                                allback,
                                new NetNamedPipeBinding(),
                                new EndpointAddress(PipeCredentials.PipeRoot 
                                                  + PipeCredentials.PipeName));
      IContract pipeProxy = pipeFactory.CreateChannel();
      pipeProxy.StopServer();

      Console.WriteLine("Complited.");    
    }
  }
}

      
      



:





using System;
using System.ServiceModel;

namespace TestTraceSource
{
  class PipeCredentials
  {
    public const String PipeName = "PipeCLMonitoring";
    public const String PipeRoot = "net.pipe://localhost/";
    public const long MaxMessageSize = 500 * 1024 * 1024; //bytes
  }

  class TestTraceContractCallback : IContractCallback
  {
    public void JobComplete()
    {
      Console.WriteLine("Job Completed.");
    }
  }

  [ServiceContract(SessionMode = SessionMode.Required, 
                   CallbackContract = typeof(IContractCallback))]
  interface IContract
  {
    [OperationContract]
    void StopServer();
  }

  interface IContractCallback
  {
    [OperationContract(IsOneWay = true)]
    void JobComplete();
  }

  [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
  class TestTraceContract : IContract
  {
    public void StopServer()
    {
      Program.isStopMonitor = true;
    }
  }
}

      
      



TraceSource.TraceEvent. , . . , TraceSource. - , .





. Twitter.





, : Nikolay Mironov. How WCF Shoots Itself in the Foot With TraceSource.








All Articles