/*****************************************************************************/ /* HyperSpi$agent.c This is the System Performance Information collection agent for the Hypertext SPI facility. It executes on selected nodes, writing selected system performance information into a data file, once every minute. The data contains per-minute average and total values (depending on the datum), and peak values based on the granularity of whatever is the collection period (currently 2 seconds). These data files are kept on a per-day basis. This data may then be processed and displayed by the Hypertext SPI facility. It utilizes the undocumented EXE$GETSPI interface. Interface details plagiarized (and extensively reworked) from a VMS X Window System utility named SPI. BUILD DETAILS ------------- See BUILD_HYPERSPI$AGENT.COM procedure. COPYRIGHT --------- Copyright (C) 1996-2003 Mark G.Daniel This program, comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under the conditions of the GNU GENERAL PUBLIC LICENSE, version 2. VERSION HISTORY (update SOFTWAREVN as well!) --------------- 23-DEC-2003 MGD v1.1.1, minor conditional mods to support IA64 09-SEP-2001 MGD v1.1.0, provide PID logical name to allow $FORCEX 07-JUL-2001 MGD v1.0.4, remove 'register' from storage declarations 15-FEB-2001 MGD v1.0.3, bugfix; MBytes memory for systems >= 4096MB 28-OCT-2000 MGD v1.0.2, make adjustments for RELEAXED_ANSI compilation 10-AUG-2000 MGD v1.0.1, work around HPARITH exception with lib$cvtf() 13-AUG-1998 MGD NOTE: this application is not Y2K compliant (in the sense data is stored in files named with a 2 digit year!) 20-JUN-1995 MGD v1.0.0, initial development */ /*****************************************************************************/ #define SOFTWAREVN "1.1.1" #define SOFTWARENM "HYPERSPI$AGENT" #ifdef __ALPHA char SoftwareID [] = SOFTWARENM " AXP-" SOFTWAREVN; #endif #ifdef __ia64 char SoftwareID [] = SOFTWARENM " IA64-" SOFTWAREVN; #endif #ifdef __VAX char SoftwareID [] = SOFTWARENM " VAX-" SOFTWAREVN; #endif /* standard C header files */ #include #include #include #include /* VMS-related header files */ #include #include #include #include #include #include #include #include #include #include /* application header file */ #include "HyperSpi.h" #ifndef __VAX # pragma nomember_alignment #endif #define boolean int #define true 1 #define false 0 #define VMSok(x) ((x) & STS$M_SUCCESS) #define VMSnok(x) !(((x) & STS$M_SUCCESS)) #define CollectIntervalSeconds 1 #define RecordIntervalSeconds 60 boolean Debug = 0, DebugSpi = 0; unsigned long SyiMemSize, SyiPageSize, TotalCpuTicks, TotalUserModeCpuTicks; char SyiNodeName [16]; float FloatNumberOfCPUs, TotalDeltaSeconds; unsigned long CurrentBinTime [2]; unsigned short CurrentNumTime [7]; unsigned long CollectSeconds [2] = { -10000000 * CollectIntervalSeconds, -1 }; /* this structure declared in HyperSpi.h */ struct HyperSpiData SpiRecord; /* function prototypes */ int SPI_AST (); int CollectTimer_AST (); void AgentPidLogicalAtExit (); /*****************************************************************************/ /* */ int main () { int status; unsigned short PrevMinute = 0; float ftmp; /*********/ /* begin */ /*********/ if (getenv ("HYPERSPI$DEBUG") != NULL) Debug = true; AgentPidLogical (true); /* set the process priority sufficiently high to have an edge */ if (VMSnok (status = sys$setpri (0, 0, 6, 0, 0, 0))) exit (status); /* get system name, physical memory size */ if (VMSnok (CollectSystemInfo ())) exit (status); SpiRecord.SystemMemoryMBytes = (SyiMemSize * (SyiPageSize / 512)) / 2048; /* initialize the time in the data record, etc */ sys$gettim (&CurrentBinTime); sys$numtim (&CurrentNumTime, &CurrentBinTime); SpiRecord.Minute = PrevMinute = CurrentNumTime[4]; SpiRecord.Hour = CurrentNumTime[3]; SpiRecord.Day = CurrentNumTime[2]; SpiRecord.Month = CurrentNumTime[1]; SpiRecord.Year = CurrentNumTime[0]; /* initialize the System Performance Information */ CollectSPI (); if (VMSnok (status = sys$setimr (0, &CollectSeconds, &CollectTimer_AST, 0, 0))) exit (status); sys$hiber (); /*****************/ /* infinite loop */ /*****************/ for (;;) { /* get an update of the system time */ sys$gettim (&CurrentBinTime); sys$numtim (&CurrentNumTime, &CurrentBinTime); if (CurrentNumTime[4] != PrevMinute) { if (Debug) fprintf (stdout, "TotalDeltaSeconds: %f\n", TotalDeltaSeconds); if (TotalDeltaSeconds >= 59.0) { if (Debug) fprintf (stdout, "TotalCpuTicks: %d TotalUserModeCpuTicks: %d\n", TotalCpuTicks, TotalUserModeCpuTicks); ftmp = (float)TotalCpuTicks / TotalDeltaSeconds / FloatNumberOfCPUs; SpiRecord.PercentCPU = (unsigned char)ftmp; if (ftmp - floor(ftmp) >= 0.5) SpiRecord.PercentCPU++; ftmp = (float)TotalUserModeCpuTicks / TotalDeltaSeconds / FloatNumberOfCPUs; SpiRecord.PercentUserModeCPU = (unsigned char)ftmp; if (ftmp - floor(ftmp) >= 0.5) SpiRecord.PercentUserModeCPU++; CollectPageSpace (); WriteSpiRecord (); } TotalCpuTicks = TotalUserModeCpuTicks = SpiRecord.PercentCPU = SpiRecord.PeakPercentCPU = SpiRecord.PercentUserModeCPU = SpiRecord.PeakPercentUserModeCPU = SpiRecord.PageFaults = SpiRecord.PeakPageFaults = SpiRecord.HardPageFaults = SpiRecord.PeakHardPageFaults = SpiRecord.BufferedIO = SpiRecord.PeakBufferedIO = SpiRecord.DirectIO = SpiRecord.PeakDirectIO = SpiRecord.MscpIO = SpiRecord.PeakMscpIO = 0; SpiRecord.Minute = PrevMinute = CurrentNumTime[4]; SpiRecord.Hour = CurrentNumTime[3]; SpiRecord.Day = CurrentNumTime[2]; SpiRecord.Month = CurrentNumTime[1]; SpiRecord.Year = CurrentNumTime[0]; TotalDeltaSeconds = 0.0; } /* collect System Performance Information */ CollectSPI (); if (Debug) DebugSpiRecord (); if (VMSnok (status = sys$setimr (0, &CollectSeconds, &CollectTimer_AST, 0, 0))) exit (status); sys$hiber (); } /* should never get to here! */ exit (STS$K_ERROR); } /*****************************************************************************/ /* Is executed whenever the "collection" timer expires. Initiate another timer and wake the hibernating main(). */ CollectTimer_AST () { if (Debug) fprintf (stdout, "CollectTimer_AST()\n"); sys$wake (0, 0); } /*****************************************************************************/ /* Open the per-day data file, write the SPI record into it, close the file. */ WriteSpiRecord () { static unsigned short PrevDay = 0; static int DataFileNameLength; static char DataFileName [256]; static struct FAB DataFileFab; static struct RAB DataFileRab; static struct XABPRO DataFileXabPro; int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "WriteSpiRecord()\n"); if (Debug) DebugSpiRecord (); if (SpiRecord.Day != PrevDay) { PrevDay = SpiRecord.Day; DataFileNameLength = sprintf (DataFileName, "%sHYPERSPI_%s_%s_%02.02d%02.02d%02.02d.DAT", HyperSpiDataDirectory, HyperSpiDataVersion, SyiNodeName, SpiRecord.Day, SpiRecord.Month, SpiRecord.Year % 100); if (Debug) fprintf (stdout, "DataFileName |%s|\n", DataFileName); DataFileFab = cc$rms_fab; DataFileFab.fab$l_fop = FAB$M_CIF | FAB$M_SQO; DataFileFab.fab$b_fac = FAB$M_PUT; DataFileFab.fab$l_fna = DataFileName; DataFileFab.fab$b_fns = DataFileNameLength; DataFileFab.fab$w_mrs = sizeof(struct HyperSpiData); DataFileFab.fab$b_org = FAB$C_SEQ; DataFileFab.fab$b_rfm = FAB$C_FIX; DataFileFab.fab$b_shr = FAB$M_SHRGET | FAB$M_SHRPUT; DataFileFab.fab$l_xab = &DataFileXabPro; DataFileRab = cc$rms_rab; DataFileRab.rab$l_fab = &DataFileFab; DataFileRab.rab$b_mbf = 2; DataFileRab.rab$l_rop = RAB$M_EOF; DataFileRab.rab$w_rsz = sizeof(struct HyperSpiData); DataFileRab.rab$l_rbf = (char*)&SpiRecord; /* initialize the extended access block (XAB) as a protection block */ DataFileXabPro.xab$b_bln = sizeof(DataFileXabPro); DataFileXabPro.xab$b_cod = XAB$C_PRO; DataFileXabPro.xab$l_nxt = 0; DataFileXabPro.xab$w_pro = 0xee00; /* w:r,g:r,o:rwed,s:rwed */ } if (VMSnok (status = sys$create (&DataFileFab, 0, 0))) { if (Debug) fprintf (stdout, "sys$create() %%X%08.08X\n", status); exit (status); } if (VMSnok (status = sys$connect (&DataFileRab, 0, 0))) { if (Debug) fprintf (stdout, "sys$connect() %%X%08.08X\n", status); sys$close (&DataFileFab, 0, 0); exit (status); } if (VMSnok (status = sys$put (&DataFileRab, 0, 0))) { if (Debug) fprintf (stdout, "sys$put() %%X%08.08X\n", status); sys$close (&DataFileFab, 0, 0); exit (status); } sys$close (&DataFileFab, 0, 0); } /*****************************************************************************/ /* Collect System Performance Information. Uses undocument system call EXE$GETSPI(). The code in this function is largely derived from a VMS system performance DECwindows utility named SPI. */ #define SPI$_MODES 0x1000 #define SPI$_FRLIST 0x101C #define SPI$_MOLIST 0x101D #define SPI$_FAULTS 0x101E #define SPI$_PREADS 0x101F #define SPI$_PWRITES 0x1020 #define SPI$_PWRITEIO 0x1021 #define SPI$_PREADIO 0x1022 #define SPI$_DIRIO 0x1036 #define SPI$_BUFIO 0x1037 #define SPI$_PROCS 0x101A #define SPI$_PROC 0x101B #define SPI$_MSCP_ALL 0x108B #define ModeCounters 8 #define InterruptMode 0 #define MultiProcMode 1 #define KernelMode 2 #define ExecutiveMode 3 #define SupervisorMode 4 #define UserMode 5 #define CompatibilityMode 6 #define NullMode 7 int CollectSPI () { const long cvtf_mode = LIB$K_DELTA_SECONDS_F; static unsigned long MscpAll[13], Processes, FreeList, CpuTicks, UserModeCpuTicks, PageFaults, PrevPageFaults = 0, HardPageFaults, PrevHardPageFaults = 0, PageReadIO, PrevPageReadIO = 0, PageWriteIO, PrevPageWriteIO = 0, BufferedIO, PrevBufferedIO = 0, DirectIO, PrevDirectIO = 0, MscpIO, PrevMscpIO = 0; /* storage for VMS binary time */ static unsigned long PrevBinTime [2], BinTime [2], DiffBinTime [2]; #ifndef __VAX # pragma nomember_alignment # pragma member_alignment __save #endif /* structure for a single CPU's mode ticks */ static struct SingleCPU { unsigned char CPUid; unsigned long Count[ModeCounters]; }; /* a structure all CPU's mode ticks for a system with up to 32 CPUs */ static struct { unsigned long NumberOfCPUs; struct SingleCPU CPU[32]; } ModeTicks; /* initialize structure for SPI items */ static struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } ItemList[] = { { sizeof(ModeTicks), SPI$_MODES, &ModeTicks, 0 }, { sizeof(unsigned long), SPI$_FRLIST, &FreeList, 0 }, { sizeof(unsigned long), SPI$_FAULTS, &PageFaults, 0 }, { sizeof(unsigned long), SPI$_PREADIO, &PageReadIO, 0 }, { sizeof(unsigned long), SPI$_PWRITEIO, &PageWriteIO, 0 }, { sizeof(unsigned long), SPI$_BUFIO, &BufferedIO, 0 }, { sizeof(unsigned long), SPI$_DIRIO, &DirectIO, 0 }, { sizeof(unsigned long), SPI$_PROCS, &Processes, 0 }, { sizeof(MscpAll), SPI$_MSCP_ALL, &MscpAll, 0 }, {0,0,0,0} }; #ifndef __VAX # pragma member_alignment __restore #endif static struct { unsigned long Status; unsigned long Reserved; } IOsb; static unsigned long PreviousModes[ModeCounters]; int status; unsigned long tmp, cidx, midx; unsigned long CurrentModes[ModeCounters]; float DeltaSeconds; /*********/ /* begin */ /*********/ /* get time immediately before getting system performance information */ sys$gettim (&BinTime); /* collect System Performance Information */ status = exe$getspi ( 0, /* efn */ 0, /* csiaddr */ 0, /* nodename */ &ItemList, /* item list */ &IOsb, /* iosb */ &SPI_AST, /* astaddr */ 0 ); /* astprm */ sys$hiber (); if (Debug) fprintf (stdout, "exe$getspi() %%X%08.08X IOsb: %%X%08.08X\n", status, IOsb.Status); if (VMSnok (status)) return (status); /**************************/ /* calculate elapsed time */ /**************************/ /* If zero (first previous) was subtracted from the current time *some* Alpha lib$cvtf...() spat with a HPARITH (high performance arithmetic trap) FLTINV (floating invalid operation). Not sure why! */ if (!PrevBinTime[0] && !PrevBinTime[1]) { PrevBinTime[0] = BinTime[0]; PrevBinTime[1] = BinTime[1]; } lib$sub_times (&BinTime, &PrevBinTime, &DiffBinTime); PrevBinTime[0] = BinTime[0]; PrevBinTime[1] = BinTime[1]; lib$cvtf_from_internal_time (&cvtf_mode, &DeltaSeconds, &DiffBinTime); TotalDeltaSeconds += DeltaSeconds; if (Debug) fprintf (stdout, "DeltaSeconds: %f TotalDeltaSeconds: %f\n", DeltaSeconds, TotalDeltaSeconds); /**********************/ /* calculate CPU time */ /**********************/ if (DebugSpi) { fprintf (stdout, "CPUs: %d\n", ModeTicks.NumberOfCPUs); for (cidx = 0; cidx < ModeTicks.NumberOfCPUs; cidx++) { for (midx = InterruptMode; midx <= NullMode; midx++) { fprintf (stdout, "[%d][%d] = %u\n", cidx, midx, ModeTicks.CPU[cidx].Count[midx]); } } } /* accumulate mode counters for all CPUs into CPU 0's area */ for (cidx = 1; cidx < ModeTicks.NumberOfCPUs; cidx++) for (midx = InterruptMode; midx <= NullMode; midx++) ModeTicks.CPU[0].Count[midx] += ModeTicks.CPU[cidx].Count[midx]; /* current raw CPU ticks */ CurrentModes[InterruptMode] = ModeTicks.CPU[0].Count[InterruptMode] - ModeTicks.CPU[0].Count[NullMode]; CurrentModes[MultiProcMode] = ModeTicks.CPU[0].Count[MultiProcMode]; CurrentModes[KernelMode] = ModeTicks.CPU[0].Count[KernelMode]; CurrentModes[ExecutiveMode] = ModeTicks.CPU[0].Count[ExecutiveMode]; CurrentModes[SupervisorMode] = ModeTicks.CPU[0].Count[SupervisorMode]; CurrentModes[UserMode] = ModeTicks.CPU[0].Count[UserMode]; CurrentModes[CompatibilityMode] = ModeTicks.CPU[0].Count[CompatibilityMode]; CurrentModes[NullMode] = ModeTicks.CPU[0].Count[NullMode]; if (DebugSpi) { fprintf (stdout, "raw CPU\nI: %u\nMP: %u\nK: %u\nE: %u\nS: %u\nU: %u\nC: %u\n?: %u\n", CurrentModes[0], CurrentModes[1], CurrentModes[2], CurrentModes[3], CurrentModes[4], CurrentModes[5], CurrentModes[6], CurrentModes[7]); } /* calculate the number of CPU ticks for this period */ CpuTicks = 0; for (midx = InterruptMode; midx <= CompatibilityMode; midx++) { tmp = CurrentModes[midx]; #ifndef __VAX /* It has been OBSERVED that in this application Interrupt mode counters can actually go backwards (only ever observed by 1). Don't know if this is an artifact of the code, or what? This kludge doesn't seem to cause any problems, but will look for a better solution/explanation when time permits. */ if (CurrentModes[midx] >= PreviousModes[midx]) CurrentModes[midx] = tmp - PreviousModes[midx]; else CurrentModes[midx] = 0; #else /* VAX */ CurrentModes[midx] = tmp - PreviousModes[midx]; #endif /* #ifndef __VAX */ PreviousModes[midx] = tmp; /* keep track of the total ticks */ CpuTicks += CurrentModes[midx]; } UserModeCpuTicks = CurrentModes[UserMode] + CurrentModes[CompatibilityMode]; if (DebugSpi) { fprintf (stdout, "ticks CPU\nI: %u\nMP: %u\nK: %u\nE: %u\nS: %u\nU: %u\nC: %u\n?: %u\n", CurrentModes[0], CurrentModes[1], CurrentModes[2], CurrentModes[3], CurrentModes[4], CurrentModes[5], CurrentModes[6], CurrentModes[7]); } /*******************/ /* calculate other */ /*******************/ if (DebugSpi) { fprintf (stdout, "CPU ticks %u\n\ CPU user mode ticks %u\n\ Free list: %u\n\ Page faults: %u %u hard\n\ Buffered I/O: %u\n\ Direct I/O: %u\n\ Processes: %u\n\ MSCP: %u %u %u %u %u %u %u %u %u %u %u %u %u\n", CpuTicks, UserModeCpuTicks, FreeList, PageFaults, PageReadIO+PageWriteIO, BufferedIO, DirectIO, Processes, MscpAll[0], MscpAll[1], MscpAll[2], MscpAll[3], MscpAll[4], MscpAll[5], MscpAll[6], MscpAll[7], MscpAll[8], MscpAll[9], MscpAll[10], MscpAll[11], MscpAll[12]); } /***********************/ /* update the counters */ /***********************/ if (!SpiRecord.NumberOfCPUs) FloatNumberOfCPUs = (float)(SpiRecord.NumberOfCPUs = ModeTicks.NumberOfCPUs); SpiRecord.SystemMemoryPercentInUse = ((SyiMemSize - FreeList) * 100) / SyiMemSize; SpiRecord.NumberOfProcesses = Processes; /* 'PrevPageFaults' will only be zero first time function is called */ if (PrevPageFaults) { TotalCpuTicks += CpuTicks; TotalUserModeCpuTicks += UserModeCpuTicks; if (DeltaSeconds < 1.0) DeltaSeconds = 1.0; tmp = (int)((float)CpuTicks / DeltaSeconds / FloatNumberOfCPUs); if (tmp > SpiRecord.PeakPercentCPU) SpiRecord.PeakPercentCPU = tmp; tmp = (int)((float)UserModeCpuTicks / DeltaSeconds / FloatNumberOfCPUs); if (tmp > SpiRecord.PeakPercentUserModeCPU) SpiRecord.PeakPercentUserModeCPU = tmp; } if (PageFaults > PrevPageFaults) { if (PrevPageFaults) { SpiRecord.PageFaults += (tmp = PageFaults - PrevPageFaults); if (tmp > SpiRecord.PeakPageFaults) SpiRecord.PeakPageFaults = tmp; SpiRecord.HardPageFaults += (tmp = PageReadIO - PrevPageReadIO + PageWriteIO - PrevPageWriteIO); if (tmp > SpiRecord.PeakHardPageFaults) SpiRecord.PeakHardPageFaults = tmp; } PrevPageFaults = PageFaults; PrevPageReadIO = PageReadIO; PrevPageWriteIO = PageWriteIO; } if (BufferedIO > PrevBufferedIO) { if (PrevBufferedIO) { SpiRecord.BufferedIO += (tmp = BufferedIO - PrevBufferedIO); if (tmp > SpiRecord.PeakBufferedIO) SpiRecord.PeakBufferedIO = tmp; } PrevBufferedIO = BufferedIO; } if (DirectIO > PrevDirectIO) { if (PrevDirectIO) { SpiRecord.DirectIO += (tmp = DirectIO - PrevDirectIO); if (tmp > SpiRecord.PeakDirectIO) SpiRecord.PeakDirectIO = tmp; } PrevDirectIO = DirectIO; } MscpIO = MscpAll[0]; if (MscpIO > PrevMscpIO) { if (PrevMscpIO) { SpiRecord.MscpIO += (tmp = MscpIO - PrevMscpIO); if (tmp > SpiRecord.PeakMscpIO) SpiRecord.PeakMscpIO = tmp; } PrevMscpIO = MscpIO; } return (SS$_NORMAL); } /*****************************************************************************/ /* AST function executed when exe$getspi() completes. Just wake the process. */ SPI_AST () { sys$wake (0, 0); } /****************************************************************************/ /* Get the size and amount of free space in the installed page file(s). */ CollectPageSpace () { static unsigned long PageFileSize, PageFileFree; static struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } SyiItems [] = { { sizeof(PageFileSize), SYI$_PAGEFILE_PAGE, &PageFileSize, 0 }, { sizeof(PageFileFree), SYI$_PAGEFILE_FREE, &PageFileFree, 0 }, {0,0,0,0}, }; int status; struct { unsigned long Status; unsigned long Reserved; } IOsb; /*********/ /* begin */ /*********/ status = sys$getsyiw (0, 0, 0, &SyiItems, &IOsb, 0, 0); if (Debug) fprintf (stdout, "sys$getsyiw() %%X%08.08X IOsb: %%X%08.08X\n", status, IOsb.Status); if (VMSnok (status)) return (status); if (VMSnok (IOsb.Status)) return (IOsb.Status); SpiRecord.PageSpaceMBytes = (PageFileSize * (SyiPageSize / 512)) / 2048; SpiRecord.PageSpacePercentInUse = 100 - ((PageFileFree * 100) / PageFileSize); if (Debug) fprintf (stdout, "PageSpaceMBytes: %d PageSpacePercentInUse: %d\n", SpiRecord.PageSpaceMBytes, SpiRecord.PageSpacePercentInUse); return (SS$_NORMAL); } /****************************************************************************/ /* Get node name, amount of physical memory, and memory page size. */ /* these are currently not in header files for VAX C */ /* discovered empirically on an AXP system using DEC C */ #define SYI$_PAGE_SIZE 4452 #define SYI$_MEMSIZE 4459 int CollectSystemInfo () { int status; unsigned short SyiNodeNameLength; struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } SyiItems [] = { { 15, SYI$_NODENAME, &SyiNodeName, &SyiNodeNameLength }, { 4, SYI$_MEMSIZE, &SyiMemSize, 0 }, { 4, SYI$_PAGE_SIZE, &SyiPageSize, 0 }, { 0,0,0,0 } }; struct { unsigned long Status; unsigned long Reserved; } IOsb; /*********/ /* begin */ /*********/ status = sys$getsyiw (0, 0, 0, &SyiItems, &IOsb, 0, 0); if (Debug) fprintf (stdout, "sys$getsyiw() %%X%08.08X IOsb: %%X%08.08X\n", status, IOsb.Status); if (VMSok (IOsb.Status)) SyiNodeName[SyiNodeNameLength] = '\0'; else SyiNodeName[0] = '\0'; if (VMSnok (IOsb.Status)) return (IOsb.Status); return (SS$_NORMAL); } /*****************************************************************************/ /* Create a logical name HYPERSPI_AGENT_PID containing the process PID (binary). */ AgentPidLogical (boolean CreateLogical) { static unsigned long PslUser = PSL$C_USER; static $DESCRIPTOR (LnmSystemDsc, "LNM$SYSTEM"); static $DESCRIPTOR (PidLogNameDsc, "HYPERSPI_AGENT_PID"); static unsigned long Pid; static struct { unsigned short buf_len; unsigned short item; void *buf_addr; unsigned short *short_ret_len; } JpiItems [] = { { sizeof(Pid), JPI$_PID, &Pid, 0 }, { 0,0,0,0 } }, LnmPidItem [] = { { sizeof(Pid), LNM$_STRING, &Pid, 0 }, { 0,0,0,0 } }; int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "AgentPidLogical()\n"); if (CreateLogical) { atexit (&AgentPidLogicalAtExit); status = sys$getjpiw (0, 0, 0, &JpiItems, 0, 0, 0); if (VMSok (status)) status = sys$crelnm (0, &LnmSystemDsc, &PidLogNameDsc, &PslUser, &LnmPidItem); if (VMSnok (status)) exit (status); } else sys$dellnm (&LnmSystemDsc, &PidLogNameDsc, &PslUser); } /*****************************************************************************/ /* Executed during image exit (in particular $FORCEX). */ void AgentPidLogicalAtExit () { if (Debug) fprintf (stdout, "AgentPidLogicalAtExit()\n"); AgentPidLogical (false); } /*****************************************************************************/ /* */ DebugSpiRecord () { fprintf (stdout, "%02.02d:%02.02d CPU %d %d %d, %d %d MEM %dMb %d%% PAG %dMb %d%%\ PRC %d FLT %d %d, %d %d IO %d %d %d, %d %d %d\n", SpiRecord.Hour, SpiRecord.Minute, SpiRecord.NumberOfCPUs, SpiRecord.PercentCPU, SpiRecord.PercentUserModeCPU, SpiRecord.PeakPercentCPU, SpiRecord.PeakPercentUserModeCPU, SpiRecord.SystemMemoryMBytes, SpiRecord.SystemMemoryPercentInUse, SpiRecord.PageSpaceMBytes, SpiRecord.PageSpacePercentInUse, SpiRecord.NumberOfProcesses, SpiRecord.PageFaults, SpiRecord.HardPageFaults, SpiRecord.PeakPageFaults, SpiRecord.PeakHardPageFaults, SpiRecord.BufferedIO, SpiRecord.DirectIO, SpiRecord.MscpIO, SpiRecord.PeakBufferedIO, SpiRecord.PeakDirectIO, SpiRecord.PeakMscpIO); } /****************************************************************************/ /* Does a case-insensitive character-by-character string compare and returns true if two strings are the same, or false if not. If a maximum number of characters are specified only those will be compared, if the entire strings should be compared then specify the number of characters as 0. */ boolean strsame ( char *sptr1, char *sptr2, int count ) { while (*sptr1 && *sptr2) { if (toupper (*sptr1++) != toupper (*sptr2++)) return (false); if (count > 0) if (--count == 0) return (true); } if (*sptr1 || *sptr2) return (false); else return (true); } /****************************************************************************/