Measuring Process memory usage gives extremely low (wrong) values - c#

I have the following code to launch and monitor a process:
Process process = new Process();
process.StartInfo.FileName = "foo.exe";
long maxMemoryUsage = 0;
process.Start()
while(!process.HasExited)
{
maxMemoryUsage = Math.Max(maxMemoryUsage, process.PrivateMemorySize64);
}
After using this code to run a large application that, according to the task manager used 328 MB at its peak (Memory "Private Working Set"). The value of maxMemoryUsage, and the value of process.PeakPagedMemorySize64 is 364544. According to MSDN this value should be interpreted as bytes, meaning it is a little over 300KB, a factor thousand away from the expected value. The other process.Peak...Memory properties also report extremely low values (all under a megabyte, except for PeakVirtualMemorySize64 which is 4MB which I think is the minimum value for this field).
I've tried launching different applications (C# and C++ based of which I have the source code) which I know to use very little or a lot of memory and the memory values where always very close to the values seen with this process. Apparently I'm doing something completely wrong.
So my question is: How can I measure the maximum memory usage of a process which I spawned from my C# application. (Note that I don't need to have the value ealtime as long as I know its value after the program exited, I also don't need it super precise as I don't care if it was 27.04MB or 30MB, but I do care if it was 30MB or 100MB).
Edit: here is a full reproducable test case
class Program
{
static void Main(string[] args)
{
Process process = new Process();
process.StartInfo.FileName = #"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe";
long maxMemoryUsage = 0;
process.Start();
while(!process.HasExited)
{
maxMemoryUsage = Math.Max(maxMemoryUsage, process.PagedMemorySize64);
}
Console.Out.WriteLine("Memory used: " + (maxMemoryUsage / 1024.0) / 1024.0 + "MB");
Console.ReadLine();
}
}
According to the task manager Visual Studio uses 103MB. After closing Visual Studio the program reports 0.3984375MB.

Process class is heavily cached. You'll get only cached result, no matter how many times you read some property unless you throw a call to Refresh method. You need to call Process.Refresh to get the non cached result.
To quote from msdn
When a Process component is associated with a process resource, the
property values of the Process are immediately populated according to
the status of the associated process. If the information about the
associated process subsequently changes, those changes are not
reflected in the Process component's cached values. The Process
component is a snapshot of the process resource at the time they are
associated. To view the current values for the associated process,
call the Refresh method.
So, your code will become:
while(!process.HasExited)
{
process.Refresh();
maxMemoryUsage = Math.Max(maxMemoryUsage, process.PrivateMemorySize64);
}
Also you may consider looking at process.PeakXXX properties, that will help you I suppose.

Related

C# Process.Start() performance

