dsi_debug.cpp 21 KB

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