提交 edc5cf45 编写于 作者: L Larry Golding

ProcessWatchdog: Set up to monitor multiple processes

上级 51de0c55
......@@ -44,7 +44,6 @@ public Process DumpProcessNow(int processId, string description)
ConsoleUtils.LogMessage(
Resources.InfoTerminatingProcess,
description,
processId,
dumpFileName);
var processStartInfo = new ProcessStartInfo
......@@ -67,7 +66,7 @@ private string GenerateCrashDumpFileName(string description)
ConsoleUtils.LogMessage(Resources.InfoCreatedOutputFolder, outputFolderInfo.FullName);
}
var fileName = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss_ff") + " - " + description + ".dmp";
var fileName = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss_ff") + "-" + description + ".dmp";
fileName = Path.Combine(outputFolderInfo.FullName, fileName);
return fileName;
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace ProcessWatchdog
{
/// <summary>
/// Keeps track of a process and all its descendants.
/// </summary>
internal class ProcessTracker : IDisposable
{
private List<TrackedProcess> _trackedProcesses;
private readonly ProcDump _procDump;
/// <summary>
/// Initializes a new instance of the <see cref="ProcessTracker"/> class from the
/// specified process id.
/// </summary>
/// <param name="parentProcess">
/// The process whose descendants are to be tracked.
/// </param>
/// <param name="procDump">
/// Object responsible for producing memory dumps of any tracked processes that
/// fail or are terminated.
/// </param>
internal ProcessTracker(Process parentProcess, ProcDump procDump)
{
_trackedProcesses = new List<TrackedProcess>();
_procDump = procDump;
TrackProcess(parentProcess);
}
private void TrackProcess(Process process)
{
string description = MakeProcessDescription(process);
Process procDumpProcess = _procDump.MonitorProcess(process.Id, description);
_trackedProcesses.Add(new TrackedProcess(process, procDumpProcess, description));
}
private static string MakeProcessDescription(Process process)
{
return $"{process.ProcessName}-{process.Id}";
}
internal bool AllFinished => !_trackedProcesses.Any();
#region IDisposable Support
private bool _isDisposed = false;
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
foreach (TrackedProcess trackedProcess in _trackedProcesses)
{
trackedProcess.Process.Dispose();
trackedProcess.ProcDumpProcess.Dispose();
}
_trackedProcesses = null;
}
_isDisposed = true;
}
}
internal void TerminateAll()
{
foreach (TrackedProcess trackedProcess in _trackedProcesses)
{
// Launch another procdump process, distinct from the one that has been
// monitoring the tracked process. This procdump process will take an
// immediate dump of the tracked process.
Process immediateDumpProcess = _procDump.DumpProcessNow(
trackedProcess.Process.Id,
trackedProcess.Description);
while (!immediateDumpProcess.HasExited)
;
// Terminate the procdump process that has been monitoring the target process
// Since this procdump is acting as a debugger, terminating it will
// terminate the target process as well.
if (!trackedProcess.ProcDumpProcess.HasExited)
{
trackedProcess.ProcDumpProcess.Kill();
}
}
}
public void Dispose()
{
Dispose(true);
}
#endregion
/// <summary>
/// Information about a single process being tracked by a <see cref="ProcessTracker"/>.
/// </summary>
private class TrackedProcess
{
/// <summary>
/// Initializes a new instance of the <see cref="TrackedProcess"/> class from
/// the specified process information.
/// </summary>
/// <param name="process">
/// The process being tracked.
/// </param>
/// <param name="procDumpProcess">
/// The procdump process attached to <paramref name="process"/>, and responsible
/// for producing a memory dump if that process should fail.
/// </param>
/// <param name="description">
/// A string that describes the process, of the form "processName-processId".
/// </param>
internal TrackedProcess(Process process, Process procDumpProcess, string description)
{
Process = process;
ProcDumpProcess = procDumpProcess;
Description = description;
}
/// <summary>
/// Gets the process being tracked.
/// </summary>
internal Process Process { get; }
/// <summary>
/// Gets the procdump process attached to <see cref="Process"/>, and responsible
/// for producing a memory dump if that process should fail..
/// </summary>
internal Process ProcDumpProcess { get; }
/// <summary>
/// Gets a string that describes the process, of the form "processName-processId".
/// </summary>
internal string Description { get; }
}
}
}
......@@ -31,6 +31,7 @@
<Compile Include="ConsoleUtils.cs" />
<Compile Include="ProcDump.cs" />
<Compile Include="Options.cs" />
<Compile Include="ProcessTracker.cs" />
<Compile Include="Program.cs" />
<Compile Include="Resources.Designer.cs">
<AutoGen>True</AutoGen>
......
......@@ -43,20 +43,19 @@ private int Run()
Arguments = _options.Arguments
};
Process parentProcess = Process.Start(processStartInfo);
ProcDump procDump = new ProcDump(_options.ProcDumpPath, _options.OutputFolder);
using (Process process = Process.Start(processStartInfo))
using (ProcessTracker processTracker = new ProcessTracker(parentProcess, procDump))
{
using (Process procDumpProcess = procDump.MonitorProcess(process.Id, _options.Executable))
while (!processTracker.AllFinished)
{
while (!process.HasExited)
{
if (DateTime.Now - process.StartTime > _timeLimit)
if (DateTime.Now - parentProcess.StartTime > _timeLimit)
{
ConsoleUtils.LogError(
Resources.ErrorProcessTimedOut,
_options.Executable,
process.Id,
parentProcess.Id,
_options.TimeLimit);
if (_options.Screenshot)
......@@ -64,31 +63,17 @@ private int Run()
ScreenshotSaver.SaveScreen(_options.Executable, _options.OutputFolder);
}
// Launch another procdump process. This one will take an
// immediate dump.
Process immediateDumpProcess = procDump.DumpProcessNow(process.Id, _options.Executable);
while (!immediateDumpProcess.HasExited)
;
// Kill the other procdump process, the one that was monitoring
// the target process. Since this procdump is acting as a
// debugger, killing it will kill the target process as well.
if (!procDumpProcess.HasExited)
{
procDumpProcess.Kill();
}
processTracker.TerminateAll();
return 1;
}
Thread.Sleep(_options.PollingInterval);
}
// If the target process exited normally, then the "monitoring" procdump
// process has also exited, so there's no need to kill it here.
ConsoleUtils.LogMessage(Resources.ProcessExited, _options.Executable, process.ExitTime - process.StartTime);
}
ConsoleUtils.LogMessage(
Resources.ProcessExited,
_options.Executable,
parentProcess.ExitTime - parentProcess.StartTime);
}
return 0;
......
......@@ -124,7 +124,7 @@ internal class Resources {
}
/// <summary>
/// Looks up a localized string similar to The process {0} ({1}) exceeded the time limit of {2} seconds and will be terminated..
/// Looks up a localized string similar to The process {0} ({1}) or one of its descendants exceeded the time limit of {2} seconds. This process and all its descendants will be terminated..
/// </summary>
internal static string ErrorProcessTimedOut {
get {
......@@ -151,7 +151,7 @@ internal class Resources {
}
/// <summary>
/// Looks up a localized string similar to Terminating process {0} ({1}) and saving crash dump to file {2}....
/// Looks up a localized string similar to Terminating process {0} and saving crash dump to file {1}....
/// </summary>
internal static string InfoTerminatingProcess {
get {
......
......@@ -139,7 +139,7 @@
<value>The value {0} is not a valid time limit. Please specify the time limit as a positive number of seconds.</value>
</data>
<data name="ErrorProcessTimedOut" xml:space="preserve">
<value>The process {0} ({1}) exceeded the time limit of {2} seconds and will be terminated.</value>
<value>The process {0} ({1}) or one of its descendants exceeded the time limit of {2} seconds. This process and all its descendants will be terminated.</value>
</data>
<data name="InfoCreatedOutputFolder" xml:space="preserve">
<value>Created output folder {0}.</value>
......@@ -148,7 +148,7 @@
<value>Saving screenshot to file {0}...</value>
</data>
<data name="InfoTerminatingProcess" xml:space="preserve">
<value>Terminating process {0} ({1}) and saving crash dump to file {2}...</value>
<value>Terminating process {0} and saving crash dump to file {1}...</value>
</data>
<data name="ProcessExited" xml:space="preserve">
<value>The process {0} exited successfully after {1}.</value>
......
......@@ -61,7 +61,7 @@ private static string GenerateScreenshotFileName(string description, string outp
ConsoleUtils.LogMessage(Resources.InfoCreatedOutputFolder, outputFolderInfo.FullName);
}
var fileName = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss_ff") + " - " + description + ".png";
var fileName = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss_ff") + "-" + description + ".png";
fileName = Path.Combine(outputFolderInfo.FullName, fileName);
return fileName;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册