internal static string FormatArguments(int parentPid, PostMortemSettings postMortemSettings) { var builder = new StringBuilder(); builder.Append(parentPid); if (postMortemSettings != null) { builder.Append(" "); builder.Append(postMortemSettings.CollectMinidumps); builder.Append(" "); builder.Append(postMortemSettings.SuppressErrorWindows); builder.Append(" "); builder.Append(postMortemSettings.HandleAccessViolations); builder.Append(" "); builder.Append(postMortemSettings.HandleCrtAsserts); builder.Append(" "); builder.Append(postMortemSettings.HandleCrtPureVirtualFunctionCalls); builder.Append(" "); builder.Append(((int)postMortemSettings.RuntimeVersions).ToString(CultureInfo.InvariantCulture)); builder.Append(" "); builder.Append(postMortemSettings.NumMinidumpsRetained); builder.Append(" "); builder.Append(postMortemSettings.MinidumpFolder ?? Path.GetTempPath()); builder.Append(" "); builder.Append(postMortemSettings.MinidumpName ?? "<Unused>"); } return(builder.ToString()); }
/// <summary> /// Initializes a new instance of this ProcessWatchdog with the specified options. /// The given host process will only be started once <see cref="Start()" /> is called. /// </summary> /// <param name="process"></param> /// <param name="options"></param> /// <param name="postMortemSettings">The settings for the post mortem debugger of the host process, if none are specified then no post mortem debugging is performed</param> /// <param name="processReadyTimeout">The amount of time the host process has to report being ready before it is assumed to be dead</param> /// <exception cref="ArgumentNullException"> /// When <paramref name="process" /> is null /// </exception> /// <exception cref="ArgumentException"> /// When <paramref name="process" /> is contains only whitespace /// </exception> public ProcessWatchdog( string process = SharpRemoteHost, ProcessOptions options = ProcessOptions.HideConsole, PostMortemSettings postMortemSettings = null, TimeSpan?processReadyTimeout = null ) { if (process == null) { throw new ArgumentNullException(nameof(process)); } if (string.IsNullOrWhiteSpace(process)) { throw new ArgumentException("process"); } if (postMortemSettings != null && !postMortemSettings.IsValid) { throw new ArgumentException("postMortemSettings"); } if (postMortemSettings != null) { _postMortemSettings = postMortemSettings.Clone(); if (_postMortemSettings.MinidumpFolder != null) { _postMortemSettings.MinidumpFolder = _postMortemSettings.MinidumpFolder.Replace('/', '\\'); if (!_postMortemSettings.MinidumpFolder.EndsWith("\\")) { _postMortemSettings.MinidumpFolder += '\\'; } } } _processReadyTimeout = processReadyTimeout ?? new FailureSettings().ProcessReadyTimeout; _waitHandle = new ManualResetEvent(false); _hostedProcessState = HostState.BootPending; _syncRoot = new object(); _parentPid = Process.GetCurrentProcess().Id; _startInfo = new ProcessStartInfo(process) { Arguments = FormatArguments(_parentPid, _postMortemSettings), RedirectStandardOutput = true, UseShellExecute = false }; switch (options) { case ProcessOptions.HideConsole: _startInfo.CreateNoWindow = true; break; case ProcessOptions.ShowConsole: _startInfo.CreateNoWindow = false; break; } _hasProcessExited = true; }
/// <summary> /// Initializes a new silo server. /// </summary> /// <param name="args">The command line arguments given to the Main() method</param> /// <param name="customTypeResolver">The type resolver, if any, responsible for resolving Type objects by their assembly qualified name</param> /// <param name="codeGenerator">The code generator to create proxy and servant types</param> /// <param name="postMortemSettings"> /// Settings to control how and if minidumps are collected - when set to null, default values are used ( /// <see /// cref="PostMortemSettings" /> /// ) /// </param> /// <param name="heartbeatSettings">The settings for heartbeat mechanism, if none are specified, then default settings are used</param> /// <param name="latencySettings">The settings for latency measurements, if none are specified, then default settings are used</param> /// <param name="endPointSettings">The settings for the endpoint itself (max. number of concurrent calls, etc...)</param> /// <param name="endPointName">The name of this silo, used for debugging (and logging)</param> public OutOfProcessSiloServer(string[] args, ITypeResolver customTypeResolver = null, ICodeGenerator codeGenerator = null, PostMortemSettings postMortemSettings = null, HeartbeatSettings heartbeatSettings = null, LatencySettings latencySettings = null, EndPointSettings endPointSettings = null, string endPointName = null) { if (postMortemSettings != null && !postMortemSettings.IsValid) { throw new ArgumentException("postMortemSettings"); } Log.InfoFormat("Silo Server starting, args ({0}): \"{1}\", {2} custom type resolver", args.Length, string.Join(" ", args), customTypeResolver != null ? "with" : "without" ); int pid; if (args.Length >= 1 && int.TryParse(args[0], out pid)) { _parentProcessId = pid; _parentProcess = Process.GetProcessById(pid); _parentProcess.EnableRaisingEvents = true; _parentProcess.Exited += ParentProcessOnExited; } if (Log.IsDebugEnabled) { Log.DebugFormat("Args.Length: {0}", args.Length); } if (args.Length >= 10) { if (postMortemSettings != null) { Log.Info("Ignoring post-mortem settings specified from the command-line"); } else { var settings = new PostMortemSettings(); bool.TryParse(args[1], out settings.CollectMinidumps); bool.TryParse(args[2], out settings.SuppressErrorWindows); bool.TryParse(args[3], out settings.HandleAccessViolations); bool.TryParse(args[4], out settings.HandleCrtAsserts); bool.TryParse(args[5], out settings.HandleCrtPureVirtualFunctionCalls); int tmp; int.TryParse(args[6], out tmp); settings.RuntimeVersions = (CRuntimeVersions)tmp; int.TryParse(args[7], out settings.NumMinidumpsRetained); settings.MinidumpFolder = args[8]; settings.MinidumpName = args[9]; if (!settings.IsValid) { Log.ErrorFormat("Received invalid post-mortem debugger settings: {0}", settings); } else { postMortemSettings = settings; } } } _registry = new DefaultImplementationRegistry(); _waitHandle = new ManualResetEvent(false); _customTypeResolver = customTypeResolver; _postMortemSettings = postMortemSettings; if (_postMortemSettings != null) { Log.InfoFormat("Using post-mortem debugger: {0}", _postMortemSettings); if (!NativeMethods.LoadPostmortemDebugger()) { int err = Marshal.GetLastWin32Error(); Log.ErrorFormat("Unable to load the post-mortem debugger dll: {0}", err); } if (_postMortemSettings.CollectMinidumps) { if (NativeMethods.InitDumpCollection(_postMortemSettings.NumMinidumpsRetained, _postMortemSettings.MinidumpFolder, _postMortemSettings.MinidumpName)) { Log.InfoFormat("Installed post-mortem debugger; up to {0} mini dumps will automatically be saved to: {1}", _postMortemSettings.NumMinidumpsRetained, _postMortemSettings.MinidumpFolder ); } } NativeMethods.InstallPostmortemDebugger(_postMortemSettings.HandleAccessViolations, _postMortemSettings.SuppressErrorWindows, _postMortemSettings.HandleCrtAsserts, _postMortemSettings.HandleCrtPureVirtualFunctionCalls, _postMortemSettings.RuntimeVersions); } _endPoint = new SocketEndPoint(EndPointType.Server, endPointName, codeGenerator: codeGenerator, heartbeatSettings: heartbeatSettings, latencySettings: latencySettings, endPointSettings: endPointSettings ); _endPoint.OnConnected += EndPointOnOnConnected; _endPoint.OnDisconnected += EndPointOnOnDisconnected; _endPoint.OnFailure += EndPointOnOnFailure; }
/// <summary> /// Initializes a new instance of this silo with the specified options. /// The given host process will only be started once <see cref="Start" /> is called. /// </summary> /// <param name="process"></param> /// <param name="options"></param> /// <param name="codeGenerator">The code generator to create proxy and servant types</param> /// <param name="latencySettings"> /// The settings for latency measurements, if none are specified, then default settings are /// used /// </param> /// <param name="postMortemSettings"> /// The settings for the post mortem debugger of the host process, if none are specified /// then no post mortem debugging is performed /// </param> /// <param name="endPointSettings">The settings for the endpoint itself (max. number of concurrent calls, etc...)</param> /// <param name="failureSettings"> /// The settings specifying when a failure is assumed to have occured in the host process - /// if none are specified, then defaults are used /// </param> /// <param name="failureHandler"> /// The object responsible for deciding how failures are dealt with - if none is specified /// then a new <see cref="ZeroFailureToleranceStrategy" /> is used /// </param> /// <param name="endPointName">The name of the endpoint - used in log messages to differentiate between different endpoints</param> /// <exception cref="ArgumentNullException">When <paramref name="process" /> is null</exception> /// <exception cref="ArgumentException">When <paramref name="process" /> is contains only whitespace</exception> public OutOfProcessSilo( string process = ProcessWatchdog.SharpRemoteHost, ProcessOptions options = ProcessOptions.HideConsole, ICodeGenerator codeGenerator = null, LatencySettings latencySettings = null, PostMortemSettings postMortemSettings = null, EndPointSettings endPointSettings = null, FailureSettings failureSettings = null, IFailureHandler failureHandler = null, string endPointName = null ) { if (process == null) { throw new ArgumentNullException(nameof(process)); } if (string.IsNullOrWhiteSpace(process)) { throw new ArgumentException("process"); } if (postMortemSettings != null && !postMortemSettings.IsValid) { throw new ArgumentException("postMortemSettings"); } if (failureSettings != null) { if (failureSettings.ProcessReadyTimeout <= TimeSpan.Zero) { throw new ArgumentOutOfRangeException(nameof(failureSettings), "ProcessReadyTimeout should be greater than zero"); } if (failureSettings.EndPointConnectTimeout <= TimeSpan.Zero) { throw new ArgumentOutOfRangeException(nameof(failureSettings), "EndPointConnectTimeout should be greater than zero"); } } failureSettings = failureSettings ?? new FailureSettings(); failureHandler = failureHandler ?? new ZeroFailureToleranceStrategy(); _endPoint = new SocketEndPoint(EndPointType.Client, endPointName, codeGenerator: codeGenerator, heartbeatSettings: failureSettings.HeartbeatSettings, latencySettings: latencySettings, endPointSettings: endPointSettings); _subjectHost = _endPoint.CreateProxy <ISubjectHost>(Constants.SubjectHostId); _syncRoot = new object(); _process = new ProcessWatchdog( process, options, postMortemSettings ); _process.OnHostOutputWritten += EmitHostOutputWritten; _queue = new OutOfProcessQueue( _process, _endPoint, failureHandler, failureSettings ); _queue.OnHostStarted += QueueOnOnHostStarted; }