comparison host/tools/ecostest/common/eCosTest.cpp @ 76:435cced73e2f ecos-v1_3_1-release

eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
author jlarmour
date Tue, 28 Mar 2000 14:10:45 +0000
parents
children 6736c52df507
comparison
equal deleted inserted replaced
75:41bf073c0c32 76:435cced73e2f
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 "eCosTrace.h"
46 #include "TestResource.h"
47 #include "eCosTestUtils.h"
48 #include "eCosTestSocket.h"
49 #include "eCosTestSerial.h"
50 #include "eCosTestSerialFilter.h"
51 #include "eCosTestDownloadFilter.h"
52
53 #define WF(n) (n+50)/1000,((n+50)%1000)/100 // Present n as whole and fractional part. Round to nearest least significant digit
54 #define WFS _T("%u.%u") // The format string to output the above
55
56 static int nAuxPort; //hack
57 static int nAuxListenSock; // hack
58
59 #ifdef _WIN32
60 #include "Subprocess.h"
61 #ifdef _DEBUG
62 #define CloseHandle(x) try { ::CloseHandle(x); } catch(...) { TRACE(_T("!!! Exception caught closing handle %08x\n"),x); }
63 #endif
64 #endif
65
66 std::vector<CeCosTest::TargetInfo> CeCosTest::arTargetInfo;
67
68 LPCTSTR const CeCosTest::arResultImage[1+CeCosTest::StatusTypeMax]=
69 {_T("NotStarted"), _T("NoResult"), _T("Inapplicable"), _T("Pass"), _T("DTimeout"), _T("Timeout"), _T("Cancelled"), _T("Fail"), _T("AssertFail"), _T("Unknown")};
70
71 CeCosTest *CeCosTest::pFirstInstance=0;
72 int CeCosTest::InstanceCount=0;
73
74 LPCTSTR const CeCosTest::arServerStatusImage[1+CeCosTest::ServerStatusMax]={
75 _T("Busy"), _T("Ready"), _T("Can't run"), _T("Connection failed"), _T("Locked"), _T("Bad server status")};
76 LPCTSTR CeCosTest::ExecutionParameters::arRequestImage [1+ExecutionParameters::RequestTypeMax]={
77 _T("Run"), _T("Query"), _T("Lock"), _T("Unlock"), _T("Stop"), _T("Bad request") };
78
79 const CeCosTest::TargetInfo CeCosTest::tDefault(_T("Unknown"),_T(""),-1);
80
81 // Do not use spaces in image strings for consideration of _stscanf
82 // One day this can be loadable from some external resource.
83 int CeCosTest::InitTargetInfo(LPCTSTR pszFilename)
84 {
85 TRACE(_T("InitTargetInfo %s\n"),pszFilename);
86 FILE *f=_tfopen(pszFilename,_T("rt"));
87 if(f){
88 enum {BUFSIZE=512};
89 int nLine=0;
90 TCHAR buf[BUFSIZE];
91 while(_fgetts(buf,sizeof(buf)-1,f)){
92 nLine++;
93 StringArray ar;
94 String(buf).Chop(ar);
95 switch(ar.size()){
96 // <target> <prefix> <nType> [<gdbcmds>]
97 case 0:
98 continue;
99 case 3:
100 arTargetInfo.push_back(TargetInfo(ar[0],ar[1],ar[2],0));
101 break;
102 case 1:
103 case 2:
104 ERROR(_T("Illegal configuration at line %d\n"),nLine);
105 continue;
106 default:
107 arTargetInfo.push_back(TargetInfo(ar[0],ar[1],ar[2],ar[3]));
108 break;
109 }
110 }
111 fclose(f);
112 }
113 return arTargetInfo.size();
114 }
115
116 int CeCosTest::InitTargetInfoReg(LPCTSTR pszRegKey)
117 {
118
119 #ifdef _WIN32
120 HKEY hKey;
121 bool rc=ERROR_SUCCESS==RegOpenKeyEx (HKEY_LOCAL_MACHINE, pszRegKey, 0L, KEY_READ, &hKey);
122 DWORD dwSubKeys=0;
123 if(rc){
124 // Found the given key.
125 // Subkeys' names are the target image names:
126 // Subkeys's values are:
127 // Prefix String
128 // Type String
129 // GdbCmd String [optional]
130 FILETIME ftLastWriteTime;
131 DWORD dwMaxSubKeyLen;
132 if(ERROR_SUCCESS==RegQueryInfoKey(hKey,NULL,NULL,NULL,&dwSubKeys,&dwMaxSubKeyLen,NULL,NULL,NULL,NULL,NULL,NULL)){
133 TCHAR *szName=new TCHAR[1+dwMaxSubKeyLen];
134 DWORD dwSizeName=dwMaxSubKeyLen;
135 for(DWORD dwIndex=0;ERROR_SUCCESS==RegEnumKeyEx(hKey, dwIndex, szName, &dwSizeName, NULL, NULL, NULL, &ftLastWriteTime); dwIndex++){
136 HKEY hKey2;
137 if(ERROR_SUCCESS!=RegOpenKeyEx (hKey, szName, 0L, KEY_READ, &hKey2)){
138 ERROR(_T("Failed to open %s\\%s\n"),pszRegKey,szName);
139 rc=false;
140 } else {
141 DWORD dwMaxValueLen;
142 if(ERROR_SUCCESS==RegQueryInfoKey(hKey2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,&dwMaxValueLen,NULL,NULL)){
143 DWORD dwSizePrefix=dwMaxValueLen;
144 DWORD dwSizeGdbCmd=dwMaxValueLen;
145 DWORD dwSizeHwType=dwMaxValueLen;
146 TCHAR *szPrefix=new TCHAR[1+dwMaxValueLen];
147 TCHAR *szGdbCmd=new TCHAR[1+dwMaxValueLen];
148 TCHAR *szHwType=new TCHAR[1+dwMaxValueLen];
149 if(ERROR_SUCCESS==RegQueryValueEx(hKey2, _T("Prefix"),NULL, NULL,(LPBYTE)szPrefix,&dwSizePrefix)){
150 arTargetInfo.push_back(TargetInfo(szName,szPrefix,
151 (ERROR_SUCCESS==RegQueryValueEx(hKey2,_T("HwType"),NULL, NULL,(LPBYTE)szHwType,&dwSizeHwType))?szHwType:_T(""),
152 (ERROR_SUCCESS==RegQueryValueEx(hKey2,_T("GdbCmd"),NULL, NULL,(LPBYTE)szGdbCmd,&dwSizeGdbCmd))?szGdbCmd:0
153 ));
154 }
155 delete [] szPrefix;
156 delete [] szGdbCmd;
157 delete [] szHwType;
158 }
159 RegCloseKey(hKey2);
160 }
161 dwSizeName=dwMaxSubKeyLen;
162 }
163 delete [] szName;
164 }
165 RegCloseKey(hKey);
166 }
167 return rc?dwSubKeys:0;
168 #else // UNIX
169 return 0;
170 #endif
171 }
172
173 bool CeCosTest::Init()
174 {
175 TRACE(_T("CeCosTest::Init\n"));
176 srand( (unsigned)time( NULL ) );
177
178 #ifdef _WIN32
179 WSADATA wsaData;
180 WORD wVersionRequested = MAKEWORD( 2, 0 );
181 WSAStartup( wVersionRequested, &wsaData );
182
183 // get target info from the registry
184 String strPlatformsKey = _T("Software\\Red Hat\\eCos\\");
185 strPlatformsKey += GetGreatestSubkey (_T("Software\\Red Hat\\eCos"));
186 strPlatformsKey += _T("\\Platforms");
187 InitTargetInfoReg (strPlatformsKey);
188
189 // add target info from .eCosrc
190 LPCTSTR psz=_tgetenv(_T("HOMEDRIVE"));
191 if(psz){
192 String strFile(psz);
193 psz=_tgetenv(_T("HOMEPATH"));
194 if(psz){
195 strFile+=psz;
196 }
197 if(_TCHAR('\\')!=strFile[strFile.GetLength()-1]){
198 strFile+=_TCHAR('\\');
199 }
200 strFile+=_T(".eCosrc");
201 InitTargetInfo(strFile);
202 }
203 #else // UNIX
204 sigset_t mask;
205
206 // Clean out all the signals
207 sigemptyset(&mask);
208
209 // Add our sigpipe
210 sigaddset(&mask, SIGPIPE);
211
212 sigprocmask(SIG_SETMASK, &mask, NULL);
213
214 // _WIN32 not defined so get target info from .eCosrc
215 LPCTSTR psz=_tgetenv(_T("HOME"));
216 if(psz){
217 String strFile(psz);
218 strFile+=_T("/.eCosrc");
219 InitTargetInfo(strFile);
220 }
221 #endif
222 if(0==TargetTypeMax()){
223 ERROR(_T("Failed to initialize any targets\n"));
224 }
225 return true;
226 }
227
228 void CeCosTest::Term()
229 {
230
231 TRACE(_T("CeCosTest::Term\n"));
232
233 #ifdef _WIN32
234 WSACleanup();
235 #endif
236 };
237
238 bool CeCosTest::SaveTargetInfo()
239 {
240 #ifdef _WIN32
241 // save target info to the registry
242 String strPlatformsKey = _T("Software\\Red Hat\\eCos\\");
243 strPlatformsKey += GetGreatestSubkey (_T("Software\\Red Hat\\eCos"));
244 strPlatformsKey += _T("\\Platforms");
245 return SaveTargetInfoReg (strPlatformsKey);
246 #else // UNIX
247 // get target info from .eCosrc
248 String strFile=_tgetenv(_T("HOME"));
249 if(strFile.GetLength()>0){
250 strFile+=_T("/.eCosrc");
251 return SaveTargetInfo(strFile);
252 } else {
253 return false;
254 }
255 #endif
256 }
257
258 bool CeCosTest::SaveTargetInfo(LPCTSTR pszFilename)
259 {
260 return false; //FIXME
261 }
262
263 #ifdef _WIN32
264 bool CeCosTest::SaveTargetInfoReg(LPCTSTR pszRegKey)
265 {
266 CProperties::CreateKey(pszRegKey,HKEY_LOCAL_MACHINE);
267 HKEY hKey;
268 bool rc=ERROR_SUCCESS==RegOpenKeyEx (HKEY_LOCAL_MACHINE, pszRegKey, 0L, KEY_ALL_ACCESS, &hKey);
269 if(rc){
270 for(int i=0;i<(signed)arTargetInfo.size();i++){
271 HKEY hKey2;
272 DWORD dwDisp;
273 const TargetInfo &ti=arTargetInfo[i];
274 rc&=(ERROR_SUCCESS==RegCreateKeyEx(hKey,ti.Image(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey2, &dwDisp));
275 if(rc){
276 LPCTSTR pszPrefix=ti.Prefix();
277 LPCTSTR pszGdb =ti.GdbCmd();
278 LPCTSTR pszHwType=CeCosTest::TargetInfo::arHwTypeImage[ti.Type()];
279 rc&=(ERROR_SUCCESS==RegSetValueEx(hKey2,_T("Prefix"),0,REG_SZ, (CONST BYTE *)pszPrefix,(1+_tcslen(pszPrefix))*sizeof TCHAR)) &&
280 (ERROR_SUCCESS==RegSetValueEx(hKey2,_T("HwType"),0,REG_SZ, (CONST BYTE *)pszHwType,(1+_tcslen(pszHwType))*sizeof TCHAR)) &&
281 (ERROR_SUCCESS==RegSetValueEx(hKey2,_T("GdbCmd"),0,REG_SZ, (CONST BYTE *)pszGdb,(1+_tcslen(pszGdb))*sizeof TCHAR));
282 }
283 RegCloseKey(hKey2);
284 }
285 RegCloseKey(hKey);
286 }
287 return rc;
288 }
289 #endif
290
291 static bool CALLBACK IsCancelled(void *pThis)
292 {
293 return CeCosTest::Cancelled==((CeCosTest *)pThis)->Status();
294 }
295
296 // Ctors and dtors:
297 CeCosTest::CeCosTest(const ExecutionParameters &e, LPCTSTR pszExecutable,LPCTSTR pszTitle):
298 m_nRunCount(0),
299 m_nStrippedSize(0),
300 m_nFileSize(0),
301 m_bDownloading(false),
302 m_pSock(0),
303 m_ep(e),
304 m_strTitle(pszTitle),
305 m_Status(NotStarted),
306 m_nDownloadTime(0),
307 m_nTotalTime(0),
308 m_nMaxInactiveTime(0),
309 m_pPort(0)
310 {
311 SetExecutable (pszExecutable);
312
313 TRACE(_T("%%%% Create test instance %08x count:=%d\n"),this,InstanceCount+1);
314
315 GetPath(m_strPath);
316
317 ENTERCRITICAL;
318 InstanceCount++;
319 m_pNextInstance=pFirstInstance;
320 if(m_pNextInstance){
321 m_pNextInstance->m_pPrevInstance=this;
322 }
323 m_pPrevInstance=0;
324 pFirstInstance=this;
325 LEAVECRITICAL;
326
327 }
328
329 CeCosTest::~CeCosTest()
330 {
331 TRACE(_T("%%%% Delete test instance %08x\n"),this);
332 Cancel();
333 CloseSocket();
334 if(m_pPort){
335 m_pPort->Release();
336 //delete m_pPort;
337 //m_pPort=0;
338 }
339
340 VTRACE(_T("~CeCosTest(): EnterCritical and decrease instance count\n"));
341 ENTERCRITICAL;
342 InstanceCount--;
343 TRACE(_T("%%%% Destroy instance. Instance count:=%d\n"),InstanceCount);
344 if(pFirstInstance==this){
345 pFirstInstance=m_pNextInstance;
346 }
347 if(m_pPrevInstance){
348 m_pPrevInstance->m_pNextInstance=m_pNextInstance;
349 }
350 if(m_pNextInstance){
351 m_pNextInstance->m_pPrevInstance=m_pPrevInstance;
352 }
353 LEAVECRITICAL;
354 }
355
356 bool CeCosTest::RunRemote (LPCTSTR pszRemoteHostPort)
357 {
358 bool rc=false;
359 m_nRunCount++;
360 TRACE(_T("RunRemote\n"));
361 m_strExecutionHostPort=pszRemoteHostPort;
362 SetPath(m_strPath);
363 m_Status=NotStarted;
364
365 VTRACE(_T("RemoteThreadFunc()\n"));
366
367 // Find a server.
368 ConnectForExecution();
369 if(Cancelled!=Status()){
370 if(ServerSideGdb()){
371 // The executable is transmitted to the server for execution.
372 // Send file size
373 if(m_pSock->sendInteger(m_nFileSize,_T("file size"))&&m_nFileSize>0){
374 int nBufSize=MIN(10000,m_nFileSize);
375 Buffer b(nBufSize);
376 TRACE(_T("Sending [%d bytes]\n"), m_nFileSize);
377 int nToSend=m_nFileSize;
378 FILE *f1=_tfopen(m_strExecutable,_T("rb"));
379 if(0==f1){
380 Log(_T("Failed to open %s - %s\n"),(LPCTSTR )m_strExecutable,strerror(errno));
381 } else {
382 while (nToSend>0){
383 int nRead=fread( b.Data(), 1, nBufSize, f1);
384 if(nRead<=0){
385 Log(_T("Failure reading %s - %s\n"),(LPCTSTR )m_strExecutable,strerror(errno));
386 break;
387 }
388 if(!send( b.Data(), nRead, _T("executable"))){
389 Log(_T("Failure sending %s - %s\n"),(LPCTSTR )m_strExecutable,(LPCTSTR )m_pSock->SocketErrString());
390 break;
391 }
392 nToSend-=nRead;
393 }
394 fclose(f1);
395 f1=0;
396 if(nToSend>0){
397 TRACE(_T("done [%d bytes sent]\n"),m_nFileSize-nToSend);
398 Log(_T("Failed to transmit %s - %d/%d bytes sent\n"),(LPCTSTR )m_strExecutable,m_nFileSize-nToSend,m_nFileSize);
399 } else {
400 TRACE(_T("done\n"));
401 rc=true;
402 }
403 }
404 if(!recvResult(9*1000*60)){ // nine minutes
405 Log(_T("Failed to receive result from remote server\n"));
406 rc=false;
407 }
408 m_pSock->sendInteger(1); // send an ack [n'importe quoi]
409 CloseSocket();
410 }
411 } else {
412 // The server sets up a connection between port and tcp/ip socket, and gdb is run locally
413 String strHostPort,strOutput;
414 // Big timeout here because we have to wait for the target to be reset
415 // We do this:
416 // do {
417 // target ready indicator (0==fail, 1==ready, 2==fail and will retry)
418 // any output so far
419 // } while (2==target ready indicator)
420 // read host:port
421 if(GetTargetReady(strHostPort)){
422 ENTERCRITICAL;
423 m_pPort=new CTestResource(Target(), strHostPort, 0);
424 m_pPort->Use();
425 LEAVECRITICAL;
426 RunLocal();
427 delete m_pPort;
428 m_pPort=0;
429 m_pSock->sendInteger(123,_T("Terminating ack"));
430 m_pSock->Close();
431 rc=true;
432 }
433 }
434 }
435 TRACE(_T("RemoteThreadFunc(): Exiting\n"));
436 return rc;
437 }
438
439 // Run the test locally
440 bool CeCosTest::RunLocal()
441 {
442 m_nRunCount++;
443 bool rc=false;
444 if(0==CTestResource::Count(m_ep)){
445 Log(_T("Cannot run a %s test\n"),m_ep.Target());
446 } else {
447
448 TRACE(_T("Run %s, timeouts: active=%d elapsed=%d\n"),(LPCTSTR )m_strExecutable, ActiveTimeout(), DownloadTimeout());
449 m_Status=NotStarted;
450 m_tPrevSample=Now();
451 m_tGdbCpuTime=0;
452
453 GetPath(m_strPath);
454
455 TRACE(_T("LocalThreadFunc - target=%s\n"),Target());
456 // Acquire a port (our caller may have done this for us)
457 VTRACE(_T("LocalThreadFunc():Tring to acquire a port\n"));
458 if(0==m_pPort){
459 for(;;){
460 m_pPort=CTestResource::GetResource(m_ep);
461 if(m_pPort||Cancelled==Status()){
462 break;
463 }
464 Sleep(2000);
465 TRACE(_T("Waiting for a port\n"));
466 }
467 }
468 VTRACE(_T("\nPort acquired!\n"));
469
470 if(Cancelled!=Status()){
471 // This means we have acquired a local port
472 bool bTargetReady=false;
473 if(!m_pPort->HasReset()){
474 bTargetReady=true;
475 } else {
476 bTargetReady=(CResetAttributes::RESET_OK==Reset(false));
477 }
478 // we may proceed to execute the test
479 if(bTargetReady){
480 SetStatus(NotStarted);
481 if(_TCHAR('\0')!=*(m_pPort->Serial())){
482 // No elapsed timeout given - calculate from knowledge of executable size and baud rate
483 // 10 baud ~= 1 byte/sec, but we halve this to account for download in hex :-(
484 // This means that a 200k executable is given ~100 seconds
485 // In any case the whole thing is an overestimate [we should use the stripped size]
486 // We use a minimum of 30 seconds and add 50% for safety
487 int nSpeed=((0==m_pPort->Baud()?9600:m_pPort->Baud())/10)/2; // Hex
488 nSpeed/=2; // Safety
489 if(NOTIMEOUT==m_ep.DownloadTimeout()){
490 m_ep.SetDownloadTimeout (1000*MAX(60,(m_nStrippedSize/nSpeed)));
491 }
492 TRACE(_T("Timeout=%d\n"),m_ep.DownloadTimeout());
493 }
494 if(NOTIMEOUT==m_ep.ActiveTimeout()){
495 m_ep.SetActiveTimeout(1000*(_TCHAR('\0')==*(m_pPort->Serial())?300:30));
496 }
497
498 {
499 // Construct commands for gdb
500 const TargetInfo &t=Target(Target());
501 TargetInfo::HwType hwt=t.Type();
502 StringArray arstrGdbCmds;
503 LPCTSTR pszPrompt;
504 String strGdb;
505 // running using gdb
506 pszPrompt=_T("(gdb) ");
507 strGdb.Format(_T("%s-gdb -nw %s"),t.Prefix(),(LPCTSTR)CygPath(m_strExecutable));
508 // Tell gdb its paper size :-)
509 arstrGdbCmds.push_back(_T("set height 0"));
510 arstrGdbCmds.push_back(_T("set remotedebug 0"));
511
512 if(_TCHAR('\0')!=*t.GdbCmd()){
513 arstrGdbCmds.push_back(t.GdbCmd());
514 }
515
516 String str;
517 if(_TCHAR('\0')!=*(m_pPort->Serial())){
518 // Talking remotely
519 if(CeCosTestSocket::IsLegalHostPort(m_pPort->Serial())){
520 // Talking to a tcp/ip socket
521 arstrGdbCmds.push_back(_T("set watchdog 0"));
522 } else {
523 // Talking to a serial port
524 str.Format(_T("set remotebaud %d"),m_pPort->Baud());
525 arstrGdbCmds.push_back(str);
526 }
527 str.Format(_T("target remote %s"),m_pPort->Serial());
528 #ifdef _WIN32
529 // Serial names on windows must be in l.c. (gdb bug)
530 for(TCHAR *c=str.GetBuffer();*c;c++){
531 if(isalpha(*c)){
532 *c=(TCHAR)_totlower(*c);
533 }
534 }
535 str.ReleaseBuffer();
536 #endif
537 arstrGdbCmds.push_back(str);
538 }
539
540 if(TargetInfo::SYNTHETIC!=hwt){
541 arstrGdbCmds.push_back(_T("load"));
542 }
543
544 arstrGdbCmds.push_back(_T("break cyg_test_exit"));
545 arstrGdbCmds.push_back(_T("break cyg_assert_fail"));
546 if(/*start hack*/BreakpointsOperational()/*end hack*/){
547 arstrGdbCmds.push_back(_T("break cyg_test_init"));
548 }
549
550 switch(hwt){
551 case TargetInfo::SYNTHETIC:
552 case TargetInfo::SIM:
553 arstrGdbCmds.push_back(_T("run"));
554 break;
555 case TargetInfo::HARDWARE:
556 case TargetInfo::HARDWARE_NO_BP:
557 case TargetInfo::REMOTE_SIM:
558 arstrGdbCmds.push_back(_T("cont")); // run the program
559 break;
560 default:
561 assert(false);
562 }
563
564 if(BreakpointsOperational()){
565 str.Format(_T("set cyg_test_is_simulator=%d"),hwt);
566 arstrGdbCmds.push_back(str);
567 arstrGdbCmds.push_back(_T("cont")); // continue from cyg_test_init breakpoint
568 }
569
570 // run/cont command must be the last (logic in DriveGdb)
571
572 TRACE(_T("Calling RunGdb\n"));
573 RunGdb(strGdb,pszPrompt,arstrGdbCmds);
574 rc=true;
575 }
576 }
577 }
578 TRACE(_T("LocalThreadFunc - releasing resource\n"));
579 if(m_pPort){
580 m_pPort->Release();
581 m_pPort=0;
582 }
583 TRACE(_T("LocalThreadFunc - exiting\n"));
584 }
585
586 return rc;
587 }
588
589 void CeCosTest::Cancel ()
590 {
591 SetStatus(Cancelled);
592 }
593
594 CeCosTest::ServerStatus CeCosTest::Connect (String strHost,int port, CeCosTestSocket *&pSock, const ExecutionParameters &e,String &strInfo,Duration dTimeout)
595 {
596 // Find out whether this host is receptive
597 ServerStatus s=CONNECTION_FAILED;
598 pSock=new CeCosTestSocket(strHost,port,dTimeout);
599 int nStatus;
600 if(pSock->Ok() &&
601 pSock->sendString(e.Image(), _T("execution parameters")) &&
602 pSock->recvInteger(nStatus,_T("ready status")) &&
603 pSock->recvString(strInfo)){
604 s=(ServerStatus)MIN(nStatus,ServerStatusMax);
605 }
606 if(SERVER_READY!=s || ExecutionParameters::RUN!=e.Request()){
607 delete pSock;
608 pSock=0;
609 }
610 return s;
611 }
612
613 // Initiate a connection to hostName:nPort and acquire the ready status [retry until this is achieved]
614 // The socket (m_pSock) is left open.
615 // This function is either called with m_strExecutionHostPort already set to a desired server
616 // or else m_strExecutionHostPort empty (in which case the server is / dynamically)
617
618 void CeCosTest::ConnectForExecution ()
619 {
620 bool bSchedule=(0==m_strExecutionHostPort.GetLength());
621 Duration nDelay=2000;
622
623 m_pSock=0;
624
625 bool *arbHostTried=0;
626
627 while(Cancelled!=Status()){
628 StringArray arstrHostPort,arstrTries;
629 int nChoices;
630
631 if(bSchedule){
632 if(!CTestResource::GetMatches(m_ep,arstrHostPort)){
633 Log(_T("Could not establish matches\n"));
634 continue;
635 }
636 nChoices=arstrHostPort.size();
637 if(nChoices>0){
638 TRACE(_T("ConnectForExecution: choices are:\n"));
639 for(int i=0;i<nChoices;i++){
640 TRACE(_T("\t%s\n"),(LPCTSTR )arstrHostPort[i]);
641 }
642 }
643 } else {
644 // Server has already been picked by caller
645 nChoices=1;
646 String str;
647 arstrHostPort.push_back(m_strExecutionHostPort);
648 }
649
650 if(nChoices>0){
651 delete [] arbHostTried;
652 arbHostTried=new bool[nChoices];
653 for(int i=0;i<nChoices;i++){
654 arbHostTried[i]=false;
655 }
656
657 // Loop around the choices
658 for(int nUntried=nChoices;nUntried>0;nUntried--) {
659 // Select one we haven't tried already:
660 int nChoice;
661 do {
662 nChoice=rand() % nChoices;
663 } while (arbHostTried[nChoice]);
664
665 m_strExecutionHostPort=arstrHostPort[nChoice];
666
667 TRACE(_T("ConnectForExecution: chosen %s\n"),(LPCTSTR )m_strExecutionHostPort);
668 String strHost;
669 int nPort;
670 CeCosTestSocket::ParseHostPort(m_strExecutionHostPort, strHost, nPort);
671 if(nPort){
672 // If we're using the resource server we had better check that the host
673 // we are about to lock has not been resource-locked (the other match checks
674 // will of course always succeed)
675 String strInfo;
676 ServerStatus s=bSchedule && !CTestResource::Matches(strHost, nPort,m_ep)?
677 SERVER_LOCKED:
678 Connect(strHost,nPort,m_pSock,m_ep,strInfo);
679 arbHostTried[nChoice]=true;
680 TRACE(_T("Connect: %s says %s %s\n"),(LPCTSTR )m_strExecutionHostPort,Image(s),(LPCTSTR )strInfo);
681 CTestResource *pResource=CTestResource::Lookup(strHost,nPort);
682 if(pResource){
683 String str;
684 str.Format(_T("%s %s %s"),(LPCTSTR )pResource->Output(),(LPCTSTR )strInfo,Image(s));
685 arstrTries.push_back(str);
686 }
687 if(SERVER_READY==s){
688 // So that's ok then. We're outta here.
689 Interactive(_T("Connected to %s\n"),(LPCTSTR )m_strExecutionHostPort);
690 goto Done;
691 } else {
692 delete m_pSock;
693 m_pSock=0;
694 }
695 }
696 }
697 }
698
699 Interactive(_T("Warning - could not connect to any test servers:\n"));
700 if(arstrTries.size()>0){
701 for(unsigned int i=0;i<arstrTries.size();i++){
702 Interactive(_T(" %s\n"),(LPCTSTR )arstrTries[i]);
703 }
704 } else {
705 Interactive(_T("No servers available to execute %s test:\n"),m_ep.Target());
706 ENTERCRITICAL;
707 for(CTestResource *pResource=CTestResource::First();pResource;pResource=pResource->Next()){
708 Interactive(_T(" %s\n"),(LPCTSTR )pResource->Output());
709 }
710 LEAVECRITICAL;
711 }
712 Interactive(_T("Retry in %d seconds...\n"),nDelay/1000);
713
714 // We have tried all possibilities - sleep before retrying
715 Sleep(nDelay);
716
717 if(Cancelled==m_Status){
718 TRACE(_T("ConnectForExecution : cancelled\n"));
719 goto Done;
720 }
721 if(nDelay<20*1000){
722 nDelay+=rand() % 500;
723 }
724 }
725 Done:
726 delete [] arbHostTried;
727 }
728
729 void CeCosTest::LogResult()
730 {
731 CeCosTrace::Out(ResultString());
732 CeCosTrace::Out(_T("\n") );
733 CeCosTrace::Trace(_T("%s\n"),ResultString());
734 }
735
736 void CeCosTest::SetStatus (StatusType status)
737 {
738 ENTERCRITICAL;
739 if((int)status>(int)m_Status){
740 TRACE(_T("Status <- %s\n"),Image(status));
741 m_Status=status;
742 }
743 LEAVECRITICAL;
744 }
745
746 bool CeCosTest::WaitForAllInstances(int nPoll,Duration nTimeout)
747 {
748 Time t0=Now();
749 while(InstanceCount>0){
750 Sleep(nPoll);
751 if(NOTIMEOUT!=nTimeout && Now()-t0>nTimeout){
752 return false;
753 }
754 }
755 return true;
756 }
757
758 void CeCosTest::DeleteAllInstances()
759 {
760 while(pFirstInstance){
761 delete pFirstInstance;
762 }
763 }
764
765 void CeCosTest::CancelAllInstances()
766 {
767 ENTERCRITICAL;
768 for(CeCosTest *pTest=pFirstInstance;pTest;pTest=pTest->m_pNextInstance){
769 pTest->Cancel();
770 }
771 LEAVECRITICAL;
772 }
773
774 // The same format is used for _stscanf as for Format (which is like printf), so restrict to the format specifiers
775 // the former is happy with. In particular, do not use %-3s etc...
776
777 LPCTSTR CeCosTest::pszFormat=
778 // 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
779 _T("%04d-%02d-%02d %02d:%02d:%02d ") // Time
780 _T("%15s ") // Execution host:port
781 _T("%16s ") // Target
782 _T("%30s ") // Executable tail
783 _T("%11s ") // Result
784 _T("%dk/%dk ") // Sizes
785 _T("D=") WFS _T("/") WFS _T(" Total=") WFS _T(" ") // Times
786 _T("E=") WFS _T("/") WFS _T(" ")
787 _T("\"%s\"");
788
789 bool CeCosTest::Value (
790 LPCTSTR pszStr,
791 struct tm &t,
792 StatusType &status,
793 String &target,
794 String &strExecutionHostPort,
795 String &strExecutableTail,
796 String &strTitle,
797
798 int &nFileSize,
799 Duration &nTotalTime,
800 Duration &nMaxInactiveTime,
801 Duration &nDownloadTime,
802 Duration &nDownloadTimeout,
803 Duration &nActiveTimeout,
804
805 int &nDownloadedSize)
806 {
807 int nLen=_tcslen(pszStr);
808 String strStatus;
809
810 nFileSize=nTotalTime=nMaxInactiveTime=nDownloadTime=nDownloadTimeout=nActiveTimeout=nDownloadedSize=0;
811
812 int nTotalTimeFrac=0;
813 int nMaxInactiveTimeFrac=0;
814 int nActiveTimeoutFrac=0;
815 int nDownloadTimeFrac=0;
816 int nDownloadTimeoutFrac=0;
817
818 static String strFormat;
819 if(0==strFormat.GetLength()){
820 // Construct a version of the format string sans length attributes for %s items
821 LPCTSTR c=pszFormat;
822 TCHAR *d=strFormat.GetBuffer(_tcslen(pszFormat));
823 while(_TCHAR('\0')!=*c){
824 if(_TCHAR('%')==c[0] && _istdigit(c[1])){
825 *d++=_TCHAR('%');
826 do {
827 c++;
828 } while (_istdigit(*c));
829 }
830 *d++=*c++;
831 }
832 *d=_TCHAR('\0');
833 strFormat.ReleaseBuffer();
834 }
835
836 _stscanf(pszStr,
837 strFormat,
838 &t.tm_year,&t.tm_mon,&t.tm_mday,
839 &t.tm_hour,&t.tm_min,&t.tm_sec, // Time of day
840 strExecutionHostPort.GetBuffer(1+nLen), // Execution host:port
841 target.GetBuffer(1+nLen), // Target
842 strExecutableTail.GetBuffer(1+nLen), // Executable
843 strStatus.GetBuffer(1+nLen), // Result
844 &nDownloadedSize,&nFileSize, // Sizes
845 &nDownloadTime,&nDownloadTimeFrac, // Times
846 &nDownloadTimeout,&nDownloadTimeoutFrac,
847 &nTotalTime,&nTotalTimeFrac,
848 &nMaxInactiveTime,&nMaxInactiveTimeFrac,
849 &nActiveTimeout,&nActiveTimeoutFrac,
850 strTitle.GetBuffer(1+nLen) // Title
851 );
852
853 strExecutionHostPort.ReleaseBuffer();
854 target.ReleaseBuffer();
855 strExecutableTail.ReleaseBuffer();
856 strStatus.ReleaseBuffer();
857 strTitle.ReleaseBuffer();
858 status=StatusTypeValue(strStatus);
859
860 LPCTSTR c1=_tcschr(pszStr,_TCHAR('"'));
861 if(c1){
862 c1++;
863 LPCTSTR c2=_tcschr(c1+1,_TCHAR('"'));
864 if(c2){
865 strTitle=String(c1,c2-c1);
866 }
867 }
868
869 nTotalTime=nTotalTime*1000+nTotalTimeFrac*100;
870 nMaxInactiveTime=nMaxInactiveTime*1000+nMaxInactiveTimeFrac*100;
871 nActiveTimeout=nActiveTimeout*1000+nActiveTimeoutFrac*100;
872 nDownloadTime=nDownloadTime*1000+nDownloadTimeFrac*100;
873 nDownloadTimeout=nDownloadTimeout*1000+nDownloadTimeoutFrac*100;
874
875 nFileSize*=1024;
876 nDownloadedSize*=1024;
877 t.tm_year-=1900;
878 t.tm_mon--;
879 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 &&
880 status!=StatusTypeMax
881 //&& exetype!=ExecutionParameters::ExecutableTypeMax
882 ;
883 }
884
885 LPCTSTR const CeCosTest::ResultString(bool bIncludeOutput) const
886 {
887 String strTitle(m_strTitle);
888 String strExecutionHostPort(m_strExecutionHostPort);
889
890 if(0==strTitle.GetLength()){
891 strTitle=CeCosTestUtils::SimpleHostName();
892 strTitle+=_TCHAR(':');
893 strTitle+=m_strExecutable;
894 }
895
896 if(0==strExecutionHostPort.GetLength()){
897 strExecutionHostPort=CeCosTestUtils::SimpleHostName();
898 strExecutionHostPort+=_T(":0");
899 }
900
901 ENTERCRITICAL;
902 time_t ltime;
903 time(&ltime);
904 struct tm *now=localtime( &ltime );
905
906 m_strResultString.Format(
907 pszFormat,
908 1900+now->tm_year,1+now->tm_mon,now->tm_mday,
909 now->tm_hour,now->tm_min,now->tm_sec, // Time of day
910 (LPCTSTR )strExecutionHostPort, // Execution host:port
911 Target(), // Target
912 ExecutableTail(), // Executable
913 Image(Status()), // Result
914 m_nStrippedSize/1024,m_nFileSize/1024, // Sizes
915 WF(m_nDownloadTime),WF(DownloadTimeout()),WF(m_nTotalTime),// Times
916 WF(m_nMaxInactiveTime),WF(ActiveTimeout()),
917 (LPCTSTR )strTitle // Title
918 );
919 if(bIncludeOutput && m_strOutput.GetLength()>0){
920 m_strResultString+=_TCHAR('\n');
921 m_strResultString+=m_strOutput;
922 }
923 LEAVECRITICAL;
924 return m_strResultString;
925 }
926
927 // Run as a server, listening on the port given as parameter
928 bool CeCosTest::RunAgent(int nTcpPort)
929 {
930 bool bLocked=false;
931
932 nAuxPort=nTcpPort+3000;//hack
933 nAuxListenSock=CeCosTestSocket::Listen(nAuxPort);//hack
934 if(-1!=nAuxListenSock){
935 // Create socket
936 int nSock = CeCosTestSocket::Listen(nTcpPort);
937 int nLastClient=0;
938 int nRejectionCount=0;
939 if (-1!=nSock) {
940 for (;;) {
941 try {
942 CeCosTestSocket *pSock=new CeCosTestSocket(nSock); // AcceptThreadFunc deletes if not deleted below
943 String str;
944 // Read the execution parameters
945 if(!pSock->recvString(str)){
946 // Socket error on the recv - nothing much we can do
947 TRACE(_T("RunAgent : could not read execution parameters\n"));
948 delete pSock;
949 pSock=0;
950 } else {
951 ExecutionParameters e;
952 e.FromStr(str);
953 TRACE(_T("Execution parameters: %s\n"),(LPCTSTR)e.Image());
954 bool bNuisance=false;
955 ServerStatus s;
956 CTestResource *pPort=0;
957 String strInfo;
958 if(!e.IsValid()){
959 // Looks like a confused client ...
960 strInfo.Format(_T("Bad target value %s read from client\n"),e.Target());
961 s=SERVER_CANT_RUN;
962 } else if(0==CTestResource::Count(e)){
963 // No chance of running this test
964 strInfo.Format(_T("Cannot run a %s test from this server\n"),e.Target());
965 s=SERVER_CANT_RUN;
966 } else {
967 switch(e.Request()) {
968 case ExecutionParameters::LOCK:
969 if(bLocked){
970 s=SERVER_BUSY;
971 } else {
972 WaitForAllInstances(1000,NOTIMEOUT);
973 bLocked=true;
974 s=SERVER_LOCKED;
975 }
976 break;
977 case ExecutionParameters::UNLOCK:
978 if(bLocked){
979 bLocked=false;
980 s=SERVER_READY;
981 } else {
982 s=SERVER_BUSY;
983 }
984 break;
985 case ExecutionParameters::QUERY:
986 if (bLocked) {
987 s=SERVER_LOCKED;
988 } else {
989 pPort=CTestResource::GetResource(e);
990 if(0==pPort){
991 s=SERVER_BUSY;
992 strInfo.Format(_T("serving %s"),(LPCTSTR )CeCosTestSocket::ClientName(nLastClient));
993 } else {
994 s=SERVER_READY;
995 pPort->Release();
996 pPort=0;
997 }
998 }
999 break;
1000 case ExecutionParameters::RUN:
1001 if (bLocked) {
1002 s=SERVER_LOCKED;
1003 } else {
1004 pPort=CTestResource::GetResource(e);
1005 if(0==pPort){
1006 // We must disappoint our client
1007 nRejectionCount++;
1008 strInfo.Format(_T("serving %s"),(LPCTSTR )CeCosTestSocket::ClientName(nLastClient));
1009 s=SERVER_BUSY;
1010 /*
1011 } else if(nLastClient==pSock->Client() && nRejectionCount>10) {
1012 // Don't answer the phone to a nuisance caller
1013 s=SERVER_BUSY;
1014 bNuisance=true;
1015 nRejectionCount--;
1016 pPort->Release();
1017 pPort=0;
1018 */
1019 } else {
1020 s=SERVER_READY;
1021 nRejectionCount=0;
1022 nLastClient=pSock->Client();
1023 }
1024 }
1025 break;
1026 case ExecutionParameters::STOP:
1027 s=SERVER_READY;
1028 break;
1029 default:
1030 s=SERVER_CANT_RUN;
1031 }
1032 }
1033
1034 #ifndef VERBOSE
1035 if(ExecutionParameters::QUERY!=e.Request())
1036 #endif
1037 TRACE(_T("RunAgent : %s request tActive=%d tElapsed=%d Target=%s Reply status=%s %s Nuisance=%d\n"),
1038 e.Image(e.Request()),
1039 e.ActiveTimeout(),e.DownloadTimeout(),e.Target(),
1040 Image(s),
1041 (LPCTSTR )strInfo,
1042 bNuisance);
1043
1044 bool bSendok=pSock->sendInteger(s) && pSock->sendString(strInfo);
1045
1046 TRACE(_T("RunAgent(1)\n"));
1047
1048 if(SERVER_READY==s && bSendok && ExecutionParameters::RUN==e.Request()){
1049
1050 // Create a new class instance
1051 // AcceptThreadFunc deletes the instance and closes new_sock
1052 // RunLocal, called by AcceptThreadFunc, releases the port
1053 // We dream up a temporary name for the executable
1054 // No need for meaningful callback, but must run asynchronously
1055 String strTempFile;
1056 ENTERCRITICAL;
1057 strTempFile.Format(_T("%s-%s-%d"),_ttmpnam(0),e.Target(),nTcpPort);
1058 LEAVECRITICAL;
1059
1060 CeCosTest *pTest=new CeCosTest(e,strTempFile);
1061 pTest->m_pSock=pSock;
1062 pTest->m_strExecutionHostPort.Format(_T("%s:%d"),CeCosTestUtils::HostName(),nTcpPort);
1063 pTest->m_pPort=pPort;
1064
1065 CeCosThreadUtils::RunThread(SAcceptThreadFunc,pTest,_T("SAcceptThreadFunc"));
1066 // AcceptThreadFunc deletes pSock
1067
1068 } else {
1069 TRACE(_T("RunAgent(2)\n"));
1070 delete pSock;
1071 pSock=0;
1072 if(pPort){
1073 pPort->Release();
1074 pPort=0;
1075 }
1076 TRACE(_T("RunAgent(3)\n"));
1077 if(CeCosTest::ExecutionParameters::STOP==e.Request()){
1078 CancelAllInstances();
1079 WaitForAllInstances(1000,20*1000);
1080 break;
1081 }
1082 }
1083 }
1084 }
1085 catch(...){
1086 TRACE(_T("!!! Exception caught in RunAgent()\n"));
1087 }
1088 }
1089 CeCosTestSocket::CloseSocket (nSock);
1090 }
1091 VTRACE(_T("RunAgent(): returning false\n"));
1092 CeCosTestSocket::CloseSocket(nAuxListenSock);
1093 }
1094 return false;
1095 }
1096
1097 CeCosTest::StatusType CeCosTest::StatusTypeValue(LPCTSTR pszStr)
1098 {
1099 for(int i=0;i<StatusTypeMax;i++){
1100 StatusType t=(StatusType)i;
1101 if(0==_tcsicmp(Image(t),pszStr)){
1102 return t;
1103 }
1104 }
1105 return StatusTypeMax;
1106 }
1107
1108 // Thread to run ConnectSocketToSerial
1109 void CeCosTest::ConnectSocketToSerialThreadFunc()
1110 {
1111 TRACE(_T("ConnectSocketToSerialThreadFunc sock=%d\n"),nAuxListenSock);
1112 {
1113
1114 CeCosTestSerialFilter serial_filter;
1115 CeCosTestSocket::FilterFunc *serial_filter_function =
1116 &SerialFilterFunction;
1117
1118 CeCosTestDownloadFilter download_filter;
1119 CeCosTestSocket::FilterFunc *download_filter_function =
1120 &DownloadFilterFunction;
1121
1122 bool accept_connection = true;
1123
1124 CeCosTestSerial serial;
1125 serial.SetBlockingReads(false);
1126 bool rc=false;
1127 // Open serial device.
1128 if (!serial.Open(m_pPort->Serial(),m_pPort->Baud())){
1129 ERROR(_T("Couldn't open port %s\n"),m_pPort->Serial());
1130 } else {
1131 while(accept_connection) {
1132 // Flush the serial buffer.
1133 serial.Flush();
1134 TRACE(_T("ConnectSocketToSerial: waiting for connection...\n"));
1135 CeCosTestSocket socket;
1136 if(!socket.Accept(nAuxListenSock,&m_bStopConnectSocketToSerial)){
1137 ERROR(_T("ConnectSocketToSerial - couldn't accept\n"));
1138 break;
1139 } else if (m_pSock->Client() != socket.Client()){
1140 // Make sure the client is who we think it is...
1141 TRACE(_T("ConnectSocketToSerialThread - illegal connection attempted from %s\n"),(LPCTSTR )socket.ClientName(socket.Client()));
1142 } else {
1143 try {
1144 rc=CeCosTestSocket::ConnectSocketToSerial(socket,serial,m_ep.m_bUseFilter?serial_filter_function:NULL, (void*)&serial_filter, m_ep.m_bUseFilter?download_filter_function:NULL, (void*)&download_filter, &m_bStopConnectSocketToSerial);
1145
1146 // If the download filter was just active, it may
1147 // allow the session to continue.
1148 accept_connection = download_filter.ContinueSession();
1149
1150 }
1151 catch (LPCTSTR pszMsg){
1152 Log(_T("!!! ConnectSocketToSerial exception caught: %s!!!\n"),pszMsg);
1153 rc=false;
1154 break;
1155 }
1156 catch (...){
1157 Log(_T("!!! ConnectSocketToSerial exception caught!!!\n"));
1158 rc=false;
1159 break;
1160 }
1161 }
1162 }
1163 }
1164 TRACE(_T("ConnectSocketToSerial : done\n"));
1165 }
1166 }
1167
1168 static bool CALLBACK DerefBool(void *pParam)
1169 {
1170 return *(bool *)pParam;
1171 }
1172
1173 // Function called (on a separate thread) to process a successful connection to the RunAgent loop
1174 void CeCosTest::AcceptThreadFunc()
1175 {
1176 SetPath(m_strPath);
1177
1178 if(ServerSideGdb()){
1179 int n;
1180 if(m_pSock->recvInteger(n,_T("file size"))){
1181 m_nFileSize=n;
1182 // Read file from the socket
1183 bool bCanRun=true;
1184 TRACE(_T("AcceptThreadFunc file size=%d reading...\n"),m_nFileSize);
1185 FILE *f2;
1186 f2=_tfopen(m_strExecutable,_T("wb"));
1187 if(0==f2){
1188 Log(_T("Could not create %s - %s\n"),(LPCTSTR )m_strExecutable,strerror(errno));
1189 bCanRun=false;
1190 }
1191 unsigned int nBufSize=MIN(100000,m_nFileSize);
1192 Buffer b(nBufSize);
1193 unsigned int nWritten=0;
1194 unsigned int nRead=0;
1195 while(nRead<m_nFileSize){
1196 int nToRead=MIN(nBufSize,m_nFileSize-nRead);
1197 if(!recv( b.Data(), nToRead, _T("executable"))){
1198 break;
1199 }
1200 nRead+=nToRead;
1201 if(0!=f2){
1202 char *c=(char *)b.Data();
1203 while(nToRead>0){
1204 int w=fwrite(c,1,nToRead,f2);
1205 if(-1==w){
1206 Log(_T("Write error on %s - %s\n"),(LPCTSTR )m_strExecutable,strerror(errno));
1207 bCanRun=false;
1208 break;
1209 }
1210 nWritten+=w;
1211 c+=w;
1212 nToRead-=w;
1213 }
1214 }
1215 }
1216 TRACE(_T("Accept - done reading [%d bytes read, %d bytes written]\n"),nRead,nWritten);
1217 if(0!=f2){
1218 fclose(f2);
1219 _tchmod(m_strExecutable,00700); // user read, write and execute
1220 }
1221 if(0!=f2 && m_nFileSize!=nWritten){
1222 Log(_T("Failed to create %s correctly [%d/%d bytes written]\n"),(LPCTSTR )m_strExecutable, nWritten, m_nFileSize);
1223 bCanRun=false;
1224 }
1225 SetExecutable(m_strExecutable); // to set stripped length and title
1226 RunLocal();
1227 _tunlink(m_strExecutable);
1228 }
1229 sendResult();
1230 m_pSock->recvInteger(n); // receive an ack
1231 } else {
1232 bool bTargetReady;
1233 if(_TCHAR('\0')==*(m_pPort->ResetString())){
1234 bTargetReady=true;
1235 TRACE(_T("No reset possible\n"));
1236 } else {
1237 Log(_T("Resetting target using %s"),(LPCTSTR)m_pPort->ResetString());
1238 bTargetReady=(CResetAttributes::RESET_OK==Reset(true));
1239 }
1240 TRACE(_T("Send Target Ready indicator=%d\n"),bTargetReady);
1241 m_pSock->sendInteger(bTargetReady,_T("target ready indicator"));
1242
1243 int nAck;
1244 int dTimeout=m_ep.DownloadTimeout()+MAX(3*m_ep.ActiveTimeout(),15*60*1000);
1245
1246 if(bTargetReady){
1247 if(CeCosTestSocket::IsLegalHostPort(m_pPort->Serial())){
1248 TRACE(_T("Sending %s\n"),(LPCTSTR )m_pPort->Serial());
1249 m_pSock->sendString(m_pPort->Serial(),_T("Serial name"));
1250 m_pSock->recvInteger(nAck,_T("Terminating ack"),dTimeout);
1251 TRACE(_T("Terminating ack=%d\n"),nAck);
1252 } else {
1253 String strHostPort(CeCosTestSocket::HostPort(CeCosTestUtils::HostName(),nAuxPort));
1254
1255 TRACE(_T("Using %s\n"),(LPCTSTR )strHostPort);
1256
1257 if(m_pSock->sendString(strHostPort,_T("host:port"))){
1258
1259 // This Boolean signifies that the serial<-->tcp/ip conversation is done. It may be set
1260 // on completion of the ConnectSocketToSerial thread (which is why we pass it to runthread)
1261 // and also set by us to *cause* the thread to complete.
1262
1263 bool bConnectSocketToSerialThreadDone=false; // Indication of termination of ConnectSocketToSerial thread
1264 m_bStopConnectSocketToSerial=false; // Used to tap ConnectSocketToSerial thread on the shoulder
1265
1266 CeCosThreadUtils::RunThread(SConnectSocketToSerialThreadFunc,this,&bConnectSocketToSerialThreadDone,_T("SConnectSocketToSerialThreadFunc"));
1267
1268 // Wait for either client or the ConnectSocketToSerial thread to finish.
1269 m_pSock->recv(&nAck,1,_T("Terminating ack"),dTimeout,DerefBool,&bConnectSocketToSerialThreadDone);
1270 TRACE(_T("Terminating ack=%d\n"),nAck);
1271 if(!bConnectSocketToSerialThreadDone){
1272 // Tap ConnectSocketToSerial thread on the shoulder
1273 TRACE(_T("Waiting for ConnectSocketToSerial thread to terminate...\n"));
1274 m_bStopConnectSocketToSerial=true;
1275 CeCosThreadUtils::WaitFor(bConnectSocketToSerialThreadDone);
1276 }
1277 TRACE(_T("ConnectSocketToSerial thread terminated...\n"));
1278
1279 }
1280 }
1281 }
1282 }
1283 delete this;
1284 }
1285
1286 bool CeCosTest::send(const void *pData,unsigned int nLength,LPCTSTR pszMsg,Duration dTimeout)
1287 {
1288 return m_pSock->send(pData,nLength,pszMsg,dTimeout,IsCancelled,this);
1289 }
1290
1291 bool CeCosTest::recv(const void *pData,unsigned int nLength,LPCTSTR pszMsg,Duration dTimeout)
1292 {
1293 return m_pSock->recv(pData,nLength,pszMsg,dTimeout,IsCancelled,this);
1294 }
1295
1296 void CeCosTest::Log(LPCTSTR pszFormat, ...)
1297 {
1298 va_list args;
1299 va_start(args, pszFormat);
1300 String str;
1301 str.vFormat(pszFormat,args);
1302 va_end(args);
1303 LogString(str);
1304 }
1305
1306 void CeCosTest::LogString(LPCTSTR psz)
1307 {
1308 if(*psz){
1309 ENTERCRITICAL;
1310 m_strOutput+=psz;
1311 LEAVECRITICAL;
1312 if(CeCosTrace::IsInteractive()){
1313 CeCosTrace::Out(psz);
1314 } else {
1315 TRACE(_T("%s"),psz);
1316 }
1317 }
1318 }
1319
1320 bool CeCosTest::sendResult(Duration dTimeout)
1321 {
1322 bool rc=
1323 m_pSock->sendInteger(m_Status,_T("result"),dTimeout) &&
1324 m_pSock->sendInteger(m_nDownloadTime,_T("result"),dTimeout) &&
1325 m_pSock->sendInteger(m_nTotalTime,_T("result"),dTimeout) &&
1326 m_pSock->sendInteger(m_nMaxInactiveTime,_T("result"),dTimeout) &&
1327 m_pSock->sendString (m_strOutput,_T("result"),dTimeout);
1328 return rc;
1329 }
1330
1331 bool CeCosTest::recvResult(Duration dTimeout)
1332 {
1333 String strOutput;
1334 int nStatus=StatusTypeMax;
1335 bool rc=
1336 m_pSock->recvInteger(nStatus,_T("result"),dTimeout) &&
1337 m_pSock->recvInteger(m_nDownloadTime,_T("result"),dTimeout) &&
1338 m_pSock->recvInteger(m_nTotalTime,_T("result"),dTimeout) &&
1339 m_pSock->recvInteger(m_nMaxInactiveTime,_T("result"),dTimeout) &&
1340 m_pSock->recvString (strOutput,_T("result"),dTimeout);
1341 m_Status=(StatusType)MIN(nStatus,StatusTypeMax);
1342 LogString(strOutput);
1343 return rc;
1344 }
1345
1346 #ifndef _WIN32
1347 // This function may be run as a thread (win32) or called (unix).
1348 // It runs gdb.
1349 void CeCosTest::DriveGdb(LPCTSTR pszPrompt,const StringArray &arstrGdbCmds)
1350 {
1351 unsigned int nCmdIndex=0;
1352
1353 SetStatus(NotStarted);
1354
1355 m_nMaxInactiveTime=0;
1356 m_nTotalTime=0;
1357 m_nDownloadTime=0;
1358
1359 m_bDownloading=true;
1360
1361 m_tBase=GdbTime();
1362 m_tBase0=GdbTime();
1363 m_tWallClock0=Now();
1364
1365 TRACE(_T("DriveGdb()\n"));
1366
1367 int nLastGdbInst=0;
1368
1369 // Loop until 1 of:
1370 // 1. Timeout detected
1371 // 2. Gdb process is dead
1372 // 3. At a gdb prompt and no more commands to send to gdb
1373 // 4. Pipe read failure
1374 // 5. Pipe write failure
1375 do {
1376 String str;
1377 int readrc=ReadPipe(str,/*bBlockingReads=*/false);
1378 switch(readrc){
1379 case 0:
1380 Sleep(250); // only unix will execute this
1381 break;
1382 case -1:
1383 goto Done; // case 4
1384 break;
1385 default:
1386 LogTimeStampedOutput(str);
1387
1388 if(m_strOutput.GetLength()>20000){
1389 LogString(_T("\n>>>> Infra FAIL\n*** too much output ***\n>>>>\n"));
1390 SetStatus(Fail);
1391 goto Done;
1392 }
1393
1394 // Test for program loaded and started:
1395 // (remember SetStatus cannot downgrade the status if already > NoResult)
1396 if(OutputContains(_T("Starting program: /")) || (OutputContains(_T("Start address"))&&OutputContains(_T("Continuing.")))){
1397 SetStatus(NoResult);
1398 }
1399
1400 m_tBase=GdbTime();
1401 // // If can only hit a single breakpoint don't expect cyg_test_exit to stop us:
1402 // if(!BreakpointsOperational() && (OutputContains(_T("EXIT:"))||OutputContains(_T("NOTAPPLICABLE:")))){
1403 // goto Done;
1404 // }
1405
1406 if(AtPrompt(pszPrompt)){
1407 m_tBase=GdbTime();
1408 TRACE(_T("DriveGdb(1)\n"));
1409 // gdb's output included one or more prompts
1410 // Send another command along
1411 if(nCmdIndex>=arstrGdbCmds.size()){
1412 // Nothing further to say to gdb - exit
1413 TRACE(_T("DriveGdb(2)\n"));
1414 goto Done; // case 3
1415 } else {
1416 String strCmd(arstrGdbCmds[nCmdIndex++]);
1417 TRACE(_T("DriveGdb(2a) - strCmd='%s' nLastGdbInst=%d\n"),(LPCTSTR)strCmd,nLastGdbInst);
1418 // If at a prompt and we can see a GDB instruction, send it down
1419 LPCTSTR pszGdbcmd=_tcsstr(nLastGdbInst+(LPCTSTR)m_strOutput,_T("GDB:"));
1420 if(pszGdbcmd){
1421 TRACE(_T("DriveGdb(2b)\n"));
1422 pszGdbcmd+=4;
1423 TCHAR cTerm;
1424 if(_TCHAR('<')==*pszGdbcmd){
1425 cTerm=_TCHAR('>');
1426 pszGdbcmd++;
1427 } else {
1428 cTerm=_TCHAR('\n');
1429 }
1430 TRACE(_T("DriveGdb(2c)\n"));
1431 LPCTSTR c=_tcschr(pszGdbcmd,cTerm);
1432 if(c){
1433 TRACE(_T("DriveGdb(2d)\n"));
1434 strCmd=String(pszGdbcmd,c-pszGdbcmd);
1435 nLastGdbInst=c+1-(LPCTSTR)m_strOutput;
1436 nCmdIndex--; // undo increment above
1437 }
1438 }
1439 strCmd+=_TCHAR('\n');
1440 LogString(strCmd);
1441 TRACE(_T("DriveGdb(3)\n"));
1442 if(!WritePipe(strCmd)){
1443 TRACE(_T("Writepipe returned error\n"));
1444 goto Done; // case 5
1445 }
1446 TRACE(_T("DriveGdb(4)\n"));
1447 if(0==_tcscmp(strCmd,_T("run\n"))||0==_tcscmp(strCmd,_T("cont\n"))){
1448 m_tBase=GdbTime();
1449 m_bDownloading=false;
1450 }
1451 TRACE(_T("DriveGdb(5)\n"));
1452 }
1453 }
1454 break;
1455 }
1456
1457 } while (GdbProcessAlive() && CheckForTimeout()); // cases 2 and 1
1458
1459 Done:
1460
1461 TRACE(_T("DriveGdb() - done\n"));
1462
1463 Suck(pszPrompt);
1464 if(GdbProcessAlive() && AtPrompt(pszPrompt)){
1465 LogString(_T("bt\n"));
1466 WritePipe(_T("bt\n"));
1467 Suck(pszPrompt);
1468 LogString(_T("quit\n"));
1469 WritePipe(_T("quit\n"));
1470 }
1471
1472 // Read anything gdb has to say [within limits]
1473 Suck(pszPrompt);
1474 AnalyzeOutput();
1475
1476 m_nTotalTime=Now()-m_tWallClock0;
1477 TRACE(_T("Exiting DriveGdb()\n"));
1478
1479 }
1480 #endif
1481
1482 // Return time used by inferior gdb process - CPU for sim, wallclock otherwise
1483 Time CeCosTest::GdbTime()
1484 {
1485 return _TCHAR('\0')==*(m_pPort->Serial())?GdbCpuTime():Now();
1486 }
1487
1488 bool CeCosTest::CheckForTimeout()
1489 {
1490 bool rc=false;
1491 if(TimeOut!=m_Status && DownloadTimeOut!=m_Status){
1492 Duration &dTime=m_bDownloading?m_nDownloadTime:m_nMaxInactiveTime;
1493 Time t=GdbTime();
1494 if(t){
1495 dTime=MAX(dTime,Duration(GdbTime()-m_tBase));
1496 }
1497 Duration dTimeout=m_bDownloading?DownloadTimeout():ActiveTimeout();
1498 if(dTimeout!=NOTIMEOUT && dTime>dTimeout) {
1499 Log(_T("\n*** Timeout - %s time ") WFS _T(" exceeds limit of ") WFS _T("\n"),
1500 m_bDownloading?_T("download"):_T("MAX inactive"),WF(dTime),WF(dTimeout));
1501 SetStatus(m_bDownloading?DownloadTimeOut:TimeOut);
1502 } else if(Now()-m_tWallClock0>MAX(3*dTimeout,15*60*1000)){
1503 Log(_T("\n*** Timeout - total time ") WFS _T(" exceeds limit of ") WFS _T("\n"),
1504 WF(Now()-m_tWallClock0),WF(MAX(3*dTimeout,15*60*1000)));
1505 SetStatus(m_bDownloading?DownloadTimeOut:TimeOut);
1506 } else {
1507 rc=true;
1508 }
1509 }
1510 return rc;
1511 }
1512
1513 void CeCosTest::Interactive(LPCTSTR pszFormat, ...)
1514 {
1515 va_list marker;
1516 va_start (marker, pszFormat);
1517 String str;
1518 str.vFormat(pszFormat,marker);
1519 va_end (marker);
1520
1521 if(CeCosTrace::IsInteractive()){
1522 CeCosTrace::Out(str);
1523 } else {
1524 CeCosTrace::Trace(_T("%s"),(LPCTSTR)str);
1525 }
1526 }
1527
1528 LPCTSTR const CeCosTest::Title() const {
1529 return m_strTitle;
1530 }
1531
1532 // Convert a path to something a cygwin tool will understand. Used when invoking -size and -gdb
1533 String CeCosTest::CygPath (LPCTSTR pszPath)
1534 {
1535 #ifdef _WIN32
1536 String str;
1537 TCHAR *buf=str.GetBuffer(2+MAX_PATH);
1538 TCHAR *pszFname;
1539 if(::GetFullPathName(pszPath,MAX_PATH,1+buf, &pszFname)){
1540 GetShortPathName(1+buf,1+buf,MAX_PATH); // ignore errors
1541 buf[0]=_TCHAR('/');
1542 buf[2]=buf[1];
1543 buf[1]=_TCHAR('/');
1544 for(int i=3;buf[i];i++){
1545 if(_TCHAR('\\')==buf[i]){
1546 buf[i]=_TCHAR('/');
1547 }
1548 }
1549 str.ReleaseBuffer();
1550 return str;
1551 } else {
1552 str.ReleaseBuffer();
1553 return pszPath;
1554 }
1555 #endif
1556 return pszPath;
1557 }
1558
1559 void CeCosTest::SetExecutable(LPCTSTR pszExecutable)
1560 {
1561 m_strOutput=_T("");
1562 m_strResultString=_T("");
1563 m_strExecutable=pszExecutable;
1564 if(pszExecutable && !GetSizes(m_strExecutable, m_ep.Target(), m_nFileSize, m_nStrippedSize)){
1565 const TargetInfo &t=Target(Target());
1566 Log(_T("Failed to run %s-size to determine executable size of %s\n"),t.Prefix(),pszExecutable);
1567 }
1568 }
1569
1570 // Calculate the sizes of the given file. The target parameter is necessary in order to
1571 // determine which -size executable to use to do the job.
1572 bool CeCosTest::GetSizes(LPCTSTR pszExecutable, LPCTSTR target,unsigned int &nFileSize, unsigned int &nStrippedSize)
1573 {
1574 bool rc=false;
1575 nStrippedSize=nFileSize=0;
1576 struct _stat buf;
1577 const TargetInfo &t=Target(target);
1578 LPCTSTR pszPrefix=t.Prefix();
1579 if(0==_tstat(pszExecutable,&buf) && _TCHAR('\0')!=*pszPrefix){
1580 // File at least exists...
1581 nFileSize=buf.st_size;
1582 String strSize(pszPrefix);
1583 strSize+=_T("-size ");
1584 strSize+=CygPath(pszExecutable);
1585 const TCHAR *c=0;
1586 #ifdef _WIN32
1587 CSubprocess sp;
1588 String strOut;
1589 if(!sp.Run(strOut,strSize,NULL)){
1590 return true;
1591 }
1592 c=_tcschr(strOut,_TCHAR('\n'));
1593 if(c){
1594 c++;
1595 }
1596 #else // UNIX
1597 TCHAR buf[256];
1598 FILE *f=POPEN(strSize,_T("r"));
1599 if(f){
1600 _fgetts(buf,sizeof buf-1,f);
1601 _fgetts(buf,sizeof buf-1,f);
1602 PCLOSE(f);
1603 c=buf;
1604 }
1605 #endif
1606 int s1=0;
1607 int s2=0;
1608 rc=(c && 2==_stscanf(c,_T(" %d %d"),&s1,&s2));
1609 nStrippedSize=s1+s2;
1610 }
1611 return rc;
1612 }
1613
1614 void CeCosTest::SetTimeouts (Duration dActive,Duration dElapsed)
1615 {
1616 m_ep.SetActiveTimeout (dActive);
1617 m_ep.SetDownloadTimeout(dElapsed);
1618 }
1619
1620 void CeCosTest::CloseSocket (){
1621 delete m_pSock;
1622 m_pSock=0;
1623 }
1624
1625 bool CeCosTest::AtPrompt(LPCTSTR pszPrompt)
1626 {
1627 unsigned int nPromptLen=_tcslen(pszPrompt);
1628 return
1629 m_strOutput.GetLength()>=nPromptLen &&
1630 0==_tcscmp((LPCTSTR )m_strOutput+m_strOutput.GetLength()-nPromptLen,pszPrompt);
1631 }
1632
1633 #ifndef _WIN32
1634 bool CeCosTest::Suck(LPCTSTR pszPrompt,Duration d)
1635 {
1636 TRACE(_T("Suck handle=%08x\n"),m_rPipeHandle);//sdf
1637 // Read until:
1638 // 8k read
1639 // timeout elapsed
1640 // gdb prompt reached
1641 // pipe error
1642 Time t0=Now();
1643 int nLength=0;
1644 while(nLength<8192 && m_rPipeHandle && !AtPrompt(pszPrompt) && Now()-t0<d){
1645 String str;
1646 int n=ReadPipe(str);
1647 if(n>0){
1648 LogTimeStampedOutput(str);
1649 nLength+=n;
1650 } else if (n<0) {
1651 break;
1652 }
1653 }
1654 TRACE(_T("End suck\n"));//sdf
1655 return nLength>0;
1656 }
1657 #endif
1658
1659 void CeCosTest::LogTimeStampedOutput(LPCTSTR psz)
1660 {
1661 LogString(psz);
1662 /*
1663 String str(psz);
1664 // Timestamp the output at each _TCHAR('\n')
1665 int nLen=m_strOutput.GetLength();
1666 TCHAR cPrev=(0==nLen?_TCHAR('\0'):((LPCTSTR )m_strOutput)[nLen-1]);
1667
1668 TCHAR *c=str.GetBuffer();
1669 LPCTSTR d=c;
1670 while(*c){
1671 if(_TCHAR('\n')==cPrev){
1672 TCHAR cSav=*c;
1673 *c=_TCHAR('\0');
1674 LogString(d);
1675 Duration &dTime=m_bDownloading?m_nDownloadTime:m_nMaxInactiveTime;
1676 dTime=MAX(dTime,GdbTime()-m_tBase);
1677
1678 String strTime;
1679 strTime.Format(_T("<") WFS _T("/") WFS _T(">\t"),WF(GdbTime()-m_tBase0), WF(GdbTime()-m_tBase));
1680 //strTime.Format(_T("<%03d.%d> "),t/1000,(t%1000)/100);
1681 LogString(strTime);
1682 *c=cSav;
1683 d=c;
1684 }
1685 cPrev=*c;
1686 c++;
1687 }
1688 LogString(d);
1689 str.ReleaseBuffer();
1690 */
1691 }
1692
1693 #ifdef _WIN32
1694 BOOL WINAPI HandlerRoutine(
1695 DWORD dwCtrlType // control signal type
1696 )
1697 {
1698 dwCtrlType; // eliminate compiler warning
1699 return TRUE;
1700 }
1701 #endif
1702
1703
1704 bool CeCosTest::InteractiveGdb(const String &strHost,int nPort,TCHAR **argv)
1705 {
1706 bool rc=false;
1707 if(strHost.GetLength()>0){
1708 m_strExecutionHostPort=CeCosTestSocket::HostPort(strHost,nPort);
1709 Log(_T("Waiting to connect to %s...\n"),(LPCTSTR )m_strExecutionHostPort);
1710 } else {
1711 Log(_T("Waiting to connect to a server...\n"));
1712 }
1713
1714 ConnectForExecution();
1715
1716 Log(_T("Connected to %s - waiting for target reset\n"),(LPCTSTR )m_strExecutionHostPort);
1717 String strHostPort,strOutput;
1718 // We read:
1719 // target ready indicator
1720 // any output so far
1721 // (if target ready) host:port
1722 if(GetTargetReady(strHostPort)){
1723 Log(_T("Use %s\n"),(LPCTSTR)strHostPort);
1724 const TargetInfo &t=Target(Target());
1725 String strGdb(t.Prefix());
1726 strGdb+=_T("-gdb");
1727 #ifdef _WIN32
1728 SetConsoleCtrlHandler(HandlerRoutine,TRUE);
1729 int n=_tspawnvp(_P_WAIT,strGdb,argv);
1730 if(-1==n){
1731 Log(_T("Failed to spawn %s\n"),(LPCTSTR)strGdb);
1732 } else {
1733 rc=(0==n);
1734 }
1735 SetConsoleCtrlHandler(HandlerRoutine,FALSE);
1736 #else // UNIX
1737
1738 int pid=fork();
1739 switch(pid){
1740 case -1:
1741 _ftprintf(stderr,_T("fork failed\n"));
1742 pid=0;
1743 break;
1744 case 0:
1745 // Process is created (we're the child)
1746 execvp(strGdb,argv);
1747 Log(_T("Error invoking %s - %s\n"),(LPCTSTR)strGdb,strerror(errno));
1748 exit(1);
1749 break;
1750 default:
1751 // Process is created (we're the parent)
1752 {
1753 signal(SIGINT,SIG_IGN);
1754 int stat;
1755 waitpid(pid,&stat,0);
1756 rc=(0==stat);
1757 signal(SIGINT,SIG_DFL);
1758 }
1759 break;
1760 }
1761 #endif
1762 Log(_T("Gdb terminated\n"));
1763 // Tell the server we're through
1764 m_pSock->sendInteger(123,_T("Terminating ack"));
1765 }
1766
1767 return rc;
1768 }
1769
1770 void CALLBACK CeCosTest::ResetLogFunc(void *pParam, LPCTSTR psz)
1771 {
1772 CeCosTest *pTest=(CeCosTest *)pParam;
1773 TRACE(_T("Send Target Ready indicator=2\n"));
1774 pTest->m_pSock->sendInteger(2,_T("target ready indicator"));
1775 TRACE(_T("Send %s\n"),psz);
1776 pTest->m_pSock->sendString(psz,_T("output so far"));
1777 }
1778
1779 CResetAttributes::ResetResult CeCosTest::Reset(bool bSendStatus)
1780 {
1781 return m_pPort->Reset(bSendStatus?ResetLogFunc:0,this);
1782 }
1783
1784 CeCosTest::ExecutionParameters::RequestType CeCosTest::ExecutionParameters::RequestTypeValue(LPCTSTR psz)
1785 {
1786 int r;
1787 for(r=0;r<RequestTypeMax;r++){
1788 if(0==_tcsicmp(psz,arRequestImage[r])){
1789 break;
1790 }
1791 }
1792 return (RequestType)r;
1793 }
1794
1795 #ifdef _WIN32
1796 void CeCosTest::RunGdb(LPCTSTR pszCmdline,LPCTSTR pszPrompt,const StringArray &arstrGdbCmds)
1797 {
1798 SetPath(m_strPath);
1799 CSubprocess sp;
1800
1801 unsigned int idProcess=sp.Run(GetCurrentThreadId(),pszCmdline);
1802 if(!idProcess){
1803 Log(_T("Failed to create gdb process: cmdline='%s'\n"),pszCmdline);
1804 TCHAR *pszMsg;
1805
1806 FormatMessage(
1807 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
1808 NULL,
1809 sp.GetExitCode(),
1810 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
1811 (LPTSTR)&pszMsg,
1812 0,
1813 NULL
1814 );
1815
1816 // Display the string.
1817 Log(_T("%s\n"),pszMsg);
1818
1819 // Free the buffer.
1820 LocalFree(pszMsg);
1821 } else {
1822
1823 unsigned int nCmdIndex=0;
1824
1825 SetStatus(NotStarted);
1826
1827 m_nMaxInactiveTime=0;
1828 m_nTotalTime=0;
1829 m_nDownloadTime=0;
1830
1831 m_bDownloading=true;
1832
1833 m_tBase=GdbTime();
1834 m_tBase0=GdbTime();
1835 m_tWallClock0=Now();
1836
1837 TRACE(_T("RunGDb()\n"));
1838
1839 int nLastGdbInst=0;
1840
1841 while(Cancelled!=Status() && CheckForTimeout()){
1842 MSG msg;
1843 if(::PeekMessage(&msg,NULL,WM_SUBPROCESS,WM_SUBPROCESS+1,PM_NOREMOVE)){
1844 switch(::GetMessage(&msg,NULL,WM_SUBPROCESS,WM_SUBPROCESS+1)){
1845 case 0: //WM_QUIT
1846 case -1: // error
1847 goto Done;
1848 case 1:
1849 if(WM_SUBPROCESS==msg.message && msg.wParam==idProcess){
1850 if(msg.lParam){
1851 LPTSTR pszMsg=(LPTSTR)msg.lParam;
1852 LogTimeStampedOutput(pszMsg);
1853
1854 if(m_strOutput.GetLength()>20000){
1855 LogString(_T("\n>>>> Infra FAIL\n*** too much output ***\n>>>>\n"));
1856 SetStatus(Fail);
1857 goto Done;
1858 }
1859
1860 // Test for program loaded and started:
1861 // (remember SetStatus cannot downgrade the status if already > NoResult)
1862 if(OutputContains(_T("Starting program: /")) || (OutputContains(_T("Start address"))&&OutputContains(_T("Continuing.")))){
1863 SetStatus(NoResult);
1864 }
1865
1866 m_tBase=GdbTime();
1867 // // If can only hit a single breakpoint don't expect cyg_test_exit to stop us:
1868 // if(!BreakpointsOperational() && (OutputContains(_T("EXIT:"))||OutputContains(_T("NOTAPPLICABLE:")))){
1869 // goto Done;
1870 // }
1871
1872 if(AtPrompt(pszPrompt)){
1873 m_tBase=GdbTime();
1874 TRACE(_T("RunGDb(1)\n"));
1875 // gdb's output included one or more prompts
1876 // Send another command along
1877 if(nCmdIndex>=arstrGdbCmds.size()){
1878 // Nothing further to say to gdb - exit
1879 TRACE(_T("RunGDb(2)\n"));
1880 goto Done; // case 3
1881 } else {
1882 String strCmd(arstrGdbCmds[nCmdIndex++]);
1883 TRACE(_T("RunGDb(2a) - strCmd='%s' nLastGdbInst=%d\n"),(LPCTSTR)strCmd,nLastGdbInst);
1884 // If at a prompt and we can see a GDB instruction, send it down
1885 LPCTSTR pszGdbcmd=_tcsstr(nLastGdbInst+(LPCTSTR)m_strOutput,_T("GDB:"));
1886 if(pszGdbcmd){
1887 TRACE(_T("RunGDb(2b)\n"));
1888 pszGdbcmd+=4;
1889 TCHAR cTerm;
1890 if(_TCHAR('<')==*pszGdbcmd){
1891 cTerm=_TCHAR('>');
1892 pszGdbcmd++;
1893 } else {
1894 cTerm=_TCHAR('\n');
1895 }
1896 TRACE(_T("RunGDb(2c)\n"));
1897 LPCTSTR c=_tcschr(pszGdbcmd,cTerm);
1898 if(c){
1899 TRACE(_T("RunGDb(2d)\n"));
1900 strCmd=String(pszGdbcmd,c-pszGdbcmd);
1901 nLastGdbInst=c+1-(LPCTSTR )m_strOutput;
1902 nCmdIndex--; // undo increment above
1903 }
1904 }
1905 strCmd+=_TCHAR('\n');
1906 LogString(strCmd);
1907 TRACE(_T("RunGDb(3)\n"));
1908 sp.Send(strCmd);
1909 TRACE(_T("RunGDb(4)\n"));
1910 if(0==_tcscmp(strCmd,_T("run\n"))||0==_tcscmp(strCmd,_T("cont\n"))){
1911 m_tBase=GdbTime();
1912 m_bDownloading=false;
1913 }
1914 TRACE(_T("RunGDb(5)\n"));
1915 }
1916 }
1917 delete [] pszMsg;
1918 } else {
1919 goto Done;
1920 }
1921 }
1922 break;
1923 }
1924 } else { // no message
1925 Sleep (500l); // sleep 500ms before the next peek
1926 }
1927 }
1928
1929 Done:
1930 // Could use CygKill here
1931 sp.Kill();
1932 TRACE(_T("RunGDb - Done\n"));
1933 AnalyzeOutput();
1934
1935 m_nTotalTime=Duration(Now()-m_tWallClock0);
1936 }
1937 TRACE(_T("Exiting RunGdb()\n"));
1938 }
1939
1940 bool CeCosTest::GdbProcessAlive()
1941 {
1942 DWORD dwExitRc;
1943 GetExitCodeProcess((HANDLE)m_pGdbProcesshandle,&dwExitRc);
1944 return STILL_ACTIVE==dwExitRc;
1945 }
1946
1947 Time CeCosTest::GdbCpuTime()
1948 {
1949 HANDLE hProcess=(HANDLE)m_pGdbProcesshandle;
1950 __int64 ftCreation,ftExit,ftKernel,ftUser;
1951 if(NULL!=hProcess && GetProcessTimes (hProcess,(FILETIME *)&ftCreation,(FILETIME *)&ftExit,(FILETIME *)&ftKernel,(FILETIME *)&ftUser)){
1952 return Time((int)((ftKernel+ftUser)/10000));
1953 } else {
1954 return 0;
1955 }
1956 }
1957
1958 void CeCosTest::GetPath(String &strPath)
1959 {
1960 int nSize=GetEnvironmentVariable(_T("PATH"), NULL, 0);
1961 if(nSize>0){
1962 GetEnvironmentVariable(_T("PATH"), strPath.GetBuffer(nSize), nSize);
1963 strPath.ReleaseBuffer();
1964 } else {
1965 strPath=_T("");
1966 }
1967 }
1968
1969 void CeCosTest::SetPath(const String &strPath)
1970 {
1971 SetEnvironmentVariable(_T("PATH"), strPath);
1972 }
1973
1974 #else // UNIX
1975
1976 void CeCosTest::RunGdb(LPCTSTR pszCmdline,LPCTSTR pszPrompt,const StringArray &arstrGdbCmds)
1977 {
1978 int pipe_ends_w[2];
1979 if (pipe(pipe_ends_w) < 0 ) {
1980 Log(_T("Failed to create pipe_ends_w - %s\n"),strerror(errno));
1981 } else {
1982 int pipe_ends_r[2];
1983 if (pipe(pipe_ends_r) < 0 ) {
1984 Log(_T("Failed to create pipe_ends_r - %s\n"),strerror(errno));
1985 } else {
1986 int new_pid;
1987 ENTERCRITICAL;
1988 // Ensure no one else has the lock such that the child might block in future
1989 new_pid = fork();
1990 // This leave is executed in *both* the child and parent
1991 LEAVECRITICAL;
1992
1993 switch (new_pid) {
1994 // Fork failed
1995 case -1:
1996 Log(_T("Failed to create gdb process - %s\n"),strerror(errno));
1997 break;
1998 case 0:
1999 // Process is created (we're the child)
2000 // No point in calling Log in this process
2001
2002 // Input to child process
2003 if (dup2(pipe_ends_w[0], 0) < 0) {
2004 TRACE(_T("dup2 error\n"));
2005 exit(1);
2006 }
2007
2008 // Output from process
2009 if (dup2(pipe_ends_r[1], 2) < 0) {
2010 TRACE(_T("dup2 error\n"));
2011 exit(2);
2012 }
2013 if (dup2(pipe_ends_r[1], 1) < 0) {
2014 TRACE(_T("dup2 error\n"));
2015 exit(3);
2016 }
2017 setvbuf(stdout,0,_IONBF,0);
2018 setvbuf(stderr,0,_IONBF,0);
2019 {
2020 StringArray ar;
2021 int argc=String(pszCmdline).Chop(ar,_TCHAR(' '),true);
2022 char **argv=new char *[1+argc];
2023 int i;
2024 for(i=0;i<argc;i++){
2025 argv[i]=ar[i];
2026 }
2027 argv[i]=0;
2028 execvp(argv[0], argv);
2029 delete [] argv;
2030 }
2031 TRACE(_T("exec error - %s\n"),strerror(errno));
2032 exit(4);
2033 default:
2034 // Process is created (we're the parent)
2035
2036 TRACE(_T("Forked to create gdb process %s - pid is <%d>\n"), pszCmdline, new_pid);
2037 if (fcntl(pipe_ends_r[0], F_SETFL, O_NONBLOCK) <0) {
2038 Log(_T("Couldn't set pipe non-blocking - %s\n"),strerror(errno));
2039 } else {
2040 m_pGdbProcesshandle=(void *)new_pid;
2041 VTRACE(_T("RunGdb():Calling DriveGdb\n"));
2042 m_rPipeHandle=(void *)pipe_ends_r[0];
2043 m_wPipeHandle=(void *)pipe_ends_w[1];
2044 DriveGdb (pszPrompt,arstrGdbCmds);
2045 // Finished one way or another. Kill gdb now
2046 TRACE(_T("Finished processing this test.\n"));
2047 if(GdbProcessAlive()){
2048 TRACE(_T("Killing gdb\n"));
2049
2050 // We need to kill gdb *and* its children
2051 FILE *f=popen(_T("ps -l"),_T("r"));
2052 if(f){
2053 TCHAR buf[100];
2054 while(_fgetts(buf,sizeof(buf)-1,f)){
2055 int F,UID,PID,PPID,C,PRI,NI,SZ,HH,MM,SS;
2056 TCHAR discard[100];
2057 // Output is in the form
2058 // F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
2059 //100 S 490 877 876 0 70 0 - 368 wait4 pts/0 00:00:00 bash
2060 if(15==_stscanf(buf,
2061 _T("%d %s %d %d %d %d %d %d %s %d %s %s %d:%d:%d"),&F,discard,&UID,&PID,&PPID,&C,&PRI,&NI,discard,&SZ,discard,discard,&HH,&MM,&SS) &&
2062 (PID==new_pid || PPID==new_pid)){
2063 kill(PID,SIGTERM);
2064 }
2065 }
2066 pclose(f);
2067 TRACE(_T("waitpid <%d>"), new_pid);
2068 int i;
2069 for(i=0;i<10;i++){
2070 int status;
2071 switch(waitpid(new_pid,&status,WNOHANG)){
2072 case 0:
2073 Sleep(1000);
2074 continue;
2075 case -1:
2076 Log(_T("Failed to wait for gdb process to die\n"));
2077 SetStatus(TimeOut);
2078 break;
2079 default:
2080 break;
2081 }
2082 break;
2083 }
2084 if(10==i){
2085 Log(_T("Failed to wait for gdb process to die\n"));
2086 SetStatus(TimeOut);
2087 } else {
2088 TRACE(_T("Killed gdb\n"));
2089 }
2090 } else {
2091 Log(_T("Failed to run ps to kill gdb process %d\n"),new_pid);
2092 }
2093 }
2094 TRACE(_T("Total elapsed time is %d\n"), m_nTotalTime);
2095 }
2096 break;
2097 }
2098 VTRACE(_T("Closing pipe_ends_r[]\n"));
2099 close (pipe_ends_r[0]);
2100 close (pipe_ends_r[1]);
2101 }
2102 close (pipe_ends_w[0]);
2103 close (pipe_ends_w[1]);
2104 }
2105 }
2106
2107 bool CeCosTest::GdbProcessAlive()
2108 {
2109 int status;
2110 return 0==waitpid((int)m_pGdbProcesshandle,&status,WNOHANG);
2111 }
2112
2113 Time CeCosTest::GdbCpuTime()
2114 {
2115 if(GdbProcessAlive()){
2116 Time now=Now();
2117 if(now-m_tPrevSample>1000){
2118
2119 m_tPrevSample=now;
2120 // Output is in the form
2121 // F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
2122 //100 S 490 877 876 0 70 0 - 368 wait4 pts/0 00:00:00 bash
2123 FILE *f=popen(_T("ps -l"),_T("r"));
2124 if(f){
2125 TCHAR buf[100];
2126 int t=0;
2127 while(_fgetts(buf,sizeof(buf)-1,f)){
2128 int F,UID,PID,PPID,C,PRI,NI,SZ,HH,MM,SS;
2129 TCHAR discard[100];
2130 TRACE(_T("ps -l : %s\n"),buf);
2131 if(15==_stscanf(buf,
2132 _T("%d %s %d %d %d %d %d %d %s %d %s %s %d:%d:%d"),&F,discard,&UID,&PID,&PPID,&C,&PRI,&NI,discard,&SZ,discard,discard,&HH,&MM,&SS) &&
2133 (PID==(int)m_pGdbProcesshandle || PPID==(int)m_pGdbProcesshandle)){
2134 t+=SS+60*(60*HH+MM);
2135 }
2136 }
2137 pclose(f);
2138 TRACE(_T("---> t=%d [secs]\n"),t);
2139 m_tGdbCpuTime=Time(1000*t);
2140 }
2141 }
2142 } else {
2143 m_tGdbCpuTime=0;
2144 }
2145 VTRACE(_T("GdbCpuTime rc=%d"),m_tGdbCpuTime/1000);
2146 return m_tGdbCpuTime;
2147 }
2148
2149 bool CeCosTest::WritePipe (const String &str)
2150 {
2151 const char *pszBuf=(const char *)str;
2152 int write_fd = (int)m_wPipeHandle;
2153 int dwWritten;
2154 int nToWrite=_tcslen(pszBuf);
2155 do {
2156 dwWritten = write(write_fd, pszBuf, nToWrite);
2157 if(-1==dwWritten){
2158 Log(_T("pipe write error - %s\n"),strerror(errno));
2159 return false;
2160 }
2161 nToWrite-=(int)dwWritten;
2162 pszBuf+=(int)dwWritten;
2163 if(!CheckForTimeout()){
2164 return false;
2165 }
2166 } while (nToWrite>0);
2167 return true;
2168 }
2169
2170 int CeCosTest::ReadPipe (String &str,bool bBlocking /* This param ignored */)
2171 {
2172 TCHAR buf[4096];
2173 int rc=read((int)m_rPipeHandle, buf, sizeof(buf)-1);
2174 if(-1==rc && EAGAIN==errno){
2175 rc=0;
2176 }
2177 buf[MAX(0,rc)]=_TCHAR('\0');
2178 str=buf;
2179 return rc;
2180 }
2181
2182 void CeCosTest::GetPath(String &strPath)
2183 {
2184 strPath=getenv(_T("PATH"));
2185 }
2186
2187
2188 void CeCosTest::SetPath(const String &strPath)
2189 {
2190 String str;
2191 str.Format(_T("PATH=%s"),(LPCTSTR )strPath);
2192 putenv(str);
2193 }
2194 #endif
2195
2196 void CeCosTest::AnalyzeOutput()
2197 {
2198 // This test is pulled out to allow ser_filter to simulate a test failure
2199 if(OutputContains(_T("FAIL:"))){
2200 SetStatus(Fail);
2201 }
2202
2203 if(OutputContains(_T("EXIT:"))||OutputContains(_T("NOTAPPLICABLE:"))){
2204 static LPCTSTR arpszKeepAlive[]={_T("FAIL:"),_T("NOTAPPLICABLE:"), _T("PASS:")};
2205 static const StatusType arStatus[] ={Fail, Inapplicable, Pass};
2206 for(unsigned int i=0;i<sizeof arpszKeepAlive/sizeof arpszKeepAlive[0];i++){
2207 if(OutputContains(arpszKeepAlive[i])){
2208 TRACE(_T("DriveGdb: saw '%s'\n"),arpszKeepAlive[i]);
2209 SetStatus(arStatus[i]); // Do not break!
2210 }
2211 }
2212 }
2213
2214 // Certain output spells failure...
2215 if(OutputContains(_T("cyg_assert_fail ("))){
2216 SetStatus(AssertFail);
2217 } else {
2218 static LPCTSTR arpszSignals[]={_T("SIGBUS"), _T("SIGSEGV"), _T("SIGILL"), _T("SIGFPE"), _T("SIGSYS"), _T("SIGTRAP")};
2219 for(unsigned int i=0;i<sizeof arpszSignals/sizeof arpszSignals[0];i++){
2220 String str1,str2;
2221 str1.Format(_T("signal %s"),arpszSignals[i]);
2222 str2.Format(_T("handle %s nostop"),arpszSignals[i]);
2223 if(OutputContains(str1)&&!OutputContains(str2)){
2224 SetStatus(Fail);
2225 break;
2226 }
2227 }
2228 }
2229
2230 // Check for expect: strings
2231 static LPCTSTR szExpect=_T("EXPECT:");
2232 static const int nExpectLen=_tcslen(szExpect);
2233 for(const TCHAR*c=_tcsstr(m_strOutput,szExpect);c;c=_tcsstr(c,szExpect)){
2234 c+=nExpectLen;
2235 if(_TCHAR('<')==*c){
2236 c++;
2237 // Find the matching _TCHAR('>')
2238 for(LPCTSTR d=_tcschr(c,_TCHAR('>'));d;d=_tcschr(d+1,_TCHAR('>'))){
2239 if(d[-1]!=_TCHAR('\\')){
2240
2241 /*
2242 // Skip whitespace immediately following the EXPECT:<...>
2243 do {
2244 d++;
2245 } while (_istspace(*d));
2246 */
2247
2248 /*
2249 // Skip timestamp
2250 if(_TCHAR('<')==*d){
2251 d=_tcschr(d,_TCHAR('>'));
2252 if(0==d){
2253 continue;
2254 }
2255 do {
2256 d++;
2257 } while (_istspace(*d));
2258 }
2259 */
2260
2261 // Now d points to the terminating _TCHAR('>') and c at the start of the expected string
2262 for(LPCTSTR e=d+1;c!=d;c++,e++){
2263 if(_TCHAR('\\')==*c){
2264 c++;
2265 }
2266 if(*c!=*e){
2267 LogString(_T("EXPECT:<> failure\n"));
2268 SetStatus(Fail);
2269 break;
2270 }
2271 }
2272 break;
2273 }
2274 }
2275 }
2276 }
2277 }
2278
2279 bool CeCosTest::ExecutionParameters::FromStr(LPCTSTR psz)
2280 {
2281 String str1,str2,str3,str4,str5;
2282 int nUseFilter,nUnused2,nUnused3;
2283 int nLen=_tcslen(psz);
2284 _stscanf(psz,_T("%s %s %d %d %d %d %d %d %d %d %s %s %s"),
2285 str1.GetBuffer(1+nLen),
2286 str2.GetBuffer(1+nLen),
2287 &m_nActiveTimeout,
2288 &m_nDownloadTimeout,
2289 &m_nUnused1,
2290 &m_nUnused2,
2291 &m_nUnused3,
2292 &nUseFilter,
2293 &nUnused2,
2294 &nUnused3,
2295 str3.GetBuffer(1+nLen),
2296 str4.GetBuffer(1+nLen),
2297 str5.GetBuffer(1+nLen)
2298 );
2299 m_bUseFilter=(0!=nUseFilter);
2300 m_bUnused2=(0!=nUnused2);
2301 m_bUnused3=(0!=nUnused3);
2302 str1.ReleaseBuffer();
2303 str2.ReleaseBuffer();
2304 str3.ReleaseBuffer();
2305 str4.ReleaseBuffer();
2306 str5.ReleaseBuffer();
2307 m_Target=str1;
2308 int r;
2309 for(r=0;r<RequestTypeMax;r++){
2310 if(0==_tcscmp(arRequestImage[r],str2)){
2311 break;
2312 }
2313 }
2314 m_Request=(RequestType)r;
2315 return CeCosTest::IsValid(m_Target);
2316 }
2317
2318 CeCosTest::ExecutionParameters::ExecutionParameters (RequestType r,
2319 LPCTSTR Target,
2320 Duration nActiveTimeout/*=NOTIMEOUT*/,
2321 Duration nDownloadTimeout/*=NOTIMEOUT*/):
2322 m_bUseFilter(true),
2323 m_Target(Target),
2324 m_nActiveTimeout(nActiveTimeout),
2325 m_nDownloadTimeout(nDownloadTimeout),
2326 m_Request(r),
2327 m_nUnused1(0),
2328 m_nUnused2(0),
2329 m_nUnused3(0),
2330 m_bUnused2(false),
2331 m_bUnused3(false)
2332 {
2333 }
2334
2335 String CeCosTest::ExecutionParameters::Image() const
2336 {
2337 String str;
2338 str.Format(_T("%s %s %d %d %d %d %d %d %d %d"),Target(),Image(Request()),ActiveTimeout(),DownloadTimeout(),
2339 m_nUnused1,
2340 m_nUnused2,
2341 m_nUnused3,
2342 m_bUseFilter,
2343 m_bUnused2,
2344 m_bUnused3);
2345 return str;
2346 }
2347
2348 CeCosTest::TargetInfo::TargetInfo()
2349 {
2350 };
2351
2352 LPCTSTR CeCosTest::TargetInfo::arHwTypeImage[]={_T("HARDWARE"), _T("SIM"), _T("SYNTHETIC"), _T("HARDWARE_NO_BP"), _T("REMOTE_SIM")
2353 };
2354
2355 CeCosTest::TargetInfo::TargetInfo(LPCTSTR pszIm,LPCTSTR pszPre,int nHwtype,LPCTSTR pszGdb/*=_T("")*/):
2356 pszImage(pszIm),
2357 pszPrefix(pszPre),
2358 nType((HwType)nHwtype),
2359 pszGdbcmd(pszGdb)
2360 {
2361 }
2362
2363 CeCosTest::TargetInfo::TargetInfo(LPCTSTR pszIm,LPCTSTR pszPre,LPCTSTR pszHwtype,LPCTSTR pszGdb/*=_T("")*/):
2364 pszImage(pszIm),
2365 pszPrefix(pszPre),
2366 nType(INVALID),
2367 pszGdbcmd(pszGdb)
2368 {
2369 if(_istdigit(pszHwtype[0])){
2370 nType=(HwType)_ttoi(pszHwtype);
2371 if((unsigned)nType>sizeof(arHwTypeImage)){
2372 nType=INVALID;
2373 }
2374 } else {
2375 for(unsigned int i=0;i<sizeof arHwTypeImage/sizeof arHwTypeImage[0];i++){
2376 if(0==_tcsicmp(pszHwtype,arHwTypeImage[i])){
2377 nType=(HwType)i;
2378 break;
2379 }
2380 }
2381 }
2382 }
2383
2384 int CeCosTest::AddPlatform(const CeCosTest::TargetInfo &t)
2385 {
2386 arTargetInfo.push_back(t);
2387 return arTargetInfo.size()-1;
2388 }
2389
2390 bool CeCosTest::GetTargetReady(String &strHostPort)
2391 {
2392 bool rc=false;
2393 int nTargetReady;
2394 do{
2395 if(!m_pSock->recvInteger(nTargetReady,_T("Target ready"),120*1000)){
2396 Log(_T("Failed to read target ready indicator from server - %s\n"),(LPCTSTR )m_pSock->SocketErrString());
2397 break;
2398 }
2399 switch(nTargetReady){
2400 case 0:
2401 LogString(_T("Failed to reset target"));
2402 break;
2403 case 1:
2404 if(m_pSock->recvString(strHostPort, _T("host:port"))){
2405 TRACE(_T("Instructed to use %s\n"),(LPCTSTR )strHostPort);
2406 rc=true;
2407 } else {
2408 Log(_T("Failed to read host:port - %s\n"),(LPCTSTR )m_pSock->SocketErrString());
2409 }
2410 break;
2411 case 2:
2412 {
2413 String strOutput;
2414 if(m_pSock->recvString(strOutput, _T("output"))){
2415 LogString(strOutput);
2416 } else {
2417 Log(_T("Failed to read output\n"),(LPCTSTR )m_pSock->SocketErrString());
2418 return false;
2419 }
2420 }
2421 break;
2422 }
2423 } while(2==nTargetReady);
2424 return rc;
2425 }
2426
2427 #ifdef _WIN32
2428 String CeCosTest::GetGreatestSubkey (LPCTSTR pszKey)
2429 {
2430 String strSubkey = _T("");
2431 HKEY hKey;
2432
2433 if (ERROR_SUCCESS == RegOpenKeyEx (HKEY_LOCAL_MACHINE, pszKey, 0L, KEY_READ, &hKey))
2434 {
2435 DWORD dwIndex = 0;
2436 TCHAR pszBuffer [MAX_PATH + 1];
2437
2438 while (ERROR_SUCCESS == RegEnumKey (hKey, dwIndex++, (LPTSTR) pszBuffer, sizeof (pszBuffer)))
2439 {
2440 if (strSubkey.compare (pszBuffer) < 0)
2441 strSubkey = pszBuffer;
2442 }
2443
2444 RegCloseKey (hKey);
2445 }
2446
2447 TRACE (_T("CeCosTest::GetGreatestSubkey(\"%s\"): %s\n"), pszKey, (LPCTSTR)strSubkey);
2448 return strSubkey;
2449 }
2450 #endif
2451
2452
2453 void CeCosTest::RemoveAllPlatforms()
2454 {
2455 for(int i=TargetTypeMax()-1;i>=0;--i){
2456 delete &Target(i);
2457 }
2458 arTargetInfo.clear();
2459 }
2460
2461 CeCosTest::ServerStatus CeCosTest::ServerStatusValue(LPCTSTR psz)
2462 {
2463 int s;
2464 for(s=0;s<ServerStatusMax;s++){
2465 if(0==_tcsicmp(psz,arServerStatusImage[s])){
2466 break;
2467 }
2468 }
2469 return (ServerStatus)s;
2470
2471 }
2472
2473 const CeCosTest::TargetInfo &CeCosTest::Target (LPCTSTR psz)
2474 {
2475 for(int i=0;i<(signed)arTargetInfo.size();i++){
2476 const TargetInfo &t=arTargetInfo[i];
2477 if(0==_tcsicmp(t.Image(),psz)){
2478 return t;
2479 }
2480 }
2481 return tDefault;
2482 }