dsi_debug.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830
  1. /*
  2. This software is subject to the license described in the License.txt file
  3. included with this software distribution. You may not use this file except
  4. in compliance with this license.
  5. Copyright (c) Dynastream Innovations Inc. 2016
  6. All rights reserved.
  7. */
  8. #include "dsi_debug.hpp"
  9. #if defined(DEBUG_FILE)
  10. #include "types.h"
  11. #include "dsi_thread.h"
  12. #include "macros.h"
  13. #include "defines.h"
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <stdarg.h>
  17. #define NEW_SESSION_MESG "New Session.\n"
  18. #define OVERFLOW_ERROR "\n*** ERROR: BUFFER OVERFLOW! ***\n"
  19. #define OVERFLOW_ERROR_LENGTH 33
  20. #define TRUNCATE_ERROR " *** TRUNCATED! ***"
  21. #define WRITE_ERROR "\n*** WRITE ERROR ***\n"
  22. #define MAX_NAME_LENGTH 255
  23. #define MAX_PORTS ((UCHAR)255)
  24. #define MAX_THREADS ((UCHAR)10)
  25. #define THREAD_EXIT_TIMEOUT ((ULONG)30)
  26. #define MUTEX_UNLOCK_DELAY ((UCHAR)10) //milliseconds to wait for other threads to unlock mutexs
  27. //Buffer class defines//
  28. #define BUFFER_SIZE_NO_CAST (0xFFFF) //Must be >= DSI_DEBUG_MAX_STRLEN && < MAX_ULONG - OVERFLOW_ERROR_LENGTH - 1
  29. #define BUFFER_SIZE ((ULONG)BUFFER_SIZE_NO_CAST)
  30. #if BUFFER_SIZE_NO_CAST == 0
  31. #error
  32. #endif
  33. #define ACTUAL_BUFFER_SIZE ((ULONG)(BUFFER_SIZE + OVERFLOW_ERROR_LENGTH + 1))
  34. //Buffer size including room for the error message and plus one so we know the buffer is empty if the pointers equal each other
  35. #define FLUSH_PERCENT ((UCHAR)50)
  36. #define FLUSH_CHECK_PERIOD ((ULONG)5000)
  37. BOOL DSIDebug::bInitialized = FALSE;
  38. //////////////////////////////////////////////////////////
  39. // Buffer Class Declaration
  40. //////////////////////////////////////////////////////////
  41. class Buffer
  42. {
  43. public:
  44. Buffer(UCHAR* pucFilename_, UCHAR* pucDirectory_);
  45. ~Buffer();
  46. BOOL Add(UCHAR* pucString_, ULONG ulSize_);
  47. void SetEnable(BOOL bEnable_);
  48. BOOL SetDirectory(const UCHAR* pucDirectory_);
  49. private:
  50. BOOL bEnable;
  51. BOOL Flush();
  52. ULONG GetBufferCount();
  53. UCHAR GetPercentFull();
  54. void WriteThread();
  55. static DSI_THREAD_RETURN StartWriteThread(void* pvParam_);
  56. //Helper functions
  57. void SignalFlush();
  58. ULONG ulInput;
  59. ULONG ulOutput;
  60. DSI_MUTEX stInputMutex;
  61. DSI_MUTEX stFilenameMutex;
  62. DSI_MUTEX stFlushMutex;
  63. DSI_CONDITION_VAR stFlushCond;
  64. UCHAR aucFilename[MAX_NAME_LENGTH];
  65. UCHAR aucFullPath[MAX_NAME_LENGTH];
  66. UCHAR aucData[ACTUAL_BUFFER_SIZE];
  67. //Thread variables
  68. BOOL bWriteThreadExit;
  69. DSI_THREAD_ID hWriteThreadID;
  70. DSI_MUTEX stWriteThreadMutex;
  71. DSI_CONDITION_VAR stWriteThreadCond;
  72. BOOL bBufferOverflow;
  73. volatile BOOL bForceFlush;
  74. };
  75. //////////////////////////////////////////////////////////
  76. // Private Declarations
  77. //////////////////////////////////////////////////////////
  78. typedef struct _TASK_PROP
  79. {
  80. _TASK_PROP(DSI_THREAD_IDNUM hThreadIDNum_, UCHAR* pucFilename_, UCHAR* pucDirectory_)
  81. {
  82. hThreadIDNum = hThreadIDNum_;
  83. pclBuffer = new Buffer(pucFilename_, pucDirectory_);
  84. }
  85. ~_TASK_PROP()
  86. {
  87. if(pclBuffer)
  88. delete pclBuffer;
  89. }
  90. DSI_THREAD_IDNUM hThreadIDNum;
  91. Buffer* pclBuffer;
  92. } THREAD_PROP;
  93. //Private Function Declarations
  94. BOOL FindThreadNum(UCHAR* pucNum_);
  95. //Private Variables
  96. ULONG ulStartTime;
  97. BOOL bWriteEnable;
  98. Buffer* apclSerialBuffer[MAX_PORTS];
  99. THREAD_PROP* apstThread[MAX_THREADS];
  100. UCHAR aucLogDirectory[MAX_NAME_LENGTH];
  101. UCHAR aucExecutablePath[MAX_NAME_LENGTH];
  102. DSI_MUTEX stThreadBufferMutex;
  103. DSI_MUTEX stSerialBufferMutex;
  104. //////////////////////////////////////////////////////////
  105. //////////////////////////////////////////////////////////
  106. /////////////// DSIDebug Class ///////////////
  107. //////////////////////////////////////////////////////////
  108. //////////////////////////////////////////////////////////
  109. //////////////////////////////////////////////////////////
  110. // Public Definitions
  111. //////////////////////////////////////////////////////////
  112. //Warning: Not thread safe!
  113. BOOL DSIDebug::Init()
  114. {
  115. if(bInitialized)
  116. return TRUE;
  117. ulStartTime = DSIThread_GetSystemTime();
  118. bWriteEnable = FALSE;
  119. //Get the Directory of the executable
  120. DSIThread_GetWorkingDirectory(aucExecutablePath, MAX_NAME_LENGTH);
  121. STRNCPY((char*)aucLogDirectory, (char*)aucExecutablePath, MAX_NAME_LENGTH);
  122. for(UCHAR i=0; i<MAX_PORTS; i++)
  123. apclSerialBuffer[i] = (Buffer*)NULL;
  124. for(UCHAR i=0; i<MAX_THREADS; i++)
  125. apstThread[i] = (THREAD_PROP*)NULL;
  126. DSIThread_MutexInit(&stThreadBufferMutex);
  127. DSIThread_MutexInit(&stSerialBufferMutex);
  128. bInitialized = TRUE;
  129. return TRUE;
  130. }
  131. //Warning: Not thread safe!
  132. void DSIDebug::Close()
  133. {
  134. if(!bInitialized)
  135. return;
  136. bWriteEnable = FALSE;
  137. //Clean up all the buffers
  138. DSIThread_MutexLock(&stSerialBufferMutex);
  139. for(UCHAR i=0; i<MAX_PORTS; i++)
  140. {
  141. if(apclSerialBuffer[i] != NULL)
  142. {
  143. delete apclSerialBuffer[i];
  144. apclSerialBuffer[i] = (Buffer*)NULL;
  145. }
  146. }
  147. DSIThread_MutexUnlock(&stSerialBufferMutex);
  148. DSIThread_MutexLock(&stThreadBufferMutex);
  149. for(UCHAR i=0; i<MAX_THREADS; i++)
  150. {
  151. if(apstThread[i] != NULL)
  152. {
  153. delete apstThread[i];
  154. apstThread[i] = (THREAD_PROP*)NULL;
  155. }
  156. }
  157. DSIThread_MutexUnlock(&stThreadBufferMutex);
  158. DSIThread_Sleep(MUTEX_UNLOCK_DELAY); //Wait for any functions that have the mutex to unlock them.
  159. DSIThread_MutexDestroy(&stThreadBufferMutex);
  160. DSIThread_MutexDestroy(&stSerialBufferMutex);
  161. bInitialized = FALSE;
  162. return;
  163. }
  164. BOOL DSIDebug::ResetTime()
  165. {
  166. if(!bInitialized)
  167. return FALSE;
  168. ulStartTime = DSIThread_GetSystemTime();
  169. if(!bWriteEnable)
  170. return FALSE;
  171. for(UCHAR i=0; i<MAX_THREADS; i++)
  172. {
  173. if(apstThread[i] != NULL)
  174. apstThread[i]->pclBuffer->Add((UCHAR*)NEW_SESSION_MESG, sizeof(NEW_SESSION_MESG)-1);
  175. }
  176. for(UCHAR i=0; i<MAX_PORTS; i++)
  177. {
  178. if(apclSerialBuffer[i] != NULL)
  179. apclSerialBuffer[i]->Add((UCHAR*)NEW_SESSION_MESG, sizeof(NEW_SESSION_MESG)-1);
  180. }
  181. return TRUE;
  182. }
  183. BOOL DSIDebug::SetDirectory(const char* pcDirectory_)
  184. {
  185. if(!bInitialized || pcDirectory_ == NULL)
  186. return FALSE;
  187. if(strcmp(pcDirectory_, "") == 0)
  188. STRNCPY((char*)aucLogDirectory, (char*)aucExecutablePath, MAX_NAME_LENGTH);
  189. else
  190. SNPRINTF((char*)aucLogDirectory, MAX_NAME_LENGTH, "%s", pcDirectory_);
  191. for(UCHAR i=0; i<MAX_THREADS; i++)
  192. {
  193. if(apstThread[i] != NULL)
  194. apstThread[i]->pclBuffer->SetDirectory(aucLogDirectory);
  195. }
  196. for(UCHAR i=0; i<MAX_PORTS; i++)
  197. {
  198. if(apclSerialBuffer[i] != NULL)
  199. apclSerialBuffer[i]->SetDirectory(aucLogDirectory);
  200. }
  201. return TRUE;
  202. }
  203. BOOL DSIDebug::ThreadInit(const char* pucName_)
  204. {
  205. if(!bInitialized || pucName_ == NULL || strlen(pucName_) > MAX_NAME_LENGTH)
  206. return FALSE;
  207. DSIThread_MutexLock(&stThreadBufferMutex);
  208. UCHAR ucThreadNum;
  209. if(!FindThreadNum(&ucThreadNum))
  210. {
  211. //Buffer is full
  212. DSIThread_MutexUnlock(&stThreadBufferMutex);
  213. return FALSE;
  214. }
  215. if(apstThread[ucThreadNum] != NULL)
  216. {
  217. //we have already set the thread
  218. DSIThread_MutexUnlock(&stThreadBufferMutex);
  219. return FALSE;
  220. }
  221. UCHAR aucString[MAX_NAME_LENGTH+20];
  222. if(pucName_ != NULL)
  223. #if defined (DSI_TYPES_MACINTOSH)
  224. SNPRINTF((char*)aucString, MAX_NAME_LENGTH+20, "ao_debug_%s.log", pucName_);
  225. #else
  226. SNPRINTF((char*)aucString, MAX_NAME_LENGTH+20, "ao_debug_%s.txt", pucName_);
  227. #endif
  228. else
  229. #if defined (DSI_TYPES_MACINTOSH)
  230. SNPRINTF((char*)aucString, MAX_NAME_LENGTH+20, "ao_debug_Thread%u.log", DSIThread_GetCurrentThreadIDNum());
  231. #else
  232. SNPRINTF((char*)aucString, MAX_NAME_LENGTH+20, "ao_debug_Thread%u.txt", DSIThread_GetCurrentThreadIDNum());
  233. #endif
  234. apstThread[ucThreadNum] = new THREAD_PROP(DSIThread_GetCurrentThreadIDNum(), aucString, aucLogDirectory);
  235. DSIThread_MutexUnlock(&stThreadBufferMutex);
  236. if(apstThread[ucThreadNum] == NULL)
  237. return FALSE;
  238. return TRUE;
  239. }
  240. BOOL DSIDebug::ThreadWrite(const char* pcMessage_)
  241. {
  242. if(!bInitialized || pcMessage_ == NULL)
  243. return FALSE;
  244. if(!bWriteEnable)
  245. return FALSE;
  246. //Find the index of the proper thread property struct
  247. UCHAR ucThreadNum;
  248. if(!FindThreadNum(&ucThreadNum))
  249. return FALSE;
  250. if(apstThread[ucThreadNum] == NULL)
  251. return FALSE;
  252. char acString[DSI_DEBUG_MAX_STRLEN];
  253. ULONG ulCurrentTime = DSIThread_GetSystemTime();
  254. SNPRINTF(acString, DSI_DEBUG_MAX_STRLEN, "%10.3f {%10lu}: %s\n", (ulCurrentTime-ulStartTime)/1000.0, ulCurrentTime, pcMessage_);
  255. ULONG totalLen = strlen(acString);
  256. //If we are too long, than overwrite the truncate error to the end
  257. if(totalLen >= DSI_DEBUG_MAX_STRLEN-1)
  258. SNPRINTF(acString + DSI_DEBUG_MAX_STRLEN - 2 - strlen(TRUNCATE_ERROR), strlen(TRUNCATE_ERROR)+2, "%s\n", TRUNCATE_ERROR);
  259. return apstThread[ucThreadNum]->pclBuffer->Add((UCHAR*)acString, totalLen);
  260. }
  261. BOOL DSIDebug::ThreadPrintf(const char* pcMessage_, ...)
  262. {
  263. char acString[DSI_DEBUG_MAX_STRLEN + 1]; //+1 so truncation error will be detected by ThreadWrite
  264. va_list args;
  265. va_start(args, pcMessage_);
  266. VSNPRINTF(acString, DSI_DEBUG_MAX_STRLEN, pcMessage_, args);
  267. va_end(args);
  268. return ThreadWrite(acString);
  269. }
  270. BOOL DSIDebug::ThreadEnable(BOOL bEnable_)
  271. {
  272. if(!bInitialized)
  273. return FALSE;
  274. //Find the index of the proper thread property struct
  275. UCHAR ucThreadNum;
  276. if(!FindThreadNum(&ucThreadNum))
  277. return FALSE;
  278. if(apstThread[ucThreadNum] == NULL)
  279. return FALSE;
  280. apstThread[ucThreadNum]->pclBuffer->SetEnable(bEnable_);
  281. return TRUE;
  282. }
  283. BOOL DSIDebug::SerialWrite(UCHAR ucPortNum_, const char* pcHeader_, UCHAR* pucData_, USHORT usSize_)
  284. {
  285. if(!bInitialized)
  286. return FALSE;
  287. //pcHeader_ == NULL and pucData_ == NULL taken care of below.
  288. if(ucPortNum_ >= MAX_PORTS) // || usSize_ < 0)
  289. return FALSE;
  290. if(!bWriteEnable)
  291. return FALSE;
  292. //Check if the serial buffer has been created yet
  293. if(apclSerialBuffer[ucPortNum_] == NULL) //is just here so you don't lock the mutex every time you write.
  294. {
  295. DSIThread_MutexLock(&stSerialBufferMutex); //Note: remember this mutex is shared among every thread that writes to any serial buffer!
  296. if(apclSerialBuffer[ucPortNum_] == NULL && bWriteEnable)
  297. {
  298. UCHAR aucString[MAX_NAME_LENGTH];
  299. #if defined (DSI_TYPES_MACINTOSH)
  300. SNPRINTF((char*)aucString, MAX_NAME_LENGTH, "Device%u.log", ucPortNum_);
  301. #else
  302. SNPRINTF((char*)aucString, MAX_NAME_LENGTH, "Device%u.txt", ucPortNum_);
  303. #endif
  304. apclSerialBuffer[ucPortNum_] = new Buffer(aucString, aucLogDirectory);
  305. }
  306. DSIThread_MutexUnlock(&stSerialBufferMutex);
  307. }
  308. if(apclSerialBuffer[ucPortNum_] == NULL || !bWriteEnable)
  309. return FALSE;
  310. //Get the current time
  311. ULONG ulCurrentTime = DSIThread_GetSystemTime();
  312. //Compose the string
  313. char acString[DSI_DEBUG_MAX_STRLEN];
  314. SNPRINTF(acString, DSI_DEBUG_MAX_STRLEN, "%10.3f {%10lu} %s - %s", (ulCurrentTime-ulStartTime)/1000.0, ulCurrentTime, pcHeader_ != NULL ? (char*)pcHeader_ : "NULL", usSize_ == 0 ? "NO DATA\n" : "");
  315. ULONG ulStringLength = strlen(acString);
  316. if(usSize_ != 0 && ulStringLength < (DSI_DEBUG_MAX_STRLEN - 6)) //6 is room to display at least one byte
  317. {
  318. //Write all the bytes we can and put '\n' on the last one
  319. char* currentPos = acString + ulStringLength;
  320. USHORT usMaxDataCount = (USHORT)MIN(usSize_, (DSI_DEBUG_MAX_STRLEN-2-ulStringLength)/4); //2 is room for the closing "\n\0"
  321. for(USHORT i=0; i < usMaxDataCount-1; ++i)
  322. {
  323. SNPRINTF(currentPos, 5, "[%02X]", pucData_[i]);
  324. currentPos += 4;
  325. }
  326. SNPRINTF(currentPos, 6, "[%02X]\n", pucData_[usMaxDataCount-1]);
  327. //Update our string length
  328. ulStringLength += ((ULONG)usMaxDataCount*4) + 1; //4*bytes + '\n'
  329. }
  330. //If we are too long, than overwrite the truncate error to the end
  331. if(ulStringLength >= DSI_DEBUG_MAX_STRLEN-1)
  332. SNPRINTF(acString + DSI_DEBUG_MAX_STRLEN - 2 - strlen(TRUNCATE_ERROR), strlen(TRUNCATE_ERROR)+2, "%s\n", TRUNCATE_ERROR);
  333. //Add the string to the buffer
  334. return apclSerialBuffer[ucPortNum_]->Add((UCHAR*)acString, ulStringLength);
  335. }
  336. BOOL DSIDebug::SerialEnable(UCHAR ucPortNum_, BOOL bEnable_)
  337. {
  338. if(!bInitialized)
  339. return FALSE;
  340. if(apclSerialBuffer[ucPortNum_] == NULL)
  341. return FALSE;
  342. apclSerialBuffer[ucPortNum_]->SetEnable(bEnable_);
  343. return TRUE;
  344. }
  345. void DSIDebug::SetDebug(BOOL bDebugOn_)
  346. {
  347. bWriteEnable = bDebugOn_;
  348. }
  349. //////////////////////////////////////////////////////////
  350. // Private Definitions
  351. //////////////////////////////////////////////////////////
  352. //Returns TRUE if buffer is not full and
  353. //pucNum_ will either be the found thread num or an available thread num
  354. //to use. Otherwise, pucNum_ is invalid.
  355. BOOL FindThreadNum(UCHAR* pucNum_)
  356. {
  357. if(pucNum_ == NULL)
  358. return FALSE;
  359. UCHAR i;
  360. BOOL bNotFull = FALSE;
  361. DSI_THREAD_IDNUM hThreadIDNum = DSIThread_GetCurrentThreadIDNum();
  362. for(i=0; i<MAX_THREADS; i++)
  363. {
  364. if(apstThread[i] != NULL)
  365. {
  366. if(DSIThread_CompareThreads(hThreadIDNum, apstThread[i]->hThreadIDNum))
  367. {
  368. *pucNum_ = i;
  369. return TRUE;
  370. }
  371. }
  372. else
  373. {
  374. *pucNum_ = i;
  375. bNotFull = TRUE;
  376. }
  377. }
  378. return bNotFull;
  379. }
  380. //////////////////////////////////////////////////////////////////////////
  381. //////////////////////////////////////////////////////////////////////////
  382. //////// Buffer Class ////////
  383. //////////////////////////////////////////////////////////////////////////
  384. //////////////////////////////////////////////////////////////////////////
  385. //////////////////////////////////////////////////////////
  386. // Public Definitions
  387. //////////////////////////////////////////////////////////
  388. Buffer::Buffer(UCHAR* pucFilename_, UCHAR* pucDirectory_)
  389. {
  390. if(pucFilename_ == NULL || pucDirectory_ == NULL)
  391. {
  392. bEnable = FALSE;
  393. return;
  394. }
  395. STRNCPY((char*)aucFilename, (char*)pucFilename_, MAX_NAME_LENGTH);
  396. SNPRINTF((char*)aucFullPath, MAX_NAME_LENGTH, "%s%s", pucDirectory_, aucFilename);
  397. DSIThread_MutexInit(&stInputMutex);
  398. bEnable = TRUE;
  399. ulInput = 0;
  400. ulOutput = 0;
  401. //Filename Mutex
  402. DSIThread_MutexInit(&stFilenameMutex);
  403. //Thread variables
  404. DSIThread_MutexInit(&stFlushMutex);
  405. DSIThread_CondInit(&stFlushCond);
  406. bForceFlush = FALSE;
  407. bBufferOverflow = FALSE;
  408. DSIThread_MutexInit(&stWriteThreadMutex);
  409. DSIThread_CondInit(&stWriteThreadCond);
  410. bWriteThreadExit = FALSE;
  411. hWriteThreadID = DSIThread_CreateThread(&Buffer::StartWriteThread, this);
  412. }
  413. Buffer::~Buffer()
  414. {
  415. //Handle output - Exit write thread
  416. DSIThread_MutexLock(&stWriteThreadMutex);
  417. if(hWriteThreadID)
  418. {
  419. bWriteThreadExit = TRUE;
  420. //Signal thread to exit.
  421. SignalFlush();
  422. //Wait for thread to exit.
  423. if(DSIThread_CondTimedWait(&stWriteThreadCond, &stWriteThreadMutex, THREAD_EXIT_TIMEOUT) != DSI_THREAD_ENONE)
  424. {
  425. DSIThread_DestroyThread(hWriteThreadID);
  426. }
  427. DSIThread_ReleaseThreadID(hWriteThreadID);
  428. hWriteThreadID = (DSI_THREAD_ID)NULL;
  429. }
  430. DSIThread_MutexUnlock(&stWriteThreadMutex);
  431. DSIThread_MutexDestroy(&stWriteThreadMutex);
  432. DSIThread_CondDestroy(&stWriteThreadCond);
  433. //Handle input
  434. DSIThread_MutexLock(&stInputMutex);
  435. bEnable = FALSE;
  436. DSIThread_MutexUnlock(&stInputMutex);
  437. DSIThread_Sleep(MUTEX_UNLOCK_DELAY*4); //wait for any remaining threads to unlock mutex
  438. //Destroy input mutex
  439. DSIThread_MutexDestroy(&stInputMutex);
  440. //Destroy flush mutex/cond
  441. DSIThread_MutexDestroy(&stFlushMutex);
  442. DSIThread_CondDestroy(&stFlushCond);
  443. //Flush the buffer to make sure it is completely empty.
  444. Flush();
  445. //Destroy filename mutex
  446. DSIThread_MutexDestroy(&stFilenameMutex);
  447. }
  448. void Buffer::SetEnable(BOOL bEnable_)
  449. {
  450. //Variable doesn't change
  451. if(bEnable == bEnable_)
  452. return;
  453. //Enabling buffer
  454. if(!bEnable)
  455. {
  456. bEnable = TRUE;
  457. return;
  458. }
  459. //Disabling buffer
  460. if(bEnable)
  461. {
  462. //Set the enable variable
  463. bEnable = FALSE;
  464. //Force a flush
  465. SignalFlush();
  466. }
  467. }
  468. BOOL Buffer::SetDirectory(const UCHAR* pucDirectory_)
  469. {
  470. if(pucDirectory_ == NULL)
  471. return FALSE;
  472. //Check if the buffer is enabled
  473. if(!bEnable)
  474. return FALSE;
  475. SignalFlush();
  476. DSIThread_Sleep(MUTEX_UNLOCK_DELAY); //wait for flush to finish
  477. DSIThread_MutexLock(&stFilenameMutex);
  478. SNPRINTF((char*)aucFullPath, MAX_NAME_LENGTH, "%s%s", pucDirectory_, aucFilename);
  479. DSIThread_MutexUnlock(&stFilenameMutex);
  480. return TRUE;
  481. }
  482. //Called by program threads
  483. //Note: Thread could be in this function when class is destroyed and code is deallocated.
  484. BOOL Buffer::Add(UCHAR* pucString_, ULONG ulSize_)
  485. {
  486. if(pucString_ == NULL || ulSize_ > BUFFER_SIZE)
  487. return FALSE;
  488. //Check if the buffer is enabled
  489. if(!bEnable)
  490. return FALSE;
  491. DSIThread_MutexLock(&stInputMutex);
  492. //Check if the buffer is enabled
  493. if(!bEnable)
  494. {
  495. DSIThread_MutexUnlock(&stInputMutex);
  496. return FALSE;
  497. }
  498. //Check if buffer has enough room for the message
  499. if(GetBufferCount() + ulSize_ > BUFFER_SIZE) //Note: possible railing if GetBufferCount()+ulSize_ > MAX_ULONG
  500. {
  501. if(!bBufferOverflow)
  502. {
  503. //Set up error message
  504. UCHAR aucErrorMesg[OVERFLOW_ERROR_LENGTH+1];
  505. STRNCPY((char*)aucErrorMesg, OVERFLOW_ERROR, OVERFLOW_ERROR_LENGTH+1);
  506. //Add the error message to the buffer
  507. ULONG ulNewInput = ulInput;
  508. for(ULONG i=0; i<OVERFLOW_ERROR_LENGTH; i++)
  509. {
  510. aucData[ulNewInput] = aucErrorMesg[i];
  511. ulNewInput++;
  512. ulNewInput %= ACTUAL_BUFFER_SIZE;
  513. }
  514. //Update the input pointer
  515. ulInput = ulNewInput;
  516. bBufferOverflow = TRUE;
  517. }
  518. //Signal WriteThread to flush
  519. SignalFlush();
  520. DSIThread_MutexUnlock(&stInputMutex);
  521. return FALSE;
  522. }
  523. //We don't have a buffer error anymore
  524. bBufferOverflow = FALSE;
  525. //Add the message to the buffer
  526. ULONG ulNewInput = ulInput;
  527. for(ULONG i=0; i<ulSize_; i++)
  528. {
  529. aucData[ulNewInput] = pucString_[i];
  530. ulNewInput++;
  531. ulNewInput %= ACTUAL_BUFFER_SIZE;
  532. }
  533. //Update the input pointer
  534. ulInput = ulNewInput;
  535. //Send a flush signal if the buffer is getting full
  536. if(GetPercentFull() >= FLUSH_PERCENT)
  537. SignalFlush();
  538. DSIThread_MutexUnlock(&stInputMutex);
  539. return TRUE;
  540. }
  541. //////////////////////////////////////////////////////////
  542. // Private Definitions
  543. //////////////////////////////////////////////////////////
  544. //Called by WriteThread
  545. BOOL Buffer::Flush()
  546. {
  547. bForceFlush = FALSE;
  548. //Get size of array we are going to write
  549. ULONG ulFixedCount = GetBufferCount();
  550. if (ulFixedCount == 0)
  551. return TRUE;
  552. //Open the file
  553. DSIThread_MutexLock(&stFilenameMutex);
  554. FILE* pfFile = FOPEN((char*)aucFullPath, "a");
  555. DSIThread_MutexUnlock(&stFilenameMutex);
  556. if(pfFile == NULL)
  557. return FALSE;
  558. //Write data to file
  559. if(ulFixedCount + ulOutput <= ACTUAL_BUFFER_SIZE)
  560. {
  561. //We don't go off the edge of the array, so we can write it all at once
  562. if(fwrite(&aucData[ulOutput], sizeof(UCHAR), ulFixedCount, pfFile) != ulFixedCount)
  563. fwrite(WRITE_ERROR, sizeof(UCHAR), strlen(WRITE_ERROR), pfFile);
  564. }
  565. else
  566. {
  567. //Write to end of array
  568. if(fwrite(&aucData[ulOutput], sizeof(UCHAR), ACTUAL_BUFFER_SIZE-ulOutput, pfFile) != ACTUAL_BUFFER_SIZE-ulOutput)
  569. fwrite(WRITE_ERROR, sizeof(UCHAR), strlen(WRITE_ERROR), pfFile);
  570. ULONG ulLeft = ulFixedCount - (ACTUAL_BUFFER_SIZE-ulOutput);
  571. //Write from beginning of array
  572. if(fwrite(&aucData[0], sizeof(UCHAR), ulLeft, pfFile) != ulLeft)
  573. fwrite(WRITE_ERROR, sizeof(UCHAR), strlen(WRITE_ERROR), pfFile);
  574. }
  575. //Close file
  576. fclose(pfFile);
  577. //Update the output pointer
  578. ULONG ulNewOutput = (ulOutput + ulFixedCount)%ACTUAL_BUFFER_SIZE;
  579. ulOutput = ulNewOutput;
  580. return TRUE;
  581. }
  582. ULONG Buffer::GetBufferCount()
  583. {
  584. //Lock values of input and output pointers
  585. ULONG ulFixedInput = ulInput;
  586. ULONG ulFixedOutput = ulOutput;
  587. ULONG ulFixedCount;
  588. if(ulFixedInput >= ulFixedOutput)
  589. ulFixedCount = ulFixedInput - ulFixedOutput;
  590. else
  591. ulFixedCount = ACTUAL_BUFFER_SIZE - (ulFixedOutput - ulFixedInput);
  592. return ulFixedCount;
  593. }
  594. UCHAR Buffer::GetPercentFull()
  595. {
  596. return (UCHAR)((double)GetBufferCount()/BUFFER_SIZE * 100);
  597. }
  598. void Buffer::SignalFlush()
  599. {
  600. DSIThread_MutexLock(&stFlushMutex);
  601. bForceFlush = TRUE;
  602. DSIThread_CondSignal(&stFlushCond);
  603. DSIThread_MutexUnlock(&stFlushMutex);
  604. }
  605. //////////////////
  606. // Write Thread //
  607. //////////////////
  608. void Buffer::WriteThread()
  609. {
  610. while(!bWriteThreadExit)
  611. {
  612. if(bEnable || bForceFlush)
  613. this->Flush();
  614. DSIThread_MutexLock(&stFlushMutex);
  615. if(!bForceFlush && !bWriteThreadExit)
  616. DSIThread_CondTimedWait(&stFlushCond, &stFlushMutex, FLUSH_CHECK_PERIOD);
  617. DSIThread_MutexUnlock(&stFlushMutex);
  618. }
  619. //Exit thread
  620. DSIThread_MutexLock(&stWriteThreadMutex);
  621. DSIThread_CondSignal(&stWriteThreadCond);
  622. bWriteThreadExit = TRUE;
  623. DSIThread_MutexUnlock(&stWriteThreadMutex);
  624. return;
  625. }
  626. DSI_THREAD_RETURN Buffer::StartWriteThread(void* pvParam_)
  627. {
  628. if(pvParam_ == NULL)
  629. return 0;
  630. Buffer* pclBuffer = (Buffer*)pvParam_;
  631. pclBuffer->WriteThread();
  632. return 0;
  633. }
  634. #endif /* DEBUG_FILE */