]> granicus.if.org Git - php/commitdiff
Multithreaded stress test program for isapi module now supports phpt files
authorShane Caraveo <shane@php.net>
Mon, 15 Jan 2001 00:29:49 +0000 (00:29 +0000)
committerShane Caraveo <shane@php.net>
Mon, 15 Jan 2001 00:29:49 +0000 (00:29 +0000)
still stuff to do before it's realy done, but does run the tests, just need
to get it to compare results right now.

sapi/isapi/stresstest/notes.txt
sapi/isapi/stresstest/stresstest.cpp
sapi/isapi/stresstest/stresstest.dsp

index e6388a060fd93d62fdabdcac30a14c212c0e0c6d..e2ee5c2161f547c87db01f0b30b8170655988a39 100644 (file)
@@ -1,10 +1,12 @@
 This stress test program is for debugging threading issues with the isapi module.
 
-Create a file that contains a list of php script files, one per line.  If you need to provide input, you can create an input file for each script file.  File contents would look like:
+2 ways to use it.
+
+1: test any php script file on multiple threads
+2: run the php test scripts bundled with the source code
 
-e:\inetpub\pages\index.php
-e:\inetpub\pages\info.php
-e:\inetpub\pages\posttest.php e:\inetpub\pages\postdata.txt
+
+GLOBAL SETTINGS
 
 If you need to set special environement variables, in addition to your regular environment, create a file that contains them, one setting per line:
 
@@ -14,5 +16,34 @@ This can be used to simulate isapi environment variables if need be.
 
 By default, stress test uses 10 threads.  To change this, change the define NUM_THREADS in stresstest.cpp.
 
-This test program is a work in progress, no garauntees on it working right.
+1: test any php script file on multiple threads
+
+Create a file that contains a list of php script files, one per line.  If you need to provide input, place the GET data, or Query String, after the filename.  File contents would look like:
+
+e:\inetpub\pages\index.php
+e:\inetpub\pages\info.php
+e:\inetpub\pages\test.php a=1&b=2
+
+run: stresstest L files.txt
+
+2: run the php test scripts bundled with the source code
+
+supply the path to the parent of the "tests" directory
+(expect a couple long pauses for a couple of the larger tests)
+
+run: stresstest T c:\php4-source
+
+
+
+TODO:
+
+Make an apropriate test of the output against the EXPECT data for the test files, right now it simply doesn't work since I'm burnt (tired that is).
+
+Make more options configurable: number of threads, itterations, etc.
+
+Improve stdout output to make it more usefull
+
+Log totals for each test.
+
+Implement support for SKIPIF
 
index 975f8d784f82f19b62fe67f3272e701c174535d4..625a3f28177218e8daf03ea3aba8fc6aebc4cbbb 100644 (file)
 //
 // The mandatory exports from the ISAPI DLL
 //
+#define NUM_THREADS 10
+#define ITERATIONS 1
+HANDLE terminate[NUM_THREADS];
+HANDLE StartNow;
+// quick and dirty environment
+typedef CMapStringToString TEnvironment;
+TEnvironment IsapiEnvironment;
+CStringArray IsapiFileList;  // list of filenames
+CStringArray TestNames;      // --TEST--
+CStringArray IsapiGetData;   // --GET--
+CStringArray IsapiPostData;  // --POST--
+CStringArray IsapiMatchData; // --EXPECT--
 
 typedef struct _TIsapiContext {
        HANDLE in;
        HANDLE out;
        DWORD tid;
+       TEnvironment env;
 } TIsapiContext;
 
 //
@@ -57,77 +70,52 @@ char * MakeDateStr(VOID);
 char * GetEnv(char *);
 
 
-#define NUM_THREADS 10
-#define ITERATIONS 1
-HANDLE terminate[NUM_THREADS];
-HANDLE StartNow;
-// quick and dirty environment
-CMapStringToString IsapiEnvironment;
-CStringArray IsapiFileList;
-CStringArray IsapiArgList;
 
 
 DWORD CALLBACK IsapiThread(void *);
