blob: 7cc09b48332962430ab8b4387f0bb981df0aa990 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id: ThreadTest.cpp 568078 2007-08-21 11:43:25Z amassari $
*
* @author Andy Heninger, IBM
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <xercesc/parsers/SAXParser.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/sax/HandlerBase.hpp>
#include <xercesc/framework/MemBufInputSource.hpp>
#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/sax2/Attributes.hpp>
#include <xercesc/sax2/DefaultHandler.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/framework/StdOutFormatTarget.hpp>
#include <xercesc/internal/XMLGrammarPoolImpl.hpp>
#include <xercesc/internal/MemoryManagerImpl.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>
void clearFileInfoMemory();
//------------------------------------------------------------------------------
//
// Windows specific code for starting threads
//
//------------------------------------------------------------------------------
#ifdef PLATFORM_WIN32
#include "Windows.h"
#include "process.h"
typedef DWORD (WINAPI *ThreadFunc)(void *);
class ThreadFuncs // This class isolates OS dependent threading
{ // functions from the rest of ThreadTest program.
public:
static void Sleep(int millis) {::Sleep(millis);};
static void startThread(ThreadFunc, void *param);
};
void ThreadFuncs::startThread(ThreadFunc func, void *param)
{
HANDLE tHandle;
DWORD threadID;
tHandle = CreateThread(0, // Security Attributes,
0x10000, // Stack Size,
func, // Starting Address.
param, // Parmeters
0, // Creation Flags,
&threadID); // Thread ID (Can not be null on 95/98)
if (tHandle == 0)
{
fprintf(stderr, "Error starting thread. Errno = %d\n", errno);
clearFileInfoMemory();
exit(-1);
}
// Set the priority of the working threads low, so that the UI of the running system will
// remain responsive.
SetThreadPriority(tHandle, THREAD_PRIORITY_IDLE);
}
#elif defined (AIX) || defined(SOLARIS) || defined(LINUX) || defined(HPUX) || defined (OS390) || defined(MACOSX) || defined(FREEBSD) || defined(__CYGWIN__) || defined(__QNXNTO__) || defined(INTERIX) || defined(IRIX)
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
//------------------------------------------------------------------------------
//
// UNIX specific code for starting threads
//
//------------------------------------------------------------------------------
extern "C" {
typedef void (*ThreadFunc)(void *);
typedef void *(*pthreadfunc)(void *);
class ThreadFuncs // This class isolates OS dependent threading
{ // functions from the rest of ThreadTest program.
public:
static void Sleep(int millis);
static void startThread(ThreadFunc, void *param);
};
void ThreadFuncs::Sleep(int millis)
{
int seconds = millis/1000;
if (seconds <= 0) seconds = 1;
#if defined(SOLARIS)
// somehow the sleep hangs on Solaris
// so ignore the call
#else
::sleep(seconds);
#endif
}
void ThreadFuncs::startThread(ThreadFunc func, void *param)
{
unsigned long x;
pthread_t tId;
//thread_t tId;
#if defined(_HP_UX) && defined(XML_USE_DCE)
x = pthread_create( &tId, pthread_attr_default, (pthreadfunc)func, param);
#else
pthread_attr_t attr;
pthread_attr_init(&attr);
x = pthread_create( &tId, &attr, (pthreadfunc)func, param);
#endif
if (x == -1)
{
fprintf(stderr, "Error starting thread. Errno = %d\n", errno);
clearFileInfoMemory();
exit(-1);
}
}
} // end of extern "C"
#else
#error This platform is not supported
#endif
//------------------------------------------------------------------------------
//
// struct InFileInfo One of these structs will be set up for each file listed
// on the command line. Once set, the data is unchanging
// and can safely be referenced by the test threads without
// use of synchronization.
//
//------------------------------------------------------------------------------
struct InFileInfo
{
char *fileName;
XMLCh *uFileName; // When doing an in-memory parse, avoid transcoding file name
// each time through.
char *fileContent; // If doing an in-memory parse, this field points
// to an allocated string containing the entire file
// contents. Otherwise it's 0.
size_t fileSize; // The file length. Only initialized when doing
// an in-memory test.
int checkSum; // The XML checksum. Set up by the main thread for
// each file before the worker threads are started.
};
//------------------------------------------------------------------------------
//
// struct threadInfo Holds information specific to an individual thread.
// One of these is set up for each thread in the test.
// The main program monitors the threads by looking
// at the status stored in these structs.
//
//------------------------------------------------------------------------------
struct ThreadInfo
{
bool fHeartBeat; // Set true by the thread each time it finishes
// parsing a file.
bool fInProgress; // Set to false by the thread when parse in progress
unsigned int fParses; // Number of parses completed.
int fThreadNum; // Identifying number for this thread.
ThreadInfo() {
fHeartBeat = false;
fInProgress = false;
fParses = 0;
fThreadNum = -1;
}
};
XERCES_CPP_NAMESPACE_USE
//------------------------------------------------------------------------------
//
// struct runInfo Holds the info extracted from the command line.
// There is only one of these, and it is static, and
// unchanging once the command line has been parsed.
// During the test, the threads will access this info without
// any synchronization.
//
//------------------------------------------------------------------------------
const int MAXINFILES = 25;
struct RunInfo
{
bool doGrammarCaching;
bool quiet;
bool verbose;
bool stopNow;
bool dom;
bool sax;
bool reuseParser;
bool inMemory;
bool dumpOnErr;
bool doSchema;
bool schemaFullChecking;
bool doNamespaces;
bool doInitialParse;
bool doNamespacePrefixes; // SAX2
SAXParser::ValSchemes valScheme;
int numThreads;
int totalTime;
int numInputFiles;
unsigned int numParses;
InFileInfo files[MAXINFILES];
};
//
//------------------------------------------------------------------------------
//
// Global Data
//
//------------------------------------------------------------------------------
RunInfo gRunInfo;
ThreadInfo *gThreadInfo;
/** Grammar caching thread testing */
MemoryManager* gpMemMgr = 0;
XMLGrammarPool* gp = 0;
// Routines which maybe helpful for debugging
static void printString(const XMLCh *str)
{
char *s = XMLString::transcode(str);
printf("%s", s);
delete s;
}
#define CHARS_PER_LINE 40
#define BYTES_PER_LINE 16
/*
* DumpLine: Dump out a buffer (address and length) to stderr.
*/
static void DumpLine(char* address, int length) {
int i, c, charCount=0;
if (length % 4) length += 4;
fprintf(stderr, "%8.8p: ", address);
for (i=0; i < length/4; ++i) {
fprintf(stderr, "%8.8X ", ((int*)address)[i]);
charCount += 9;
}
for (i=charCount; i < CHARS_PER_LINE; ++i) {
putc(' ', stderr);
}
fprintf(stderr, "| ");
for (i=0; i < length; ++i) {
c = address[i];
c = (isprint(c) ? c : '.');
fprintf(stderr, "%c", c);
}
fprintf(stderr, "\n");
}
/*
* dump: dump out a buffer (address and length) to stderr by dumping out
* a line at a time (DumpLine), until the buffer is written out.
*/
static void dump(void* generalAddress, int length) {
int curr = 0;
char* address = (char*) generalAddress;
while (&address[curr] < &address[length-BYTES_PER_LINE]) {
DumpLine(&address[curr], BYTES_PER_LINE);
curr += BYTES_PER_LINE;
}
if (curr < length) {
DumpLine(&address[curr], length-curr);
}
fflush(stderr);
}
//------------------------------------------------------------------------------
//
// class ThreadParser Bundles together a SAX parser and the SAX handlers
// and contains the API that the rest of this test
// program uses for creating parsers and doing parsing.
//
// Multiple instances of this class can operate concurrently
// in different threads.
//
//-------------------------------------------------------------------------------
class ThreadParser
{
public:
class SAXHandler;
class SAX2Handler;
SAXHandler* fSAXHandler;
SAX2Handler* fSAX2Handler;
ErrorHandler* fDOMErrorHandler;
// This is the API used by the rest of the test program
ThreadParser();
~ThreadParser();
int parse(int fileNum); // Parse the specified file. fileNum is an index
// into the gRunInfo.files array.
// return the XML checksum, or
// 0 if a parse error occurred.
int getCheckSum() {
return fCheckSum;
};
int reCheck(); // Try to compute the checksum again.
// for DOM, re-walk the tree.
// for SAX, can't do, just return previous value.
void domPrint(); // including any children. Default (no param)
// version dumps the entire document.
void addToCheckSum(const XMLCh *chars, int len=-1);
// These are the SAX call-back functions that this class implements. Can be used
// for SAX and SAX2.
void characters(const XMLCh* const chars, const unsigned int length) {
addToCheckSum(chars, length);
};
void ignorableWhitespace(const XMLCh* const chars, const unsigned int length) {
addToCheckSum(chars, length);
};
void resetDocument() {
};
void warning(const SAXParseException& exc) {
fprintf(stderr, "*** Warning ");
fflush(stderr);
throw exc;
};
void error(const SAXParseException& exc) {
fprintf(stderr, "*** Error ");
fflush(stderr);
throw exc;
};
void fatalError(const SAXParseException& exc) {
fprintf(stderr, "***** Fatal error ");
fflush(stderr);
throw exc;
};
// Create a nested class that can inherit from HandlerBase
// for SAX startElement callbacks.
class SAXHandler : public HandlerBase
{
public:
ThreadParser* SAXInstance;
void startElement(const XMLCh* const name, AttributeList& attributes);
};
// Create a nested class that can inherit from DefaultHandler
// for SAX2 startElement callbacks.
class SAX2Handler : public DefaultHandler
{
public:
ThreadParser* SAX2Instance;
void startElement(const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname,
const Attributes& attributes);
};
private:
int fCheckSum;
SAXParser* fSAXParser;
SAX2XMLReader* fSAX2Parser;
XercesDOMParser* fXercesDOMParser;
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument * fDoc;
ThreadParser(const ThreadParser &); // No copy constructor
const ThreadParser & operator =(const ThreadParser &); // No assignment.
void domCheckSum(const DOMNode *);
};
//
// ThreadParser constructor. Invoked by the threads of the test program
// to create parsers.
//
ThreadParser::ThreadParser()
{
fSAXParser = 0;
fSAX2Parser = 0;
fXercesDOMParser = 0;
fSAXHandler = 0;
fSAX2Handler = 0;
fDOMErrorHandler = 0;
fDoc = 0;
fCheckSum = 0;
if (gRunInfo.dom) {
// Set up to use a DOM parser
/** Grammar caching thread testing */
if (gp) {
fXercesDOMParser = new XercesDOMParser(0, XMLPlatformUtils::fgMemoryManager, gp);
fXercesDOMParser->cacheGrammarFromParse(true);
fXercesDOMParser->useCachedGrammarInParse(true);
}
else {
fXercesDOMParser = new XercesDOMParser;
}
switch (gRunInfo.valScheme) {
case SAXParser::Val_Never:
fXercesDOMParser->setValidationScheme(XercesDOMParser::Val_Never);
break;
case SAXParser::Val_Auto:
fXercesDOMParser->setValidationScheme(XercesDOMParser::Val_Auto);
break;
default: //SAXParser::Val_Always:
fXercesDOMParser->setValidationScheme(XercesDOMParser::Val_Always);
break;
}
fXercesDOMParser->setDoSchema(gRunInfo.doSchema);
fXercesDOMParser->setValidationSchemaFullChecking(gRunInfo.schemaFullChecking);
fXercesDOMParser->setDoNamespaces(gRunInfo.doNamespaces);
fDOMErrorHandler = (ErrorHandler*) new HandlerBase();
fXercesDOMParser->setErrorHandler(fDOMErrorHandler);
}
else if (gRunInfo.sax) {
// Set up to use a SAX1 parser.
/** Grammar caching thread testing */
if (gp) {
fSAXParser = new SAXParser(0, XMLPlatformUtils::fgMemoryManager, gp);
fSAXParser->cacheGrammarFromParse(true);
fSAXParser->useCachedGrammarInParse(true);
}
else {
fSAXParser = new SAXParser();
}
fSAXParser->setValidationScheme(gRunInfo.valScheme);
fSAXParser->setDoSchema(gRunInfo.doSchema);
fSAXParser->setValidationSchemaFullChecking(gRunInfo.schemaFullChecking);
fSAXParser->setDoNamespaces(gRunInfo.doNamespaces);
fSAXHandler = new ThreadParser::SAXHandler();
fSAXHandler->SAXInstance = this;
fSAXParser->setDocumentHandler(fSAXHandler);
fSAXParser->setErrorHandler(fSAXHandler);
}
else {
// Set up to use a SAX2 parser.
/** Grammar caching thread testing */
if (gp) {
fSAX2Parser = XMLReaderFactory::createXMLReader(gpMemMgr, gp);
fSAX2Parser->setFeature(XMLUni::fgXercesCacheGrammarFromParse,true);
fSAX2Parser->setFeature(XMLUni::fgXercesUseCachedGrammarInParse,true);
}
else {
fSAX2Parser = XMLReaderFactory::createXMLReader();
}
fSAX2Parser->setFeature(XMLUni::fgSAX2CoreNameSpaces,(gRunInfo.doNamespaces));
fSAX2Parser->setFeature(XMLUni::fgXercesSchema,(gRunInfo.doSchema));
fSAX2Parser->setFeature(XMLUni::fgXercesSchemaFullChecking,(gRunInfo.schemaFullChecking));
switch (gRunInfo.valScheme) {
case SAXParser::Val_Never:
fSAX2Parser->setFeature(XMLUni::fgSAX2CoreValidation, false);
break;
case SAXParser::Val_Auto:
fSAX2Parser->setFeature(XMLUni::fgSAX2CoreValidation, true);
fSAX2Parser->setFeature(XMLUni::fgXercesDynamic, true);
break;
default: //SAXParser::Val_Always:
fSAX2Parser->setFeature(XMLUni::fgSAX2CoreValidation, true);
fSAX2Parser->setFeature(XMLUni::fgXercesDynamic, false);
break;
}
fSAX2Parser->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes,(gRunInfo.doNamespacePrefixes));
fSAX2Handler = new ThreadParser::SAX2Handler();
fSAX2Handler->SAX2Instance = this;
fSAX2Parser->setContentHandler(fSAX2Handler);
fSAX2Parser->setErrorHandler(fSAX2Handler);
}
}
ThreadParser::~ThreadParser()
{
delete fSAXParser;
delete fSAX2Parser;
delete fXercesDOMParser;
delete fSAXHandler;
delete fSAX2Handler;
delete fDOMErrorHandler;
}
//------------------------------------------------------------------------
//
// parse - This is the method that is invoked by the rest of
// the test program to actually parse an XML file.
//
//------------------------------------------------------------------------
int ThreadParser::parse(int fileNum)
{
MemBufInputSource *mbis = 0;
InFileInfo *fInfo = &gRunInfo.files[fileNum];
bool errors = false;
fCheckSum = 0;
if (gRunInfo.inMemory) {
mbis = new MemBufInputSource((const XMLByte *) fInfo->fileContent,
fInfo->fileSize,
fInfo->uFileName,
false);
}
try
{
if (gRunInfo.dom) {
// Do a DOM parse
fXercesDOMParser->resetDocumentPool();
if (gRunInfo.inMemory)
fXercesDOMParser->parse(*mbis);
else
fXercesDOMParser->parse(fInfo->fileName);
fDoc = fXercesDOMParser->getDocument();
domCheckSum(fDoc);
}
else if (gRunInfo.sax) {
// Do a SAX1 parse
if (gRunInfo.inMemory)
fSAXParser->parse(*mbis);
else
fSAXParser->parse(fInfo->fileName);
}
else {
// Do a SAX2 parse
if (gRunInfo.inMemory)
fSAX2Parser->parse(*mbis);
else
fSAX2Parser->parse(fInfo->fileName);
}
}
catch (const OutOfMemoryException&)
{
fprintf(stderr, " during parsing: %s\n OutOfMemoryException.\n", fInfo->fileName);
errors = true;
}
catch (const XMLException& e)
{
char *exceptionMessage = XMLString::transcode(e.getMessage());
fprintf(stderr, " during parsing: %s\n Exception message is: %s\n",
fInfo->fileName, exceptionMessage);
XMLString::release(&exceptionMessage);
errors = true;
}
catch (const DOMException& toCatch)
{
fprintf(stderr, " during parsing: %s\n DOMException code is: %i\n",
fInfo->fileName, toCatch.code);
errors = true;
}
catch (const SAXParseException& e)
{
char *exceptionMessage = XMLString::transcode(e.getMessage());
fprintf(stderr, " during parsing: %s\n Exception message is: %s\n",
fInfo->fileName, exceptionMessage);
XMLString::release(&exceptionMessage);
errors = true;
}
catch (...)
{
fprintf(stderr, "Unexpected exception during parsing\n");
errors = true;
}
delete mbis;
if (errors) {
fflush(stderr);
return 0; // if errors occurred, return zero as if checksum = 0;
}
return fCheckSum;
}
//
// addToCheckSum - private function, used within ThreadParser in
// computing the checksum of the XML file.
//
// Unichar Strings to be added to the checksum
// can either be null terminated (omit len param, which
// will then default to -1), or provide an explicit
// length.
//
void ThreadParser::addToCheckSum(const XMLCh *chars, int len)
{
if (len == -1)
{
// Null terminated string.
while (*chars != 0)
{
fCheckSum = fCheckSum*5 + *chars;
chars++;
}
}
else
{
// String with character count.
int i;
for (i=0; i<len; i++)
fCheckSum = fCheckSum*5 + chars[i];
}
}
//
// startElement - our SAX handler callback function for startElement.
// Update the document checksum with the element name
// and any attribute names and values.
//
void ThreadParser::SAXHandler::startElement(const XMLCh *const name, AttributeList &attributes)
{
SAXInstance->addToCheckSum(name);
int n = attributes.getLength();
int i;
for (i=0; i<n; i++)
{
const XMLCh *attNam = attributes.getName(i);
SAXInstance->addToCheckSum(attNam);
const XMLCh *attVal = attributes.getValue(i);
SAXInstance->addToCheckSum(attVal);
}
}
//
// startElement - our SAX2 handler callback function for startElement.
// Update the document checksum with the element name
// and any attribute names and values.
//
void ThreadParser::SAX2Handler::startElement(const XMLCh *const uri,
const XMLCh *const localname,
const XMLCh *const qname,
const Attributes& attributes)
{
SAX2Instance->addToCheckSum(localname);
int n = attributes.getLength();
int i;
for (i=0; i<n; i++)
{
const XMLCh *attNam = attributes.getQName(i);
SAX2Instance->addToCheckSum(attNam);
const XMLCh *attVal = attributes.getValue(i);
SAX2Instance->addToCheckSum(attVal);
}
}
//
// domCheckSum - Compute the check sum for a DOM node.
// Works recursively - initially called with a document node.
//
void ThreadParser::domCheckSum(const DOMNode *node)
{
const XMLCh *s;
DOMNode *child;
DOMNamedNodeMap *attributes;
switch (node->getNodeType() )
{
case DOMNode::ELEMENT_NODE:
{
s = node->getNodeName(); // the element name
attributes = node->getAttributes(); // Element's attributes
int numAttributes = attributes->getLength();
int i;
for (i=0; i<numAttributes; i++)
domCheckSum(attributes->item(i));
addToCheckSum(s); // Content and Children
for (child=node->getFirstChild(); child!=0; child=child->getNextSibling())
domCheckSum(child);
break;
}
case DOMNode::ATTRIBUTE_NODE:
{
s = node->getNodeName(); // The attribute name
addToCheckSum(s);
s = node->getNodeValue(); // The attribute value
if (s != 0)
addToCheckSum(s);
break;
}
case DOMNode::TEXT_NODE:
case DOMNode::CDATA_SECTION_NODE:
{
s = node->getNodeValue();
addToCheckSum(s);
break;
}
case DOMNode::ENTITY_REFERENCE_NODE:
case DOMNode::DOCUMENT_NODE:
{
// For entity references and the document, nothing is dirctly
// added to the checksum, but we do want to process the chidren nodes.
//
for (child=node->getFirstChild(); child!=0; child=child->getNextSibling())
domCheckSum(child);
break;
}
}
}
//
// Recompute the checksum. Meaningful only for DOM, will tell us whether
// a failure is transient, or whether the DOM data is permanently corrupted.
//
int ThreadParser::reCheck()
{
if (gRunInfo.dom) {
fCheckSum = 0;
domCheckSum(fDoc);
}
return fCheckSum;
}
//
// domPrint - Dump the contents of a DOM node.
// For debugging failures, when all else fails.
// Works recursively - initially called with a document node.
//
void ThreadParser::domPrint()
{
printf("Begin DOMPrint ...\n");
if (gRunInfo.dom)
{
try
{
XMLCh tempStr[100];
XMLString::transcode("LS", tempStr, 99);
DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(tempStr);
DOMWriter *theSerializer = ((DOMImplementationLS*)impl)->createDOMWriter();
XMLFormatTarget *myFormTarget = new StdOutFormatTarget();
DOMNode *doc = fXercesDOMParser->getDocument();
theSerializer->writeNode(myFormTarget, *doc);
delete theSerializer;
}
catch (...)
{
// do nothing
}
}
printf("End DOMPrint\n");
}
//----------------------------------------------------------------------
//
// parseCommandLine Read through the command line, and save all
// of the options in the gRunInfo struct.
//
// Display the usage message if the command line
// is no good.
//
// Probably ought to be a member function of RunInfo.
//
//----------------------------------------------------------------------
void parseCommandLine(int argc, char **argv)
{
gRunInfo.doGrammarCaching = false;
gRunInfo.quiet = false; // Set up defaults for run.
gRunInfo.verbose = false;
gRunInfo.stopNow = false;
gRunInfo.dom = false;
gRunInfo.sax = true;
gRunInfo.reuseParser = false;
gRunInfo.inMemory = false;
gRunInfo.dumpOnErr = false;
gRunInfo.doSchema = false;
gRunInfo.schemaFullChecking = false;
gRunInfo.doNamespaces = false;
gRunInfo.doInitialParse = false;
gRunInfo.doNamespacePrefixes = false;
gRunInfo.valScheme = SAXParser::Val_Auto;
gRunInfo.numThreads = 2;
gRunInfo.totalTime = 0;
gRunInfo.numInputFiles = 0;
gRunInfo.numParses = 0;
try // Use exceptions for command line syntax errors.
{
int argnum = 1;
while (argnum < argc) {
if (strcmp(argv[argnum], "-quiet") == 0)
gRunInfo.quiet = true;
else if (strcmp(argv[argnum], "-verbose") == 0)
gRunInfo.verbose = true;
else if (strncmp(argv[argnum], "-v=", 3) == 0) {
const char* const parm = &argv[argnum][3];
if (!strcmp(parm, "never"))
gRunInfo.valScheme = SAXParser::Val_Never;
else if (!strcmp(parm, "auto"))
gRunInfo.valScheme = SAXParser::Val_Auto;
else if (!strcmp(parm, "always"))
gRunInfo.valScheme = SAXParser::Val_Always;
else {
fprintf(stderr, "Unrecognized -v option \"%s\"\n", parm);
throw 1;
}
}
else if (strcmp(argv[argnum], "-v") == 0) {
fprintf(stderr, "Please note the -v option has been changed to -v=[always | never | auto]\n");
fprintf(stderr, "ThreadTest will continue with -v=always\n");
gRunInfo.valScheme = SAXParser::Val_Always;
}
else if (strcmp(argv[argnum], "-s") == 0)
gRunInfo.doSchema = true;
else if (strcmp(argv[argnum], "-f") == 0)
gRunInfo.schemaFullChecking = true;
else if (strcmp(argv[argnum], "-n") == 0)
gRunInfo.doNamespaces = true;
else if (strcmp(argv[argnum], "-p") == 0)
gRunInfo.doNamespacePrefixes = true;
else if (!strncmp(argv[argnum], "-parser=", 8)) {
const char* const parm = &argv[argnum][8];
if (!strcmp(parm, "dom")) {
gRunInfo.dom = true;
gRunInfo.sax = false;
}
else if (!strcmp(parm, "sax")) {
gRunInfo.dom = false;
gRunInfo.sax = true;
}
else if (!strcmp(parm, "sax2")) {
gRunInfo.dom = false;
gRunInfo.sax = false;
}
else {
fprintf(stderr, "Unrecognized -parser option \"%s\"\n", parm);
throw 1;
}
}
else if (strcmp(argv[argnum], "-init") == 0)
gRunInfo.doInitialParse = true;
else if (strcmp(argv[argnum], "-reuse") == 0)
gRunInfo.reuseParser = true;
else if (strcmp(argv[argnum], "-dump") == 0)
gRunInfo.dumpOnErr = true;
else if (strcmp(argv[argnum], "-mem") == 0)
gRunInfo.inMemory = true;
else if (strcmp(argv[argnum], "-threads") == 0) {
++argnum;
if (argnum >= argc) {
fprintf(stderr, "Invalid -threads option (missing # of threads)\n");
throw 1;
}
gRunInfo.numThreads = atoi(argv[argnum]);
if (gRunInfo.numThreads < 0) {
fprintf(stderr, "Invalid -threads option (negative # of threads)\n");
throw 1;
}
}
else if (strcmp(argv[argnum], "-time") == 0) {
++argnum;
if (argnum >= argc) {
fprintf(stderr, "Invalid -time option (missing time value)\n");
throw 1;
}
gRunInfo.totalTime = atoi(argv[argnum]);
if (gRunInfo.totalTime < 1) {
fprintf(stderr, "Invalid -time option (time value < 1)\n");
throw 1;
}
}
else if (strcmp(argv[argnum], "-gc") == 0)
gRunInfo.doGrammarCaching = true;
else if (strcmp(argv[argnum], "-parses") == 0) {
++argnum;
if (argnum >= argc) {
fprintf(stderr, "Invalid -parses option (missing # of parses)\n");
throw 1;
}
int temp = atoi(argv[argnum]);
if (temp < 0) {
fprintf(stderr, "Invalid -parses option (negative # of parses)\n");
throw 1;
}
gRunInfo.numParses = temp;
}
else if (argv[argnum][0] == '-') {
fprintf(stderr, "Unrecognized command line option. Scanning \"%s\"\n",
argv[argnum]);
throw 1;
}
else {
gRunInfo.numInputFiles++;
if (gRunInfo.numInputFiles >= MAXINFILES) {
fprintf(stderr, "Too many input files. Limit is %d\n", MAXINFILES);
throw 1;
}
gRunInfo.files[gRunInfo.numInputFiles-1].fileName = argv[argnum];
}
argnum++;
}
// We've made it through the command line.
// Verify that at least one input file to be parsed was specified.
if (gRunInfo.numInputFiles == 0) {
fprintf(stderr, "No input XML file specified on command line.\n");
throw 1;
};
if (gRunInfo.numParses && gRunInfo.totalTime) {
fprintf(stderr, "Both -parses nnn and -time nnn were specified. Ignoring -time nnn.\n");
}
}
catch (int)
{
fprintf(stderr, "usage: ThreadTest [-v] [-threads nnn] [-time nnn] [-quiet] [-verbose] xmlfile...\n"
" -v=xxx Validation scheme [always | never | auto]. Default is AUTO.\n"
" -n Enable namespace processing. Defaults to off.\n"
" -s Enable schema processing. Defaults to off.\n"
" -f Enable full schema constraint checking. Defaults to off.\n"
" -parser=xxx Parser Type [dom | sax | sax2]. Default is SAX (SAX1).\n"
" -p Enable namespace prefixes. Defaults to off.\n"
" (Only used with -parser=sax2, ignored otherwise.)\n"
" -quiet Suppress periodic status display.\n"
" -verbose Display extra messages.\n"
" -reuse Retain and reuse parser. Default creates new for each parse.\n"
" -threads nnn Number of threads. Default is 2.\n"
" -time nnn Total time to run, in seconds. Default is forever.\n"
" -parses nnn Run for nnn parses instead of time. Default is to use time\n"
" -dump Dump DOM tree on error.\n"
" -mem Read files into memory once only, and parse them from there.\n"
" -gc Enable grammar caching (i.e. grammar cached and used in subsequent parses). Defaults to off.\n"
" -init Perform an initial parse of the file(s) before starting up the individual threads.\n\n"
);
exit(1);
}
}
//---------------------------------------------------------------------------
//
// ReadFilesIntoMemory For use when parsing from memory rather than
// reading the files each time, here is the code that
// reads the files into local memory buffers.
//
// This function is only called once, from the main
// thread, before all of the worker threads are started.
//
//---------------------------------------------------------------------------
void ReadFilesIntoMemory()
{
int fileNum;
FILE *fileF;
size_t t;
if (gRunInfo.inMemory)
{
for (fileNum = 0; fileNum <gRunInfo.numInputFiles; fileNum++)
{
InFileInfo *fInfo = &gRunInfo.files[fileNum];
fInfo->uFileName = XMLString::transcode(fInfo->fileName);
fileF = fopen( fInfo->fileName, "rb" );
if (fileF == 0) {
fprintf(stderr, "Can not open file \"%s\".\n", fInfo->fileName);
clearFileInfoMemory();
exit(-1);
}
fseek(fileF, 0, SEEK_END);
fInfo->fileSize = ftell(fileF);
fseek(fileF, 0, SEEK_SET);
fInfo->fileContent = new char[fInfo->fileSize + 1];
t = fread(fInfo->fileContent, 1, fInfo->fileSize, fileF);
if (t != fInfo->fileSize) {
fprintf(stderr, "Error reading file \"%s\".\n", fInfo->fileName);
clearFileInfoMemory();
exit(-1);
}
fclose(fileF);
fInfo->fileContent[fInfo->fileSize] = 0;
}
}
}
void clearFileInfoMemory()
{
int fileNum;
if (gRunInfo.inMemory)
{
for (fileNum = 0; fileNum <gRunInfo.numInputFiles; fileNum++)
{
InFileInfo *fInfo = &gRunInfo.files[fileNum];
XMLString::release(&fInfo->uFileName);
delete [] fInfo->fileContent;
}
}
}
//----------------------------------------------------------------------
//
// threadMain The main function for each of the swarm of test threads.
// Run in an infinite loop, parsing each of the documents
// given on the command line in turn.
//
//----------------------------------------------------------------------
#ifdef PLATFORM_WIN32
unsigned long WINAPI threadMain (void *param)
#else
extern "C" {
void threadMain (void *param)
#endif
{
ThreadInfo *thInfo = (ThreadInfo *)param;
ThreadParser *thParser = 0;
if (gRunInfo.verbose)
printf("Thread #%d: starting\n", thInfo->fThreadNum);
int docNum = gRunInfo.numInputFiles;
//
// Each time through this loop, one file will be parsed and its checksum
// computed and compared with the precomputed value for that file.
//
while (gRunInfo.stopNow == false) {
if (gRunInfo.numParses == 0 || thInfo->fParses < gRunInfo.numParses) {
thInfo->fInProgress = true;
if (thParser == 0)
thParser = new ThreadParser;
docNum++;
if (docNum >= gRunInfo.numInputFiles)
docNum = 0;
InFileInfo *fInfo = &gRunInfo.files[docNum];
if (gRunInfo.verbose )
printf("Thread #%d: parse %d starting file %s\n", thInfo->fThreadNum, thInfo->fParses, fInfo->fileName);
int checkSum = 0;
checkSum = thParser->parse(docNum);
// For the case where we skip the preparse we will have nothing to
// compare the first parse's results to ... so if this looks like first
// parse move the checkSum back into the gRunInfo data for this file.
if (gRunInfo.files[docNum].checkSum == 0) {
gRunInfo.files[docNum].checkSum = checkSum;
}
else if (checkSum != gRunInfo.files[docNum].checkSum) {
if (checkSum == 0) {
// parse returns 0 if there was an error so do this to get the real
// checksum value
checkSum = thParser->getCheckSum();
}
fprintf(stderr, "\nThread %d: Parse Check sum error on file \"%s\" for parse # %d. Expected %x, got %x\n",
thInfo->fThreadNum, thInfo->fParses, fInfo->fileName, fInfo->checkSum, checkSum);
double totalParsesCompleted = 0;
for (int threadNum=0; threadNum < gRunInfo.numThreads; threadNum++) {
totalParsesCompleted += gThreadInfo[threadNum].fParses;
}
fprintf(stderr, "Total number of parses completed is %f.\n", totalParsesCompleted);
// Revisit - let the loop continue to run?
int secondTryCheckSum = thParser->reCheck();
fprintf(stderr, " Retry checksum is %x\n", secondTryCheckSum);
if (gRunInfo.dumpOnErr && gRunInfo.dom) {
thParser->domPrint();
}
fflush(stdout);
fflush(stderr);
clearFileInfoMemory();
exit(-1);
}
if (gRunInfo.reuseParser == false) {
delete thParser;
thParser = 0;
}
thInfo->fHeartBeat = true;
thInfo->fParses++;
thInfo->fInProgress = false;
}
else {
ThreadFuncs::Sleep(1000);
}
}
delete thParser;
#ifdef PLATFORM_WIN32
return 0;
#else
return;
}
#endif
}
//----------------------------------------------------------------------
//
// main
//
//----------------------------------------------------------------------
int main (int argc, char **argv)
{
parseCommandLine(argc, argv);
//
// Initialize the XML system.
//
try
{
XMLPlatformUtils::Initialize();
}
catch (...)
{
fprintf(stderr, "Exception from XMLPlatfromUtils::Initialize.\n");
return 1;
}
/** Grammar caching thread testing */
// Initialize memory manger and grammar pool
// set doInitialParse to true so that the first parse will cache the
// grammar and it'll be used in subsequent parses
if (gRunInfo.doSchema == true && gRunInfo.doNamespaces == true && gRunInfo.doGrammarCaching == true) {
gpMemMgr = new MemoryManagerImpl();
gp = new XMLGrammarPoolImpl(gpMemMgr);
gRunInfo.doInitialParse = true;
}
//
// If we will be parsing from memory, read each of the input files
// into memory now.
//
ReadFilesIntoMemory();
// Initialize checksums to zero so we can check first parse and if
// zero then we need to move first parse's checksum into array. This
// is for the cse where we skip the initial parse.
for (int n = 0; n < gRunInfo.numInputFiles; n++)
{
gRunInfo.files[n].checkSum = 0;
}
if (gRunInfo.doInitialParse)
{
//
// While we are still single threaded, parse each of the documents
// once, to check for errors, and to note the checksum.
// Blow off the rest of the test if there are errors.
//
ThreadParser *mainParser = new ThreadParser;
int n;
bool errors = false;
int cksum;
for (n = 0; n < gRunInfo.numInputFiles; n++)
{
char *fileName = gRunInfo.files[n].fileName;
if (gRunInfo.verbose)
printf("%s checksum is ", fileName);
cksum = mainParser->parse(n);
if (cksum == 0) {
fprintf(stderr, "An error occurred while initially parsing %s\n",
fileName);
errors = true;
};
gRunInfo.files[n].checkSum = cksum;
if (gRunInfo.verbose )
printf("%x\n", cksum);
if (gRunInfo.dumpOnErr && errors && gRunInfo.dom) {
mainParser->domPrint();
}
}
delete mainParser;
if (errors) {
fprintf(stderr, "Quitting due to error incurred during initial parse\n");
clearFileInfoMemory();
return 1;
}
}
//
// Fire off the requested number of parallel threads
//
if (gRunInfo.numThreads == 0) {
clearFileInfoMemory();
exit(0);
}
gThreadInfo = new ThreadInfo[gRunInfo.numThreads];
int threadNum;
for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
{
gThreadInfo[threadNum].fThreadNum = threadNum;
ThreadFuncs::startThread(threadMain, &gThreadInfo[threadNum]);
}
if (gRunInfo.numParses)
{
bool notDone;
while (true)
{
ThreadFuncs::Sleep(1000);
notDone = false;
for (threadNum = 0; threadNum < gRunInfo.numThreads; threadNum++) {
if (gThreadInfo[threadNum].fParses < gRunInfo.numParses)
notDone = true;
}
if (notDone == false) {
break;
}
}
}
else
{
//
// Loop, watching the heartbeat of the worker threads.
// Each second, display "+" when all threads have completed a parse
// display "." if some thread hasn't since previous "+"
//
unsigned long startTime = XMLPlatformUtils::getCurrentMillis();
int elapsedSeconds = 0;
while (gRunInfo.totalTime == 0 || gRunInfo.totalTime > elapsedSeconds) {
ThreadFuncs::Sleep(1000);
if (gRunInfo.quiet == false && gRunInfo.verbose == false) {
char c = '+';
for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++) {
if (gThreadInfo[threadNum].fHeartBeat == false) {
c = '.';
break;
}
}
fputc(c, stdout);
fflush(stdout);
if (c == '+')
for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
gThreadInfo[threadNum].fHeartBeat = false;
}
elapsedSeconds = (XMLPlatformUtils::getCurrentMillis() - startTime) / 1000;
}
}
//
// Time's up, we are done. (We only get here if this was a timed run)
// Tally up the total number of parses completed by each of the threads.
//
gRunInfo.stopNow = true; // set flag, which will cause worker threads to stop.
//
// Make sure all threads are done before terminate
//
for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++) {
while (gThreadInfo[threadNum].fInProgress == true) {
ThreadFuncs::Sleep(1000);
}
if (gRunInfo.verbose)
printf("Thread #%d: is finished.\n", threadNum);
}
//
// We are done! Count the number of parse and terminate the program
//
double totalParsesCompleted = 0;
for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
{
totalParsesCompleted += gThreadInfo[threadNum].fParses;
// printf("%f ", totalParsesCompleted);
}
if (gRunInfo.quiet == false) {
if (gRunInfo.numParses) {
printf("\n%8.0f total parses were completed.\n", totalParsesCompleted);
}
else {
double parsesPerMinute = totalParsesCompleted / (double(gRunInfo.totalTime) / double(60));
printf("\n%8.2f parses per minute.\n", parsesPerMinute);
}
}
// delete grammar pool and memory manager
if (gp) {
delete gp;
delete gpMemMgr;
}
XMLPlatformUtils::Terminate();
clearFileInfoMemory();
delete [] gThreadInfo;
printf("Test Run Successfully\n");
return 0;
}