]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - tools/src/tools/ecostest/common/eCosTest.cpp
Initial revision
[karo-tx-redboot.git] / tools / src / tools / ecostest / common / eCosTest.cpp
1 //####COPYRIGHTBEGIN####
2 //                                                                          
3 // ----------------------------------------------------------------------------
4 // Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
5 //
6 // This program is part of the eCos host tools.
7 //
8 // This program is free software; you can redistribute it and/or modify it 
9 // under the terms of the GNU General Public License as published by the Free 
10 // Software Foundation; either version 2 of the License, or (at your option) 
11 // any later version.
12 // 
13 // This program is distributed in the hope that it will be useful, but WITHOUT 
14 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
15 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
16 // more details.
17 // 
18 // You should have received a copy of the GNU General Public License along with
19 // this program; if not, write to the Free Software Foundation, Inc., 
20 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21 //
22 // ----------------------------------------------------------------------------
23 //                                                                          
24 //####COPYRIGHTEND####
25 //=================================================================
26 //
27 //        eCosTest.cpp
28 //
29 //        Test class
30 //
31 //=================================================================
32 //=================================================================
33 //#####DESCRIPTIONBEGIN####
34 //
35 // Author(s):     sdf
36 // Contributors:  sdf
37 // Date:          1999-04-01
38 // Description:   This class abstracts a test for use in the testing infrastructure
39 // Usage:
40 //
41 //####DESCRIPTIONEND####
42 ///////////////////////////////////////////////////////////////////////////////
43 #include "eCosStd.h"
44 #include "eCosTest.h"
45 #include "eCosTestPlatform.h"
46 #include "eCosTrace.h"
47 #include "TestResource.h"
48 #include "eCosTestUtils.h"
49 #include "eCosSocket.h"
50 #include "eCosSerial.h"
51 #include "eCosTestSerialFilter.h"
52 #include "eCosTestDownloadFilter.h"
53 #include "Properties.h"
54 #include "Subprocess.h"
55
56 #define WF(n) (n+50)/1000,((n+50)%1000)/100     // Present n as whole and fractional part.  Round to nearest least significant digit
57 #define WFS   _T("%u.%u")                           // The format string to output the above
58
59 LPCTSTR  const CeCosTest::arResultImage[1+CeCosTest::StatusTypeMax]=
60 {_T("NotStarted"), _T("NoResult"), _T("Inapplicable"), _T("Pass"), _T("DTimeout"), _T("Timeout"), _T("Cancelled"), _T("Fail"), _T("AssertFail"), _T("Unknown")};
61
62 CeCosTest *CeCosTest::pFirstInstance=0;
63 int CeCosTest::InstanceCount=0;
64
65 LPCTSTR  const CeCosTest::arServerStatusImage[1+CeCosTest::ServerStatusMax]={
66   _T("Busy"), _T("Ready"), _T("Can't run"), _T("Connection failed"), _T("Locked"), _T("Bad server status")};
67 LPCTSTR  CeCosTest::ExecutionParameters::arRequestImage [1+ExecutionParameters::RequestTypeMax]={
68   _T("Run"), _T("Query"), _T("Lock"), _T("Unlock"), _T("Stop"), _T("Bad request") };
69   
70 static bool CALLBACK IsCancelled(void *pThis)
71 {
72   return CeCosTest::Cancelled==((CeCosTest *)pThis)->Status();
73 }
74
75 // Ctors and dtors:
76 CeCosTest::CeCosTest(const ExecutionParameters &e, LPCTSTR pszExecutable,LPCTSTR pszTitle):
77   m_pspPipe(0),
78   m_nStrippedSize(0),
79   m_nFileSize(0),
80   m_bDownloading(false),
81   m_pSock(0),
82   m_ep(e),
83   m_strTitle(pszTitle),
84   m_Status(NotStarted),
85   m_nDownloadTime(0),
86   m_nTotalTime(0),
87   m_nMaxInactiveTime(0),
88   m_pResource(0),
89   m_psp(0)
90 {
91   
92   assert(e.Platform());
93   
94   SetExecutable (pszExecutable);
95   
96   TRACE(_T("%%%% Create test instance %08x count:=%d\n"),this,InstanceCount+1);  
97
98   // By recording the path now, we ensure processes are always run in the context in which the test instance
99   // is created (important for the ConfigTool to be able to call PrepareEnvironment).
100
101 #ifdef _WIN32
102   // for some reason _tgetenv() doesn't return the PATH set
103   // by PrepareEnvironment() so use GetEnvironmentVariable() instead
104   // JLD - 2000-06-09
105   String strPath;
106   int nSize=GetEnvironmentVariable(_T("PATH"), NULL, 0);
107   GetEnvironmentVariable(_T("PATH"), strPath.GetBuffer(nSize), nSize);
108   strPath.ReleaseBuffer();
109   m_strPath=strPath;
110 #else
111   LPCTSTR pszPath=_tgetenv(_T("PATH"));
112   if(pszPath){
113     m_strPath=pszPath;
114   }
115 #endif
116   
117   ENTERCRITICAL;
118   InstanceCount++;
119   m_pNextInstance=pFirstInstance;
120   if(m_pNextInstance){
121     m_pNextInstance->m_pPrevInstance=this;
122   } 
123   m_pPrevInstance=0;
124   pFirstInstance=this;
125   LEAVECRITICAL;
126   
127 }
128
129 CeCosTest::~CeCosTest()
130 {
131   for(int i=0;i<(signed)m_arpExecsp.size();i++){
132     delete (CSubprocess *)m_arpExecsp[i];
133   }
134   delete m_pspPipe;
135
136   TRACE(_T("%%%% Delete test instance %08x\n"),this);
137   Cancel();
138   CloseSocket();
139   if(m_pResource){
140     m_pResource->Release();
141     //delete m_pResource;
142     //m_pResource=0;
143   }
144   
145   VTRACE(_T("~CeCosTest(): EnterCritical and decrease instance count\n"));
146   ENTERCRITICAL;
147   InstanceCount--;
148   TRACE(_T("%%%% Destroy instance.  Instance count:=%d\n"),InstanceCount);
149   if(pFirstInstance==this){
150     pFirstInstance=m_pNextInstance;
151   }
152   if(m_pPrevInstance){
153     m_pPrevInstance->m_pNextInstance=m_pNextInstance;
154   }
155   if(m_pNextInstance){
156     m_pNextInstance->m_pPrevInstance=m_pPrevInstance;
157   }
158   LEAVECRITICAL;
159 }
160
161 bool CeCosTest::RunRemote (LPCTSTR pszRemoteHostPort)
162 {
163   bool rc=false;
164   TRACE(_T("RunRemote\n"));
165   m_strExecutionHostPort=pszRemoteHostPort;
166   m_Status=NotStarted;
167   
168   VTRACE(_T("RemoteThreadFunc()\n"));
169   
170   // Find a server.
171   ConnectForExecution();
172   if(Cancelled!=Status()){       
173     if(m_ep.Platform()->ServerSideGdb()){
174       // The executable is transmitted to the server for execution.
175       // Send file size
176       if(m_pSock->sendInteger(m_nFileSize,_T("file size"))&&m_nFileSize>0){
177         int nBufSize=MIN(10000,m_nFileSize);
178         Buffer b(nBufSize);
179         TRACE(_T("Sending [%d bytes]\n"), m_nFileSize);
180         int nToSend=m_nFileSize;
181         FILE *f1=_tfopen(m_strExecutable,_T("rb"));
182         if(0==f1){
183           Log(_T("Failed to open %s - %s\n"),(LPCTSTR)m_strExecutable,strerror(errno));
184         } else {
185           while (nToSend>0){
186             int nRead=fread( b.Data(), 1, nBufSize, f1);
187             if(nRead<=0){
188               Log(_T("Failure reading %s - %s\n"),(LPCTSTR)m_strExecutable,strerror(errno));
189               break;
190             }
191             if(!send( b.Data(), nRead, _T("executable"))){
192               Log(_T("Failure sending %s - %s\n"),(LPCTSTR)m_strExecutable,(LPCTSTR)m_pSock->SocketErrString());
193               break;
194             }
195             nToSend-=nRead;
196           }
197           fclose(f1);
198           f1=0;
199           if(nToSend>0){
200             TRACE(_T("done [%d bytes sent]\n"),m_nFileSize-nToSend);
201             Log(_T("Failed to transmit %s - %d/%d bytes sent\n"),(LPCTSTR)m_strExecutable,m_nFileSize-nToSend,m_nFileSize);
202           } else {
203             TRACE(_T("done\n"));
204             rc=true;
205           }
206         }
207         if(!recvResult(9*1000*60)){ // nine minutes
208           Log(_T("Failed to receive result from remote server\n"));
209           rc=false;
210         }
211         m_pSock->sendInteger(456); // send an ack [n'importe quoi]
212         CloseSocket();
213       }
214     } else {
215       // The server sets up a connection between port and tcp/ip socket, and gdb is run locally
216       // Big timeout here because we have to wait for the target to be reset
217       // We do this:
218       // do {
219       //     target ready indicator (0==fail, 1==ready, 2==fail and will retry)
220       //     any output so far
221       // } while (2==target ready indicator)
222       // read host:port
223       String strHostPort;
224       if(GetTargetReady(strHostPort)){
225         // Fix up a resource to represent permission to use the host:port we have been told about
226         CTestResource resource;
227         resource.SetTarget(m_ep.PlatformName());
228         resource.SetDownload(strHostPort,0);
229         m_pResource=&resource;
230         RunLocal();
231         m_pResource=0;
232         m_pSock->sendInteger(Status(),_T("Terminating ack"));
233         m_pSock->Close();
234         rc=true;
235       }
236     }
237   }
238   TRACE(_T("RemoteThreadFunc - exiting\n"));
239   return rc;
240 }
241
242 // Run the test locally
243 bool CeCosTest::RunLocal()
244 {
245   bool rc=false;
246
247   TRACE(_T("RunLocal %s\n"),(LPCTSTR)Executable());
248
249   if(!CeCosTestUtils::IsFile(Executable())){
250     Log(_T("Cannot run - %s is not a file\n"),(LPCTSTR)Executable());
251   } else if(0==m_pResource && 0==CTestResource::Count(m_ep)){
252     Log(_T("Cannot run a %s test\n"),(LPCTSTR)m_ep.PlatformName());
253   } else {
254     
255     m_Status=NotStarted;
256
257     TRACE(_T("LocalThreadFunc - target=%s\n"),(LPCTSTR)m_ep.PlatformName());
258     // Acquire a port (our caller may have done this for us)
259     VTRACE(_T("LocalThreadFunc():Trying to acquire a port\n"));
260     if(0==m_pResource){
261       for(;;){
262         m_pResource=CTestResource::GetResource(m_ep);
263         if(m_pResource||Cancelled==Status()){
264           break;
265         }
266         CeCosThreadUtils::Sleep(2000);
267         TRACE(_T("Waiting for a port\n"));
268       }
269     }
270     VTRACE(_T("\nPort acquired!\n"));
271     
272     if(Cancelled!=Status()){
273       // This means we have acquired a local port 
274       bool bTargetReady=false;
275       if(!m_pResource->HasReset()){
276         bTargetReady=true;
277       } else {
278         bTargetReady=(CResetAttributes::RESET_OK==m_pResource->Reset(0,this));
279       }
280       // we may proceed to execute the test
281       if(bTargetReady){
282         SetStatus(NotStarted);
283
284         if(NOTIMEOUT==m_ep.DownloadTimeout()){
285           // No elapsed timeout given - calculate from knowledge of executable size and baud rate
286           // 10 baud ~= 1 byte/sec, but we halve this to account for download in hex :-(
287           // We use a minimum of 30 seconds and double the calculated result for safety
288           // Note that the baud rate is generally unknown on the client side.
289           int nBaud=m_pResource->Baud();
290           if(0==nBaud){
291             CTestResource *pExecutionResource=CTestResource::Lookup(m_strExecutionHostPort);
292             if(pExecutionResource){
293               nBaud=pExecutionResource->Baud();
294             } 
295           }
296           if(0==nBaud){
297             nBaud=38400;
298           }
299
300           int nBytesPerSec=(nBaud/10)/2; // division by 2 assumes download in "ascii" (2 bytes/char)
301           m_ep.SetDownloadTimeout (1000*MAX(30,2*(m_nStrippedSize/nBytesPerSec)));
302           TRACE(_T("Estimated download time %d sec (%d bytes @ %d bytes/sec [%d baud])\n"),m_nStrippedSize/nBytesPerSec,m_nStrippedSize,nBytesPerSec,nBaud);
303         }
304
305         TRACE(_T("Active timeout=%d download timeout=%d\n"),m_ep.ActiveTimeout(), m_ep.DownloadTimeout());
306
307         GetInferiorCommands(m_arstrInferiorCmds);
308         String strInferior(m_ep.Platform()->Inferior());
309         strInferior.Replace(_T("%e"),CygPath(m_strExecutable),true);
310         RunInferior(strInferior);
311         rc=true;          
312       }
313     }
314     if(m_pResource){
315       m_pResource->Release();
316       m_pResource=0;
317     }
318     TRACE(_T("RunLocal - exiting\n"));
319   }
320
321   return rc;
322 }
323
324 void CeCosTest::Cancel ()
325 {
326   SetStatus(Cancelled);
327 }
328
329 CeCosTest::ServerStatus CeCosTest::Connect (LPCTSTR pszHostPort, CeCosSocket *&pSock, const ExecutionParameters &e,String &strInfo,Duration dTimeout)
330 {
331   // Find out whether this host is receptive
332   ServerStatus s=CONNECTION_FAILED;
333   pSock=new CeCosSocket(pszHostPort,dTimeout);
334   int nStatus;    
335   if(pSock->Ok() &&
336     pSock->sendString(e.Image(), _T("execution parameters")) &&
337     pSock->recvInteger(nStatus,_T("ready status")) &&
338     pSock->recvString(strInfo)){
339     s=(ServerStatus)MIN(nStatus,ServerStatusMax);
340   }
341   if(SERVER_READY!=s || ExecutionParameters::RUN!=e.Request()){
342     delete pSock;
343     pSock=0;
344   }
345   return s;
346 }
347
348 // Initiate a connection to hostName:nPort and acquire the ready status [retry until this is achieved]
349 // The socket (m_pSock) is left open.
350 // This function is either called with m_strExecutionHostPort already set to a desired server
351 // or else m_strExecutionHostPort empty (in which case the server is / dynamically)
352
353 void CeCosTest::ConnectForExecution ()
354 {
355   bool bSchedule=(0==m_strExecutionHostPort.size());
356   Duration nDelay=2000;
357   
358   m_pSock=0;
359   
360   bool *arbHostTried=0;
361   
362   while(Cancelled!=Status()){
363     StringArray arstrHostPort,arstrTries;
364     int nChoices;
365     
366     if(bSchedule){
367       if(!CTestResource::GetMatches(m_ep,arstrHostPort)){
368         Log(_T("Could not establish matches\n"));
369         continue;
370       }
371       nChoices=arstrHostPort.size();
372       if(nChoices>0){
373         TRACE(_T("ConnectForExecution: choices are:\n"));
374         for(int i=0;i<nChoices;i++){
375           TRACE(_T("\t%s\n"),(LPCTSTR)arstrHostPort[i]);
376         }
377       }
378     } else {
379       // Server has already been picked by caller
380       nChoices=1;
381       String str;
382       arstrHostPort.push_back(m_strExecutionHostPort);
383     }
384     
385     if(nChoices>0){
386       delete [] arbHostTried;
387       arbHostTried=new bool[nChoices];
388       for(int i=0;i<nChoices;i++){
389         arbHostTried[i]=false;
390       }
391       
392       // Loop around the choices
393       for(int nUntried=nChoices;nUntried>0;nUntried--) {
394         // Select one we haven't tried already:
395         int nChoice;        
396         do {
397           nChoice=rand() % nChoices;
398         } while (arbHostTried[nChoice]);
399         
400         m_strExecutionHostPort=arstrHostPort[nChoice];
401         
402         TRACE(_T("ConnectForExecution: chosen %s\n"),(LPCTSTR)m_strExecutionHostPort);
403         if(CeCosSocket::IsLegalHostPort(m_strExecutionHostPort)){
404           // If we're using the resource server we had better check that the host
405           // we are about to lock has not been resource-locked (the other match checks
406           // will of course always succeed)
407           String strInfo; 
408           ServerStatus s=bSchedule && !CTestResource::Matches(m_strExecutionHostPort,m_ep)?SERVER_LOCKED:  
409             Connect(m_strExecutionHostPort,m_pSock,m_ep,strInfo);
410           arbHostTried[nChoice]=true;        
411           TRACE(_T("Connect: %s says %s %s\n"),(LPCTSTR)m_strExecutionHostPort,(LPCTSTR)Image(s),(LPCTSTR)strInfo);
412           CTestResource *pResource=CTestResource::Lookup(m_strExecutionHostPort);
413           if(pResource){
414             String str;
415             str.Format(_T("%s %s %s"),(LPCTSTR)pResource->Image(),(LPCTSTR)strInfo,(LPCTSTR)Image(s));
416             arstrTries.push_back(str);
417           }
418           if(SERVER_READY==s){
419             // So that's ok then.  We're outta here.
420             INTERACTIVE(_T("Connected to %s\n"),(LPCTSTR)m_strExecutionHostPort);
421             goto Done;
422           } else {
423             delete m_pSock;
424             m_pSock=0;
425           }
426         }
427       } 
428     }
429     
430     INTERACTIVE(_T("Warning - could not connect to any test servers:\n"));
431     if(arstrTries.size()>0){
432       for(unsigned int i=0;i<arstrTries.size();i++){
433         INTERACTIVE(_T("    %s\n"),(LPCTSTR)arstrTries[i]);
434       }
435     } else {
436       INTERACTIVE(_T("No servers available to execute %s test:\n"),(LPCTSTR)m_ep.PlatformName());
437       ENTERCRITICAL;
438       for(CTestResource *pResource=CTestResource::First();pResource;pResource=pResource->Next()){
439         INTERACTIVE(_T("    %s\n"),(LPCTSTR)pResource->Image());
440       }
441       LEAVECRITICAL;
442     }
443     INTERACTIVE(_T("Retry in %d seconds...\n"),nDelay/1000);
444     
445     // We have tried all possibilities - sleep before retrying
446     CeCosThreadUtils::Sleep(nDelay);
447     
448     if(Cancelled==m_Status){
449       TRACE(_T("ConnectForExecution : cancelled\n"));
450       goto Done;
451     }
452     if(nDelay<20*1000){
453       nDelay+=rand() % 500;
454     }
455   }
456 Done:    
457   delete [] arbHostTried;
458 }
459
460 void CeCosTest::SetStatus (StatusType status)
461 {
462   ENTERCRITICAL;
463   if((int)status>(int)m_Status){
464     TRACE(_T("Status <- %s\n"),(LPCTSTR)Image(status));
465     m_Status=status;
466   }
467   LEAVECRITICAL;
468 }
469
470 bool CeCosTest::WaitForAllInstances(int nPoll,Duration nTimeout)
471 {
472   Time t0=Now();
473   while(InstanceCount>0){
474     CeCosThreadUtils::Sleep(nPoll);
475     if(NOTIMEOUT!=nTimeout && Now()-t0>nTimeout){
476       return false;
477     }
478   }
479   return true;
480 }
481
482 void CeCosTest::DeleteAllInstances()
483 {
484   while(pFirstInstance){
485     delete pFirstInstance;
486   }
487 }
488
489 void CeCosTest::CancelAllInstances()
490 {
491   ENTERCRITICAL;
492   for(CeCosTest *pTest=pFirstInstance;pTest;pTest=pTest->m_pNextInstance){
493     pTest->Cancel();
494   }
495   LEAVECRITICAL;
496 }
497
498 // The same format is used for _stscanf as for Format (which is like printf), so restrict to the format specifiers
499 // the former is happy with.  In particular, do not use %-3s etc...
500
501 LPCTSTR CeCosTest::pszFormat=
502 // 1999-01-15 17:24:36 Fireblade:5002 MN10300 sin.exe 219k/134k Pass sin download=106.3/117.0 Total=107.6 Max inactive=1.0/300.0    
503 _T("%04d-%02d-%02d %02d:%02d:%02d ")                   // Time
504 _T("%15s ")                                            // Execution host:port
505 _T("%16s ")                                             // Target
506 _T("%30s ")                                            // Executable tail
507 _T("%11s ")                                            // Result
508 _T("%dk/%dk ")                                         // Sizes
509 _T("D=") WFS _T("/") WFS _T(" Total=") WFS _T(" ")     // Times
510 _T("E=") WFS _T("/") WFS _T(" ")
511 _T("\"%s\"");
512
513 bool CeCosTest::Value (
514   LPCTSTR pszStr, 
515   struct tm &t,
516   StatusType &status,
517   String &target,
518   String &strExecutionHostPort,
519   String &strExecutableTail,
520   String &strTitle,
521
522   int &nFileSize,
523   Duration &nTotalTime,
524   Duration &nMaxInactiveTime,
525   Duration &nDownloadTime,
526   Duration &nDownloadTimeout,
527   Duration &nActiveTimeout,
528
529   int &nDownloadedSize)
530 {
531   int nLen=_tcslen(pszStr);
532   String strStatus;
533
534   nFileSize=nTotalTime=nMaxInactiveTime=nDownloadTime=nDownloadTimeout=nActiveTimeout=nDownloadedSize=0;
535   
536   int nTotalTimeFrac=0;
537   int nMaxInactiveTimeFrac=0;
538   int nActiveTimeoutFrac=0;
539   int nDownloadTimeFrac=0;
540   int nDownloadTimeoutFrac=0;
541   
542   static String strFormat;
543   if(0==strFormat.size()){
544     // Construct a version of the format string sans length attributes for %s items
545     LPCTSTR c=pszFormat;
546     TCHAR *d=strFormat.GetBuffer(_tcslen(pszFormat));
547     while(_TCHAR('\0')!=*c){
548       if(_TCHAR('%')==c[0] && _istdigit(c[1])){
549         *d++=_TCHAR('%');
550         do {
551           c++;
552         } while (_istdigit(*c));
553       }
554       *d++=*c++;
555     }
556     *d=_TCHAR('\0');
557     strFormat.ReleaseBuffer();
558   }
559   
560   _stscanf(pszStr,
561     strFormat,
562     &t.tm_year,&t.tm_mon,&t.tm_mday,
563     &t.tm_hour,&t.tm_min,&t.tm_sec,         // Time of day
564     strExecutionHostPort.GetBuffer(1+nLen),       // Execution host:port
565     target.GetBuffer(1+nLen),                  // Target
566     strExecutableTail.GetBuffer(1+nLen),          // Executable
567     strStatus.GetBuffer(1+nLen),                  // Result
568     &nDownloadedSize,&nFileSize,            // Sizes
569     &nDownloadTime,&nDownloadTimeFrac,      // Times
570     &nDownloadTimeout,&nDownloadTimeoutFrac,
571     &nTotalTime,&nTotalTimeFrac,
572     &nMaxInactiveTime,&nMaxInactiveTimeFrac,
573     &nActiveTimeout,&nActiveTimeoutFrac,
574     strTitle.GetBuffer(1+nLen)                    // Title
575     );
576   
577   strExecutionHostPort.ReleaseBuffer();
578   target.ReleaseBuffer();
579   strExecutableTail.ReleaseBuffer();
580   strStatus.ReleaseBuffer();
581   strTitle.ReleaseBuffer();
582   status=StatusTypeValue(strStatus);
583
584   LPCTSTR c1=_tcschr(pszStr,_TCHAR('"'));
585   if(c1){
586     c1++;
587     LPCTSTR c2=_tcschr(c1+1,_TCHAR('"'));
588     if(c2){
589       strTitle=String(c1,c2-c1);
590     }
591   }
592   
593   nTotalTime=nTotalTime*1000+nTotalTimeFrac*100;
594   nMaxInactiveTime=nMaxInactiveTime*1000+nMaxInactiveTimeFrac*100;
595   nActiveTimeout=nActiveTimeout*1000+nActiveTimeoutFrac*100;
596   nDownloadTime=nDownloadTime*1000+nDownloadTimeFrac*100;
597   nDownloadTimeout=nDownloadTimeout*1000+nDownloadTimeoutFrac*100;
598   
599   nFileSize*=1024;
600   nDownloadedSize*=1024;
601   t.tm_year-=1900;
602   t.tm_mon--;
603   return t.tm_year>=0 && t.tm_year<=200 && t.tm_mon>=0 && t.tm_mon<=11 && t.tm_mday>=1 && t.tm_mday<=31 && t.tm_hour>=0 && t.tm_hour<=23 && t.tm_min>=0 && t.tm_min<=59 && t.tm_sec>=0 && t.tm_sec<=59 &&
604     status!=StatusTypeMax 
605     //&& exetype!=ExecutionParameters::ExecutableTypeMax
606     ;
607 }
608   
609 const String CeCosTest::ResultString(bool bIncludeOutput) const
610 {
611   String strResultString;
612   String strTitle(m_strTitle);
613   String strExecutionHostPort(m_strExecutionHostPort);
614   
615   if(0==strTitle.size()){
616     strTitle=CeCosSocket::MySimpleHostName();
617     strTitle+=_TCHAR(':');
618     strTitle+=m_strExecutable; 
619   }
620   
621   if(0==strExecutionHostPort.size()){
622     strExecutionHostPort=CeCosSocket::MySimpleHostName();
623     strExecutionHostPort+=_T(":0");
624   }
625   
626   ENTERCRITICAL;
627   time_t ltime;
628   time(&ltime);
629   struct tm *now=localtime( &ltime );
630   
631   strResultString.Format(
632     pszFormat,
633     1900+now->tm_year,1+now->tm_mon,now->tm_mday,
634     now->tm_hour,now->tm_min,now->tm_sec,               // Time of day
635     (LPCTSTR)strExecutionHostPort,                      // Execution host:port
636     (LPCTSTR)m_ep.PlatformName(),                       // Target
637     (LPCTSTR)CeCosTestUtils::Tail(m_strExecutable),     // Executable
638     (LPCTSTR)Image(Status()),                           // Result
639     m_nStrippedSize/1024,m_nFileSize/1024,              // Sizes
640     WF(m_nDownloadTime),WF(m_ep.DownloadTimeout()),WF(m_nTotalTime),// Times
641     WF(m_nMaxInactiveTime),WF(m_ep.ActiveTimeout()),
642     (LPCTSTR)strTitle                                   // Title
643     );
644   if(bIncludeOutput && m_strOutput.size()>0){                            
645     strResultString+=_TCHAR('\n');
646     strResultString+=m_strOutput;
647   }
648   LEAVECRITICAL;
649   return strResultString;
650 }
651
652 // Run as a server, listening on the port given as parameter
653 bool CeCosTest::RunAgent(int nTcpPort)
654 {
655   bool bLocked=false;
656   
657   // Create socket
658   int nSock = CeCosSocket::Listen(nTcpPort);
659   int nLastClient=0;
660   if (-1!=nSock) {
661     for (;;) {
662       try {
663         CeCosSocket *pSock=new CeCosSocket(nSock); // AcceptThreadFunc deletes if not deleted below
664         String str;
665         // Read the execution parameters
666         if(!pSock->recvString(str)){
667           // Socket error on the recv - nothing much we can do
668           TRACE(_T("RunAgent : could not read execution parameters\n"));
669           delete pSock;
670           pSock=0;
671         } else {
672           ExecutionParameters e;
673           e.FromStr(str);
674           TRACE(_T("Execution parameters: %s\n"),(LPCTSTR)e.Image());
675           ServerStatus s;
676           CTestResource *pPort=0;
677           String strInfo;
678
679           switch(e.Request()) {
680             case ExecutionParameters::LOCK:
681               if(bLocked){
682                 s=SERVER_BUSY;
683               } else {
684                 WaitForAllInstances(1000,NOTIMEOUT);
685                 bLocked=true;
686                 s=SERVER_LOCKED;
687               }
688               break;
689             case ExecutionParameters::UNLOCK:
690               if(bLocked){
691                 bLocked=false;
692                 s=SERVER_READY;
693               } else {
694                 s=SERVER_BUSY;
695               }
696               break;
697             case ExecutionParameters::QUERY:
698               if (bLocked) {
699                 s=SERVER_LOCKED;
700               } else {
701                 s=SERVER_BUSY;
702                 ENTERCRITICAL;
703                 for(CTestResource *pResource=CTestResource::First();pResource;pResource=pResource->Next()){
704                   if(!pResource->InUse()){
705                     s=SERVER_READY;
706                     break;
707                   }
708                 }
709                 LEAVECRITICAL;
710                 if(SERVER_READY!=s){
711                   strInfo.Format(_T("serving %s"),(LPCTSTR)CeCosSocket::ClientName(nLastClient));
712                 }
713               }
714               break;
715             case ExecutionParameters::RUN:
716               if(NULL==e.Platform()){
717                 // Looks like a confused client ...
718                 strInfo.Format(_T("Bad target value %s read from client\n"),(LPCTSTR)str);
719                 s=SERVER_CANT_RUN;
720               } else if(0==CTestResource::Count(e)){
721                 // No chance of running this test
722                 strInfo.Format(_T("Cannot run a %s test from this server\n"),(LPCTSTR)e.PlatformName());
723                 s=SERVER_CANT_RUN;
724               } else if (bLocked) {
725                 s=SERVER_LOCKED;
726               } else {
727                 pPort=CTestResource::GetResource(e);
728                 if(0==pPort){
729                   // We must disappoint our client
730                   strInfo.Format(_T("serving %s"),(LPCTSTR)CeCosSocket::ClientName(nLastClient));
731                   s=SERVER_BUSY;
732                 } else {
733                   s=SERVER_READY;
734                   nLastClient=pSock->Client();
735                 }
736               }
737               break;
738             case ExecutionParameters::STOP:
739               s=SERVER_READY;
740               break;
741             default:
742               s=SERVER_CANT_RUN;
743           }
744           
745 #ifndef VERBOSE
746           if(ExecutionParameters::QUERY!=e.Request())
747 #endif
748             TRACE(_T("RunAgent : %s request tActive=%d tDownload=%d Target=%s Reply status=%s %s\n"),
749               (LPCTSTR)e.Image(e.Request()),e.ActiveTimeout(),e.DownloadTimeout(),
750               (LPCTSTR)e.PlatformName(),
751               (LPCTSTR)Image(s),(LPCTSTR)strInfo);
752           
753           bool bSendok=pSock->sendInteger(s) && pSock->sendString(strInfo);
754           
755           if(SERVER_READY==s && bSendok && ExecutionParameters::RUN==e.Request()){
756             
757             // Create a new class instance
758             // AcceptThreadFunc deletes the instance and closes new_sock
759             // RunLocal, called by AcceptThreadFunc, releases the port
760             // No need for meaningful callback, but must run asynchronously
761             
762             int nAuxPort=30000;
763             int nAuxListenSock=-1;
764       
765             do {
766               nAuxListenSock=CeCosSocket::Listen(nAuxPort);
767             } while (-1==nAuxListenSock && nAuxPort++<=0xffff);
768             
769             if(-1==nAuxListenSock){
770               ERROR(_T("Couldn't find a socket to bind to for RDI\n"));
771             } else {
772               
773               CeCosTest *pTest=new CeCosTest(e,NULL);
774               pTest->m_nAuxPort=nAuxPort;
775               pTest->m_nAuxListenSock=nAuxListenSock;
776               pTest->m_pSock=pSock;
777               pTest->m_strExecutionHostPort=CeCosSocket::HostPort(CeCosSocket::MyHostName(),nTcpPort);
778               pTest->m_pResource=pPort;
779               CeCosThreadUtils::RunThread(SAcceptThreadFunc,pTest,_T("SAcceptThreadFunc"));
780               // AcceptThreadFunc deletes pSock
781             }
782             
783           } else {
784             delete pSock;
785             pSock=0;
786             if(pPort){
787               pPort->Release();
788               pPort=0;
789             }
790             if(CeCosTest::ExecutionParameters::STOP==e.Request()){
791               CancelAllInstances();
792               WaitForAllInstances(1000,20*1000);
793               break;
794             }
795           }
796         }
797       }
798       catch(...){
799         TRACE(_T("!!! Exception caught in RunAgent()\n"));
800       }
801     }
802     CeCosSocket::CloseSocket (nSock);
803   }
804     
805   return false;
806 }
807
808 CeCosTest::StatusType CeCosTest::StatusTypeValue(LPCTSTR  pszStr)
809 {
810   for(int i=0;i<StatusTypeMax;i++){
811     StatusType t=(StatusType)i;
812     if(0==_tcsicmp(Image(t),pszStr)){
813       return t;
814     }
815   }
816   return StatusTypeMax;
817 }
818
819 // Thread to run ConnectSocketToSerial
820 void CeCosTest::ConnectSocketToSerialThreadFunc()
821 {
822   TRACE(_T("ConnectSocketToSerialThreadFunc sock=%d\n"),m_nAuxListenSock);
823     
824   CeCosTestSerialFilter serial_filter;
825   CeCosTestDownloadFilter download_filter;
826   
827   CeCosSerial serial;
828   serial.SetBlockingReads(false);
829   bool rc=false;
830   // Open serial device.
831   if (!serial.Open(m_pResource->Serial(),m_pResource->Baud())){
832     ERROR(_T("Couldn't open port %s\n"),m_pResource->Serial());
833   } else {
834     for(;;){
835       // Flush the serial buffer.
836       serial.Flush();
837       TRACE(_T("ConnectSocketToSerial: waiting for connection...\n"));
838       CeCosSocket socket;
839       if(!socket.Accept(m_nAuxListenSock,&m_bStopConnectSocketToSerial)){
840         ERROR(_T("ConnectSocketToSerial - couldn't accept: %s\n"),(LPCTSTR)socket.SocketErrString());
841         break;
842       } else if (m_pSock->Client() != socket.Client()){    
843         // Make sure the client is who we think it is...
844         TRACE(_T("ConnectSocketToSerialThread - illegal connection attempted from %s\n"),(LPCTSTR)socket.ClientName(socket.Client()));
845       } else {
846         try {
847             rc=CeCosSocket::ConnectSocketToSerial(socket,serial,m_ep.m_bUseFilter?SerialFilterFunction:NULL, (void*)&serial_filter, m_ep.m_bUseFilter?DownloadFilterFunction:NULL, (void*)&download_filter, &m_bStopConnectSocketToSerial);
848           
849           // If the download filter was just active, it may
850           // allow the session to continue.
851           if(!download_filter.ContinueSession()){
852             break;
853           }
854           
855         }
856         catch (LPCTSTR pszMsg){
857           Log(_T("!!! ConnectSocketToSerial exception caught: %s!!!\n"),pszMsg);
858           rc=false;
859           break;
860         }
861         catch (...){
862           Log(_T("!!! ConnectSocketToSerial exception caught!!!\n"));
863           rc=false;
864           break;
865         }
866       }
867     }
868   }
869   TRACE(_T("ConnectSocketToSerial : done\n"));
870   CeCosSocket::CloseSocket(m_nAuxListenSock);
871 }
872
873 static bool CALLBACK DerefBool(void *pParam)
874 {
875   return *(bool *)pParam;
876 }
877
878 // Function called (on a separate thread) to process a successful connection to the RunAgent loop
879 // In the case of a simulator server, we can have many of these active at the same time.
880 void CeCosTest::AcceptThreadFunc()
881 {
882   if(m_ep.Platform()->ServerSideGdb()){
883     // We dream up a temporary name for the executable
884     ENTERCRITICAL;
885     m_strExecutable.Format(_T("%s-%s-%d"),_ttmpnam(0),(LPCTSTR)m_ep.PlatformName(),m_nAuxPort);
886     LEAVECRITICAL;
887
888     int n;
889     if(m_pSock->recvInteger(n,_T("file size"))){
890       m_nFileSize=n;
891       // Read file from the socket
892       bool bCanRun=true;
893       TRACE(_T("AcceptThreadFunc file size=%d reading...\n"),m_nFileSize);
894       FILE *f2;
895       f2=_tfopen(m_strExecutable,_T("wb"));
896       if(0==f2){
897         Log(_T("Could not create %s - %s\n"),(LPCTSTR)m_strExecutable,strerror(errno));
898         bCanRun=false;
899       }
900       unsigned int nBufSize=MIN(100000,m_nFileSize);
901       Buffer b(nBufSize);
902       unsigned int nWritten=0;
903       unsigned int nRead=0;
904       while(nRead<m_nFileSize){
905         int nToRead=MIN(nBufSize,m_nFileSize-nRead);
906         if(!recv( b.Data(), nToRead, _T("executable"))){
907           break;
908         }
909         nRead+=nToRead;
910         if(0!=f2){
911           char *c=(char *)b.Data();
912           while(nToRead>0){
913             int w=fwrite(c,1,nToRead,f2);
914             if(-1==w){
915               Log(_T("Write error on %s - %s\n"),(LPCTSTR)m_strExecutable,strerror(errno));
916               bCanRun=false;
917               break;
918             }
919             nWritten+=w;
920             c+=w;
921             nToRead-=w;
922           }
923         }
924       }
925       TRACE(_T("Accept - done reading [%d bytes read, %d bytes written]\n"),nRead,nWritten);
926       if(0!=f2){
927         fclose(f2);
928         _tchmod(m_strExecutable,00700); // user read, write and execute
929       } 
930       if(0!=f2 && m_nFileSize!=nWritten){
931         Log(_T("Failed to create %s correctly [%d/%d bytes written]\n"),(LPCTSTR)m_strExecutable, nWritten, m_nFileSize);
932         bCanRun=false;
933       }
934       SetExecutable(m_strExecutable); // to set stripped length and title
935       RunLocal();
936       _tunlink(m_strExecutable);
937     }
938     sendResult();
939     m_pSock->recvInteger(n); // receive an ack
940   } else {
941     // Client-side GDB
942     bool bTargetReady;
943     if(_TCHAR('\0')==*(m_pResource->ResetString())){
944       bTargetReady=true;
945       TRACE(_T("No reset possible\n"));
946     } else {
947       Log(_T("Resetting target using %s"),(LPCTSTR)m_pResource->ResetString());
948       bTargetReady=(CResetAttributes::RESET_OK==m_pResource->Reset(ResetLogFunc,this));
949     }
950     TRACE(_T("Send Target Ready indicator=%d\n"),bTargetReady);
951     m_pSock->sendInteger(bTargetReady,_T("target ready indicator"));
952     
953     int nAck=-1;
954     
955     if(bTargetReady){
956       if(CeCosSocket::IsLegalHostPort(m_pResource->Serial())){
957         TRACE(_T("Sending %s\n"),(LPCTSTR)m_pResource->Serial());
958         if(m_pSock->sendString(m_pResource->Serial(),_T("Serial name")) && m_pSock->recvInteger(nAck,_T("Terminating ack"),CeCosSocket::NOTIMEOUT)){
959           TRACE(_T("Terminating ack=%d\n"),nAck);
960         }
961       } else {
962         String strHostPort(CeCosSocket::HostPort(CeCosSocket::MyHostName(),m_nAuxPort));
963         
964         TRACE(_T("Using %s\n"),(LPCTSTR)strHostPort);
965         
966         if(m_pSock->sendString(strHostPort,_T("host:port"))){
967           
968           // This Boolean signifies that the serial<-->tcp/ip conversation is done.  It may be set
969           // on completion of the ConnectSocketToSerial thread (which is why we pass it to runthread)
970           // and also set by us to *cause* the thread to complete.
971           
972           bool bConnectSocketToSerialThreadDone=false; // Indication of termination of ConnectSocketToSerial thread
973           m_bStopConnectSocketToSerial=false; // Used to tap ConnectSocketToSerial thread on the shoulder
974           
975           CeCosThreadUtils::RunThread(SConnectSocketToSerialThreadFunc,this,&bConnectSocketToSerialThreadDone,_T("SConnectSocketToSerialThreadFunc")); 
976           
977           // Wait for either client or the ConnectSocketToSerial thread to finish.
978           if(m_pSock->recv(&nAck,sizeof(int),_T("Terminating ack"),CeCosSocket::NOTIMEOUT,DerefBool,&bConnectSocketToSerialThreadDone)){
979             TRACE(_T("Session terminated by request of client (%s)\n"),(LPCTSTR)Image((StatusType)nAck));
980           } else if(0!=m_pSock->SocketError()){
981             TRACE(_T("Session terminated by socket error - %s\n"),(LPCTSTR)m_pSock->SocketErrString());
982           }
983           if(!bConnectSocketToSerialThreadDone){
984             // Tap ConnectSocketToSerial thread on the shoulder
985             TRACE(_T("Waiting for ConnectSocketToSerial thread to terminate...\n"));
986             m_bStopConnectSocketToSerial=true;
987             CeCosThreadUtils::WaitFor(bConnectSocketToSerialThreadDone);
988           }
989         }
990       }
991     }
992   }
993   delete this;
994 }
995
996 bool CeCosTest::send(const void *pData,unsigned int nLength,LPCTSTR pszMsg,Duration dTimeout)
997 {
998   return m_pSock->send(pData,nLength,pszMsg,dTimeout,IsCancelled,this);
999 }
1000
1001 bool CeCosTest::recv(const void *pData,unsigned int nLength,LPCTSTR pszMsg,Duration dTimeout)
1002 {
1003   return m_pSock->recv(pData,nLength,pszMsg,dTimeout,IsCancelled,this);
1004 }
1005
1006 void CeCosTest::Log(LPCTSTR  pszFormat, ...)
1007 {
1008   va_list args;
1009   va_start(args, pszFormat);
1010   String str;
1011   str.vFormat(pszFormat,args);
1012   va_end(args);
1013   LogString(str);
1014 }
1015
1016 void CeCosTest::LogString(LPCTSTR psz)
1017 {
1018   if(*psz){
1019     ENTERCRITICAL;
1020     m_strOutput+=psz;
1021     LEAVECRITICAL;
1022     if(CeCosTrace::IsInteractive()){
1023       CeCosTrace::Out(psz);
1024     } else {
1025       TRACE(_T("%s"),psz);
1026     }
1027   }
1028 }
1029
1030 bool CeCosTest::sendResult(Duration dTimeout)
1031 {
1032   bool rc=
1033     m_pSock->sendInteger(m_Status,_T("result"),dTimeout) &&
1034     m_pSock->sendInteger(m_nDownloadTime,_T("result"),dTimeout) &&
1035     m_pSock->sendInteger(m_nTotalTime,_T("result"),dTimeout) &&
1036     m_pSock->sendInteger(m_nMaxInactiveTime,_T("result"),dTimeout) &&
1037     m_pSock->sendString (m_strOutput,_T("result"),dTimeout);
1038   return rc;
1039 }
1040
1041 bool CeCosTest::recvResult(Duration dTimeout)
1042 {
1043   String strOutput;
1044   int nStatus=StatusTypeMax;
1045   bool rc=
1046     m_pSock->recvInteger(nStatus,_T("result"),dTimeout) &&
1047     m_pSock->recvInteger(m_nDownloadTime,_T("result"),dTimeout) &&
1048     m_pSock->recvInteger(m_nTotalTime,_T("result"),dTimeout) &&
1049     m_pSock->recvInteger(m_nMaxInactiveTime,_T("result"),dTimeout) &&
1050     m_pSock->recvString (strOutput,_T("result"),dTimeout);
1051   m_Status=(StatusType)MIN(nStatus,StatusTypeMax);
1052   LogString(strOutput);
1053   return rc;
1054 }
1055
1056 // Return time used by inferior gdb process - CPU for sim, wallclock otherwise
1057 Time CeCosTest::InferiorTime() const
1058 {
1059   if(*(m_pResource->Serial())){
1060     return Now();
1061   }
1062   if(!m_psp){
1063     return 0;
1064   }
1065   Time now=Now();
1066   if(now-m_tPrevSample>1000){
1067     m_tPrevSample=now;
1068     m_tInferiorCpuTime=m_psp->CpuTime();
1069   }
1070   return m_tInferiorCpuTime;
1071 }
1072
1073 bool CeCosTest::CheckForTimeout()
1074 {
1075   bool rc=(Cancelled!=Status());
1076   if(TimeOut!=m_Status && DownloadTimeOut!=m_Status){
1077     Time t=InferiorTime();
1078     if(t){
1079       // We have been able to measure the time
1080       if(m_bDownloading){
1081         m_nDownloadTime=MAX(m_nDownloadTime,Duration(InferiorTime()-m_tBase));
1082         if(m_nDownloadTime>m_ep.DownloadTimeout()){
1083           Log(_T("\n*** Timeout - download time ") WFS _T(" exceeds limit of ") WFS _T("\n"),WF(m_nDownloadTime),WF(m_ep.DownloadTimeout()));
1084           rc=false;
1085         }
1086       } else {
1087         m_nMaxInactiveTime=MAX(m_nMaxInactiveTime,Duration(InferiorTime()-m_tBase));
1088         if (m_nMaxInactiveTime>m_ep.ActiveTimeout()) {
1089           Log(_T("\n*** Timeout - inactive time ") WFS _T(" exceeds limit of ") WFS _T("\n"),WF(m_nMaxInactiveTime),WF(m_ep.ActiveTimeout()));
1090           rc=false;
1091         }
1092       }
1093     }
1094     m_nTotalTime=Duration(Now()-m_tWallClock0);
1095 /*
1096     if(m_nTotalTime>m_ep.ElapsedTimeout()){
1097       Log(_T("\n*** Timeout - total time ") WFS _T(" exceeds limit of ") WFS _T("\n"),   WF(m_nTotalTime),WF(m_ep.ElapsedTimeout()));
1098       rc=false;
1099     }
1100 */
1101     if(!rc){
1102       SetStatus(m_bDownloading?DownloadTimeOut:TimeOut);
1103     }
1104   }
1105   return rc;
1106 }
1107
1108 // Convert a path to something a cygwin tool will understand.  Used when invoking -size and -gdb
1109 String CeCosTest::CygPath (LPCTSTR pszPath)
1110 {
1111 #ifdef _WIN32
1112   String str = "";
1113   HKEY hKey = 0;
1114   DWORD type;
1115   BYTE value[256];
1116   DWORD sz = sizeof(value);
1117
1118   // look for the cygdrive prefix in the user's registry settings
1119   if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Cygnus Solutions\\Cygwin\\mounts v2", 0, KEY_READ, &hKey)) {
1120     if (ERROR_SUCCESS == RegQueryValueEx(hKey, "cygdrive prefix", NULL, & type, value, & sz)) {
1121       str = (const char*) value;
1122     }
1123     RegCloseKey(hKey);
1124   }
1125
1126   // if not yet found, look for the cygdrive prefix in the system registry settings
1127   hKey = 0;
1128   sz = sizeof(value);
1129   if (str.empty() && (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Cygnus Solutions\\Cygwin\\mounts v2", 0, KEY_READ, &hKey))) {
1130     if (ERROR_SUCCESS == RegQueryValueEx(hKey, "cygdrive prefix", NULL, & type, value, & sz)) {
1131       str = (const char*) value;
1132     }
1133     RegCloseKey(hKey);
1134   }
1135
1136   int prefixlen = str.length();
1137   TCHAR *buf=str.GetBuffer(prefixlen+1+MAX_PATH);
1138   TCHAR *pszFname;
1139   if(::GetFullPathName(pszPath,MAX_PATH,prefixlen+buf, &pszFname)){
1140     GetShortPathName(prefixlen+buf,prefixlen+buf,MAX_PATH); // ignore errors
1141     buf[prefixlen+1]=buf[prefixlen];
1142     buf[prefixlen]=_TCHAR('/');
1143     for(int i=prefixlen+2;buf[i];i++){
1144       if(_TCHAR('\\')==buf[i]){
1145         buf[i]=_TCHAR('/');
1146       }
1147     }
1148     str.ReleaseBuffer();
1149     return str;
1150   } else {
1151     str.ReleaseBuffer();
1152     return pszPath;
1153   }
1154 #endif
1155   return pszPath;
1156 }
1157
1158 void CeCosTest::SetExecutable(LPCTSTR pszExecutable)
1159 {
1160   m_strOutput=_T("");
1161   if(pszExecutable){
1162     m_strExecutable=pszExecutable;
1163     if(m_ep.Platform()){
1164       GetSizes();
1165     } else {
1166       ERROR(_T("Don't know how to get sizes of this platform type\n"));
1167     }
1168   } else {
1169     m_strExecutable=_T("");
1170   }
1171 }
1172
1173 // Calculate the sizes of the given file.  The target parameter is necessary in order to 
1174 // determine which -size executable to use to do the job.
1175 bool CeCosTest::GetSizes()
1176 {
1177 TRACE(_T("GetSizes %s\n"),(LPCTSTR)Executable());
1178   bool rc=false;
1179   m_nStrippedSize=m_nFileSize=0;
1180   LPCTSTR pszPrefix=m_ep.Platform()->Prefix();
1181   struct _stat buf;
1182   if(-1==_tstat(Executable(),&buf)){
1183     Log(_T("%s does not exist\n"),(LPCTSTR)Executable());
1184   } else if (_TCHAR('\0')==*pszPrefix){
1185     LogString(_T("No prefix to run a size program\n"));
1186   } else {
1187     m_nFileSize=buf.st_size;
1188     const String strSizeCmd(String::SFormat(_T("%s-size %s"),pszPrefix,(LPCTSTR)CygPath(Executable())));
1189     String strOut;
1190     CSubprocess sp;
1191     if(!sp.Run(strOut,strSizeCmd)){
1192       Log(_T("Failed to run \"%s\" - %s\n"),(LPCTSTR)strSizeCmd,(LPCTSTR)sp.ErrorString());
1193     } else {
1194       const TCHAR *c=_tcschr(strOut,_TCHAR('\n'));
1195       if(c){
1196         c++;
1197       }
1198       int s1=0;
1199       int s2=0;
1200       if(c && 2==_stscanf(c,_T(" %d %d"),&s1,&s2)){
1201         rc=true;
1202         m_nStrippedSize=s1+s2;
1203       }
1204       TRACE(_T("GetSizes %s rc=%d file size=%d stripped size=%d\n"),(LPCTSTR)Executable(),rc,m_nFileSize,m_nStrippedSize);
1205     }
1206   }
1207   return rc;
1208 }
1209
1210 void CeCosTest::SetTimeouts (Duration dActive,Duration dDownload/*,Duration dElapsed*/)
1211 {
1212   m_ep.SetActiveTimeout  (dActive);
1213   m_ep.SetDownloadTimeout(dDownload);
1214 /*
1215   m_ep.SetElapsedTimeout (dElapsed);
1216 */
1217 }
1218
1219 void CeCosTest::CloseSocket (){
1220   delete m_pSock;
1221   m_pSock=0;
1222 }
1223
1224 bool CeCosTest::AtPrompt()
1225 {
1226   const String strPrompt(m_ep.Platform()->Prompt());
1227   unsigned int nPromptLen=_tcslen(strPrompt);
1228   return
1229     nPromptLen>0 &&
1230     m_strOutput.size()>=nPromptLen && 
1231     0==_tcscmp((LPCTSTR)m_strOutput+m_strOutput.size()-nPromptLen,strPrompt);
1232 }
1233
1234 #ifdef _WIN32
1235 BOOL WINAPI HandlerRoutine(
1236                            DWORD dwCtrlType   //  control signal type
1237                            )
1238 {
1239   dwCtrlType; // eliminate compiler warning
1240   return TRUE;
1241 }
1242 #endif
1243
1244
1245 bool CeCosTest::InteractiveInferior(LPCTSTR pszHostPort,TCHAR **argv)
1246 {
1247   bool rc=false;
1248   if(_TCHAR('\0')!=*pszHostPort){
1249     if(!CeCosSocket::IsLegalHostPort(pszHostPort)){
1250       ERROR(_T("Illegal host:port '%s'\n"),pszHostPort);
1251       return false;
1252     } else {
1253       m_strExecutionHostPort=pszHostPort;
1254       Log(_T("Waiting to connect to %s...\n"),(LPCTSTR)m_strExecutionHostPort);
1255     }
1256   } else {
1257     Log(_T("Waiting to connect to a server...\n"));
1258   }
1259   
1260   ConnectForExecution();
1261   
1262   Log(_T("Connected to %s - waiting for target reset\n"),(LPCTSTR)m_strExecutionHostPort);
1263   String strHostPort,strOutput;
1264   // We read:
1265   //     target ready indicator
1266   //     any output so far
1267   //     (if target ready) host:port
1268   if(GetTargetReady(strHostPort)){
1269     Log(_T("Use target remote %s\n"),(LPCTSTR)strHostPort);
1270     String strInferior(m_ep.Platform()->Prefix());
1271     strInferior+=_T("-gdb");
1272 #ifdef _WIN32
1273     SetConsoleCtrlHandler(HandlerRoutine,TRUE);
1274     int n=_tspawnvp(_P_WAIT,strInferior,argv);
1275     if(-1==n){
1276       Log(_T("Failed to spawn %s\n"),(LPCTSTR)strInferior);
1277     } else {
1278       rc=(0==n);
1279     }   
1280     SetConsoleCtrlHandler(HandlerRoutine,FALSE);
1281 #else // UNIX
1282     
1283     int pid=fork();
1284     switch(pid){
1285     case -1:
1286       _ftprintf(stderr,_T("fork failed\n"));
1287       pid=0;
1288       break;  
1289     case 0:
1290       // Process is created (we're the child)
1291       execvp(strInferior,argv);
1292       Log(_T("Error invoking %s - %s\n"),(LPCTSTR)strInferior,strerror(errno));
1293       exit(1);
1294       break;
1295     default:
1296       // Process is created (we're the parent)
1297       {
1298         signal(SIGINT,SIG_IGN);
1299         int stat;
1300         waitpid(pid,&stat,0);
1301         rc=(0==stat);
1302         signal(SIGINT,SIG_DFL);
1303       }
1304       break;
1305     }
1306 #endif
1307     Log(_T("Inferior terminated\n"));
1308     // Tell the server we're through
1309     m_pSock->sendInteger(123,_T("Terminating ack"));
1310   }
1311   return rc;
1312 }
1313
1314 void CALLBACK CeCosTest::ResetLogFunc(void *pParam, LPCTSTR psz) 
1315 {
1316   CeCosTest *pTest=(CeCosTest *)pParam;
1317   TRACE(_T("Send Target Ready indicator=2\n"));
1318   pTest->m_pSock->sendInteger(2,_T("target ready indicator"));
1319   TRACE(_T("Send %s\n"),psz);
1320   pTest->m_pSock->sendString(psz,_T("output so far"));
1321 }
1322
1323 CeCosTest::ExecutionParameters::RequestType CeCosTest::ExecutionParameters::RequestTypeValue(LPCTSTR psz)
1324 {
1325   int r;
1326   for(r=0;r<RequestTypeMax;r++){
1327     if(0==_tcsicmp(psz,arRequestImage[r])){
1328       break;
1329     }
1330   }
1331   return (RequestType)r;
1332 }
1333
1334 void CeCosTest::InferiorOutputFunc(LPCTSTR pszMsg)
1335 {
1336   LogString(pszMsg);
1337
1338   m_nOutputLen+=_tcslen(pszMsg);
1339
1340   if(m_pspPipe){
1341     m_pspPipe->Send(pszMsg);
1342   }
1343
1344   if(m_nOutputLen>20000){
1345     LogString(_T("\n>>>> Infra FAIL\n*** too much output ***\n>>>>\n"));
1346     SetStatus(Fail);
1347     m_psp->Kill();
1348   }
1349   
1350   m_tBase=InferiorTime(); // We are seeing life, so reset the clock for timeouts
1351   
1352   if(AtPrompt()){
1353     
1354     // gdb's output included one or more prompts
1355     // Send another command along
1356     if(m_nCmdIndex>=m_arstrInferiorCmds.size()){
1357       // Nothing further to say to gdb - exit
1358       
1359       m_psp->Kill(); // case 3
1360     } else {
1361
1362       if(m_nCmdIndex>0 && 0==_tcscmp(_T("load"),m_arstrInferiorCmds[m_nCmdIndex-1])){
1363         // load command was previous command - we are no longer downloading
1364         m_bDownloading=false;
1365       }
1366
1367       String strCmd(m_arstrInferiorCmds[m_nCmdIndex++]);
1368
1369       // If we can there is a GDB instruction to send to gdb, do it
1370       String str;
1371       if(GetDirective(_T("GDB:"),str,m_nLastGdbInst)){
1372         strCmd=str;
1373         m_nCmdIndex--; // undo increment above
1374       }
1375
1376       if(0==_tcscmp(_T("load"),strCmd)){
1377         // load command issued - we are now "downloading"
1378         m_bDownloading=true;
1379       } else if(0==_tcscmp(_T("run"),strCmd) || 0==_tcscmp(_T("cont"),strCmd)){
1380         SetStatus(NoResult);
1381       } 
1382       
1383       strCmd+=_TCHAR('\n');
1384       LogString(strCmd);
1385       m_psp->Send(strCmd);          
1386
1387     }
1388   }
1389
1390   // If there is a EXEC instruction to process, obey it
1391   String strCmd;
1392   while(GetDirective(_T("EXEC:"),strCmd,m_nLastExecInst)){
1393     CSubprocess *pExecsp=new CSubprocess;
1394     pExecsp->SetPath(m_strPath);
1395     if(!pExecsp->Run(AppendFunc,this,(LPCTSTR)strCmd,false)){
1396       Log(_T("%%%% Failed to create process '%s'\n"),(LPCTSTR)strCmd);
1397       delete pExecsp;
1398     } else {
1399       m_arpExecsp.push_back(pExecsp);
1400     }
1401   }
1402
1403   // If there is a PIPE instruction to process, obey it
1404   while(GetDirective(_T("PIPE:"),strCmd,m_nLastPipeInst)){
1405     if(m_pspPipe){
1406       Log(_T("%%%% Two PIPE commands are a no-no\n"));
1407     } else {
1408       m_pspPipe=new CSubprocess;
1409       m_pspPipe->SetPath(m_strPath);
1410
1411       if(!m_pspPipe->Run(AppendFunc,this,(LPCTSTR)strCmd,false)){
1412         Log(_T("%%%% Failed to create process '%s'\n"),(LPCTSTR)strCmd);
1413         delete m_pspPipe;
1414         m_pspPipe=0;
1415       } else {
1416         // Send what we read have so far
1417         m_pspPipe->Send(m_strOutput);
1418       }
1419     }
1420   }
1421
1422   while(GetDirective(_T("TIMEOUT:"),strCmd,m_nLastTimeoutInst)){
1423     int n=_ttoi(strCmd);
1424     if(n){
1425       SetTimeouts(n); // second parameter is download timeout, which is now irrelevant
1426     } else {
1427       Log(_T("%%%% Illegal timeout specified: %s\n"),(LPCTSTR)strCmd);
1428     }
1429   }
1430 }
1431
1432 void CeCosTest::RunInferior(LPCTSTR pszCmdline)
1433 {
1434   m_psp=new CSubprocess;
1435   m_psp->SetContinuationFunc(SCheckForTimeout,this);
1436   try {
1437     m_nMaxInactiveTime=0;
1438     m_nTotalTime=0;
1439     m_nDownloadTime=0;
1440     m_nOutputLen=0;
1441     m_bDownloading=false;
1442     
1443     // Decide on the baseline status - NotStarted if there is a download element, NoResult otherwise.
1444     m_Status=NoResult;
1445     for(unsigned int i=0;i<m_arstrInferiorCmds.size();i++){
1446       if(0==_tcscmp(_T("run"),m_arstrInferiorCmds[i]) || 0==_tcscmp(_T("cont"),m_arstrInferiorCmds[i])){
1447         m_Status=NotStarted;
1448         break;
1449       }
1450     }
1451     TRACE(_T("Status <- %s\n"),(LPCTSTR)Image(m_Status));
1452
1453     m_tPrevSample=0; // force an initial reading
1454     m_tInferiorCpuTime=0;
1455
1456     m_tBase=m_tBase0=InferiorTime(); // Returns either Now() or nothing
1457     m_tWallClock0=Now();
1458
1459     m_nCmdIndex=0;
1460   
1461     TRACE(_T("RunGDB()\n"));
1462   
1463     m_nLastGdbInst=m_nLastExecInst=m_nLastTimeoutInst=m_nLastPipeInst=0;
1464     m_psp->SetPath(m_strPath);
1465     if(m_psp->Run(SInferiorOutputFunc,this,pszCmdline,true)){
1466
1467       if(m_pspPipe){
1468         m_pspPipe->Send(_T("\n"));
1469         m_pspPipe->CloseInput();
1470         if(m_pspPipe->Wait(5000)){
1471           // OK the pipe process terminated.
1472           int rc=m_pspPipe->GetExitCode();
1473           if(0!=rc){
1474             Log(_T("%%%% Pipe process returned rc=%d\n"),rc);
1475             SetStatus(Fail);
1476           }
1477         } else {
1478           LogString(_T("%%%% Pipe process would not complete\n"));
1479         }
1480       }
1481
1482       AnalyzeOutput();
1483     
1484     } else {
1485       Log(_T("Failed to run \"%s\" - %s\n"),pszCmdline,(LPCTSTR)m_psp->ErrorString());
1486     }
1487   } 
1488   catch(...){
1489     ERROR(_T("!!! Exception caught in RunInferior()\n"));
1490   }
1491   delete m_psp; // will cause process to be killed as necessary and completion to be waited for
1492   m_psp=NULL;
1493   for(int i=0;i<(signed)m_arpExecsp.size();i++){
1494     delete (CSubprocess *)m_arpExecsp[i]; // ditto
1495   }
1496   m_arpExecsp.clear();
1497   TRACE(_T("Exiting RunInferior()\n"));
1498 }
1499
1500 void CeCosTest::AnalyzeOutput()
1501 {
1502   // This test is pulled out to allow ser_filter to simulate a test failure
1503   if(OutputContains(_T("FAIL:"))){
1504     SetStatus(Fail);
1505   }
1506   
1507   if(OutputContains(_T("EXIT:"))||OutputContains(_T("NOTAPPLICABLE:"))){
1508     static LPCTSTR arpszKeepAlive[]={_T("FAIL:"),_T("NOTAPPLICABLE:"), _T("PASS:")}; 
1509     static const StatusType arStatus[] ={Fail, Inapplicable, Pass};
1510     for(unsigned int i=0;i<sizeof arpszKeepAlive/sizeof arpszKeepAlive[0];i++){
1511       if(OutputContains(arpszKeepAlive[i])){
1512         TRACE(_T("DriveInferior: saw '%s'\n"),arpszKeepAlive[i]);
1513         SetStatus(arStatus[i]); // Do not break!
1514       }
1515     }
1516   }
1517   
1518   // Certain output spells failure...
1519   if(OutputContains(_T("cyg_assert_fail ("))){
1520     SetStatus(AssertFail);
1521   } else {
1522     static LPCTSTR arpszSignals[]={_T("SIGBUS"), _T("SIGSEGV"), _T("SIGILL"), _T("SIGFPE"), _T("SIGSYS"), _T("SIGTRAP")};
1523     for(unsigned int i=0;i<sizeof arpszSignals/sizeof arpszSignals[0];i++){
1524       String str1,str2;
1525       str1.Format(_T("signal %s"),arpszSignals[i]);
1526       str2.Format(_T("handle %s nostop"),arpszSignals[i]);
1527       if(OutputContains(str1)&&!OutputContains(str2)){
1528         SetStatus(Fail);
1529         break;
1530       }
1531     }
1532   }
1533   
1534   int nIndex=0;
1535   String str;
1536   while(GetDirective(_T("EXPECT:"),str,nIndex)){
1537     // s1 is the pointer to the text following the expect - that to be tested
1538     LPCTSTR s1=(LPCTSTR)m_strOutput+nIndex;
1539     while (_istspace(*s1)){
1540       s1++;
1541     }
1542     // whereas s2 is the pointer to the text in the expect string (what we are expecting)
1543     LPCTSTR s2=(LPCTSTR)str;
1544     while(*s2){
1545       if(*s2!=*s1){
1546         Log(_T("EXPECT:<> failure - expected '%s' saw '%s'\n"),(LPCTSTR)str,(LPCTSTR)m_strOutput+nIndex);
1547         SetStatus(Fail);
1548         break;
1549       }
1550       s1++;
1551       s2++;
1552     }
1553   }
1554 }
1555
1556 bool CeCosTest::ExecutionParameters::FromStr(LPCTSTR psz)
1557 {
1558   String str1,str2,str3,str4,str5;
1559   int nUseFilter,nUnused2,nUnused3;
1560   int nLen=_tcslen(psz);
1561   _stscanf(psz,_T("%s %s %d %d %d %d %d %d %d %d %s %s %s"),
1562     str1.GetBuffer(1+nLen),
1563     str2.GetBuffer(1+nLen),
1564     &m_nActiveTimeout,
1565     &m_nDownloadTimeout,
1566     &m_nUnused1,
1567     &m_nUnused2,
1568     &m_nUnused3,
1569     &nUseFilter,
1570     &nUnused2,
1571     &nUnused3,
1572     str3.GetBuffer(1+nLen),
1573     str4.GetBuffer(1+nLen),
1574     str5.GetBuffer(1+nLen)
1575     );
1576   m_bUseFilter=(0!=nUseFilter);            
1577   m_bUnused2=(0!=nUnused2);            
1578   m_bUnused3=(0!=nUnused3);            
1579   str1.ReleaseBuffer();
1580   str2.ReleaseBuffer();
1581   str3.ReleaseBuffer();
1582   str4.ReleaseBuffer();
1583   str5.ReleaseBuffer();
1584   m_Target=str1;
1585   int r;
1586   for(r=0;r<RequestTypeMax;r++){
1587     if(0==_tcscmp(arRequestImage[r],str2)){
1588       break;
1589     }
1590   }
1591   m_Request=(RequestType)r;
1592   return CeCosTestPlatform::IsValid(m_Target);
1593 }
1594
1595 CeCosTest::ExecutionParameters::ExecutionParameters (RequestType r,
1596                                                      LPCTSTR  Target,
1597                                                      Duration    nActiveTimeout/*=NOTIMEOUT*/,
1598                                                      Duration    nDownloadTimeout/*=NOTIMEOUT*/):
1599   m_bUseFilter(true),
1600   m_Target(Target),
1601   m_nActiveTimeout(nActiveTimeout),
1602   m_nDownloadTimeout(nDownloadTimeout),
1603   m_Request(r),
1604   m_nUnused1(0),
1605   m_nUnused2(0),
1606   m_nUnused3(0),
1607   m_bUnused2(false),
1608   m_bUnused3(false)
1609 {
1610 }
1611
1612 String CeCosTest::ExecutionParameters::Image() const
1613 {
1614   String str;
1615   str.Format(_T("%s %s %d %d %d %d %d %d %d %d"),(LPCTSTR)PlatformName(),(LPCTSTR)Image(Request()),
1616     ActiveTimeout(),DownloadTimeout(),
1617     m_nUnused1,
1618     m_nUnused2,
1619     m_nUnused3,
1620     m_bUseFilter,
1621     m_bUnused2,
1622     m_bUnused3);
1623   return str;
1624 }
1625
1626 bool CeCosTest::GetTargetReady(String &strHostPort)
1627 {
1628   bool rc=false;
1629   int nTargetReady;
1630   do{
1631     if(!m_pSock->recvInteger(nTargetReady,_T("Target ready"),120*1000)){
1632       Log(_T("Failed to read target ready indicator from server - %s\n"),(LPCTSTR)m_pSock->SocketErrString());
1633       break;
1634     }
1635     switch(nTargetReady){
1636     case 0:
1637       LogString(_T("Failed to reset target"));
1638       break;
1639     case 1:
1640       if(m_pSock->recvString(strHostPort, _T("host:port"))){
1641         TRACE(_T("Instructed to use %s\n"),(LPCTSTR)strHostPort);
1642         rc=true;
1643       } else {
1644         Log(_T("Failed to read host:port - %s\n"),(LPCTSTR)m_pSock->SocketErrString());
1645       }
1646       break;
1647     case 2:
1648       {
1649         String strOutput;
1650         if(m_pSock->recvString(strOutput, _T("output"))){
1651           LogString(strOutput);               
1652         } else {
1653           Log(_T("Failed to read output\n"),(LPCTSTR)m_pSock->SocketErrString());
1654           return false;
1655         }
1656       }
1657       break;
1658     }
1659   } while(2==nTargetReady);
1660   return rc;
1661 }
1662
1663
1664 CeCosTest::ServerStatus CeCosTest::ServerStatusValue(LPCTSTR psz)
1665 {
1666   int s;
1667   for(s=0;s<ServerStatusMax;s++){
1668     if(0==_tcsicmp(psz,arServerStatusImage[s])){
1669       break;
1670     }
1671   }
1672   return (ServerStatus)s;
1673
1674 }
1675
1676 // Gets a directive from the test output (like EXEC:)
1677 bool CeCosTest::GetDirective(LPCTSTR pszDirective, String &str, int &nIndex)
1678 {
1679   bool rc=false;
1680   ENTERCRITICAL;
1681   LPCTSTR pszOutput=(LPCTSTR)m_strOutput;
1682   LPCTSTR pc=_tcsstr(pszOutput+nIndex,pszDirective);
1683   if(pc){
1684     
1685     pc+=_tcslen(pszDirective); // Now after the final character (':') of the directive
1686     if(_TCHAR('<')==*pc){
1687
1688       pc++;
1689
1690       // Extract the argument
1691       str=_T("");
1692       while(*pc){
1693         // Process escapes: FIXME more escapes?
1694         TCHAR c=*pc;
1695         if(_TCHAR('\\')==c){
1696           switch(pc[1]){
1697             case _TCHAR('t'):
1698               c=_TCHAR('\t');
1699               break;
1700             case _TCHAR('n'):
1701               c=_TCHAR('\n');
1702               break;
1703             case _TCHAR('\0'):
1704               pc--; // avoid grief
1705               break;
1706             default:
1707               c=pc[1];
1708               break;
1709           }
1710           pc++;
1711         } else if (_TCHAR('>')==c) {
1712           nIndex=pc+1-pszOutput;
1713           rc=true;
1714           break;
1715         } else if (_TCHAR('\n')==c) {
1716           nIndex=pc+1-pszOutput;
1717           Log(_T("%%%% Unterminated directive: %s"),(LPCTSTR)str);
1718           break;
1719         }
1720         str+=c;
1721         pc++;
1722       }
1723     }
1724   }
1725   LEAVECRITICAL;
1726   return rc;
1727 }
1728
1729 void CeCosTest::GetInferiorCommands(StringArray &arstrInferiorCmds)
1730 {
1731   arstrInferiorCmds.clear();
1732
1733   // Construct commands for gdb.  The commands may be found (semicolon-separated) in the target info:
1734   const String strInferiorCmds(m_ep.Platform()->GdbCmds());
1735   StringArray ar;
1736   int nCmds=strInferiorCmds.Chop(ar,_TCHAR(';'),false);
1737   for(int i=0;i<nCmds;i++){
1738     // Into each command must be substituted:
1739     // Baud rate (%b)  
1740     // Port      (%p)  This will be a serial port (e.g. COM1) or a socket connection (e.g.aloo:8000) depending on circumstances.
1741     // and escapes must be dealt with.
1742     String strCmd;
1743     for(const TCHAR *pc=ar[i];*pc;pc++){
1744       switch(*pc){
1745         // Process escapes: FIXME more escapes?
1746         case _TCHAR('\\'):
1747           switch(pc[1]){
1748             case _TCHAR('t'):
1749               strCmd+=_TCHAR('\t');
1750               pc++;
1751               continue;
1752             case _TCHAR('n'):
1753               strCmd+=_TCHAR('\n');
1754               pc++;
1755               continue;
1756             case _TCHAR('\0'):
1757               continue;
1758             default:
1759               break;
1760           }
1761           break;
1762         case _TCHAR('%'):
1763           switch(pc[1]){
1764             case _TCHAR('%'):
1765               strCmd+=_TCHAR('%');
1766               pc++;
1767               break;
1768             case _TCHAR('b'):
1769               if(0==m_pResource->Baud()){
1770                 goto NextCmd; // Suppress output of this command if there is no baud rate to output
1771               }
1772               strCmd+=String::SFormat(_T("%d"),m_pResource->Baud());
1773               pc++;
1774               continue;
1775             case _TCHAR('p'):
1776               if(_TCHAR('\0')==*(m_pResource->Serial())){
1777                 goto NextCmd; // Suppress output of this command if there is no serial port
1778               }
1779               strCmd+=m_pResource->Serial();
1780               pc++;
1781               continue;
1782             case _TCHAR('\0'):
1783               continue;
1784             default:
1785               break;
1786           }
1787           break;
1788         default:
1789           break;
1790       }
1791       strCmd+=*pc;
1792     }
1793     arstrInferiorCmds.push_back(strCmd);
1794 NextCmd:
1795     ;
1796   }
1797   return;
1798 }
1799