public static string PrepareDebuggerCommandString(ProcessGovernor procgov, string appImageExe) { var buffer = new StringBuilder(); var procgovPath = Assembly.GetAssembly(typeof(ProcessGovernor)).Location; buffer.Append('"').Append(procgovPath).Append('"').Append(" --nogui --debugger"); if (procgov.AdditionalEnvironmentVars.Count > 0) { // we will create a file in the procgov folder with the environment variables string appEnvironmentFilePath = GetAppEnvironmentFilePath(appImageExe); using (var writer = new StreamWriter(appEnvironmentFilePath, false)) { foreach (var kv in procgov.AdditionalEnvironmentVars) { writer.WriteLine("{0}={1}", kv.Key, kv.Value); } } buffer.AppendFormat(" --env=\"{0}\"", appEnvironmentFilePath); } if (procgov.CpuAffinityMask != 0) { buffer.AppendFormat(" --cpu=0x{0:X}", procgov.CpuAffinityMask); } if (procgov.MaxProcessMemory > 0) { buffer.AppendFormat(" --maxmem={0}", procgov.MaxProcessMemory); } return(buffer.ToString()); }
public static void LoadCustomEnvironmentVariables(ProcessGovernor procgov, string file) { if (file == null || !File.Exists(file)) { throw new ArgumentException("the text file with environment variables does not exist"); } try { using (var reader = File.OpenText(file)) { int linenum = 1; string line; while ((line = reader.ReadLine()) != null) { var ind = line.IndexOf("="); if (ind > 0) { var key = line.Substring(0, ind).Trim(); if (!string.IsNullOrEmpty(key)) { var val = line.Substring(ind + 1, line.Length - ind - 1).Trim(); procgov.AdditionalEnvironmentVars.Add(key, val); linenum++; continue; } } throw new ArgumentException(string.Format("the environment file contains invalid data (line: {0})", linenum)); } } } catch (IOException ex) { throw new ArgumentException("can't read the text file with environment variables, {0}", ex.Message); } }
public static void SetupRegistryForProcessGovernor(ProcessGovernor procgov, string appImageExe, RegistryOperation oper) { if (!IsUserAdmin()) { Console.Error.WriteLine("You must be admin to do that. Run the app from the administrative console."); return; } // extrace image.exe if path is provided appImageExe = Path.GetFileName(appImageExe); var regkey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options", true); // add to image file execution path if (oper == RegistryOperation.INSTALL) { regkey = regkey.CreateSubKey(appImageExe); regkey.SetValue("Debugger", PrepareDebuggerCommandString(procgov, appImageExe)); } else if (oper == RegistryOperation.UNINSTALL) { regkey.DeleteSubKey(appImageExe, false); var appEnvironmentFilePath = GetAppEnvironmentFilePath(appImageExe); if (File.Exists(appEnvironmentFilePath)) { File.Delete(appEnvironmentFilePath); } } }
public void PrepareDebuggerCommandStringTest() { var procgov = new ProcessGovernor() { CpuAffinityMask = 0x2, MaxProcessMemory = 1024 * 1024 }; procgov.AdditionalEnvironmentVars.Add("TEST", "TESTVAL"); procgov.AdditionalEnvironmentVars.Add("TEST2", "TESTVAL2"); var appImageExe = Path.GetFileName(@"C:\temp\test.exe"); var debugger = Program.PrepareDebuggerCommandString(procgov, appImageExe); var envFilePath = Program.GetAppEnvironmentFilePath(appImageExe); Assert.True(File.Exists(envFilePath)); try { var txt = File.ReadAllText(envFilePath); Assert.Equal("TEST=TESTVAL\r\nTEST2=TESTVAL2\r\n", txt); Assert.Equal(string.Format("\"{0}\" --nogui --debugger --env=\"{1}\" --cpu=0x2 --maxmem=1048576", Assembly.GetAssembly(typeof(ProcessGovernor)).Location, envFilePath), debugger); } finally { File.Delete(envFilePath); } }
public void LoadCustomEnvironmentVariablesTest() { var envVarsFile = Path.GetTempFileName(); try { using (var writer = new StreamWriter(envVarsFile, false)) { writer.WriteLine("TEST=TESTVAL"); writer.WriteLine(" TEST2 = TEST VAL2 "); } var procgov = new ProcessGovernor(); Program.LoadCustomEnvironmentVariables(procgov, envVarsFile); Assert.Equal <KeyValuePair <string, string> >(new Dictionary <string, string>() { { "TEST", "TESTVAL" }, { "TEST2", "TEST VAL2" } }, procgov.AdditionalEnvironmentVars); using (var writer = new StreamWriter(envVarsFile, false)) { writer.WriteLine(" = TEST VAL2 "); } Assert.Throws <ArgumentException>(() => { Program.LoadCustomEnvironmentVariables(procgov, envVarsFile); }); } finally { if (File.Exists(envVarsFile)) { File.Delete(envVarsFile); } } }
static void ShowLimits(ProcessGovernor procgov) { Console.WriteLine("CPU affinity mask: {0}", procgov.CpuAffinityMask != 0 ? $"0x{procgov.CpuAffinityMask:X}" : "(not set)"); Console.WriteLine("Max CPU rate: {0}", procgov.CpuMaxRate > 0 ? $"{procgov.CpuMaxRate}%" : "(not set)"); Console.WriteLine("Maximum committed memory (MB): {0}", procgov.MaxProcessMemory > 0 ? $"{(procgov.MaxProcessMemory / 1048576):0,0}" : "(not set)"); Console.WriteLine("Maximum WS memory (MB): {0}", procgov.MaxWorkingSetSize > 0 ? $"{(procgov.MaxWorkingSetSize / 1048576):0,0}" : "(not set)"); Console.WriteLine("Preferred NUMA node: {0}", procgov.NumaNode != 0xffff ? $"{procgov.NumaNode}" : "(not set)"); Console.WriteLine("Process user-time execution limit (ms): {0}", procgov.ProcessUserTimeLimitInMilliseconds > 0 ? $"{procgov.ProcessUserTimeLimitInMilliseconds:0,0}" : "(not set)"); Console.WriteLine("Job user-time execution limit (ms): {0}", procgov.JobUserTimeLimitInMilliseconds > 0 ? $"{procgov.JobUserTimeLimitInMilliseconds:0,0}" : "(not set)"); Console.WriteLine("Clock-time execution limit (ms): {0}", procgov.ClockTimeLimitInMilliseconds > 0 ? $"{procgov.ClockTimeLimitInMilliseconds:0,0}" : "(not set)"); if (procgov.PropagateOnChildProcesses) { Console.WriteLine(); Console.WriteLine("All configured limits will also apply to the child processes."); } Console.WriteLine(); Console.WriteLine("Press Ctrl-C to end execution without terminating the process."); Console.WriteLine(); }
public static int Main(string[] args) { ShowHeader(); using (var procgov = new ProcessGovernor()) { List <string> procargs = null; bool showhelp = false, nogui = false, debug = false; int pid = 0; RegistryOperation registryOperation = RegistryOperation.NONE; var p = new OptionSet() { { "m|maxmem=", "Max committed memory usage in bytes (accepted suffixes: K, M or G).", v => { procgov.MaxProcessMemory = ParseMemoryString(v); } }, { "env=", "A text file with environment variables (each line in form: VAR=VAL). Applies only to newly created processes.", v => LoadCustomEnvironmentVariables(procgov, v) }, { "c|cpu=", "If in hex (starts with 0x) it is treated as an affinity mask, otherwise it is a number of CPU cores assigned to your app.", v => { if (v.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) { procgov.CpuAffinityMask = long.Parse(v.Substring(2), NumberStyles.HexNumber); } else { procgov.CpuAffinityMask = CalculateAffinityMaskFromCpuCount(int.Parse(v)); } } }, { "r|recursive", "Apply limits to child processes too (will wait for all processes to finish).", v => { procgov.PropagateOnChildProcesses = v != null; } }, { "newconsole", "Start the process in a new console window.", v => { procgov.SpawnNewConsoleWindow = v != null; } }, { "nogui", "Hide Process Governor console window (set always when installed as debugger).", v => { nogui = v != null; } }, { "p|pid=", "Attach to an already running process", (int v) => pid = v }, { "install", "Install procgov as a debugger for a specific process using Image File Executions. " + "DO NOT USE this option if the process you want to control starts child instances of itself (for example, Chrome).", v => { registryOperation = RegistryOperation.INSTALL; } }, { "t|timeout=", "Kill the process (with -r, also all its children) if it does not finish within the specified time. " + "Add suffix to define the time unit. Valid suffixes are: ms, s, m, h.", (string v) => procgov.ClockTimeLimitInMilliseconds = ParseTimeStringToMilliseconds(v) }, { "process-utime=", "Kill the process (with -r, also applies to its children) if it exceeds the given " + "user-mode execution time. Add suffix to define the time unit. Valid suffixes are: ms, s, m, h.", (string v) => procgov.ProcessUserTimeLimitInMilliseconds = ParseTimeStringToMilliseconds(v) }, { "job-utime=", "Kill the process (with -r, also all its children) if the total user-mode execution " + "time exceed the specified value. Add suffix to define the time unit. Valid suffixes are: ms, s, m, h.", (string v) => procgov.JobUserTimeLimitInMilliseconds = ParseTimeStringToMilliseconds(v) }, { "uninstall", "Uninstall procgov for a specific process.", v => { registryOperation = RegistryOperation.UNINSTALL; } }, { "debugger", "Internal - do not use.", v => debug = v != null }, { "v|verbose", "Show verbose messages in the console.", v => procgov.ShowTraceMessages = v != null }, { "h|help", "Show this message and exit", v => showhelp = v != null }, { "?", "Show this message and exit", v => showhelp = v != null } }; try { procargs = p.Parse(args); } catch (OptionException ex) { Console.Error.Write("ERROR: invalid argument"); Console.Error.WriteLine(ex.Message); Console.WriteLine(); showhelp = true; } catch (FormatException) { Console.Error.WriteLine("ERROR: invalid number in one of the constraints"); Console.WriteLine(); showhelp = true; } catch (ArgumentException ex) { Console.Error.WriteLine("ERROR: {0}", ex.Message); Console.WriteLine(); showhelp = true; } if (!showhelp && registryOperation != RegistryOperation.NONE) { if (procargs.Count == 0) { Console.Error.WriteLine("ERROR: please provide an image name for a process you would like to intercept."); return(1); } SetupRegistryForProcessGovernor(procgov, procargs[0], registryOperation); return(0); } if (!showhelp && (procargs.Count == 0 && pid == 0) || (pid > 0 && procargs.Count > 0)) { Console.Error.WriteLine("ERROR: please provide either process name or PID of the already running process"); Console.WriteLine(); showhelp = true; } if (showhelp) { ShowHelp(p); return(0); } if (nogui) { WinWindows.NativeMethods.ShowWindow(WinWindows.NativeMethods.GetConsoleWindow(), WinWindows.NativeMethods.SW_HIDE); } try { ShowLimits(procgov); if (debug) { return(procgov.StartProcessUnderDebuggerAndDetach(procargs)); } if (pid > 0) { return(procgov.AttachToProcess(pid)); } return(procgov.StartProcess(procargs)); } catch (Win32Exception ex) { Console.Error.WriteLine("ERROR: {0} (0x{1:X})", ex.Message, ex.ErrorCode); return(1); } catch (Exception ex) { Console.Error.WriteLine("ERROR: {0}", ex.Message); return(1); } } }
public static void Main(string[] args) { using (var procgov = new ProcessGovernor()) { List <string> procargs = null; bool showhelp = false, nogui = false, debug = false; int pid = 0; RegistryOperation registryOperation = RegistryOperation.NONE; var p = new OptionSet() { { "m|maxmem=", "Max committed memory usage in bytes (accepted suffixes: K, M or G).", v => { procgov.MaxProcessMemory = ParseMemoryString(v); } }, { "env=", "A text file with environment variables (each line in form: VAR=VAL). Applies only to newly created processes.", v => LoadCustomEnvironmentVariables(procgov, v) }, { "c|cpu=", "If in hex (starts with 0x) it is treated as an affinity mask, otherwise it is a number of CPU cores assigned to your app.", v => { if (v.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) { procgov.CpuAffinityMask = long.Parse(v.Substring(2), NumberStyles.HexNumber); } else { procgov.CpuAffinityMask = CalculateAffinityMaskFromCpuCount(int.Parse(v)); } } }, { "newconsole", "Start the process in a new console window.", v => { procgov.SpawnNewConsoleWindow = v != null; } }, { "nogui", "Hide Process Governor console window (set always when installed as debugger).", v => { nogui = v != null; } }, { "p|pid=", "Attach to an already running process", (int v) => pid = v }, { "install", "Installs procgov as a debugger for a specific process using Image File Executions.", v => { registryOperation = RegistryOperation.INSTALL; } }, { "uninstall", "Uninstalls procgov for a specific process.", v => { registryOperation = RegistryOperation.UNINSTALL; } }, { "debugger", "Internal - do not use.", v => { debug = v != null; } }, { "h|help", "Show this message and exit", v => showhelp = v != null }, { "?", "Show this message and exit", v => showhelp = v != null } }; try { procargs = p.Parse(args); } catch (OptionException ex) { Console.Write("ERROR: invalid argument"); Console.WriteLine(ex.Message); Console.WriteLine(); showhelp = true; } catch (FormatException) { Console.WriteLine("ERROR: invalid number in one of the constraints"); Console.WriteLine(); showhelp = true; } catch (ArgumentException ex) { Console.WriteLine("ERROR: {0}", ex.Message); Console.WriteLine(); showhelp = true; } if (!showhelp && registryOperation != RegistryOperation.NONE) { if (procargs.Count == 0) { Console.WriteLine("ERROR: please provide an image name for a process you would like to intercept."); return; } SetupRegistryForProcessGovernor(procgov, procargs[0], registryOperation); return; } if (!showhelp && (procargs.Count == 0 && pid == 0) || (pid > 0 && procargs.Count > 0)) { Console.WriteLine("ERROR: please provide either process name or PID of the already running process"); Console.WriteLine(); showhelp = true; } if (showhelp) { ShowHelp(p); return; } if (nogui) { WinWindows.NativeMethods.ShowWindow(WinWindows.NativeMethods.GetConsoleWindow(), WinWindows.NativeMethods.SW_HIDE); } try { if (debug) { procgov.StartProcessUnderDebuggerAndDetach(procargs); } else if (pid > 0) { procgov.AttachToProcess(pid); } else { procgov.StartProcess(procargs); } } catch (Win32Exception ex) { Console.WriteLine("ERROR: {0} (0x{1:X})", ex.Message, ex.ErrorCode); } catch (Exception ex) { Console.WriteLine("ERROR: {0}", ex.Message); } } }