diff --git a/src/python/build_pyrte.com b/src/python/build_pyrte.com index 4751697f3268ba738013ea88b5ab829ced45ce18_c3JjL3B5dGhvbi9idWlsZF9weXJ0ZS5jb20=..46790834bbe052e9409a894eeac68ad640ab5006_c3JjL3B5dGhvbi9idWlsZF9weXJ0ZS5jb20= 100755 --- a/src/python/build_pyrte.com +++ b/src/python/build_pyrte.com @@ -1,7 +1,7 @@ $!----------------------------------------------------------------------------- $! BUILD_PYRTE.COM $! -$! Copyright (C) 2007-2009 Mark G.Daniel. +$! Copyright (C) 2007-2011 Mark G.Daniel. $! This package (all associated programs), 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. @@ -10,7 +10,7 @@ $! VERSION HISTORY $! --------------- $! -$ IMAGE_IDENT = "PYRTE114" +$ IMAGE_IDENT = "PYRTE119" $! $! 22-APR-2007 JFP use same build options as Python $! 10-APR-2007 MGD initial @@ -34,6 +34,5 @@ $! $ CC_DEFINES = "/DEFINE=(BUILD_DATETIME=""""""''DATETIME'"""""")" $ CC_INCLUDES = "/INCLUDE=(PYTHON_INCLUDE:)" -$ CC_OPTIONS = "/DECC/OPTIMIZE/PREFIX=ALL/NAMES=(AS_IS,SHORTENED)/FLOAT=IEEE" -$ CC_OPTIONS = CC_OPTIONS + "/IEEE_MODE=DENORM_RESULTS/PREFIX=ALL" +$ CC_OPTIONS = "/DECC/OPTIMIZE/PREFIX=ALL/NAMES=(AS_IS,SHORTENED)/PREFIX=ALL" $ CC_OPTIONS = CC_OPTIONS + "/DEFINE=(_LARGEFILE)/ACCEPT=NOVAXC_KEYWORDS" @@ -39,4 +38,5 @@ $ CC_OPTIONS = CC_OPTIONS + "/DEFINE=(_LARGEFILE)/ACCEPT=NOVAXC_KEYWORDS" +$ CC_OPTIONS = CC_OPTIONS + "/FLOAT=IEEE/IEEE_MODE=DENORM_RESULTS" $! $ IF F$SEARCH("OBJ_''ARCH_NAME'.DIR") .EQS. "" - THEN CREATE /DIR [.OBJ_'ARCH_NAME'] diff --git a/src/python/pyrte.c b/src/python/pyrte.c index 4751697f3268ba738013ea88b5ab829ced45ce18_c3JjL3B5dGhvbi9weXJ0ZS5j..46790834bbe052e9409a894eeac68ad640ab5006_c3JjL3B5dGhvbi9weXJ0ZS5j 100755 --- a/src/python/pyrte.c +++ b/src/python/pyrte.c @@ -187,7 +187,7 @@ PYRTE COPYRIGHT --------------- -Copyright (C) 2007-2009 Mark G.Daniel +Copyright (C) 2007-2011 Mark G.Daniel This package 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 3, or any later version. @@ -202,6 +202,27 @@ http://www.python.org/psf/license.html +PYRTE METRICS REPORT +-------------------- +When using the byte-code caching RTE it is possible to generate a report on +each cached entry. This includes the number of times the cached code has been +used, the size of the byte-code, a list of the last ten (default compilation) +uses with each use's time-stamp, duration, CPU ticks attributed, BIO count, +DIO count, working set pages, page faults, when the cached script was last +modified, the script file location, and any associated pre-compiled python byte +code file. Each of the entry's uses are listed top to bottom from most to +least recent. A little care in interpreting the data is required. Note the +CPU consumed is accumulated in 10mS ticks and therefore a little coarse. + +This report is only available if the logical name PYRTE_METRICS is defined +and accessable by the RTE. For example + + $ DEFINE /SYSTEM PYRTE_METRICS 1 + +Accessing *any* script with a query string beginning "$metrics$" will then +display the report. + + MAPPING RULES ------------- For standard (non-CGIplus) Python scripts ... @@ -260,6 +281,7 @@ as per Apache mod_wsgi behaviour PYRTE_CACHE_ENTRY enables function RtePyMethRteCache() PYRTE_CACHE_MAX integer size of code cache (zero to compare uncached) +PYRTE_METRICS enable metric collection and report via "?$metrics$" query PYRTE_NOREUSE_INTERPRETER do not reuse interpreter for each script instance PYRTE_USAGE_LIMIT integer number of requests before voluntary exit PYRTE_WSGI_FORCE_BUFFERING enable buffering with WASD, this break the WSGI @@ -274,6 +296,10 @@ VERSION HISTORY (update SOFTWAREVN as well!) --------------- +05-NOV-2011 MGD v1.1.10, Python 2.7.2, see changes to includes +27-FEB-2011 MGD v1.1.9, /CLI= to allow a command-line script activation + logical PYRTE_METRICS enables "?$metrics$" report +20-JUL-2010 MGD v1.1.8, WASD v10.1 ProctorDetect() 17-MAY-2010 JFP v1.1.7, add missing Py_Finalize() in ProcessCachingRte() PyErr_Occurred is a borrowed reference - remove erroneous calls to Py_DECREF @@ -293,8 +319,8 @@ */ /*****************************************************************************/ -#define SOFTWARECR "Copyright (C) 2007-2010 Mark G.Daniel" -#define SOFTWAREVN "1.1.7" +#define SOFTWARECR "Copyright (C) 2007-2011 Mark G.Daniel" +#define SOFTWAREVN "1.1.10" #define SOFTWARENM "PYRTE" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN @@ -316,6 +342,26 @@ /* includes */ /************/ +/* + Actually <unistd.h> must come before the python include to avoid + +char *crypt (const char *__key, const char *__salt); +......^ +%CC-E-NOTEXPECTING, Error parsing parameter list. Found "(" when expecting one of: <type-specifier>, <identifier>, "...", ")". +at line number 406 in module UNISTD of text library SYS$COMMON:[SYSLIB]DECC$RTLDEF.TLB;1 + + with HP C V7.1-011 on OpenVMS IA64 V8.4, and + + ctime_r ((const int*)tsecs,buf); +............^ +%CC-W-PTRMISMATCH, In this statement, the referenced type of the pointer value +"(const int ...)tsecs" is "const int", which is not compatible with "const unsigned long". + + with HP C V7.1-011 on OpenVMS IA64 V8.4 and + HP C V7.3-009 on OpenVMS Alpha V8.3 ... at least! +*/ +#include <unistd.h> + /* Python-specific includes (must come first!) */ #include <python.h> @@ -323,6 +369,7 @@ /* standard C header files */ #include <ctype.h> +#include <descrip.h> #include <errno.h> #include <stdarg.h> #include <stdio.h> @@ -334,5 +381,7 @@ /* VMS-specific header files */ -#include <descrip.h> +#include <cvt$routines.h> +#include <cvtdef.h> +#include <jpidef.h> #include <lib$routines.h> @@ -338,4 +387,5 @@ #include <lib$routines.h> +#include <libdef.h> #include <lnmdef.h> #include <ssdef.h> #include <starlet.h> @@ -372,6 +422,7 @@ int AppOutputCount, CgiPlusPythonEnd, + CodeMetricEnabled, Debug, GlobalDebug, HeadCvtGet, @@ -387,7 +438,16 @@ WsgiRunApp, WsgiForceBuffering; -unsigned long ExitStatus; +unsigned long ExitStatus, + ProcessPid, + RteJpiCpuTim, + RteJpiBufIO, + RteJpiDirIO, + RteJpiPageFlts, + RteJpiPpgCnt; + +unsigned long CurrentBinTime [2], + RteDeltaBinTime [2] = { -1, -1 }; char *CgiPlusEofPtr, *CgiPlusEotPtr, @@ -422,6 +482,53 @@ unsigned long ExitStatusPtr; } ExitHandler; +/*********************/ +/* global code cache */ +/*********************/ + +/* somewhat arbitrary */ +#define CACHE_MAX 32 + +int CodeCacheCount = 0, + CodeCacheMax = CACHE_MAX; + +/* number of requests instrumented */ +#define METRIC_MAX 10 + +/* size of instrumentation URI buffer */ +#define METRIC_PATH_MAX 48 + +struct CodeMetricStruct +{ + int MetricIndex; + unsigned long JpiBufIO[METRIC_MAX+1], + JpiCpuTim[METRIC_MAX+1], + JpiDirIO[METRIC_MAX+1], + JpiPageFlts[METRIC_MAX+1], + JpiPpgCnt[METRIC_MAX+1], + UsageNumber[METRIC_MAX+1]; + unsigned long DeltaBinTime[METRIC_MAX+1][2], + EndBinTime[METRIC_MAX+1][2], + StartBinTime[METRIC_MAX+1][2]; + char RequestPath [METRIC_MAX+1][METRIC_PATH_MAX]; +}; + +struct CodeCacheStruct +{ + int CodeUsageCount, + InterpreterUsageCount; + unsigned long LastUsedSecs, + ScriptMtimeSecs; + char *FileNamePtr, + *FileNameByteCodePtr; + PyThreadState *pInterpState; + PyCodeObject *pByteCode; + struct CodeMetricStruct *CodeMetricPtr; +} +CodeCache [CACHE_MAX]; + +struct CodeCacheStruct *CodeCachePtr; + /****************************/ /* python extension methods */ /****************************/ @@ -565,33 +672,8 @@ { NULL, NULL, 0, NULL } }; -/*********************/ -/* global code cache */ -/*********************/ - -/* somewhat arbitrary */ -#define CACHE_MAX 32 - -int CodeCacheCount = 0, - CodeCacheMax = CACHE_MAX; - -struct CodeCacheStruct -{ - int CodeUsageCount, - InterpreterUsageCount; - unsigned long LastUsedSecs, - ScriptMtimeSecs; - char *FileNamePtr, - *FileNameByteCodePtr; - PyThreadState *pInterpState; - PyCodeObject *pByteCode; -} -CodeCache [CACHE_MAX]; - -struct CodeCacheStruct *CodeCachePtr; - /**************/ /* prototypes */ /**************/ void at_Exit (); @@ -593,8 +675,9 @@ /**************/ /* prototypes */ /**************/ void at_Exit (); +void AddMetrics (struct CodeCacheStruct*); int ByteCodeUpToDate (struct CodeCacheStruct*); char* CgiVar (char*); char* CgiVarDclSymbol (char*); @@ -598,5 +681,6 @@ int ByteCodeUpToDate (struct CodeCacheStruct*); char* CgiVar (char*); char* CgiVarDclSymbol (char*); +void CollectMetrics (struct CodeCacheStruct*, int); int EmptyScriptName (char*, char*); void EnsureExit (unsigned long*); @@ -601,3 +685,4 @@ int EmptyScriptName (char*, char*); void EnsureExit (unsigned long*); +void FlushCacheEntry (struct CodeCacheStruct*); int lib$stop (__unknown_params); @@ -603,2 +688,3 @@ int lib$stop (__unknown_params); +int LibVmReport (void*); void LoadByteCode (char*, char*, struct CodeCacheStruct*); @@ -604,2 +690,3 @@ void LoadByteCode (char*, char*, struct CodeCacheStruct*); +int MetricsReport (); int ModuleCgiibLoaded (); @@ -605,5 +692,6 @@ int ModuleCgiibLoaded (); +void ProcessCLI (char*); void ProcessCGI (int, char**); void ProcessCGIplus (int, char**); void ProcessBasicRte (); void ProcessCachingRte (); @@ -606,7 +694,8 @@ void ProcessCGI (int, char**); void ProcessCGIplus (int, char**); void ProcessBasicRte (); void ProcessCachingRte (); +int ProctorDetect (); void ReportError (int, int, int, char*, ...); void RteScriptParam (); int SetupOsEnvCgiVar (int); @@ -628,6 +717,18 @@ main (int argc, char *argv[]) { + static struct + { + unsigned short buf_len; + unsigned short item; + void *buf_addr; + void *ret_len; + } JpiItems [] = + { + { sizeof(ProcessPid), JPI$_PID, &ProcessPid, 0 }, + { 0,0,0,0 } + }; + int status; char *cptr, *sptr; @@ -635,6 +736,5 @@ /* begin */ /*********/ - if (argc == 2 && (!strcmp(argv[1], "/version") || - !strcmp(argv[1], "/VERSION"))) + if (argc == 2) { @@ -640,6 +740,14 @@ { - fprintf (stdout, "%%PYRTE-I-VERSION, %s (%s)\n%s\n%s", - SOFTWAREID, BUILD_DATETIME, SOFTWARECR, SOFTWAREGPL); - exit (SS$_NORMAL); + if (!strncasecmp(argv[1], "/cli=", 5)) + { + ProcessCLI (argv[1]+5); + exit (SS$_NORMAL); + } + if (!strcasecmp(argv[1], "/version")) + { + fprintf (stdout, "%%PYRTE-I-VERSION, %s (%s)\n%s\n%s", + SOFTWAREID, BUILD_DATETIME, SOFTWARECR, SOFTWAREGPL); + exit (SS$_NORMAL); + } } @@ -644,5 +752,8 @@ } + status = sys$getjpiw (0, 0, 0, &JpiItems, 0, 0, 0); + if (!(status & 1)) exit (status); + /* this is almost a 'bit too clever' :-) */ if ((cptr = TrnLnm ("PYRTE$DBUG", NULL)) != NULL) if (strchr (cptr, '*')) @@ -747,6 +858,45 @@ /*****************************************************************************/ /* +Process from the command-line. +For base-line comparison purposes only. +*/ + +void ProcessCLI (char *ScriptName) + +{ + int status; + char *cptr, *sptr; + FILE *fp; + + /*********/ + /* begin */ + /*********/ + + if (Debug) fprintf (stdout, "ProcessCLI()\n"); + + Py_Initialize(); + pWasdModule = Py_InitModule ("wasd", RtePyMethods); + + Py_SetProgramName (ScriptName); + + fp = fopen (ScriptName, "r"); + if (fp) + { + PyRun_AnyFileExFlags (fp, ScriptName, 0, NULL); + fclose (fp); + } + else + { + status = vaxc$errno; + ReportError (__LINE__, status, 0, "Error opening !AZ", ScriptName); + } + + Py_Finalize(); +} + +/*****************************************************************************/ +/* Process with a full, standard CGI request cycle. For base-line comparison purposes only. */ @@ -825,7 +975,7 @@ /* set the global pointer used by some functions */ CodeCachePtr = ccptr; - /* it will have bee used the once! */ + /* it will have been used the once! */ ccptr->InterpreterUsageCount++; Py_SetProgramName (argv[1]); @@ -897,8 +1047,7 @@ if (pError = PyErr_Occurred()) { ReportError (__LINE__, 0, 1, NULL); - free (ccptr->FileNamePtr); - ccptr->FileNamePtr = NULL; + FlushCacheEntry (ccptr); } DEBUGPYOBJ("PyEval_EvalCode",pResult) Py_XDECREF (pResult); @@ -983,6 +1132,7 @@ fclose (fp); } else + if (!ProctorDetect ()) { status = vaxc$errno; ReportError (__LINE__, status, 0, "Error opening !AZ", sptr); @@ -1065,8 +1215,8 @@ /* block waiting for the first/next request */ CgiVar (""); - UsageCount++; - time (&CurrentTimeSecs); + sys$gettim (&CurrentBinTime); + CurrentTimeSecs = decc$fix_time (&CurrentBinTime); if (!(Debug = GlobalDebug)) if (cptr = TrnLnm ("PYRTE$DBUG", NULL)) @@ -1079,6 +1229,11 @@ fflush (stdout); } + if (TrnLnm ("PYRTE_METRICS", NULL)) + CodeMetricEnabled = 1; + else + CodeMetricEnabled = 0; + if (TrnLnm ("PYRTE_NOREUSE_INTERPRETER", NULL)) ReuseInterpreter = 0; else @@ -1101,4 +1256,16 @@ break; } + sptr = CgiVar ("QUERY_STRING"); + if (*sptr == '$' && + !strncmp (sptr, "$metrics$", 9) && + MetricsReport()) continue; + + /* delay to here so that metrics report is excluded */ + UsageCount++; + + /*******************/ + /* byte-code cache */ + /*******************/ + /* look to see if we have the script byte-code cached */ @@ -1104,4 +1271,3 @@ /* look to see if we have the script byte-code cached */ - ccptr = &CodeCache[0]; for (idx = 0; idx < CodeCacheCount; idx++) { @@ -1106,6 +1272,7 @@ for (idx = 0; idx < CodeCacheCount; idx++) { + ccptr = &CodeCache[idx]; if (!ccptr->FileNamePtr) continue; if (Debug) fprintf (stdout, "%d %d |%s|%s|\n", idx, ccptr->InterpreterUsageCount, cptr, ccptr->FileNamePtr); @@ -1108,8 +1275,8 @@ if (!ccptr->FileNamePtr) continue; if (Debug) fprintf (stdout, "%d %d |%s|%s|\n", idx, ccptr->InterpreterUsageCount, cptr, ccptr->FileNamePtr); - if (!strcmp (cptr, ccptr->FileNamePtr)) break; + if (!strcasecmp (cptr, ccptr->FileNamePtr)) break; ccptr++; } @@ -1122,8 +1289,7 @@ if (!ByteCodeUpToDate (ccptr)) { /* apparently not */ - free (ccptr->FileNamePtr); - ccptr->FileNamePtr = NULL; + FlushCacheEntry (ccptr); } } else @@ -1152,11 +1318,10 @@ if (CodeCache[idx].LastUsedSecs < CodeCache[tidx].LastUsedSecs) tidx = idx; ccptr = &CodeCache[tidx]; - free (ccptr->FileNamePtr); - ccptr->FileNamePtr = NULL; + FlushCacheEntry (ccptr); } } else { /* code caching is disabled */ ccptr = &CodeCache[0]; @@ -1157,14 +1322,10 @@ } } else { /* code caching is disabled */ ccptr = &CodeCache[0]; - if (ccptr->FileNamePtr) - { - free (ccptr->FileNamePtr); - ccptr->FileNamePtr = NULL; - } + if (ccptr->FileNamePtr) FlushCacheEntry (ccptr); } /* set the global pointer used by some functions */ @@ -1192,4 +1353,5 @@ ReportError (__LINE__, 0, 0, "Error getting SCRIPT_NAME."); break; } + LoadByteCode (cptr, sptr, ccptr); @@ -1195,3 +1357,5 @@ LoadByteCode (cptr, sptr, ccptr); + + AddMetrics (ccptr); } @@ -1196,5 +1360,7 @@ } + CollectMetrics (ccptr, 0); + if (ccptr->pByteCode) { /*********************/ @@ -1271,8 +1437,7 @@ if (pError = PyErr_Occurred()) { ReportError (__LINE__, 0, 1, NULL); - free (ccptr->FileNamePtr); - ccptr->FileNamePtr = NULL; + FlushCacheEntry (ccptr); } DEBUGPYOBJ("PyEval_EvalCode",pResult) @@ -1288,8 +1453,7 @@ else { /* no code - no cache! */ - free (ccptr->FileNamePtr); - ccptr->FileNamePtr = NULL; + FlushCacheEntry (ccptr); } /******************/ @@ -1297,6 +1461,10 @@ /******************/ fflush (stdout); + + /* after the flush to accumulate any outstanding BIO */ + CollectMetrics (ccptr, 1); + fputs (CgiPlusEofPtr, stdout); fflush (stdout); @@ -1322,6 +1490,38 @@ /*****************************************************************************/ /* +Free dynamic components and NULL related pointers. +*/ + +void FlushCacheEntry (struct CodeCacheStruct *ccptr) + +{ + + /*********/ + /* begin */ + /*********/ + + if (Debug) fprintf (stdout, "FlushCacheEntry()\n"); + + if (ccptr->FileNamePtr) + { + free (ccptr->FileNamePtr); + ccptr->FileNamePtr = NULL; + } + if (ccptr->FileNameByteCodePtr) + { + free (ccptr->FileNameByteCodePtr); + ccptr->FileNameByteCodePtr = NULL; + } + if (ccptr->CodeMetricPtr) + { + free (ccptr->CodeMetricPtr); + ccptr->CodeMetricPtr = NULL; + } +} + +/*****************************************************************************/ +/* Create the script byte-code by either loading the source code (.PY) and compiling, or loading the pre-compiled byte code (.PYC) or optimised, pre-compiled byte-code (.PYO). Reports it's own errors. Success is indicated @@ -1345,6 +1545,7 @@ FILE *fp; stat_t FstatBuffer; struct _node *pNode; + struct CodeMetricStruct *cmptr; /*********/ /* begin */ @@ -1361,16 +1562,7 @@ ccptr->pByteCode = NULL; } - if (ccptr->FileNamePtr) - { - free (ccptr->FileNamePtr); - ccptr->FileNamePtr = NULL; - } - if (ccptr->FileNameByteCodePtr) - { - free (ccptr->FileNameByteCodePtr); - ccptr->FileNameByteCodePtr = NULL; - } + FlushCacheEntry (ccptr); zptr = (sptr = FileNameByteCode) + sizeof(FileNameByteCode); for (cptr = FileName; *cptr && sptr < zptr; *sptr++ = *cptr++); @@ -1406,6 +1598,7 @@ fp = fopen (FileName, "r", "ctx=rec", "shr=get"); if (!fp) { + if (ProctorDetect ()) return; status = vaxc$errno; ReportError (__LINE__, status, 0, "Error opening !AZ", ScriptName); return; @@ -2561,6 +2754,45 @@ /*****************************************************************************/ /* +Self-contained functionality. Designed to be called if the script is not +found. If found the script needs to instantiate whatever resources it is +proctored for and then return an HTTP 204 to the server. If not found then +call this function and if this is the first call then check if there is a +REQUEST_METHOD. If there is then return false. If not assume WASD is +proactively starting the RTE. Then respond with an HTTP 204 and return true. +If the calling routine receives a false then it continues processing, if a true +then it concludes and waits for the next request. +*/ + +int ProctorDetect () + +{ + static int DetectCount; + + char *cptr; + + /*********/ + /* begin */ + /*********/ + + if (Debug) fprintf (stdout, "ProctorDetect()\n"); + + if (DetectCount++) return (0); + + /* if this CGI variable does not exist then probably not scripting */ + cptr = CgiVar ("SERVER_SOFTWARE"); + if (!(cptr && *cptr)) return (0); + + cptr = CgiVar ("REQUEST_METHOD"); + if (cptr && *cptr) return (0); + + fputs ("Status: 204 RTE Proctor Response\r\n\r\n", stdout); + + return (1); +} + +/*****************************************************************************/ +/* Make it look a bit like the standard WASD error message. */ @@ -3050,6 +3282,471 @@ /*****************************************************************************/ /* +Return 1 if the report was enabled, 0 if not enabled. +Displays cache entry statistics. +*/ + +char* my_ctime_r (unsigned long *tsecs, char *buf) +{ + ctime_r (tsecs,buf); + buf[19] = '\0'; + return (buf); +} + +int MetricsReport () + +{ +#define LIB$K_DELTA_SECONDS_F 30 + + static unsigned long LibDeltaSecs = LIB$K_DELTA_SECONDS_F; + + int bcsize, cnt, idx, midx, status; + unsigned long ffloat, tsecs; + double tfloat; + char lubuf [32], + mtbuf [32], + pyver [32]; + struct CodeCacheStruct *ccptr; + struct CodeMetricStruct *cmptr; + PyObject *pFunc, + *pValue, + *pPlatMod; + + /*********/ + /* begin */ + /*********/ + + if (Debug) fprintf (stdout, "MetricsReport()\n"); + + if (!CodeMetricEnabled) return (0); + + pPlatMod = PyImport_ImportModule ("platform"); + if (!pPlatMod) return (0); + + pFunc = PyObject_GetAttrString (pPlatMod, "python_version"); + if (!pFunc) return (0); + + pValue = PyObject_CallFunction (pFunc, NULL); + if (!pValue) return (0); + strcpy (pyver, PyString_AsString (pValue)); + + Py_DECREF(pPlatMod); + Py_DECREF(pFunc); + Py_DECREF(pValue); + + fprintf (stdout, +"Status: 200\n\ +Content-Type: text/html\n\ +\n\ +<HTML>\n\ +<HEAD>\n\ +<TITLE>Metrics Report - %s (%s)</TITLE>\n\ +</HEAD>\n\ +<STYLE TYPE=\"text/css\">\n\ +th { margin:0; padding:0 0 2px 1em; white-space:nowrap; \ +text-decoration:underline; }\n\ +td { margin:0; padding:0 0 2px 1em; white-space:nowrap; }\n\ +pre { font-size:80%%; line-height:90%%; }\n\ +</STYLE>\n\ +<BODY>\n\ +<FONT SIZE=\"+1\"><B>Metrics Report - %s \ + - Python %s</B></FONT>\n\ +<P>PID <TT>%08.08X</TT> Usage Count: %u\n\ +<P>%d Item(s)\n", + SOFTWAREID, BUILD_DATETIME, + SOFTWAREID, pyver, + ProcessPid, UsageCount, + CodeCacheCount); + + if (CodeCacheCount) + { + fputs ( +"<P><TABLE BORDER=\"0\">\n\ +<TR>\ +<TH></TH>\n\ +<TH ALIGN=\"right\">Usage</TH>\ +<TH ALIGN=\"right\">Size</TH>\ +<TH ALIGN=\"right\">Number</TH>\ +<TH ALIGN=\"left\">Last Used</TH>\ +<TH ALIGN=\"right\">Duration</TH>\ +<TH ALIGN=\"right\">CPU</TH>\ +<TH ALIGN=\"right\">BIO</TH>\ +<TH ALIGN=\"right\">DIO</TH>\ +<TH ALIGN=\"right\">FLT</TH>\ +<TH ALIGN=\"right\">PGS</TH>\ +<TH ALIGN=\"left\">Modified</TH>\ +<TH ALIGN=\"left\">Script File</TH>\ +<TH ALIGN=\"left\">Byte-Code File</TH>\ +</TR>\n", + stdout); + + /* lib$cvts..() is not available before VMS V8.2 so work around */ + status = lib$cvtf_from_internal_time (&LibDeltaSecs, &ffloat, + &RteDeltaBinTime); + if (!(status & 1)) exit (status); + status = cvt$convert_float (&ffloat, CVT$K_VAX_F, + &tfloat, CVT$K_IEEE_T, + CVT$M_ROUND_TO_NEAREST); + if (!(status & 1)) exit (status); + + fprintf (stdout, +"<TR><TD COLSPAN=\"4\"></TD>\ +<TD ALIGN=\"right\"><I>RTE cummulative</I></TD>\ +<TD ALIGN=\"right\">%.4f</TD>\ +<TD ALIGN=\"right\">%.4f</TD>\ +<TD ALIGN=\"right\">%u</TD>\ +<TD ALIGN=\"right\">%u</TD>\ +<TD ALIGN=\"right\">%u</TD>\ +<TD ALIGN=\"right\">%u</TD></TR>\n", + tfloat, + (float)(RteJpiCpuTim)/100.0, + RteJpiBufIO, + RteJpiDirIO, + RteJpiPageFlts, + RteJpiPpgCnt); + } + else + fputs ("<P>", stdout); + + for (idx = 0; idx < CodeCacheCount; idx++) + { + if (!CodeCache[idx].FileNamePtr) continue; + /* possible if metric reporting is enabled on an existing RTE */ + if (!CodeCache[idx].CodeMetricPtr) continue; + + ccptr = &CodeCache[idx]; + cmptr = ccptr->CodeMetricPtr; + + status = lib$cvtf_from_internal_time (&LibDeltaSecs, &ffloat, + &cmptr->DeltaBinTime[0]); + if (!(status & 1)) exit (status); + status = cvt$convert_float (&ffloat, CVT$K_VAX_F, + &tfloat, CVT$K_IEEE_T, + CVT$M_ROUND_TO_NEAREST); + if (!(status & 1)) exit (status); + + tsecs = decc$fix_time(&cmptr->StartBinTime[0]); + + pValue = PyObject_GetAttrString ((PyObject*)ccptr->pByteCode, "co_code"); + if (pValue) + bcsize = PyObject_Size (pValue); + else + bcsize = -1; + + fprintf (stdout, +"<TR><TD ALIGN=\"right\">%d.</TD>\ +<TD ALIGN=\"right\">%d</TD>\ +<TD ALIGN=\"right\">%d</TD>\ +<TD ALIGN=\"right\"></TD>\ +<TD ALIGN=\"right\"><I>cummulative</I></TD>\ +<TD ALIGN=\"right\">%.4f</TD>\ +<TD ALIGN=\"right\">%.4f</TD>\ +<TD ALIGN=\"right\">%u</TD>\ +<TD ALIGN=\"right\">%u</TD>\ +<TD ALIGN=\"right\">%u</TD>\ +<TD ALIGN=\"right\">%u</TD>\ +<TD>%s</TD>\ +<TD><TT>%s</TT></TD>\ +<TD><TT>%s</TT></TD>\ +</TR>\n", + idx + 1, + ccptr->CodeUsageCount, + bcsize, + tfloat, + (float)(cmptr->JpiCpuTim[0])/100.0, + cmptr->JpiBufIO[0], + cmptr->JpiDirIO[0], + cmptr->JpiPageFlts[0], + cmptr->JpiPpgCnt[0], + my_ctime_r(&ccptr->ScriptMtimeSecs,mtbuf), + ccptr->FileNamePtr, + ccptr->FileNameByteCodePtr ? ccptr->FileNameByteCodePtr : ""); + + midx = cmptr->MetricIndex; + + for (cnt = 1; cnt <= METRIC_MAX; cnt++) + { + if (cmptr->StartBinTime[midx][0] == 0 && + cmptr->StartBinTime[midx][1] == 0) break; + + status = lib$cvtf_from_internal_time (&LibDeltaSecs, &ffloat, + &cmptr->DeltaBinTime[midx]); + if (!(status & 1)) exit (status); + status = cvt$convert_float (&ffloat, CVT$K_VAX_F, + &tfloat, CVT$K_IEEE_T, + CVT$M_ROUND_TO_NEAREST); + if (!(status & 1)) exit (status); + + tsecs = decc$fix_time(&cmptr->StartBinTime[midx]); + + fprintf (stdout, +"<TR><TD COLSPAN=\"3\"></TD>\ +<TD ALIGN=\"right\">%u</TD>\ +<TD>%s</TD>\ +<TD ALIGN=\"right\">%.4f</TD>\ +<TD ALIGN=\"right\">%.4f</TD>\ +<TD ALIGN=\"right\">%u</TD>\ +<TD ALIGN=\"right\">%u</TD>\ +<TD ALIGN=\"right\">%u</TD>\ +<TD ALIGN=\"right\">%u</TD>\ +<TD COLSPAN=\"3\"><TT>%s</TT>%s</TD></TR>\n", + cmptr->UsageNumber[midx], + my_ctime_r(&tsecs,lubuf), + tfloat, + (float)(cmptr->JpiCpuTim[midx])/100.0, + cmptr->JpiBufIO[midx], + cmptr->JpiDirIO[midx], + cmptr->JpiPageFlts[midx], + cmptr->JpiPpgCnt[midx], + cmptr->RequestPath[midx], + strlen(cmptr->RequestPath[midx]) == + METRIC_PATH_MAX-1 ? "..." : ""); + + /* zero is the 'cummulative' index */ + if (midx > 1) + midx--; + else + midx = METRIC_MAX; + } + +/*** + if (pValue) + { + fputs("<TR><TD COLSPAN=\"3\"></TD><TD COLSPAN=\"5\"><TT>", stdout); + PyObject_Print ((PyObject*)CodeCache[idx].pByteCode, stdout, 0); + fputs("</TT></TD></TR>\n", stdout); + + fputs("<TR><TD COLSPAN=\"3\"></TD><TD COLSPAN=\"5\"><TT>", stdout); + PyObject_Print (pValue, stdout, 0); + fputs("</TT></TD></TR>\n", stdout); + + Py_DECREF(pValue); + } + else + { + } +***/ + } + + fputs ("</TABLE>\n<PRE>", stdout); + +/*** + LibVmReport (NULL); +***/ + + fputs ("</PRE>\n\ +<P><HR WIDTH=85%% ALIGN=left SIZE=2 NOSHADE>\n\ +</BODY>\n\ +</HTML>\n", stdout); + + fflush (stdout); + fputs (CgiPlusEofPtr, stdout); + fflush (stdout); + + return (1); +} + +/*****************************************************************************/ +/* +Add a metrics structure and initialise required elements. +*/ + +void AddMetrics (struct CodeCacheStruct *ccptr) + +{ + int midx; + struct CodeMetricStruct *cmptr; + + /*********/ + /* begin */ + /*********/ + + if (Debug) fprintf (stdout, "AddMetrics()\n"); + + if (!CodeMetricEnabled) return; + if (ccptr->CodeMetricPtr) return; + + ccptr->CodeMetricPtr = cmptr = calloc (1, sizeof(struct CodeMetricStruct)); + if (!ccptr->CodeMetricPtr) exit (vaxc$errno); + + /* set these to delta "zero" (actually the smallest delta time) */ + for (midx = 0; midx <= METRIC_MAX; midx++) + cmptr->DeltaBinTime[midx][0] = cmptr->DeltaBinTime[midx][1] = -1; +} + +/*****************************************************************************/ +/* +Accumulate the measurements. +*/ + +void CollectMetrics +( +struct CodeCacheStruct *ccptr, +int AtEnd +) +{ + static unsigned long JpiBufIO, + JpiCpuTim, + JpiDirIO, + JpiPageFlts, + JpiPpgCnt; + static struct + { + unsigned short buf_len; + unsigned short item; + void *buf_addr; + void *ret_len; + } JpiItems [] = + { + { sizeof(JpiBufIO), JPI$_BUFIO, &JpiBufIO, 0 }, + { sizeof(JpiCpuTim), JPI$_CPUTIM, &JpiCpuTim, 0 }, + { sizeof(JpiDirIO), JPI$_DIRIO, &JpiDirIO, 0 }, + { sizeof(JpiPageFlts), JPI$_PAGEFLTS, &JpiPageFlts, 0 }, + { sizeof(JpiPpgCnt), JPI$_PPGCNT, &JpiPpgCnt, 0 }, + { 0,0,0,0 } + }; + + int midx, status; + char *cptr, *sptr, *zptr; + struct CodeMetricStruct *cmptr; + + /*********/ + /* begin */ + /*********/ + + if (Debug) fprintf (stdout, "CollectMetrics()\n"); + + if (!CodeMetricEnabled) return; + /* possible if metric reporting is enabled on an existing RTE */ + if (!(cmptr = ccptr->CodeMetricPtr)) return; + + status = sys$getjpiw (0, 0, 0, &JpiItems, 0, 0, 0); + if (!(status & 1)) exit (status); + + if (AtEnd) + { + midx = cmptr->MetricIndex; + + sys$gettim (&cmptr->EndBinTime[midx]); + lib$sub_times (&cmptr->EndBinTime[midx], + &cmptr->StartBinTime[midx], + &cmptr->DeltaBinTime[midx]); + /* accumulate the durations */ + lib$add_times (&cmptr->DeltaBinTime[midx], + &cmptr->DeltaBinTime[0], + &cmptr->DeltaBinTime[0]); + lib$add_times (&cmptr->DeltaBinTime[midx], + &RteDeltaBinTime, + &RteDeltaBinTime); + + cmptr->JpiBufIO[midx] = JpiBufIO - cmptr->JpiBufIO[midx]; + cmptr->JpiBufIO[0] += cmptr->JpiBufIO[midx]; + RteJpiBufIO += cmptr->JpiBufIO[midx]; + + cmptr->JpiCpuTim[midx] = JpiCpuTim - cmptr->JpiCpuTim[midx]; + cmptr->JpiCpuTim[0] += cmptr->JpiCpuTim[midx]; + RteJpiCpuTim += cmptr->JpiCpuTim[midx]; + + cmptr->JpiDirIO[midx] = JpiDirIO - cmptr->JpiDirIO[midx]; + cmptr->JpiDirIO[0] += cmptr->JpiDirIO[midx]; + RteJpiDirIO += cmptr->JpiDirIO[midx]; + + cmptr->JpiPageFlts[midx] = JpiPageFlts - cmptr->JpiPageFlts[midx]; + cmptr->JpiPageFlts[0] += cmptr->JpiPageFlts[midx]; + RteJpiPageFlts += cmptr->JpiPageFlts[midx]; + + cmptr->JpiPpgCnt[midx] = JpiPpgCnt; + if (cmptr->JpiPpgCnt[0] < JpiPpgCnt) cmptr->JpiPpgCnt[0] = JpiPpgCnt; + if (RteJpiPpgCnt < JpiPpgCnt) RteJpiPpgCnt = JpiPpgCnt; + } + else + { + if (cmptr->MetricIndex < METRIC_MAX) + cmptr->MetricIndex++; + else + cmptr->MetricIndex = 1; + midx = cmptr->MetricIndex; + + /* includes the byte-cache search, etc., and so is a little more true */ + cmptr->StartBinTime[midx][0] = CurrentBinTime[0]; + cmptr->StartBinTime[midx][1] = CurrentBinTime[1]; + + cmptr->JpiBufIO[midx] = JpiBufIO; + cmptr->JpiCpuTim[midx] = JpiCpuTim; + cmptr->JpiDirIO[midx] = JpiDirIO; + cmptr->JpiPageFlts[midx] = JpiPageFlts; + cmptr->UsageNumber[midx] = UsageCount; + + zptr = (sptr = cmptr->RequestPath[midx]) + + sizeof(cmptr->RequestPath[midx])-1; + for (cptr = CgiVar ("SCRIPT_NAME"); + sptr < zptr; + *sptr++ = *cptr++); + *sptr = '\0'; + } +} + +/*****************************************************************************/ +/* +Show (debug) the virtual memory zone. +(Just not interested in the user-arg parameter.) +Casting from (void*) to avoid compiler message %CC-I-PROTOSCOPE. +*/ + +int LibVmReport (void *StrDscPtr) + +{ +#define LIB$_NOTFOU 1409652 + + static unsigned long DetailLevel = 1; + static unsigned long DetailLevel64 [2] = { 1, 0 }; + + int status; + unsigned long Context, + ZoneId; + unsigned long Context64 [2], + ZoneId64; + struct dsc$descriptor *DscPtr; + + /*********/ + /* begin */ + /*********/ + + if (Debug) fprintf (stdout, "LibVmReport()\n"); + + if (StrDscPtr) + { + /* action-routine */ + DscPtr = (struct dsc$descriptor*)StrDscPtr; + fprintf (stdout, "%*.*s\n", + DscPtr->dsc$w_length, DscPtr->dsc$w_length, + DscPtr->dsc$a_pointer); + return (SS$_NORMAL); + } + + Context = 0; + while ((status = lib$find_vm_zone (&Context, &ZoneId)) == SS$_NORMAL) + { + status = lib$show_vm_zone (&ZoneId, &DetailLevel, &LibVmReport, 0); + if (!(status & 1)) break; + } + fflush (stdout); + if (status != LIB$_NOTFOU) return (status); + + Context64[0] = Context64[1] = 0; + while ((status = lib$find_vm_zone_64 (&Context64, &ZoneId64)) == SS$_NORMAL) + { + status = lib$show_vm_zone_64 (&ZoneId64, &DetailLevel64, &LibVmReport, 0); + if (!(status & 1)) break; + } + fflush (stdout); + if (status != LIB$_NOTFOU) return (status); + + return (SS$_NORMAL); +} + +/*****************************************************************************/ +/* If false then initialize the LIB$ stats timer. If true return a pointer to a static buffer containings a stats string. */ diff --git a/src/python/readme.txt b/src/python/readme.txt index 4751697f3268ba738013ea88b5ab829ced45ce18_c3JjL3B5dGhvbi9yZWFkbWUudHh0..46790834bbe052e9409a894eeac68ad640ab5006_c3JjL3B5dGhvbi9yZWFkbWUudHh0 100755 --- a/src/python/readme.txt +++ b/src/python/readme.txt @@ -4,7 +4,7 @@ PYRTE COPYRIGHT --------------- -Copyright (C) 2007-2010 Mark G.Daniel +Copyright (C) 2007-2011 Mark G.Daniel This package 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 3, or any later version. diff --git a/src/python/readmore.html b/src/python/readmore.html index 4751697f3268ba738013ea88b5ab829ced45ce18_c3JjL3B5dGhvbi9yZWFkbW9yZS5odG1s..46790834bbe052e9409a894eeac68ad640ab5006_c3JjL3B5dGhvbi9yZWFkbW9yZS5odG1s 100755 --- a/src/python/readmore.html +++ b/src/python/readmore.html @@ -60,5 +60,5 @@ <CENTER> <H1>Python Run-Time Environment</H1> -<H3>Version 1.1.7, 4th June 2010</H3> +<H3>Version 1.1.10, 5th November 2011</H3> @@ -64,5 +64,5 @@ -<P><B>Copyright © 2007-2010 Mark G. Daniel</B> +<P><B>Copyright © 2007-2011 Mark G. Daniel</B> <BR>This program, comes with ABSOLUTELY NO WARRANTY. <BR>This is free software, and you are welcome to redistribute it under the <BR>conditions of the GNU GENERAL PUBLIC LICENSE, version 3, or any later version. @@ -99,9 +99,10 @@ Alpha (AXP) and Itanium object modules are provided for the VMS platforms available for Python. -<P> PyRTE has been developed and tested on VMS V8.3 using the WASD v9.2.0 and -VMS Python kit JFP AXPVMS PYTHON251 available from and documented at <A -HREF="http://vmspython.dyndns.org/">http://vmspython.dyndns.org/</A>. JFP's +<P> PyRTE has been developed and tested on VMS V8.3 and V8.4, Alpha and +Itanium, using WASD v9.<I>n</I> and v10.<I>n</I> and VMS Python kit JFP +AXPVMS PYTHON272 available from and documented at +<A HREF="http://www.vmspython.org/">http://www.vmspython.org/</A>. JFP's Python kits are kept very up-to-date as Python updates or implementation problems arise. @@ -115,7 +116,7 @@ up-to-date, obtain the latest kit from <BLOCKQUOTE> -<A HREF="http://vmspython.dyndns.org/">http://vmspython.dyndns.org/</A> +<A HREF="http://www.vmspython.org/">http://www.vmspython.org/</A> </BLOCKQUOTE> and install according to the instructions available. @@ -267,7 +268,8 @@ <UL> <LI> <A HREF="/py-bin/pyrte_test1.py">/py-bin/pyrte_test1.py</A> <LI> <A HREF="/py-bin/pyrte_test2.py">/py-bin/pyrte_test2.py</A> -<LI> <A HREF="/py-bin/pyrte_test3.py">/py-bin/pyrte_test3.py</A> +<LI> <A HREF="/py-bin/pyrte_test3.py">/py-bin/pyrte_test3.py</A> +<I>(this tests an error handler and therefore displays an error!)</I> <LI> <A HREF="/py-bin/pyrte_test4.py">/py-bin/pyrte_test4.py</A> <LI> <A HREF="/py-bin/pyrte_chart_pie.py">/py-bin/pyrte_chart_pie.py</A> </UL> @@ -385,8 +387,8 @@ <UL> <LI><A HREF="http://moinmoin.wikiwikiweb.de/">http://moinmoin.wikiwikiweb.de/</A> -<LI><A HREF="http://vmspython.dyndns.org/MoinMoin">http://vmspython.dyndns.org/MoinMoin</A> -<LI><A HREF="http://vmspython.dyndns.org/DownloadAndInstallationMoinMoin">http://vmspython.dyndns.org/DownloadAndInstallationMoinMoin</A> +<LI><A HREF="http://www.vmspython.org/MoinMoin">http://www.vmspython.org/MoinMoin</A> +<LI><A HREF="http://www.vmspython.org/DownloadAndInstallationMoinMoin">http://www.vmspython.org/DownloadAndInstallationMoinMoin</A> </UL> <P> MoinMoin is generally run independently to a system's primary Web server @@ -423,7 +425,7 @@ <UL> <LI> With the PyRTE kit ... Mark.Daniel@wasd.vsm.com.au -<LI> With the VMS Python kit ... jf.pieronne@laposte.net +<LI> With the VMS Python kit ... jf.pieronne@gmail.com <LI> The info-WASD mailing list </UL> @@ -432,9 +434,9 @@ There is the VMS Python Wiki <BLOCKQUOTE> -<A HREF="http://vmspython.dyndns.org/">http://vmspython.dyndns.org/</A> +<A HREF="http://www.vmspython.org/">http://www.vmspython.org/</A> </BLOCKQUOTE> <P> and support forum <BLOCKQUOTE> @@ -436,9 +438,9 @@ </BLOCKQUOTE> <P> and support forum <BLOCKQUOTE> -<A HREF="http://vmspython.dyndns.org/piforum/index.py">http://vmspython.dyndns.org/piforum/index.py</A> +<A HREF="http://www.vmspython.org/piforum/index.py">http://www.vmspython.org/piforum/index.py</A> </BLOCKQUOTE> <P> for VMS Python issues. If there's an obvious behavioural problem with