Leaked source code of windows server 2003
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.

1719 lines
49 KiB

  1. /*===================================================================
  2. Microsoft Denali
  3. Microsoft Confidential.
  4. Copyright 1996 Microsoft Corporation. All Rights Reserved.
  5. Component: Main
  6. File: perfdef.h
  7. Owner: DmitryR
  8. Data definitions shared between asp.dll and aspperf.dll
  9. ===================================================================*/
  10. #ifndef _ASP_PERFDEF_H
  11. #define _ASP_PERFDEF_H
  12. #include <winbase.h>
  13. #include <pudebug.h>
  14. #ifndef ErrInitCriticalSection
  15. #define ErrInitCriticalSection( cs, hr ) \
  16. do { \
  17. hr = S_OK; \
  18. __try \
  19. { \
  20. InitializeCriticalSection (cs); \
  21. } \
  22. __except(1) \
  23. { \
  24. hr = E_UNEXPECTED; \
  25. } \
  26. } while (0)
  27. #endif
  28. /*===================================================================
  29. PerfData indices
  30. ===================================================================*/
  31. // Counter offsets in the array
  32. #define ID_DEBUGDOCREQ 0
  33. #define ID_REQERRRUNTIME 1
  34. #define ID_REQERRPREPROC 2
  35. #define ID_REQERRCOMPILE 3
  36. #define ID_REQERRORPERSEC 4
  37. #define ID_REQTOTALBYTEIN 5
  38. #define ID_REQTOTALBYTEOUT 6
  39. #define ID_REQEXECTIME 7
  40. #define ID_REQWAITTIME 8
  41. #define ID_REQCOMFAILED 9
  42. #define ID_REQBROWSEREXEC 10
  43. #define ID_REQFAILED 11
  44. #define ID_REQNOTAUTH 12
  45. #define ID_REQNOTFOUND 13
  46. #define ID_REQCURRENT 14
  47. #define ID_REQREJECTED 15
  48. #define ID_REQSUCCEEDED 16
  49. #define ID_REQTIMEOUT 17
  50. #define ID_REQTOTAL 18
  51. #define ID_REQPERSEC 19
  52. #define ID_SCRIPTFREEENG 20
  53. #define ID_SESSIONLIFETIME 21
  54. #define ID_SESSIONCURRENT 22
  55. #define ID_SESSIONTIMEOUT 23
  56. #define ID_SESSIONSTOTAL 24
  57. #define ID_TEMPLCACHE 25
  58. #define ID_TEMPLCACHEHITS 26
  59. #define ID_TEMPLCACHETRYS 27
  60. #define ID_TEMPLFLUSHES 28
  61. #define ID_TRANSABORTED 29
  62. #define ID_TRANSCOMMIT 30
  63. #define ID_TRANSPENDING 31
  64. #define ID_TRANSTOTAL 32
  65. #define ID_TRANSPERSEC 33
  66. #define ID_MEMORYTEMPLCACHE 34
  67. #define ID_MEMORYTEMPLCACHEHITS 35
  68. #define ID_MEMORYTEMPLCACHETRYS 36
  69. #define ID_ENGINECACHEHITS 37
  70. #define ID_ENGINECACHETRYS 38
  71. #define ID_ENGINEFLUSHES 39
  72. // Number of counters in per-process file map
  73. #define C_PERF_PROC_COUNTERS 40
  74. /*===================================================================
  75. Definitions of names, sizes and mapped data block structures
  76. ===================================================================*/
  77. // Mutex name to access the main file map
  78. #define SZ_PERF_MUTEX "Global\\ASP_PERFMON_MUTEX"
  79. // WaitForSingleObject arg (how long to wait for mutext before failing)
  80. #define PERM_MUTEX_WAIT 1000
  81. // event signaled by ASP processes when a procId is added
  82. #define SZ_PERF_ADD_EVENT "Global\\ASP_PERFMON_ADD_EVENT"
  83. // Main shared file map name
  84. #define SZ_PERF_MAIN_FILEMAP "Global\\ASP_PERFMON_MAIN_BLOCK"
  85. // Max number of registered (ASP) processes in main file map
  86. #define C_PERF_PROC_MAX 2048
  87. // Structure that defines main file map
  88. struct CPerfMainBlockData
  89. {
  90. DWORD m_dwTimestamp; // time (GetTickCount()) of the last change
  91. DWORD m_cItems; // number of registred processes
  92. // array of process IDs
  93. DWORD m_dwProcIds[C_PERF_PROC_MAX];
  94. // array of accumulated counters for dead processes
  95. DWORD m_rgdwCounters[C_PERF_PROC_COUNTERS];
  96. DWORD m_dwWASPid;
  97. };
  98. #define CB_PERF_MAIN_BLOCK (sizeof(struct CPerfMainBlockData))
  99. // Name for per-process file map
  100. #define SZ_PERF_PROC_FILEMAP_PREFIX "Global\\ASP_PERFMON_BLOCK_"
  101. #define CCH_PERF_PROC_FILEMAP_PREFIX 25
  102. struct CPerfProcBlockData
  103. {
  104. DWORD m_dwProcId; // process CLS ID
  105. DWORD m_rgdwCounters[C_PERF_PROC_COUNTERS]; // array counters
  106. };
  107. #define CB_PERF_PROC_BLOCK (sizeof(struct CPerfProcBlockData))
  108. #define CB_COUNTERS (sizeof(DWORD) * C_PERF_PROC_COUNTERS)
  109. class CASPPerfManager;
  110. /*===================================================================
  111. CSharedMemBlock -- generic shared memory block
  112. ===================================================================*/
  113. class CSharedMemBlock
  114. {
  115. public:
  116. HANDLE m_hMemory;
  117. void *m_pMemory;
  118. SECURITY_ATTRIBUTES m_sa;
  119. inline CSharedMemBlock() : m_hMemory(NULL), m_pMemory(NULL) {
  120. m_sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  121. m_sa.lpSecurityDescriptor = NULL;
  122. m_sa.bInheritHandle = FALSE;
  123. }
  124. inline ~CSharedMemBlock() {
  125. UnInitMap();
  126. if (m_sa.lpSecurityDescriptor)
  127. free(m_sa.lpSecurityDescriptor);
  128. }
  129. inline void *PMemory() { return m_pMemory; }
  130. HRESULT InitSD();
  131. HRESULT InitMap(LPCSTR szName, DWORD dwSize, BOOL bCreate = TRUE);
  132. HRESULT UnInitMap();
  133. SECURITY_ATTRIBUTES *PGetSA() { return &m_sa; }
  134. private:
  135. HRESULT CreateSids( PSID *ppBuiltInAdministrators,
  136. PSID *ppPowerUsers,
  137. PSID *ppAuthenticatedUsers,
  138. PSID *ppPerfMonUsers,
  139. PSID *ppPerfLogUsers);
  140. };
  141. //
  142. // CreateSids
  143. //
  144. // Create 3 Security IDs
  145. //
  146. // Caller must free memory allocated to SIDs on success.
  147. //
  148. // Returns: HRESULT indicating SUCCESS or FAILURE
  149. //
  150. inline HRESULT CSharedMemBlock::CreateSids(
  151. PSID *ppBuiltInAdministrators,
  152. PSID *ppPowerUsers,
  153. PSID *ppAuthenticatedUsers,
  154. PSID *ppPerfMonUsers,
  155. PSID *ppPerfLogUsers
  156. )
  157. {
  158. HRESULT hr = S_OK;
  159. *ppBuiltInAdministrators = NULL;
  160. *ppPowerUsers = NULL;
  161. *ppAuthenticatedUsers = NULL;
  162. *ppPerfMonUsers = NULL;
  163. *ppPerfLogUsers = NULL;
  164. //
  165. // An SID is built from an Identifier Authority and a set of Relative IDs
  166. // (RIDs). The Authority of interest to us SECURITY_NT_AUTHORITY.
  167. //
  168. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  169. //
  170. // Each RID represents a sub-unit of the authority. Two of the SIDs we
  171. // want to build, Local Administrators, and Power Users, are in the "built
  172. // in" domain. The other SID, for Authenticated users, is based directly
  173. // off of the authority.
  174. //
  175. // For examples of other useful SIDs consult the list in
  176. // \nt\public\sdk\inc\ntseapi.h.
  177. //
  178. if (!AllocateAndInitializeSid(&NtAuthority,
  179. 2, // 2 sub-authorities
  180. SECURITY_BUILTIN_DOMAIN_RID,
  181. DOMAIN_ALIAS_RID_ADMINS,
  182. 0,0,0,0,0,0,
  183. ppBuiltInAdministrators)) {
  184. hr = HRESULT_FROM_WIN32(GetLastError());
  185. } else if (!AllocateAndInitializeSid(&NtAuthority,
  186. 2, // 2 sub-authorities
  187. SECURITY_BUILTIN_DOMAIN_RID,
  188. DOMAIN_ALIAS_RID_POWER_USERS,
  189. 0,0,0,0,0,0,
  190. ppPowerUsers)) {
  191. hr = HRESULT_FROM_WIN32(GetLastError());
  192. } else if (!AllocateAndInitializeSid(&NtAuthority,
  193. 1, // 1 sub-authority
  194. SECURITY_AUTHENTICATED_USER_RID,
  195. 0,0,0,0,0,0,0,
  196. ppAuthenticatedUsers)) {
  197. hr = HRESULT_FROM_WIN32(GetLastError());
  198. } else if (!AllocateAndInitializeSid(&NtAuthority,
  199. 2, // 1 sub-authority
  200. SECURITY_BUILTIN_DOMAIN_RID,
  201. DOMAIN_ALIAS_RID_MONITORING_USERS,
  202. 0,0,0,0,0,0,
  203. ppPerfMonUsers)) {
  204. hr = HRESULT_FROM_WIN32(GetLastError());
  205. } else if (!AllocateAndInitializeSid(&NtAuthority,
  206. 2, // 1 sub-authority
  207. SECURITY_BUILTIN_DOMAIN_RID,
  208. DOMAIN_ALIAS_RID_LOGGING_USERS,
  209. 0,0,0,0,0,0,
  210. ppPerfLogUsers)) {
  211. hr = HRESULT_FROM_WIN32(GetLastError());
  212. }
  213. if (FAILED(hr)) {
  214. if (*ppBuiltInAdministrators) {
  215. FreeSid(*ppBuiltInAdministrators);
  216. *ppBuiltInAdministrators = NULL;
  217. }
  218. if (*ppPowerUsers) {
  219. FreeSid(*ppPowerUsers);
  220. *ppPowerUsers = NULL;
  221. }
  222. if (*ppAuthenticatedUsers) {
  223. FreeSid(*ppAuthenticatedUsers);
  224. *ppAuthenticatedUsers = NULL;
  225. }
  226. if (*ppPerfMonUsers) {
  227. FreeSid(*ppPerfMonUsers);
  228. *ppPerfMonUsers = NULL;
  229. }
  230. if (*ppPerfLogUsers) {
  231. FreeSid(*ppPerfLogUsers);
  232. *ppPerfLogUsers = NULL;
  233. }
  234. }
  235. return hr;
  236. }
  237. //
  238. // InitSD
  239. //
  240. // Creates a SECURITY_DESCRIPTOR with specific DACLs.
  241. //
  242. inline HRESULT CSharedMemBlock::InitSD()
  243. {
  244. HRESULT hr = S_OK;
  245. PSID pAuthenticatedUsers = NULL;
  246. PSID pBuiltInAdministrators = NULL;
  247. PSID pPowerUsers = NULL;
  248. PSID pMonUsers = NULL;
  249. PSID pLogUsers = NULL;
  250. PSECURITY_DESCRIPTOR pSD = NULL;
  251. if (m_sa.lpSecurityDescriptor != NULL) {
  252. return S_OK;
  253. }
  254. if (FAILED(hr = CreateSids(&pBuiltInAdministrators,
  255. &pPowerUsers,
  256. &pAuthenticatedUsers,
  257. &pMonUsers,
  258. &pLogUsers)));
  259. else {
  260. //
  261. // Calculate the size of and allocate a buffer for the DACL, we need
  262. // this value independently of the total alloc size for ACL init.
  263. //
  264. ULONG AclSize;
  265. //
  266. // "- sizeof (ULONG)" represents the SidStart field of the
  267. // ACCESS_ALLOWED_ACE. Since we're adding the entire length of the
  268. // SID, this field is counted twice.
  269. //
  270. AclSize = sizeof (ACL) +
  271. (5 * (sizeof (ACCESS_ALLOWED_ACE) - sizeof (ULONG))) +
  272. GetLengthSid(pAuthenticatedUsers) +
  273. GetLengthSid(pBuiltInAdministrators) +
  274. GetLengthSid(pPowerUsers) +
  275. GetLengthSid(pMonUsers) +
  276. GetLengthSid(pLogUsers);
  277. pSD = malloc(SECURITY_DESCRIPTOR_MIN_LENGTH + AclSize);
  278. if (!pSD) {
  279. hr = E_OUTOFMEMORY;
  280. } else {
  281. ACL *Acl;
  282. Acl = (ACL *)((BYTE *)pSD + SECURITY_DESCRIPTOR_MIN_LENGTH);
  283. if (!InitializeAcl(Acl,
  284. AclSize,
  285. ACL_REVISION)) {
  286. hr = HRESULT_FROM_WIN32(GetLastError());
  287. } else if (!AddAccessAllowedAce(Acl,
  288. ACL_REVISION,
  289. SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
  290. pAuthenticatedUsers)) {
  291. hr = HRESULT_FROM_WIN32(GetLastError());
  292. } else if (!AddAccessAllowedAce(Acl,
  293. ACL_REVISION,
  294. SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
  295. pPowerUsers)) {
  296. hr = HRESULT_FROM_WIN32(GetLastError());
  297. } else if (!AddAccessAllowedAce(Acl,
  298. ACL_REVISION,
  299. SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
  300. pBuiltInAdministrators)) {
  301. hr = HRESULT_FROM_WIN32(GetLastError());
  302. } else if (!AddAccessAllowedAce(Acl,
  303. ACL_REVISION,
  304. SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
  305. pMonUsers)) {
  306. hr = HRESULT_FROM_WIN32(GetLastError());
  307. } else if (!AddAccessAllowedAce(Acl,
  308. ACL_REVISION,
  309. SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
  310. pLogUsers)) {
  311. hr = HRESULT_FROM_WIN32(GetLastError());
  312. } else if (!InitializeSecurityDescriptor(pSD,
  313. SECURITY_DESCRIPTOR_REVISION)) {
  314. hr = HRESULT_FROM_WIN32(GetLastError());
  315. } else if (!SetSecurityDescriptorDacl(pSD,
  316. TRUE,
  317. Acl,
  318. FALSE)) {
  319. hr = HRESULT_FROM_WIN32(GetLastError());
  320. }
  321. }
  322. }
  323. if (pAuthenticatedUsers)
  324. FreeSid(pAuthenticatedUsers);
  325. if (pBuiltInAdministrators)
  326. FreeSid(pBuiltInAdministrators);
  327. if (pPowerUsers)
  328. FreeSid(pPowerUsers);
  329. if (pMonUsers)
  330. FreeSid(pMonUsers);
  331. if (pLogUsers)
  332. FreeSid(pLogUsers);
  333. if (FAILED(hr) && pSD) {
  334. free(pSD);
  335. pSD = NULL;
  336. }
  337. m_sa.lpSecurityDescriptor = pSD;
  338. return hr;
  339. }
  340. inline HRESULT CSharedMemBlock::InitMap
  341. (
  342. LPCSTR szName,
  343. DWORD dwSize,
  344. BOOL bCreate /* = TRUE */
  345. )
  346. {
  347. HRESULT hr = S_OK;
  348. if (FAILED(hr = InitSD())) {
  349. return hr;
  350. }
  351. // If we are suppose to be the one's creating the memory,
  352. // then make sure that we are.
  353. if ( bCreate )
  354. {
  355. m_hMemory = CreateFileMappingA
  356. (
  357. INVALID_HANDLE_VALUE,
  358. &m_sa,
  359. PAGE_READWRITE,
  360. 0,
  361. dwSize,
  362. szName
  363. );
  364. if ( m_hMemory == NULL )
  365. {
  366. return HRESULT_FROM_WIN32( GetLastError () );
  367. }
  368. else
  369. {
  370. if ( GetLastError() == ERROR_ALREADY_EXISTS )
  371. {
  372. CloseHandle ( m_hMemory );
  373. m_hMemory = NULL;
  374. return HRESULT_FROM_WIN32( ERROR_ALREADY_EXISTS );
  375. }
  376. }
  377. }
  378. else
  379. {
  380. // Try to open existing
  381. m_hMemory = OpenFileMappingA
  382. (
  383. FILE_MAP_WRITE | FILE_MAP_READ,
  384. FALSE,
  385. szName
  386. );
  387. if (!m_hMemory)
  388. return E_FAIL;
  389. }
  390. m_pMemory = MapViewOfFile
  391. (
  392. m_hMemory,
  393. FILE_MAP_ALL_ACCESS,
  394. 0,
  395. 0,
  396. 0
  397. );
  398. if (!m_pMemory)
  399. {
  400. UnInitMap();
  401. return E_FAIL;
  402. }
  403. if (bCreate)
  404. memset(m_pMemory, 0, dwSize);
  405. return S_OK;
  406. }
  407. inline HRESULT CSharedMemBlock::UnInitMap()
  408. {
  409. if (m_pMemory)
  410. {
  411. UnmapViewOfFile(m_pMemory);
  412. m_pMemory = NULL;
  413. }
  414. if (m_hMemory)
  415. {
  416. CloseHandle(m_hMemory);
  417. m_hMemory = NULL;
  418. }
  419. return S_OK;
  420. }
  421. /*===================================================================
  422. CPerfProcBlock - class representing pref data for a single process
  423. ===================================================================*/
  424. class CPerfProcBlock : public CSharedMemBlock
  425. {
  426. friend class CPerfMainBlock;
  427. friend class CASPPerfManager;
  428. #ifndef _PERF_CMD
  429. protected:
  430. #else
  431. public:
  432. #endif
  433. DWORD m_fInited : 1;
  434. DWORD m_fMemCSInited : 1;
  435. DWORD m_fReqCSInited : 1;
  436. DWORD m_fProcessDead : 1;
  437. HANDLE m_hWaitHandle;
  438. // critical sections (only used in ASP.DLL)
  439. CRITICAL_SECTION m_csMemLock; // CS for memory counters
  440. CRITICAL_SECTION m_csReqLock; // CS for per-request counters
  441. // block of counters
  442. CPerfProcBlockData *m_pData;
  443. // next process data (used in ASPPERF.DLL/WAS)
  444. CPerfProcBlock *m_pNext;
  445. // access shared memory
  446. HRESULT MapMemory(DWORD procId, BOOL bCreate = TRUE);
  447. static VOID CALLBACK WaitCallback(PVOID pArg, BOOLEAN fReason);
  448. public:
  449. inline CPerfProcBlock()
  450. : m_fInited(FALSE),
  451. m_fMemCSInited(FALSE), m_fReqCSInited(FALSE),
  452. m_fProcessDead(FALSE),
  453. m_hWaitHandle(NULL),
  454. m_pData(NULL), m_pNext(NULL)
  455. {}
  456. inline ~CPerfProcBlock() { UnInit(); }
  457. HRESULT InitCriticalSections();
  458. HRESULT UnInitCriticalSections();
  459. HRESULT InitExternal(DWORD procId); // from ASPPERF.DLL
  460. HRESULT InitForThisProcess // from ASP.DLL
  461. (
  462. DWORD procId,
  463. DWORD *pdwInitCounters = NULL
  464. );
  465. HRESULT UnInit();
  466. };
  467. inline HRESULT CPerfProcBlock::MapMemory
  468. (
  469. DWORD procId,
  470. BOOL bCreate /* = TRUE */
  471. )
  472. {
  473. // Construct unique map name with CLSID
  474. char szMapName[CCH_PERF_PROC_FILEMAP_PREFIX+32+1];
  475. strcpy(szMapName, SZ_PERF_PROC_FILEMAP_PREFIX);
  476. char *pszHex = szMapName + CCH_PERF_PROC_FILEMAP_PREFIX;
  477. sprintf(pszHex, "%08x", procId);
  478. // create or open the map
  479. HRESULT hr = InitMap(szMapName, CB_PERF_PROC_BLOCK, bCreate);
  480. if (SUCCEEDED(hr))
  481. {
  482. m_pData = (CPerfProcBlockData *)PMemory();
  483. if (m_pData->m_dwProcId == 0)
  484. m_pData->m_dwProcId = procId;
  485. else if (m_pData->m_dwProcId != procId)
  486. hr = E_FAIL; // cls id mismatch
  487. }
  488. return hr;
  489. }
  490. inline HRESULT CPerfProcBlock::InitCriticalSections()
  491. {
  492. HRESULT hr = S_OK;
  493. if (!m_fMemCSInited)
  494. {
  495. __try { INITIALIZE_CRITICAL_SECTION(&m_csMemLock); }
  496. __except(1) { hr = E_UNEXPECTED; }
  497. if (SUCCEEDED(hr))
  498. m_fMemCSInited = TRUE;
  499. else
  500. return hr;
  501. }
  502. if (!m_fReqCSInited)
  503. {
  504. __try { INITIALIZE_CRITICAL_SECTION(&m_csReqLock); }
  505. __except(1) { hr = E_UNEXPECTED; }
  506. if (SUCCEEDED(hr))
  507. m_fReqCSInited = TRUE;
  508. else
  509. return hr;
  510. }
  511. return S_OK;
  512. }
  513. inline HRESULT CPerfProcBlock::UnInitCriticalSections()
  514. {
  515. if (m_fMemCSInited)
  516. {
  517. DeleteCriticalSection(&m_csMemLock);
  518. m_fMemCSInited = FALSE;
  519. }
  520. if (m_fReqCSInited)
  521. {
  522. DeleteCriticalSection(&m_csReqLock);
  523. m_fReqCSInited = FALSE;
  524. }
  525. return S_OK;
  526. }
  527. inline HRESULT CPerfProcBlock::InitExternal
  528. (
  529. DWORD procId
  530. )
  531. {
  532. HRESULT hr = MapMemory(procId, FALSE);
  533. if (SUCCEEDED(hr))
  534. m_fInited = TRUE;
  535. else
  536. UnInit();
  537. return hr;
  538. }
  539. inline HRESULT CPerfProcBlock::InitForThisProcess
  540. (
  541. DWORD procId,
  542. DWORD *pdwInitCounters
  543. )
  544. {
  545. HRESULT hr = S_OK;
  546. // Map the shared memory
  547. if (SUCCEEDED(hr))
  548. hr = MapMemory(procId, TRUE);
  549. if (SUCCEEDED(hr))
  550. {
  551. // init the counters
  552. if (pdwInitCounters)
  553. memcpy(m_pData->m_rgdwCounters, pdwInitCounters, CB_COUNTERS);
  554. else
  555. memset(m_pData->m_rgdwCounters, 0, CB_COUNTERS);
  556. m_fInited = TRUE;
  557. }
  558. else
  559. {
  560. UnInit();
  561. }
  562. return hr;
  563. }
  564. inline HRESULT CPerfProcBlock::UnInit()
  565. {
  566. UnInitMap();
  567. m_pData = NULL;
  568. m_pNext = NULL;
  569. m_fInited = FALSE;
  570. return S_OK;
  571. }
  572. inline VOID CALLBACK CPerfProcBlock::WaitCallback(PVOID pArg, BOOLEAN fReason)
  573. {
  574. CPerfProcBlock *pPerfBlock = (CPerfProcBlock *)pArg;
  575. pPerfBlock->m_fProcessDead = TRUE;
  576. }
  577. /*===================================================================
  578. CPerfMainBlock - class representing the main perf data
  579. ===================================================================*/
  580. class CPerfMainBlock : public CSharedMemBlock
  581. {
  582. friend CASPPerfManager;
  583. #ifndef _PERF_CMD
  584. private:
  585. #else
  586. public:
  587. #endif
  588. DWORD m_fInited : 1;
  589. // the process block directory
  590. CPerfMainBlockData *m_pData;
  591. // mutex to access the process block directory
  592. HANDLE m_hMutex;
  593. HANDLE m_hChangeEvent;
  594. HANDLE m_WASProcessHandle;
  595. // first process data (used in ASPPERF.DLL)
  596. CPerfProcBlock *m_pProcBlock;
  597. // timestamp of main block when the list of process blocks
  598. // last loaded -- to make decide to reload (ASPPREF.DLL only)
  599. DWORD m_dwTimestamp;
  600. public:
  601. inline CPerfMainBlock()
  602. : m_fInited(FALSE),
  603. m_hChangeEvent(NULL),
  604. m_pData(NULL),
  605. m_hMutex(NULL),
  606. m_pProcBlock(NULL),
  607. m_dwTimestamp(NULL),
  608. m_WASProcessHandle(NULL)
  609. {}
  610. inline ~CPerfMainBlock() { UnInit(); }
  611. HRESULT Init(BOOL bWASInit = FALSE);
  612. HRESULT UnInit();
  613. // lock / unlock using mutex
  614. HRESULT Lock();
  615. HRESULT UnLock();
  616. // add/remove process record to the main block (used from ASP.DLL)
  617. HRESULT AddProcess(DWORD procId);
  618. // load CPerfProcBlock blocks from the main block into
  619. // objects (used from APPPREF.DLL)
  620. HRESULT Load();
  621. // gather (sum-up) the statistics from each proc block
  622. HRESULT GetStats(DWORD *pdwCounters);
  623. // copies the counters from a process that is going away into
  624. // the shared array of the accumulated counters from dead processes.
  625. // Used from WAS.
  626. VOID AggregateDeadProcCounters(CPerfProcBlock *pBlock);
  627. HRESULT CreateChangeEvent(BOOL bMustCreate);
  628. VOID SetChangeEvent() { SetEvent(m_hChangeEvent); }
  629. HANDLE GetWASProcessHandle();
  630. };
  631. inline
  632. HRESULT CPerfMainBlock::Init(BOOL bWASInit)
  633. {
  634. HRESULT hr = S_OK;
  635. if (FAILED(hr = InitSD()))
  636. {
  637. return hr;
  638. }
  639. // only WAS can create the Mutex, others have to just open it
  640. if (bWASInit)
  641. {
  642. m_hMutex = CreateMutexA(&m_sa, FALSE, SZ_PERF_MUTEX);
  643. if (m_hMutex == NULL )
  644. {
  645. hr = HRESULT_FROM_WIN32( GetLastError () );
  646. }
  647. // If we got it, but we didn't create it then throw it
  648. // back. Only WAS can create this.
  649. else if ( GetLastError() == ERROR_ALREADY_EXISTS )
  650. {
  651. hr = HRESULT_FROM_WIN32( GetLastError () );
  652. CloseHandle( m_hMutex );
  653. m_hMutex = NULL;
  654. }
  655. }
  656. else
  657. {
  658. m_hMutex = OpenMutexA(SYNCHRONIZE, FALSE, SZ_PERF_MUTEX);
  659. }
  660. if (!m_hMutex)
  661. {
  662. hr = E_FAIL;
  663. }
  664. if (SUCCEEDED(hr))
  665. {
  666. hr = InitMap(SZ_PERF_MAIN_FILEMAP, CB_PERF_MAIN_BLOCK, bWASInit);
  667. if (SUCCEEDED(hr))
  668. {
  669. m_pData = (CPerfMainBlockData *)PMemory();
  670. // We got the memory mapped, so set the WAS PID into it
  671. // if we are setting up the WAS side, otherwise read
  672. // the value in for others to use.
  673. if ( bWASInit )
  674. {
  675. m_pData->m_dwWASPid = GetCurrentProcessId();
  676. }
  677. }
  678. }
  679. if (SUCCEEDED(hr))
  680. {
  681. hr = CreateChangeEvent(bWASInit);
  682. }
  683. if (SUCCEEDED(hr))
  684. {
  685. m_fInited = TRUE;
  686. }
  687. else
  688. {
  689. UnInit();
  690. }
  691. return hr;
  692. }
  693. inline HRESULT CPerfMainBlock::UnInit()
  694. {
  695. // DBGPRINTF((DBG_CONTEXT, "Cleaning up ProcBlocks\n"));
  696. while (m_pProcBlock)
  697. {
  698. CPerfProcBlock *pNext = m_pProcBlock->m_pNext;
  699. m_pProcBlock->UnInit();
  700. delete m_pProcBlock;
  701. m_pProcBlock = pNext;
  702. }
  703. // DBGPRINTF((DBG_CONTEXT, "Cleaning up mutex\n"));
  704. if (m_hMutex)
  705. {
  706. CloseHandle(m_hMutex);
  707. m_hMutex = NULL;
  708. }
  709. // DBGPRINTF((DBG_CONTEXT, "Uninit'ing map\n"));
  710. UnInitMap();
  711. m_dwTimestamp = 0;
  712. m_pData = NULL;
  713. m_pProcBlock = NULL;
  714. m_fInited = FALSE;
  715. // close its handle
  716. // DBGPRINTF((DBG_CONTEXT, "Closing ChangeEvent Handle\n"));
  717. if (m_hChangeEvent != NULL)
  718. CloseHandle(m_hChangeEvent);
  719. m_hChangeEvent = NULL;
  720. if ( m_WASProcessHandle != NULL )
  721. {
  722. CloseHandle ( m_WASProcessHandle );
  723. m_WASProcessHandle = NULL;
  724. }
  725. return S_OK;
  726. }
  727. inline HRESULT CPerfMainBlock::CreateChangeEvent(BOOL bMustCreate)
  728. {
  729. // Create the changed event using the standard SD. Make the
  730. // reset Automatic and initial state to unsignalled.
  731. if (bMustCreate) {
  732. m_hChangeEvent = CreateEventA(&m_sa,
  733. FALSE,
  734. FALSE,
  735. SZ_PERF_ADD_EVENT);
  736. // if GetLastError indicates that the handle already exists, this
  737. // is bad. Return an error. This process should always be the creator
  738. // of the event.
  739. if ((GetLastError() == ERROR_ALREADY_EXISTS)) {
  740. CloseHandle(m_hChangeEvent);
  741. m_hChangeEvent = NULL;
  742. return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
  743. }
  744. }
  745. else {
  746. m_hChangeEvent = OpenEventA(EVENT_MODIFY_STATE,
  747. FALSE,
  748. SZ_PERF_ADD_EVENT);
  749. }
  750. if (m_hChangeEvent == NULL)
  751. return HRESULT_FROM_WIN32(GetLastError());
  752. return S_OK;
  753. }
  754. inline
  755. HANDLE CPerfMainBlock::GetWASProcessHandle()
  756. {
  757. HRESULT hr = S_OK;
  758. if ( m_WASProcessHandle == NULL )
  759. {
  760. m_WASProcessHandle = OpenProcess ( SYNCHRONIZE, // security
  761. FALSE, // not inheritable
  762. m_pData->m_dwWASPid);
  763. // If we failed to open the process the handle will be null.
  764. // This will be checked for by the caller.
  765. }
  766. return m_WASProcessHandle;
  767. }
  768. inline HRESULT CPerfMainBlock::Lock()
  769. {
  770. if (!m_hMutex)
  771. return E_FAIL;
  772. if (WaitForSingleObject(m_hMutex, PERM_MUTEX_WAIT) == WAIT_TIMEOUT)
  773. return E_FAIL;
  774. return S_OK;
  775. }
  776. inline HRESULT CPerfMainBlock::UnLock()
  777. {
  778. if (m_hMutex)
  779. ReleaseMutex(m_hMutex);
  780. return S_OK;
  781. }
  782. inline HRESULT CPerfMainBlock::AddProcess
  783. (
  784. DWORD procId
  785. )
  786. {
  787. if (!m_fInited)
  788. return E_FAIL;
  789. if (FAILED(Lock())) // lock mutex
  790. return E_FAIL;
  791. HRESULT hr = S_OK;
  792. BOOL fFound = FALSE;
  793. DWORD idx = 0;
  794. // find
  795. for (DWORD cnt = min(m_pData->m_cItems,C_PERF_PROC_MAX); idx < cnt; idx++)
  796. {
  797. if (m_pData->m_dwProcIds[idx] == procId)
  798. {
  799. fFound = TRUE;
  800. break;
  801. }
  802. }
  803. // add only if not already there
  804. if (!fFound)
  805. {
  806. if (idx < C_PERF_PROC_MAX)
  807. {
  808. m_pData->m_dwProcIds[idx] = procId;
  809. m_pData->m_cItems = idx + 1;
  810. m_pData->m_dwTimestamp = GetTickCount();
  811. SetChangeEvent();
  812. }
  813. else
  814. {
  815. hr = E_OUTOFMEMORY;
  816. }
  817. }
  818. UnLock(); // unlock mutex
  819. return hr;
  820. }
  821. inline HRESULT CPerfMainBlock::Load()
  822. {
  823. if (!m_fInited)
  824. return E_FAIL;
  825. if (m_dwTimestamp == m_pData->m_dwTimestamp)
  826. return S_OK; // already up-to-date
  827. // clear out what we have
  828. while (m_pProcBlock)
  829. {
  830. CPerfProcBlock *pNext = m_pProcBlock->m_pNext;
  831. m_pProcBlock->UnInit();
  832. delete m_pProcBlock;
  833. m_pProcBlock = pNext;
  834. }
  835. if (FAILED(Lock())) // lock mutex
  836. return E_FAIL;
  837. HRESULT hr = S_OK;
  838. // populate new objects for blocks
  839. for (DWORD i = 0, cnt = min(m_pData->m_cItems,C_PERF_PROC_MAX); i < cnt;)
  840. {
  841. CPerfProcBlock *pBlock = new CPerfProcBlock;
  842. if (!pBlock)
  843. {
  844. hr = E_OUTOFMEMORY;
  845. break;
  846. }
  847. hr = pBlock->InitExternal(m_pData->m_dwProcIds[i]);
  848. if (FAILED(hr))
  849. {
  850. delete pBlock;
  851. hr = S_OK;
  852. cnt--;
  853. m_pData->m_cItems--;
  854. for (DWORD j = i; j < min(m_pData->m_cItems,C_PERF_PROC_MAX); j++) {
  855. m_pData->m_dwProcIds[j] = m_pData->m_dwProcIds[j+1];
  856. }
  857. continue;
  858. }
  859. pBlock->m_pNext = m_pProcBlock;
  860. m_pProcBlock = pBlock;
  861. i++;
  862. }
  863. // remember timestamp
  864. m_dwTimestamp = SUCCEEDED(hr) ? m_pData->m_dwTimestamp : 0;
  865. UnLock(); // unlock mutex
  866. return hr;
  867. }
  868. inline HRESULT CPerfMainBlock::GetStats
  869. (
  870. DWORD *pdwCounters
  871. )
  872. {
  873. if (!m_fInited)
  874. return E_FAIL;
  875. // reload if needed
  876. if (FAILED(Load()))
  877. return E_FAIL;
  878. // first add in the accumulated stats from the dead procs...
  879. for (int i = 0; i < C_PERF_PROC_COUNTERS; i++)
  880. pdwCounters[i] = m_pData->m_rgdwCounters[i];
  881. // gather
  882. CPerfProcBlock *pBlock = m_pProcBlock;
  883. while (pBlock) {
  884. if (pBlock->m_fProcessDead) {
  885. m_dwTimestamp = 0;
  886. pBlock = pBlock->m_pNext;
  887. continue;
  888. }
  889. for (int i = 0; i < C_PERF_PROC_COUNTERS; i++)
  890. pdwCounters[i] += pBlock->m_pData->m_rgdwCounters[i];
  891. pBlock = pBlock->m_pNext;
  892. }
  893. return S_OK;
  894. }
  895. inline VOID CPerfMainBlock::AggregateDeadProcCounters(CPerfProcBlock *pBlock)
  896. {
  897. DWORD *pOut = m_pData->m_rgdwCounters;
  898. DWORD *pIn = pBlock->m_pData->m_rgdwCounters;
  899. // DBGPRINTF((DBG_CONTEXT, "Aggregating Dead Proc Counters\n"));
  900. pOut[ID_DEBUGDOCREQ] += pIn[ID_DEBUGDOCREQ];
  901. pOut[ID_REQERRRUNTIME] += pIn[ID_REQERRRUNTIME];
  902. pOut[ID_REQERRPREPROC] += pIn[ID_REQERRPREPROC];
  903. pOut[ID_REQERRCOMPILE] += pIn[ID_REQERRCOMPILE];
  904. pOut[ID_REQERRORPERSEC] += pIn[ID_REQERRORPERSEC];
  905. pOut[ID_REQTOTALBYTEIN] += pIn[ID_REQTOTALBYTEIN];
  906. pOut[ID_REQTOTALBYTEOUT] += pIn[ID_REQTOTALBYTEOUT];
  907. pOut[ID_REQCOMFAILED] += pIn[ID_REQCOMFAILED];
  908. pOut[ID_REQFAILED] += pIn[ID_REQFAILED];
  909. pOut[ID_REQNOTAUTH] += pIn[ID_REQNOTAUTH];
  910. pOut[ID_REQREJECTED] += pIn[ID_REQREJECTED];
  911. pOut[ID_REQSUCCEEDED] += pIn[ID_REQSUCCEEDED];
  912. pOut[ID_REQTIMEOUT] += pIn[ID_REQTIMEOUT];
  913. pOut[ID_REQTOTAL] += pIn[ID_REQTOTAL];
  914. pOut[ID_REQPERSEC] += pIn[ID_REQPERSEC];
  915. pOut[ID_SESSIONTIMEOUT] += pIn[ID_SESSIONTIMEOUT];
  916. pOut[ID_TEMPLFLUSHES] += pIn[ID_TEMPLFLUSHES];
  917. pOut[ID_TRANSABORTED] += pIn[ID_TRANSABORTED];
  918. pOut[ID_TRANSCOMMIT] += pIn[ID_TRANSCOMMIT];
  919. pOut[ID_TRANSTOTAL] += pIn[ID_TRANSTOTAL];
  920. pOut[ID_ENGINEFLUSHES] += pIn[ID_ENGINEFLUSHES];
  921. }
  922. /******************************************************************************
  923. Class Definition and support structures for the centralized Global Perf
  924. Counter structures. A single CASPPerfManager object will be declared and
  925. initialized in WAS. If in new mode, WAS will call the public ProcessDied()
  926. method to inform when a worker process should no longer be considered alive.
  927. If not in new mode, WAS does not know about the various ASP host processes
  928. and so RegisterWaitForSingleObject will be used to monitor the lifetime of
  929. the ASP host process.
  930. When a process is declared dead through one of these mechanism, the ASP
  931. counters associated with that process are moved into a global table to
  932. accummulate counter counter perf counter types - e.g. Total ASP Requests.
  933. A named event is used by the ASP host process to single that a new
  934. host process is up.
  935. ****************************************************************************/
  936. typedef struct {
  937. HANDLE hWaitHandle;
  938. HANDLE hProcHandle;
  939. CPerfProcBlock *pBlock;
  940. CASPPerfManager *pPerfGlobal;
  941. } sWaitInfo;
  942. class CASPPerfManager
  943. {
  944. public:
  945. CASPPerfManager() :
  946. m_hChangeWaitHandle(NULL),
  947. m_dwCntProcsDied(0),
  948. m_fcsProcsDiedInited(0),
  949. m_fCompatMode(0),
  950. m_fInited(0)
  951. {
  952. ZeroMemory(m_dwProcIdInWaitState, sizeof(m_dwProcIdInWaitState));
  953. ZeroMemory(m_aWaitInfos, sizeof(m_aWaitInfos));
  954. }
  955. HRESULT Init(BOOL bCompatMode);
  956. HRESULT UnInit();
  957. VOID ProcessDied(DWORD procId);
  958. private:
  959. CPerfMainBlock m_MainBlock;
  960. HANDLE m_hChangeWaitHandle;
  961. DWORD m_fcsProcsDiedInited : 1;
  962. DWORD m_fCompatMode : 1;
  963. DWORD m_fInited : 1;
  964. // Booleans to track status of Process IDs in above array
  965. BOOL m_dwProcIdInWaitState[C_PERF_PROC_MAX];
  966. // array of structures tracking the WaitInfo data
  967. sWaitInfo *m_aWaitInfos[C_PERF_PROC_MAX];
  968. // array of proc IDs, protected by CritSec, of dead procs
  969. CRITICAL_SECTION m_csProcsDied;
  970. DWORD m_dwProcsDied[C_PERF_PROC_MAX];
  971. DWORD m_dwCntProcsDied;
  972. static VOID CALLBACK ChangeEventWaitCallback(PVOID pArg, BOOLEAN fReason);
  973. static VOID CALLBACK ProcDiedCallback(PVOID pArg, BOOLEAN fReason);
  974. VOID ScanForNewProcIDs();
  975. VOID HandleDeadProcIDs();
  976. HRESULT RegisterWaitOnProcess(sWaitInfo *pWaitInfo);
  977. VOID AddProcDiedToList(DWORD procID);
  978. };
  979. inline HRESULT CASPPerfManager::Init(BOOL bCompatMode)
  980. {
  981. HRESULT hr = S_OK;
  982. // DBGPRINTF((DBG_CONTEXT, "Initializing CASPPerfManager\n"));
  983. m_fCompatMode = bCompatMode;
  984. // initialize the MainBlock. TRUE here indicates that this is
  985. // a WAS init and the expectation is that the global shared
  986. // memory is created by this process.
  987. hr = m_MainBlock.Init(TRUE);
  988. if (FAILED(hr)) {
  989. DBGPRINTF((DBG_CONTEXT, "Initializing CASPPerfManager FAILED (%x)\n",hr));
  990. return hr;
  991. }
  992. // Use the RegisterWaitForSingleObject() API to handle the event
  993. // firing. Relieves us of the burden of managing a thread.
  994. if (SUCCEEDED(hr)
  995. && RegisterWaitForSingleObject(&m_hChangeWaitHandle,
  996. m_MainBlock.m_hChangeEvent,
  997. ChangeEventWaitCallback,
  998. this,
  999. INFINITE,
  1000. WT_EXECUTEINIOTHREAD) == FALSE) {
  1001. hr = HRESULT_FROM_WIN32(GetLastError());
  1002. }
  1003. // Initialize the CriticalSection used to added dead proc ids to
  1004. // the deadprocids array
  1005. if (SUCCEEDED(hr)) {
  1006. ErrInitCriticalSection(&m_csProcsDied, hr);
  1007. if (SUCCEEDED(hr))
  1008. m_fcsProcsDiedInited = TRUE;
  1009. }
  1010. if (FAILED(hr)) {
  1011. DBGPRINTF((DBG_CONTEXT, "Initializing CASPPerfManager FAILED (%x)\n", hr));
  1012. m_MainBlock.UnInit();
  1013. }
  1014. if (SUCCEEDED(hr))
  1015. m_fInited = TRUE;
  1016. return hr;
  1017. }
  1018. inline HRESULT CASPPerfManager::UnInit()
  1019. {
  1020. m_fInited = FALSE;
  1021. // DBGPRINTF((DBG_CONTEXT, "Uninitializing CASPPerfManager\n"));
  1022. // unregister the ChangeEvent wait, if we pass INVALID_HANDLE_VALUE
  1023. // the routine will block until all callbacks have completed before
  1024. // returning
  1025. // Note: We intentionally don't check the return value for the
  1026. // UnregisterWaitEx, because there is nothing we can do if it fails
  1027. if (m_hChangeWaitHandle != NULL) {
  1028. UnregisterWaitEx(m_hChangeWaitHandle, INVALID_HANDLE_VALUE);
  1029. }
  1030. // DBGPRINTF((DBG_CONTEXT, "Unregistered ChangeWait\n"));
  1031. // DBGPRINTF((DBG_CONTEXT, "WaitForSingleObject on unregister completed\n"));
  1032. // clean up the WaitInfo array
  1033. for (DWORD i=0; m_aWaitInfos[i]; i++) {
  1034. //
  1035. // UnregisterWaitEx will wait for all callback routines to complete
  1036. // before returning if the INVALID_HANDLE_VALUE is passed in.
  1037. // Note: We intentionally don't check the return value for the
  1038. // UnregisterWaitEx, because there is nothing we can do if it fails
  1039. UnregisterWaitEx(m_aWaitInfos[i]->hWaitHandle, INVALID_HANDLE_VALUE);
  1040. CloseHandle(m_aWaitInfos[i]->hProcHandle);
  1041. delete m_aWaitInfos[i];
  1042. }
  1043. // DBGPRINTF((DBG_CONTEXT, "Cleaned up WaitInfos\n"));
  1044. // if successfully created the ProcsDiedCS, clean it up
  1045. if (m_fcsProcsDiedInited == TRUE)
  1046. DeleteCriticalSection(&m_csProcsDied);
  1047. // DBGPRINTF((DBG_CONTEXT, "Calling m_MainBlock.UnInit\n"));
  1048. return m_MainBlock.UnInit();
  1049. }
  1050. inline VOID CALLBACK CASPPerfManager::ChangeEventWaitCallback(PVOID pArg, BOOLEAN fReason)
  1051. {
  1052. // DBGPRINTF((DBG_CONTEXT, "ChangeEventWaitCallback called\n"));
  1053. CASPPerfManager *pPerfGlob = (CASPPerfManager *)pArg;
  1054. // when the ChangeEvent fires, check for new ProcIDs in the global
  1055. // array and then check for DeadProcIDs.
  1056. pPerfGlob->ScanForNewProcIDs();
  1057. pPerfGlob->HandleDeadProcIDs();
  1058. }
  1059. inline VOID CASPPerfManager::ScanForNewProcIDs()
  1060. {
  1061. HRESULT hr;
  1062. // DBGPRINTF((DBG_CONTEXT, "Scanning for New Proc IDS\n"));
  1063. // We'll need to hold the lock the entire time we're
  1064. // looking thru the list
  1065. m_MainBlock.Lock();
  1066. // start from the back looking for entries that haven't had
  1067. // their WaitState set. Again note that there is always the
  1068. // key assumption that the three arrays, m_dwProcIdInWaitState
  1069. // m_aWaitInfo and the m_dwProcIDs array in the global array
  1070. // track each other with respect to position in array.
  1071. for (LONG i = m_MainBlock.m_pData->m_cItems - 1; i >= 0; i--) {
  1072. // as soon as we hit one that is in the Wait state,
  1073. // we're done.
  1074. if (m_dwProcIdInWaitState[i] == TRUE) {
  1075. // DBGPRINTF((DBG_CONTEXT, "Done Scanning for New Proc IDS\n"));
  1076. break;
  1077. }
  1078. // DBGPRINTF((DBG_CONTEXT, "Found a new Proc ID at idx - %d\n", i));
  1079. // found one that is not waiting. Build up the necessary
  1080. // structures and objects
  1081. // we'll need another CPerfProcBlock for the list
  1082. CPerfProcBlock *pBlock = new CPerfProcBlock;
  1083. if (!pBlock) {
  1084. break;
  1085. }
  1086. // we'll also need a new WaitInfo, if in backwards
  1087. // compat mode. Remember, in backwards compat mode,
  1088. // this object will do all the register for waits, but
  1089. // in new mode, WAS will tell us when a process has died.
  1090. sWaitInfo *pWaitInfo = NULL;
  1091. if (m_fCompatMode) {
  1092. pWaitInfo = new sWaitInfo;
  1093. if (!pWaitInfo) {
  1094. delete pBlock;
  1095. break;
  1096. }
  1097. pWaitInfo->pPerfGlobal = this;
  1098. pWaitInfo->pBlock = pBlock;
  1099. }
  1100. // call InitExternal to hook up to the ASP host processes
  1101. // shared memory. This is where we will get that needed handle
  1102. // to the shared memory and will allow us to access the shared
  1103. // memory even after the process itself has terminated.
  1104. if (FAILED(hr = pBlock->InitExternal(m_MainBlock.m_pData->m_dwProcIds[i]))) {
  1105. // DBGPRINTF((DBG_CONTEXT, "InitExternal failed in ScanForNewProcIDS (%x)\n", hr));
  1106. delete pWaitInfo;
  1107. delete pBlock;
  1108. continue;
  1109. }
  1110. // Register the Wait if in compatmode
  1111. if (m_fCompatMode && FAILED(hr = RegisterWaitOnProcess(pWaitInfo))) {
  1112. // DBGPRINTF((DBG_CONTEXT, "RegisterWait failed in ScanForNewProcIDs (%x)\n", hr));
  1113. pBlock->UnInit();
  1114. delete pWaitInfo;
  1115. delete pBlock;
  1116. continue;
  1117. }
  1118. // Since it's a single linked list, just put the new block
  1119. // at the head of the list.
  1120. pBlock->m_pNext = m_MainBlock.m_pProcBlock;
  1121. m_MainBlock.m_pProcBlock = pBlock;
  1122. // note that this procID is now in a wait state
  1123. m_dwProcIdInWaitState[i] = TRUE;
  1124. // Add the WaitInfo to the array.
  1125. m_aWaitInfos[i] = pWaitInfo;
  1126. }
  1127. m_MainBlock.UnLock();
  1128. return;
  1129. }
  1130. inline HRESULT CASPPerfManager::RegisterWaitOnProcess(sWaitInfo *pWaitInfo)
  1131. {
  1132. HRESULT hr = S_OK;
  1133. // get a handle to the process to wait on
  1134. pWaitInfo->hProcHandle = OpenProcess(SYNCHRONIZE,
  1135. FALSE,
  1136. pWaitInfo->pBlock->m_pData->m_dwProcId);
  1137. if (!pWaitInfo->hProcHandle) {
  1138. hr = HRESULT_FROM_WIN32(GetLastError());
  1139. }
  1140. else {
  1141. // register the wait.
  1142. if (!RegisterWaitForSingleObject(&pWaitInfo->hWaitHandle, // wait handle
  1143. pWaitInfo->hProcHandle, // handle to object
  1144. CASPPerfManager::ProcDiedCallback, // timer callback function
  1145. pWaitInfo, // callback function parameter
  1146. INFINITE, // time-out interval
  1147. WT_EXECUTEONLYONCE)) { // options
  1148. CloseHandle(pWaitInfo->hProcHandle);
  1149. pWaitInfo->hProcHandle = NULL;
  1150. hr = HRESULT_FROM_WIN32(GetLastError());
  1151. }
  1152. }
  1153. return hr;
  1154. }
  1155. inline VOID CALLBACK CASPPerfManager::ProcDiedCallback(PVOID pArg, BOOLEAN fReason)
  1156. {
  1157. sWaitInfo *pWaitInfo = (sWaitInfo *)pArg;
  1158. // DBGPRINTF((DBG_CONTEXT, "ProcDiedCallback enterred\n"));
  1159. // The callback will simply call the public ProcessDied method on the
  1160. // PerfGlobal object. This is for simplicity. There is no reason why the
  1161. // process couldn't be cleaned up on this thread.
  1162. pWaitInfo->pPerfGlobal->ProcessDied(pWaitInfo->pBlock->m_pData->m_dwProcId);
  1163. }
  1164. inline VOID CASPPerfManager::ProcessDied(DWORD procID)
  1165. {
  1166. // DBGPRINTF((DBG_CONTEXT, "CASPPerfManager::ProcessDied enterred for %d\n", procID));
  1167. if (m_fInited == FALSE)
  1168. return;
  1169. // Add the ProcID to the list of dead proc IDS and wakeup the
  1170. // change callback
  1171. AddProcDiedToList(procID);
  1172. m_MainBlock.SetChangeEvent();
  1173. return;
  1174. }
  1175. inline VOID CASPPerfManager::AddProcDiedToList(DWORD procID)
  1176. {
  1177. // DBGPRINTF((DBG_CONTEXT, "Adding Process (%d) to proc died list\n", procID));
  1178. // take the critical section, add the process to the list
  1179. // and leave the critical section.
  1180. EnterCriticalSection(&m_csProcsDied);
  1181. m_dwProcsDied[m_dwCntProcsDied++] = procID;
  1182. // DBGPRINTF((DBG_CONTEXT, "New count of ProcsDied list is %d\n",m_dwCntProcsDied));
  1183. LeaveCriticalSection(&m_csProcsDied);
  1184. }
  1185. inline VOID CASPPerfManager::HandleDeadProcIDs()
  1186. {
  1187. DWORD procID;
  1188. // DBGPRINTF((DBG_CONTEXT, "HandleDeadProcIDs Enterred\n"));
  1189. sWaitInfo *pWaitInfo = NULL;
  1190. CPerfProcBlock *pLast = NULL;
  1191. CPerfProcBlock *pBlock = NULL;
  1192. // Ok, this is the critical routine. It's here where
  1193. // we will handle the dead processes. Cleanup will occur on
  1194. // the various structures around this process as well as the
  1195. // aggregation of it's counters into the global shared memory.
  1196. // enter the critsec to check for dead procs to process
  1197. EnterCriticalSection(&m_csProcsDied);
  1198. // Enter a while loop to process all of the dead procs. Will also
  1199. // bail if we were uninited.
  1200. while(m_dwCntProcsDied && m_fInited) {
  1201. // DBGPRINTF((DBG_CONTEXT, "current m_dwCntProcsDies is %d\n", m_dwCntProcsDied));
  1202. // get a proc id from the list. Note that we start at
  1203. // the back so that we can release the critical section.
  1204. // The alternative would be to take it off the front, and
  1205. // then move all the remaining items forward. This seems
  1206. // unnecessary. There should be no issue with LIFO
  1207. // processing that I can see.
  1208. procID = m_dwProcsDied[--m_dwCntProcsDied];
  1209. // we can safely leave the critsec now that we've popped
  1210. // an entry of the end of the list
  1211. LeaveCriticalSection(&m_csProcsDied);
  1212. // now that we have the entry, we need to find it's position
  1213. // in the MainBlock. Need to hold the lock to do so.
  1214. m_MainBlock.Lock();
  1215. int iFound = -1;
  1216. DWORD idx, cnt;
  1217. // the search begins in the main shared array of procIDs
  1218. for (idx = 0, cnt = min(m_MainBlock.m_pData->m_cItems,C_PERF_PROC_MAX); idx < cnt; idx++) {
  1219. // break if we found it
  1220. if (m_MainBlock.m_pData->m_dwProcIds[idx] == procID) {
  1221. iFound = idx;
  1222. break;
  1223. }
  1224. }
  1225. // if we didn't find it, oh well, move to the next item.
  1226. if (iFound == -1) {
  1227. // DBGPRINTF((DBG_CONTEXT, "Didn't find DeadProcID (%d) in global array\n", procID));
  1228. goto NextItem;
  1229. }
  1230. pWaitInfo = m_aWaitInfos[iFound];
  1231. // DBGPRINTF((DBG_CONTEXT, "Found DeadProcID (%d) in global array at idx\n", procID,iFound));
  1232. m_aWaitInfos[iFound] = NULL;
  1233. m_dwProcIdInWaitState[iFound] = FALSE;
  1234. // This for loop will compact the various arrays to effective remove
  1235. // this entry from the arrays. I could care about not moving the aWaitInfo
  1236. // when not in compat mode, but it doesn't seem like a big deal.
  1237. for (idx = iFound, cnt = min(m_MainBlock.m_pData->m_cItems,C_PERF_PROC_MAX)-1; idx < cnt; idx++) {
  1238. m_MainBlock.m_pData->m_dwProcIds[idx] = m_MainBlock.m_pData->m_dwProcIds[idx+1];
  1239. m_aWaitInfos[idx] = m_aWaitInfos[idx+1];
  1240. m_dwProcIdInWaitState[idx] = m_dwProcIdInWaitState[idx+1];
  1241. }
  1242. // Reset the last value of the list to NULL / FALSE to make sure they
  1243. // all are initialzied correctly when we add the next.
  1244. m_aWaitInfos[m_MainBlock.m_pData->m_cItems-1] = NULL;
  1245. m_dwProcIdInWaitState[m_MainBlock.m_pData->m_cItems-1] = FALSE;
  1246. // note that there is one less item and that the global array has changed.
  1247. // changing the timestamp will notify ASPPERF.DLL to reload its perfprocblocks
  1248. m_MainBlock.m_pData->m_cItems--;
  1249. m_MainBlock.m_pData->m_dwTimestamp = GetTickCount();
  1250. // Now we have to find the PerfProcBlock in the single linked list of
  1251. // PerfBlocks. There is an obvious optimization to make the PerfProcBlocks
  1252. // double linked lists to avoid the scan. Skipping this for now in favor
  1253. // of simplicity.
  1254. pLast = NULL;
  1255. pBlock = m_MainBlock.m_pProcBlock;
  1256. // search for the block, maintaining pBlock and pLast variables to allow
  1257. // for the removal of the block.
  1258. while (pBlock && (pBlock->m_pData->m_dwProcId != procID)) {
  1259. pLast = pBlock;
  1260. pBlock = pBlock->m_pNext;
  1261. }
  1262. // if we didn't find it, we'll move on, but Assert.
  1263. if (!pBlock) {
  1264. // DBGPRINTF((DBG_CONTEXT, "Didn't find pBlock (%d) in list\n", procID));
  1265. goto NextItem;
  1266. }
  1267. // now do the removal. Two cases to handle. 1) the block was the first
  1268. // in the list or 2) it was in the middle. If not the first, set the previous
  1269. // next to the removed block's next, else set the head list in the mainblock
  1270. // to point to the removed block's next.
  1271. if (pLast)
  1272. pLast->m_pNext = pBlock->m_pNext;
  1273. else
  1274. m_MainBlock.m_pProcBlock = pBlock->m_pNext;
  1275. // we'll only have wait info in compat mode, which means that this
  1276. // pointer could be NULL.
  1277. if (pWaitInfo) {
  1278. UnregisterWait(pWaitInfo->hWaitHandle);
  1279. CloseHandle(pWaitInfo->hProcHandle);
  1280. delete pWaitInfo;
  1281. }
  1282. // ahh.... the moment we've all been waiting for - actually saving
  1283. // the accumulated counters!!!
  1284. m_MainBlock.AggregateDeadProcCounters(pBlock);
  1285. // UnInit() the block, which will release our handle on the shared
  1286. // memory, and delete it
  1287. pBlock->UnInit();
  1288. delete pBlock;
  1289. NextItem:
  1290. // get ready for the next item
  1291. m_MainBlock.UnLock();
  1292. EnterCriticalSection(&m_csProcsDied);
  1293. }
  1294. LeaveCriticalSection(&m_csProcsDied);
  1295. // if we're no longer inited, then set m_dwCntProcsDied to 0
  1296. // to signal to the UnInit routine that we're done processing.
  1297. if (m_fInited == FALSE)
  1298. m_dwCntProcsDied = 0;
  1299. return;
  1300. }
  1301. #endif // _ASP_PERFDEF_H