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 :
'CLMonitor.exe monitor';
;
'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 , , , ...
, :
WCF.
. 10 -.
TraceEvent - Initialize.
, .
, TraceEvent, - lock . lock.
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.