Hi Lois,
Win32::Job is part of the libwin32 bundle of modules. There is
a special mailing list for libwin32 development at [email protected], but
discussion of new features for a module should be fine on perl-win32-users too.
However, patches to the module should be submitted to either the libwin32
mailing list, or to the request tracker at
http://rt.cpan.org/Public/Dist/Display.html?Name=libwin32
Feel free to copy me directly on all discussion related to this,
as I’ll be the person ultimately applying the change.
Cheers,
-Jan
PS: Please try to use plain text instead of HTML for mailing
list messages whenever possible!
From:
[EMAIL PROTECTED]
[mailto:[EMAIL PROTECTED] On Behalf Of Louis
Lecaroz
Sent: August 30, 2006 3:46 AM
To: [email protected];
[email protected]
Subject: New feature added in W32::Job, job statistics
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.
So
my first question is :
according
to other participants of my project, Do know where & how can I propose my
enhancement to be taken in account in standard ActiveState Perl Distributions ?
For
those who are interrested here is a a here is now an example of the result stat
returned from Job->Stats() :
%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 */
|