source: icXML/icXML-devel/tests/src/ThreadTest/ThreadTest.cpp @ 2726

Last change on this file since 2726 was 2726, checked in by cameron, 6 years ago

Add original Xerces tests and samples directories

File size: 45.6 KB
Line 
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18/*
19 * $Id: ThreadTest.cpp 833057 2009-11-05 15:25:10Z borisk $
20 *
21 * @author Andy Heninger, IBM
22 */
23
24#if HAVE_CONFIG_H
25#       include <config.h>
26#endif
27
28#include <stdlib.h>
29#include <stdio.h>
30#include <string.h>
31#include <ctype.h>
32#include <xercesc/parsers/SAXParser.hpp>
33#include <xercesc/parsers/XercesDOMParser.hpp>
34#include <xercesc/util/PlatformUtils.hpp>
35#include <xercesc/sax/HandlerBase.hpp>
36#include <xercesc/framework/MemBufInputSource.hpp>
37
38#include <xercesc/sax2/SAX2XMLReader.hpp>
39#include <xercesc/sax2/XMLReaderFactory.hpp>
40#include <xercesc/sax2/Attributes.hpp>
41#include <xercesc/sax2/DefaultHandler.hpp>
42
43#include <xercesc/dom/DOM.hpp>
44
45#include <xercesc/framework/StdOutFormatTarget.hpp>
46#include <xercesc/framework/XMLGrammarPoolImpl.hpp>
47#include <xercesc/internal/MemoryManagerImpl.hpp>
48#include <xercesc/util/OutOfMemoryException.hpp>
49
50void clearFileInfoMemory();
51
52#ifdef HAVE_PTHREAD
53#include <pthread.h>
54#include <unistd.h>
55#include <errno.h>
56
57
58//------------------------------------------------------------------------------
59//
60//   UNIX specific code for starting threads
61//
62//------------------------------------------------------------------------------
63
64extern "C" {
65
66typedef void (*ThreadFunc)(void *);
67typedef void *(*pthreadfunc)(void *);
68
69class ThreadFuncs           // This class isolates OS dependent threading
70{                           //   functions from the rest of ThreadTest program.
71public:
72    static void Sleep(int millis);
73    static void startThread(ThreadFunc, void *param);
74};
75
76void ThreadFuncs::Sleep(int millis)
77{
78   int seconds = millis/1000;
79   if (seconds <= 0) seconds = 1;
80#if defined(SOLARIS)
81   // somehow the sleep hangs on Solaris
82   // so ignore the call
83#else
84   ::sleep(seconds);
85#endif
86}
87
88
89void ThreadFuncs::startThread(ThreadFunc func, void *param)
90{
91    int x;
92
93    pthread_t tId;
94    //thread_t tId;
95#if defined(_HP_UX) && defined(XML_USE_DCE)
96    x = pthread_create( &tId, pthread_attr_default,  (pthreadfunc)func,  param);
97#else
98    pthread_attr_t attr;
99    pthread_attr_init(&attr);
100    x = pthread_create( &tId, &attr,  (pthreadfunc)func,  param);
101#endif
102    if (x == -1)
103    {
104        fprintf(stderr, "Error starting thread.  Errno = %d\n", errno);
105        clearFileInfoMemory();
106        exit(-1);
107    }
108
109}
110
111} // end of extern "C"
112
113#else
114
115//------------------------------------------------------------------------------
116//
117//   Windows specific code for starting threads
118//
119//------------------------------------------------------------------------------
120
121#include <Windows.h>
122#include <process.h>
123
124typedef DWORD (WINAPI *ThreadFunc)(void *);
125
126class ThreadFuncs           // This class isolates OS dependent threading
127{                           //   functions from the rest of ThreadTest program.
128public:
129    static void Sleep(int millis) {::Sleep(millis);};
130    static void startThread(ThreadFunc, void *param);
131};
132
133void ThreadFuncs::startThread(ThreadFunc func, void *param)
134{
135    HANDLE  tHandle;
136    DWORD   threadID;
137
138    tHandle = CreateThread(0,          // Security Attributes,
139                           0x10000,    // Stack Size,
140                           func,       // Starting Address.
141                           param,      // Parmeters
142                           0,          // Creation Flags,
143                           &threadID); // Thread ID (Can not be null on 95/98)
144
145    if (tHandle == 0)
146    {
147        fprintf(stderr, "Error starting thread.  Errno = %d\n", errno);
148        clearFileInfoMemory();
149        exit(-1);
150    }
151
152    // Set the priority of the working threads low, so that the UI of the running system will
153    //   remain responsive.
154    SetThreadPriority(tHandle, THREAD_PRIORITY_IDLE);
155}
156
157#endif
158
159
160//------------------------------------------------------------------------------
161//
162//  struct InFileInfo   One of these structs will be set up for each file listed
163//                      on the command line.  Once set, the data is unchanging
164//                      and can safely be referenced by the test threads without
165//                      use of synchronization.
166//
167//------------------------------------------------------------------------------
168struct InFileInfo
169{
170    char    *fileName;
171    XMLCh   *uFileName;      // When doing an in-memory parse, avoid transcoding file name
172                             //    each time through.
173    char    *fileContent;    // If doing an in-memory parse, this field points
174                             //   to an allocated string containing the entire file
175                             //   contents.  Otherwise it's 0.
176    size_t  fileSize;        // The file length.  Only initialized when doing
177                             //   an in-memory test.
178    int     checkSum;        // The XML checksum.  Set up by the main thread for
179                             //   each file before the worker threads are started.
180};
181
182
183//------------------------------------------------------------------------------
184//
185//  struct threadInfo  Holds information specific to an individual thread.
186//                     One of these is set up for each thread in the test.
187//                     The main program monitors the threads by looking
188//                     at the status stored in these structs.
189//
190//------------------------------------------------------------------------------
191struct ThreadInfo
192{
193    bool            fHeartBeat;   // Set true by the thread each time it finishes
194                                  //   parsing a file.
195    bool            fInProgress;  // Set to false by the thread when parse in progress
196    unsigned int    fParses;      // Number of parses completed.
197    int             fThreadNum;   // Identifying number for this thread.
198    ThreadInfo() {
199        fHeartBeat = false;
200        fInProgress = false;
201        fParses = 0;
202        fThreadNum = -1;
203    }
204};
205
206
207XERCES_CPP_NAMESPACE_USE
208//------------------------------------------------------------------------------
209//
210//  struct runInfo     Holds the info extracted from the command line.
211//                     There is only one of these, and it is static, and
212//                     unchanging once the command line has been parsed.
213//                     During the test, the threads will access this info without
214//                     any synchronization.
215//
216//------------------------------------------------------------------------------
217const int MAXINFILES = 25;
218struct RunInfo
219{
220    bool                            doGrammarCaching;
221    bool                            quiet;
222    bool                            verbose;
223    bool                            stopNow;
224    bool                            dom;
225    bool                            sax;
226    bool                            reuseParser;
227    bool                            inMemory;
228    bool                            dumpOnErr;
229    bool                            doSchema;
230    bool                            schemaFullChecking;
231    bool                            doNamespaces;
232    bool                            doInitialParse;
233    bool                            doNamespacePrefixes;  // SAX2
234    SAXParser::ValSchemes           valScheme;
235    int                             numThreads;
236    int                             totalTime;
237    int                             numInputFiles;
238    unsigned int                    numParses;
239    InFileInfo                      files[MAXINFILES];
240};
241
242//
243//------------------------------------------------------------------------------
244//
245//  Global Data
246//
247//------------------------------------------------------------------------------
248RunInfo         gRunInfo;
249ThreadInfo      *gThreadInfo;
250
251/** Grammar caching thread testing */
252MemoryManager*  gpMemMgr = 0;
253XMLGrammarPool* gp = 0;
254
255#ifdef HELPER_ROUTINES
256// Routines which maybe helpful for debugging
257static void printString(const XMLCh *str)
258{
259    char *s = XMLString::transcode(str);
260    printf("%s", s);
261    delete s;
262}
263
264#define CHARS_PER_LINE           40
265#define BYTES_PER_LINE           16
266
267/*
268 * DumpLine: Dump out a buffer (address and length) to stderr.
269 */
270static void DumpLine(char* address, int length) {
271    int i, c, charCount=0;
272    if (length % 4) length += 4;
273    fprintf(stderr, "%8.8p: ", address);
274    for (i=0; i < length/4; ++i) {
275        fprintf(stderr, "%8.8X ", ((int*)address)[i]);
276        charCount += 9;
277    }
278    for (i=charCount; i < CHARS_PER_LINE; ++i) {
279        putc(' ', stderr);
280    }
281    fprintf(stderr, "| ");
282    for (i=0; i < length; ++i) {
283        c = address[i];
284        c = (isprint(c) ? c : '.');
285        fprintf(stderr, "%c", c);
286    }
287    fprintf(stderr, "\n");
288}
289
290/*
291 * dump: dump out a buffer (address and length) to stderr by dumping out
292 *       a line at a time (DumpLine), until the buffer is written out.
293 */
294
295static void dump(void* generalAddress, int length) {
296    int curr = 0;
297    char* address = (char*) generalAddress;
298    while (&address[curr] < &address[length-BYTES_PER_LINE]) {
299        DumpLine(&address[curr], BYTES_PER_LINE);
300        curr += BYTES_PER_LINE;
301    }
302    if (curr < length) {
303        DumpLine(&address[curr], length-curr);
304    }
305    fflush(stderr);
306}
307#endif
308
309//------------------------------------------------------------------------------
310//
311//  class ThreadParser   Bundles together a SAX parser and the SAX handlers
312//                       and contains the API that the rest of this test
313//                       program uses for creating parsers and doing parsing.
314//
315//                       Multiple instances of this class can operate concurrently
316//                       in different threads.
317//
318//-------------------------------------------------------------------------------
319
320class ThreadParser
321{
322public:
323    class SAXHandler;
324    class SAX2Handler;
325    SAXHandler*     fSAXHandler;
326    SAX2Handler*    fSAX2Handler;
327    ErrorHandler*   fDOMErrorHandler;
328
329    //  This is the API used by the rest of the test program
330    ThreadParser();
331    ~ThreadParser();
332
333    int parse(int fileNum);           // Parse the specified file.  fileNum is an index
334                                      //   into the gRunInfo.files array.
335                                      //  return the XML checksum, or
336                                      //  0 if a parse error occurred.
337
338    int getCheckSum() {
339        return fCheckSum;
340    }
341
342    int reCheck();                    // Try to compute the checksum again.
343                                      //  for DOM, re-walk the tree.
344                                      //  for SAX, can't do, just return previous value.
345
346    void domPrint();                  //   including any children.  Default (no param)
347                                       //   version dumps the entire document.
348    void  addToCheckSum(const XMLCh *chars, XMLSize_t len=(XMLSize_t)-1);
349
350    //  These are the SAX call-back functions that this class implements. Can be used
351    //  for SAX and SAX2.
352    void characters(const XMLCh* const chars, const XMLSize_t length) {
353        addToCheckSum(chars, length);
354    }
355
356    void ignorableWhitespace(const XMLCh* const chars, const XMLSize_t length) {
357        addToCheckSum(chars, length);
358    }
359
360    void resetDocument() {
361    }
362
363    void warning(const SAXParseException& exc)     {
364        fprintf(stderr, "*** Warning ");
365        fflush(stderr);
366        throw exc;
367    }
368
369    void error(const SAXParseException& exc)       {
370        fprintf(stderr, "*** Error ");
371        fflush(stderr);
372        throw exc;
373    }
374
375    void fatalError(const SAXParseException& exc)  {
376        fprintf(stderr, "***** Fatal error ");
377        fflush(stderr);
378        throw exc;
379    }
380
381    // Create a nested class that can inherit from HandlerBase
382    // for SAX startElement callbacks.
383    class SAXHandler :  public HandlerBase
384    {
385    public:
386        ThreadParser* SAXInstance;
387
388        void startElement(const XMLCh* const name, AttributeList& attributes);
389    };
390
391    // Create a nested class that can inherit from DefaultHandler
392    // for SAX2 startElement callbacks.
393    class SAX2Handler :  public DefaultHandler
394    {
395    public:
396        ThreadParser* SAX2Instance;
397
398        void startElement(const XMLCh* const uri,
399                          const XMLCh* const localname,
400                          const XMLCh* const qname,
401                          const Attributes& attributes);
402    };
403
404private:
405    int                                             fCheckSum;
406    SAXParser*                                      fSAXParser;
407    SAX2XMLReader*                                  fSAX2Parser;
408    XercesDOMParser*                                fXercesDOMParser;
409    XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *    fDoc;
410
411
412    ThreadParser(const ThreadParser &); // No copy constructor
413    const ThreadParser & operator =(const ThreadParser &); // No assignment.
414
415    void  domCheckSum(const DOMNode *);
416};
417
418//
419//  ThreadParser constructor.  Invoked by the threads of the test program
420//                              to create parsers.
421//
422ThreadParser::ThreadParser()
423{
424    fSAXParser       = 0;
425    fSAX2Parser      = 0;
426    fXercesDOMParser = 0;
427    fSAXHandler      = 0;
428    fSAX2Handler     = 0;
429    fDOMErrorHandler = 0;
430    fDoc             = 0;
431    fCheckSum        = 0;
432
433    if (gRunInfo.dom) {
434        // Set up to use a DOM parser
435        /** Grammar caching thread testing */
436        if (gp) {
437            fXercesDOMParser = new XercesDOMParser(0, XMLPlatformUtils::fgMemoryManager, gp);
438            fXercesDOMParser->cacheGrammarFromParse(true);
439            fXercesDOMParser->useCachedGrammarInParse(true);
440        }
441        else {
442            fXercesDOMParser = new XercesDOMParser;
443        }
444        switch (gRunInfo.valScheme) {
445            case SAXParser::Val_Never:
446                fXercesDOMParser->setValidationScheme(XercesDOMParser::Val_Never);
447                break;
448            case SAXParser::Val_Auto:
449                fXercesDOMParser->setValidationScheme(XercesDOMParser::Val_Auto);
450                break;
451            default: //SAXParser::Val_Always:
452                fXercesDOMParser->setValidationScheme(XercesDOMParser::Val_Always);
453                break;
454        }
455        fXercesDOMParser->setDoSchema(gRunInfo.doSchema);
456        fXercesDOMParser->setHandleMultipleImports (true);
457        fXercesDOMParser->setValidationSchemaFullChecking(gRunInfo.schemaFullChecking);
458        fXercesDOMParser->setDoNamespaces(gRunInfo.doNamespaces);
459        fDOMErrorHandler = (ErrorHandler*) new HandlerBase();
460        fXercesDOMParser->setErrorHandler(fDOMErrorHandler);
461    }
462
463    else if (gRunInfo.sax) {
464        // Set up to use a SAX1 parser.
465        /** Grammar caching thread testing */
466        if (gp) {
467            fSAXParser = new SAXParser(0, XMLPlatformUtils::fgMemoryManager, gp);
468            fSAXParser->cacheGrammarFromParse(true);
469            fSAXParser->useCachedGrammarInParse(true);
470        }
471        else {
472            fSAXParser = new SAXParser();
473        }
474        fSAXParser->setValidationScheme(gRunInfo.valScheme);
475        fSAXParser->setDoSchema(gRunInfo.doSchema);
476        fSAXParser->setHandleMultipleImports (true);
477        fSAXParser->setValidationSchemaFullChecking(gRunInfo.schemaFullChecking);
478        fSAXParser->setDoNamespaces(gRunInfo.doNamespaces);
479        fSAXHandler = new ThreadParser::SAXHandler();
480        fSAXHandler->SAXInstance = this;
481        fSAXParser->setDocumentHandler(fSAXHandler);
482        fSAXParser->setErrorHandler(fSAXHandler);
483    }
484
485    else {
486        // Set up to use a SAX2 parser.
487        /** Grammar caching thread testing */
488        if (gp) {
489            fSAX2Parser = XMLReaderFactory::createXMLReader(gpMemMgr, gp);
490            fSAX2Parser->setFeature(XMLUni::fgXercesCacheGrammarFromParse,true);
491            fSAX2Parser->setFeature(XMLUni::fgXercesUseCachedGrammarInParse,true);
492        }
493        else {
494            fSAX2Parser = XMLReaderFactory::createXMLReader();
495        }
496
497        fSAX2Parser->setFeature(XMLUni::fgSAX2CoreNameSpaces,(gRunInfo.doNamespaces));
498        fSAX2Parser->setFeature(XMLUni::fgXercesSchema,(gRunInfo.doSchema));
499        fSAX2Parser->setFeature(XMLUni::fgXercesHandleMultipleImports, true);
500        fSAX2Parser->setFeature(XMLUni::fgXercesSchemaFullChecking,(gRunInfo.schemaFullChecking));
501
502        switch (gRunInfo.valScheme) {
503            case SAXParser::Val_Never:
504                fSAX2Parser->setFeature(XMLUni::fgSAX2CoreValidation, false);
505                break;
506            case SAXParser::Val_Auto:
507                fSAX2Parser->setFeature(XMLUni::fgSAX2CoreValidation, true);
508                fSAX2Parser->setFeature(XMLUni::fgXercesDynamic, true);
509                break;
510            default: //SAXParser::Val_Always:
511                fSAX2Parser->setFeature(XMLUni::fgSAX2CoreValidation, true);
512                fSAX2Parser->setFeature(XMLUni::fgXercesDynamic, false);
513                break;
514        }
515
516        fSAX2Parser->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes,(gRunInfo.doNamespacePrefixes));
517        fSAX2Handler = new ThreadParser::SAX2Handler();
518        fSAX2Handler->SAX2Instance = this;
519        fSAX2Parser->setContentHandler(fSAX2Handler);
520        fSAX2Parser->setErrorHandler(fSAX2Handler);
521    }
522}
523
524ThreadParser::~ThreadParser()
525{
526     delete fSAXParser;
527     delete fSAX2Parser;
528     delete fXercesDOMParser;
529     delete fSAXHandler;
530     delete fSAX2Handler;
531     delete fDOMErrorHandler;
532}
533
534//------------------------------------------------------------------------
535//
536//  parse   - This is the method that is invoked by the rest of
537//            the test program to actually parse an XML file.
538//
539//------------------------------------------------------------------------
540int ThreadParser::parse(int fileNum)
541{
542    MemBufInputSource *mbis = 0;
543    InFileInfo        *fInfo = &gRunInfo.files[fileNum];
544    bool              errors = false;
545
546    fCheckSum = 0;
547
548    if (gRunInfo.inMemory) {
549        mbis = new  MemBufInputSource((const XMLByte *) fInfo->fileContent,
550                                       fInfo->fileSize,
551                                       fInfo->uFileName,
552                                       false);
553    }
554
555    try
556    {
557        if (gRunInfo.dom) {
558            // Do a DOM parse
559            fXercesDOMParser->resetDocumentPool();
560            if (gRunInfo.inMemory)
561                fXercesDOMParser->parse(*mbis);
562            else
563                fXercesDOMParser->parse(fInfo->fileName);
564            fDoc = fXercesDOMParser->getDocument();
565            domCheckSum(fDoc);
566        }
567        else if (gRunInfo.sax) {
568            // Do a SAX1 parse
569            if (gRunInfo.inMemory)
570                fSAXParser->parse(*mbis);
571            else
572                fSAXParser->parse(fInfo->fileName);
573        }
574        else {
575            // Do a SAX2 parse
576            if (gRunInfo.inMemory)
577                fSAX2Parser->parse(*mbis);
578            else
579                fSAX2Parser->parse(fInfo->fileName);
580        }
581    }
582    catch (const OutOfMemoryException&)
583    {
584            fprintf(stderr, " during parsing: %s\n OutOfMemoryException.\n", fInfo->fileName);
585            errors = true;
586    }
587    catch (const XMLException& e)
588    {
589        char *exceptionMessage = XMLString::transcode(e.getMessage());
590        fprintf(stderr, " during parsing: %s\n Exception message is: %s\n",
591            fInfo->fileName, exceptionMessage);
592        XMLString::release(&exceptionMessage);
593        errors = true;
594    }
595    catch (const DOMException& toCatch)
596    {
597        fprintf(stderr, " during parsing: %s\n DOMException code is: %i\n",
598            fInfo->fileName, toCatch.code);
599        errors = true;
600    }
601    catch (const SAXParseException& e)
602    {
603        char *exceptionMessage = XMLString::transcode(e.getMessage());
604        fprintf(stderr, " during parsing: %s\n Exception message is: %s\n",
605            fInfo->fileName, exceptionMessage);
606        XMLString::release(&exceptionMessage);
607        errors = true;
608    }
609    catch (...)
610    {
611        fprintf(stderr, "Unexpected exception during parsing\n");
612        errors = true;
613    }
614
615    delete mbis;
616    if (errors) {
617        fflush(stderr);
618        return 0;  // if errors occurred, return zero as if checksum = 0;
619    }
620    return fCheckSum;
621}
622
623
624//
625//  addToCheckSum - private function, used within ThreadParser in
626//                  computing the checksum of the XML file.
627//
628//                  Unichar Strings to be added to the checksum
629//                  can either be null terminated (omit len param, which
630//                  will then default to -1), or provide an explicit
631//                  length.
632//
633void ThreadParser::addToCheckSum(const XMLCh *chars, XMLSize_t len)
634{
635    if (len == (XMLSize_t)-1)
636    {
637        // Null terminated string.
638        while (*chars != 0)
639        {
640            fCheckSum = fCheckSum*5 + *chars;
641            chars++;
642        }
643    }
644    else
645    {
646        // String with character count.
647        XMLSize_t i;
648        for (i=0; i<len; i++)
649            fCheckSum = fCheckSum*5 + chars[i];
650    }
651}
652
653
654//
655// startElement - our SAX handler callback function for startElement.
656//                Update the document checksum with the element name
657//                and any attribute names and values.
658//
659 void ThreadParser::SAXHandler::startElement(const XMLCh *const name, AttributeList &attributes)
660{
661    SAXInstance->addToCheckSum(name);
662    XMLSize_t n = attributes.getLength();
663    XMLSize_t i;
664    for (i=0; i<n; i++)
665    {
666        const XMLCh *attNam = attributes.getName(i);
667        SAXInstance->addToCheckSum(attNam);
668        const XMLCh *attVal = attributes.getValue(i);
669        SAXInstance->addToCheckSum(attVal);
670    }
671}
672
673//
674// startElement - our SAX2 handler callback function for startElement.
675//                Update the document checksum with the element name
676//                and any attribute names and values.
677//
678
679void ThreadParser::SAX2Handler::startElement(const XMLCh *const /*uri*/,
680                              const XMLCh *const localname,
681                              const XMLCh *const /*qname*/,
682                              const Attributes& attributes)
683{
684    SAX2Instance->addToCheckSum(localname);
685
686    XMLSize_t n = attributes.getLength();
687    XMLSize_t i;
688    for (i=0; i<n; i++)
689    {
690        const XMLCh *attNam = attributes.getQName(i);
691        SAX2Instance->addToCheckSum(attNam);
692        const XMLCh *attVal = attributes.getValue(i);
693        SAX2Instance->addToCheckSum(attVal);
694    }
695}
696
697//
698// domCheckSum  -  Compute the check sum for a DOM node.
699//                 Works recursively - initially called with a document node.
700//
701void ThreadParser::domCheckSum(const DOMNode *node)
702{
703    const XMLCh        *s;
704    DOMNode          *child;
705    DOMNamedNodeMap  *attributes;
706
707    switch (node->getNodeType() )
708    {
709    case DOMNode::ELEMENT_NODE:
710        {
711            s = node->getNodeName();   // the element name
712
713            attributes = node->getAttributes();  // Element's attributes
714            XMLSize_t numAttributes = attributes->getLength();
715            XMLSize_t i;
716            for (i=0; i<numAttributes; i++)
717                domCheckSum(attributes->item(i));
718
719            addToCheckSum(s);          // Content and Children
720            for (child=node->getFirstChild(); child!=0; child=child->getNextSibling())
721                domCheckSum(child);
722
723            break;
724        }
725
726    case DOMNode::ATTRIBUTE_NODE:
727        {
728            s = node->getNodeName();  // The attribute name
729            addToCheckSum(s);
730            s = node->getNodeValue();  // The attribute value
731            if (s != 0)
732                addToCheckSum(s);
733            break;
734        }
735
736    case DOMNode::TEXT_NODE:
737    case DOMNode::CDATA_SECTION_NODE:
738        {
739            s = node->getNodeValue();
740            addToCheckSum(s);
741            break;
742        }
743
744    case DOMNode::ENTITY_REFERENCE_NODE:
745    case DOMNode::DOCUMENT_NODE:
746        {
747            // For entity references and the document, nothing is dirctly
748            //  added to the checksum, but we do want to process the chidren nodes.
749            //
750            for (child=node->getFirstChild(); child!=0; child=child->getNextSibling())
751                domCheckSum(child);
752            break;
753        }
754    }
755}
756
757
758//
759// Recompute the checksum.  Meaningful only for DOM, will tell us whether
760//  a failure is transient, or whether the DOM data is permanently corrupted.
761//
762int ThreadParser::reCheck()
763{
764    if (gRunInfo.dom) {
765        fCheckSum = 0;
766        domCheckSum(fDoc);
767    }
768    return fCheckSum;
769}
770
771//
772// domPrint  -  Dump the contents of a DOM node.
773//              For debugging failures, when all else fails.
774//                 Works recursively - initially called with a document node.
775//
776void ThreadParser::domPrint()
777{
778    printf("Begin DOMPrint ...\n");
779    if (gRunInfo.dom)
780    {
781        try
782        {
783            XMLCh tempStr[100];
784            XMLString::transcode("LS", tempStr, 99);
785            DOMImplementation *impl          = DOMImplementationRegistry::getDOMImplementation(tempStr);
786            DOMLSSerializer   *theSerializer = ((DOMImplementationLS*)impl)->createLSSerializer();
787            DOMLSOutput       *theOutput     = ((DOMImplementationLS*)impl)->createLSOutput();
788            XMLFormatTarget   *myFormTarget  = new StdOutFormatTarget();
789            theOutput->setByteStream(myFormTarget);
790            DOMNode           *doc           = fXercesDOMParser->getDocument();
791            theSerializer->write(doc,theOutput);
792            delete myFormTarget;
793            theSerializer->release();
794            theOutput->release();
795        }
796        catch (...)
797        {
798            // do nothing
799        }
800    }
801    printf("End DOMPrint\n");
802}
803
804//----------------------------------------------------------------------
805//
806//   parseCommandLine   Read through the command line, and save all
807//                      of the options in the gRunInfo struct.
808//
809//                      Display the usage message if the command line
810//                      is no good.
811//
812//                      Probably ought to be a member function of RunInfo.
813//
814//----------------------------------------------------------------------
815
816void parseCommandLine(int argc, char **argv)
817{
818    gRunInfo.doGrammarCaching = false;
819    gRunInfo.quiet = false;               // Set up defaults for run.
820    gRunInfo.verbose = false;
821    gRunInfo.stopNow = false;
822    gRunInfo.dom = false;
823    gRunInfo.sax = true;
824    gRunInfo.reuseParser = false;
825    gRunInfo.inMemory = false;
826    gRunInfo.dumpOnErr = false;
827    gRunInfo.doSchema = false;
828    gRunInfo.schemaFullChecking = false;
829    gRunInfo.doNamespaces = false;
830    gRunInfo.doInitialParse = false;
831    gRunInfo.doNamespacePrefixes = false;
832
833    gRunInfo.valScheme = SAXParser::Val_Auto;
834    gRunInfo.numThreads = 2;
835    gRunInfo.totalTime = 0;
836    gRunInfo.numInputFiles = 0;
837    gRunInfo.numParses = 0;
838
839    try             // Use exceptions for command line syntax errors.
840    {
841        int argnum = 1;
842        while (argnum < argc) {
843            if (strcmp(argv[argnum], "-quiet") == 0)
844                gRunInfo.quiet = true;
845            else if (strcmp(argv[argnum], "-verbose") == 0)
846                gRunInfo.verbose = true;
847            else if (strncmp(argv[argnum], "-v=", 3) == 0) {
848                const char* const parm = &argv[argnum][3];
849                if (!strcmp(parm, "never"))
850                    gRunInfo.valScheme = SAXParser::Val_Never;
851                else if (!strcmp(parm, "auto"))
852                    gRunInfo.valScheme = SAXParser::Val_Auto;
853                else if (!strcmp(parm, "always"))
854                    gRunInfo.valScheme = SAXParser::Val_Always;
855                else {
856                    fprintf(stderr, "Unrecognized -v option \"%s\"\n", parm);
857                    throw 1;
858                }
859            }
860            else if (strcmp(argv[argnum], "-v") == 0) {
861                fprintf(stderr, "Please note the -v option has been changed to -v=[always | never | auto]\n");
862                fprintf(stderr, "ThreadTest will continue with -v=always\n");
863                gRunInfo.valScheme = SAXParser::Val_Always;
864            }
865            else if (strcmp(argv[argnum], "-s") == 0)
866                gRunInfo.doSchema = true;
867            else if (strcmp(argv[argnum], "-f") == 0)
868                gRunInfo.schemaFullChecking = true;
869            else if (strcmp(argv[argnum], "-n") == 0)
870                gRunInfo.doNamespaces = true;
871            else if (strcmp(argv[argnum], "-p") == 0)
872                gRunInfo.doNamespacePrefixes = true;
873            else if (!strncmp(argv[argnum], "-parser=", 8)) {
874                const char* const parm = &argv[argnum][8];
875                if (!strcmp(parm, "dom")) {
876                    gRunInfo.dom = true;
877                    gRunInfo.sax = false;
878                }
879                else if (!strcmp(parm, "sax")) {
880                    gRunInfo.dom = false;
881                    gRunInfo.sax = true;
882                }
883                else if (!strcmp(parm, "sax2")) {
884                    gRunInfo.dom = false;
885                    gRunInfo.sax = false;
886                }
887                else {
888                    fprintf(stderr, "Unrecognized -parser option \"%s\"\n", parm);
889                    throw 1;
890                }
891            }
892            else if (strcmp(argv[argnum], "-init") == 0)
893                gRunInfo.doInitialParse = true;
894            else if (strcmp(argv[argnum], "-reuse") == 0)
895                gRunInfo.reuseParser = true;
896            else if (strcmp(argv[argnum], "-dump") == 0)
897                gRunInfo.dumpOnErr = true;
898            else if (strcmp(argv[argnum], "-mem") == 0)
899                gRunInfo.inMemory = true;
900            else if (strcmp(argv[argnum], "-threads") == 0) {
901                ++argnum;
902                if (argnum >= argc) {
903                    fprintf(stderr, "Invalid -threads option (missing # of threads)\n");
904                    throw 1;
905                }
906                gRunInfo.numThreads = atoi(argv[argnum]);
907                if (gRunInfo.numThreads < 0) {
908                    fprintf(stderr, "Invalid -threads option (negative # of threads)\n");
909                    throw 1;
910                }
911            }
912            else if (strcmp(argv[argnum], "-time") == 0) {
913                ++argnum;
914                if (argnum >= argc) {
915                    fprintf(stderr, "Invalid -time option (missing time value)\n");
916                    throw 1;
917                }
918                gRunInfo.totalTime = atoi(argv[argnum]);
919                if (gRunInfo.totalTime < 1) {
920                    fprintf(stderr, "Invalid -time option (time value < 1)\n");
921                    throw 1;
922                }
923            }
924            else if (strcmp(argv[argnum], "-gc") == 0)
925                gRunInfo.doGrammarCaching = true;
926            else if (strcmp(argv[argnum], "-parses") == 0) {
927                ++argnum;
928                if (argnum >= argc) {
929                    fprintf(stderr, "Invalid -parses option (missing # of parses)\n");
930                    throw 1;
931                }
932                int temp = atoi(argv[argnum]);
933                if (temp < 0) {
934                    fprintf(stderr, "Invalid -parses option (negative # of parses)\n");
935                    throw 1;
936                }
937                gRunInfo.numParses = temp;
938            }
939            else  if (argv[argnum][0] == '-') {
940                fprintf(stderr, "Unrecognized command line option.  Scanning \"%s\"\n",
941                    argv[argnum]);
942                throw 1;
943            }
944            else {
945                gRunInfo.numInputFiles++;
946                if (gRunInfo.numInputFiles >= MAXINFILES) {
947                    fprintf(stderr, "Too many input files.  Limit is %d\n", MAXINFILES);
948                    throw 1;
949                }
950                gRunInfo.files[gRunInfo.numInputFiles-1].fileName = argv[argnum];
951            }
952            argnum++;
953        }
954
955        // We've made it through the command line.
956        // Verify that at least one input file to be parsed was specified.
957        if (gRunInfo.numInputFiles == 0) {
958            fprintf(stderr, "No input XML file specified on command line.\n");
959            throw 1;
960        };
961
962        if (gRunInfo.numParses && gRunInfo.totalTime) {
963            fprintf(stderr, "Both -parses nnn and -time nnn were specified. Ignoring -time nnn.\n");
964        }
965    }
966    catch (int)
967    {
968        fprintf(stderr, "usage:  ThreadTest [-v] [-threads nnn] [-time nnn] [-quiet] [-verbose] xmlfile...\n"
969            "     -v=xxx         Validation scheme [always | never | auto].  Default is AUTO.\n"
970            "     -n             Enable namespace processing. Defaults to off.\n"
971            "     -s             Enable schema processing. Defaults to off.\n"
972            "     -f             Enable full schema constraint checking. Defaults to off.\n"
973            "     -parser=xxx    Parser Type [dom | sax | sax2].  Default is SAX (SAX1).\n"
974            "     -p             Enable namespace prefixes. Defaults to off.\n"
975            "                    (Only used with -parser=sax2, ignored otherwise.)\n"
976            "     -quiet         Suppress periodic status display.\n"
977            "     -verbose       Display extra messages.\n"
978            "     -reuse         Retain and reuse parser.  Default creates new for each parse.\n"
979            "     -threads nnn   Number of threads.  Default is 2.\n"
980            "     -time nnn      Total time to run, in seconds.  Default is forever.\n"
981            "     -parses nnn    Run for nnn parses instead of time.  Default is to use time\n"
982            "     -dump          Dump DOM tree on error.\n"
983            "     -mem           Read files into memory once only, and parse them from there.\n"
984            "     -gc            Enable grammar caching (i.e. grammar cached and used in subsequent parses). Defaults to off.\n"
985            "     -init          Perform an initial parse of the file(s) before starting up the individual threads.\n\n"
986            );
987        exit(1);
988    }
989}
990
991
992//---------------------------------------------------------------------------
993//
994//   ReadFilesIntoMemory   For use when parsing from memory rather than
995//                          reading the files each time, here is the code that
996//                          reads the files into local memory buffers.
997//
998//                          This function is only called once, from the main
999//                          thread, before all of the worker threads are started.
1000//
1001//---------------------------------------------------------------------------
1002void ReadFilesIntoMemory()
1003{
1004    int     fileNum;
1005    FILE    *fileF;
1006    size_t  t;
1007
1008    if (gRunInfo.inMemory)
1009    {
1010        for (fileNum = 0; fileNum <gRunInfo.numInputFiles; fileNum++)
1011        {
1012            InFileInfo *fInfo = &gRunInfo.files[fileNum];
1013            fInfo->uFileName = XMLString::transcode(fInfo->fileName);
1014            fileF = fopen( fInfo->fileName, "rb" );
1015            if (fileF == 0) {
1016                fprintf(stderr, "Can not open file \"%s\".\n", fInfo->fileName);
1017                clearFileInfoMemory();
1018                exit(-1);
1019            }
1020            fseek(fileF, 0, SEEK_END);
1021            fInfo->fileSize = ftell(fileF);
1022            fseek(fileF, 0, SEEK_SET);
1023            fInfo->fileContent = new char[fInfo->fileSize + 1];
1024            t = fread(fInfo->fileContent, 1, fInfo->fileSize, fileF);
1025            if (t != fInfo->fileSize) {
1026                fprintf(stderr, "Error reading file \"%s\".\n", fInfo->fileName);
1027                clearFileInfoMemory();
1028                exit(-1);
1029            }
1030            fclose(fileF);
1031            fInfo->fileContent[fInfo->fileSize] = 0;
1032        }
1033    }
1034}
1035
1036void clearFileInfoMemory()
1037{
1038    int     fileNum;
1039
1040    if (gRunInfo.inMemory)
1041    {
1042        for (fileNum = 0; fileNum <gRunInfo.numInputFiles; fileNum++)
1043        {
1044            InFileInfo *fInfo = &gRunInfo.files[fileNum];
1045            XMLString::release(&fInfo->uFileName);
1046            delete [] fInfo->fileContent;
1047        }
1048    }
1049}
1050
1051
1052
1053//----------------------------------------------------------------------
1054//
1055//  threadMain   The main function for each of the swarm of test threads.
1056//               Run in an infinite loop, parsing each of the documents
1057//               given on the command line in turn.
1058//
1059//----------------------------------------------------------------------
1060
1061#ifdef HAVE_PTHREAD
1062extern "C" {
1063void threadMain (void *param)
1064#else
1065unsigned long WINAPI threadMain (void *param)
1066#endif
1067{
1068    ThreadInfo   *thInfo = (ThreadInfo *)param;
1069    ThreadParser *thParser = 0;
1070
1071    if (gRunInfo.verbose)
1072        printf("Thread #%d: starting\n", thInfo->fThreadNum);
1073
1074    int docNum = gRunInfo.numInputFiles;
1075
1076    //
1077    // Each time through this loop, one file will be parsed and its checksum
1078    // computed and compared with the precomputed value for that file.
1079    //
1080    while (gRunInfo.stopNow == false) {
1081        if (gRunInfo.numParses == 0 || thInfo->fParses < gRunInfo.numParses) {
1082            thInfo->fInProgress = true;
1083
1084            if (thParser == 0)
1085                thParser = new ThreadParser;
1086
1087            docNum++;
1088
1089            if (docNum >= gRunInfo.numInputFiles)
1090                docNum = 0;
1091
1092            InFileInfo *fInfo = &gRunInfo.files[docNum];
1093
1094            if (gRunInfo.verbose )
1095                printf("Thread #%d: parse %d starting file %s\n", thInfo->fThreadNum, thInfo->fParses, fInfo->fileName);
1096
1097            int checkSum = 0;
1098
1099            checkSum = thParser->parse(docNum);
1100
1101            // For the case where we skip the preparse we will have nothing to
1102            // compare the first parse's results to ... so if this looks like first
1103            // parse move the checkSum back into the gRunInfo data for this file.
1104
1105            if (gRunInfo.files[docNum].checkSum == 0) {
1106                gRunInfo.files[docNum].checkSum = checkSum;
1107            }
1108            else if (checkSum != gRunInfo.files[docNum].checkSum) {
1109                if (checkSum == 0) {
1110                    // parse returns 0 if there was an error so do this to get the real
1111                    // checksum value
1112                    checkSum = thParser->getCheckSum();
1113                }
1114                fprintf(stderr, "\nThread %d: Parse Check sum error on file  \"%s\" for parse # %d.  Expected %x,  got %x\n",
1115                    thInfo->fThreadNum, fInfo->fileName, thInfo->fParses, fInfo->checkSum, checkSum);
1116
1117                    double totalParsesCompleted = 0;
1118                for (int threadNum=0; threadNum < gRunInfo.numThreads; threadNum++) {
1119                    totalParsesCompleted += gThreadInfo[threadNum].fParses;
1120                }
1121                fprintf(stderr, "Total number of parses completed is %f.\n", totalParsesCompleted);
1122
1123                // Revisit - let the loop continue to run?
1124                int secondTryCheckSum = thParser->reCheck();
1125                fprintf(stderr, "   Retry checksum is %x\n", secondTryCheckSum);
1126                if (gRunInfo.dumpOnErr && gRunInfo.dom) {
1127                    thParser->domPrint();
1128                }
1129                fflush(stdout);
1130                fflush(stderr);
1131                clearFileInfoMemory();
1132                exit(-1);
1133            }
1134
1135            if (gRunInfo.reuseParser == false) {
1136                delete thParser;
1137                thParser = 0;
1138            }
1139
1140            thInfo->fHeartBeat = true;
1141            thInfo->fParses++;
1142            thInfo->fInProgress = false;
1143        }
1144        else {
1145            ThreadFuncs::Sleep(1000);
1146        }
1147    }
1148    delete thParser;
1149#ifdef HAVE_PTHREAD
1150        return;
1151}
1152#else
1153    return 0;
1154#endif
1155}
1156
1157
1158//----------------------------------------------------------------------
1159//
1160//   main
1161//
1162//----------------------------------------------------------------------
1163
1164int main (int argc, char **argv)
1165{
1166
1167
1168    parseCommandLine(argc, argv);
1169
1170    //
1171    // Initialize the XML system.
1172    //
1173    try
1174    {
1175         XMLPlatformUtils::Initialize();
1176    }
1177    catch (...)
1178    {
1179        fprintf(stderr, "Exception from XMLPlatfromUtils::Initialize.\n");
1180        return 1;
1181    }
1182
1183
1184    /** Grammar caching thread testing */
1185    // Initialize memory manger and grammar pool
1186    // set doInitialParse to true so that the first parse will cache the
1187    // grammar and it'll be used in subsequent parses
1188
1189    if (gRunInfo.doSchema == true && gRunInfo.doNamespaces == true && gRunInfo.doGrammarCaching == true) {
1190        gpMemMgr = new MemoryManagerImpl();
1191        gp = new XMLGrammarPoolImpl(gpMemMgr);
1192        gRunInfo.doInitialParse = true;
1193    }
1194
1195    //
1196    // If we will be parsing from memory, read each of the input files
1197    //  into memory now.
1198    //
1199    ReadFilesIntoMemory();
1200
1201    // Initialize checksums to zero so we can check first parse and if
1202    // zero then we need to move first parse's checksum into array. This
1203    // is for the cse where we skip the initial parse.
1204    for (int n = 0; n < gRunInfo.numInputFiles; n++)
1205    {
1206        gRunInfo.files[n].checkSum = 0;
1207    }
1208
1209    if (gRunInfo.doInitialParse)
1210    {
1211    //
1212    // While we are still single threaded, parse each of the documents
1213    // once, to check for errors, and to note the checksum.
1214    // Blow off the rest of the test if there are errors.
1215    //
1216        ThreadParser *mainParser = new ThreadParser;
1217        int     n;
1218        bool    errors = false;
1219        int     cksum;
1220
1221        for (n = 0; n < gRunInfo.numInputFiles; n++)
1222        {
1223            char *fileName = gRunInfo.files[n].fileName;
1224            if (gRunInfo.verbose)
1225                printf("%s checksum is ", fileName);
1226
1227            cksum = mainParser->parse(n);
1228
1229            if (cksum == 0) {
1230                fprintf(stderr, "An error occurred while initially parsing %s\n",
1231                    fileName);
1232                errors = true;
1233            };
1234
1235            gRunInfo.files[n].checkSum = cksum;
1236            if (gRunInfo.verbose )
1237                printf("%x\n", cksum);
1238            if (gRunInfo.dumpOnErr && errors && gRunInfo.dom) {
1239                mainParser->domPrint();
1240            }
1241
1242        }
1243        delete mainParser;
1244
1245        if (errors) {
1246            fprintf(stderr, "Quitting due to error incurred during initial parse\n");
1247            clearFileInfoMemory();
1248            return 1;
1249        }
1250    }
1251
1252    //
1253    //  Fire off the requested number of parallel threads
1254    //
1255
1256    if (gRunInfo.numThreads == 0) {
1257        clearFileInfoMemory();
1258        exit(0);
1259    }
1260
1261    gThreadInfo = new ThreadInfo[gRunInfo.numThreads];
1262
1263    int threadNum;
1264    for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
1265    {
1266        gThreadInfo[threadNum].fThreadNum = threadNum;
1267        ThreadFuncs::startThread(threadMain, &gThreadInfo[threadNum]);
1268    }
1269
1270    if (gRunInfo.numParses)
1271    {
1272        bool notDone;
1273        while (true)
1274        {
1275            ThreadFuncs::Sleep(1000);
1276            notDone = false;
1277
1278            for (threadNum = 0; threadNum < gRunInfo.numThreads; threadNum++) {
1279                if (gThreadInfo[threadNum].fParses < gRunInfo.numParses)
1280                    notDone = true;
1281            }
1282            if (notDone == false) {
1283                break;
1284            }
1285        }
1286    }
1287    else
1288    {
1289        //
1290        //  Loop, watching the heartbeat of the worker threads.
1291        //    Each second, display "+" when all threads have completed a parse
1292        //                 display "." if some thread hasn't since previous "+"
1293        //
1294
1295        unsigned long startTime = XMLPlatformUtils::getCurrentMillis();
1296        int elapsedSeconds = 0;
1297        while (gRunInfo.totalTime == 0 || gRunInfo.totalTime > elapsedSeconds) {
1298            ThreadFuncs::Sleep(1000);
1299            if (gRunInfo.quiet == false && gRunInfo.verbose == false) {
1300                char c = '+';
1301                for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++) {
1302                    if (gThreadInfo[threadNum].fHeartBeat == false) {
1303                        c = '.';
1304                        break;
1305                    }
1306                }
1307                fputc(c, stdout);
1308                fflush(stdout);
1309                if (c == '+')
1310                    for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
1311                        gThreadInfo[threadNum].fHeartBeat = false;
1312            }
1313            elapsedSeconds = (XMLPlatformUtils::getCurrentMillis() - startTime) / 1000;
1314        }
1315    }
1316
1317    //
1318    //  Time's up, we are done.  (We only get here if this was a timed run)
1319    //  Tally up the total number of parses completed by each of the threads.
1320    //
1321    gRunInfo.stopNow = true;      // set flag, which will cause worker threads to stop.
1322
1323    //
1324    //  Make sure all threads are done before terminate
1325    //
1326    for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++) {
1327        while (gThreadInfo[threadNum].fInProgress == true) {
1328            ThreadFuncs::Sleep(1000);
1329        }
1330        if (gRunInfo.verbose)
1331            printf("Thread #%d: is finished.\n", threadNum);
1332    }
1333
1334    //
1335    //  We are done!   Count the number of parse and terminate the program
1336    //
1337    double totalParsesCompleted = 0;
1338    for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
1339    {
1340        totalParsesCompleted += gThreadInfo[threadNum].fParses;
1341        // printf("%f   ", totalParsesCompleted);
1342    }
1343
1344    if (gRunInfo.quiet == false) {
1345        if (gRunInfo.numParses) {
1346            printf("\n%8.0f total parses were completed.\n", totalParsesCompleted);
1347        }
1348        else {
1349            double parsesPerMinute = totalParsesCompleted / (double(gRunInfo.totalTime) / double(60));
1350            printf("\n%8.2f parses per minute.\n", parsesPerMinute);
1351        }
1352    }
1353
1354    // delete grammar pool and memory manager
1355    if (gp) {
1356        delete gp;
1357        delete gpMemMgr;
1358    }
1359
1360    XMLPlatformUtils::Terminate();
1361
1362    clearFileInfoMemory();
1363
1364    delete [] gThreadInfo;
1365
1366    printf("Test Run Successfully\n");
1367
1368    return 0;
1369}
Note: See TracBrowser for help on using the repository browser.