My testing seems to suggest that Process.Start() at its bare bones costs ~15-27ms (depending on if you use CreateNoWindow, which ADDS ~10ms). In this question, I'm not trying to say the code I'm running is slow, but the actual act of starting a process, even if it does nothing.
I got these numbers by using a stopwatch on a console app that ran another console app literally just returning in its main method.
namespace RunNothing
{
class Program
{
static void Main(string[] args)
{
var startInfo = new ProcessStartInfo(#"C:\Users\Noggog\Documents\visual studio 2017\Projects\DoNothing\DoNothing\bin\Release\DoNothing.exe")
{
CreateNoWindow = true,
UseShellExecute = false,
};
var sw = new Stopwatch();
var proc = new Process()
{
StartInfo = startInfo,
EnableRaisingEvents = true
};
proc.Exited += (sender, a) =>
{
sw.Stop();
System.Console.WriteLine(sw.ElapsedMilliseconds);
};
sw.Start();
proc.Start();
System.Console.ReadLine();
}
}
}
So my question is whether anyone knows a good way to improve that startup time per Process.Start() call?
For background, I have an app that will be starting various .exes, most of which should do a quick bool check and short circuit out, asap. Every so often one of the calls will do a longer operation, but usually it will do nothing. Right now a ~15-27ms call per go is getting a bit heavy for my use case.
Edit:
Some people were asking for more details of the end usage. This is the project that drove the question. The end usage is an extension of git hooks to provide more hookable commands, and provide convenience features such as calling an exe in response to hooks being fired. One of the modes is that a single exe can handle multiple hooks, and decide which ones it would respond to. In that scenario, every git command would "check in" with the exe to see if it wanted to do any logic. This is where the Process.Start() time adds up. If you have 6 repos and your git client is initializing things by running several commands a pre and post hook combo can be 27ms (proc start time) * 2(pre/post) * X(commands) * 6(repos) = ~2+ seconds. Potentially none of these commands are ones needing response, but it's already added several seconds of sluggishness to the system.

Performance Counter read access very slow - How does Task Manager do it?

Im trying to implement a performance monitoring tool, I want to monitor basic things such as Memory and CPU.
I am attempting to do so by using Performance Counters as I believe this is what Task Manager is using behind the scenes too. I have no idea how Task Manager is able to do this however as to me it seems to take a VERY long time to retrieve process data using this method:
class Program
{
static void Main(string[] args)
{
while (true)
{
var pcs = Process.GetProcesses()
.Select(p => new PerformanceCounter("Process", "Working Set - Private", p.ProcessName));
var sw = Stopwatch.StartNew();
foreach (var pc in pcs)
pc.NextValue();
Console.WriteLine($"Time taken to read {pcs.Count()} performance counters: {sw.ElapsedMilliseconds}ms");
Thread.Sleep(1000);
}
}
}
Has anyone got any suggestions on how to do this or how even Task Manager or Process Explorer is able to do this?
How does Task Manager do it?
he used calls to ZwQuerySystemInformation, ZwQueryInformationProcess, ZwQueryInformationThread ..
Task Manager maintain database of active processes and periodically update this info by calling ZwQuerySystemInformation(SystemProcessInformation,) - so got array of SYSTEM_PROCESS_INFORMATION on exit.
add new entries if found new process, yet not in DB, remove entries for died processes, update info for lived
SYSTEM_PROCESS_INFORMATION already containing a lot information of process. additional information can be get by open process and call ZwQueryInformationProcess with appropriate info class
if you want implement a performance monitoring tool, without "quantum effect" (when the measurement affects the state itself) you need use this ntdll api. for definitions look at http://processhacker.sourceforge.net/doc/ntexapi_8h_source.html
despite this is undocumented, existing functions and structures not changed how minimum from win2000 (so ~17 years) - new version of windows add a lot new info classes, some fields which was spare/unused in old version - can become used, but old(legacy) not changed

Limiting user processor time and peek virtual memory with Job Objects

I'm writing an application runner on Windows which can limit processor user time and virtual memory for the application it will run. Idea is to have following:
runner.exe mem_limit_in_MB time_limit_in_sec command.exe command_arguments ...
My investigation lead me to Windows Job Objects API and since I'm trying to do all that from C#, I found JobObjectWrapper to help me.
The main part of the code if following:
using (JobObject jo = new JobObject())
{
// Time
jo.Limits.PerProcessUserTimeLimit = TimeSpan.FromMilliseconds(limitTime);
jo.Events.OnEndOfProcessTime += new jobEventHandler<EndOfProcessTimeEventArgs>(Events_OnEndOfProcessTime);
// Memory
jo.Limits.ProcessMemoryLimit = new IntPtr(limitMemory);
jo.Events.OnProcessMemoryLimit += new jobEventHandler<ProcessMemoryLimitEventArgs>(Events_OnProcessMemoryLimit);
// Process
ProcessStartInfo psi = new ProcessStartInfo(command, arguments);
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
jo.Limits.IsChildProcessBreakAway = false;
Process p = jo.CreateProcessSecured(psi);
p.WaitForExit();
}
Now, the problem is that it seams that Events_OnEndOfProcessTime is not called timely. If I set 0.5 sec limit for an app that takes several minutes, depending on the run application is once terminated after 0.5 sec and sometimes after 4 sec. Why is this happening? I cannot find any reference if Job Objects are checking limits periodically or in real-time.
My question is two-fold:
- Does anyone know about already developed code that does what I need?
- Does anyone know if Job Objects are executed periodically or in real time?
Thanks in advance...
PerProcessUserTimeLimit specifies the amount of user-mode time is granted to the process.
"The system periodically checks to determine whether each process associated with the job has accumulated more user-mode time than the set limit. If it has, the process is terminated." (MSDN)
Consequently it depends on your application, particulary on how effient it is burning user-mode time. Ending a process with PerProcessUserTimeLimit = 0.5 after 0.5 sec. means that it has used ~100% cpu (user-mode) during that time.
... if Job Objects are executed periodically or in real time? Periodically, as stated above.

Process.PrivateMemorySize64 returning committed memory instead of private

I'm writing a program in C# using .NET 4.5, that will allow me to monitor the memory, CPU and network usage of a particular process, and then chart that data according to my needs.
In order to obtain the memory usage for a particular process, I am checking the PrivateMemorySize64 property for that Process object. I am expecting to see the Private memory used by that process, but instead, it is showing the amount in Commit, as confirmed by the Windows Resource Monitor.
My questions are:
1) Does anybody know why this error is happening?
2) Is there a fix for it?
3) If no fix, is there another straightforward way I can obtain the private memory reserved for a process?
Here are the relevant parts of my code:
using System;
// I add all the open Processes to an array
Process[] localAll = Process.GetProcesses();
// I then add all the processes to a combobox to select from
// There's a button that updates labels with requested info
Process[] p = Process.GetProcessesByName(comboBox1.SelectedItem.ToString());
label1.Text = p[0].PrivateMemorySize64.ToString() + " bytes";
From your comment you said you are looking for private working set. It appears from this link How to calculate private working set (memory)?
that it is indeed not a part of the Process class. You must instead use a performance counter.
Copied and pasted from the other answer just in case for some reason it gets deleted.
using System;
using System.Diagnostics;
class Program {
static void Main(string[] args) {
string prcName = Process.GetCurrentProcess().ProcessName;
var counter = new PerformanceCounter("Process", "Working Set - Private", prcName);
Console.WriteLine("{0}K", counter.RawValue / 1024);
Console.ReadLine();
}
}

