Mercurial > nand-ecoscentric
diff 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 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/host/tools/ecostest/common/eCosTest.cpp @@ -0,0 +1,2482 @@ +//####COPYRIGHTBEGIN#### +// +// ---------------------------------------------------------------------------- +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// +// This program is part of the eCos host tools. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// ---------------------------------------------------------------------------- +// +//####COPYRIGHTEND#### +//================================================================= +// +// eCosTest.cpp +// +// Test class +// +//================================================================= +//================================================================= +//#####DESCRIPTIONBEGIN#### +// +// Author(s): sdf +// Contributors: sdf +// Date: 1999-04-01 +// Description: This class abstracts a test for use in the testing infrastructure +// Usage: +// +//####DESCRIPTIONEND#### +/////////////////////////////////////////////////////////////////////////////// +#include "eCosStd.h" +#include "eCosTest.h" +#include "eCosTrace.h" +#include "TestResource.h" +#include "eCosTestUtils.h" +#include "eCosTestSocket.h" +#include "eCosTestSerial.h" +#include "eCosTestSerialFilter.h" +#include "eCosTestDownloadFilter.h" + +#define WF(n) (n+50)/1000,((n+50)%1000)/100 // Present n as whole and fractional part. Round to nearest least significant digit +#define WFS _T("%u.%u") // The format string to output the above + +static int nAuxPort; //hack +static int nAuxListenSock; // hack + +#ifdef _WIN32 + #include "Subprocess.h" + #ifdef _DEBUG + #define CloseHandle(x) try { ::CloseHandle(x); } catch(...) { TRACE(_T("!!! Exception caught closing handle %08x\n"),x); } + #endif +#endif + +std::vector<CeCosTest::TargetInfo> CeCosTest::arTargetInfo; + +LPCTSTR const CeCosTest::arResultImage[1+CeCosTest::StatusTypeMax]= +{_T("NotStarted"), _T("NoResult"), _T("Inapplicable"), _T("Pass"), _T("DTimeout"), _T("Timeout"), _T("Cancelled"), _T("Fail"), _T("AssertFail"), _T("Unknown")}; + +CeCosTest *CeCosTest::pFirstInstance=0; +int CeCosTest::InstanceCount=0; + +LPCTSTR const CeCosTest::arServerStatusImage[1+CeCosTest::ServerStatusMax]={ + _T("Busy"), _T("Ready"), _T("Can't run"), _T("Connection failed"), _T("Locked"), _T("Bad server status")}; +LPCTSTR CeCosTest::ExecutionParameters::arRequestImage [1+ExecutionParameters::RequestTypeMax]={ + _T("Run"), _T("Query"), _T("Lock"), _T("Unlock"), _T("Stop"), _T("Bad request") }; + +const CeCosTest::TargetInfo CeCosTest::tDefault(_T("Unknown"),_T(""),-1); + +// Do not use spaces in image strings for consideration of _stscanf +// One day this can be loadable from some external resource. +int CeCosTest::InitTargetInfo(LPCTSTR pszFilename) +{ + TRACE(_T("InitTargetInfo %s\n"),pszFilename); + FILE *f=_tfopen(pszFilename,_T("rt")); + if(f){ + enum {BUFSIZE=512}; + int nLine=0; + TCHAR buf[BUFSIZE]; + while(_fgetts(buf,sizeof(buf)-1,f)){ + nLine++; + StringArray ar; + String(buf).Chop(ar); + switch(ar.size()){ + // <target> <prefix> <nType> [<gdbcmds>] + case 0: + continue; + case 3: + arTargetInfo.push_back(TargetInfo(ar[0],ar[1],ar[2],0)); + break; + case 1: + case 2: + ERROR(_T("Illegal configuration at line %d\n"),nLine); + continue; + default: + arTargetInfo.push_back(TargetInfo(ar[0],ar[1],ar[2],ar[3])); + break; + } + } + fclose(f); + } + return arTargetInfo.size(); +} + +int CeCosTest::InitTargetInfoReg(LPCTSTR pszRegKey) +{ + +#ifdef _WIN32 + HKEY hKey; + bool rc=ERROR_SUCCESS==RegOpenKeyEx (HKEY_LOCAL_MACHINE, pszRegKey, 0L, KEY_READ, &hKey); + DWORD dwSubKeys=0; + if(rc){ + // Found the given key. + // Subkeys' names are the target image names: + // Subkeys's values are: + // Prefix String + // Type String + // GdbCmd String [optional] + FILETIME ftLastWriteTime; + DWORD dwMaxSubKeyLen; + if(ERROR_SUCCESS==RegQueryInfoKey(hKey,NULL,NULL,NULL,&dwSubKeys,&dwMaxSubKeyLen,NULL,NULL,NULL,NULL,NULL,NULL)){ + TCHAR *szName=new TCHAR[1+dwMaxSubKeyLen]; + DWORD dwSizeName=dwMaxSubKeyLen; + for(DWORD dwIndex=0;ERROR_SUCCESS==RegEnumKeyEx(hKey, dwIndex, szName, &dwSizeName, NULL, NULL, NULL, &ftLastWriteTime); dwIndex++){ + HKEY hKey2; + if(ERROR_SUCCESS!=RegOpenKeyEx (hKey, szName, 0L, KEY_READ, &hKey2)){ + ERROR(_T("Failed to open %s\\%s\n"),pszRegKey,szName); + rc=false; + } else { + DWORD dwMaxValueLen; + if(ERROR_SUCCESS==RegQueryInfoKey(hKey2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,&dwMaxValueLen,NULL,NULL)){ + DWORD dwSizePrefix=dwMaxValueLen; + DWORD dwSizeGdbCmd=dwMaxValueLen; + DWORD dwSizeHwType=dwMaxValueLen; + TCHAR *szPrefix=new TCHAR[1+dwMaxValueLen]; + TCHAR *szGdbCmd=new TCHAR[1+dwMaxValueLen]; + TCHAR *szHwType=new TCHAR[1+dwMaxValueLen]; + if(ERROR_SUCCESS==RegQueryValueEx(hKey2, _T("Prefix"),NULL, NULL,(LPBYTE)szPrefix,&dwSizePrefix)){ + arTargetInfo.push_back(TargetInfo(szName,szPrefix, + (ERROR_SUCCESS==RegQueryValueEx(hKey2,_T("HwType"),NULL, NULL,(LPBYTE)szHwType,&dwSizeHwType))?szHwType:_T(""), + (ERROR_SUCCESS==RegQueryValueEx(hKey2,_T("GdbCmd"),NULL, NULL,(LPBYTE)szGdbCmd,&dwSizeGdbCmd))?szGdbCmd:0 + )); + } + delete [] szPrefix; + delete [] szGdbCmd; + delete [] szHwType; + } + RegCloseKey(hKey2); + } + dwSizeName=dwMaxSubKeyLen; + } + delete [] szName; + } + RegCloseKey(hKey); + } + return rc?dwSubKeys:0; +#else // UNIX + return 0; +#endif +} + +bool CeCosTest::Init() +{ + TRACE(_T("CeCosTest::Init\n")); + srand( (unsigned)time( NULL ) ); + +#ifdef _WIN32 + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD( 2, 0 ); + WSAStartup( wVersionRequested, &wsaData ); + + // get target info from the registry + String strPlatformsKey = _T("Software\\Red Hat\\eCos\\"); + strPlatformsKey += GetGreatestSubkey (_T("Software\\Red Hat\\eCos")); + strPlatformsKey += _T("\\Platforms"); + InitTargetInfoReg (strPlatformsKey); + + // add target info from .eCosrc + LPCTSTR psz=_tgetenv(_T("HOMEDRIVE")); + if(psz){ + String strFile(psz); + psz=_tgetenv(_T("HOMEPATH")); + if(psz){ + strFile+=psz; + } + if(_TCHAR('\\')!=strFile[strFile.GetLength()-1]){ + strFile+=_TCHAR('\\'); + } + strFile+=_T(".eCosrc"); + InitTargetInfo(strFile); + } +#else // UNIX + sigset_t mask; + + // Clean out all the signals + sigemptyset(&mask); + + // Add our sigpipe + sigaddset(&mask, SIGPIPE); + + sigprocmask(SIG_SETMASK, &mask, NULL); + + // _WIN32 not defined so get target info from .eCosrc + LPCTSTR psz=_tgetenv(_T("HOME")); + if(psz){ + String strFile(psz); + strFile+=_T("/.eCosrc"); + InitTargetInfo(strFile); + } +#endif + if(0==TargetTypeMax()){ + ERROR(_T("Failed to initialize any targets\n")); + } + return true; +} + +void CeCosTest::Term() +{ + + TRACE(_T("CeCosTest::Term\n")); + +#ifdef _WIN32 + WSACleanup(); +#endif +}; + +bool CeCosTest::SaveTargetInfo() +{ +#ifdef _WIN32 + // save target info to the registry + String strPlatformsKey = _T("Software\\Red Hat\\eCos\\"); + strPlatformsKey += GetGreatestSubkey (_T("Software\\Red Hat\\eCos")); + strPlatformsKey += _T("\\Platforms"); + return SaveTargetInfoReg (strPlatformsKey); +#else // UNIX + // get target info from .eCosrc + String strFile=_tgetenv(_T("HOME")); + if(strFile.GetLength()>0){ + strFile+=_T("/.eCosrc"); + return SaveTargetInfo(strFile); + } else { + return false; + } +#endif +} + +bool CeCosTest::SaveTargetInfo(LPCTSTR pszFilename) +{ + return false; //FIXME +} + +#ifdef _WIN32 +bool CeCosTest::SaveTargetInfoReg(LPCTSTR pszRegKey) +{ + CProperties::CreateKey(pszRegKey,HKEY_LOCAL_MACHINE); + HKEY hKey; + bool rc=ERROR_SUCCESS==RegOpenKeyEx (HKEY_LOCAL_MACHINE, pszRegKey, 0L, KEY_ALL_ACCESS, &hKey); + if(rc){ + for(int i=0;i<(signed)arTargetInfo.size();i++){ + HKEY hKey2; + DWORD dwDisp; + const TargetInfo &ti=arTargetInfo[i]; + rc&=(ERROR_SUCCESS==RegCreateKeyEx(hKey,ti.Image(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey2, &dwDisp)); + if(rc){ + LPCTSTR pszPrefix=ti.Prefix(); + LPCTSTR pszGdb =ti.GdbCmd(); + LPCTSTR pszHwType=CeCosTest::TargetInfo::arHwTypeImage[ti.Type()]; + rc&=(ERROR_SUCCESS==RegSetValueEx(hKey2,_T("Prefix"),0,REG_SZ, (CONST BYTE *)pszPrefix,(1+_tcslen(pszPrefix))*sizeof TCHAR)) && + (ERROR_SUCCESS==RegSetValueEx(hKey2,_T("HwType"),0,REG_SZ, (CONST BYTE *)pszHwType,(1+_tcslen(pszHwType))*sizeof TCHAR)) && + (ERROR_SUCCESS==RegSetValueEx(hKey2,_T("GdbCmd"),0,REG_SZ, (CONST BYTE *)pszGdb,(1+_tcslen(pszGdb))*sizeof TCHAR)); + } + RegCloseKey(hKey2); + } + RegCloseKey(hKey); + } + return rc; +} +#endif + +static bool CALLBACK IsCancelled(void *pThis) +{ + return CeCosTest::Cancelled==((CeCosTest *)pThis)->Status(); +} + +// Ctors and dtors: +CeCosTest::CeCosTest(const ExecutionParameters &e, LPCTSTR pszExecutable,LPCTSTR pszTitle): +m_nRunCount(0), + m_nStrippedSize(0), + m_nFileSize(0), + m_bDownloading(false), + m_pSock(0), + m_ep(e), + m_strTitle(pszTitle), + m_Status(NotStarted), + m_nDownloadTime(0), + m_nTotalTime(0), + m_nMaxInactiveTime(0), + m_pPort(0) +{ + SetExecutable (pszExecutable); + + TRACE(_T("%%%% Create test instance %08x count:=%d\n"),this,InstanceCount+1); + + GetPath(m_strPath); + + ENTERCRITICAL; + InstanceCount++; + m_pNextInstance=pFirstInstance; + if(m_pNextInstance){ + m_pNextInstance->m_pPrevInstance=this; + } + m_pPrevInstance=0; + pFirstInstance=this; + LEAVECRITICAL; + +} + +CeCosTest::~CeCosTest() +{ + TRACE(_T("%%%% Delete test instance %08x\n"),this); + Cancel(); + CloseSocket(); + if(m_pPort){ + m_pPort->Release(); + //delete m_pPort; + //m_pPort=0; + } + + VTRACE(_T("~CeCosTest(): EnterCritical and decrease instance count\n")); + ENTERCRITICAL; + InstanceCount--; + TRACE(_T("%%%% Destroy instance. Instance count:=%d\n"),InstanceCount); + if(pFirstInstance==this){ + pFirstInstance=m_pNextInstance; + } + if(m_pPrevInstance){ + m_pPrevInstance->m_pNextInstance=m_pNextInstance; + } + if(m_pNextInstance){ + m_pNextInstance->m_pPrevInstance=m_pPrevInstance; + } + LEAVECRITICAL; +} + +bool CeCosTest::RunRemote (LPCTSTR pszRemoteHostPort) +{ + bool rc=false; + m_nRunCount++; + TRACE(_T("RunRemote\n")); + m_strExecutionHostPort=pszRemoteHostPort; + SetPath(m_strPath); + m_Status=NotStarted; + + VTRACE(_T("RemoteThreadFunc()\n")); + + // Find a server. + ConnectForExecution(); + if(Cancelled!=Status()){ + if(ServerSideGdb()){ + // The executable is transmitted to the server for execution. + // Send file size + if(m_pSock->sendInteger(m_nFileSize,_T("file size"))&&m_nFileSize>0){ + int nBufSize=MIN(10000,m_nFileSize); + Buffer b(nBufSize); + TRACE(_T("Sending [%d bytes]\n"), m_nFileSize); + int nToSend=m_nFileSize; + FILE *f1=_tfopen(m_strExecutable,_T("rb")); + if(0==f1){ + Log(_T("Failed to open %s - %s\n"),(LPCTSTR )m_strExecutable,strerror(errno)); + } else { + while (nToSend>0){ + int nRead=fread( b.Data(), 1, nBufSize, f1); + if(nRead<=0){ + Log(_T("Failure reading %s - %s\n"),(LPCTSTR )m_strExecutable,strerror(errno)); + break; + } + if(!send( b.Data(), nRead, _T("executable"))){ + Log(_T("Failure sending %s - %s\n"),(LPCTSTR )m_strExecutable,(LPCTSTR )m_pSock->SocketErrString()); + break; + } + nToSend-=nRead; + } + fclose(f1); + f1=0; + if(nToSend>0){ + TRACE(_T("done [%d bytes sent]\n"),m_nFileSize-nToSend); + Log(_T("Failed to transmit %s - %d/%d bytes sent\n"),(LPCTSTR )m_strExecutable,m_nFileSize-nToSend,m_nFileSize); + } else { + TRACE(_T("done\n")); + rc=true; + } + } + if(!recvResult(9*1000*60)){ // nine minutes + Log(_T("Failed to receive result from remote server\n")); + rc=false; + } + m_pSock->sendInteger(1); // send an ack [n'importe quoi] + CloseSocket(); + } + } else { + // The server sets up a connection between port and tcp/ip socket, and gdb is run locally + String strHostPort,strOutput; + // Big timeout here because we have to wait for the target to be reset + // We do this: + // do { + // target ready indicator (0==fail, 1==ready, 2==fail and will retry) + // any output so far + // } while (2==target ready indicator) + // read host:port + if(GetTargetReady(strHostPort)){ + ENTERCRITICAL; + m_pPort=new CTestResource(Target(), strHostPort, 0); + m_pPort->Use(); + LEAVECRITICAL; + RunLocal(); + delete m_pPort; + m_pPort=0; + m_pSock->sendInteger(123,_T("Terminating ack")); + m_pSock->Close(); + rc=true; + } + } + } + TRACE(_T("RemoteThreadFunc(): Exiting\n")); + return rc; +} + +// Run the test locally +bool CeCosTest::RunLocal() +{ + m_nRunCount++; + bool rc=false; + if(0==CTestResource::Count(m_ep)){ + Log(_T("Cannot run a %s test\n"),m_ep.Target()); + } else { + + TRACE(_T("Run %s, timeouts: active=%d elapsed=%d\n"),(LPCTSTR )m_strExecutable, ActiveTimeout(), DownloadTimeout()); + m_Status=NotStarted; + m_tPrevSample=Now(); + m_tGdbCpuTime=0; + + GetPath(m_strPath); + + TRACE(_T("LocalThreadFunc - target=%s\n"),Target()); + // Acquire a port (our caller may have done this for us) + VTRACE(_T("LocalThreadFunc():Tring to acquire a port\n")); + if(0==m_pPort){ + for(;;){ + m_pPort=CTestResource::GetResource(m_ep); + if(m_pPort||Cancelled==Status()){ + break; + } + Sleep(2000); + TRACE(_T("Waiting for a port\n")); + } + } + VTRACE(_T("\nPort acquired!\n")); + + if(Cancelled!=Status()){ + // This means we have acquired a local port + bool bTargetReady=false; + if(!m_pPort->HasReset()){ + bTargetReady=true; + } else { + bTargetReady=(CResetAttributes::RESET_OK==Reset(false)); + } + // we may proceed to execute the test + if(bTargetReady){ + SetStatus(NotStarted); + if(_TCHAR('\0')!=*(m_pPort->Serial())){ + // No elapsed timeout given - calculate from knowledge of executable size and baud rate + // 10 baud ~= 1 byte/sec, but we halve this to account for download in hex :-( + // This means that a 200k executable is given ~100 seconds + // In any case the whole thing is an overestimate [we should use the stripped size] + // We use a minimum of 30 seconds and add 50% for safety + int nSpeed=((0==m_pPort->Baud()?9600:m_pPort->Baud())/10)/2; // Hex + nSpeed/=2; // Safety + if(NOTIMEOUT==m_ep.DownloadTimeout()){ + m_ep.SetDownloadTimeout (1000*MAX(60,(m_nStrippedSize/nSpeed))); + } + TRACE(_T("Timeout=%d\n"),m_ep.DownloadTimeout()); + } + if(NOTIMEOUT==m_ep.ActiveTimeout()){ + m_ep.SetActiveTimeout(1000*(_TCHAR('\0')==*(m_pPort->Serial())?300:30)); + } + + { + // Construct commands for gdb + const TargetInfo &t=Target(Target()); + TargetInfo::HwType hwt=t.Type(); + StringArray arstrGdbCmds; + LPCTSTR pszPrompt; + String strGdb; + // running using gdb + pszPrompt=_T("(gdb) "); + strGdb.Format(_T("%s-gdb -nw %s"),t.Prefix(),(LPCTSTR)CygPath(m_strExecutable)); + // Tell gdb its paper size :-) + arstrGdbCmds.push_back(_T("set height 0")); + arstrGdbCmds.push_back(_T("set remotedebug 0")); + + if(_TCHAR('\0')!=*t.GdbCmd()){ + arstrGdbCmds.push_back(t.GdbCmd()); + } + + String str; + if(_TCHAR('\0')!=*(m_pPort->Serial())){ + // Talking remotely + if(CeCosTestSocket::IsLegalHostPort(m_pPort->Serial())){ + // Talking to a tcp/ip socket + arstrGdbCmds.push_back(_T("set watchdog 0")); + } else { + // Talking to a serial port + str.Format(_T("set remotebaud %d"),m_pPort->Baud()); + arstrGdbCmds.push_back(str); + } + str.Format(_T("target remote %s"),m_pPort->Serial()); +#ifdef _WIN32 + // Serial names on windows must be in l.c. (gdb bug) + for(TCHAR *c=str.GetBuffer();*c;c++){ + if(isalpha(*c)){ + *c=(TCHAR)_totlower(*c); + } + } + str.ReleaseBuffer(); +#endif + arstrGdbCmds.push_back(str); + } + + if(TargetInfo::SYNTHETIC!=hwt){ + arstrGdbCmds.push_back(_T("load")); + } + + arstrGdbCmds.push_back(_T("break cyg_test_exit")); + arstrGdbCmds.push_back(_T("break cyg_assert_fail")); + if(/*start hack*/BreakpointsOperational()/*end hack*/){ + arstrGdbCmds.push_back(_T("break cyg_test_init")); + } + + switch(hwt){ + case TargetInfo::SYNTHETIC: + case TargetInfo::SIM: + arstrGdbCmds.push_back(_T("run")); + break; + case TargetInfo::HARDWARE: + case TargetInfo::HARDWARE_NO_BP: + case TargetInfo::REMOTE_SIM: + arstrGdbCmds.push_back(_T("cont")); // run the program + break; + default: + assert(false); + } + + if(BreakpointsOperational()){ + str.Format(_T("set cyg_test_is_simulator=%d"),hwt); + arstrGdbCmds.push_back(str); + arstrGdbCmds.push_back(_T("cont")); // continue from cyg_test_init breakpoint + } + + // run/cont command must be the last (logic in DriveGdb) + + TRACE(_T("Calling RunGdb\n")); + RunGdb(strGdb,pszPrompt,arstrGdbCmds); + rc=true; + } + } + } + TRACE(_T("LocalThreadFunc - releasing resource\n")); + if(m_pPort){ + m_pPort->Release(); + m_pPort=0; + } + TRACE(_T("LocalThreadFunc - exiting\n")); + } + + return rc; +} + +void CeCosTest::Cancel () +{ + SetStatus(Cancelled); +} + +CeCosTest::ServerStatus CeCosTest::Connect (String strHost,int port, CeCosTestSocket *&pSock, const ExecutionParameters &e,String &strInfo,Duration dTimeout) +{ + // Find out whether this host is receptive + ServerStatus s=CONNECTION_FAILED; + pSock=new CeCosTestSocket(strHost,port,dTimeout); + int nStatus; + if(pSock->Ok() && + pSock->sendString(e.Image(), _T("execution parameters")) && + pSock->recvInteger(nStatus,_T("ready status")) && + pSock->recvString(strInfo)){ + s=(ServerStatus)MIN(nStatus,ServerStatusMax); + } + if(SERVER_READY!=s || ExecutionParameters::RUN!=e.Request()){ + delete pSock; + pSock=0; + } + return s; +} + +// Initiate a connection to hostName:nPort and acquire the ready status [retry until this is achieved] +// The socket (m_pSock) is left open. +// This function is either called with m_strExecutionHostPort already set to a desired server +// or else m_strExecutionHostPort empty (in which case the server is / dynamically) + +void CeCosTest::ConnectForExecution () +{ + bool bSchedule=(0==m_strExecutionHostPort.GetLength()); + Duration nDelay=2000; + + m_pSock=0; + + bool *arbHostTried=0; + + while(Cancelled!=Status()){ + StringArray arstrHostPort,arstrTries; + int nChoices; + + if(bSchedule){ + if(!CTestResource::GetMatches(m_ep,arstrHostPort)){ + Log(_T("Could not establish matches\n")); + continue; + } + nChoices=arstrHostPort.size(); + if(nChoices>0){ + TRACE(_T("ConnectForExecution: choices are:\n")); + for(int i=0;i<nChoices;i++){ + TRACE(_T("\t%s\n"),(LPCTSTR )arstrHostPort[i]); + } + } + } else { + // Server has already been picked by caller + nChoices=1; + String str; + arstrHostPort.push_back(m_strExecutionHostPort); + } + + if(nChoices>0){ + delete [] arbHostTried; + arbHostTried=new bool[nChoices]; + for(int i=0;i<nChoices;i++){ + arbHostTried[i]=false; + } + + // Loop around the choices + for(int nUntried=nChoices;nUntried>0;nUntried--) { + // Select one we haven't tried already: + int nChoice; + do { + nChoice=rand() % nChoices; + } while (arbHostTried[nChoice]); + + m_strExecutionHostPort=arstrHostPort[nChoice]; + + TRACE(_T("ConnectForExecution: chosen %s\n"),(LPCTSTR )m_strExecutionHostPort); + String strHost; + int nPort; + CeCosTestSocket::ParseHostPort(m_strExecutionHostPort, strHost, nPort); + if(nPort){ + // If we're using the resource server we had better check that the host + // we are about to lock has not been resource-locked (the other match checks + // will of course always succeed) + String strInfo; + ServerStatus s=bSchedule && !CTestResource::Matches(strHost, nPort,m_ep)? +SERVER_LOCKED: + Connect(strHost,nPort,m_pSock,m_ep,strInfo); + arbHostTried[nChoice]=true; + TRACE(_T("Connect: %s says %s %s\n"),(LPCTSTR )m_strExecutionHostPort,Image(s),(LPCTSTR )strInfo); + CTestResource *pResource=CTestResource::Lookup(strHost,nPort); + if(pResource){ + String str; + str.Format(_T("%s %s %s"),(LPCTSTR )pResource->Output(),(LPCTSTR )strInfo,Image(s)); + arstrTries.push_back(str); + } + if(SERVER_READY==s){ + // So that's ok then. We're outta here. + Interactive(_T("Connected to %s\n"),(LPCTSTR )m_strExecutionHostPort); + goto Done; + } else { + delete m_pSock; + m_pSock=0; + } + } + } + } + + Interactive(_T("Warning - could not connect to any test servers:\n")); + if(arstrTries.size()>0){ + for(unsigned int i=0;i<arstrTries.size();i++){ + Interactive(_T(" %s\n"),(LPCTSTR )arstrTries[i]); + } + } else { + Interactive(_T("No servers available to execute %s test:\n"),m_ep.Target()); + ENTERCRITICAL; + for(CTestResource *pResource=CTestResource::First();pResource;pResource=pResource->Next()){ + Interactive(_T(" %s\n"),(LPCTSTR )pResource->Output()); + } + LEAVECRITICAL; + } + Interactive(_T("Retry in %d seconds...\n"),nDelay/1000); + + // We have tried all possibilities - sleep before retrying + Sleep(nDelay); + + if(Cancelled==m_Status){ + TRACE(_T("ConnectForExecution : cancelled\n")); + goto Done; + } + if(nDelay<20*1000){ + nDelay+=rand() % 500; + } + } +Done: + delete [] arbHostTried; +} + +void CeCosTest::LogResult() +{ + CeCosTrace::Out(ResultString()); + CeCosTrace::Out(_T("\n") ); + CeCosTrace::Trace(_T("%s\n"),ResultString()); +} + +void CeCosTest::SetStatus (StatusType status) +{ + ENTERCRITICAL; + if((int)status>(int)m_Status){ + TRACE(_T("Status <- %s\n"),Image(status)); + m_Status=status; + } + LEAVECRITICAL; +} + +bool CeCosTest::WaitForAllInstances(int nPoll,Duration nTimeout) +{ + Time t0=Now(); + while(InstanceCount>0){ + Sleep(nPoll); + if(NOTIMEOUT!=nTimeout && Now()-t0>nTimeout){ + return false; + } + } + return true; +} + +void CeCosTest::DeleteAllInstances() +{ + while(pFirstInstance){ + delete pFirstInstance; + } +} + +void CeCosTest::CancelAllInstances() +{ + ENTERCRITICAL; + for(CeCosTest *pTest=pFirstInstance;pTest;pTest=pTest->m_pNextInstance){ + pTest->Cancel(); + } + LEAVECRITICAL; +} + +// The same format is used for _stscanf as for Format (which is like printf), so restrict to the format specifiers +// the former is happy with. In particular, do not use %-3s etc... + +LPCTSTR CeCosTest::pszFormat= +// 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 +_T("%04d-%02d-%02d %02d:%02d:%02d ") // Time +_T("%15s ") // Execution host:port +_T("%16s ") // Target +_T("%30s ") // Executable tail +_T("%11s ") // Result +_T("%dk/%dk ") // Sizes +_T("D=") WFS _T("/") WFS _T(" Total=") WFS _T(" ") // Times +_T("E=") WFS _T("/") WFS _T(" ") +_T("\"%s\""); + +bool CeCosTest::Value ( + LPCTSTR pszStr, + struct tm &t, + StatusType &status, + String &target, + String &strExecutionHostPort, + String &strExecutableTail, + String &strTitle, + + int &nFileSize, + Duration &nTotalTime, + Duration &nMaxInactiveTime, + Duration &nDownloadTime, + Duration &nDownloadTimeout, + Duration &nActiveTimeout, + + int &nDownloadedSize) +{ + int nLen=_tcslen(pszStr); + String strStatus; + + nFileSize=nTotalTime=nMaxInactiveTime=nDownloadTime=nDownloadTimeout=nActiveTimeout=nDownloadedSize=0; + + int nTotalTimeFrac=0; + int nMaxInactiveTimeFrac=0; + int nActiveTimeoutFrac=0; + int nDownloadTimeFrac=0; + int nDownloadTimeoutFrac=0; + + static String strFormat; + if(0==strFormat.GetLength()){ + // Construct a version of the format string sans length attributes for %s items + LPCTSTR c=pszFormat; + TCHAR *d=strFormat.GetBuffer(_tcslen(pszFormat)); + while(_TCHAR('\0')!=*c){ + if(_TCHAR('%')==c[0] && _istdigit(c[1])){ + *d++=_TCHAR('%'); + do { + c++; + } while (_istdigit(*c)); + } + *d++=*c++; + } + *d=_TCHAR('\0'); + strFormat.ReleaseBuffer(); + } + + _stscanf(pszStr, + strFormat, + &t.tm_year,&t.tm_mon,&t.tm_mday, + &t.tm_hour,&t.tm_min,&t.tm_sec, // Time of day + strExecutionHostPort.GetBuffer(1+nLen), // Execution host:port + target.GetBuffer(1+nLen), // Target + strExecutableTail.GetBuffer(1+nLen), // Executable + strStatus.GetBuffer(1+nLen), // Result + &nDownloadedSize,&nFileSize, // Sizes + &nDownloadTime,&nDownloadTimeFrac, // Times + &nDownloadTimeout,&nDownloadTimeoutFrac, + &nTotalTime,&nTotalTimeFrac, + &nMaxInactiveTime,&nMaxInactiveTimeFrac, + &nActiveTimeout,&nActiveTimeoutFrac, + strTitle.GetBuffer(1+nLen) // Title + ); + + strExecutionHostPort.ReleaseBuffer(); + target.ReleaseBuffer(); + strExecutableTail.ReleaseBuffer(); + strStatus.ReleaseBuffer(); + strTitle.ReleaseBuffer(); + status=StatusTypeValue(strStatus); + + LPCTSTR c1=_tcschr(pszStr,_TCHAR('"')); + if(c1){ + c1++; + LPCTSTR c2=_tcschr(c1+1,_TCHAR('"')); + if(c2){ + strTitle=String(c1,c2-c1); + } + } + + nTotalTime=nTotalTime*1000+nTotalTimeFrac*100; + nMaxInactiveTime=nMaxInactiveTime*1000+nMaxInactiveTimeFrac*100; + nActiveTimeout=nActiveTimeout*1000+nActiveTimeoutFrac*100; + nDownloadTime=nDownloadTime*1000+nDownloadTimeFrac*100; + nDownloadTimeout=nDownloadTimeout*1000+nDownloadTimeoutFrac*100; + + nFileSize*=1024; + nDownloadedSize*=1024; + t.tm_year-=1900; + t.tm_mon--; + 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 && + status!=StatusTypeMax + //&& exetype!=ExecutionParameters::ExecutableTypeMax + ; +} + +LPCTSTR const CeCosTest::ResultString(bool bIncludeOutput) const +{ + String strTitle(m_strTitle); + String strExecutionHostPort(m_strExecutionHostPort); + + if(0==strTitle.GetLength()){ + strTitle=CeCosTestUtils::SimpleHostName(); + strTitle+=_TCHAR(':'); + strTitle+=m_strExecutable; + } + + if(0==strExecutionHostPort.GetLength()){ + strExecutionHostPort=CeCosTestUtils::SimpleHostName(); + strExecutionHostPort+=_T(":0"); + } + + ENTERCRITICAL; + time_t ltime; + time(<ime); + struct tm *now=localtime( <ime ); + + m_strResultString.Format( + pszFormat, + 1900+now->tm_year,1+now->tm_mon,now->tm_mday, + now->tm_hour,now->tm_min,now->tm_sec, // Time of day + (LPCTSTR )strExecutionHostPort, // Execution host:port + Target(), // Target + ExecutableTail(), // Executable + Image(Status()), // Result + m_nStrippedSize/1024,m_nFileSize/1024, // Sizes + WF(m_nDownloadTime),WF(DownloadTimeout()),WF(m_nTotalTime),// Times + WF(m_nMaxInactiveTime),WF(ActiveTimeout()), + (LPCTSTR )strTitle // Title + ); + if(bIncludeOutput && m_strOutput.GetLength()>0){ + m_strResultString+=_TCHAR('\n'); + m_strResultString+=m_strOutput; + } + LEAVECRITICAL; + return m_strResultString; +} + +// Run as a server, listening on the port given as parameter +bool CeCosTest::RunAgent(int nTcpPort) +{ + bool bLocked=false; + + nAuxPort=nTcpPort+3000;//hack + nAuxListenSock=CeCosTestSocket::Listen(nAuxPort);//hack + if(-1!=nAuxListenSock){ + // Create socket + int nSock = CeCosTestSocket::Listen(nTcpPort); + int nLastClient=0; + int nRejectionCount=0; + if (-1!=nSock) { + for (;;) { + try { + CeCosTestSocket *pSock=new CeCosTestSocket(nSock); // AcceptThreadFunc deletes if not deleted below + String str; + // Read the execution parameters + if(!pSock->recvString(str)){ + // Socket error on the recv - nothing much we can do + TRACE(_T("RunAgent : could not read execution parameters\n")); + delete pSock; + pSock=0; + } else { + ExecutionParameters e; + e.FromStr(str); + TRACE(_T("Execution parameters: %s\n"),(LPCTSTR)e.Image()); + bool bNuisance=false; + ServerStatus s; + CTestResource *pPort=0; + String strInfo; + if(!e.IsValid()){ + // Looks like a confused client ... + strInfo.Format(_T("Bad target value %s read from client\n"),e.Target()); + s=SERVER_CANT_RUN; + } else if(0==CTestResource::Count(e)){ + // No chance of running this test + strInfo.Format(_T("Cannot run a %s test from this server\n"),e.Target()); + s=SERVER_CANT_RUN; + } else { + switch(e.Request()) { + case ExecutionParameters::LOCK: + if(bLocked){ + s=SERVER_BUSY; + } else { + WaitForAllInstances(1000,NOTIMEOUT); + bLocked=true; + s=SERVER_LOCKED; + } + break; + case ExecutionParameters::UNLOCK: + if(bLocked){ + bLocked=false; + s=SERVER_READY; + } else { + s=SERVER_BUSY; + } + break; + case ExecutionParameters::QUERY: + if (bLocked) { + s=SERVER_LOCKED; + } else { + pPort=CTestResource::GetResource(e); + if(0==pPort){ + s=SERVER_BUSY; + strInfo.Format(_T("serving %s"),(LPCTSTR )CeCosTestSocket::ClientName(nLastClient)); + } else { + s=SERVER_READY; + pPort->Release(); + pPort=0; + } + } + break; + case ExecutionParameters::RUN: + if (bLocked) { + s=SERVER_LOCKED; + } else { + pPort=CTestResource::GetResource(e); + if(0==pPort){ + // We must disappoint our client + nRejectionCount++; + strInfo.Format(_T("serving %s"),(LPCTSTR )CeCosTestSocket::ClientName(nLastClient)); + s=SERVER_BUSY; + /* + } else if(nLastClient==pSock->Client() && nRejectionCount>10) { + // Don't answer the phone to a nuisance caller + s=SERVER_BUSY; + bNuisance=true; + nRejectionCount--; + pPort->Release(); + pPort=0; + */ + } else { + s=SERVER_READY; + nRejectionCount=0; + nLastClient=pSock->Client(); + } + } + break; + case ExecutionParameters::STOP: + s=SERVER_READY; + break; + default: + s=SERVER_CANT_RUN; + } + } + +#ifndef VERBOSE + if(ExecutionParameters::QUERY!=e.Request()) +#endif + TRACE(_T("RunAgent : %s request tActive=%d tElapsed=%d Target=%s Reply status=%s %s Nuisance=%d\n"), + e.Image(e.Request()), + e.ActiveTimeout(),e.DownloadTimeout(),e.Target(), + Image(s), + (LPCTSTR )strInfo, + bNuisance); + + bool bSendok=pSock->sendInteger(s) && pSock->sendString(strInfo); + + TRACE(_T("RunAgent(1)\n")); + + if(SERVER_READY==s && bSendok && ExecutionParameters::RUN==e.Request()){ + + // Create a new class instance + // AcceptThreadFunc deletes the instance and closes new_sock + // RunLocal, called by AcceptThreadFunc, releases the port + // We dream up a temporary name for the executable + // No need for meaningful callback, but must run asynchronously + String strTempFile; + ENTERCRITICAL; + strTempFile.Format(_T("%s-%s-%d"),_ttmpnam(0),e.Target(),nTcpPort); + LEAVECRITICAL; + + CeCosTest *pTest=new CeCosTest(e,strTempFile); + pTest->m_pSock=pSock; + pTest->m_strExecutionHostPort.Format(_T("%s:%d"),CeCosTestUtils::HostName(),nTcpPort); + pTest->m_pPort=pPort; + + CeCosThreadUtils::RunThread(SAcceptThreadFunc,pTest,_T("SAcceptThreadFunc")); + // AcceptThreadFunc deletes pSock + + } else { + TRACE(_T("RunAgent(2)\n")); + delete pSock; + pSock=0; + if(pPort){ + pPort->Release(); + pPort=0; + } + TRACE(_T("RunAgent(3)\n")); + if(CeCosTest::ExecutionParameters::STOP==e.Request()){ + CancelAllInstances(); + WaitForAllInstances(1000,20*1000); + break; + } + } + } + } + catch(...){ + TRACE(_T("!!! Exception caught in RunAgent()\n")); + } + } + CeCosTestSocket::CloseSocket (nSock); + } + VTRACE(_T("RunAgent(): returning false\n")); + CeCosTestSocket::CloseSocket(nAuxListenSock); + } + return false; +} + +CeCosTest::StatusType CeCosTest::StatusTypeValue(LPCTSTR pszStr) +{ + for(int i=0;i<StatusTypeMax;i++){ + StatusType t=(StatusType)i; + if(0==_tcsicmp(Image(t),pszStr)){ + return t; + } + } + return StatusTypeMax; +} + +// Thread to run ConnectSocketToSerial +void CeCosTest::ConnectSocketToSerialThreadFunc() +{ + TRACE(_T("ConnectSocketToSerialThreadFunc sock=%d\n"),nAuxListenSock); + { + + CeCosTestSerialFilter serial_filter; + CeCosTestSocket::FilterFunc *serial_filter_function = + &SerialFilterFunction; + + CeCosTestDownloadFilter download_filter; + CeCosTestSocket::FilterFunc *download_filter_function = + &DownloadFilterFunction; + + bool accept_connection = true; + + CeCosTestSerial serial; + serial.SetBlockingReads(false); + bool rc=false; + // Open serial device. + if (!serial.Open(m_pPort->Serial(),m_pPort->Baud())){ + ERROR(_T("Couldn't open port %s\n"),m_pPort->Serial()); + } else { + while(accept_connection) { + // Flush the serial buffer. + serial.Flush(); + TRACE(_T("ConnectSocketToSerial: waiting for connection...\n")); + CeCosTestSocket socket; + if(!socket.Accept(nAuxListenSock,&m_bStopConnectSocketToSerial)){ + ERROR(_T("ConnectSocketToSerial - couldn't accept\n")); + break; + } else if (m_pSock->Client() != socket.Client()){ + // Make sure the client is who we think it is... + TRACE(_T("ConnectSocketToSerialThread - illegal connection attempted from %s\n"),(LPCTSTR )socket.ClientName(socket.Client())); + } else { + try { + 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); + + // If the download filter was just active, it may + // allow the session to continue. + accept_connection = download_filter.ContinueSession(); + + } + catch (LPCTSTR pszMsg){ + Log(_T("!!! ConnectSocketToSerial exception caught: %s!!!\n"),pszMsg); + rc=false; + break; + } + catch (...){ + Log(_T("!!! ConnectSocketToSerial exception caught!!!\n")); + rc=false; + break; + } + } + } + } + TRACE(_T("ConnectSocketToSerial : done\n")); + } +} + +static bool CALLBACK DerefBool(void *pParam) +{ + return *(bool *)pParam; +} + +// Function called (on a separate thread) to process a successful connection to the RunAgent loop +void CeCosTest::AcceptThreadFunc() +{ + SetPath(m_strPath); + + if(ServerSideGdb()){ + int n; + if(m_pSock->recvInteger(n,_T("file size"))){ + m_nFileSize=n; + // Read file from the socket + bool bCanRun=true; + TRACE(_T("AcceptThreadFunc file size=%d reading...\n"),m_nFileSize); + FILE *f2; + f2=_tfopen(m_strExecutable,_T("wb")); + if(0==f2){ + Log(_T("Could not create %s - %s\n"),(LPCTSTR )m_strExecutable,strerror(errno)); + bCanRun=false; + } + unsigned int nBufSize=MIN(100000,m_nFileSize); + Buffer b(nBufSize); + unsigned int nWritten=0; + unsigned int nRead=0; + while(nRead<m_nFileSize){ + int nToRead=MIN(nBufSize,m_nFileSize-nRead); + if(!recv( b.Data(), nToRead, _T("executable"))){ + break; + } + nRead+=nToRead; + if(0!=f2){ + char *c=(char *)b.Data(); + while(nToRead>0){ + int w=fwrite(c,1,nToRead,f2); + if(-1==w){ + Log(_T("Write error on %s - %s\n"),(LPCTSTR )m_strExecutable,strerror(errno)); + bCanRun=false; + break; + } + nWritten+=w; + c+=w; + nToRead-=w; + } + } + } + TRACE(_T("Accept - done reading [%d bytes read, %d bytes written]\n"),nRead,nWritten); + if(0!=f2){ + fclose(f2); + _tchmod(m_strExecutable,00700); // user read, write and execute + } + if(0!=f2 && m_nFileSize!=nWritten){ + Log(_T("Failed to create %s correctly [%d/%d bytes written]\n"),(LPCTSTR )m_strExecutable, nWritten, m_nFileSize); + bCanRun=false; + } + SetExecutable(m_strExecutable); // to set stripped length and title + RunLocal(); + _tunlink(m_strExecutable); + } + sendResult(); + m_pSock->recvInteger(n); // receive an ack + } else { + bool bTargetReady; + if(_TCHAR('\0')==*(m_pPort->ResetString())){ + bTargetReady=true; + TRACE(_T("No reset possible\n")); + } else { + Log(_T("Resetting target using %s"),(LPCTSTR)m_pPort->ResetString()); + bTargetReady=(CResetAttributes::RESET_OK==Reset(true)); + } + TRACE(_T("Send Target Ready indicator=%d\n"),bTargetReady); + m_pSock->sendInteger(bTargetReady,_T("target ready indicator")); + + int nAck; + int dTimeout=m_ep.DownloadTimeout()+MAX(3*m_ep.ActiveTimeout(),15*60*1000); + + if(bTargetReady){ + if(CeCosTestSocket::IsLegalHostPort(m_pPort->Serial())){ + TRACE(_T("Sending %s\n"),(LPCTSTR )m_pPort->Serial()); + m_pSock->sendString(m_pPort->Serial(),_T("Serial name")); + m_pSock->recvInteger(nAck,_T("Terminating ack"),dTimeout); + TRACE(_T("Terminating ack=%d\n"),nAck); + } else { + String strHostPort(CeCosTestSocket::HostPort(CeCosTestUtils::HostName(),nAuxPort)); + + TRACE(_T("Using %s\n"),(LPCTSTR )strHostPort); + + if(m_pSock->sendString(strHostPort,_T("host:port"))){ + + // This Boolean signifies that the serial<-->tcp/ip conversation is done. It may be set + // on completion of the ConnectSocketToSerial thread (which is why we pass it to runthread) + // and also set by us to *cause* the thread to complete. + + bool bConnectSocketToSerialThreadDone=false; // Indication of termination of ConnectSocketToSerial thread + m_bStopConnectSocketToSerial=false; // Used to tap ConnectSocketToSerial thread on the shoulder + + CeCosThreadUtils::RunThread(SConnectSocketToSerialThreadFunc,this,&bConnectSocketToSerialThreadDone,_T("SConnectSocketToSerialThreadFunc")); + + // Wait for either client or the ConnectSocketToSerial thread to finish. + m_pSock->recv(&nAck,1,_T("Terminating ack"),dTimeout,DerefBool,&bConnectSocketToSerialThreadDone); + TRACE(_T("Terminating ack=%d\n"),nAck); + if(!bConnectSocketToSerialThreadDone){ + // Tap ConnectSocketToSerial thread on the shoulder + TRACE(_T("Waiting for ConnectSocketToSerial thread to terminate...\n")); + m_bStopConnectSocketToSerial=true; + CeCosThreadUtils::WaitFor(bConnectSocketToSerialThreadDone); + } + TRACE(_T("ConnectSocketToSerial thread terminated...\n")); + + } + } + } + } + delete this; +} + +bool CeCosTest::send(const void *pData,unsigned int nLength,LPCTSTR pszMsg,Duration dTimeout) +{ + return m_pSock->send(pData,nLength,pszMsg,dTimeout,IsCancelled,this); +} + +bool CeCosTest::recv(const void *pData,unsigned int nLength,LPCTSTR pszMsg,Duration dTimeout) +{ + return m_pSock->recv(pData,nLength,pszMsg,dTimeout,IsCancelled,this); +} + +void CeCosTest::Log(LPCTSTR pszFormat, ...) +{ + va_list args; + va_start(args, pszFormat); + String str; + str.vFormat(pszFormat,args); + va_end(args); + LogString(str); +} + +void CeCosTest::LogString(LPCTSTR psz) +{ + if(*psz){ + ENTERCRITICAL; + m_strOutput+=psz; + LEAVECRITICAL; + if(CeCosTrace::IsInteractive()){ + CeCosTrace::Out(psz); + } else { + TRACE(_T("%s"),psz); + } + } +} + +bool CeCosTest::sendResult(Duration dTimeout) +{ + bool rc= + m_pSock->sendInteger(m_Status,_T("result"),dTimeout) && + m_pSock->sendInteger(m_nDownloadTime,_T("result"),dTimeout) && + m_pSock->sendInteger(m_nTotalTime,_T("result"),dTimeout) && + m_pSock->sendInteger(m_nMaxInactiveTime,_T("result"),dTimeout) && + m_pSock->sendString (m_strOutput,_T("result"),dTimeout); + return rc; +} + +bool CeCosTest::recvResult(Duration dTimeout) +{ + String strOutput; + int nStatus=StatusTypeMax; + bool rc= + m_pSock->recvInteger(nStatus,_T("result"),dTimeout) && + m_pSock->recvInteger(m_nDownloadTime,_T("result"),dTimeout) && + m_pSock->recvInteger(m_nTotalTime,_T("result"),dTimeout) && + m_pSock->recvInteger(m_nMaxInactiveTime,_T("result"),dTimeout) && + m_pSock->recvString (strOutput,_T("result"),dTimeout); + m_Status=(StatusType)MIN(nStatus,StatusTypeMax); + LogString(strOutput); + return rc; +} + +#ifndef _WIN32 +// This function may be run as a thread (win32) or called (unix). +// It runs gdb. +void CeCosTest::DriveGdb(LPCTSTR pszPrompt,const StringArray &arstrGdbCmds) +{ + unsigned int nCmdIndex=0; + + SetStatus(NotStarted); + + m_nMaxInactiveTime=0; + m_nTotalTime=0; + m_nDownloadTime=0; + + m_bDownloading=true; + + m_tBase=GdbTime(); + m_tBase0=GdbTime(); + m_tWallClock0=Now(); + + TRACE(_T("DriveGdb()\n")); + + int nLastGdbInst=0; + + // Loop until 1 of: + // 1. Timeout detected + // 2. Gdb process is dead + // 3. At a gdb prompt and no more commands to send to gdb + // 4. Pipe read failure + // 5. Pipe write failure + do { + String str; + int readrc=ReadPipe(str,/*bBlockingReads=*/false); + switch(readrc){ + case 0: + Sleep(250); // only unix will execute this + break; + case -1: + goto Done; // case 4 + break; + default: + LogTimeStampedOutput(str); + + if(m_strOutput.GetLength()>20000){ + LogString(_T("\n>>>> Infra FAIL\n*** too much output ***\n>>>>\n")); + SetStatus(Fail); + goto Done; + } + + // Test for program loaded and started: + // (remember SetStatus cannot downgrade the status if already > NoResult) + if(OutputContains(_T("Starting program: /")) || (OutputContains(_T("Start address"))&&OutputContains(_T("Continuing.")))){ + SetStatus(NoResult); + } + + m_tBase=GdbTime(); + // // If can only hit a single breakpoint don't expect cyg_test_exit to stop us: + // if(!BreakpointsOperational() && (OutputContains(_T("EXIT:"))||OutputContains(_T("NOTAPPLICABLE:")))){ + // goto Done; + // } + + if(AtPrompt(pszPrompt)){ + m_tBase=GdbTime(); + TRACE(_T("DriveGdb(1)\n")); + // gdb's output included one or more prompts + // Send another command along + if(nCmdIndex>=arstrGdbCmds.size()){ + // Nothing further to say to gdb - exit + TRACE(_T("DriveGdb(2)\n")); + goto Done; // case 3 + } else { + String strCmd(arstrGdbCmds[nCmdIndex++]); + TRACE(_T("DriveGdb(2a) - strCmd='%s' nLastGdbInst=%d\n"),(LPCTSTR)strCmd,nLastGdbInst); + // If at a prompt and we can see a GDB instruction, send it down + LPCTSTR pszGdbcmd=_tcsstr(nLastGdbInst+(LPCTSTR)m_strOutput,_T("GDB:")); + if(pszGdbcmd){ + TRACE(_T("DriveGdb(2b)\n")); + pszGdbcmd+=4; + TCHAR cTerm; + if(_TCHAR('<')==*pszGdbcmd){ + cTerm=_TCHAR('>'); + pszGdbcmd++; + } else { + cTerm=_TCHAR('\n'); + } + TRACE(_T("DriveGdb(2c)\n")); + LPCTSTR c=_tcschr(pszGdbcmd,cTerm); + if(c){ + TRACE(_T("DriveGdb(2d)\n")); + strCmd=String(pszGdbcmd,c-pszGdbcmd); + nLastGdbInst=c+1-(LPCTSTR)m_strOutput; + nCmdIndex--; // undo increment above + } + } + strCmd+=_TCHAR('\n'); + LogString(strCmd); + TRACE(_T("DriveGdb(3)\n")); + if(!WritePipe(strCmd)){ + TRACE(_T("Writepipe returned error\n")); + goto Done; // case 5 + } + TRACE(_T("DriveGdb(4)\n")); + if(0==_tcscmp(strCmd,_T("run\n"))||0==_tcscmp(strCmd,_T("cont\n"))){ + m_tBase=GdbTime(); + m_bDownloading=false; + } + TRACE(_T("DriveGdb(5)\n")); + } + } + break; + } + + } while (GdbProcessAlive() && CheckForTimeout()); // cases 2 and 1 + +Done: + + TRACE(_T("DriveGdb() - done\n")); + + Suck(pszPrompt); + if(GdbProcessAlive() && AtPrompt(pszPrompt)){ + LogString(_T("bt\n")); + WritePipe(_T("bt\n")); + Suck(pszPrompt); + LogString(_T("quit\n")); + WritePipe(_T("quit\n")); + } + + // Read anything gdb has to say [within limits] + Suck(pszPrompt); + AnalyzeOutput(); + + m_nTotalTime=Now()-m_tWallClock0; + TRACE(_T("Exiting DriveGdb()\n")); + +} +#endif + +// Return time used by inferior gdb process - CPU for sim, wallclock otherwise +Time CeCosTest::GdbTime() +{ + return _TCHAR('\0')==*(m_pPort->Serial())?GdbCpuTime():Now(); +} + +bool CeCosTest::CheckForTimeout() +{ + bool rc=false; + if(TimeOut!=m_Status && DownloadTimeOut!=m_Status){ + Duration &dTime=m_bDownloading?m_nDownloadTime:m_nMaxInactiveTime; + Time t=GdbTime(); + if(t){ + dTime=MAX(dTime,Duration(GdbTime()-m_tBase)); + } + Duration dTimeout=m_bDownloading?DownloadTimeout():ActiveTimeout(); + if(dTimeout!=NOTIMEOUT && dTime>dTimeout) { + Log(_T("\n*** Timeout - %s time ") WFS _T(" exceeds limit of ") WFS _T("\n"), + m_bDownloading?_T("download"):_T("MAX inactive"),WF(dTime),WF(dTimeout)); + SetStatus(m_bDownloading?DownloadTimeOut:TimeOut); + } else if(Now()-m_tWallClock0>MAX(3*dTimeout,15*60*1000)){ + Log(_T("\n*** Timeout - total time ") WFS _T(" exceeds limit of ") WFS _T("\n"), + WF(Now()-m_tWallClock0),WF(MAX(3*dTimeout,15*60*1000))); + SetStatus(m_bDownloading?DownloadTimeOut:TimeOut); + } else { + rc=true; + } + } + return rc; +} + +void CeCosTest::Interactive(LPCTSTR pszFormat, ...) +{ + va_list marker; + va_start (marker, pszFormat); + String str; + str.vFormat(pszFormat,marker); + va_end (marker); + + if(CeCosTrace::IsInteractive()){ + CeCosTrace::Out(str); + } else { + CeCosTrace::Trace(_T("%s"),(LPCTSTR)str); + } +} + +LPCTSTR const CeCosTest::Title() const { + return m_strTitle; +} + +// Convert a path to something a cygwin tool will understand. Used when invoking -size and -gdb +String CeCosTest::CygPath (LPCTSTR pszPath) +{ +#ifdef _WIN32 + String str; + TCHAR *buf=str.GetBuffer(2+MAX_PATH); + TCHAR *pszFname; + if(::GetFullPathName(pszPath,MAX_PATH,1+buf, &pszFname)){ + GetShortPathName(1+buf,1+buf,MAX_PATH); // ignore errors + buf[0]=_TCHAR('/'); + buf[2]=buf[1]; + buf[1]=_TCHAR('/'); + for(int i=3;buf[i];i++){ + if(_TCHAR('\\')==buf[i]){ + buf[i]=_TCHAR('/'); + } + } + str.ReleaseBuffer(); + return str; + } else { + str.ReleaseBuffer(); + return pszPath; + } +#endif + return pszPath; +} + +void CeCosTest::SetExecutable(LPCTSTR pszExecutable) +{ + m_strOutput=_T(""); + m_strResultString=_T(""); + m_strExecutable=pszExecutable; + if(pszExecutable && !GetSizes(m_strExecutable, m_ep.Target(), m_nFileSize, m_nStrippedSize)){ + const TargetInfo &t=Target(Target()); + Log(_T("Failed to run %s-size to determine executable size of %s\n"),t.Prefix(),pszExecutable); + } +} + +// Calculate the sizes of the given file. The target parameter is necessary in order to +// determine which -size executable to use to do the job. +bool CeCosTest::GetSizes(LPCTSTR pszExecutable, LPCTSTR target,unsigned int &nFileSize, unsigned int &nStrippedSize) +{ + bool rc=false; + nStrippedSize=nFileSize=0; + struct _stat buf; + const TargetInfo &t=Target(target); + LPCTSTR pszPrefix=t.Prefix(); + if(0==_tstat(pszExecutable,&buf) && _TCHAR('\0')!=*pszPrefix){ + // File at least exists... + nFileSize=buf.st_size; + String strSize(pszPrefix); + strSize+=_T("-size "); + strSize+=CygPath(pszExecutable); + const TCHAR *c=0; +#ifdef _WIN32 + CSubprocess sp; + String strOut; + if(!sp.Run(strOut,strSize,NULL)){ + return true; + } + c=_tcschr(strOut,_TCHAR('\n')); + if(c){ + c++; + } +#else // UNIX + TCHAR buf[256]; + FILE *f=POPEN(strSize,_T("r")); + if(f){ + _fgetts(buf,sizeof buf-1,f); + _fgetts(buf,sizeof buf-1,f); + PCLOSE(f); + c=buf; + } +#endif + int s1=0; + int s2=0; + rc=(c && 2==_stscanf(c,_T(" %d %d"),&s1,&s2)); + nStrippedSize=s1+s2; + } + return rc; +} + +void CeCosTest::SetTimeouts (Duration dActive,Duration dElapsed) +{ + m_ep.SetActiveTimeout (dActive); + m_ep.SetDownloadTimeout(dElapsed); +} + +void CeCosTest::CloseSocket (){ + delete m_pSock; + m_pSock=0; +} + +bool CeCosTest::AtPrompt(LPCTSTR pszPrompt) +{ + unsigned int nPromptLen=_tcslen(pszPrompt); + return + m_strOutput.GetLength()>=nPromptLen && + 0==_tcscmp((LPCTSTR )m_strOutput+m_strOutput.GetLength()-nPromptLen,pszPrompt); +} + +#ifndef _WIN32 +bool CeCosTest::Suck(LPCTSTR pszPrompt,Duration d) +{ + TRACE(_T("Suck handle=%08x\n"),m_rPipeHandle);//sdf + // Read until: + // 8k read + // timeout elapsed + // gdb prompt reached + // pipe error + Time t0=Now(); + int nLength=0; + while(nLength<8192 && m_rPipeHandle && !AtPrompt(pszPrompt) && Now()-t0<d){ + String str; + int n=ReadPipe(str); + if(n>0){ + LogTimeStampedOutput(str); + nLength+=n; + } else if (n<0) { + break; + } + } + TRACE(_T("End suck\n"));//sdf + return nLength>0; +} +#endif + +void CeCosTest::LogTimeStampedOutput(LPCTSTR psz) +{ + LogString(psz); + /* + String str(psz); + // Timestamp the output at each _TCHAR('\n') + int nLen=m_strOutput.GetLength(); + TCHAR cPrev=(0==nLen?_TCHAR('\0'):((LPCTSTR )m_strOutput)[nLen-1]); + + TCHAR *c=str.GetBuffer(); + LPCTSTR d=c; + while(*c){ + if(_TCHAR('\n')==cPrev){ + TCHAR cSav=*c; + *c=_TCHAR('\0'); + LogString(d); + Duration &dTime=m_bDownloading?m_nDownloadTime:m_nMaxInactiveTime; + dTime=MAX(dTime,GdbTime()-m_tBase); + + String strTime; + strTime.Format(_T("<") WFS _T("/") WFS _T(">\t"),WF(GdbTime()-m_tBase0), WF(GdbTime()-m_tBase)); + //strTime.Format(_T("<%03d.%d> "),t/1000,(t%1000)/100); + LogString(strTime); + *c=cSav; + d=c; + } + cPrev=*c; + c++; + } + LogString(d); + str.ReleaseBuffer(); + */ +} + +#ifdef _WIN32 +BOOL WINAPI HandlerRoutine( + DWORD dwCtrlType // control signal type + ) +{ + dwCtrlType; // eliminate compiler warning + return TRUE; +} +#endif + + +bool CeCosTest::InteractiveGdb(const String &strHost,int nPort,TCHAR **argv) +{ + bool rc=false; + if(strHost.GetLength()>0){ + m_strExecutionHostPort=CeCosTestSocket::HostPort(strHost,nPort); + Log(_T("Waiting to connect to %s...\n"),(LPCTSTR )m_strExecutionHostPort); + } else { + Log(_T("Waiting to connect to a server...\n")); + } + + ConnectForExecution(); + + Log(_T("Connected to %s - waiting for target reset\n"),(LPCTSTR )m_strExecutionHostPort); + String strHostPort,strOutput; + // We read: + // target ready indicator + // any output so far + // (if target ready) host:port + if(GetTargetReady(strHostPort)){ + Log(_T("Use %s\n"),(LPCTSTR)strHostPort); + const TargetInfo &t=Target(Target()); + String strGdb(t.Prefix()); + strGdb+=_T("-gdb"); +#ifdef _WIN32 + SetConsoleCtrlHandler(HandlerRoutine,TRUE); + int n=_tspawnvp(_P_WAIT,strGdb,argv); + if(-1==n){ + Log(_T("Failed to spawn %s\n"),(LPCTSTR)strGdb); + } else { + rc=(0==n); + } + SetConsoleCtrlHandler(HandlerRoutine,FALSE); +#else // UNIX + + int pid=fork(); + switch(pid){ + case -1: + _ftprintf(stderr,_T("fork failed\n")); + pid=0; + break; + case 0: + // Process is created (we're the child) + execvp(strGdb,argv); + Log(_T("Error invoking %s - %s\n"),(LPCTSTR)strGdb,strerror(errno)); + exit(1); + break; + default: + // Process is created (we're the parent) + { + signal(SIGINT,SIG_IGN); + int stat; + waitpid(pid,&stat,0); + rc=(0==stat); + signal(SIGINT,SIG_DFL); + } + break; + } +#endif + Log(_T("Gdb terminated\n")); + // Tell the server we're through + m_pSock->sendInteger(123,_T("Terminating ack")); + } + + return rc; +} + +void CALLBACK CeCosTest::ResetLogFunc(void *pParam, LPCTSTR psz) +{ + CeCosTest *pTest=(CeCosTest *)pParam; + TRACE(_T("Send Target Ready indicator=2\n")); + pTest->m_pSock->sendInteger(2,_T("target ready indicator")); + TRACE(_T("Send %s\n"),psz); + pTest->m_pSock->sendString(psz,_T("output so far")); +} + +CResetAttributes::ResetResult CeCosTest::Reset(bool bSendStatus) +{ + return m_pPort->Reset(bSendStatus?ResetLogFunc:0,this); +} + +CeCosTest::ExecutionParameters::RequestType CeCosTest::ExecutionParameters::RequestTypeValue(LPCTSTR psz) +{ + int r; + for(r=0;r<RequestTypeMax;r++){ + if(0==_tcsicmp(psz,arRequestImage[r])){ + break; + } + } + return (RequestType)r; +} + +#ifdef _WIN32 +void CeCosTest::RunGdb(LPCTSTR pszCmdline,LPCTSTR pszPrompt,const StringArray &arstrGdbCmds) +{ + SetPath(m_strPath); + CSubprocess sp; + + unsigned int idProcess=sp.Run(GetCurrentThreadId(),pszCmdline); + if(!idProcess){ + Log(_T("Failed to create gdb process: cmdline='%s'\n"),pszCmdline); + TCHAR *pszMsg; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + sp.GetExitCode(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR)&pszMsg, + 0, + NULL + ); + + // Display the string. + Log(_T("%s\n"),pszMsg); + + // Free the buffer. + LocalFree(pszMsg); + } else { + + unsigned int nCmdIndex=0; + + SetStatus(NotStarted); + + m_nMaxInactiveTime=0; + m_nTotalTime=0; + m_nDownloadTime=0; + + m_bDownloading=true; + + m_tBase=GdbTime(); + m_tBase0=GdbTime(); + m_tWallClock0=Now(); + + TRACE(_T("RunGDb()\n")); + + int nLastGdbInst=0; + + while(Cancelled!=Status() && CheckForTimeout()){ + MSG msg; + if(::PeekMessage(&msg,NULL,WM_SUBPROCESS,WM_SUBPROCESS+1,PM_NOREMOVE)){ + switch(::GetMessage(&msg,NULL,WM_SUBPROCESS,WM_SUBPROCESS+1)){ + case 0: //WM_QUIT + case -1: // error + goto Done; + case 1: + if(WM_SUBPROCESS==msg.message && msg.wParam==idProcess){ + if(msg.lParam){ + LPTSTR pszMsg=(LPTSTR)msg.lParam; + LogTimeStampedOutput(pszMsg); + + if(m_strOutput.GetLength()>20000){ + LogString(_T("\n>>>> Infra FAIL\n*** too much output ***\n>>>>\n")); + SetStatus(Fail); + goto Done; + } + + // Test for program loaded and started: + // (remember SetStatus cannot downgrade the status if already > NoResult) + if(OutputContains(_T("Starting program: /")) || (OutputContains(_T("Start address"))&&OutputContains(_T("Continuing.")))){ + SetStatus(NoResult); + } + + m_tBase=GdbTime(); + // // If can only hit a single breakpoint don't expect cyg_test_exit to stop us: + // if(!BreakpointsOperational() && (OutputContains(_T("EXIT:"))||OutputContains(_T("NOTAPPLICABLE:")))){ + // goto Done; + // } + + if(AtPrompt(pszPrompt)){ + m_tBase=GdbTime(); + TRACE(_T("RunGDb(1)\n")); + // gdb's output included one or more prompts + // Send another command along + if(nCmdIndex>=arstrGdbCmds.size()){ + // Nothing further to say to gdb - exit + TRACE(_T("RunGDb(2)\n")); + goto Done; // case 3 + } else { + String strCmd(arstrGdbCmds[nCmdIndex++]); + TRACE(_T("RunGDb(2a) - strCmd='%s' nLastGdbInst=%d\n"),(LPCTSTR)strCmd,nLastGdbInst); + // If at a prompt and we can see a GDB instruction, send it down + LPCTSTR pszGdbcmd=_tcsstr(nLastGdbInst+(LPCTSTR)m_strOutput,_T("GDB:")); + if(pszGdbcmd){ + TRACE(_T("RunGDb(2b)\n")); + pszGdbcmd+=4; + TCHAR cTerm; + if(_TCHAR('<')==*pszGdbcmd){ + cTerm=_TCHAR('>'); + pszGdbcmd++; + } else { + cTerm=_TCHAR('\n'); + } + TRACE(_T("RunGDb(2c)\n")); + LPCTSTR c=_tcschr(pszGdbcmd,cTerm); + if(c){ + TRACE(_T("RunGDb(2d)\n")); + strCmd=String(pszGdbcmd,c-pszGdbcmd); + nLastGdbInst=c+1-(LPCTSTR )m_strOutput; + nCmdIndex--; // undo increment above + } + } + strCmd+=_TCHAR('\n'); + LogString(strCmd); + TRACE(_T("RunGDb(3)\n")); + sp.Send(strCmd); + TRACE(_T("RunGDb(4)\n")); + if(0==_tcscmp(strCmd,_T("run\n"))||0==_tcscmp(strCmd,_T("cont\n"))){ + m_tBase=GdbTime(); + m_bDownloading=false; + } + TRACE(_T("RunGDb(5)\n")); + } + } + delete [] pszMsg; + } else { + goto Done; + } + } + break; + } + } else { // no message + Sleep (500l); // sleep 500ms before the next peek + } + } + +Done: + // Could use CygKill here + sp.Kill(); + TRACE(_T("RunGDb - Done\n")); + AnalyzeOutput(); + + m_nTotalTime=Duration(Now()-m_tWallClock0); + } + TRACE(_T("Exiting RunGdb()\n")); +} + +bool CeCosTest::GdbProcessAlive() +{ + DWORD dwExitRc; + GetExitCodeProcess((HANDLE)m_pGdbProcesshandle,&dwExitRc); + return STILL_ACTIVE==dwExitRc; +} + +Time CeCosTest::GdbCpuTime() +{ + HANDLE hProcess=(HANDLE)m_pGdbProcesshandle; + __int64 ftCreation,ftExit,ftKernel,ftUser; + if(NULL!=hProcess && GetProcessTimes (hProcess,(FILETIME *)&ftCreation,(FILETIME *)&ftExit,(FILETIME *)&ftKernel,(FILETIME *)&ftUser)){ + return Time((int)((ftKernel+ftUser)/10000)); + } else { + return 0; + } +} + +void CeCosTest::GetPath(String &strPath) +{ + int nSize=GetEnvironmentVariable(_T("PATH"), NULL, 0); + if(nSize>0){ + GetEnvironmentVariable(_T("PATH"), strPath.GetBuffer(nSize), nSize); + strPath.ReleaseBuffer(); + } else { + strPath=_T(""); + } +} + +void CeCosTest::SetPath(const String &strPath) +{ + SetEnvironmentVariable(_T("PATH"), strPath); +} + +#else // UNIX + +void CeCosTest::RunGdb(LPCTSTR pszCmdline,LPCTSTR pszPrompt,const StringArray &arstrGdbCmds) +{ + int pipe_ends_w[2]; + if (pipe(pipe_ends_w) < 0 ) { + Log(_T("Failed to create pipe_ends_w - %s\n"),strerror(errno)); + } else { + int pipe_ends_r[2]; + if (pipe(pipe_ends_r) < 0 ) { + Log(_T("Failed to create pipe_ends_r - %s\n"),strerror(errno)); + } else { + int new_pid; + ENTERCRITICAL; + // Ensure no one else has the lock such that the child might block in future + new_pid = fork(); + // This leave is executed in *both* the child and parent + LEAVECRITICAL; + + switch (new_pid) { + // Fork failed + case -1: + Log(_T("Failed to create gdb process - %s\n"),strerror(errno)); + break; + case 0: + // Process is created (we're the child) + // No point in calling Log in this process + + // Input to child process + if (dup2(pipe_ends_w[0], 0) < 0) { + TRACE(_T("dup2 error\n")); + exit(1); + } + + // Output from process + if (dup2(pipe_ends_r[1], 2) < 0) { + TRACE(_T("dup2 error\n")); + exit(2); + } + if (dup2(pipe_ends_r[1], 1) < 0) { + TRACE(_T("dup2 error\n")); + exit(3); + } + setvbuf(stdout,0,_IONBF,0); + setvbuf(stderr,0,_IONBF,0); + { + StringArray ar; + int argc=String(pszCmdline).Chop(ar,_TCHAR(' '),true); + char **argv=new char *[1+argc]; + int i; + for(i=0;i<argc;i++){ + argv[i]=ar[i]; + } + argv[i]=0; + execvp(argv[0], argv); + delete [] argv; + } + TRACE(_T("exec error - %s\n"),strerror(errno)); + exit(4); + default: + // Process is created (we're the parent) + + TRACE(_T("Forked to create gdb process %s - pid is <%d>\n"), pszCmdline, new_pid); + if (fcntl(pipe_ends_r[0], F_SETFL, O_NONBLOCK) <0) { + Log(_T("Couldn't set pipe non-blocking - %s\n"),strerror(errno)); + } else { + m_pGdbProcesshandle=(void *)new_pid; + VTRACE(_T("RunGdb():Calling DriveGdb\n")); + m_rPipeHandle=(void *)pipe_ends_r[0]; + m_wPipeHandle=(void *)pipe_ends_w[1]; + DriveGdb (pszPrompt,arstrGdbCmds); + // Finished one way or another. Kill gdb now + TRACE(_T("Finished processing this test.\n")); + if(GdbProcessAlive()){ + TRACE(_T("Killing gdb\n")); + + // We need to kill gdb *and* its children + FILE *f=popen(_T("ps -l"),_T("r")); + if(f){ + TCHAR buf[100]; + while(_fgetts(buf,sizeof(buf)-1,f)){ + int F,UID,PID,PPID,C,PRI,NI,SZ,HH,MM,SS; + TCHAR discard[100]; + // Output is in the form + // F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD + //100 S 490 877 876 0 70 0 - 368 wait4 pts/0 00:00:00 bash + if(15==_stscanf(buf, + _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) && + (PID==new_pid || PPID==new_pid)){ + kill(PID,SIGTERM); + } + } + pclose(f); + TRACE(_T("waitpid <%d>"), new_pid); + int i; + for(i=0;i<10;i++){ + int status; + switch(waitpid(new_pid,&status,WNOHANG)){ + case 0: + Sleep(1000); + continue; + case -1: + Log(_T("Failed to wait for gdb process to die\n")); + SetStatus(TimeOut); + break; + default: + break; + } + break; + } + if(10==i){ + Log(_T("Failed to wait for gdb process to die\n")); + SetStatus(TimeOut); + } else { + TRACE(_T("Killed gdb\n")); + } + } else { + Log(_T("Failed to run ps to kill gdb process %d\n"),new_pid); + } + } + TRACE(_T("Total elapsed time is %d\n"), m_nTotalTime); + } + break; + } + VTRACE(_T("Closing pipe_ends_r[]\n")); + close (pipe_ends_r[0]); + close (pipe_ends_r[1]); + } + close (pipe_ends_w[0]); + close (pipe_ends_w[1]); + } +} + +bool CeCosTest::GdbProcessAlive() +{ + int status; + return 0==waitpid((int)m_pGdbProcesshandle,&status,WNOHANG); +} + +Time CeCosTest::GdbCpuTime() +{ + if(GdbProcessAlive()){ + Time now=Now(); + if(now-m_tPrevSample>1000){ + + m_tPrevSample=now; + // Output is in the form + // F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD + //100 S 490 877 876 0 70 0 - 368 wait4 pts/0 00:00:00 bash + FILE *f=popen(_T("ps -l"),_T("r")); + if(f){ + TCHAR buf[100]; + int t=0; + while(_fgetts(buf,sizeof(buf)-1,f)){ + int F,UID,PID,PPID,C,PRI,NI,SZ,HH,MM,SS; + TCHAR discard[100]; +TRACE(_T("ps -l : %s\n"),buf); + if(15==_stscanf(buf, + _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) && + (PID==(int)m_pGdbProcesshandle || PPID==(int)m_pGdbProcesshandle)){ + t+=SS+60*(60*HH+MM); + } + } + pclose(f); +TRACE(_T("---> t=%d [secs]\n"),t); + m_tGdbCpuTime=Time(1000*t); + } + } + } else { + m_tGdbCpuTime=0; + } +VTRACE(_T("GdbCpuTime rc=%d"),m_tGdbCpuTime/1000); + return m_tGdbCpuTime; +} + +bool CeCosTest::WritePipe (const String &str) +{ + const char *pszBuf=(const char *)str; + int write_fd = (int)m_wPipeHandle; + int dwWritten; + int nToWrite=_tcslen(pszBuf); + do { + dwWritten = write(write_fd, pszBuf, nToWrite); + if(-1==dwWritten){ + Log(_T("pipe write error - %s\n"),strerror(errno)); + return false; + } + nToWrite-=(int)dwWritten; + pszBuf+=(int)dwWritten; + if(!CheckForTimeout()){ + return false; + } + } while (nToWrite>0); + return true; +} + +int CeCosTest::ReadPipe (String &str,bool bBlocking /* This param ignored */) +{ + TCHAR buf[4096]; + int rc=read((int)m_rPipeHandle, buf, sizeof(buf)-1); + if(-1==rc && EAGAIN==errno){ + rc=0; + } + buf[MAX(0,rc)]=_TCHAR('\0'); + str=buf; + return rc; +} + +void CeCosTest::GetPath(String &strPath) +{ + strPath=getenv(_T("PATH")); +} + + +void CeCosTest::SetPath(const String &strPath) +{ + String str; + str.Format(_T("PATH=%s"),(LPCTSTR )strPath); + putenv(str); +} +#endif + +void CeCosTest::AnalyzeOutput() +{ + // This test is pulled out to allow ser_filter to simulate a test failure + if(OutputContains(_T("FAIL:"))){ + SetStatus(Fail); + } + + if(OutputContains(_T("EXIT:"))||OutputContains(_T("NOTAPPLICABLE:"))){ + static LPCTSTR arpszKeepAlive[]={_T("FAIL:"),_T("NOTAPPLICABLE:"), _T("PASS:")}; + static const StatusType arStatus[] ={Fail, Inapplicable, Pass}; + for(unsigned int i=0;i<sizeof arpszKeepAlive/sizeof arpszKeepAlive[0];i++){ + if(OutputContains(arpszKeepAlive[i])){ + TRACE(_T("DriveGdb: saw '%s'\n"),arpszKeepAlive[i]); + SetStatus(arStatus[i]); // Do not break! + } + } + } + + // Certain output spells failure... + if(OutputContains(_T("cyg_assert_fail ("))){ + SetStatus(AssertFail); + } else { + static LPCTSTR arpszSignals[]={_T("SIGBUS"), _T("SIGSEGV"), _T("SIGILL"), _T("SIGFPE"), _T("SIGSYS"), _T("SIGTRAP")}; + for(unsigned int i=0;i<sizeof arpszSignals/sizeof arpszSignals[0];i++){ + String str1,str2; + str1.Format(_T("signal %s"),arpszSignals[i]); + str2.Format(_T("handle %s nostop"),arpszSignals[i]); + if(OutputContains(str1)&&!OutputContains(str2)){ + SetStatus(Fail); + break; + } + } + } + + // Check for expect: strings + static LPCTSTR szExpect=_T("EXPECT:"); + static const int nExpectLen=_tcslen(szExpect); + for(const TCHAR*c=_tcsstr(m_strOutput,szExpect);c;c=_tcsstr(c,szExpect)){ + c+=nExpectLen; + if(_TCHAR('<')==*c){ + c++; + // Find the matching _TCHAR('>') + for(LPCTSTR d=_tcschr(c,_TCHAR('>'));d;d=_tcschr(d+1,_TCHAR('>'))){ + if(d[-1]!=_TCHAR('\\')){ + + /* + // Skip whitespace immediately following the EXPECT:<...> + do { + d++; + } while (_istspace(*d)); + */ + + /* + // Skip timestamp + if(_TCHAR('<')==*d){ + d=_tcschr(d,_TCHAR('>')); + if(0==d){ + continue; + } + do { + d++; + } while (_istspace(*d)); + } + */ + + // Now d points to the terminating _TCHAR('>') and c at the start of the expected string + for(LPCTSTR e=d+1;c!=d;c++,e++){ + if(_TCHAR('\\')==*c){ + c++; + } + if(*c!=*e){ + LogString(_T("EXPECT:<> failure\n")); + SetStatus(Fail); + break; + } + } + break; + } + } + } + } +} + +bool CeCosTest::ExecutionParameters::FromStr(LPCTSTR psz) +{ + String str1,str2,str3,str4,str5; + int nUseFilter,nUnused2,nUnused3; + int nLen=_tcslen(psz); + _stscanf(psz,_T("%s %s %d %d %d %d %d %d %d %d %s %s %s"), + str1.GetBuffer(1+nLen), + str2.GetBuffer(1+nLen), + &m_nActiveTimeout, + &m_nDownloadTimeout, + &m_nUnused1, + &m_nUnused2, + &m_nUnused3, + &nUseFilter, + &nUnused2, + &nUnused3, + str3.GetBuffer(1+nLen), + str4.GetBuffer(1+nLen), + str5.GetBuffer(1+nLen) + ); + m_bUseFilter=(0!=nUseFilter); + m_bUnused2=(0!=nUnused2); + m_bUnused3=(0!=nUnused3); + str1.ReleaseBuffer(); + str2.ReleaseBuffer(); + str3.ReleaseBuffer(); + str4.ReleaseBuffer(); + str5.ReleaseBuffer(); + m_Target=str1; + int r; + for(r=0;r<RequestTypeMax;r++){ + if(0==_tcscmp(arRequestImage[r],str2)){ + break; + } + } + m_Request=(RequestType)r; + return CeCosTest::IsValid(m_Target); +} + +CeCosTest::ExecutionParameters::ExecutionParameters (RequestType r, + LPCTSTR Target, + Duration nActiveTimeout/*=NOTIMEOUT*/, + Duration nDownloadTimeout/*=NOTIMEOUT*/): + m_bUseFilter(true), + m_Target(Target), + m_nActiveTimeout(nActiveTimeout), + m_nDownloadTimeout(nDownloadTimeout), + m_Request(r), + m_nUnused1(0), + m_nUnused2(0), + m_nUnused3(0), + m_bUnused2(false), + m_bUnused3(false) +{ +} + +String CeCosTest::ExecutionParameters::Image() const +{ + String str; + str.Format(_T("%s %s %d %d %d %d %d %d %d %d"),Target(),Image(Request()),ActiveTimeout(),DownloadTimeout(), + m_nUnused1, + m_nUnused2, + m_nUnused3, + m_bUseFilter, + m_bUnused2, + m_bUnused3); + return str; +} + +CeCosTest::TargetInfo::TargetInfo() +{ +}; + +LPCTSTR CeCosTest::TargetInfo::arHwTypeImage[]={_T("HARDWARE"), _T("SIM"), _T("SYNTHETIC"), _T("HARDWARE_NO_BP"), _T("REMOTE_SIM") +}; + +CeCosTest::TargetInfo::TargetInfo(LPCTSTR pszIm,LPCTSTR pszPre,int nHwtype,LPCTSTR pszGdb/*=_T("")*/): + pszImage(pszIm), + pszPrefix(pszPre), + nType((HwType)nHwtype), + pszGdbcmd(pszGdb) +{ +} + +CeCosTest::TargetInfo::TargetInfo(LPCTSTR pszIm,LPCTSTR pszPre,LPCTSTR pszHwtype,LPCTSTR pszGdb/*=_T("")*/): + pszImage(pszIm), + pszPrefix(pszPre), + nType(INVALID), + pszGdbcmd(pszGdb) +{ + if(_istdigit(pszHwtype[0])){ + nType=(HwType)_ttoi(pszHwtype); + if((unsigned)nType>sizeof(arHwTypeImage)){ + nType=INVALID; + } + } else { + for(unsigned int i=0;i<sizeof arHwTypeImage/sizeof arHwTypeImage[0];i++){ + if(0==_tcsicmp(pszHwtype,arHwTypeImage[i])){ + nType=(HwType)i; + break; + } + } + } +} + +int CeCosTest::AddPlatform(const CeCosTest::TargetInfo &t) +{ + arTargetInfo.push_back(t); + return arTargetInfo.size()-1; +} + +bool CeCosTest::GetTargetReady(String &strHostPort) +{ + bool rc=false; + int nTargetReady; + do{ + if(!m_pSock->recvInteger(nTargetReady,_T("Target ready"),120*1000)){ + Log(_T("Failed to read target ready indicator from server - %s\n"),(LPCTSTR )m_pSock->SocketErrString()); + break; + } + switch(nTargetReady){ + case 0: + LogString(_T("Failed to reset target")); + break; + case 1: + if(m_pSock->recvString(strHostPort, _T("host:port"))){ + TRACE(_T("Instructed to use %s\n"),(LPCTSTR )strHostPort); + rc=true; + } else { + Log(_T("Failed to read host:port - %s\n"),(LPCTSTR )m_pSock->SocketErrString()); + } + break; + case 2: + { + String strOutput; + if(m_pSock->recvString(strOutput, _T("output"))){ + LogString(strOutput); + } else { + Log(_T("Failed to read output\n"),(LPCTSTR )m_pSock->SocketErrString()); + return false; + } + } + break; + } + } while(2==nTargetReady); + return rc; +} + +#ifdef _WIN32 +String CeCosTest::GetGreatestSubkey (LPCTSTR pszKey) +{ + String strSubkey = _T(""); + HKEY hKey; + + if (ERROR_SUCCESS == RegOpenKeyEx (HKEY_LOCAL_MACHINE, pszKey, 0L, KEY_READ, &hKey)) + { + DWORD dwIndex = 0; + TCHAR pszBuffer [MAX_PATH + 1]; + + while (ERROR_SUCCESS == RegEnumKey (hKey, dwIndex++, (LPTSTR) pszBuffer, sizeof (pszBuffer))) + { + if (strSubkey.compare (pszBuffer) < 0) + strSubkey = pszBuffer; + } + + RegCloseKey (hKey); + } + + TRACE (_T("CeCosTest::GetGreatestSubkey(\"%s\"): %s\n"), pszKey, (LPCTSTR)strSubkey); + return strSubkey; +} +#endif + + +void CeCosTest::RemoveAllPlatforms() +{ + for(int i=TargetTypeMax()-1;i>=0;--i){ + delete &Target(i); + } + arTargetInfo.clear(); +} + +CeCosTest::ServerStatus CeCosTest::ServerStatusValue(LPCTSTR psz) +{ + int s; + for(s=0;s<ServerStatusMax;s++){ + if(0==_tcsicmp(psz,arServerStatusImage[s])){ + break; + } + } + return (ServerStatus)s; + +} + +const CeCosTest::TargetInfo &CeCosTest::Target (LPCTSTR psz) +{ + for(int i=0;i<(signed)arTargetInfo.size();i++){ + const TargetInfo &t=arTargetInfo[i]; + if(0==_tcsicmp(t.Image(),psz)){ + return t; + } + } + return tDefault; +}
