Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

901 lines
26 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. LogThred.c
  5. Abstract:
  6. module containing logging thread functions
  7. Author:
  8. Bob Watson (a-robw) 10 Apr 96
  9. Revision History:
  10. --*/
  11. #ifndef UNICODE
  12. #define UNICODE 1
  13. #endif
  14. #ifndef _UNICODE
  15. #define _UNICODE 1
  16. #endif
  17. //
  18. // Windows Include files
  19. //
  20. #include <windows.h>
  21. #include <stdio.h>
  22. #include <tchar.h>
  23. #include <pdh.h>
  24. #include <pdhmsg.h>
  25. #include "pdlsvc.h"
  26. //#include "logutils.h"
  27. #include "pdlmsg.h"
  28. static
  29. long
  30. JulianDateFromSystemTime(
  31. SYSTEMTIME *pST
  32. )
  33. {
  34. static WORD wDaysInRegularMonth[] = {
  35. 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
  36. };
  37. static WORD wDaysInLeapYearMonth[] = {
  38. 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366
  39. };
  40. long JDate = 0;
  41. // Check for leap year.
  42. if (pST->wMonth > 1) {
  43. if( pST->wYear % 100 != 0 && pST->wYear % 4 == 0 ) {
  44. // this is a leap year
  45. JDate += wDaysInLeapYearMonth[pST->wMonth - 1];
  46. } else {
  47. // this is not a leap year
  48. JDate += wDaysInRegularMonth[pST->wMonth - 1];
  49. }
  50. }
  51. // Add in days for this month.
  52. JDate += pST->wDay;
  53. // Add in year.
  54. JDate += (pST->wYear % 100) * 10000;
  55. return JDate;
  56. }
  57. static
  58. BOOL
  59. GetLocalFileTime (
  60. SYSTEMTIME *pST,
  61. LONGLONG *pFileTime
  62. )
  63. {
  64. BOOL bResult;
  65. GetLocalTime (pST);
  66. if (pFileTime != NULL) {
  67. bResult = SystemTimeToFileTime (pST, (LPFILETIME)pFileTime);
  68. } else {
  69. bResult = TRUE;
  70. }
  71. return bResult;
  72. }
  73. static
  74. DWORD
  75. GetSamplesInRenameInterval(
  76. IN DWORD dwSampleInterval, // in seconds
  77. IN DWORD dwRenameIntervalCount, // in units
  78. IN DWORD dwRenameIntervalUnits) // for "count" arg
  79. {
  80. DWORD dwRenameIntervalSeconds;
  81. // convert rename interval to seconds and account for the
  82. // first (or zero-th) sample in the log
  83. dwRenameIntervalSeconds = dwRenameIntervalCount;
  84. switch (dwRenameIntervalUnits) {
  85. case OPD_RENAME_HOURS:
  86. dwRenameIntervalSeconds *= SECONDS_IN_HOUR;
  87. break;
  88. case OPD_RENAME_DAYS:
  89. default:
  90. dwRenameIntervalSeconds *= SECONDS_IN_DAY;
  91. break;
  92. case OPD_RENAME_MONTHS:
  93. dwRenameIntervalSeconds *= SECONDS_IN_DAY * 30;
  94. break;
  95. case OPD_RENAME_KBYTES:
  96. case OPD_RENAME_MBYTES:
  97. // these don't use a rename counter
  98. return (DWORD)0;
  99. break;
  100. }
  101. dwRenameIntervalSeconds /= dwSampleInterval;
  102. dwRenameIntervalSeconds += 1; // add in the "zero-th" sample
  103. return (dwRenameIntervalSeconds);
  104. }
  105. static
  106. LONG
  107. BuildCurrentLogFileName (
  108. IN LPCTSTR szBaseFileName,
  109. IN LPCTSTR szDefaultDir,
  110. IN LPTSTR szOutFileBuffer,
  111. IN LPDWORD lpdwSerialNumber,
  112. IN DWORD dwDateFormat,
  113. IN DWORD dwLogFormat
  114. )
  115. // presumes OutFileBuffer is large enough (i.e. >= MAX_PATH)
  116. {
  117. SYSTEMTIME st;
  118. BOOL bUseCurrentDir = FALSE;
  119. TCHAR szAuto[MAX_PATH];
  120. LPTSTR szExt;
  121. if (szDefaultDir != NULL) {
  122. if (*szDefaultDir == 0) {
  123. bUseCurrentDir = TRUE;
  124. }
  125. } else {
  126. bUseCurrentDir = TRUE;
  127. }
  128. if (bUseCurrentDir) {
  129. GetCurrentDirectory (MAX_PATH, szOutFileBuffer);
  130. } else {
  131. lstrcpy (szOutFileBuffer, szDefaultDir);
  132. }
  133. // add a backslash to the path name if it doesn't have one already
  134. if (szOutFileBuffer[lstrlen(szOutFileBuffer)-1] != TEXT('\\')) {
  135. lstrcat (szOutFileBuffer, TEXT("\\"));
  136. }
  137. // add the base filename
  138. lstrcat (szOutFileBuffer, szBaseFileName);
  139. // add the auto name part
  140. // get date/time/serial integer format
  141. GetLocalTime(&st);
  142. switch (dwDateFormat) {
  143. case OPD_NAME_NNNNNN:
  144. _stprintf (szAuto, TEXT("_%6.6d"), *lpdwSerialNumber);
  145. (*lpdwSerialNumber)++; // increment
  146. if (*lpdwSerialNumber >= 1000000) {
  147. // roll over to 0
  148. *lpdwSerialNumber = 0;
  149. }
  150. break;
  151. case OPD_NAME_YYDDD:
  152. _stprintf (szAuto, TEXT("_%5.5d"),
  153. JulianDateFromSystemTime(&st));
  154. break;
  155. case OPD_NAME_YYMM:
  156. _stprintf (szAuto, TEXT("_%2.2d%2.2d"),
  157. st.wYear % 100, st.wMonth);
  158. break;
  159. case OPD_NAME_YYMMDDHH:
  160. _stprintf (szAuto, TEXT("_%2.2d%2.2d%2.2d%2.2d"),
  161. (st.wYear % 100), st.wMonth, st.wDay, st.wHour);
  162. break;
  163. case OPD_NAME_MMDDHH:
  164. _stprintf (szAuto, TEXT("_%2.2d%2.2d%2.2d"),
  165. st.wMonth, st.wDay, st.wHour);
  166. break;
  167. case OPD_NAME_YYMMDD:
  168. default:
  169. _stprintf (szAuto, TEXT("_%2.2d%2.2d%2.2d"),
  170. st.wYear % 100, st.wMonth, st.wDay);
  171. break;
  172. }
  173. lstrcat (szOutFileBuffer, szAuto);
  174. // get file type
  175. switch (dwLogFormat) {
  176. case PDH_LOG_TYPE_TSV:
  177. szExt = TEXT(".tsv");
  178. break;
  179. case PDH_LOG_TYPE_BINARY:
  180. szExt = TEXT(".blg");
  181. break;
  182. case PDH_LOG_TYPE_CSV:
  183. default:
  184. szExt = TEXT(".csv");
  185. break;
  186. }
  187. lstrcat (szOutFileBuffer, szExt);
  188. return ERROR_SUCCESS;
  189. }
  190. static
  191. BOOL
  192. LoadDataFromRegistry (
  193. IN LPLOG_THREAD_DATA pArg,
  194. IN LPTSTR szDefaultDir,
  195. IN LPTSTR szBaseName,
  196. IN LPTSTR szCurrentLogFile
  197. )
  198. {
  199. LONG lStatus;
  200. DWORD dwType;
  201. DWORD dwSize;
  202. DWORD dwData;
  203. LPTSTR szStringArray[2];
  204. // get size of buffer required by counter list,
  205. // then allocate the buffer and retrieve the counter list
  206. dwType = 0;
  207. dwData = 0;
  208. dwSize = 0;
  209. lStatus = RegQueryValueEx (
  210. pArg->hKeyQuery,
  211. TEXT("Counter List"),
  212. NULL,
  213. &dwType,
  214. (LPBYTE)NULL,
  215. &dwSize);
  216. pArg->mszCounterList = (LPTSTR)G_ALLOC(dwSize);
  217. if (pArg->mszCounterList != NULL) {
  218. dwType = 0;
  219. lStatus = RegQueryValueEx (
  220. pArg->hKeyQuery,
  221. TEXT("Counter List"),
  222. NULL,
  223. &dwType,
  224. (LPBYTE)pArg->mszCounterList,
  225. &dwSize);
  226. if ((lStatus != ERROR_SUCCESS) || (dwSize == 0)) {
  227. // no counter list retrieved so there's not much
  228. // point in continuing
  229. szStringArray[0] = pArg->szQueryName;
  230. ReportEvent (hEventLog,
  231. EVENTLOG_ERROR_TYPE,
  232. 0,
  233. PERFLOG_UNABLE_READ_COUNTER_LIST,
  234. NULL,
  235. 1,
  236. sizeof(DWORD),
  237. szStringArray,
  238. (LPVOID)&lStatus);
  239. return FALSE;
  240. }
  241. } else {
  242. szStringArray[0] = pArg->szQueryName;
  243. ReportEvent (hEventLog,
  244. EVENTLOG_ERROR_TYPE,
  245. 0,
  246. PERFLOG_UNABLE_ALLOC_COUNTER_LIST,
  247. NULL,
  248. 1,
  249. sizeof(DWORD),
  250. szStringArray,
  251. (LPVOID)&lStatus);
  252. return FALSE;
  253. }
  254. dwType = 0;
  255. dwData = 0;
  256. dwSize = sizeof(DWORD);
  257. lStatus = RegQueryValueEx (
  258. pArg->hKeyQuery,
  259. TEXT("Auto Name Interval"),
  260. NULL,
  261. &dwType,
  262. (LPBYTE)&dwData,
  263. &dwSize);
  264. if (lStatus != ERROR_SUCCESS) {
  265. dwData = 0; // default is no autonaming
  266. } else if (dwType != REG_DWORD) {
  267. dwData = 0; // default is no autonaming
  268. } // else assume success
  269. pArg->dwRenameIntervalCount = dwData;
  270. dwType = 0;
  271. dwData = 0;
  272. dwSize = sizeof(DWORD);
  273. lStatus = RegQueryValueEx (
  274. pArg->hKeyQuery,
  275. TEXT("Auto Rename Units"),
  276. NULL,
  277. &dwType,
  278. (LPBYTE)&dwData,
  279. &dwSize);
  280. if (lStatus != ERROR_SUCCESS) {
  281. dwData = OPD_RENAME_DAYS; // default is days
  282. } else if (dwType != REG_DWORD) {
  283. dwData = OPD_RENAME_DAYS; // default is days
  284. } // else assume success
  285. pArg->dwRenameIntervalUnits = dwData;
  286. dwType = 0;
  287. dwData = 0;
  288. dwSize = sizeof(DWORD);
  289. lStatus = RegQueryValueEx (
  290. pArg->hKeyQuery,
  291. TEXT("Log File Auto Format"),
  292. NULL,
  293. &dwType,
  294. (LPBYTE)&dwData,
  295. &dwSize);
  296. if (lStatus != ERROR_SUCCESS) {
  297. dwData = OPD_NAME_NNNNNN; // default is a serial number
  298. } else if (dwType != REG_DWORD) {
  299. dwData = OPD_NAME_NNNNNN; // default is a serial number
  300. } // else assume success
  301. pArg->dwAutoNameFormat = dwData;
  302. dwType = 0;
  303. dwData = 0;
  304. dwSize = sizeof(DWORD);
  305. lStatus = RegQueryValueEx (
  306. pArg->hKeyQuery,
  307. TEXT("Log File Type"),
  308. NULL,
  309. &dwType,
  310. (LPBYTE)&dwData,
  311. &dwSize);
  312. if (lStatus != ERROR_SUCCESS) {
  313. dwData = OPD_CSV_FILE; // default is a CSV file
  314. } else if (dwType != REG_DWORD) {
  315. dwData = OPD_CSV_FILE; // default is a CSV file
  316. } // else assume success
  317. // convert from OPD to PDH constant
  318. switch (dwData) {
  319. case OPD_TSV_FILE:
  320. pArg->dwLogType = PDH_LOG_TYPE_TSV;
  321. break;
  322. case OPD_BIN_FILE:
  323. pArg->dwLogType = PDH_LOG_TYPE_BINARY;
  324. break;
  325. case OPD_CSV_FILE:
  326. default:
  327. pArg->dwLogType = PDH_LOG_TYPE_CSV;
  328. break;
  329. }
  330. dwType = 0;
  331. dwData = 0;
  332. dwSize = sizeof(DWORD);
  333. lStatus = RegQueryValueEx (
  334. pArg->hKeyQuery,
  335. TEXT("Sample Interval"),
  336. NULL,
  337. &dwType,
  338. (LPBYTE)&dwData,
  339. &dwSize);
  340. if (lStatus != ERROR_SUCCESS) {
  341. dwData = SECONDS_IN_MINUTE; // default is 1 Minute samples
  342. } else if (dwType != REG_DWORD) {
  343. dwData = SECONDS_IN_MINUTE; // default is 1 Minute samples
  344. } // else assume success
  345. pArg->dwTimeInterval = dwData;
  346. // get filename or components if auto name
  347. if (pArg->dwRenameIntervalCount > 0) {
  348. // this is an autoname file so get components
  349. dwType = 0;
  350. *szDefaultDir = 0;
  351. dwSize = MAX_PATH * sizeof(TCHAR);
  352. lStatus = RegQueryValueEx (
  353. pArg->hKeyQuery,
  354. TEXT("Log Default Directory"),
  355. NULL,
  356. &dwType,
  357. (LPBYTE)&szDefaultDir[0],
  358. &dwSize);
  359. if (lStatus != ERROR_SUCCESS) {
  360. *szDefaultDir = 0;
  361. } // else assume success
  362. dwType = 0;
  363. *szBaseName = 0;
  364. dwSize = MAX_PATH * sizeof(TCHAR);
  365. lStatus = RegQueryValueEx (
  366. pArg->hKeyQuery,
  367. TEXT("Base Filename"),
  368. NULL,
  369. &dwType,
  370. (LPBYTE)&szBaseName[0],
  371. &dwSize);
  372. if (lStatus != ERROR_SUCCESS) {
  373. // apply default
  374. lstrcpy (szBaseName, TEXT("perfdata"));
  375. } // else assume success
  376. dwType = 0;
  377. dwSize = 0;
  378. lStatus = RegQueryValueEx (
  379. pArg->hKeyQuery,
  380. TEXT("Command File"),
  381. NULL,
  382. &dwType,
  383. NULL,
  384. &dwSize);
  385. if (lStatus != ERROR_SUCCESS) {
  386. // assume no command filname
  387. pArg->szCmdFileName = NULL;
  388. } else {
  389. // allocate a buffer for this field and collect data
  390. pArg->szCmdFileName = (LPTSTR)G_ALLOC(dwSize);
  391. if (pArg->szCmdFileName != NULL) {
  392. // get command filename
  393. dwType = 0;
  394. lStatus = RegQueryValueEx (
  395. pArg->hKeyQuery,
  396. TEXT("Command File"),
  397. NULL,
  398. &dwType,
  399. (LPBYTE)pArg->szCmdFileName,
  400. &dwSize);
  401. if (lStatus != ERROR_SUCCESS) {
  402. // apply default
  403. pArg->szCmdFileName = NULL;
  404. } // else assume success
  405. }
  406. if (pArg->szCmdFileName == NULL) {
  407. // log error message
  408. // no command file could be read so issue
  409. // warning and continue
  410. szStringArray[0] = pArg->szQueryName;
  411. ReportEvent (hEventLog,
  412. EVENTLOG_WARNING_TYPE,
  413. 0,
  414. PERFLOG_ALLOC_CMDFILE_BUFFER,
  415. NULL,
  416. 1,
  417. sizeof(DWORD),
  418. szStringArray,
  419. (LPVOID)&lStatus);
  420. }
  421. }
  422. } else {
  423. // this is a manual name file so read name
  424. dwType = 0;
  425. *szCurrentLogFile = 0;
  426. dwSize = MAX_PATH * sizeof(TCHAR);
  427. lStatus = RegQueryValueEx (
  428. pArg->hKeyQuery,
  429. TEXT("Log Filename"),
  430. NULL,
  431. &dwType,
  432. (LPBYTE)&szCurrentLogFile[0],
  433. &dwSize);
  434. if (lStatus != ERROR_SUCCESS) {
  435. // apply default
  436. lstrcpy (szCurrentLogFile, TEXT("c:\\perfdata.log"));
  437. } // else assume success
  438. }
  439. dwType = 0;
  440. dwData = 0;
  441. dwSize = sizeof(DWORD);
  442. lStatus = RegQueryValueEx (
  443. pArg->hKeyQuery,
  444. TEXT("Log File Serial Number"),
  445. NULL,
  446. &dwType,
  447. (LPBYTE)&dwData,
  448. &dwSize);
  449. if (lStatus != ERROR_SUCCESS) {
  450. dwData = 1; // default is to start at 1
  451. } else if (dwType != REG_DWORD) {
  452. dwData = 1; // default is to start at 1
  453. } // else assume success
  454. pArg->dwCurrentSerialNumber = dwData;
  455. return TRUE;
  456. }
  457. static
  458. LONG
  459. DoCommandFile (
  460. IN LPLOG_THREAD_DATA pArg,
  461. IN LPTSTR szLogFileName,
  462. IN BOOL bStillRunning
  463. )
  464. {
  465. LONG lStatus;
  466. BOOL bStatus;
  467. LPTSTR szCommandString;
  468. LONG nErrorMode;
  469. TCHAR TempBuffer [ 5 * MAX_PATH] ;
  470. DWORD StringLen;
  471. STARTUPINFO si;
  472. PROCESS_INFORMATION pi;
  473. DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS;
  474. szCommandString = (LPTSTR)G_ALLOC(4096 * sizeof(TCHAR));
  475. if (szCommandString != NULL) {
  476. // build command line arguments
  477. szCommandString[0] = _T('\"');
  478. lstrcpy (&szCommandString[1], szLogFileName);
  479. lstrcat (szCommandString, TEXT("\" "));
  480. lstrcat (szCommandString,
  481. (bStillRunning ? TEXT("1") : TEXT("0")));
  482. // initialize Startup Info block
  483. memset (&si, 0, sizeof(si));
  484. si.cb = sizeof(si);
  485. si.dwFlags = STARTF_USESHOWWINDOW ;
  486. si.wShowWindow = SW_SHOWNOACTIVATE ;
  487. memset (&pi, 0, sizeof(pi));
  488. // supress pop-ups inf the detached process
  489. nErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
  490. lstrcpy (TempBuffer, pArg->szCmdFileName) ;
  491. // see if this is a CMD or a BAT file
  492. // if it is then create a process with a console window, otherwise
  493. // assume it's an executable file that will create it's own window
  494. // or console if necessary
  495. //
  496. _tcslwr (TempBuffer);
  497. if ((_tcsstr(TempBuffer, TEXT(".bat")) != NULL) ||
  498. (_tcsstr(TempBuffer, TEXT(".cmd")) != NULL)){
  499. dwCreationFlags |= CREATE_NEW_CONSOLE;
  500. } else {
  501. dwCreationFlags |= DETACHED_PROCESS;
  502. }
  503. // recopy the image name to the temp buffer since it was modified
  504. // (i.e. lowercased) for the previous comparison.
  505. lstrcpy (TempBuffer, pArg->szCmdFileName) ;
  506. StringLen = lstrlen (TempBuffer) ;
  507. // now add on the alert text preceded with a space char
  508. TempBuffer [StringLen] = TEXT(' ') ;
  509. StringLen++ ;
  510. lstrcpy (&TempBuffer[StringLen], szCommandString) ;
  511. bStatus = CreateProcess (
  512. NULL,
  513. TempBuffer,
  514. NULL, NULL, FALSE,
  515. dwCreationFlags,
  516. NULL,
  517. NULL,
  518. &si,
  519. &pi);
  520. SetErrorMode(nErrorMode);
  521. if (bStatus) {
  522. lStatus = ERROR_SUCCESS;
  523. } else {
  524. lStatus = GetLastError();
  525. }
  526. } else {
  527. lStatus = ERROR_OUTOFMEMORY;
  528. }
  529. return lStatus;
  530. }
  531. BOOL
  532. LoggingProc (
  533. IN LPLOG_THREAD_DATA pArg
  534. )
  535. {
  536. HQUERY hQuery;
  537. HCOUNTER hThisCounter;
  538. HLOG hLog;
  539. DWORD dwDelay;
  540. DWORD dwSampleInterval, dwSampleTime=-1;
  541. PDH_STATUS pdhStatus;
  542. DWORD dwNumCounters;
  543. LONG lStatus;
  544. TCHAR szDefaultDir[MAX_PATH];
  545. TCHAR szBaseName[MAX_PATH];
  546. LPTSTR szThisPath;
  547. DWORD dwLogType = PDH_LOG_TYPE_CSV;
  548. BOOL bRun = FALSE;
  549. DWORD dwSamplesUntilNewFile;
  550. TCHAR szCurrentLogFile[MAX_PATH];
  551. LONG lWaitStatus;
  552. LPTSTR szStringArray[4];
  553. DWORD dwFileSizeLimit;
  554. LONGLONG llFileSizeLimit;
  555. LONGLONG llFileSize;
  556. PLOG_COUNTER_INFO pCtrInfo;
  557. SYSTEMTIME st;
  558. LONGLONG llStartTime = 0;
  559. LONGLONG llFinishTime = 0;
  560. // read registry values
  561. if (!LoadDataFromRegistry (pArg, szDefaultDir, szBaseName, szCurrentLogFile)) {
  562. // unable to initialize the query from the registry
  563. return FALSE;
  564. }
  565. // convert to milliseconds for use in timeouts
  566. dwSampleInterval = pArg->dwTimeInterval * 1000L;
  567. // open query and add counters from info file
  568. pdhStatus = PdhOpenQuery (NULL, 0, &hQuery); // from current activity
  569. if (pdhStatus == ERROR_SUCCESS) {
  570. dwNumCounters = 0;
  571. for (szThisPath = pArg->mszCounterList;
  572. *szThisPath != 0;
  573. szThisPath += lstrlen(szThisPath) + 1) {
  574. pdhStatus = PdhAddCounter (hQuery,
  575. (LPTSTR)szThisPath, dwNumCounters++, &hThisCounter);
  576. if (pdhStatus == ERROR_SUCCESS) {
  577. // then add this handle to the list
  578. pCtrInfo = G_ALLOC (sizeof (LOG_COUNTER_INFO));
  579. if (pCtrInfo != NULL) {
  580. // insert at front of list since the order isn't
  581. // important and this is simpler than walking the
  582. // list each time.
  583. pCtrInfo->hCounter = hThisCounter;
  584. pCtrInfo->next = pFirstCounter;
  585. pFirstCounter = pCtrInfo;
  586. }
  587. }
  588. }
  589. // to make sure we get to log the data
  590. SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
  591. bRun = TRUE;
  592. while (bRun) {
  593. // Get the current Log filename
  594. if (pArg->dwRenameIntervalCount != 0) {
  595. // then this is an autonamed file
  596. // so make current name
  597. BuildCurrentLogFileName (
  598. szBaseName,
  599. szDefaultDir,
  600. szCurrentLogFile,
  601. &pArg->dwCurrentSerialNumber,
  602. pArg->dwAutoNameFormat,
  603. pArg->dwLogType);
  604. // reset loop counter
  605. switch (pArg->dwRenameIntervalUnits) {
  606. case OPD_RENAME_KBYTES:
  607. dwFileSizeLimit = pArg->dwRenameIntervalCount * 1024;
  608. dwSamplesUntilNewFile = 0;
  609. break;
  610. case OPD_RENAME_MBYTES:
  611. dwFileSizeLimit = pArg->dwRenameIntervalCount * 1024 * 1024;
  612. dwSamplesUntilNewFile = 0;
  613. break;
  614. case OPD_RENAME_HOURS:
  615. case OPD_RENAME_DAYS:
  616. case OPD_RENAME_MONTHS:
  617. default:
  618. dwSamplesUntilNewFile = GetSamplesInRenameInterval(
  619. pArg->dwTimeInterval,
  620. pArg->dwRenameIntervalCount,
  621. pArg->dwRenameIntervalUnits);
  622. dwFileSizeLimit = 0;
  623. break;
  624. }
  625. } else {
  626. // filename is left as read from the registry
  627. dwSamplesUntilNewFile = 0;
  628. dwFileSizeLimit = 0;
  629. }
  630. llFileSizeLimit = dwFileSizeLimit;
  631. // open log file using this query
  632. dwLogType = pArg->dwLogType;
  633. pdhStatus = PdhOpenLog (
  634. szCurrentLogFile,
  635. PDH_LOG_WRITE_ACCESS |
  636. PDH_LOG_CREATE_ALWAYS,
  637. &dwLogType,
  638. hQuery,
  639. 0,
  640. NULL,
  641. &hLog);
  642. if (pdhStatus == ERROR_SUCCESS) {
  643. szStringArray[0] = pArg->szQueryName;
  644. szStringArray[1] = szCurrentLogFile;
  645. ReportEvent (hEventLog,
  646. EVENTLOG_INFORMATION_TYPE,
  647. 0,
  648. PERFLOG_LOGGING_QUERY,
  649. NULL,
  650. 2,
  651. 0,
  652. szStringArray,
  653. NULL);
  654. // start sampling immediately
  655. dwDelay = 0;
  656. while ((lWaitStatus = WaitForSingleObject (pArg->hExitEvent, dwDelay)) == WAIT_TIMEOUT) {
  657. // the event flag will be set when the sampling should exit. if
  658. // the wait times out, then that means it's time to collect and
  659. // log another sample of data.
  660. // time the call to adjust the wait time
  661. GetLocalFileTime (&st, &llStartTime);
  662. pdhStatus = PdhUpdateLog (hLog, NULL);
  663. if (pdhStatus == ERROR_SUCCESS) {
  664. // see if it's time to rename the file
  665. if (dwSamplesUntilNewFile) {
  666. if (!--dwSamplesUntilNewFile) break;
  667. } else if (llFileSizeLimit) {
  668. // see if the file is too big
  669. pdhStatus = PdhGetLogFileSize (hLog, &llFileSize);
  670. if (pdhStatus == ERROR_SUCCESS) {
  671. if (llFileSizeLimit <= llFileSize) break;
  672. }
  673. }
  674. // compute new timeout value
  675. if (dwSampleTime < dwSampleInterval) {
  676. dwDelay = dwSampleInterval - dwSampleTime;
  677. } else {
  678. dwDelay = 0;
  679. }
  680. } else {
  681. // unable to update the log so log event and exit
  682. ReportEvent (hEventLog,
  683. EVENTLOG_ERROR_TYPE,
  684. 0,
  685. PERFLOG_UNABLE_UPDATE_LOG,
  686. NULL,
  687. 0,
  688. sizeof(DWORD),
  689. NULL,
  690. (LPVOID)&pdhStatus);
  691. bRun = FALSE;
  692. break;
  693. }
  694. GetLocalFileTime (&st, &llFinishTime);
  695. // compute difference and convert to milliseconds
  696. dwSampleTime = (DWORD)((llFinishTime - llStartTime) / 10000L);
  697. } // end while wait keeps timing out
  698. if (lWaitStatus == WAIT_OBJECT_0) {
  699. // then the loop was terminated by the Exit event
  700. // so clear the "run" flag to exit the loop & thread
  701. bRun = FALSE;
  702. }
  703. // close log file, but keep query open
  704. PdhCloseLog (hLog, 0);
  705. if (pArg->szCmdFileName != NULL) {
  706. DoCommandFile (pArg, szCurrentLogFile, bRun);
  707. }
  708. } else {
  709. // unable to open log file so log event log message
  710. szStringArray[0] = szCurrentLogFile;
  711. ReportEvent (hEventLog,
  712. EVENTLOG_ERROR_TYPE,
  713. 0,
  714. PERFLOG_UNABLE_OPEN_LOG_FILE,
  715. NULL,
  716. 1,
  717. sizeof(DWORD),
  718. szStringArray,
  719. (LPVOID)&pdhStatus);
  720. bRun = FALSE; // exit now
  721. }
  722. } // end while (bRun)
  723. PdhCloseQuery (hQuery);
  724. // update log serial number if necssary
  725. if (pArg->dwAutoNameFormat == OPD_NAME_NNNNNN) {
  726. lStatus = RegSetValueEx (
  727. pArg->hKeyQuery,
  728. TEXT("Log File Serial Number"),
  729. 0L,
  730. REG_DWORD,
  731. (LPBYTE)&pArg->dwCurrentSerialNumber,
  732. sizeof(DWORD));
  733. }
  734. } else {
  735. // unable to open query so write event log message
  736. ReportEvent (hEventLog,
  737. EVENTLOG_ERROR_TYPE,
  738. 0,
  739. PERFLOG_UNABLE_OPEN_LOG_QUERY,
  740. NULL,
  741. 0,
  742. sizeof(DWORD),
  743. NULL,
  744. (LPVOID)&pdhStatus);
  745. }
  746. // free allocated buffers
  747. if (pArg->mszCounterList != NULL) {
  748. G_FREE(pArg->mszCounterList);
  749. pArg->mszCounterList = NULL;
  750. }
  751. if (pArg->szCmdFileName != NULL) {
  752. G_FREE(pArg->szCmdFileName);
  753. pArg->szCmdFileName = NULL;
  754. }
  755. return bRun;
  756. }
  757. DWORD
  758. LoggingThreadProc (
  759. IN LPVOID lpThreadArg
  760. )
  761. {
  762. LPLOG_THREAD_DATA pThreadData = (LPLOG_THREAD_DATA)lpThreadArg;
  763. DWORD dwStatus = ERROR_SUCCESS;
  764. BOOL bContinue = TRUE;
  765. if (pThreadData != NULL) {
  766. // read config from registry
  767. do {
  768. // read config from registry
  769. // expand counter paths as necessary
  770. // call Logging function
  771. bContinue = LoggingProc (pThreadData);
  772. // see if this thread was paused for reloading
  773. // or stopped to terminate
  774. if (pThreadData->bReloadNewConfig) {
  775. bContinue = TRUE;
  776. } // else bContinue is always returned as FALSE
  777. // so that will terminate this loop
  778. } while (bContinue);
  779. dwStatus = ERROR_SUCCESS;
  780. } else {
  781. // unable to find data block so return
  782. dwStatus = ERROR_INVALID_PARAMETER;
  783. }
  784. return dwStatus;
  785. }