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.

1231 lines
42 KiB

  1. /*++ BUILD Version: 0002 // Increment this if a change has global effects
  2. Copyright (c) 1998-1999 Microsoft Corporation
  3. Module Name:
  4. PschdPrf.c
  5. Abstract:
  6. This file implements the Extensible Objects for the PSched Flow and
  7. Pipe object types. In particular, it implements the three Open,
  8. Collect, and Close functions called by PerfMon/SysMon.
  9. Author:
  10. Eliot Gillum (t-eliotg) June 14, 1998
  11. Revision History
  12. Rajesh Sundaram : Reworked the code to work with flows/instances coming up and down.
  13. --*/
  14. // Useful macro
  15. #define WRITEBUF(_addr, _len) memcpy(pdwBuf,(_addr),(_len)); pdwBuf = (PULONG)((PUCHAR)pdwBuf + (_len));
  16. #define MULTIPLE_OF_EIGHT(_x) (((_x)+7) & ~7)
  17. #include <windows.h>
  18. #include <winerror.h>
  19. #include <string.h>
  20. #include <wtypes.h>
  21. #include <ntprfctr.h>
  22. #include <malloc.h>
  23. #include <ntddndis.h>
  24. #include <qos.h>
  25. #include <ntddpsch.h>
  26. #include <objbase.h>
  27. #include "PschdPrf.h"
  28. #include "PerfUtil.h"
  29. #include "PschdCnt.h"
  30. #include <rtutils.h>
  31. // Psched Performance Key
  32. #define PSCHED_PERF_KEY TEXT("SYSTEM\\CurrentControlSet\\Services\\PSched\\Performance")
  33. HINSTANCE ghInst; // module instance handle
  34. DWORD dwOpenCount = 0; // count of "Open" threads
  35. BOOL bInitOK = FALSE; // true = DLL initialized OK
  36. HANDLE ghTciClient; // TCI client handle
  37. HANDLE ghClRegCtx; // TCI Client Registration Context
  38. PPIPE_INFO gpPI; // Pipe and flow information array
  39. ULONG gTotalIfcNameSize; // Number of bytes of all the interface names (incl. NULL term. char)
  40. ULONG gTotalFlowNameSize; // Number of bytes of all the flow names (incl. NULL term. char)
  41. ULONG giIfcBufSize = 1024; // set the initial buffer size to 1kb
  42. DWORD gPipeStatLen; // Length of the buffer used to define all the
  43. // Pipe statistics that will be reported by the
  44. // underlying components
  45. DWORD gFlowStatLen; // Length of the buffer used to define all the
  46. // Flow statistics that will be reported by the
  47. // underlying components
  48. CRITICAL_SECTION ghPipeFlowCriticalSection;
  49. #if DBG
  50. //
  51. // For tracing support.
  52. //
  53. #define DBG_INFO (0x00010000 | TRACE_USE_MASK)
  54. #define DBG_ERROR (0x00020000 | TRACE_USE_MASK)
  55. DWORD gTraceID = INVALID_TRACEID;
  56. #define Trace0(_mask, _str) TracePrintfEx(gTraceID, _mask, _str)
  57. #define Trace1(_mask, _str, _a) TracePrintfEx(gTraceID, _mask, _str, _a)
  58. #else
  59. #define Trace0(_mask, _str)
  60. #define Trace1(_mask, _str, _a)
  61. #endif
  62. // Function Prototypes
  63. //
  64. // these are used to ensure that the data collection functions
  65. // accessed by Perflib will have the correct calling format.
  66. PM_OPEN_PROC OpenPschedPerformanceData;
  67. PM_COLLECT_PROC CollectPschedPerformanceData;
  68. PM_CLOSE_PROC ClosePschedPerformanceData;
  69. // Declared in PschdDat.c
  70. extern PERF_OBJECT_TYPE PsPipeObjType;
  71. extern PS_PIPE_PIPE_STAT_DEF PsPipePipeStatDef;
  72. extern PS_PIPE_CONFORMER_STAT_DEF PsPipeConformerStatDef;
  73. extern PS_PIPE_SHAPER_STAT_DEF PsPipeShaperStatDef;
  74. extern PS_PIPE_SEQUENCER_STAT_DEF PsPipeSequencerStatDef;
  75. extern PERF_OBJECT_TYPE PsFlowObjType;
  76. extern PS_FLOW_FLOW_STAT_DEF PsFlowFlowStatDef;
  77. extern PS_FLOW_CONFORMER_STAT_DEF PsFlowConformerStatDef;
  78. extern PS_FLOW_SHAPER_STAT_DEF PsFlowShaperStatDef;
  79. extern PS_FLOW_SEQUENCER_STAT_DEF PsFlowSequencerStatDef;
  80. BOOL
  81. getFlowInfo(IN PPIPE_INFO pPI, IN ULONG flowCount)
  82. {
  83. HANDLE hEnum;
  84. ULONG size;
  85. PVOID pFlowBuf;
  86. static ULONG FlowBufSize=1024;
  87. ULONG j;
  88. ULONG BytesWritten;
  89. ULONG status;
  90. ULONG nameSize;
  91. // initialize the enumeration handle
  92. hEnum = NULL;
  93. for(j=0; j<pPI->numFlows; j++)
  94. {
  95. size = ((wcslen(pPI->pFlowInfo[j].FriendlyName) + 1) * sizeof(WCHAR));
  96. gTotalFlowNameSize -= MULTIPLE_OF_EIGHT(size);
  97. }
  98. PsFlowObjType.NumInstances -= pPI->numFlows;
  99. PsFlowObjType.NumInstances += flowCount;
  100. pPI->numFlows = flowCount;
  101. if(pPI->pFlowInfo)
  102. free(pPI->pFlowInfo);
  103. if(flowCount)
  104. {
  105. pPI->pFlowInfo = (PFLOW_INFO) malloc(flowCount * sizeof(FLOW_INFO));
  106. //
  107. // We cannot allocate memory for the flow names. There is nothing much we can do here.
  108. // let's pretend as though there are no flows.
  109. //
  110. if(!pPI->pFlowInfo)
  111. {
  112. Trace0(DBG_ERROR, L"[getFlowInfo]: malloc failed \n");
  113. PsFlowObjType.NumInstances -= flowCount;
  114. pPI->numFlows = 0;
  115. return FALSE;
  116. }
  117. else
  118. {
  119. memset(pPI->pFlowInfo, 0, sizeof(FLOW_INFO) * flowCount);
  120. }
  121. // allocate the flow enumeration buffer
  122. pFlowBuf = malloc(FlowBufSize);
  123. if(!pFlowBuf)
  124. {
  125. Trace0(DBG_ERROR, L"[getFlowInfo]: malloc failed \n");
  126. free(pPI->pFlowInfo);
  127. pPI->pFlowInfo = NULL;
  128. PsFlowObjType.NumInstances -= flowCount;
  129. pPI->numFlows = 0;
  130. return FALSE;
  131. }
  132. // initialize the enumeration handle
  133. hEnum = NULL;
  134. // enumerate each flow and remember its name
  135. for (j=0; j<pPI->numFlows; j++)
  136. {
  137. PENUMERATION_BUFFER pEnum;
  138. LPQOS_FRIENDLY_NAME pFriendly;
  139. ULONG TcObjectLength, FriendlyNameFound;
  140. // get the next flow
  141. BytesWritten = FlowBufSize;
  142. flowCount = 1;
  143. status = TcEnumerateFlows(pPI->hIfc, &hEnum, &flowCount, &BytesWritten, pFlowBuf);
  144. while (ERROR_INSUFFICIENT_BUFFER == status)
  145. {
  146. free(pFlowBuf);
  147. FlowBufSize *= 2;
  148. BytesWritten = FlowBufSize;
  149. pFlowBuf = malloc(BytesWritten);
  150. if(!pFlowBuf)
  151. {
  152. Trace0(DBG_ERROR, L"[getFlowInfo]: malloc failed \n");
  153. free(pPI->pFlowInfo);
  154. pPI->pFlowInfo = NULL;
  155. PsFlowObjType.NumInstances -= flowCount;
  156. pPI->numFlows = 0;
  157. return FALSE;
  158. }
  159. status = TcEnumerateFlows(pPI->hIfc, &hEnum, &flowCount, &BytesWritten, pFlowBuf);
  160. }
  161. if ( (NO_ERROR != status)
  162. || (BytesWritten == 0) )
  163. {
  164. if ( status )
  165. Trace1(DBG_ERROR, L"[getFlowInfo]: TcEnumerateFlows failed with 0x%x \n", status);
  166. else if ( BytesWritten == 0 )
  167. Trace0(DBG_ERROR, L"[getFlowInfo]: TcEnumerateFlows returned 0 bytes \n");
  168. free(pFlowBuf);
  169. free(pPI->pFlowInfo);
  170. pPI->pFlowInfo = NULL;
  171. PsFlowObjType.NumInstances -= flowCount;
  172. pPI->numFlows = 0;
  173. return FALSE;
  174. }
  175. // save the flow's name
  176. pEnum = (PENUMERATION_BUFFER)pFlowBuf;
  177. FriendlyNameFound = 0;
  178. pFriendly = (LPQOS_FRIENDLY_NAME)pEnum->pFlow->TcObjects;
  179. TcObjectLength = pEnum->pFlow->TcObjectsLength;
  180. while(0)
  181. {
  182. if(pFriendly->ObjectHdr.ObjectType == QOS_OBJECT_FRIENDLY_NAME)
  183. {
  184. // We found a friendly name. Lets use it.
  185. memcpy(
  186. pPI->pFlowInfo[j].FriendlyName,
  187. pFriendly->FriendlyName,
  188. PS_FRIENDLY_NAME_LENGTH *sizeof(WCHAR) );
  189. pPI->pFlowInfo[j].FriendlyName[PS_FRIENDLY_NAME_LENGTH] = L'\0';
  190. nameSize = (wcslen(pPI->pFlowInfo[j].FriendlyName) + 1) * sizeof(WCHAR);
  191. gTotalFlowNameSize += MULTIPLE_OF_EIGHT(nameSize);
  192. FriendlyNameFound = 1;
  193. break;
  194. }
  195. else {
  196. // Move on to the next QoS object.
  197. TcObjectLength -= pFriendly->ObjectHdr.ObjectLength;
  198. pFriendly = (LPQOS_FRIENDLY_NAME)((PCHAR) pFriendly + pFriendly->ObjectHdr.ObjectLength);
  199. }
  200. }
  201. if(!FriendlyNameFound)
  202. {
  203. //
  204. // If there is no friendly name, the Instance name becomes the friendly name.
  205. //
  206. memcpy(pPI->pFlowInfo[j].FriendlyName,
  207. ((PENUMERATION_BUFFER)pFlowBuf)->FlowName,
  208. PS_FRIENDLY_NAME_LENGTH * sizeof(WCHAR) );
  209. pPI->pFlowInfo[j].FriendlyName[PS_FRIENDLY_NAME_LENGTH] = L'\0';
  210. nameSize = (wcslen(pPI->pFlowInfo[j].FriendlyName) + 1) * sizeof(WCHAR);
  211. gTotalFlowNameSize += MULTIPLE_OF_EIGHT(nameSize);
  212. }
  213. //
  214. // We have to always store the instance name since we call TcQueryFlow with this name.
  215. //
  216. nameSize = (wcslen(((PENUMERATION_BUFFER)pFlowBuf)->FlowName) + 1) * sizeof(WCHAR);
  217. memcpy(pPI->pFlowInfo[j].InstanceName, ((PENUMERATION_BUFFER)pFlowBuf)->FlowName, nameSize);
  218. }
  219. free(pFlowBuf);
  220. }
  221. else
  222. {
  223. pPI->pFlowInfo = NULL;
  224. Trace0(DBG_INFO, L"[getFlowInfo]: No flows to enumerate \n");
  225. }
  226. return TRUE;
  227. }
  228. // getPipeFlowInfo() initializes an array of PIPE_INFO structs to contain
  229. // up-to-date information about the pipes available and the flows installed on them
  230. //
  231. // Parameters: ppPI - pointer to a pointer to an array of PIPE_INFO structs
  232. // Return value: TRUE if all info in *ppPI is valid, FALSE otherwise
  233. BOOL getPipeFlowInfo(OUT PPIPE_INFO *ppPI)
  234. {
  235. ULONG status;
  236. ULONG BytesWritten;
  237. ULONG i,j,k;
  238. PVOID pIfcDescBuf = NULL;
  239. PTC_IFC_DESCRIPTOR currentIfc;
  240. PPIPE_INFO pPI = NULL;
  241. HANDLE hEnum;
  242. PVOID pFlowBuf;
  243. static ULONG FlowBufSize=1024;
  244. ULONG flowCount;
  245. ULONG nameSize;
  246. ULONG size;
  247. PPERF_COUNTER_DEFINITION pCntDef;
  248. PsPipeObjType.NumInstances=0;
  249. PsFlowObjType.NumInstances = 0;
  250. pIfcDescBuf = (PVOID)malloc(giIfcBufSize);
  251. if (NULL == pIfcDescBuf)
  252. {
  253. Trace0(DBG_ERROR, L"[getPipeFlowInfo]: Malloc failed \n");
  254. return FALSE;
  255. }
  256. BytesWritten = giIfcBufSize;
  257. status = TcEnumerateInterfaces(ghTciClient, &BytesWritten, pIfcDescBuf);
  258. while (ERROR_INSUFFICIENT_BUFFER==status)
  259. {
  260. free(pIfcDescBuf);
  261. giIfcBufSize *= 2;
  262. pIfcDescBuf = (PTC_IFC_DESCRIPTOR)malloc(giIfcBufSize);
  263. if (NULL == pIfcDescBuf)
  264. {
  265. Trace0(DBG_ERROR, L"[getPipeFlowInfo]: Malloc failed \n");
  266. return FALSE;
  267. }
  268. BytesWritten = giIfcBufSize;
  269. status = TcEnumerateInterfaces(ghTciClient, &BytesWritten, pIfcDescBuf);
  270. }
  271. if (NO_ERROR!=status)
  272. {
  273. // If we're not going to be able to enumerate the interfaces, we have no alternatives
  274. Trace1(DBG_ERROR, L"[getPipeFlowInfo]: TcEnumerateInterfaces failed with 0x%x\n", status);
  275. free(pIfcDescBuf);
  276. return FALSE;
  277. }
  278. // Figure out the number of interfaces
  279. for (i=0; i<BytesWritten; i+=((PTC_IFC_DESCRIPTOR)((BYTE *)pIfcDescBuf+i))->Length)
  280. {
  281. PsPipeObjType.NumInstances++;
  282. }
  283. // Open each interface and remember the handle to it
  284. if (0 != PsPipeObjType.NumInstances) {
  285. // Allocate space for our structs
  286. *ppPI=(PPIPE_INFO)malloc(PsPipeObjType.NumInstances * sizeof(PIPE_INFO) );
  287. if (NULL == *ppPI)
  288. {
  289. Trace0(DBG_ERROR, L"[getPipeFlowInfo]: Malloc failed \n");
  290. free(pIfcDescBuf);
  291. return FALSE;
  292. }
  293. else
  294. {
  295. memset(*ppPI, 0, sizeof(PIPE_INFO) * PsPipeObjType.NumInstances);
  296. }
  297. pPI = *ppPI; // less typing, cleaner source code
  298. gTotalIfcNameSize = 0;
  299. gTotalFlowNameSize = 0;
  300. currentIfc = pIfcDescBuf;
  301. // Initialize struct information for each interface
  302. for (i=0; i<(unsigned)PsPipeObjType.NumInstances; i++)
  303. {
  304. // remember the inteface's name
  305. nameSize = (wcslen(currentIfc->pInterfaceName) + 1) * sizeof(WCHAR);
  306. pPI[i].IfcName = malloc(nameSize);
  307. if (NULL == pPI[i].IfcName)
  308. {
  309. Trace0(DBG_ERROR, L"[getPipeFlowInfo]: Malloc failed \n");
  310. goto Error;
  311. }
  312. wcscpy(pPI[i].IfcName, currentIfc->pInterfaceName);
  313. //
  314. // add this name size to gTotalIfcNameSize.
  315. //
  316. gTotalIfcNameSize += MULTIPLE_OF_EIGHT(nameSize);
  317. //
  318. // open the interface
  319. //
  320. status = TcOpenInterface(
  321. pPI[i].IfcName,
  322. ghTciClient,
  323. &pPI[i],
  324. &pPI[i].hIfc);
  325. if (status != NO_ERROR)
  326. {
  327. Trace1(DBG_ERROR, L"[getPipeFlowInfo]: TcOpenInterface failed with 0x%x\n", status);
  328. goto Error;
  329. }
  330. //
  331. // Enumerate the flows on the interface
  332. // find out how many flows to expect
  333. //
  334. pPI[i].numFlows = 0;
  335. pPI[i].pFlowInfo = 0;
  336. status = TcQueryInterface(pPI[i].hIfc,
  337. (LPGUID)&GUID_QOS_FLOW_COUNT,
  338. TRUE,
  339. &BytesWritten,
  340. &flowCount);
  341. if (NO_ERROR != status)
  342. {
  343. Trace1( DBG_ERROR,
  344. L"[getPipeFlowInfo]: TcQueryInterface failed with 0x%x, ignoring this error\n",
  345. status);
  346. }
  347. else
  348. {
  349. getFlowInfo(&pPI[i], flowCount);
  350. }
  351. // move to the next interface
  352. currentIfc = (PTC_IFC_DESCRIPTOR)((PBYTE)currentIfc + currentIfc->Length);
  353. }
  354. }
  355. // determine what components will be contributing stats, if there any stats to get
  356. if (PsPipeObjType.NumInstances > 0) {
  357. //
  358. // compute the counter definition lengths. Each set of counters is preceeded by a PERF_OBJECT_TYPE, followed
  359. // by 'n' PERF_COUNTER_DEFINITIONS. All these are aligned on 8 byte boundaries, so we don't have to do any
  360. // fancy aligining.
  361. //
  362. PsPipeObjType.DefinitionLength = sizeof(PERF_OBJECT_TYPE) +
  363. sizeof(PsPipePipeStatDef) +
  364. sizeof(PsPipeConformerStatDef) +
  365. sizeof(PsPipeShaperStatDef) +
  366. sizeof(PsPipeSequencerStatDef);
  367. PsFlowObjType.DefinitionLength = sizeof(PERF_OBJECT_TYPE) +
  368. sizeof(PsFlowFlowStatDef) +
  369. sizeof(PsFlowConformerStatDef) +
  370. sizeof(PsFlowShaperStatDef) +
  371. sizeof(PsFlowSequencerStatDef);
  372. // compute the sizes of the stats buffers.
  373. gPipeStatLen = FIELD_OFFSET(PS_COMPONENT_STATS, Stats) + // initial offset
  374. sizeof(PS_ADAPTER_STATS) + // every interface has adapter stats
  375. FIELD_OFFSET(PS_COMPONENT_STATS, Stats) +
  376. sizeof(PS_CONFORMER_STATS) +
  377. FIELD_OFFSET(PS_COMPONENT_STATS, Stats) +
  378. sizeof(PS_SHAPER_STATS) +
  379. FIELD_OFFSET(PS_COMPONENT_STATS, Stats) +
  380. sizeof(PS_DRRSEQ_STATS);
  381. gFlowStatLen = FIELD_OFFSET(PS_COMPONENT_STATS, Stats) + // initial offset
  382. sizeof(PS_FLOW_STATS) + // the flow's stats
  383. FIELD_OFFSET(PS_COMPONENT_STATS, Stats) +
  384. sizeof(PS_CONFORMER_STATS) +
  385. FIELD_OFFSET(PS_COMPONENT_STATS, Stats) +
  386. sizeof(PS_SHAPER_STATS) +
  387. FIELD_OFFSET(PS_COMPONENT_STATS, Stats) +
  388. sizeof(PS_DRRSEQ_STATS);
  389. // Align these to 8 byte boundaries.
  390. gPipeStatLen = MULTIPLE_OF_EIGHT(gPipeStatLen);
  391. gFlowStatLen = MULTIPLE_OF_EIGHT(gFlowStatLen);
  392. // update the number of counters to be reported for each object type
  393. PsPipeObjType.NumCounters = PIPE_PIPE_NUM_STATS + PIPE_CONFORMER_NUM_STATS +
  394. PIPE_SHAPER_NUM_STATS + PIPE_SEQUENCER_NUM_STATS;
  395. PsFlowObjType.NumCounters = FLOW_FLOW_NUM_STATS + FLOW_CONFORMER_NUM_STATS +
  396. FLOW_SHAPER_NUM_STATS + FLOW_SEQUENCER_NUM_STATS;
  397. }
  398. // free up resources
  399. free(pIfcDescBuf);
  400. // Everything worked so return that we're happy
  401. return TRUE;
  402. Error:
  403. for (i=0; i<(unsigned)PsPipeObjType.NumInstances; i++)
  404. {
  405. if ( pPI[i].hIfc )
  406. {
  407. // Deregister for flow count notifications.
  408. TcQueryInterface(
  409. pPI[i].hIfc,
  410. (LPGUID)&GUID_QOS_FLOW_COUNT,
  411. FALSE,
  412. &BytesWritten,
  413. &flowCount);
  414. TcCloseInterface(pPI[i].hIfc);
  415. }
  416. }
  417. if (pIfcDescBuf)
  418. free(pIfcDescBuf);
  419. if(pPI)
  420. free(pPI);
  421. return FALSE;
  422. }
  423. // closePipeFlowInfo() is the counterpart to getPipeFlowInfo()
  424. // It closes all open interfaces and flows, as well as freeing memory
  425. //
  426. // Parameters: ppPI - pointer to a pointer to an array of valid PIPE_INFO structs
  427. // Return value: None
  428. void closePipeFlowInfo(PPIPE_INFO *ppPI)
  429. {
  430. ULONG i;
  431. PPIPE_INFO pPI=*ppPI; // makes for less typing and cleaner code
  432. ULONG BytesWritten, flowCount;
  433. BytesWritten = sizeof(flowCount);
  434. // free up resources associated with each interface, then close the interface
  435. for (i=0; i<(unsigned)PsPipeObjType.NumInstances; i++)
  436. {
  437. if(pPI[i].IfcName)
  438. {
  439. free(pPI[i].IfcName);
  440. }
  441. if(pPI[i].pFlowInfo)
  442. {
  443. free(pPI[i].pFlowInfo);
  444. }
  445. // Deregister for flow count notifications.
  446. TcQueryInterface(pPI[i].hIfc,
  447. (LPGUID)&GUID_QOS_FLOW_COUNT,
  448. FALSE,
  449. &BytesWritten,
  450. &flowCount);
  451. TcCloseInterface(pPI[i].hIfc);
  452. }
  453. // now free up the whole buffer
  454. free(*ppPI);
  455. }
  456. // This func recieves notifcations from traffic.dll and makes the appropriate
  457. // updates to internal structures
  458. void tciNotifyHandler(IN HANDLE ClRegCtx,
  459. IN HANDLE ClIfcCtx,
  460. IN ULONG Event,
  461. IN HANDLE SubCode,
  462. IN ULONG BufSize,
  463. IN PVOID Buffer)
  464. {
  465. switch (Event)
  466. {
  467. case TC_NOTIFY_IFC_UP:
  468. case TC_NOTIFY_IFC_CLOSE:
  469. case TC_NOTIFY_IFC_CHANGE:
  470. // we'll need sync'ed access
  471. EnterCriticalSection(&ghPipeFlowCriticalSection);
  472. // now reinit the data struct
  473. closePipeFlowInfo(&gpPI);
  474. getPipeFlowInfo(&gpPI);
  475. LeaveCriticalSection(&ghPipeFlowCriticalSection);
  476. break;
  477. case TC_NOTIFY_PARAM_CHANGED:
  478. // A flow has been closed by the TC interface
  479. // for example: after a remote call close, or the whole interface
  480. // is going down
  481. //
  482. // we'll need sync'ed access
  483. EnterCriticalSection(&ghPipeFlowCriticalSection);
  484. if(!memcmp((LPGUID) SubCode, &GUID_QOS_FLOW_COUNT, sizeof(GUID)) )
  485. {
  486. PULONG FlowCount = (PULONG) Buffer;
  487. getFlowInfo(ClIfcCtx, *FlowCount);
  488. }
  489. LeaveCriticalSection(&ghPipeFlowCriticalSection);
  490. break;
  491. default:
  492. break;
  493. }
  494. }
  495. DWORD APIENTRY OpenPschedPerformanceData(LPWSTR lpDeviceNames)
  496. /*++
  497. Routine Description:
  498. This routine will open and map the memory used by the PSched driver to
  499. pass performance data in. This routine also initializes the data
  500. structures used to pass data back to the registry
  501. Arguments:
  502. Pointer to object ID of each device to be opened (PSched)
  503. Return Value:
  504. None.
  505. --*/
  506. {
  507. LONG status;
  508. HKEY hPerfKey;
  509. DWORD size;
  510. DWORD type;
  511. DWORD dwFirstCounter;
  512. DWORD dwFirstHelp;
  513. TCI_CLIENT_FUNC_LIST tciCallbFuncList = {tciNotifyHandler, NULL, NULL, NULL};
  514. // Since SCREG is multi-threaded and will call this routine in
  515. // order to service remote performance queries, this library
  516. // must keep track of how many times it has been opened (i.e.
  517. // how many threads have accessed it). the registry routines will
  518. // limit access to the initialization routine to only one thread
  519. // at a time so synchronization (i.e. reentrancy) should not be
  520. // a problem
  521. if (InterlockedIncrement(&dwOpenCount) == 1)
  522. {
  523. #if DBG
  524. gTraceID = TraceRegister(L"PschdPrf");
  525. #endif
  526. // get counter and help index base values
  527. // update static data structures by adding base to
  528. // offset value in structure.
  529. status = RegOpenKeyEx ( HKEY_LOCAL_MACHINE,
  530. PSCHED_PERF_KEY,
  531. 0L,
  532. KEY_READ,
  533. &hPerfKey);
  534. if (status != ERROR_SUCCESS) {
  535. // this is fatal, if we can't get the base values of the
  536. // counter or help names, then the names won't be available
  537. // to the requesting application, so there's not much
  538. // point in continuing.
  539. goto OpenExitPoint;
  540. }
  541. size = sizeof (DWORD);
  542. status = RegQueryValueEx( hPerfKey,
  543. TEXT("First Counter"),
  544. 0L,
  545. &type,
  546. (LPBYTE)&dwFirstCounter,
  547. &size);
  548. if (status != ERROR_SUCCESS) {
  549. // this is fatal, if we can't get the base values of the
  550. // counter or help names, then the names won't be available
  551. // to the requesting application, so there's not much
  552. // point in continuing.
  553. goto OpenExitPoint;
  554. }
  555. size = sizeof (DWORD);
  556. status = RegQueryValueEx( hPerfKey,
  557. TEXT("First Help"),
  558. 0L,
  559. &type,
  560. (LPBYTE)&dwFirstHelp,
  561. &size);
  562. if (status != ERROR_SUCCESS) {
  563. // this is fatal, if we can't get the base values of the
  564. // counter or help names, then the names won't be available
  565. // to the requesting application, so there's not much
  566. // point in continuing.
  567. goto OpenExitPoint;
  568. }
  569. RegCloseKey (hPerfKey); // close key to registry
  570. // Convert Pipe object and counters from offset to absolute index
  571. PsPipeObjType.ObjectNameTitleIndex += dwFirstCounter;
  572. PsPipeObjType.ObjectHelpTitleIndex += dwFirstHelp;
  573. convertIndices((BYTE *)&PsPipePipeStatDef,
  574. sizeof PsPipePipeStatDef/sizeof(PERF_COUNTER_DEFINITION),
  575. dwFirstCounter,
  576. dwFirstHelp);
  577. convertIndices((BYTE *)&PsPipeConformerStatDef,
  578. sizeof PsPipeConformerStatDef/sizeof(PERF_COUNTER_DEFINITION),
  579. dwFirstCounter,
  580. dwFirstHelp);
  581. convertIndices((BYTE *)&PsPipeShaperStatDef,
  582. sizeof PsPipeShaperStatDef/sizeof(PERF_COUNTER_DEFINITION),
  583. dwFirstCounter,
  584. dwFirstHelp);
  585. convertIndices((BYTE *)&PsPipeSequencerStatDef,
  586. sizeof PsPipeSequencerStatDef/sizeof(PERF_COUNTER_DEFINITION),
  587. dwFirstCounter,
  588. dwFirstHelp);
  589. // Convert Flow object and counters from offset to absolute index
  590. PsFlowObjType.ObjectNameTitleIndex += dwFirstCounter;
  591. PsFlowObjType.ObjectHelpTitleIndex += dwFirstHelp;
  592. convertIndices((BYTE *)&PsFlowFlowStatDef,
  593. sizeof PsFlowFlowStatDef/sizeof(PERF_COUNTER_DEFINITION),
  594. dwFirstCounter,
  595. dwFirstHelp);
  596. convertIndices((BYTE *)&PsFlowConformerStatDef,
  597. sizeof PsFlowConformerStatDef/sizeof(PERF_COUNTER_DEFINITION),
  598. dwFirstCounter,
  599. dwFirstHelp);
  600. convertIndices((BYTE *)&PsFlowShaperStatDef,
  601. sizeof PsFlowShaperStatDef/sizeof(PERF_COUNTER_DEFINITION),
  602. dwFirstCounter,
  603. dwFirstHelp);
  604. convertIndices((BYTE *)&PsFlowSequencerStatDef,
  605. sizeof PsFlowSequencerStatDef/sizeof(PERF_COUNTER_DEFINITION),
  606. dwFirstCounter,
  607. dwFirstHelp);
  608. // initialize the mutex
  609. __try {
  610. InitializeCriticalSection(&ghPipeFlowCriticalSection);
  611. } __except (EXCEPTION_EXECUTE_HANDLER) {
  612. status = GetExceptionCode();
  613. goto OpenExitPoint;
  614. }
  615. // initialize with traffic.dll
  616. if (TcRegisterClient(CURRENT_TCI_VERSION, ghClRegCtx, &tciCallbFuncList, &ghTciClient)!=NO_ERROR)
  617. {
  618. // if we can't connect with traffic.dll we are a non admin thread in OpenPschedPerformanceData.
  619. // We cannot fail because of this, because an admin thread might call us at our collect routine.
  620. // We'll try to register as the Traffic Control client in the Collect Thread.
  621. //
  622. ghTciClient = 0;
  623. }
  624. else
  625. {
  626. // we'll need sync'ed access
  627. EnterCriticalSection(&ghPipeFlowCriticalSection);
  628. // get all necessary info about pipes and flows currently installed
  629. if (getPipeFlowInfo(&gpPI)!=TRUE) {
  630. // we didn't get all the info we wanted, so we're
  631. // going to have to try again, including re-registering
  632. LeaveCriticalSection(&ghPipeFlowCriticalSection);
  633. TcDeregisterClient(ghTciClient);
  634. goto OpenExitPoint;
  635. }
  636. LeaveCriticalSection(&ghPipeFlowCriticalSection);
  637. }
  638. // if we got to here, then we're all ready
  639. bInitOK = TRUE;
  640. }
  641. Trace0(DBG_INFO, L"[OpenPschedPerformanceData]: success \n");
  642. status = ERROR_SUCCESS; // for successful exit
  643. return status;
  644. OpenExitPoint:
  645. Trace1(DBG_ERROR, L"[OpenPschedPerformanceData]: Failed with 0x%x \n", status);
  646. return status;
  647. }
  648. DWORD APIENTRY CollectPschedPerformanceData(
  649. IN LPWSTR lpValueName,
  650. IN OUT LPVOID *lppData,
  651. IN OUT LPDWORD lpcbTotalBytes,
  652. IN OUT LPDWORD lpNumObjectTypes
  653. )
  654. /*++
  655. Routine Description:
  656. This routine will return the data for the PSched counters.
  657. The data is returned in the foll. format. The steps below are carried out for Pipe/Flows.
  658. 1. First, we write the PERF_OBJECT_TYPE for the Pipe (and/or) the Flow Counters.
  659. 2. for(i=0; i<NumCounters; i++)
  660. Write PERF_COUNTER_DEFINITION for counter i;
  661. 3. for(i=0; i<NumInstances; i++)
  662. Write PERF_INSTANCE_DEFINITION for instance i;
  663. Write Instance Name
  664. Write PERF_COUNTER_BLOCK
  665. Write the Stats;
  666. Arguments:
  667. IN LPWSTR lpValueName
  668. pointer to a wide character string passed by registry.
  669. IN OUT LPVOID *lppData
  670. IN: pointer to the address of the buffer to receive the completed
  671. PerfDataBlock and subordinate structures. This routine will
  672. append its data to the buffer starting at the point referenced
  673. by *lppData.
  674. OUT: points to the first byte after the data structure added by this
  675. routine. This routine updated the value at lppdata after appending
  676. its data.
  677. IN OUT LPDWORD lpcbTotalBytes
  678. IN: the address of the DWORD that tells the size in bytes of the
  679. buffer referenced by the lppData argument
  680. OUT: the number of bytes added by this routine is written to the
  681. DWORD pointed to by this argument
  682. IN OUT LPDWORD NumObjectTypes
  683. IN: the address of the DWORD to receive the number of objects added
  684. by this routine
  685. OUT: the number of objects added by this routine is written to the
  686. DWORD pointed to by this argument
  687. Return Value:
  688. ERROR_MORE_DATA if buffer passed is too small to hold data
  689. any error conditions encountered could be reported to the event log if
  690. event logging support were added.
  691. ERROR_SUCCESS if success or any other error. Errors, however could
  692. also reported to the event log.
  693. --*/
  694. {
  695. ULONG i,j;
  696. ULONG SpaceNeeded;
  697. PDWORD pdwBuf;
  698. DWORD dwQueryType;
  699. DWORD status;
  700. DWORD bufSize;
  701. PS_PERF_COUNTER_BLOCK pcb;
  702. PERF_INSTANCE_DEFINITION pid={0, 0, 0, PERF_NO_UNIQUE_ID, sizeof(pid), 0};
  703. ULONG size;
  704. PVOID pStatsBuf;
  705. // save the size of the buffer
  706. bufSize = *lpcbTotalBytes;
  707. // default to returning nothing
  708. *lpcbTotalBytes = (DWORD) 0;
  709. *lpNumObjectTypes = (DWORD) 0;
  710. // Before doing anything else, see if Open went OK
  711. if (!bInitOK)
  712. {
  713. // unable to continue because open failed.
  714. Trace0(DBG_ERROR, L"[CollectPschedPerformanceData]: open failed \n");
  715. return ERROR_SUCCESS; // yes, this is a successful exit
  716. }
  717. // See if this is a foreign (i.e. non-NT) computer data request
  718. dwQueryType = GetQueryType (lpValueName);
  719. if (dwQueryType == QUERY_FOREIGN)
  720. {
  721. // this routine does not service requests for data from
  722. // Non-NT computers
  723. Trace0(DBG_ERROR, L"[CollectPschedPerformanceData]: received QUERY_FOREIGN \n");
  724. return ERROR_SUCCESS;
  725. }
  726. // Is PerfMon requesting PSched items?
  727. if (dwQueryType == QUERY_ITEMS)
  728. {
  729. if ( !(IsNumberInUnicodeList(PsPipeObjType.ObjectNameTitleIndex,
  730. lpValueName))
  731. && !(IsNumberInUnicodeList(PsFlowObjType.ObjectNameTitleIndex,
  732. lpValueName)) ) {
  733. // request received for data object not provided by this routine
  734. Trace0(DBG_INFO, L"[CollectPschedPerformanceData]: Not for psched \n");
  735. return ERROR_SUCCESS;
  736. }
  737. }
  738. // from this point on, we need sync'ed access
  739. EnterCriticalSection(&ghPipeFlowCriticalSection);
  740. // we might need to rereigster as a Traffic control client.
  741. if(ghTciClient == NULL)
  742. {
  743. TCI_CLIENT_FUNC_LIST tciCallbFuncList = {tciNotifyHandler, NULL, NULL, NULL};
  744. status = TcRegisterClient(CURRENT_TCI_VERSION, ghClRegCtx, &tciCallbFuncList, &ghTciClient);
  745. if(status != NO_ERROR)
  746. {
  747. Trace1(DBG_ERROR, L"[CollectPschedPerformanceData]: Could not register as Traffic Client. Error 0x%x \n",
  748. status);
  749. LeaveCriticalSection(&ghPipeFlowCriticalSection);
  750. return ERROR_SUCCESS;
  751. }
  752. // get all necessary info about pipes and flows currently installed
  753. if (getPipeFlowInfo(&gpPI)!=TRUE) {
  754. LeaveCriticalSection(&ghPipeFlowCriticalSection);
  755. Trace0(DBG_ERROR, L"[CollectPschedPerformanceData]: getPipeFlowInfo failed \n");
  756. return ERROR_SUCCESS;
  757. }
  758. }
  759. //
  760. // We have to write the PERF_OBJECT_TYPE unconditionally even if there are no instances. So, we proceed
  761. // to compute the space needed even when there are no flows.
  762. //
  763. // Calculate the space needed for the pipe stats.
  764. SpaceNeeded = PsPipeObjType.DefinitionLength + gTotalIfcNameSize + (PsPipeObjType.NumInstances *
  765. (sizeof pid + sizeof pcb + gPipeStatLen) );
  766. SpaceNeeded += PsFlowObjType.DefinitionLength + gTotalFlowNameSize + (PsFlowObjType.NumInstances *
  767. (sizeof pid + sizeof pcb + gFlowStatLen) );
  768. if (bufSize < SpaceNeeded)
  769. {
  770. LeaveCriticalSection(&ghPipeFlowCriticalSection);
  771. Trace0(DBG_ERROR, L"[CollectPschedPerformanceData]: Need More data\n");
  772. return ERROR_MORE_DATA;
  773. }
  774. pdwBuf = (PDWORD)*lppData;
  775. // Record the total length of the pipe stats
  776. PsPipeObjType.TotalByteLength =
  777. PsPipeObjType.DefinitionLength + gTotalIfcNameSize + (PsPipeObjType.NumInstances *
  778. (sizeof pid + sizeof pcb + gPipeStatLen) );
  779. // copy object and counter definitions, increment count of object types
  780. WRITEBUF(&PsPipeObjType,sizeof PsPipeObjType);
  781. WRITEBUF(&PsPipePipeStatDef, sizeof PsPipePipeStatDef);
  782. WRITEBUF(&PsPipeConformerStatDef, sizeof PsPipeConformerStatDef);
  783. WRITEBUF(&PsPipeShaperStatDef, sizeof PsPipeShaperStatDef);
  784. WRITEBUF(&PsPipeSequencerStatDef, sizeof PsPipeSequencerStatDef);
  785. (*lpNumObjectTypes)++;
  786. //
  787. // for each pipe, write out its instance definition, counter block, and actual stats
  788. //
  789. if(ghTciClient)
  790. {
  791. for (i=0; i<(unsigned)PsPipeObjType.NumInstances; i++) {
  792. PWCHAR InstanceName;
  793. //
  794. // Write out the PERF_INSTANCE_DEFINITION, which identifies an interface and gives it a name.
  795. //
  796. pid.NameLength = (wcslen(gpPI[i].IfcName)+1) * sizeof(WCHAR);
  797. pid.ByteLength = sizeof pid + MULTIPLE_OF_EIGHT(pid.NameLength);
  798. WRITEBUF(&pid, sizeof pid);
  799. InstanceName = (PWCHAR) pdwBuf;
  800. memcpy(pdwBuf, gpPI[i].IfcName, pid.NameLength);
  801. pdwBuf = (PULONG)((PUCHAR)pdwBuf + MULTIPLE_OF_EIGHT(pid.NameLength));
  802. CorrectInstanceName(InstanceName);
  803. //
  804. // get pipe stats and copy them to the buffer
  805. //
  806. size = gPipeStatLen;
  807. pStatsBuf = malloc(size);
  808. if (NULL == pStatsBuf)
  809. {
  810. *lpcbTotalBytes = (DWORD) 0;
  811. *lpNumObjectTypes = (DWORD) 0;
  812. LeaveCriticalSection(&ghPipeFlowCriticalSection);
  813. Trace0(DBG_ERROR, L"[CollectPschedPerformanceData]: Insufficient memory\n");
  814. return ERROR_SUCCESS;
  815. }
  816. status = TcQueryInterface(gpPI[i].hIfc,
  817. (LPGUID)&GUID_QOS_STATISTICS_BUFFER,
  818. FALSE,
  819. &size,
  820. pStatsBuf);
  821. if (ERROR_INSUFFICIENT_BUFFER==status)
  822. {
  823. free(pStatsBuf);
  824. size = gPipeStatLen = MULTIPLE_OF_EIGHT(size);
  825. pStatsBuf = (PPS_COMPONENT_STATS)malloc(gPipeStatLen);
  826. if (NULL == pStatsBuf)
  827. {
  828. *lpcbTotalBytes = (DWORD) 0;
  829. *lpNumObjectTypes = (DWORD) 0;
  830. LeaveCriticalSection(&ghPipeFlowCriticalSection);
  831. Trace0(DBG_ERROR, L"[CollectPschedPerformanceData]: Insufficient memory\n");
  832. return ERROR_SUCCESS;
  833. }
  834. status = TcQueryInterface(gpPI[i].hIfc,
  835. (LPGUID)&GUID_QOS_STATISTICS_BUFFER,
  836. FALSE,
  837. &size,
  838. pStatsBuf);
  839. }
  840. if (NO_ERROR != status) {
  841. Trace1(DBG_ERROR, L"[CollectPschedPerformanceData]: TcQueryInterface failed with 0x%x \n", status);
  842. memset ( pStatsBuf, 0, gPipeStatLen );
  843. }
  844. //
  845. // Now, write the PERF_COUNTER_BLOCK
  846. //
  847. pcb.pcb.ByteLength = gPipeStatLen + sizeof(pcb);
  848. WRITEBUF(&pcb,sizeof pcb);
  849. //
  850. // Write out all the counters.
  851. //
  852. WRITEBUF(pStatsBuf,gPipeStatLen);
  853. free(pStatsBuf);
  854. }
  855. }
  856. // set the pointer to where the pipe object type said the next object would start
  857. pdwBuf = (PDWORD)( ((BYTE *)*lppData) + PsPipeObjType.TotalByteLength );
  858. // first copy flow data def (object_type struct).
  859. // Record the total length of the flow stats
  860. PsFlowObjType.TotalByteLength =
  861. PsFlowObjType.DefinitionLength + gTotalFlowNameSize + (PsFlowObjType.NumInstances *
  862. (sizeof pid + sizeof pcb + gFlowStatLen) );
  863. // copy object and counter definitions, increment count of object types
  864. WRITEBUF(&PsFlowObjType,sizeof PsFlowObjType);
  865. WRITEBUF(&PsFlowFlowStatDef, sizeof PsFlowFlowStatDef);
  866. WRITEBUF(&PsFlowConformerStatDef, sizeof PsFlowConformerStatDef);
  867. WRITEBUF(&PsFlowShaperStatDef, sizeof PsFlowShaperStatDef);
  868. WRITEBUF(&PsFlowSequencerStatDef, sizeof PsFlowSequencerStatDef);
  869. (*lpNumObjectTypes)++;
  870. // if there are any flows, process them
  871. if (PsFlowObjType.NumInstances && ghTciClient) {
  872. // initialize parent structure
  873. pid.ParentObjectTitleIndex = PsPipeObjType.ObjectNameTitleIndex;
  874. // loop over each interface checking for flow installed on them
  875. for (i=0; i<(unsigned)PsPipeObjType.NumInstances; i++) {
  876. // keep parent instance up to date
  877. pid.ParentObjectInstance = i;
  878. for (j=0; j<gpPI[i].numFlows; j++) {
  879. PWCHAR InstanceName;
  880. // copy flow instance definition and name
  881. pid.NameLength = (wcslen(gpPI[i].pFlowInfo[j].FriendlyName)+1) * sizeof(WCHAR);
  882. pid.ByteLength = sizeof(pid) + MULTIPLE_OF_EIGHT(pid.NameLength);
  883. WRITEBUF(&pid,sizeof pid);
  884. InstanceName = (PWCHAR) pdwBuf;
  885. memcpy(pdwBuf, gpPI[i].pFlowInfo[j].FriendlyName, pid.NameLength);
  886. pdwBuf = (PULONG)((PUCHAR)pdwBuf + MULTIPLE_OF_EIGHT(pid.NameLength));
  887. CorrectInstanceName(InstanceName);
  888. // get flow stats and copy them to the buffer
  889. size = gFlowStatLen;
  890. pStatsBuf = malloc(size);
  891. if (NULL == pStatsBuf) {
  892. *lpcbTotalBytes = (DWORD) 0;
  893. *lpNumObjectTypes = (DWORD) 0;
  894. LeaveCriticalSection(&ghPipeFlowCriticalSection);
  895. Trace0(DBG_ERROR, L"[CollectPschedPerformanceData]: Insufficient memory\n");
  896. return ERROR_SUCCESS;
  897. }
  898. status = TcQueryFlow(gpPI[i].pFlowInfo[j].InstanceName, (LPGUID)&GUID_QOS_STATISTICS_BUFFER,
  899. &size, pStatsBuf);
  900. if (ERROR_INSUFFICIENT_BUFFER==status) {
  901. free(pStatsBuf);
  902. size = gFlowStatLen = MULTIPLE_OF_EIGHT(size);
  903. pStatsBuf = (PPS_COMPONENT_STATS)malloc(gFlowStatLen);
  904. if (NULL == pStatsBuf)
  905. {
  906. *lpcbTotalBytes = (DWORD) 0;
  907. *lpNumObjectTypes = (DWORD) 0;
  908. LeaveCriticalSection(&ghPipeFlowCriticalSection);
  909. Trace0(DBG_ERROR, L"[CollectPschedPerformanceData]: Insufficient memory\n");
  910. return ERROR_SUCCESS;
  911. }
  912. status = TcQueryFlow(gpPI[i].pFlowInfo[j].InstanceName, (LPGUID)&GUID_QOS_STATISTICS_BUFFER,
  913. &size, pStatsBuf);
  914. }
  915. if (NO_ERROR != status) {
  916. free(pStatsBuf);
  917. *lpcbTotalBytes = (DWORD) 0;
  918. *lpNumObjectTypes = (DWORD) 0;
  919. LeaveCriticalSection(&ghPipeFlowCriticalSection);
  920. Trace1(DBG_ERROR,
  921. L"[CollectPschedPerformanceData]: TcQueryFlow failed with status 0x%x \n", status);
  922. return ERROR_SUCCESS;
  923. }
  924. // copy flow instance counter_block
  925. pcb.pcb.ByteLength = gFlowStatLen + sizeof(pcb);
  926. WRITEBUF(&pcb,sizeof pcb);
  927. WRITEBUF(pStatsBuf, gFlowStatLen);
  928. free(pStatsBuf);
  929. }
  930. }
  931. }
  932. // update the data pointer
  933. *lpcbTotalBytes = PsPipeObjType.TotalByteLength + PsFlowObjType.TotalByteLength;
  934. *lppData = ((PBYTE)*lppData) + *lpcbTotalBytes;
  935. // free up the sync lock
  936. LeaveCriticalSection(&ghPipeFlowCriticalSection);
  937. Trace0(DBG_INFO, L"[CollectPschedPerformanceData]: Succcess \n");
  938. return ERROR_SUCCESS;
  939. }
  940. /*
  941. Routine Description:
  942. This routine closes the open handles to PSched device performance counters
  943. Arguments:
  944. None.
  945. Return Value:
  946. ERROR_SUCCESS
  947. */
  948. DWORD APIENTRY ClosePschedPerformanceData()
  949. {
  950. if(InterlockedDecrement(&dwOpenCount) == 0)
  951. {
  952. // Clean up with traffic.dll and free up resources
  953. closePipeFlowInfo(&gpPI);
  954. // then deregister
  955. if(ghTciClient)
  956. TcDeregisterClient(ghTciClient);
  957. // get rid of the mutex
  958. DeleteCriticalSection(&ghPipeFlowCriticalSection);
  959. #if DBG
  960. TraceDeregister(gTraceID);
  961. #endif
  962. }
  963. return ERROR_SUCCESS;
  964. }
  965. //////////////////////////////////////////////////////////////////////
  966. //
  967. // PERF UTILITY STUFF BELOW!
  968. //
  969. //////////////////////////////////////////////////////////////////////
  970. BOOL WINAPI DllEntryPoint(
  971. HANDLE hDLL,
  972. DWORD dwReason,
  973. LPVOID lpReserved)
  974. {
  975. switch (dwReason)
  976. {
  977. case DLL_PROCESS_ATTACH:
  978. ghInst = hDLL;
  979. break;
  980. case DLL_PROCESS_DETACH:
  981. break;
  982. case DLL_THREAD_ATTACH:
  983. break;
  984. case DLL_THREAD_DETACH:
  985. break;
  986. } // switch
  987. return TRUE;
  988. }