OpenCBM
libmisc/WINDOWS/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, 2009 Spiro Trikaliotis
8  *
9  */
10 
19 #include <windows.h>
20 
21 #define DBG_USERMODE
22 
23 #include "perfeval.h"
24 
25 #include <stdlib.h>
26 
27 #ifdef PERFEVAL
28 
30 #undef PERFEVAL_DEBUG
31 
34 #undef PERFEVAL_WRITE_TO_MEMORY
35 
36 
37 #ifdef PERFEVAL_DEBUG
38 
39  #define DBG_PRINT_REG( _xxx ) DBG_PRINT( _xxx )
40  #define DBG_PRINT_FILE( _xxx ) DBG_PRINT( _xxx )
41 
42 #else /* #ifdef PERFEVAL_DEBUG */
43 
44  #define DBG_PRINT_REG( _xxx )
45  #define DBG_PRINT_FILE( _xxx )
46 
47 #endif /* #ifdef PERFEVAL_DEBUG */
48 
51 #define MAX_PERFORMANCE_EVAL_ENTRIES 0xffff
52 
54 static PPERFORMANCE_EVAL_ENTRY PerformanceEvalEntries = NULL;
55 
59 static ULONG CurrentPerformanceEvalEntry = -1;
60 
64 static ULONG ProcessorFrequency = -1;
65 
82 #pragma warning(push)
83 #pragma warning(disable:4035)
84 
85 __forceinline __int64 RDTSC(void)
86 {
87 #ifdef USE_RDTSC
88  __asm {
89  // RDTSC
90  _emit 0x0F
91  _emit 0x31
92  }
93 #else
94  LARGE_INTEGER li;
95 
96  QueryPerformanceCounter(&li);
97 
98  return li.QuadPart;
99 #endif
100 }
101 #pragma warning(pop)
102 
111 VOID
112 PerfInit(VOID)
113 {
114  FUNC_ENTER();
115 
116  DBG_ASSERT(PerformanceEvalEntries == NULL);
117  DBG_ASSERT(CurrentPerformanceEvalEntry == -1);
118 
119  // Allocate memory for the entries
120 
121  PerformanceEvalEntries = (PPERFORMANCE_EVAL_ENTRY) malloc(
122  MAX_PERFORMANCE_EVAL_ENTRIES * sizeof(*PerformanceEvalEntries));
123 
124  if (PerformanceEvalEntries)
125  {
126 #ifdef USE_RDTSC
127 
128  UNICODE_STRING registryPath;
129  NTSTATUS ntStatus;
130  HANDLE handleRegistry;
131 
132  // Get the processor frequency from the registry. As Windows
133  // already tried to calculate the frequency, do not try to
134  // calculate it on my own, but rely on this.
135 
136  RtlInitUnicodeString(&registryPath,
137  L"\\REGISTRY\\MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
138 
139  DBG_PRINT_REG((DBG_PREFIX "trying to open %wZ", &registryPath));
140  ntStatus = cbm_registry_open_for_read(&handleRegistry, &registryPath);
141  DBG_PRINT_REG((DBG_PREFIX "cbm_registry_open() returned %s", DebugNtStatus(ntStatus)));
142 
143  if (NT_SUCCESS(ntStatus))
144  {
145  ntStatus = cbm_registry_read_ulong(handleRegistry, L"~MHz", &ProcessorFrequency);
146  DBG_PRINT_REG((DBG_PREFIX "cbm_registry_read() returned %s, value = %u",
147  DebugNtStatus(ntStatus), ProcessorFrequency));
148 
149  ntStatus = cbm_registry_close(handleRegistry);
150  DBG_PRINT_REG((DBG_PREFIX "cbm_registry_close() returned %s", DebugNtStatus(ntStatus)));
151  }
152 
153 #else
154  LARGE_INTEGER li;
155 
156  QueryPerformanceCounter(&li);
157 
158  DBG_ASSERT(li.HighPart == 0);
159  ProcessorFrequency = li.LowPart;
160 
161 #endif
162  }
163 
164  FUNC_LEAVE();
165 }
166 
191 VOID
192 PerfEvent(IN ULONG_PTR Event, IN ULONG_PTR Data)
193 {
194  ULONG currentEntry;
195 
196  FUNC_ENTER();
197 
198  // only try to write if we successfully allocated a buffer!
199 
200  if (PerformanceEvalEntries)
201  {
202  // first of all, increment the entry number of the event
203  // This makes sure that we are multiprocessor-safe, as
204  // we only write to the log after we have successfully
205  // incremented the value.
206 
207  currentEntry = InterlockedIncrement(&CurrentPerformanceEvalEntry);
208 
209  if (currentEntry < MAX_PERFORMANCE_EVAL_ENTRIES)
210  {
211  PerformanceEvalEntries[currentEntry].Timestamp = RDTSC();
212  PerformanceEvalEntries[currentEntry].Processor = 0; // KeGetCurrentProcessorNumber();
213  PerformanceEvalEntries[currentEntry].PeThread = GetCurrentThread();
214  PerformanceEvalEntries[currentEntry].Event = Event;
215  PerformanceEvalEntries[currentEntry].Data = Data;
216  }
217  else
218  {
219  // If we incremented "out of the buffer", make sure
220  // that we decrement the value back to where it was
221  // before
222 
223  InterlockedDecrement(&CurrentPerformanceEvalEntry);
224  }
225  }
226 
227  FUNC_LEAVE();
228 }
229 
232 typedef
233 struct PERFEVAL_FILE_HEADER
234 {
236  ULONG FileVersion;
237 
240  ULONG ProcessorFrequency;
241 
243  ULONG CountEntries;
244 
245 } PERFEVAL_FILE_HEADER, *PPERFEVAL_FILE_HEADER;
246 
257 VOID
258 PerfSynchronize(VOID)
259 {
260  FUNC_ENTER();
261 
262  // Make sure to only output the entries if there are some
263 
264  DBG_ASSERT(((LONG)CurrentPerformanceEvalEntry) >= 0);
265 
266  if (PerformanceEvalEntries)
267  {
268 
269 #ifdef PERFEVAL_WRITE_TO_MEMORY
270 
271  __int64 FirstTimestamp;
272  __int64 LastTimestamp;
273  ULONG i;
274 
275  FirstTimestamp = PerformanceEvalEntries[0].Timestamp;
276  LastTimestamp = FirstTimestamp;
277 
278  for (i = 0; i < CurrentPerformanceEvalEntry; i++)
279  {
280  __int64 tempTimeAbs;
281  __int64 tempTimeRel;
282  ULONG usTime;
283  ULONG msTime;
284  ULONG sTime;
285 
286  tempTimeAbs = (PerformanceEvalEntries[i].Timestamp - FirstTimestamp) / ProcessorFrequency;
287  tempTimeRel = (PerformanceEvalEntries[i].Timestamp - LastTimestamp) / ProcessorFrequency;
288 
289  LastTimestamp = PerformanceEvalEntries[i].Timestamp;
290 
291  usTime = (ULONG) (tempTimeAbs % 1000);
292  msTime = (ULONG) ((tempTimeAbs / 1000) % 1000);
293  sTime = (ULONG) ((tempTimeAbs / 1000000) % 1000);
294 
296  "%6u - Time: %3u.%03u.%03u us (+%7I64u us) - Event = %08x, Data = %08x"
297  " on processur %u in thread %p",
298  i,
299  sTime, msTime, usTime,
300  tempTimeRel,
301  PerformanceEvalEntries[i].Event,
302  PerformanceEvalEntries[i].Data,
303  PerformanceEvalEntries[i].Processor,
304  PerformanceEvalEntries[i].PeThread));
305  }
306 
307  // make sure we start storing the performance values
308  // at the beginning again
309 
310  InterlockedExchange(&CurrentPerformanceEvalEntry, -1);
311 
312 #else /* #ifdef PERFEVAL_WRITE_TO_MEMORY */
313 
314  HANDLE fileHandle = CreateFile("time.perfeval", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
315 
316  if (fileHandle != INVALID_HANDLE_VALUE)
317  {
318  PERFEVAL_FILE_HEADER fileHeader;
319  DWORD byteWritten;
320 
321  fileHeader.FileVersion = 2
322 #ifndef _X86_
323  | (1<<31);
324 #endif /* #ifndef _X86_ */
325  ;
326 
327  fileHeader.ProcessorFrequency = ProcessorFrequency;
328  fileHeader.CountEntries = CurrentPerformanceEvalEntry + 1;
329 
330  // the file has been opened; now, we can write to it
331 
332  WriteFile(fileHandle, &fileHeader, sizeof(fileHeader), &byteWritten, NULL);
333 
334  WriteFile(fileHandle, PerformanceEvalEntries, fileHeader.CountEntries * sizeof(*PerformanceEvalEntries), &byteWritten, NULL);
335 
336  CloseHandle(fileHandle);
337 
338  // make sure we start storing the performance values
339  // at the beginning again
340 
341  InterlockedExchange(&CurrentPerformanceEvalEntry, -1);
342  }
343 
344 #endif /* #ifdef PERFEVAL_WRITE_TO_MEMORY */
345 
346  }
347 
348  FUNC_LEAVE();
349 }
350 
362 #ifndef InterlockedExchangePointer
363 # define InterlockedExchangePointer(_x, _y) (void*) InterlockedExchange( (void*) (_x), (_y))
364 #endif /* #ifndef InterlockedExchangePointer */
365 
366 VOID
367 PerfSave(VOID)
368 {
369  FUNC_ENTER();
370 
371  if (PerformanceEvalEntries)
372  {
373  PVOID buffer;
374 
375  // write the event logs to where they should be written
376 
377  PerfSynchronize();
378 
379  // stop the event logging by writing the default values
380 
381  buffer = InterlockedExchangePointer(&PerformanceEvalEntries, 0);
382  InterlockedExchange(&CurrentPerformanceEvalEntry, -1);
383 
384  // free the previously allocated buffer.
385 
386  free(buffer);
387  }
388 
389  FUNC_LEAVE();
390 }
391 
392 #endif /* #ifdef PERFEVAL */
NTSTATUS cbm_registry_close(IN HANDLE HandleKey)
Close a registry key.
Definition: util-reg.c:230
#define FUNC_LEAVE()
Definition: debug.h:349
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 FUNC_ENTER()
Definition: debug.h:347
const UCHAR * DebugNtStatus(NTSTATUS Value)
Return the description of an NTSTATUS code.
#define DBG_PREFIX
Definition: debug.h:320
Functions and macros for performance evaluation purposes.
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