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.

434 lines
12 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1998, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // perflib.cpp
  8. //
  9. // SYNOPSIS
  10. //
  11. // Defines classes for implementing a PerfMon DLL.
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 09/06/1998 Original version.
  16. // 10/19/1998 Throw LONG's on error.
  17. // 03/18/1999 Data buffer must be 8-byte aligned.
  18. // 05/13/1999 Fix offset to first help text.
  19. //
  20. ///////////////////////////////////////////////////////////////////////////////
  21. #include <ias.h>
  22. #include <align.h>
  23. #include <perflib.h>
  24. /////////
  25. // Application offsets into the string title database.
  26. /////////
  27. DWORD theFirstCounter;
  28. DWORD theFirstHelp;
  29. /////////
  30. // Reads the name offsets for a given application.
  31. /////////
  32. LONG GetCounterOffsets(PCWSTR appName) throw ()
  33. {
  34. // Build the name of the performance key.
  35. WCHAR keyPath[MAX_PATH + 1] = L"SYSTEM\\CurrentControlSet\\Services\\";
  36. wcscat(keyPath, appName);
  37. wcscat(keyPath, L"\\Performance");
  38. // Open the performance key.
  39. LONG status;
  40. HKEY hKey;
  41. status = RegOpenKeyExW(
  42. HKEY_LOCAL_MACHINE,
  43. keyPath,
  44. 0,
  45. KEY_READ,
  46. &hKey
  47. );
  48. if (status != NO_ERROR) { return status; }
  49. // Get the first counter offset.
  50. DWORD type, cbData = sizeof(DWORD);
  51. status = RegQueryValueExW(
  52. hKey,
  53. L"First Counter",
  54. 0,
  55. &type,
  56. (PBYTE)&theFirstCounter,
  57. &cbData
  58. );
  59. if (status != ERROR_SUCCESS) { goto close_key; };
  60. // Make sure it's a DWORD.
  61. if (type != REG_DWORD || cbData != sizeof(DWORD))
  62. {
  63. status = ERROR_BADKEY;
  64. goto close_key;
  65. }
  66. // Get the first help offset.
  67. status = RegQueryValueExW(
  68. hKey,
  69. L"First Help",
  70. 0,
  71. &type,
  72. (PBYTE)&theFirstHelp,
  73. &cbData
  74. );
  75. if (status != ERROR_SUCCESS) { goto close_key; };
  76. // Make sure it's a DWORD.
  77. if (type != REG_DWORD || cbData != sizeof(DWORD))
  78. {
  79. status = ERROR_BADKEY;
  80. goto close_key;
  81. }
  82. close_key:
  83. RegCloseKey(hKey);
  84. return status;
  85. }
  86. PBYTE PerfCounterBlock::collect(PBYTE first, PBYTE last)
  87. {
  88. PBYTE retval = first + pcb.ByteLength;
  89. if (retval > last) { throw ERROR_MORE_DATA; }
  90. memcpy(first, this, pcb.ByteLength);
  91. return retval;
  92. }
  93. PerfCounterBlock* PerfCounterBlock::create(DWORD numDWORDs)
  94. {
  95. // Compute various lengths.
  96. DWORD cntrLength = sizeof(DWORD) * numDWORDs;
  97. DWORD byteLength = sizeof(PERF_COUNTER_BLOCK) + cntrLength;
  98. // Allocate enough extra space for the counters.
  99. PerfCounterBlock* pcb = new (operator new(byteLength)) PerfCounterBlock;
  100. // Initialize the PERF_COUNTER_BLOCK.
  101. pcb->pcb.ByteLength = byteLength;
  102. // Initialize the counters.
  103. memset(pcb->counters, 0, cntrLength);
  104. return pcb;
  105. }
  106. PBYTE PerfInstanceDefinition::collect(PBYTE first, PBYTE last)
  107. {
  108. PBYTE retval = first + pid.ByteLength;
  109. if (retval > last) { throw ERROR_MORE_DATA; }
  110. memcpy(first, this, pid.ByteLength);
  111. return retval;
  112. }
  113. PerfInstanceDefinition* PerfInstanceDefinition::create(
  114. PCWSTR name,
  115. LONG uniqueID
  116. )
  117. {
  118. // Compute various lengths.
  119. DWORD nameLength = name ? (wcslen(name) + 1) * sizeof(WCHAR) : 0;
  120. DWORD byteLength = sizeof(PERF_INSTANCE_DEFINITION) + nameLength;
  121. // Keep everything DWORD aligned.
  122. byteLength = ROUND_UP_COUNT(byteLength, ALIGN_DWORD);
  123. // Allocate enough extra space for the name.
  124. PerfInstanceDefinition* pid = new (operator new(byteLength))
  125. PerfInstanceDefinition;
  126. // Initialize the PERF_INSTANCE_DEFINITION.
  127. pid->pid.ByteLength = byteLength;
  128. pid->pid.ParentObjectTitleIndex = 0;
  129. pid->pid.ParentObjectInstance = 0;
  130. pid->pid.UniqueID = uniqueID;
  131. pid->pid.NameOffset = sizeof(PERF_INSTANCE_DEFINITION);
  132. pid->pid.NameLength = nameLength;
  133. // Initialize the name.
  134. memcpy(pid->name, name, nameLength);
  135. return pid;
  136. }
  137. PBYTE PerfInstance::collect(PBYTE first, PBYTE last)
  138. {
  139. return pcb->collect((pid.get() ? pid->collect(first, last) : first), last);
  140. }
  141. PerfObjectType::~PerfObjectType() throw ()
  142. {
  143. for (MyVec::iterator i = instances.begin(); i != instances.end(); ++i)
  144. {
  145. delete *i;
  146. }
  147. }
  148. void PerfObjectType::clear() throw ()
  149. {
  150. // Never clear the default instance.
  151. if (pot.NumInstances != -1)
  152. {
  153. for (MyVec::iterator i = instances.begin(); i != instances.end(); ++i)
  154. {
  155. delete *i;
  156. }
  157. instances.clear();
  158. }
  159. }
  160. void PerfObjectType::addInstance(PCWSTR name, LONG uniqueID)
  161. {
  162. // Resize first. If we threw an exception in push_back, we'd leak.
  163. instances.reserve(size() + 1);
  164. instances.push_back(new PerfInstance(name, uniqueID, numDWORDs));
  165. }
  166. PBYTE PerfObjectType::collect(PBYTE first, PBYTE last)
  167. {
  168. // Reserve enough room for the type definition.
  169. PBYTE retval = first + pot.DefinitionLength;
  170. if (retval > last) { throw ERROR_MORE_DATA; }
  171. // Give the user a chance to fill-in the data.
  172. if (dataSource) { dataSource(*this); }
  173. if (pot.NumInstances == -1)
  174. {
  175. // We always have exactly one instance.
  176. retval = at(0).collect(retval, last);
  177. }
  178. else if (instances.empty())
  179. {
  180. // If we're empty, then we right one instance's worth of zeros.
  181. // Otherwise, PerfMon.exe has a tendency to display garbage.
  182. DWORD nbyte = sizeof(PERF_INSTANCE_DEFINITION) +
  183. sizeof(PERF_COUNTER_BLOCK) +
  184. numDWORDs * sizeof(DWORD);
  185. if (retval + nbyte > last) { throw ERROR_MORE_DATA; }
  186. memset(retval, 0, nbyte);
  187. retval += nbyte;
  188. pot.NumInstances = 0;
  189. }
  190. else
  191. {
  192. // iterate through and collect all instances.
  193. for (size_type i = 0; i < size(); ++i)
  194. {
  195. retval = at(i).collect(retval, last);
  196. }
  197. pot.NumInstances = (DWORD)size();
  198. }
  199. // Now that we've collected all the data, we can finish the definition ...
  200. retval = (PBYTE)ROUND_UP_POINTER(retval, ALIGN_QUAD);
  201. pot.TotalByteLength = retval - first;
  202. QueryPerformanceCounter(&pot.PerfTime);
  203. // ... and copy in the data.
  204. memcpy(first, &pot, pot.DefinitionLength);
  205. return retval;
  206. }
  207. PerfObjectType* PerfObjectType::create(const PerfObjectTypeDef& def)
  208. {
  209. // Allocate a new object.
  210. size_t counterLength = def.numCounters * sizeof(PERF_COUNTER_DEFINITION);
  211. size_t nbyte = sizeof(PerfObjectType) + counterLength;
  212. std::auto_ptr<PerfObjectType> po(new (operator new(nbyte)) PerfObjectType);
  213. // Save the data source.
  214. po->dataSource = def.dataSource;
  215. // Fill in the PERF_COUNTER_DEFINITION structs first since we also
  216. // need to compute the object detail level.
  217. DWORD detailLevel = PERF_DETAIL_WIZARD;
  218. PERF_COUNTER_DEFINITION* dst = po->pcd;
  219. PerfCounterDef* src = def.counters;
  220. DWORD offset = sizeof(PERF_COUNTER_BLOCK);
  221. for (DWORD i = 0; i < def.numCounters; ++i, ++dst, ++src)
  222. {
  223. dst->ByteLength = sizeof(PERF_COUNTER_DEFINITION);
  224. dst->CounterNameTitleIndex = src->nameTitleOffset + theFirstCounter;
  225. dst->CounterNameTitle = 0;
  226. dst->CounterHelpTitleIndex = src->nameTitleOffset + theFirstHelp;
  227. dst->CounterHelpTitle = 0;
  228. dst->DefaultScale = src->defaultScale;
  229. dst->DetailLevel = src->detailLevel;
  230. dst->CounterType = src->counterType;
  231. dst->CounterOffset = offset;
  232. // Compute the counter size.
  233. switch (dst->CounterOffset & 0x300)
  234. {
  235. case PERF_SIZE_DWORD:
  236. dst->CounterSize = sizeof(DWORD);
  237. break;
  238. case PERF_SIZE_LARGE:
  239. dst->CounterSize = sizeof(LARGE_INTEGER);
  240. break;
  241. default:
  242. dst->CounterSize = 0;
  243. }
  244. // Update the offset based on the size.
  245. offset += dst->CounterSize;
  246. // The object detail level is the minimum counter detail level.
  247. if (dst->DetailLevel < detailLevel)
  248. {
  249. detailLevel = dst->DetailLevel;
  250. }
  251. }
  252. // Calculate the number of DWORD's of counter data.
  253. po->numDWORDs = (offset - sizeof(PERF_COUNTER_BLOCK)) / sizeof(DWORD);
  254. // Fill in the PERF_OBJECT_TYPE struct.
  255. po->pot.DefinitionLength = sizeof(PERF_OBJECT_TYPE) + counterLength;
  256. po->pot.HeaderLength = sizeof(PERF_OBJECT_TYPE);
  257. po->pot.ObjectNameTitleIndex = def.nameTitleOffset + theFirstCounter;
  258. po->pot.ObjectNameTitle = 0;
  259. po->pot.ObjectHelpTitleIndex = def.nameTitleOffset + theFirstHelp;
  260. po->pot.ObjectHelpTitle = 0;
  261. po->pot.DetailLevel = detailLevel;
  262. po->pot.NumCounters = def.numCounters;
  263. po->pot.DefaultCounter = def.defaultCounter;
  264. po->pot.NumInstances = 0;
  265. po->pot.CodePage = 0;
  266. QueryPerformanceFrequency(&(po->pot.PerfFreq));
  267. // If it doesn't support multiple instances, then it must have exactly one.
  268. if (!def.multipleInstances)
  269. {
  270. po->pot.NumInstances = -1;
  271. po->instances.reserve(1);
  272. po->instances.push_back(new PerfInstance(po->numDWORDs));
  273. }
  274. return po.release();
  275. }
  276. PerfCollector::~PerfCollector() throw ()
  277. {
  278. close();
  279. }
  280. void PerfCollector::clear() throw ()
  281. {
  282. for (PerfObjectType** i = types; *i; ++i)
  283. {
  284. (*i)->clear();
  285. }
  286. }
  287. void PerfCollector::open(const PerfCollectorDef& def)
  288. {
  289. // Read the registry.
  290. LONG success = GetCounterOffsets(def.name);
  291. if (success != NO_ERROR) { throw success; }
  292. // Allocate a null terminated array to hold the object types.
  293. DWORD len = def.numTypes + 1;
  294. types = new PerfObjectType*[len];
  295. memset(types, 0, sizeof(PerfObjectType*) * len);
  296. // Create the various object types.
  297. for (DWORD i = 0; i < def.numTypes; ++i)
  298. {
  299. types[i] = PerfObjectType::create(def.types[i]);
  300. }
  301. }
  302. void PerfCollector::collect(
  303. PCWSTR values,
  304. PVOID& data,
  305. DWORD& numBytes,
  306. DWORD& numTypes
  307. )
  308. {
  309. PBYTE cursor = (PBYTE)data;
  310. PBYTE last = cursor + numBytes;
  311. numBytes = 0;
  312. numTypes = 0;
  313. if (values == NULL || *values == L'\0' || !wcscmp(values, L"Global"))
  314. {
  315. // For global we get everything.
  316. for (PerfObjectType** i = types; *i; ++i)
  317. {
  318. cursor = (*i)->collect(cursor, last);
  319. ++numTypes;
  320. }
  321. }
  322. else if (wcsncmp(values, L"Foreign", 7) && wcscmp(values, L"Costly"))
  323. {
  324. // It's not Global, Foreign, or Costly, so we parse the tokens and
  325. // convert them to title indices.
  326. PWSTR endptr;
  327. PCWSTR nptr = values;
  328. ULONG index = wcstoul(nptr, &endptr, 10);
  329. while (endptr != nptr)
  330. {
  331. // We got a valid index, so find the object ...
  332. for (PerfObjectType** i = types; *i; ++i)
  333. {
  334. if ((*i)->getIndex() == index)
  335. {
  336. // ... and collect the data.
  337. cursor = (*i)->collect(cursor, last);
  338. ++numTypes;
  339. break;
  340. }
  341. }
  342. index = wcstoul(nptr = endptr, &endptr, 10);
  343. }
  344. }
  345. numBytes = cursor - (PBYTE)data;
  346. data = cursor;
  347. }
  348. void PerfCollector::close() throw ()
  349. {
  350. if (types)
  351. {
  352. for (PerfObjectType** i = types ; *i; ++i)
  353. {
  354. delete *i;
  355. }
  356. delete[] types;
  357. types = NULL;
  358. }
  359. }