Search code examples
c#phpiiswindows-server

IIS process monitor to list IIS w3wp.exe processes with their PHP (php-cgi.exe) processes


I'm trying to write small IIS process monitor (CPU and memory usage) which should be as light for the system as possible. All I need is IIS App Pool Name, current Memory Usage and current CPU Usage.

I've managed to do it without bigger problems using these two commands:

using (var srvman = new ServerManager())
{
    workerProcesses = srvman.WorkerProcesses;
}

and

processes = Process.GetProcesses().Where(processItem => processItem.ProcessName.Contains("w3wp") || processItem.ProcessName.Contains("php-cgi"))

The first one returns running app pools with process ID's and second one is returning process information from system (so here is where I'm getting CPU and Memory usage). Joining this two informations gives me almost what I want. The one exception is PHP apps which are running on IIS.

For PHP apps, php-cgi.exe processes are being spawned which I need to correlate to the IIS App Pool worker process. Do you have any idea how to connect php-cgi.exe processes to their w3wp.exe parents?

Here is an example of the output I would like to produce:

1x w3wp.exe for site.com is using 15MB memory
4x php-cgi.exe is using 4x 15MB = 60MB memory

I'm planning to sum this information (which in this scenario would be 75 MB of memory usage for site.com).


Solution

  • You can get the parent process PID using a bit of P/Invoke as described by Simon Mourier. Doing so is a low cost operation and will tie the list of all php-cgi process back to their creators.

    using (var srvman = new ServerManager())
    {
        var procs = from worker in srvman.WorkerProcesses
                    let workerProcess = Process.GetProcessById(worker.ProcessId)
                    join cgi in Process.GetProcessesByName("php-cgi")
                        on workerProcess.Id equals ParentProcessUtilities.GetParentProcess(cgi.Handle).Id
                        into childProcesses
                    select new
                    {
                        Worker = worker,
                        WorkerProcess = workerProcess,
                        Children = childProcesses,
                        TotalMemoryUse = workerProcess.PrivateMemorySize64
                            + childProcesses.Sum(p => p.PrivateMemorySize64)
                    };
    
        foreach (var proc in procs)
        {
            Console.WriteLine("Worker {0}:{1} using {2} total bytes", proc.Worker.AppPoolName,
                proc.Worker.ProcessId, proc.TotalMemoryUse);
    
            foreach (var child in proc.Children)
            {
                Console.WriteLine("\tphp-cgi process {0} using {1} bytes", child.Id, child.PrivateMemorySize64);
            }
        }
    }
    

    Output

    C:\drop> phpProcessTest.exe
    Worker DefaultAppPool:4396 using 61530112 total bytes
        php-cgi process 3540 using 7024640 bytes
        php-cgi process 3144 using 6389760 bytes