Process.HasExited returns true even though process is running?

I have been observing that Process.HasExited sometimes returns true even though the process is still running.
My code below starts a process with name "testprogram.exe" and then waits for it to exit. The problem is that sometimes I get thrown the exception; it seems that even though HasExited returns true the process itself is still alive in the system - how can this be??
My program writes to a log file just before it terminates and thus I need to be absolutely sure that this log file exists (aka the process has terminated/finished) before reading it. Continuously checking for it's existence is not an option.
// Create new process object
process = new Process();
// Setup event handlers
process.EnableRaisingEvents = true;
process.OutputDataReceived += OutputDataReceivedEvent;
process.ErrorDataReceived += ErrorDataReceivedEvent;
process.Exited += ProgramExitedEvent;
// Setup start info
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = ExePath,
// Must be false to redirect IO
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
Arguments = arguments
};
process.StartInfo = psi;
// Start the program
process.Start();
while (!process.HasExited)
Thread.Sleep( 500 );
Process[] p = Process.GetProcessesByName( "testprogram" );
if ( p.Length != 0 )
throw new Exception("Oh oh");
UPDATE: I just tried waiting with process.WaitForExit() instead of the polling loop and the result is the exact same.
Addition: The above code was only to demonstrate a 'clearer' problem alike. To make it clear; my problem is NOT that I still can get a hold of the process by Process.GetProcessesByName( "testprogram" ); after it set HasExited to true.
The real problem is that the program I am running externally writes a file -just before- it terminates (gracefully). I use HasExited to check when the process has finished and thus I know I can read the file (because the process exited!), but it seems that HasExited returns true even sometimes when the program has NOT written the file to disk yet. Here's example code that illustrates the exact problem:
// Start the program
process.Start();
while (!process.HasExited)
Thread.Sleep( 500 );
// Could also be process.WaitForExit(), makes no difference to the result
// Now the process has quit, I can read the file it has exported
if ( !File.Exists( xmlFile ) )
{
// But this exception is thrown occasionally, why?
throw new Exception("xml file not found");
}
I realize this is an old post, but in my quest to find out why my app running the Exited event before the app had even opened I found out something that I though might be useful to people experiencing this problem in the future.
When a process is started, it is assigned a PID. If the User is then prompted with the User Account Control dialog and selects 'Yes', the process is re-started and assigned a new PID.
I sat with this for a few hours, hopefully this can save someone time.
I would suggest you to try this way:
process.Start();
while (!process.HasExited)
{
// Discard cached information about the process.
process.Refresh();
// Just a little check!
Console.WriteLine("Physical Memory Usage: " + process.WorkingSet64.ToString());
Thread.Sleep(500);
}
foreach (Process current in Process.GetProcessesByName("testprogram"))
{
if ((current.Id == process.Id) && !current.HasExited)
throw new Exception("Oh oh!");
}
Anyway... in MSDN page of HasExited I'm reading the following hightlighted note:
When standard output has been redirected to asynchronous event
handlers, it is possible that output processing will not have
completed when this property returns true. To ensure that asynchronous
event handling has been completed, call the WaitForExit() overload
that takes no parameter before checking HasExited.
That could be somehow linked to your problem as you are redirecting everything.
I know, this is an old post but maybe I can help someone. The Process class may behave unexpectedly. HasExited will return true if the process has exited or if the process runs with administrator privileges and your program only has user privileges.
I have posted a question regarding this a while back here, but did not receive a satisfiable answer.
First off, are you sure testprogram does not spawn a process of its own and exit without waiting for that process to finish? We're dealing with some kind of race condition here, and testprogram can be significant.
Second point I'd like to make is about this - "I need to be absolutely sure that this logfile exists". Well, there is no such thing. You can make your check, and then the file is gone. The common way to address this is not to check, but rather to do what you want to do with the file. Go ahead, read it, catch exceptions, retry if the thing seems unstable and you don't want to change anything. The functional check-and-do does not work well if you have more than one actor (thread or whatever) in the system.
A bunch of random ideas follows.
Have you tried using FileSystemWatcher and not depending on process completion?
Does it get any better if you try reading the file (not checking if it exists, but acting instead) in the process.Exited event? [it shouldn't]
Is the system healthy? Anything suspicious in the Event Log?
Can some really aggressive antivirus policy be involved?
(Can't tell much without seeing all the code and looking into testprogram.)
So just for a further investigation into the root cause of the problem you should maybe check out what's really happening by using Process Monitor. Simply start it and include the external program and your own tool and let it record what happens.
Within the log you should see how the external tool writes to the output file and how you open that file. But within this log you should see in which order all these accesses happen.
The first thing that came to my mind is that the Process class doesn't lie and the process is really gone when it tells so. So problem is that at this point in time it seems that the file is still not fully available. I think this is a problem of the OS, cause it holds some parts of the file still within a cache that is not fully written onto the disk and the tool has simply exited itself without flushing its file handles.
With this in mind you should see within the log that the external tool created the file, exited and AFTER that the file will be flushed/closed (by the OS [maybe remove any filters when you found this point within the log]).
So if my assumptions are correct the root cause would be the bad behavior of your external tool which you can't change thus leading to simply wait a little bit after the process has exited and hope that the timeout is long enough to get the file flushed/closed by the OS (maybe try to open the file in a loop with a timeout till it succeeded).
There's two possibilities, the process object continues to hold a reference to the process, so it has exited, but it hasn't yet been deleted. Or you have a second instance of the process running. You should also compare the process Id to make sure. Try this.
....
// Start the program
process.Start();
while (!process.HasExited)
Thread.Sleep( 500 );
Process[] p = Process.GetProcessesByName( "testprogram" );
if ( p.Length != 0 && p[0].Id == process.id && ! p[0].HasExited)
throw new Exception("Oh oh");
For a start, is there an issue with using Process.WaitForExit rather than polling it?
Anyway, it is technically possible for the process to exit from a usable point of view but the process still be around briefly while it does stuff like flush disk cache. Is the log file especially large (or any operation it is performing heavy on disk writes)?
As per MSDN documentation for HasExited.
If a handle is open to the process,
the operating system releases the
process memory when the process has
exited, but retains administrative
information about the process, such as
the handle, exit code, and exit time.
Probably not related, but it's worth noting.
If it's only a problem 1/10 of the time, and the process disappears after a second anyway, depending on your usage of HasExited, try just adding another delay after the HasExited check works, like
while (!process.HasExited)
DoStuff();
Thread.Sleep(500);
Cleanup();
and see if the problem persists.
Personally, I've always just used the Exited event handler instead of any kind of polling, and a simplistic custom wrapper around System.Diagnostics.Process to handle things like thread safety, wrapping a call to CloseMainWindow() followed by WaitForExit(timeout) and finally Kill(), logging, et cetera, and never encountered a problem.
Maybe the problem is in the testprogram? Does this code nicely flush/close etc.? It seems to me if testprogram writes a file to disk, the file should at least be available (empty or not)
If you have web application, and your external program/process is generating files (write to disk) check if your IIS have rights to write to that folder if not on properties security add permission for your IIS user, that was the reason in my case, i was receiving process.HasExited =true, but produced files from the process was not completed, after struggling for a while i add full permissions to the folder where process was writhing and process.Refresh() as Zarathos described from above and everything was working as expected.
Use process_name.Refresh() before checking whether process has exited or not. Refresh() will clear all the cached information related to the process.

Categories