OpenCBM
sys/libcommon/perfeval.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version
5  * 2 of the License, or (at your option) any later version.
6  *
7  * Copyright 2004 Spiro Trikaliotis
8  *
9  */
10 
19 #include <wdm.h>
20 
21 #include "cbm_driver.h"
22 
23 #include "../libiec/i_iec.h"
24 
25 #ifdef PERFEVAL
26 
28 #undef PERFEVAL_DEBUG
29 
32 #undef PERFEVAL_WRITE_TO_MEMORY
33 
34 
35 #ifdef PERFEVAL_DEBUG
36 
37  #define DBG_PRINT_REG( _xxx ) DBG_PRINT( _xxx )
38  #define DBG_PRINT_FILE( _xxx ) DBG_PRINT( _xxx )
39 
40 #else /* #ifdef PERFEVAL_DEBUG */
41 
42  #define DBG_PRINT_REG( _xxx )
43  #define DBG_PRINT_FILE( _xxx )
44 
45 #endif /* #ifdef PERFEVAL_DEBUG */
46 
49 #define MAX_PERFORMANCE_EVAL_ENTRIES 0xffff
50 
52 static PPERFORMANCE_EVAL_ENTRY PerformanceEvalEntries = NULL;
53 
57 static ULONG CurrentPerformanceEvalEntry = -1;
58 
62 static ULONG ProcessorFrequency = -1;
63 
80 #pragma warning(push)
81 #pragma warning(disable:4035)
82 
83 __forceinline __int64 RDTSC(void)
84 {
85 #ifdef USE_RDTSC
86  __asm {
87  // RDTSC
88  _emit 0x0F
89  _emit 0x31
90  }
91 #else
92  LARGE_INTEGER li;
93 
94  li = KeQueryPerformanceCounter(NULL);
95 
96  return li.QuadPart;
97 #endif
98 }
99 #pragma warning(pop)
100 
109 VOID
110 PerfInit(VOID)
111 {
112  FUNC_ENTER();
113 
114  DBG_ASSERT(PerformanceEvalEntries == NULL);
115  DBG_ASSERT(CurrentPerformanceEvalEntry == -1);
116 
117  // Allocate memory for the entries
118 
119  PerformanceEvalEntries = (PPERFORMANCE_EVAL_ENTRY) ExAllocatePoolWithTag(NonPagedPool,
120  MAX_PERFORMANCE_EVAL_ENTRIES * sizeof(*PerformanceEvalEntries), MTAG_PERFEVAL);
121 
122  if (PerformanceEvalEntries)
123  {
124 #ifdef USE_RDTSC
125 
126  UNICODE_STRING registryPath;
127  NTSTATUS ntStatus;
128  HANDLE handleRegistry;
129 
130  // Get the processor frequency from the registry. As Windows
131  // already tried to calculate the frequency, do not try to
132  // calculate it on my own, but rely on this.
133 
134  RtlInitUnicodeString(&registryPath,
135  L"\\REGISTRY\\MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
136 
137  DBG_PRINT_REG((DBG_PREFIX "trying to open %wZ", &registryPath));
138  ntStatus = cbm_registry_open_for_read(&handleRegistry, &registryPath);
139  DBG_PRINT_REG((DBG_PREFIX "cbm_registry_open() returned %s", DebugNtStatus(ntStatus)));
140 
141  if (NT_SUCCESS(ntStatus))
142  {
143  ntStatus = cbm_registry_read_ulong(handleRegistry, L"~MHz", &ProcessorFrequency);
144  DBG_PRINT_REG((DBG_PREFIX "cbm_registry_read() returned %s, value = %u",
145  DebugNtStatus(ntStatus), ProcessorFrequency));
146 
147  ntStatus = cbm_registry_close(handleRegistry);
148  DBG_PRINT_REG((DBG_PREFIX "cbm_registry_close() returned %s", DebugNtStatus(ntStatus)));
149  }
150 
151 #else
152  LARGE_INTEGER li;
153 
154  KeQueryPerformanceCounter(&li);
155 
156  DBG_ASSERT(li.HighPart == 0);
157  ProcessorFrequency = li.LowPart;
158 
159 #endif
160 
161  {
162  unsigned int waitPeriod = 100;
163 
164  DBG_PRINT((DBG_PREFIX "Measuring time:"));
166  cbmiec_udelay(waitPeriod);
167  PERF_EVENT_MEASURE_TIME(waitPeriod);
168  DBG_PRINT((DBG_PREFIX "We waited %u us.", waitPeriod));
169  }
170  }
171 
172  FUNC_LEAVE();
173 }
174 
199 VOID
200 PerfEvent(IN ULONG_PTR Event, IN ULONG_PTR Data)
201 {
202  ULONG currentEntry;
203 
204  FUNC_ENTER();
205 
206  // only try to write if we successfully allocated a buffer!
207 
208  if (PerformanceEvalEntries)
209  {
210  // first of all, increment the entry number of the event
211  // This makes sure that we are multiprocessor-safe, as
212  // we only write to the log after we have successfully
213  // incremented the value.
214 
215  currentEntry = InterlockedIncrement(&CurrentPerformanceEvalEntry);
216 
217  if (currentEntry < MAX_PERFORMANCE_EVAL_ENTRIES)
218  {
219  PerformanceEvalEntries[currentEntry].Timestamp = RDTSC();
220  PerformanceEvalEntries[currentEntry].Processor = CbmGetCurrentProcessorNumber();
221  PerformanceEvalEntries[currentEntry].PeThread = PsGetCurrentThread();
222  PerformanceEvalEntries[currentEntry].Event = Event;
223  PerformanceEvalEntries[currentEntry].Data = Data;
224  }
225  else
226  {
227  // If we incremented "out of the buffer", make sure
228  // that we decrement the value back to where it was
229  // before
230 
231  InterlockedDecrement(&CurrentPerformanceEvalEntry);
232  }
233  }
234 
235  FUNC_LEAVE();
236 }
237 
240 typedef
241 struct PERFEVAL_FILE_HEADER
242 {
244  ULONG FileVersion;
245 
248  ULONG ProcessorFrequency;
249 
251  ULONG CountEntries;
252 
253 } PERFEVAL_FILE_HEADER, *PPERFEVAL_FILE_HEADER;
254 
265 VOID
266 PerfSynchronize(VOID)
267 {
268  FUNC_ENTER();
269 
270  // Make sure to only output the entries if there are some
271 
272  DBG_ASSERT(((LONG)CurrentPerformanceEvalEntry) >= 0);
273 
274  if (PerformanceEvalEntries)
275  {
276 
277 #ifdef PERFEVAL_WRITE_TO_MEMORY
278 
279  __int64 FirstTimestamp;
280  __int64 LastTimestamp;
281  ULONG i;
282 
283  FirstTimestamp = PerformanceEvalEntries[0].Timestamp;
284  LastTimestamp = FirstTimestamp;
285 
286  for (i = 0; i < CurrentPerformanceEvalEntry; i++)
287  {
288  __int64 tempTimeAbs;
289  __int64 tempTimeRel;
290  ULONG usTime;
291  ULONG msTime;
292  ULONG sTime;
293 
294  tempTimeAbs = (PerformanceEvalEntries[i].Timestamp - FirstTimestamp) / ProcessorFrequency;
295  tempTimeRel = (PerformanceEvalEntries[i].Timestamp - LastTimestamp) / ProcessorFrequency;
296 
297  LastTimestamp = PerformanceEvalEntries[i].Timestamp;
298 
299  usTime = (ULONG) (tempTimeAbs % 1000);
300  msTime = (ULONG) ((tempTimeAbs / 1000) % 1000);
301  sTime = (ULONG) ((tempTimeAbs / 1000000) % 1000);
302 
304  "%6u - Time: %3u.%03u.%03u us (+%7I64u us) - Event = %08x, Data = %08x"
305  " on processur %u in thread %p",
306  i,
307  sTime, msTime, usTime,
308  tempTimeRel,
309  PerformanceEvalEntries[i].Event,
310  PerformanceEvalEntries[i].Data,
311  PerformanceEvalEntries[i].Processor,
312  PerformanceEvalEntries[i].PeThread));
313  }
314 
315  // make sure we start storing the performance values
316  // at the beginning again
317 
318  InterlockedExchange(&CurrentPerformanceEvalEntry, -1);
319 
320 #else /* #ifdef PERFEVAL_WRITE_TO_MEMORY */
321 
322  IO_STATUS_BLOCK ioStatusBlock;
323  OBJECT_ATTRIBUTES objectAttributes;
324  UNICODE_STRING fileName;
325  NTSTATUS ntStatus;
326  HANDLE fileHandle;
327 
328  RtlZeroMemory(&objectAttributes, sizeof(objectAttributes));
329  RtlInitUnicodeString(&fileName, L"\\??\\c:\\opencbm\\timing\\timing.dat");
330 
331  DBG_IRQL( == PASSIVE_LEVEL);
332  InitializeObjectAttributes(&objectAttributes,
333  &fileName,
334  OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
335  NULL,
336  NULL
337  );
338 
339  ntStatus = ZwCreateFile(
340  &fileHandle,
341  FILE_APPEND_DATA | SYNCHRONIZE,
342  &objectAttributes,
343  &ioStatusBlock,
344  NULL,
345  FILE_ATTRIBUTE_NORMAL,
346  0,
347  FILE_OPEN_IF,
348  FILE_NON_DIRECTORY_FILE | FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT,
349  NULL,
350  0
351  );
352 
353  DBG_PRINT_FILE((DBG_PREFIX "ZwCreateFile returned %s", DebugNtStatus(ntStatus)));
354 
355  if (NT_SUCCESS(ntStatus))
356  {
357  PERFEVAL_FILE_HEADER fileHeader;
358 
359  fileHeader.FileVersion = 2
360 #ifndef _X86_
361  | (1<<31);
362 #endif /* #ifndef _X86_ */
363  ;
364 
365  fileHeader.ProcessorFrequency = ProcessorFrequency;
366  fileHeader.CountEntries = CurrentPerformanceEvalEntry + 1;
367 
368  // the file has been opened; now, we can write to it
369 
370  ntStatus = ZwWriteFile(fileHandle,
371  NULL, // event
372  NULL, // ApcRoutine
373  NULL, // ApcContext
374  &ioStatusBlock,
375  &fileHeader,
376  sizeof(fileHeader),
377  NULL,
378  NULL);
379 
380  DBG_PRINT_FILE((DBG_PREFIX "ZwWriteFile (header) returned %s",
381  DebugNtStatus(ntStatus)));
382 
383  ntStatus = ZwWriteFile(fileHandle,
384  NULL, // event
385  NULL, // ApcRoutine
386  NULL, // ApcContext
387  &ioStatusBlock,
388  PerformanceEvalEntries,
389  fileHeader.CountEntries * sizeof(*PerformanceEvalEntries),
390  NULL,
391  NULL);
392 
393  DBG_PRINT_FILE((DBG_PREFIX "ZwWriteFile (data) returned %s",
394  DebugNtStatus(ntStatus)));
395 
396  ntStatus = ZwClose(fileHandle);
397 
398  DBG_PRINT_FILE((DBG_PREFIX "ZwCloseFile returned %s",
399  DebugNtStatus(ntStatus)));
400 
401  // make sure we start storing the performance values
402  // at the beginning again
403 
404  InterlockedExchange(&CurrentPerformanceEvalEntry, -1);
405  }
406 
407 #endif /* #ifdef PERFEVAL_WRITE_TO_MEMORY */
408 
409  }
410 
411  FUNC_LEAVE();
412 }
413 
425 VOID
426 PerfSave(VOID)
427 {
428  FUNC_ENTER();
429 
430  if (PerformanceEvalEntries)
431  {
432  PVOID buffer;
433 
434  // write the event logs to where they should be written
435 
436  PerfSynchronize();
437 
438  // stop the event logging by writing the default values
439 
440  buffer = InterlockedExchangePointer(&PerformanceEvalEntries, NULL);
441  InterlockedExchange(&CurrentPerformanceEvalEntry, -1);
442 
443  // free the previously allocated buffer.
444 
445  ExFreePool(buffer);
446  }
447 
448  FUNC_LEAVE();
449 }
450 
451 #endif /* #ifdef PERFEVAL */
VOID cbmiec_udelay(IN ULONG howlong)
Wait for a timeout.
Definition: libiec/util.c:66
ULONG CbmGetCurrentProcessorNumber(VOID)
Wrapper for KeGetCurrentProcessorNumber()
Definition: processor.c:31
NTSTATUS cbm_registry_close(IN HANDLE HandleKey)
Close a registry key.
Definition: util-reg.c:230
#define FUNC_LEAVE()
Definition: debug.h:349
#define PERF_EVENT_MEASURE_TIME(_x_)
Definition: cbm_driver.h:37
NTSTATUS cbm_registry_read_ulong(IN HANDLE HandleKey, IN PCWSTR KeyName, OUT PULONG Value)
Read a ULONG value out of a registry key.
Definition: util-reg.c:299
#define DBG_ASSERT(_xxx)
Definition: debug.h:401
#define MTAG_PERFEVAL
Definition: memtags.h:38
#define FUNC_ENTER()
Definition: debug.h:347
const UCHAR * DebugNtStatus(NTSTATUS Value)
Return the description of an NTSTATUS code.
Definitions for the opencbm driver.
#define DBG_PREFIX
Definition: debug.h:320
NTSTATUS cbm_registry_open_for_read(OUT PHANDLE HandleKey, IN PUNICODE_STRING Path)
Open a registry path for reading.
Definition: util-reg.c:43
#define DBG_PRINT(_xxx)
Definition: debug.h:403