1/* 2 Title: PolyPerf.cpp 3 4 Copyright (c) 2011, 2019 David C.J. Matthews 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License version 2.1 as published by the Free Software Foundation. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 19*/ 20 21#include <windows.h> 22#include <winperf.h> 23#include <stdlib.h> 24#include <malloc.h> 25#include <psapi.h> 26#include <stdio.h> 27#include <string.h> 28#include <stddef.h> 29 30#include "../polystatistics.h" 31 32// Statistics currently provided. These are copied from statistics.cpp 33// although the statistics returned may not match. 34enum { 35 PSC_THREADS = 0, // Total number of threads 36 PSC_THREADS_IN_ML, // Threads running ML code 37 PSC_THREADS_WAIT_IO, // Threads waiting for IO 38 PSC_THREADS_WAIT_MUTEX, // Threads waiting for a mutex 39 PSC_THREADS_WAIT_CONDVAR, // Threads waiting for a condition var 40 PSC_THREADS_WAIT_SIGNAL, // Special case - signal handling thread 41 PSC_GC_FULLGC, // Number of full garbage collections 42 PSC_GC_PARTIALGC, // Number of partial GCs 43 PSC_GC_SHARING, // Number of sharing passes 44 N_PS_COUNTERS 45}; 46 47enum { 48 PSS_TOTAL_HEAP = 0, // Total size of the local heap 49 PSS_AFTER_LAST_GC, // Space free after last GC 50 PSS_AFTER_LAST_FULLGC, // Space free after the last full GC 51 PSS_ALLOCATION, // Size of allocation space 52 PSS_ALLOCATION_FREE, // Space available in allocation area 53// PSS_CODE_SPACE, // Space for code 54// PSS_STACK_SPACE, // Space for stack 55 N_PS_SIZES 56}; 57 58enum { 59 PST_NONGC_UTIME = 0, 60 PST_NONGC_STIME, 61 PST_GC_UTIME, 62 PST_GC_STIME, 63 PST_NONGC_RTIME, 64 PST_GC_RTIME, 65 N_PS_TIMES 66}; 67 68#define N_PS_USER 8 69 70/* 71This DLL is a plug-in for Windows performance monitoring. The whole 72interface is extremely messy and seems to have remained unchanged 73from NT 3.5. The localised string names displayed in the performance 74monitor are held in the registry and need to be set up before this 75DLL can be loaded. Wix v3 supports the strings directly and this 76replaces the old method of using lodctr with .ini and .h files. 77The DLL is loaded by the performance monitor. In XP that seems to 78be part of the management console application mmc.exe and perfmon.exe 79seems to be just a stub. 80*/ 81 82extern "C" { 83 /* These are the functions exported from the DLL. The names here appear in the 84 HKLM\SYSTEM\CurrentControlSet\Services\PolyML\Performance registry key. 85 This DLL is loaded by mmc.exe and these functions are called to begin and 86 end monitoring and to extract the current performance values from the 87 shared memory. */ 88 __declspec(dllexport) PM_OPEN_PROC OpenPolyPerfMon; 89 __declspec(dllexport) PM_CLOSE_PROC ClosePolyPerfMon; 90 __declspec(dllexport) PM_COLLECT_PROC CollectPolyPerfMon; 91}; 92 93// Export the functions without any decoration. 94#ifndef _WIN64 95#pragma comment(linker, "/export:OpenPolyPerfMon=_OpenPolyPerfMon@4") 96#pragma comment(linker, "/export:ClosePolyPerfMon=_ClosePolyPerfMon@0") 97#pragma comment(linker, "/export:CollectPolyPerfMon=_CollectPolyPerfMon@16") 98#endif 99 100class PolyProcess { 101public: 102 ~PolyProcess(); 103 static PolyProcess* CreateProcessEntry(DWORD pID); 104 105 PolyProcess(); 106 107 unsigned char *sharedMem; // Pointer to shared memory 108 DWORD processID; // Process ID 109 WCHAR *processName; // Unicode name 110}; 111 112// This is the structure of the decoded statistics. These values 113// are set from the ASN1 coding. 114// The types of the fields must match the types set in the registry 115// by the installer (PolyML.wxs). For counters that is numberOfItems32 116// and for sizes they have to be DWORDs. The information we display 117// for sizes is always a ratio of two sizes (i.e. % full) so we 118// have to scale the values consistently to get them to fit. 119// Since we also provide type information in the PERF_COUNTER_DEFINITION 120// structure it's possible we may be able to override that but it's 121// not clear. 122typedef struct { 123 PERF_COUNTER_BLOCK header; 124 // Statistics decoded 125 UINT32 psCounters[N_PS_COUNTERS]; // numberOfItems32 126#define SIZEOF_COUNTER (sizeof(UINT32)) 127 DWORD psSizes[N_PS_SIZES]; // rawFraction/rawBase (i.e. 32-bits) 128#define SIZEOF_SIZE (sizeof(DWORD)) 129 FILETIME psTimers[N_PS_TIMES]; // timer100Ns 130#define SIZEOF_TIME (sizeof(FILETIME)) 131 UINT32 psUser[N_PS_USER]; // numberOfItems32 132#define SIZEOF_USER (sizeof(UINT32)) 133} statistics; 134 135PolyProcess::PolyProcess() 136{ 137 sharedMem = NULL; 138 processName = NULL; 139} 140 141PolyProcess::~PolyProcess() 142{ 143 if (sharedMem) 144 ::UnmapViewOfFile(sharedMem); 145 free(processName); 146} 147 148// Try to open the shared memory and if it succeeds create an entry for 149// this process. 150PolyProcess *PolyProcess::CreateProcessEntry(DWORD pId) 151{ 152 char shmName[MAX_PATH]; 153 sprintf(shmName, POLY_STATS_NAME "%lu", pId); 154 HANDLE hRemMemory = OpenFileMapping(FILE_MAP_READ, FALSE, shmName); 155 if (hRemMemory == NULL) 156 return NULL; // Probably not a Poly/ML process 157 158 unsigned char *sMem = (unsigned char*)MapViewOfFile(hRemMemory, FILE_MAP_READ, 0, 0, 0); 159 CloseHandle(hRemMemory); // We don't need this whether it succeeded or not 160 if (sMem == NULL) 161 return NULL; 162 if (*sMem != POLY_STATS_C_STATISTICS) 163 { 164 UnmapViewOfFile(sMem); 165 return NULL; 166 } 167 // Looks good. 168 PolyProcess *result = new PolyProcess; 169 result->processID = pId; 170 result->sharedMem = sMem; 171 172 // Find the name of the process. 173 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pId); 174 if (hProcess != NULL) 175 { 176 HMODULE hMod; 177 DWORD cbNeeded; 178 if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) 179 { 180 WCHAR processName[MAX_PATH]; 181 size_t len = 0; 182 processName[0] = 0; 183 if (GetModuleBaseNameW(hProcess, hMod, processName, sizeof(processName)/sizeof(WCHAR)) != 0) 184 { 185 // Remove any ".exe" or similar at the end 186 len = wcslen(processName); 187 if (len > 4 && processName[len-4] == '.') 188 len -= 4; 189 processName[len] = 0; 190 } 191 // Add the process Id in the name 192 _snwprintf(processName+len, MAX_PATH-len, L" (%lu)", pId); 193 // Copy it into the heap 194 result->processName = _wcsdup(processName); 195 } 196 CloseHandle(hProcess); 197 } 198 199 return result; 200} 201 202class ASN1Parse { 203public: 204 ASN1Parse (statistics *s, unsigned char *p): stats(s), ptr(p) {} 205 void asn1Decode(); 206 unsigned getLength(); 207 INT64 parseInt(unsigned length); 208 UINT32 parseUnsigned(unsigned length); 209 DWORD parseSize(unsigned length); 210 void parseAStatistic(int subTag, unsigned statlen); 211 void parseTime(FILETIME *ft, unsigned length); 212 213 statistics *stats; 214 unsigned char *ptr; 215}; 216 217// Decode the ASN1 encoding. If the decoding fails we just leave the 218// values as zero rather than returning any error. 219void ASN1Parse::asn1Decode() 220{ 221 unsigned char ch = *ptr++; 222 if (ch != POLY_STATS_C_STATISTICS) return; 223 unsigned overallLength = getLength(); 224 unsigned char *endOfData = ptr+overallLength; 225 while (ptr < endOfData) 226 { 227 // Decode a statistic 228 unsigned tag = *ptr++; 229 unsigned statLen = getLength(); 230 switch (tag) 231 { 232 case POLY_STATS_C_COUNTERSTAT: 233 parseAStatistic(POLY_STATS_C_COUNTER_VALUE, statLen); 234 break; 235 case POLY_STATS_C_SIZESTAT: 236 parseAStatistic(POLY_STATS_C_BYTE_COUNT, statLen); 237 break; 238 case POLY_STATS_C_TIMESTAT: 239 parseAStatistic(POLY_STATS_C_TIME, statLen); 240 break; 241 case POLY_STATS_C_USERSTAT: 242 parseAStatistic(POLY_STATS_C_COUNTER_VALUE, statLen); 243 break; 244 default: ptr += statLen; // Skip it; it's not known 245 } 246 } 247} 248 249// Return the length of the next item 250unsigned ASN1Parse::getLength() 251{ 252 unsigned ch = *ptr++; 253 if (ch & 0x80) 254 { 255 int lengthOfLength = ch & 0x7f; 256 unsigned length = 0; 257 // Ignore "indefinite length", it's not used here. 258 while (lengthOfLength--) 259 { 260 ch = *ptr++; 261 length = (length << 8) | ch; 262 } 263 return length; 264 } 265 else return ch; 266} 267 268// General case for integer. 269INT64 ASN1Parse::parseInt(unsigned length) 270{ 271 if (length == 0) return 0; 272 INT64 result = *ptr & 0x80 ? -1 : 0; 273 while (length--) result = (result << 8) | *ptr++; 274 return result; 275} 276 277UINT32 ASN1Parse::parseUnsigned(unsigned length) 278{ 279 INT64 value = parseInt(length); 280 if (value < 0) return 0; // Can't display negative nos 281 return (UINT32)value; 282} 283 284DWORD ASN1Parse::parseSize(unsigned length) 285{ 286 INT64 value = parseInt(length); 287 if (value < 0) return 0; // Can't display negative nos 288 return (DWORD)(value / 1024); // Return kilobytes 289} 290 291void ASN1Parse::parseTime(FILETIME *ft, unsigned length) 292{ 293 unsigned char *end = ptr+length; 294 UINT32 seconds = 0, useconds = 0; 295 while (ptr < end) 296 { 297 unsigned char tag = *ptr++; 298 unsigned elemLen = getLength(); 299 switch (tag) 300 { 301 case POLY_STATS_C_SECONDS: 302 seconds = parseUnsigned(elemLen); 303 break; 304 case POLY_STATS_C_MICROSECS: 305 useconds = parseUnsigned(elemLen); 306 break; 307 default: ptr += elemLen; 308 } 309 } 310 ULARGE_INTEGER li; 311 li.QuadPart = (ULONGLONG)seconds * 10000000 + (ULONGLONG)useconds * 10; 312 ft->dwHighDateTime = li.HighPart; 313 ft->dwLowDateTime = li.LowPart; 314} 315 316void ASN1Parse::parseAStatistic(int subTag, unsigned statLen) 317{ 318 unsigned char *endOfStat = ptr+statLen; 319 unsigned tagId = 0; 320 while (ptr < endOfStat) 321 { 322 unsigned char tag = *ptr++; 323 unsigned elemLen = getLength(); 324 switch (tag) 325 { 326 case POLY_STATS_C_IDENTIFIER: 327 // The identifier of the statistic 328 // We rely on the fact that the Id occurs before the value. 329 tagId = parseUnsigned(elemLen); 330 break; 331 332 case POLY_STATS_C_COUNTER_VALUE: 333 if (subTag = POLY_STATS_C_COUNTER_VALUE) 334 { 335 UINT32 cValue = parseUnsigned(elemLen); 336 // A counter value occurs in these statistics 337 switch (tagId) 338 { 339 case POLY_STATS_ID_THREADS: 340 stats->psCounters[PSC_THREADS] = cValue; break; 341 case POLY_STATS_ID_THREADS_IN_ML: 342 stats->psCounters[PSC_THREADS_IN_ML] = cValue; break; 343 case POLY_STATS_ID_THREADS_WAIT_IO: 344 stats->psCounters[PSC_THREADS_WAIT_IO] = cValue; break; 345 case POLY_STATS_ID_THREADS_WAIT_MUTEX: 346 stats->psCounters[PSC_THREADS_WAIT_MUTEX] = cValue; break; 347 case POLY_STATS_ID_THREADS_WAIT_CONDVAR: 348 stats->psCounters[PSC_THREADS_WAIT_CONDVAR] = cValue; break; 349 case POLY_STATS_ID_THREADS_WAIT_SIGNAL: 350 stats->psCounters[PSC_THREADS_WAIT_SIGNAL] = cValue; break; 351 case POLY_STATS_ID_GC_FULLGC: 352 stats->psCounters[PSC_GC_FULLGC] = cValue; break; 353 case POLY_STATS_ID_GC_PARTIALGC: 354 stats->psCounters[PSC_GC_PARTIALGC] = cValue; break; 355 case POLY_STATS_ID_GC_SHARING: 356 stats->psCounters[PSC_GC_SHARING] = cValue; break; 357 case POLY_STATS_ID_USER0: 358 stats->psUser[0] = cValue; break; 359 case POLY_STATS_ID_USER1: 360 stats->psUser[1] = cValue; break; 361 case POLY_STATS_ID_USER2: 362 stats->psUser[2] = cValue; break; 363 case POLY_STATS_ID_USER3: 364 stats->psUser[3] = cValue; break; 365 case POLY_STATS_ID_USER4: 366 stats->psUser[4] = cValue; break; 367 case POLY_STATS_ID_USER5: 368 stats->psUser[5] = cValue; break; 369 case POLY_STATS_ID_USER6: 370 stats->psUser[6] = cValue; break; 371 case POLY_STATS_ID_USER7: 372 stats->psUser[7] = cValue; break; 373 // Anything else is an unknown tag; skip 374 } 375 } 376 else ptr += elemLen; // Skip it - not expected here 377 break; 378 379 case POLY_STATS_C_BYTE_COUNT: 380 if (subTag == POLY_STATS_C_BYTE_COUNT) 381 { 382 DWORD cValue = parseSize(elemLen); 383 switch (tagId) 384 { 385 case POLY_STATS_ID_TOTAL_HEAP: 386 stats->psSizes[PSS_TOTAL_HEAP] = cValue; break; 387 case POLY_STATS_ID_AFTER_LAST_GC: 388 stats->psSizes[PSS_AFTER_LAST_GC] = cValue; break; 389 case POLY_STATS_ID_AFTER_LAST_FULLGC: 390 stats->psSizes[PSS_AFTER_LAST_FULLGC] = cValue; break; 391 case POLY_STATS_ID_ALLOCATION: 392 stats->psSizes[PSS_ALLOCATION] = cValue; break; 393 case POLY_STATS_ID_ALLOCATION_FREE: 394 stats->psSizes[PSS_ALLOCATION_FREE] = cValue; break; 395// case POLY_STATS_ID_CODE_SPACE: 396// stats->psSizes[PSS_CODE_SPACE] = cValue; break; 397// case POLY_STATS_ID_STACK_SPACE: 398// stats->psSizes[PSS_STACK_SPACE] = cValue; break; 399 } 400 } 401 else ptr += elemLen; // Skip it - not expected here 402 break; 403 404 case POLY_STATS_C_TIME: 405 if (subTag == POLY_STATS_C_TIME) 406 { 407 FILETIME ft = { 0, 0}; 408 parseTime(&ft, elemLen); 409 switch (tagId) 410 { 411 case POLY_STATS_ID_NONGC_UTIME: 412 stats->psTimers[PST_NONGC_UTIME] = ft; break; 413 case POLY_STATS_ID_NONGC_STIME: 414 stats->psTimers[PST_NONGC_STIME] = ft; break; 415 case POLY_STATS_ID_GC_UTIME: 416 stats->psTimers[PST_GC_UTIME] = ft; break; 417 case POLY_STATS_ID_GC_STIME: 418 stats->psTimers[PST_GC_STIME] = ft; break; 419 case POLY_STATS_ID_NONGC_RTIME: 420 stats->psTimers[PST_NONGC_RTIME] = ft; break; 421 case POLY_STATS_ID_GC_RTIME: 422 stats->psTimers[PST_GC_RTIME] = ft; break; 423 } 424 } 425 else ptr += elemLen; 426 break; 427 428 default: ptr += elemLen; // Unknown - skip 429 } 430 } 431} 432 433// Pointer to table of processes with the Poly/ML run-time 434static PolyProcess **polyProcesses; 435static DWORD numProcesses; 436 437// Open: Find the current ML instances. 438DWORD APIENTRY OpenPolyPerfMon(LPWSTR lpInstanceNames) 439{ 440 // Get the list of all process IDs. Because we don't know 441 // how many there are we increase the buffer size until the 442 // size returned is less than the buffer size. 443 DWORD buffItems = 10, numItems; 444 DWORD *processIds = NULL; 445 while (true) { 446 processIds = (DWORD*)malloc(buffItems * sizeof(DWORD)); 447 if (processIds == NULL) 448 return ERROR_NOT_ENOUGH_MEMORY; 449 DWORD bytesNeeded; 450 if (! EnumProcesses(processIds, buffItems * sizeof(DWORD), &bytesNeeded)) 451 return GetLastError(); 452 if (bytesNeeded < buffItems * sizeof(DWORD)) 453 { 454 numItems = bytesNeeded / sizeof(DWORD); 455 break; 456 } 457 buffItems = buffItems * 2; 458 free(processIds); 459 } 460 // How many of these processes provide the Poly/ML shared memory? 461 // Make an array big enough for all processes to simplify allocation. 462 polyProcesses = (PolyProcess **)malloc(numItems * sizeof(PolyProcess*)); 463 if (polyProcesses == NULL) 464 { 465 free(processIds); 466 free(polyProcesses); 467 return ERROR_NOT_ENOUGH_MEMORY; 468 } 469 470 for (DWORD dw = 0; dw < numItems; dw++) 471 { 472 // See if this is a Poly/ML process 473 PolyProcess *pProc = PolyProcess::CreateProcessEntry(processIds[dw]); 474 if (pProc != NULL) // We can use this 475 polyProcesses[numProcesses++] = pProc; 476 } 477 478 free(processIds); 479 return ERROR_SUCCESS; 480} 481 482// Delete the entries. 483DWORD APIENTRY ClosePolyPerfMon(void) 484{ 485 if (polyProcesses != NULL) 486 { 487 for (DWORD dw = 0; dw < numProcesses; dw++) 488 delete(polyProcesses[dw]); 489 free(polyProcesses); 490 } 491 polyProcesses = NULL; 492 numProcesses = 0; 493 494 return ERROR_SUCCESS; 495} 496 497static LPVOID allocBuffSpace(LPVOID * &lppData, LPDWORD &lpcbTotalBytes, DWORD &dwBytesAvailable, DWORD size) 498{ 499 if (dwBytesAvailable < size) return NULL; 500 LPVOID pResult = *lppData; 501 *lppData = (LPVOID)((char*)pResult + size); 502 memset(pResult, 0, size); 503 *lpcbTotalBytes += size; 504 dwBytesAvailable -= size; 505 return pResult; 506} 507 508// This is the entry that actually does the work. 509DWORD APIENTRY CollectPolyPerfMon( 510 /* IN */LPWSTR lpRequest, 511 /* lpRequest is either "Global" (all counters) or a list of counter numbers to return. 512 These are the indexes into counter table in the Perflib\009 registry entry. */ 513 /* IN OUT */LPVOID* lppData, 514 /* IN OUT */LPDWORD lpcbTotalBytes, 515 /* OUT */LPDWORD lpNumObjectTypes) 516{ 517 DWORD dwBytesAvailable = *lpcbTotalBytes; 518 LPVOID lpDataStart = *lppData; 519 *lpcbTotalBytes = 0; // Bytes written 520 *lpNumObjectTypes = 0; // Object types written 521 // For the moment we ignore the lpRequest argument and return all the counters. 522 523 // First find out where our strings are in the list. This depends on 524 // the strings installed by other applications/services so will vary 525 // from machine to machine. The installer will have added keys under 526 // our "service". If these can't be read then there's nothing we can do. 527 528 HKEY hkPerform; 529 LONG err; 530 531 err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 532 "SYSTEM\\CurrentControlSet\\Services\\PolyML\\Performance", 0, 533 KEY_READ, &hkPerform); 534 if (err != ERROR_SUCCESS) 535 return err; 536 537 DWORD dwType, dwSize, dwFirstCounter, dwFirstHelp; 538 539 dwSize = sizeof(dwFirstCounter); 540 err = RegQueryValueEx(hkPerform, "First Counter", 0, &dwType, (LPBYTE)&dwFirstCounter, &dwSize); 541 if (err != ERROR_SUCCESS) 542 { 543 RegCloseKey(hkPerform); 544 return err; 545 } 546 547 dwSize = sizeof(dwFirstHelp); 548 err = RegQueryValueEx(hkPerform, "First Help", 0, &dwType, (LPBYTE)&dwFirstHelp, &dwSize); 549 if (err != ERROR_SUCCESS) 550 { 551 RegCloseKey(hkPerform); 552 return err; 553 } 554 RegCloseKey(hkPerform); 555 556 // The actual strings are inserted by the installer. See PolyML.wxs. 557 unsigned stringCount = 0; 558 559 // Object header. Just one object. 560 PERF_OBJECT_TYPE *pObjectType = 561 (PERF_OBJECT_TYPE*)allocBuffSpace(lppData, lpcbTotalBytes, dwBytesAvailable, sizeof(PERF_OBJECT_TYPE)); 562 if (pObjectType == NULL) return ERROR_MORE_DATA; 563 pObjectType->HeaderLength = sizeof(PERF_OBJECT_TYPE); 564 pObjectType->ObjectNameTitleIndex = dwFirstCounter + stringCount*2; // First string is the name of the object 565 pObjectType->ObjectHelpTitleIndex = dwFirstHelp + (stringCount++)*2; 566 pObjectType->DetailLevel = PERF_DETAIL_NOVICE; 567 pObjectType->NumCounters = 0; 568 pObjectType->DefaultCounter = -1; 569 pObjectType->NumInstances = numProcesses; 570 571 // Counter block for each counter. 572 // First the numbers 573 PERF_COUNTER_DEFINITION *pCounters = 574 (PERF_COUNTER_DEFINITION*)allocBuffSpace(lppData, lpcbTotalBytes, dwBytesAvailable, 575 sizeof(PERF_COUNTER_DEFINITION) * N_PS_COUNTERS); 576 if (pCounters == NULL) return ERROR_MORE_DATA; 577 for (unsigned i = 0; i < N_PS_COUNTERS; i++) 578 { 579 pCounters[i].ByteLength = sizeof(PERF_COUNTER_DEFINITION); 580 pCounters[i].CounterNameTitleIndex = dwFirstCounter + stringCount*2; 581 pCounters[i].CounterHelpTitleIndex = dwFirstHelp + (stringCount++)*2; 582 pCounters[i].DetailLevel = PERF_DETAIL_NOVICE; 583 pCounters[i].CounterType = PERF_COUNTER_RAWCOUNT; 584 pCounters[i].CounterSize = SIZEOF_COUNTER; 585 pCounters[i].CounterOffset = offsetof(statistics, psCounters)+i*SIZEOF_COUNTER; 586 pObjectType->NumCounters++; 587 } 588 589 // The sizes are dealt with specially. We need to divide the values and express 590 // them as percentages. Each displayed value is followed by a base value. 591 PERF_COUNTER_DEFINITION *pSizes = 592 (PERF_COUNTER_DEFINITION*)allocBuffSpace(lppData, lpcbTotalBytes, dwBytesAvailable, 593 sizeof(PERF_COUNTER_DEFINITION) * 6); 594 if (pSizes == NULL) return ERROR_MORE_DATA; 595 // First - Heap usage after last GC 596 pSizes[0].ByteLength = sizeof(PERF_COUNTER_DEFINITION); 597 pSizes[0].CounterNameTitleIndex = dwFirstCounter + stringCount*2; 598 pSizes[0].CounterHelpTitleIndex = dwFirstHelp + (stringCount++)*2; 599 pSizes[0].DetailLevel = PERF_DETAIL_NOVICE; 600 pSizes[0].CounterType = PERF_RAW_FRACTION; 601 pSizes[0].CounterSize = SIZEOF_SIZE; 602 pSizes[0].CounterOffset = 603 offsetof(statistics, psSizes)+PSS_AFTER_LAST_GC*SIZEOF_SIZE; 604 pObjectType->NumCounters++; 605 pSizes[1].ByteLength = sizeof(PERF_COUNTER_DEFINITION); 606 pSizes[1].CounterNameTitleIndex = dwFirstCounter + stringCount*2; 607 pSizes[1].CounterHelpTitleIndex = dwFirstHelp + (stringCount++)*2; 608 pSizes[1].DetailLevel = PERF_DETAIL_NOVICE; 609 pSizes[1].CounterType = PERF_RAW_BASE; 610 pSizes[1].CounterSize = SIZEOF_SIZE; 611 pSizes[1].CounterOffset = 612 offsetof(statistics, psSizes)+PSS_TOTAL_HEAP*SIZEOF_SIZE; 613 pObjectType->NumCounters++; 614 // Second - Heap usage after last full GC 615 pSizes[2].ByteLength = sizeof(PERF_COUNTER_DEFINITION); 616 pSizes[2].CounterNameTitleIndex = dwFirstCounter + stringCount*2; 617 pSizes[2].CounterHelpTitleIndex = dwFirstHelp + (stringCount++)*2; 618 pSizes[2].DetailLevel = PERF_DETAIL_NOVICE; 619 pSizes[2].CounterType = PERF_RAW_FRACTION; 620 pSizes[2].CounterSize = SIZEOF_SIZE; 621 pSizes[2].CounterOffset = 622 offsetof(statistics, psSizes)+PSS_AFTER_LAST_FULLGC*SIZEOF_SIZE; 623 pObjectType->NumCounters++; 624 pSizes[3].ByteLength = sizeof(PERF_COUNTER_DEFINITION); 625 pSizes[3].CounterNameTitleIndex = dwFirstCounter + stringCount*2; 626 pSizes[3].CounterHelpTitleIndex = dwFirstHelp + (stringCount++)*2; 627 pSizes[3].DetailLevel = PERF_DETAIL_NOVICE; 628 pSizes[3].CounterType = PERF_RAW_BASE; 629 pSizes[3].CounterSize = SIZEOF_SIZE; 630 pSizes[3].CounterOffset = 631 offsetof(statistics, psSizes)+PSS_TOTAL_HEAP*SIZEOF_SIZE; 632 pObjectType->NumCounters++; 633 // Third - Unreserved space in allocation area 634 pSizes[4].ByteLength = sizeof(PERF_COUNTER_DEFINITION); 635 pSizes[4].CounterNameTitleIndex = dwFirstCounter + stringCount*2; 636 pSizes[4].CounterHelpTitleIndex = dwFirstHelp + (stringCount++)*2; 637 pSizes[4].DetailLevel = PERF_DETAIL_NOVICE; 638 pSizes[4].CounterType = PERF_RAW_FRACTION; 639 pSizes[4].CounterSize = SIZEOF_SIZE; 640 pSizes[4].CounterOffset = 641 offsetof(statistics, psSizes)+PSS_ALLOCATION_FREE*SIZEOF_SIZE; 642 pObjectType->NumCounters++; 643 pSizes[5].ByteLength = sizeof(PERF_COUNTER_DEFINITION); 644 pSizes[5].CounterNameTitleIndex = dwFirstCounter + stringCount*2; 645 pSizes[5].CounterHelpTitleIndex = dwFirstHelp + (stringCount++)*2; 646 pSizes[5].DetailLevel = PERF_DETAIL_NOVICE; 647 pSizes[5].CounterType = PERF_RAW_BASE; 648 pSizes[5].CounterSize = SIZEOF_SIZE; 649 pSizes[5].CounterOffset = 650 offsetof(statistics, psSizes)+PSS_ALLOCATION*SIZEOF_SIZE; 651 pObjectType->NumCounters++; 652 653 // Then the times 654 PERF_COUNTER_DEFINITION *pTimes = 655 (PERF_COUNTER_DEFINITION*)allocBuffSpace(lppData, lpcbTotalBytes, dwBytesAvailable, 656 sizeof(PERF_COUNTER_DEFINITION) * N_PS_TIMES); 657 if (pTimes == NULL) return ERROR_MORE_DATA; 658 for (unsigned k = 0; k < N_PS_TIMES; k++) 659 { 660 pTimes[k].ByteLength = sizeof(PERF_COUNTER_DEFINITION); 661 pTimes[k].CounterNameTitleIndex = dwFirstCounter + stringCount*2; 662 pTimes[k].CounterHelpTitleIndex = dwFirstHelp + (stringCount++)*2; 663 pTimes[k].DetailLevel = PERF_DETAIL_NOVICE; 664 pTimes[k].CounterType = PERF_100NSEC_TIMER; 665 pTimes[k].CounterSize = SIZEOF_TIME; 666 pTimes[k].CounterOffset = offsetof(statistics, psTimers)+k*SIZEOF_TIME; 667 pObjectType->NumCounters++; 668 } 669 670 // Finally the user counters 671 PERF_COUNTER_DEFINITION *pUsers = 672 (PERF_COUNTER_DEFINITION*)allocBuffSpace(lppData, lpcbTotalBytes, dwBytesAvailable, 673 sizeof(PERF_COUNTER_DEFINITION) * N_PS_USER); 674 if (pUsers == NULL) return ERROR_MORE_DATA; 675 for (unsigned l = 0; l < N_PS_USER; l++) 676 { 677 pUsers[l].ByteLength = sizeof(PERF_COUNTER_DEFINITION); 678 pUsers[l].CounterNameTitleIndex = dwFirstCounter + stringCount*2; 679 pUsers[l].CounterHelpTitleIndex = dwFirstHelp + (stringCount++)*2; 680 pUsers[l].DetailLevel = PERF_DETAIL_NOVICE; 681 pUsers[l].CounterType = PERF_COUNTER_RAWCOUNT; 682 pUsers[l].CounterSize = SIZEOF_USER; 683 pUsers[l].CounterOffset = offsetof(statistics, psUser)+l*SIZEOF_USER; 684 pObjectType->NumCounters++; 685 } 686 687 pObjectType->DefinitionLength = *lpcbTotalBytes; // End of definitions; start of instance data 688 689 // Instance data - One entry for each process. Includes the instance name (i.e. the process) 690 // and the counter data. 691 for (DWORD dw = 0; dw < numProcesses; dw++) 692 { 693 PERF_INSTANCE_DEFINITION *pInst = 694 (PERF_INSTANCE_DEFINITION*)allocBuffSpace(lppData, lpcbTotalBytes, dwBytesAvailable, sizeof(PERF_INSTANCE_DEFINITION)); 695 if (pInst == NULL) return ERROR_MORE_DATA; 696 PolyProcess *pProc = polyProcesses[dw]; 697 pInst->UniqueID = PERF_NO_UNIQUE_ID; // Better to show the name 698 pInst->NameOffset = sizeof(PERF_INSTANCE_DEFINITION); // Name follows 699 DWORD len = (DWORD)wcslen(pProc->processName); 700 DWORD byteLength = (len+1)*sizeof(WCHAR); // Length including terminators 701 pInst->NameLength = byteLength; 702 byteLength = (byteLength + 7) / 8 * 8; // Must be rounded up to an eight-byte boundary. 703 pInst->ByteLength = byteLength + sizeof(PERF_INSTANCE_DEFINITION); 704 WCHAR *pName = (WCHAR*)allocBuffSpace(lppData, lpcbTotalBytes, dwBytesAvailable, byteLength); 705 wcscpy(pName, pProc->processName); 706 707 // Now the statistics including a PERF_COUNTER_BLOCK 708 DWORD statSize = (sizeof(statistics) + 7) / 8 * 8; 709 statistics *pStats = 710 (statistics*)allocBuffSpace(lppData, lpcbTotalBytes, dwBytesAvailable, statSize); 711 if (pStats == NULL) return ERROR_MORE_DATA; 712 713 pStats->header.ByteLength = sizeof(PERF_COUNTER_BLOCK)+statSize; 714 ASN1Parse decode(pStats, pProc->sharedMem); 715 decode.asn1Decode(); 716 } 717 718 pObjectType->TotalByteLength = *lpcbTotalBytes; 719 *lpNumObjectTypes = 1; // Single object 720 return ERROR_SUCCESS; 721} 722