[WSD] Add perfmon counter object implementation. WSD installation now
[mirror/winof/.git] / ulp / wsd / user / ibsp_perfmon.c
1 /*\r
2  * Copyright (c) 2005 SilverStorm Technologies.  All rights reserved.\r
3  *\r
4  * This software is available to you under the OpenIB.org BSD license\r
5  * below:\r
6  *\r
7  *     Redistribution and use in source and binary forms, with or\r
8  *     without modification, are permitted provided that the following\r
9  *     conditions are met:\r
10  *\r
11  *      - Redistributions of source code must retain the above\r
12  *        copyright notice, this list of conditions and the following\r
13  *        disclaimer.\r
14  *\r
15  *      - Redistributions in binary form must reproduce the above\r
16  *        copyright notice, this list of conditions and the following\r
17  *        disclaimer in the documentation and/or other materials\r
18  *        provided with the distribution.\r
19  *\r
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
23  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
24  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
25  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
27  * SOFTWARE.\r
28  *\r
29  * $Id$\r
30  */\r
31 \r
32 \r
33 #include <string.h>\r
34 #include "ibspdebug.h"\r
35 #include "ibsp_perfmon.h"\r
36 \r
37 \r
38 struct _ibsp_pm_definition      g_ibsp_pm_def; /* IB WSD performance object */\r
39 \r
40 \r
41 void\r
42 IBSPPmInit( void )\r
43 {\r
44         HANDLE                          h_mapping;\r
45         BOOL                            just_created;\r
46         SECURITY_ATTRIBUTES     sec_attr;\r
47 \r
48         IBSP_ENTER( IBSP_DBG_PERFMON );\r
49 \r
50         g_pm_stat.idx = INVALID_IDX;\r
51         g_pm_stat.p_shmem = NULL;\r
52 \r
53         sec_attr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
54         sec_attr.bInheritHandle = FALSE;\r
55 \r
56         if( !ConvertStringSecurityDescriptorToSecurityDescriptor(\r
57                 IBSP_PM_SEC_STRING, SDDL_REVISION_1,\r
58                 &(sec_attr.lpSecurityDescriptor), NULL ) )\r
59         {\r
60                 IBSP_ERROR( ("SecurityDescriptor error %d\n", GetLastError()) );\r
61                 return;\r
62         }\r
63 \r
64         h_mapping = CreateFileMapping(\r
65                 INVALID_HANDLE_VALUE,   // use paging file\r
66                 &sec_attr,                              // security attributes\r
67                 PAGE_READWRITE,                 // read/write access\r
68                 0,                                              // size: high 32-bits\r
69                 sizeof(pm_shmem_t),             // size: low 32-bits\r
70                 IBSP_PM_MAPPED_OBJ_NAME );\r
71 \r
72         LocalFree( sec_attr.lpSecurityDescriptor );\r
73 \r
74         if( h_mapping == NULL )\r
75         {\r
76                 IBSP_ERROR_EXIT( ("CreateFileMapping error %d\n", GetLastError()) );\r
77                 return;\r
78         }\r
79 \r
80         just_created = (GetLastError() != ERROR_ALREADY_EXISTS);\r
81 \r
82         /* Get a pointer to the shared memory. */\r
83         g_pm_stat.p_shmem = MapViewOfFile(\r
84                                                 h_mapping,     // object handle\r
85                                                 FILE_MAP_ALL_ACCESS,\r
86                                                 0,              // high offset:  map from\r
87                                                 0,              // low offset:   beginning\r
88                                                 0);             // num bytes to map\r
89 \r
90         /* Now that we have the view mapped, we don't need the mapping handle. */\r
91         g_pm_stat.h_mapping = h_mapping;\r
92 \r
93         if( g_pm_stat.p_shmem == NULL )\r
94         {\r
95                 IBSP_ERROR( ("MapViewOfFile returned %d\n", GetLastError()) );\r
96                 return;\r
97         }\r
98         \r
99         if( just_created )\r
100         {\r
101                 /*\r
102                  * Reserve instance 0 for fallback counters\r
103                  * Apps that can't get a dedicated slot will share this one.\r
104                  */\r
105                 wcscpy( g_pm_stat.p_shmem->obj[0].app_name,\r
106                         IBSP_PM_TOTAL_COUNTER_NAME );\r
107                 g_pm_stat.p_shmem->obj[0].taken = 1;\r
108         }\r
109 \r
110         IBSP_EXIT( IBSP_DBG_PERFMON );\r
111 }\r
112 \r
113 \r
114 /*\r
115  * We always get a slot - either an individual one, or fall back on the \r
116  * common one.\r
117  */\r
118 void\r
119 IBSPPmGetSlot( void )\r
120 {\r
121         WCHAR           mod_path[MAX_PATH];\r
122         WCHAR*          buf;\r
123         int                     idx;\r
124         size_t          name_len;\r
125         WCHAR           id_str[12];\r
126         mem_obj_t       *p_slot;\r
127         pm_shmem_t* p_mem = g_pm_stat.p_shmem;\r
128 \r
129         IBSP_ENTER( IBSP_DBG_PERFMON );\r
130 \r
131         if( g_pm_stat.p_shmem == NULL )\r
132         {\r
133                 g_pm_stat.pdata = g_pm_stat.fall_back_data;\r
134                 return;\r
135         }\r
136 \r
137         GetModuleFileNameW( NULL, mod_path, MAX_PATH );\r
138 \r
139         buf = wcsrchr( mod_path, L'\\' );\r
140         if( !buf )\r
141                 buf = mod_path;\r
142         else\r
143                 buf++;\r
144 \r
145         /* The max length is 11, one for the ':', and 10 for the process ID. */\r
146         id_str[0] = ':';\r
147         _ultow( GetCurrentProcessId(), &id_str[1], 10 );\r
148 \r
149         /* Cap the length of the application. */\r
150         name_len = min( wcslen( buf ),\r
151                 IBSP_PM_APP_NAME_SIZE - 1 - wcslen( id_str ) );\r
152 \r
153         /* instance 0 is taken for "Total" counters, so don't try it */\r
154         for( idx = 1; idx < IBSP_PM_NUM_INSTANCES; idx++)\r
155         {\r
156                 /* Compare with 0, exchange with 1 */\r
157                 if( InterlockedCompareExchange(\r
158                         &g_pm_stat.p_shmem->obj[idx].taken, 1, 0 ) )\r
159                 {\r
160                         continue;\r
161                 }\r
162 \r
163                 p_slot = &g_pm_stat.p_shmem->obj[idx];\r
164 \r
165                 /* Copy the app name. */\r
166                 CopyMemory( p_slot->app_name, buf, name_len * sizeof(WCHAR) );\r
167                 CopyMemory( &p_slot->app_name[name_len], id_str,\r
168                         (wcslen( id_str ) + 1) * sizeof(WCHAR) );\r
169 \r
170                 g_pm_stat.idx = idx;\r
171                 g_pm_stat.pdata = g_pm_stat.p_shmem->obj[idx].data;\r
172                 IBSP_TRACE2( IBSP_DBG_PERFMON,\r
173                         ("%S got slot %d\n", p_slot->app_name, idx) );\r
174                 break;\r
175         }\r
176         \r
177         if( idx == IBSP_PM_NUM_INSTANCES )\r
178         {\r
179                 /*\r
180                  * Assign "Total" slot for this process to avoid loosing precious\r
181                  * statistic.  Keep saved idx INVALID so data won't be flushed during\r
182                  * process closeout.\r
183                  */\r
184                 g_pm_stat.pdata = p_mem->obj[0].data;\r
185         }\r
186                 \r
187         IBSP_EXIT( IBSP_DBG_PERFMON );\r
188 }\r
189 \r
190 \r
191 void\r
192 IBSPPmReleaseSlot( void )\r
193 {\r
194         mem_obj_t       *p_slot;\r
195         int                     idx;\r
196 \r
197         /* perfmon never get registered itself in shared mem buffer */\r
198         if ( g_pm_stat.idx == INVALID_IDX )\r
199                 return;\r
200 \r
201         if( g_pm_stat.p_shmem == NULL )\r
202                 return;\r
203 \r
204         p_slot = &g_pm_stat.p_shmem->obj[g_pm_stat.idx];\r
205 \r
206         /* Add all the data to the "Total" bin (0) */\r
207         for( idx = 0; idx < IBSP_PM_NUM_COUNTERS; idx++ )\r
208         {\r
209                 InterlockedExchangeAdd64( &g_pm_stat.p_shmem->obj[0].data[idx],\r
210                         InterlockedExchange64( &g_pm_stat.pdata[idx], 0 ) );\r
211         }\r
212         ZeroMemory( p_slot->app_name, sizeof(p_slot->app_name) );\r
213         InterlockedExchange( &p_slot->taken, 0 );\r
214 \r
215         g_pm_stat.idx = INVALID_IDX;\r
216 \r
217         IBSP_EXIT( IBSP_DBG_PERFMON );\r
218 }\r
219 \r
220 \r
221 static BOOL\r
222 __PmIsQuerySupported(\r
223         IN                              WCHAR*                                  p_query_str )\r
224 {\r
225         if( p_query_str == NULL )\r
226                 return TRUE;\r
227 \r
228         if( *p_query_str == 0 )\r
229                 return TRUE;\r
230 \r
231         if( wcsstr( p_query_str, L"Global" ) != NULL )\r
232                 return TRUE;\r
233 \r
234         if( wcsstr( p_query_str, L"Foreign" ) != NULL )\r
235                 return FALSE;\r
236 \r
237         if( wcsstr( p_query_str, L"Costly" ) != NULL )\r
238                 return FALSE;\r
239 \r
240         else\r
241                 return TRUE;\r
242 }\r
243 \r
244 \r
245 /*\r
246  * http://msdn.microsoft.com/library/en-us/perfctrs/perf/openperformancedata.asp\r
247  */\r
248 DWORD APIENTRY\r
249 IBSPPmOpen(\r
250         IN                              LPWSTR                                          lpDeviceNames )\r
251 {\r
252         DWORD status = ERROR_SUCCESS;\r
253         HKEY  pm_hkey = NULL;\r
254         DWORD data_size;\r
255         DWORD data_type;\r
256         DWORD first_counter = 0;\r
257         DWORD first_help = 0;\r
258         int num         = 0;\r
259         int num_offset;\r
260 \r
261         IBSP_ENTER( IBSP_DBG_PERFMON );\r
262 \r
263         UNUSED_PARAM(lpDeviceNames);\r
264 \r
265         if( g_pm_stat.threads++ )\r
266         {\r
267                 IBSP_EXIT( IBSP_DBG_PERFMON );\r
268                 return ERROR_SUCCESS;\r
269         }\r
270 \r
271         /* open Registry and query for the first and last keys */\r
272         status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,\r
273                 IBSP_PM_REGISTRY_PATH IBSP_PM_SUBKEY_PERF,\r
274                 0L, KEY_READ, &pm_hkey);\r
275 \r
276         if( status != ERROR_SUCCESS )\r
277         {\r
278                 g_pm_stat.threads--;\r
279                 IBSP_ERROR_EXIT(\r
280                         ("RegOpenKeyEx for perf information returned %d.\n", status) );\r
281                 return status;\r
282         }\r
283 \r
284         data_size = sizeof(DWORD);\r
285         status = RegQueryValueEx( pm_hkey, "First Counter", 0L,\r
286                 &data_type, (LPBYTE)&first_counter, &data_size );\r
287 \r
288         if( status != ERROR_SUCCESS )\r
289         {\r
290                 RegCloseKey(pm_hkey);\r
291                 g_pm_stat.threads--;\r
292                 IBSP_ERROR_EXIT(\r
293                         ("RegQueryValueEx for \"First Counter\" returned %d.\n", status) );\r
294                 return status;\r
295         }\r
296 \r
297         data_size = sizeof(DWORD);\r
298         status = RegQueryValueEx( pm_hkey, "First Help", 0L,\r
299                 &data_type, (LPBYTE)&first_help, &data_size );\r
300 \r
301         RegCloseKey( pm_hkey );\r
302 \r
303         if( status != ERROR_SUCCESS )\r
304         {\r
305                 g_pm_stat.threads--;\r
306                 IBSP_ERROR_EXIT(\r
307                         ("RegQueryValueEx for \"First Help\" returned %d.\n", status) );\r
308                 return status;\r
309         }\r
310 \r
311         /* perf_obj */          \r
312         g_ibsp_pm_def.perf_obj.ObjectNameTitleIndex  = IBSP_PM_OBJ + first_counter;\r
313         g_ibsp_pm_def.perf_obj.ObjectHelpTitleIndex  = IBSP_PM_OBJ + first_help;\r
314         g_ibsp_pm_def.perf_obj.TotalByteLength  =\r
315                 sizeof(ibsp_pm_definition_t) + sizeof(ibsp_pm_counters_t);\r
316         g_ibsp_pm_def.perf_obj.DefinitionLength = sizeof(ibsp_pm_definition_t);\r
317         g_ibsp_pm_def.perf_obj.HeaderLength     = sizeof(PERF_OBJECT_TYPE);\r
318 \r
319         g_ibsp_pm_def.perf_obj.ObjectNameTitle = 0;\r
320         g_ibsp_pm_def.perf_obj.ObjectHelpTitle = 0;\r
321 \r
322         g_ibsp_pm_def.perf_obj.DetailLevel = PERF_DETAIL_NOVICE;\r
323         g_ibsp_pm_def.perf_obj.NumCounters = IBSP_PM_NUM_COUNTERS;\r
324         g_ibsp_pm_def.perf_obj.DefaultCounter = 0;\r
325         g_ibsp_pm_def.perf_obj.NumInstances = 0;\r
326         g_ibsp_pm_def.perf_obj.CodePage = 0;\r
327 \r
328         QueryPerformanceFrequency( &g_ibsp_pm_def.perf_obj.PerfFreq );\r
329 \r
330         /* initialize all counter definitions */\r
331         num_offset = IBSP_PM_OBJ + 2;\r
332         for ( num = 0; num <  IBSP_PM_NUM_COUNTERS ; num++, num_offset += 2)\r
333         {\r
334                 g_ibsp_pm_def.counter[num].CounterNameTitleIndex = num_offset + first_counter;\r
335                 g_ibsp_pm_def.counter[num].CounterHelpTitleIndex = num_offset + first_help;\r
336                 g_ibsp_pm_def.counter[num].ByteLength = sizeof(PERF_COUNTER_DEFINITION);\r
337                 g_ibsp_pm_def.counter[num].CounterNameTitle = 0;\r
338                 g_ibsp_pm_def.counter[num].CounterHelpTitle = 0;\r
339                 g_ibsp_pm_def.counter[num].DefaultScale = 0;\r
340                 g_ibsp_pm_def.counter[num].DetailLevel = PERF_DETAIL_NOVICE;\r
341                 g_ibsp_pm_def.counter[num].CounterType = PERF_COUNTER_BULK_COUNT;\r
342                 /* All counters should be kept to 64-bits for consistency and simplicity. */\r
343                 g_ibsp_pm_def.counter[num].CounterSize = sizeof(LONG64);\r
344                 g_ibsp_pm_def.counter[num].CounterOffset =\r
345                         (DWORD)offsetof( ibsp_pm_counters_t, data[num] );\r
346         }\r
347 \r
348         g_pm_stat.h_evlog = RegisterEventSource( NULL, IBSP_PM_SUBKEY_NAME );\r
349         if( !g_pm_stat.h_evlog )\r
350         {\r
351                 g_pm_stat.threads--;\r
352                 status = GetLastError();\r
353                 IBSP_ERROR_EXIT( ("RegisterEventSource failed with %d\n", status) );\r
354                 return status;\r
355         }\r
356 \r
357         IBSP_EXIT( IBSP_DBG_PERFMON );\r
358         return ERROR_SUCCESS;\r
359 }\r
360 \r
361 \r
362 /*\r
363  * http://msdn.microsoft.com/library/en-us/perfctrs/perf/closeperformancedata.asp\r
364  */\r
365 DWORD APIENTRY\r
366 IBSPPmClose( void )\r
367 {\r
368         BOOL status;\r
369 \r
370         IBSP_ENTER( IBSP_DBG_PERFMON );\r
371 \r
372         if( --g_pm_stat.threads )\r
373         {\r
374                 IBSP_EXIT( IBSP_DBG_PERFMON );\r
375                 return ERROR_SUCCESS;\r
376         }\r
377 \r
378         IBSPPmReleaseSlot();\r
379 \r
380         /* avoid double closing */\r
381         if( g_pm_stat.p_shmem != NULL )\r
382         {\r
383                 status = UnmapViewOfFile( g_pm_stat.p_shmem );\r
384                 g_pm_stat.p_shmem = NULL;\r
385         }\r
386         \r
387         if( g_pm_stat.h_evlog != NULL )\r
388         {\r
389                 DeregisterEventSource( g_pm_stat.h_evlog );\r
390                 g_pm_stat.h_evlog = NULL;\r
391         }\r
392 \r
393         IBSP_EXIT( IBSP_DBG_PERFMON );\r
394         return ERROR_SUCCESS;\r
395 }\r
396 \r
397 \r
398 \r
399 /*\r
400  * http://msdn.microsoft.com/library/en-us/perfctrs/perf/collectperformancedata.asp\r
401  */\r
402 DWORD WINAPI\r
403 IBSPPmCollectData(\r
404         IN                              LPWSTR                                          lpValueName,\r
405         IN      OUT                     LPVOID*                                         lppData,\r
406         IN      OUT                     LPDWORD                                         lpcbTotalBytes,\r
407         IN      OUT                     LPDWORD                                         lpNumObjectTypes )\r
408 {\r
409         int32_t                                         sh_num;\r
410         int32_t                                         num_instances, max_instances;\r
411         uint32_t                                        use_bytes;\r
412         ibsp_pm_definition_t            *p_obj_def;\r
413         ibsp_pm_counters_t                      *p_count_def;\r
414         PERF_INSTANCE_DEFINITION        *p_inst_def;\r
415         pm_shmem_t                                      *p_mem;\r
416         LONG64                                          total_data[IBSP_PM_NUM_COUNTERS];\r
417         \r
418         IBSP_ENTER( IBSP_DBG_PERFMON );\r
419 \r
420         p_mem = (pm_shmem_t * __ptr64 )g_pm_stat.p_shmem;\r
421         \r
422         if( p_mem == NULL )\r
423         {\r
424                 IBSP_ERROR( ("No shared memory object\n") );\r
425                 goto done;\r
426         }\r
427 \r
428         if( !__PmIsQuerySupported(lpValueName ) )\r
429         {\r
430                 IBSP_TRACE1( IBSP_DBG_PERFMON, ("Unsupported query\n") );\r
431                 goto done;\r
432         }\r
433 \r
434         if( !g_pm_stat.threads )\r
435         {\r
436                 IBSP_ERROR( ("Initialization was not completed\n") );\r
437 done:\r
438                 *lpcbTotalBytes   = 0;\r
439                 *lpNumObjectTypes = 0;\r
440 \r
441                 IBSP_EXIT( IBSP_DBG_PERFMON );\r
442                 return ERROR_SUCCESS;\r
443         }\r
444 \r
445         ZeroMemory( &total_data, sizeof(total_data) );\r
446         num_instances = 0;\r
447         /* sum total counters that were not filled in completion routine */\r
448         for( sh_num = 0; sh_num < IBSP_PM_NUM_INSTANCES; sh_num++ )\r
449         {\r
450                 if( !InterlockedCompareExchange( &p_mem->obj[sh_num].taken, 1, 1 ) )\r
451                         continue;\r
452 \r
453                 total_data[BYTES_SEND] += p_mem->obj[sh_num].data[BYTES_SEND];\r
454                 total_data[BYTES_RECV] += p_mem->obj[sh_num].data[BYTES_RECV];\r
455                 total_data[BYTES_WRITE] += p_mem->obj[sh_num].data[BYTES_WRITE];\r
456                 total_data[BYTES_READ] += p_mem->obj[sh_num].data[BYTES_READ];\r
457                 /* Update total for current slot. */\r
458                 p_mem->obj[sh_num].data[BYTES_TOTAL] =\r
459                         p_mem->obj[sh_num].data[BYTES_SEND] +\r
460                         p_mem->obj[sh_num].data[BYTES_RECV] +\r
461                         p_mem->obj[sh_num].data[BYTES_WRITE] +\r
462                         p_mem->obj[sh_num].data[BYTES_READ];\r
463                 total_data[BYTES_TOTAL] += p_mem->obj[sh_num].data[BYTES_TOTAL];\r
464                 total_data[COMP_SEND] += p_mem->obj[sh_num].data[COMP_SEND];\r
465                 total_data[COMP_RECV] += p_mem->obj[sh_num].data[COMP_RECV];\r
466                 total_data[COMP_TOTAL] += p_mem->obj[sh_num].data[COMP_TOTAL];\r
467                 total_data[INTR_TOTAL] += p_mem->obj[sh_num].data[INTR_TOTAL];\r
468 \r
469                 num_instances++;\r
470         }\r
471 \r
472         IBSP_TRACE1( IBSP_DBG_PERFMON, ("%d instances.\n", num_instances) );\r
473 \r
474         /* calc buffer size required for data return */\r
475         use_bytes = sizeof(ibsp_pm_definition_t) + \\r
476                                 (sizeof(PERF_INSTANCE_DEFINITION) + \\r
477                                 sizeof(ibsp_pm_counters_t) + \\r
478                                 (sizeof(WCHAR) * IBSP_PM_APP_NAME_SIZE)) * num_instances;\r
479 \r
480         if( *lpcbTotalBytes < use_bytes )\r
481         {\r
482                 *lpcbTotalBytes   = 0;\r
483                 *lpNumObjectTypes = 0;\r
484                 return ERROR_MORE_DATA;\r
485         }\r
486 \r
487         p_obj_def = (ibsp_pm_definition_t*)*lppData;\r
488         use_bytes = sizeof(ibsp_pm_definition_t);\r
489 \r
490         /* Copy counter definition */\r
491         CopyMemory( p_obj_def, &g_ibsp_pm_def, sizeof(ibsp_pm_definition_t) );\r
492 \r
493         p_obj_def->perf_obj.NumInstances = num_instances;\r
494         QueryPerformanceCounter( &p_obj_def->perf_obj.PerfTime );\r
495 \r
496         max_instances = num_instances;\r
497 \r
498         /* Assign pointers for the first instance */\r
499         p_inst_def = (PERF_INSTANCE_DEFINITION*)(p_obj_def + 1);\r
500 \r
501         for( sh_num = 0; sh_num < IBSP_PM_NUM_INSTANCES; sh_num++ )\r
502         {\r
503                 if( !InterlockedCompareExchange( &p_mem->obj[sh_num].taken, 1, 1 ) )\r
504                         continue;\r
505 \r
506                 /* Make sure we don't overrun the buffer! */\r
507                 if( max_instances-- == 0 )\r
508                         break;\r
509 \r
510                 p_inst_def->ByteLength = sizeof(PERF_INSTANCE_DEFINITION) + \r
511                         (sizeof(WCHAR) * IBSP_PM_APP_NAME_SIZE);\r
512                 p_inst_def->ParentObjectTitleIndex = 0;\r
513                 p_inst_def->ParentObjectInstance = 0;\r
514                 p_inst_def->UniqueID = -1;  /* using module names */\r
515                 p_inst_def->NameOffset = sizeof(PERF_INSTANCE_DEFINITION);\r
516 \r
517                 /* Length in bytes of Unicode name string, including terminating NULL */\r
518                 p_inst_def->NameLength =\r
519                         (DWORD)wcslen( p_mem->obj[sh_num].app_name ) + 1;\r
520                 p_inst_def->NameLength *= sizeof(WCHAR);\r
521 \r
522                 CopyMemory( (WCHAR*)(p_inst_def + 1),\r
523                         p_mem->obj[sh_num].app_name, p_inst_def->NameLength );\r
524 \r
525                 use_bytes += p_inst_def->ByteLength;\r
526 \r
527                 /* advance to counter definition */\r
528                 p_count_def = (ibsp_pm_counters_t*)\r
529                         (((BYTE*)p_inst_def) + p_inst_def->ByteLength);\r
530 \r
531                 p_count_def->pm_block.ByteLength = sizeof(ibsp_pm_counters_t);\r
532                 use_bytes += sizeof(ibsp_pm_counters_t);\r
533 \r
534                 /* Here we report actual counter values. */\r
535                 if( sh_num == 0 )\r
536                 {\r
537                         CopyMemory( p_count_def->data, total_data, sizeof(total_data) );\r
538                 }\r
539                 else\r
540                 {\r
541                         CopyMemory( p_count_def->data, p_mem->obj[sh_num].data,\r
542                                 sizeof(p_mem->obj[sh_num].data) );\r
543                 }\r
544 \r
545                 /* Advance pointers for the next instance definition */\r
546                 p_inst_def = (PERF_INSTANCE_DEFINITION*)(p_count_def + 1);\r
547         }\r
548 \r
549         p_obj_def->perf_obj.TotalByteLength = (DWORD)use_bytes;\r
550 \r
551         *lppData = ((BYTE*)*lppData) + use_bytes;\r
552         *lpNumObjectTypes = 1;\r
553         *lpcbTotalBytes = (DWORD)use_bytes;\r
554 \r
555         IBSP_EXIT( IBSP_DBG_PERFMON );\r
556         return ERROR_SUCCESS;\r
557 }\r