-int stress_main(const char *filename, const char *arg);
-
-
+int stress_main(const char *filename, 
+                               const char *arg, 
+                               const char *postfile, 
+                               const char *matchdata,
+                               const char *testname);
 
-int main(int argc, char* argv[]) {
-       LPVOID lpMsgBuf;
-       char *filelist, *environment;
-
-       if (argc < 2) {
-               printf("Usage: stress filelist.txt env.txt\r\n");
-               return 0;
-       }
-       filelist = argv[1];
-       environment = argv[2];
-
-       hDll = LoadLibrary("php4isapi.dll"); // Load our DLL
 
-       if (!hDll) {
-               FormatMessage( 
-                  FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
-                   NULL,
-                   GetLastError(),
-                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
-                   (LPTSTR) &lpMsgBuf,
-                   0,
-                   NULL 
-               );
-               fprintf(stderr,"Error: Dll 'php4isapi.dll' not found -%d\n%s\n", GetLastError(),lpMsgBuf);
-               LocalFree( lpMsgBuf );
-               return -1;
-       }
 
-       //
-       // Find the exported functions
+BOOL test = FALSE;
+char temppath[MAX_PATH];
 
-       IsapiGetExtensionVersion = (VersionProc)GetProcAddress(hDll,"GetExtensionVersion");
-       if (!IsapiGetExtensionVersion) {
-               fprintf(stderr,"Can't Get Extension Version %d\n",GetLastError());
-               return -1;
-       }
-       IsapiHttpExtensionProc = (HttpExtProc)GetProcAddress(hDll,"HttpExtensionProc");
-       if (!IsapiHttpExtensionProc) {
-               fprintf(stderr,"Can't Get Extension proc %d\n",GetLastError());
-               return -1;
-       }
+void stripcrlf(char *line)
+{
+       DWORD l = strlen(line)-1;
+       if (line[l]==10 || line[l]==13) line[l]=0;
+       l = strlen(line)-1;
+       if (line[l]==10 || line[l]==13) line[l]=0;
+}
 
-       // This should really check if the version information matches what we
-       // expect.
-       //
-       __try {
-               if (!IsapiGetExtensionVersion(&version_info) ) {
-                       fprintf(stderr,"Fatal: GetExtensionVersion failed\n");
-                       return -1;
+BOOL ReadGlobalEnvironment(const char *environment)
+{
+       if (environment) {
+       FILE *fp = fopen(environment, "r");
+       DWORD i=0;
+       if (fp) {
+               char line[2048];
+               while (fgets(line,sizeof(line)-1,fp)) {
+                       // file.php arg1 arg2 etc.
+                       char *p = strchr(line, '=');
+                       if (p) {
+                               *p=0;
+                               IsapiEnvironment[line]=p+1;
+                       }
                }
+               fclose(fp);
+               return IsapiEnvironment.GetCount() > 0;
        }
-       __except(1) {
-               return -1;
        }
+       return FALSE;
+}
 
-       // read config files
+BOOL ReadFileList(const char *filelist)
+{
        FILE *fp = fopen(filelist, "r");
        if (!fp) {
                printf("Unable to open %s\r\n", filelist);
@@ -136,45 +124,45 @@ int main(int argc, char* argv[]) {
        int i=0;
        while (fgets(line,sizeof(line)-1,fp)) {
                // file.php arg1 arg2 etc.
+               stripcrlf(line);
                if (strlen(line)>3) {
                        char *p = strchr(line, ' ');
                        if (p) {
                                *p = 0;
                                // get file
+
                                IsapiFileList.Add(line);
-                               IsapiArgList.Add(p+1);
+                               IsapiGetData.Add(p+1);
                        } else {
                                // just a filename is all
                                IsapiFileList.Add(line);
-                               IsapiArgList.Add("");
+                               IsapiGetData.Add("");
                        }
                }
+
+               // future use
+               IsapiPostData.Add("");
+               IsapiMatchData.Add("");
+               TestNames.Add("");
+
                i++;
        }
        fclose(fp);
+       return IsapiFileList.GetSize() > 0;
+}
 
-       if (environment) {
-       fp = fopen(environment, "r");
-       i=0;
-       if (fp) {
-               char line[2048];
-               while (fgets(line,sizeof(line)-1,fp)) {
-                       // file.php arg1 arg2 etc.
-                       char *p = strchr(line, '=');
-                       if (p) {
-                               *p=0;
-                               IsapiEnvironment[line]=p+1;
-                       }
-               }
-               fclose(fp);
-       }
+void DoThreads() {
+
+       if (IsapiFileList.GetSize() == 0) {
+               printf("No Files to test\n");
+               return;
        }
 
        printf("Starting Threads...\n");
        // loop creating threads
        DWORD tid;
        HANDLE threads[NUM_THREADS];
-       for (i=0; i< NUM_THREADS; i++) {
+       for (DWORD i=0; i< NUM_THREADS; i++) {
                terminate[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
                if ((threads[i]=CreateThread(NULL, 0, IsapiThread, &terminate[i], CREATE_SUSPENDED, &tid))==NULL){
                        SetEvent(terminate[i]);
@@ -185,9 +173,226 @@ int main(int argc, char* argv[]) {
        }
        // wait for threads to finish
        WaitForMultipleObjects(NUM_THREADS, terminate, TRUE, INFINITE);
+}
+
+void DoFileList(const char *filelist, const char *environment)
+{
+       // read config files
+
+       if (!ReadFileList(filelist)) {
+               printf("No Files to test!\r\n");
+               return;
+       }
+
+       ReadGlobalEnvironment(environment);
+
+       DoThreads();
+}
+
+
+/**
+ * ParseTestFile
+ * parse a single phpt file and add it to the arrays
+ */
+BOOL ParseTestFile(const char *path, const char *fn)
+{
+       // parse the test file
+       char filename[MAX_PATH];
+       _snprintf(filename, sizeof(filename)-1, "%s\\%s", path, fn);
+       char line[1024];
+       memset(line, 0, sizeof(line));
+       CString cTest, cSkipIf, cPost, cGet, cFile, cExpect;
+       printf("Reading %s\r\n", filename);
+
+       enum state {none, test, skipif, post, get, file, expect} parsestate = none;
+
+       FILE *fp = fopen(filename, "r");
+       if (fp) {
+               while (fgets(line,sizeof(line)-1,fp)) {
+                       if (line[0]=='-') {
+                               if (_strnicmp(line, "--TEST--", 8)==0) {
+                                       parsestate = test;
+                                       continue;
+                               } else if (_strnicmp(line, "--SKIPIF--", 10)==0) {
+                                       parsestate = skipif;
+                                       continue;
+                               } else if (_strnicmp(line, "--POST--", 8)==0) {
+                                       parsestate = post;
+                                       continue;
+                               } else if (_strnicmp(line, "--GET--", 7)==0) {
+                                       parsestate = get;
+                                       continue;
+                               } else if (_strnicmp(line, "--FILE--", 8)==0) {
+                                       parsestate = file;
+                                       continue;
+                               } else if (_strnicmp(line, "--EXPECT--", 10)==0) {
+                                       parsestate = expect;
+                                       continue;
+                               }
+                       }
+                       switch (parsestate) {
+                       case test:
+                               stripcrlf(line);
+                               cTest = line;
+                               break;
+                       case skipif:
+                               cSkipIf += line;
+                               break;
+                       case post:
+                               cPost += line;
+                               break;
+                       case get:
+                               cGet += line;
+                               break;
+                       case file:
+                               cFile += line;
+                               break;
+                       case expect:
+                               cExpect += line;
+                               break;
+                       }
+               }               
+
+               fclose(fp);
+
+               if (!cTest.IsEmpty() && !cFile.IsEmpty() && !cExpect.IsEmpty()) {
+                       BOOL created = FALSE;
+                       char *fn = _tempnam(temppath,"pht.");
+                       char *en = _tempnam(temppath,"exp.");
+                       FILE *fp = fopen(fn, "w+");
+                       FILE *fe = fopen(en, "w+");
+                       if (fp && en) {
+                               fwrite(cFile, cFile.GetLength(), 1, fp);
+                               fwrite(cExpect, cExpect.GetLength(), 1, fe);
+                               IsapiFileList.Add(fn);
+                               TestNames.Add(cTest);
+                               IsapiGetData.Add(cGet);
+                               IsapiPostData.Add(cPost);
+                               IsapiMatchData.Add(en);
+                               created = TRUE;
+                       }
+                       if (fp) fclose(fp);
+                       if (fe) fclose(fe);
+
+                       return created;
+               }
+       }
+       return FALSE;
+}
+
+
+/**
+ * GetTestFiles
+ * Recurse through the path and subdirectories, parse each phpt file
+ */
+BOOL GetTestFiles(const char *path)
+{
+       // find all files .phpt under testpath\tests
+       char FindPath[MAX_PATH];
+       WIN32_FIND_DATA fd;
+       memset(&fd, 0, sizeof(WIN32_FIND_DATA));
+
+       _snprintf(FindPath, sizeof(FindPath)-1, "%s\\*.*",path);
+       HANDLE fh = FindFirstFile(FindPath, &fd);
+       if (fh != INVALID_HANDLE_VALUE) {
+               do {
+                       if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+                               !strchr(fd.cFileName, '.')) {
+                               // subdirectory, recurse into it
+                               char NewFindPath[MAX_PATH];
+                               _snprintf(NewFindPath, sizeof(NewFindPath)-1, "%s\\%s",path, fd.cFileName);
+                               GetTestFiles(NewFindPath);
+                       } else if (strstr(fd.cFileName, ".phpt")) {
+                               // got test file, parse it now
+                               if (ParseTestFile(path, fd.cFileName)) {
+                                       printf("Test File Added: %s\\%s\r\n", path, fd.cFileName);
+                               }
+                       }
+                       memset(&fd, 0, sizeof(WIN32_FIND_DATA));
+               } while (FindNextFile(fh, &fd) != 0);
+               FindClose(fh);
+       }
+       return IsapiFileList.GetSize() > 0;
+}
+
+void DoTestFiles(const char *filelist, const char *environment)
+{
+       if (!GetTestFiles(filelist)) {
+               printf("No Files to test!\r\n");
+               return;
+       }
+
+       ReadGlobalEnvironment(environment);
+
+       DoThreads();
+}
+
+int main(int argc, char* argv[]) {
+       LPVOID lpMsgBuf;
+       char *filelist=NULL, *environment=NULL;
+
+       if (argc < 3) {
+               // look for phpt files in tests
+               printf("USAGE: stresstest [L|T] filelist [environment]\r\n");
+               return 0;
+       } else {
+               if (argv[1][0]=='T') test = TRUE;
+               if (argc > 1) filelist = argv[2];
+               if (argc > 2) environment = argv[3];
+       }
+
+       GetTempPath(sizeof(temppath), temppath);
+       hDll = LoadLibrary("php4isapi.dll"); // Load our DLL
+
+       if (!hDll) {
+               FormatMessage( 
+                  FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+                   NULL,
+                   GetLastError(),
+                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+                   (LPTSTR) &lpMsgBuf,
+                   0,
+                   NULL 
+               );
+               fprintf(stderr,"Error: Dll 'php4isapi.dll' not found -%d\n%s\n", GetLastError(),lpMsgBuf);
+               LocalFree( lpMsgBuf );
+               return -1;
+       }
+
+       //
+       // Find the exported functions
+
+       IsapiGetExtensionVersion = (VersionProc)GetProcAddress(hDll,"GetExtensionVersion");
+       if (!IsapiGetExtensionVersion) {
+               fprintf(stderr,"Can't Get Extension Version %d\n",GetLastError());
+               return -1;
+       }
+       IsapiHttpExtensionProc = (HttpExtProc)GetProcAddress(hDll,"HttpExtensionProc");
+       if (!IsapiHttpExtensionProc) {
+               fprintf(stderr,"Can't Get Extension proc %d\n",GetLastError());
+               return -1;
+       }
+
+       // This should really check if the version information matches what we
+       // expect.
+       //
+       if (!IsapiGetExtensionVersion(&version_info) ) {
+               fprintf(stderr,"Fatal: GetExtensionVersion failed\n");
+               return -1;
+       }
+
+       if (test) {
+               char TestPath[MAX_PATH];
+               if (filelist != NULL) 
+                       _snprintf(TestPath, sizeof(TestPath)-1, "%s\\tests", filelist);
+               else strcpy(TestPath, "tests");
+               DoTestFiles(TestPath, environment);
+       } else
+               DoFileList(filelist, environment);
 
        // cleanup
 
+
        // We should really free memory (e.g., from GetEnv), but we'll be dead
        // soon enough
 
@@ -204,8 +409,12 @@ DWORD CALLBACK IsapiThread(void *p)
                for (DWORD i=0; i<filecount; i++) {
                        // execute each file
                        printf("Thread %d File %s\n", GetCurrentThreadId(), IsapiFileList.GetAt(i));
-                       stress_main(IsapiFileList.GetAt(i), IsapiArgList.GetAt(i));
-                       //Sleep(10);
+                       stress_main(IsapiFileList.GetAt(i), 
+                                               IsapiGetData.GetAt(i), 
+                                               IsapiPostData.GetAt(i),
+                                               IsapiMatchData.GetAt(i),
+                                               TestNames.GetAt(i));
+                       Sleep(10);
                }
        }
        SetEvent(*terminate);
@@ -221,7 +430,12 @@ DWORD CALLBACK IsapiThread(void *p)
  * the DLL to load. There is no recompilation required.                    *
  * ======================================================================= *
 */
-int stress_main(const char *filename, const char *arg) {
+int stress_main(const char *filename, 
+                               const char *arg, 
+                               const char *postdata,
+                               const char *matchdata,
+                               const char *testname) 
+{
 
        EXTENSION_CONTROL_BLOCK ECB;
        DWORD rc;
@@ -231,13 +445,16 @@ int stress_main(const char *filename, const char *arg) {
        context.tid = GetCurrentThreadId();
        CString fname;
        fname.Format("%08X.out", context.tid);
+
        context.out = CreateFile(fname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
        if (context.out==INVALID_HANDLE_VALUE) {
                printf("failed to open output file %s\n", fname);
                return 0;
        }
-       if (arg) context.in  = CreateFile(arg, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
-       else context.in = INVALID_HANDLE_VALUE;
+
+       // not using post files
+       context.in = INVALID_HANDLE_VALUE;
+
        //
        // Fill the ECB with the necessary information
        //
@@ -250,16 +467,54 @@ int stress_main(const char *filename, const char *arg) {
        // first arg = filename
        // this is added for testing php from command line
 
+       context.env.RemoveAll();
+       context.env["PATH_TRANSLATED"]= filename;
+       context.env["SCRIPT_MAP"]= filename;
+       context.env["CONTENT_TYPE"]= "";
+       context.env["CONTENT_LENGTH"]= "";
+       context.env["QUERY_STRING"]= arg;
+       context.env["METHOD"]="GET";
+
+       char buf[MAX_PATH];
+       if (postdata && *postdata !=0) {
+               ECB.cbAvailable = strlen(postdata);
+               ECB.cbTotalBytes = ECB.cbAvailable;
+               ECB.lpbData = (unsigned char *)postdata;
+               context.env["METHOD"]="POST";
+
+               _snprintf(buf, sizeof(buf)-1, "%d", ECB.cbTotalBytes);
+               context.env["CONTENT_LENGTH"]=buf;
+
+               context.env["CONTENT_TYPE"]="application/x-www-form-urlencoded";
+       }
     ECB.lpszPathTranslated = strdup(filename);
+       ECB.lpszQueryString = strdup(arg);
 
        // Call the DLL
        //
        rc = IsapiHttpExtensionProc(&ECB);
 
-       free (ECB.lpszPathTranslated);
+       free(ECB.lpszPathTranslated);
+       free(ECB.lpszQueryString);
+
+
+       // compare the output with the EXPECT section
+       if (matchdata && *matchdata != 0) {
+               // wimpy I know, but I'm tired and lazy now
+               HANDLE md = CreateFile(matchdata, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+               if (md) {
+                       DWORD osz = 0, msz = 0;
+                       GetFileSize(md, &msz);
+                       GetFileSize(context.out, &osz);
+                       if (osz == msz && osz != 0) {
+                               printf("%s OK\r\n", testname);
+                       } else {
+                               printf("%s FAILED\r\n", testname);
+                       }
+               }
+       }
 
        if (context.out) CloseHandle(context.out);
-       if (context.in) CloseHandle(context.in);
 
        //if (rc == HSE_STATUS_PENDING) // We will exit in ServerSupportFunction
        //      Sleep(INFINITE);
@@ -276,10 +531,15 @@ BOOL WINAPI GetServerVariable(HCONN hConn, LPSTR lpszVariableName,
 
        DWORD rc;
        CString value;
+       TIsapiContext *c = (TIsapiContext *)hConn;
+       if (!c) return FALSE;
 
        if (IsapiEnvironment.Lookup(lpszVariableName, value)) {
                rc = value.GetLength();
                strncpy((char *)lpBuffer, value, *lpdwSize-1);
+       } else if (c->env.Lookup(lpszVariableName, value)) {
+               rc = value.GetLength();
+               strncpy((char *)lpBuffer, value, *lpdwSize-1);
        } else
                rc = GetEnvironmentVariable(lpszVariableName,(char *)lpBuffer,*lpdwSize) ;
 
@@ -305,7 +565,10 @@ BOOL WINAPI GetServerVariable(HCONN hConn, LPSTR lpszVariableName,
 BOOL WINAPI ReadClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize) {
        TIsapiContext *c = (TIsapiContext *)hConn;
        if (!c) return FALSE;
-       if (c->in) return ReadFile(c->in,lpBuffer,(*lpdwSize), lpdwSize,NULL);
+
+       if (c->in != INVALID_HANDLE_VALUE) 
+               return ReadFile(c->in,lpBuffer,(*lpdwSize), lpdwSize,NULL);
+
        return FALSE;
 }
 //
@@ -315,7 +578,10 @@ BOOL WINAPI WriteClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize,
                        DWORD dwReserved) {
        TIsapiContext *c = (TIsapiContext *)hConn;
        if (!c) return FALSE;
-       if (c->out) return WriteFile(c->out,lpBuffer,*lpdwSize, lpdwSize,NULL);
+
+       if (c->out != INVALID_HANDLE_VALUE) 
+               return WriteFile(c->out,lpBuffer,*lpdwSize, lpdwSize,NULL);
+
        return FALSE;
 }
 //
index 847de279e582a5a30d40fc13b54652f625ee69fc..bb2ce57a5e206ebb56d8d58cc88e4a9deae49617 100644 (file)
@@ -61,8 +61,9 @@ LINK32=link.exe
 # PROP BASE Target_Dir ""\r
 # PROP Use_MFC 2\r
 # PROP Use_Debug_Libraries 1\r
-# PROP Output_Dir "Debug"\r
+# PROP Output_Dir "e:\php4test"\r
 # PROP Intermediate_Dir "Debug"\r
+# PROP Ignore_Export_Lib 0\r
 # PROP Target_Dir ""\r
 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c\r
 # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "_AFXDLL" /FD /GZ /c\r