-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add option to collect cpu usage from processes #13
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,23 +4,29 @@ | |
*--------------------------------------------------------------------------------------------*/ | ||
|
||
const native = require('../build/Release/windows_process_tree.node'); | ||
import { IProcessTreeNode } from 'windows-process-tree'; | ||
import { IProcessInfo, IProcessTreeNode, IProcessCpuInfo } from 'windows-process-tree'; | ||
|
||
export enum ProcessDataFlag { | ||
None = 0, | ||
Memory = 1 | ||
} | ||
|
||
interface IProcessInfo { | ||
pid: number; | ||
ppid: number; | ||
name: string; | ||
memory?: number; | ||
} | ||
|
||
// requestInProgress is used for any function that uses CreateToolhelp32Snapshot, as multiple calls | ||
// to this cannot be done at the same time. | ||
let requestInProgress = false; | ||
const requestQueue = []; | ||
let cpuUsageRequestInProgress = false; | ||
|
||
const requestQueue = { | ||
getProcessCpuUsage: [], | ||
getProcessList: [], | ||
getProcessTree: [] | ||
}; | ||
|
||
/** | ||
* Filters a list of processes to rootPid and its descendents and creates a tree | ||
* @param processList The list of processes | ||
* @param rootPid The process to use as the root | ||
*/ | ||
function buildProcessTree(processList: IProcessInfo[], rootPid: number): IProcessTreeNode { | ||
const rootIndex = processList.findIndex(v => v.pid === rootPid); | ||
if (rootIndex === -1) { | ||
|
@@ -37,9 +43,83 @@ function buildProcessTree(processList: IProcessInfo[], rootPid: number): IProces | |
}; | ||
} | ||
|
||
/** | ||
* Filters processList to contain the process with rootPid and all of its descendants | ||
* @param rootPid The root pid | ||
* @param processList The list of all processes | ||
*/ | ||
function filterProcessList(rootPid: number, processList: IProcessInfo[]): IProcessInfo[] { | ||
const rootIndex = processList.findIndex(v => v.pid === rootPid); | ||
if (rootIndex === -1) { | ||
return undefined; | ||
} | ||
|
||
const rootProcess = processList[rootIndex]; | ||
const childIndexes = processList.filter(v => v.ppid === rootPid); | ||
return childIndexes.map(c => filterProcessList(c.pid, processList)).reduce((prev, current) => prev.concat(current), [rootProcess]); | ||
} | ||
|
||
/** | ||
* Returns a list of processes containing the rootPid process and all of its descendants | ||
* @param rootPid The pid of the process of interest | ||
* @param callback The callback to use with the returned set of processes | ||
* @param flags The flags for what process data should be included | ||
*/ | ||
export function getProcessList(rootPid: number, callback: (processList: IProcessInfo[]) => void, flags?: ProcessDataFlag): void { | ||
// Push the request to the queue | ||
requestQueue.getProcessList.push({ | ||
callback: callback, | ||
rootPid: rootPid | ||
}); | ||
|
||
// Only make a new request if there is not currently a request in progress. | ||
// This prevents too many requests from being made, there is also a crash that | ||
// can occur when performing multiple calls to CreateToolhelp32Snapshot at | ||
// once. | ||
if (!requestInProgress) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should refactor this to share code with getProcessTree, I think we need to pass in a queue and a filter function |
||
requestInProgress = true; | ||
native.getProcessList((processList) => { | ||
requestQueue.getProcessList.forEach(r => { | ||
r.callback(filterProcessList(r.rootPid, processList)); | ||
}); | ||
requestQueue.getProcessList.length = 0; | ||
requestInProgress = false; | ||
}, flags || 0); | ||
} | ||
} | ||
|
||
/** | ||
* Returns the list of processes annotated with cpu usage information | ||
* @param processList The list of processes | ||
* @param callback The callback to use with the returned list of processes | ||
*/ | ||
export function getProcessCpuUsage(processList: IProcessInfo[], callback: (tree: IProcessCpuInfo[]) => void): void { | ||
// Push the request to the queue | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indentation, we should add an editor config file :) |
||
requestQueue.getProcessCpuUsage.push({ | ||
callback: callback | ||
}); | ||
|
||
if (!cpuUsageRequestInProgress) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure we need this blocking flag |
||
cpuUsageRequestInProgress = true; | ||
native.getProcessCpuUsage(processList, (processListWithCpu) => { | ||
requestQueue.getProcessCpuUsage.forEach(r => { | ||
r.callback(processListWithCpu); | ||
}); | ||
requestQueue.getProcessCpuUsage.length = 0; | ||
cpuUsageRequestInProgress = false; | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* Returns a tree of processes with rootPid as the root | ||
* @param rootPid The pid of the process that will be the root of the tree | ||
* @param callback The callback to use with the returned list of processes | ||
* @param flags Flags indicating what process data should be written on each node | ||
*/ | ||
export function getProcessTree(rootPid: number, callback: (tree: IProcessTreeNode) => void, flags?: ProcessDataFlag): void { | ||
// Push the request to the queue | ||
requestQueue.push({ | ||
requestQueue.getProcessTree.push({ | ||
callback: callback, | ||
rootPid: rootPid | ||
}); | ||
|
@@ -51,10 +131,10 @@ export function getProcessTree(rootPid: number, callback: (tree: IProcessTreeNod | |
if (!requestInProgress) { | ||
requestInProgress = true; | ||
native.getProcessList((processList) => { | ||
requestQueue.forEach(r => { | ||
requestQueue.getProcessTree.forEach(r => { | ||
r.callback(buildProcessTree(processList, r.rootPid)); | ||
}); | ||
requestQueue.length = 0; | ||
requestQueue.getProcessTree.length = 0; | ||
requestInProgress = false; | ||
}, flags || 0); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,7 @@ void GetRawProcessList(ProcessInfo process_info[1024], uint32_t* process_count, | |
} | ||
} while (*process_count < 1024 && Process32Next(snapshot_handle, &process_entry)); | ||
} | ||
|
||
CloseHandle(snapshot_handle); | ||
} | ||
|
||
|
@@ -48,6 +49,43 @@ void GetProcessMemoryUsage(ProcessInfo process_info[1024], uint32_t* process_cou | |
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) { | ||
process_info[*process_count].memory = (DWORD)pmc.WorkingSetSize; | ||
} | ||
|
||
} | ||
|
||
ULONGLONG GetTotalTime(const FILETIME* kernelTime, const FILETIME* userTime) { | ||
ULARGE_INTEGER kt, ut; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment to explain function |
||
kt.LowPart = (*kernelTime).dwLowDateTime; | ||
kt.HighPart = (*kernelTime).dwHighDateTime; | ||
|
||
ut.LowPart = (*userTime).dwLowDateTime; | ||
ut.HighPart = (*userTime).dwHighDateTime; | ||
|
||
return kt.QuadPart + ut.QuadPart; | ||
} | ||
|
||
void GetCpuUsage(Cpu* cpu_info, uint32_t* process_index, BOOL first_pass) { | ||
DWORD pid = cpu_info[*process_index].pid; | ||
HANDLE hProcess; | ||
|
||
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); | ||
|
||
if (hProcess == NULL) { | ||
return; | ||
} | ||
|
||
FILETIME creationTime, exitTime, kernelTime, userTime; | ||
FILETIME sysIdleTime, sysKernelTime, sysUserTime; | ||
if (GetProcessTimes(hProcess, &creationTime, &exitTime, &kernelTime, &userTime) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We may want to handle when the if condition is false (cpu = undefined?) |
||
&& GetSystemTimes(&sysIdleTime, &sysKernelTime, &sysUserTime)) { | ||
if (first_pass) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indentation |
||
cpu_info[*process_index].initialProcRunTime = GetTotalTime(&kernelTime, &userTime); | ||
cpu_info[*process_index].initialSystemTime = GetTotalTime(&sysKernelTime, &sysUserTime); | ||
} else { | ||
ULONGLONG endProcTime = GetTotalTime(&kernelTime, &userTime); | ||
ULONGLONG endSysTime = GetTotalTime(&sysKernelTime, &sysUserTime); | ||
|
||
cpu_info[*process_index].cpu = 100.0 * (endProcTime - cpu_info[*process_index].initialProcRunTime) / (endSysTime - cpu_info[*process_index].initialSystemTime); | ||
} | ||
} | ||
|
||
CloseHandle(hProcess); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,13 @@ | |
#include <nan.h> | ||
#include <windows.h> | ||
|
||
struct Cpu { | ||
DWORD pid; | ||
double cpu; | ||
ULONGLONG initialProcRunTime; | ||
ULONGLONG initialSystemTime; | ||
}; | ||
|
||
struct ProcessInfo { | ||
TCHAR name[MAX_PATH]; | ||
DWORD pid; | ||
|
@@ -18,11 +25,14 @@ struct ProcessInfo { | |
|
||
enum ProcessDataFlags { | ||
NONE = 0, | ||
MEMORY = 1 | ||
MEMORY = 1, | ||
CPU = 2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove CPU flag |
||
}; | ||
|
||
void GetRawProcessList(ProcessInfo process_info[1024], uint32_t* process_count, DWORD* flags); | ||
|
||
void GetProcessMemoryUsage(ProcessInfo process_info[1024], uint32_t* process_count); | ||
|
||
void GetCpuUsage(Cpu cpu_info[1024], uint32_t* process_count, BOOL first_run); | ||
|
||
#endif // SRC_PROCESS_H_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should type this so it's clear what they do.