Subject: | Retrieving all job statistics in Win32::Jobs |
As requested by Jan, I am forwarding my enhancement request here. I
modified job.c to enhance this great class :
For a project, I modified the Win32::Job for retreiving Job
statistics. The Win32::Job is really a great Perl class mapping Win32
Job features.
But developers also use Win32 job features no to only start a process
& its child to track them but also for retrieving global statistics on
them.
But in the current version of Win32::Job, only some minor details are
returned like ONLY parent process exitcode, memory/cpu time.
Because sometimes, you need global statistics on a parent process &
ALL ITS childs, I modified it.
This is really cheap in term of development & cpu usage because it
needs only to other calls two times a Win32 api
(QueryInformationJobObject). I tested it on more than 10000 process
loading when the job was finished by calling stats() & it appears to
work correctly. But I did not try to call the stats() method when the
job was not finished to retreive statistics based on a scheduled time
when the job runs ;).
thx in advance,
Louis
here is now an example of the result stat returned from Job->Stats()
after having added my modification :
%stat = (
'Job' => {
'LimitInfo' => {
'PeakJobMemoryUsed' => '21037056',
'PeakProcessMemoryUsed' => '8572928'
},
'BasicInfo' => {
'ThisPeriodTotalKernelTime' => '44.234375',
'TotalKernelTime' => '44.234375',
'ThisPeriodTotalUserTime' => '18.625',
'TotalUserTime' => '18.625',
'TotalPageFaultCount' => '655537',
'ActiveProcesses' => '0',
'TotalProcesses' => '606',
'TotalTerminatedProcesses' => '0'
},
'IoInfo' => {
'OtherTransferCount' => '29616278',
'WriteTransferCount' => '793999762',
'OtherOperationCount' => '745504',
'ReadOperationCount' => '600463',
'WriteOperationCount' => '583931',
'ReadTransferCount' => '770897934'
}
},
'3568' => {
'time' => {
'kernel' => '0',
'user' => '0',
'elapsed' => '332.4019952'
},
'exitcode' => 0
}
);
& the modifed code for returining all these useful stats, (as you will
see my modification is really minor because I only added some fiew
lines in the get_status() method) :
static void
get_status(pTHX_ JOB_T self, int wait)
{
I32 i, imax = AV_REAL_LEN(self->procs);
if (imax)
hv_clear(self->info);
for (i = 0; i < imax; i++) {
STRLEN l;
SV *tmp = *av_fetch(self->procs, i, 0);
PROC_T inf = (PROC_T)SvPV(tmp, l);
HV *proc = newHV();
HV *htime = newHV();
SV *ent = newSVuv(inf->dwProcessId);
DWORD ecode;
FILETIME stime, etime, ktime, utime;
double te, tk, tu;
/* Wait for the process to finish terminating */
if (wait)
WaitForSingleObject(inf->hProcess, INFINITE);
/* Get information about the process (only care about user and
* kernel times */
GetExitCodeProcess(inf->hProcess, &ecode);
GetProcessTimes(inf->hProcess, &stime, &etime, &ktime, &utime);
{
ULARGE_INTEGER user, kernel, start, end, elapsed;
kernel.LowPart = ktime.dwLowDateTime;
kernel.HighPart = ktime.dwHighDateTime;
user.LowPart = utime.dwLowDateTime;
user.HighPart = utime.dwHighDateTime;
start.LowPart = stime.dwLowDateTime;
start.HighPart = stime.dwHighDateTime;
end.LowPart = etime.dwLowDateTime;
end.HighPart = etime.dwHighDateTime;
if (!end.QuadPart) { /* process is not finished yet */
SYSTEMTIME now;
GetSystemTime(&now);
SystemTimeToFileTime(&now, &etime);
end.LowPart = etime.dwLowDateTime;
end.HighPart = etime.dwHighDateTime;
}
elapsed.QuadPart = end.QuadPart - start.QuadPart;
/* We must cast to signed __int64 because MSVC++ can't
* convert unsigned __int64 to double. It's probably okay;
* if the process is running long enough to overflow a
* signed 64-bit integer, it won't fit into a double
* anyway. */
tk = ((__int64) kernel.QuadPart) / 10000000.0;
tu = ((__int64) user.QuadPart) / 10000000.0;
te = ((__int64)elapsed.QuadPart) / 10000000.0;
}
/* Create a tree structure like this:
* <pid>:
* exitcode: 123
* time:
* user: 123
* kernel: 123
* elapsed: 123
*/
hv_store(htime, "user", 4, newSVnv(tu), 0);
hv_store(htime, "kernel", 6, newSVnv(tk), 0);
hv_store(htime, "elapsed", 7, newSVnv(te), 0);
hv_store(proc, "exitcode", 8, newSVuv(ecode), 0);
hv_store(proc, "time", 4, newRV_noinc((SV*)htime), 0);
hv_store_ent(self->info, ent, newRV_noinc((SV*)proc), 0);
SvREFCNT_dec(ent); /* free */
}
if(self->hJob)
{
SV *ent = newSVpv("Job",3);
HV *job = newHV();
{
JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION jobBasicAndIOInfo;
if(QueryInformationJobObject(self->hJob,
JobObjectBasicAndIoAccountingInformation, &jobBasicAndIOInfo, sizeof
(jobBasicAndIOInfo), NULL))
{
{
HV *haccounting = newHV();
double tk, tu, tkp, tup;
tk = ((__int64)
jobBasicAndIOInfo.BasicInfo.TotalKernelTime.QuadPart) / 10000000.0;
tu = ((__int64)
jobBasicAndIOInfo.BasicInfo.TotalUserTime.QuadPart) / 10000000.0;
tkp = ((__int64)
jobBasicAndIOInfo.BasicInfo.ThisPeriodTotalKernelTime.QuadPart) /
10000000.0;
tup = ((__int64)
jobBasicAndIOInfo.BasicInfo.ThisPeriodTotalUserTime.QuadPart) /
10000000.0;
hv_store(haccounting, "TotalUserTime", 13, newSVnv(tu), 0);
hv_store(haccounting, "TotalKernelTime", 15, newSVnv(tk), 0);
hv_store(haccounting, "ThisPeriodTotalUserTime", 23, newSVnv
(tup), 0);
hv_store(haccounting, "ThisPeriodTotalKernelTime", 25, newSVnv
(tkp), 0);
hv_store(haccounting, "TotalPageFaultCount", 19, newSVnv
(jobBasicAndIOInfo.BasicInfo.TotalPageFaultCount), 0);
hv_store(haccounting, "TotalProcesses", 14, newSVnv
(jobBasicAndIOInfo.BasicInfo.TotalProcesses), 0);
hv_store(haccounting, "ActiveProcesses", 15, newSVnv
(jobBasicAndIOInfo.BasicInfo.ActiveProcesses), 0);
hv_store(haccounting, "TotalTerminatedProcesses", 24, newSVnv
(jobBasicAndIOInfo.BasicInfo.TotalTerminatedProcesses), 0);
hv_store(job, "BasicInfo", 9, newRV_noinc((SV*)haccounting),
0);
}
{
HV *hio = newHV();
char szI64[32];
sprintf(szI64,"%
I64u",jobBasicAndIOInfo.IoInfo.ReadOperationCount);
hv_store(hio, "ReadOperationCount", 18, newSVpv(szI64,strlen
(szI64)), 0);
sprintf(szI64,"%
I64u",jobBasicAndIOInfo.IoInfo.WriteOperationCount);
hv_store(hio, "WriteOperationCount", 19, newSVpv(szI64,strlen
(szI64)), 0);
sprintf(szI64,"%
I64u",jobBasicAndIOInfo.IoInfo.OtherOperationCount);
hv_store(hio, "OtherOperationCount", 19, newSVpv(szI64,strlen
(szI64)), 0);
sprintf(szI64,"%I64u",jobBasicAndIOInfo.IoInfo.ReadTransferCount);
hv_store(hio, "ReadTransferCount", 17, newSVpv(szI64,strlen
(szI64)), 0);
sprintf(szI64,"%
I64u",jobBasicAndIOInfo.IoInfo.WriteTransferCount);
hv_store(hio, "WriteTransferCount", 18, newSVpv(szI64,strlen
(szI64)), 0);
sprintf(szI64,"%
I64u",jobBasicAndIOInfo.IoInfo.OtherTransferCount);
hv_store(hio, "OtherTransferCount", 18, newSVpv(szI64,strlen
(szI64)), 0);
hv_store(job, "IoInfo", 6, newRV_noinc((SV*)hio), 0);
}
}
}
{
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobExtendedLimitInfo;
if(QueryInformationJobObject(self->hJob,
JobObjectExtendedLimitInformation, &jobExtendedLimitInfo, sizeof
(jobExtendedLimitInfo), NULL))
{
{
HV *hlimits = newHV();
if
(jobExtendedLimitInfo.BasicLimitInformation.LimitFlags&JOB_OBJECT_LIMIT
_JOB_MEMORY) hv_store(hlimits, "ProcessMemoryLimit", 18, newSVnv
(jobExtendedLimitInfo.ProcessMemoryLimit), 0);
if
(jobExtendedLimitInfo.BasicLimitInformation.LimitFlags&JOB_OBJECT_LIMIT
_JOB_MEMORY) hv_store(hlimits, "JobMemoryLimit", 14, newSVnv
(jobExtendedLimitInfo.JobMemoryLimit), 0);
hv_store(hlimits, "PeakProcessMemoryUsed", 21, newSVnv
(jobExtendedLimitInfo.PeakProcessMemoryUsed), 0);
hv_store(hlimits, "PeakJobMemoryUsed", 17, newSVnv
(jobExtendedLimitInfo.PeakJobMemoryUsed), 0);
hv_store(job, "LimitInfo", 9, newRV_noinc((SV*)hlimits), 0);
}
}
}
hv_store_ent(self->info, ent, newRV_noinc((SV*)job), 0);
SvREFCNT_dec(ent); /* free */
}
}
Subject: | Job.c |
Message body is not shown because it is too large.