Skip Menu |

This queue is for tickets about the Win32-Job CPAN distribution.

Report information
The Basics
Id: 21254
Status: new
Priority: 0/
Queue: Win32-Job

People
Owner: Nobody in particular
Requestors: louis.lecaroz [...] le-resistant.com
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: (no value)
Fixed in: (no value)



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.