Bitdefender Hypervisor Memory Introspection
stats.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
17 
18 #include "guests.h"
19 
22 
23 #ifdef STATS_HAS_HIGHRES_TIMER
24 static INT64 gStatCallTimeNs;
26 #endif
27 
28 __pure
29 static const char *
31  _In_ STAT_ID StatId
32  )
40 {
41 #define __stats_case(x) case x: return &(#x[5])
42 
43  switch (StatId)
44  {
112  }
113 
114  return "<err_counter>";
115 
116 #undef __stats_case
117 }
118 
119 static __forceinline void
121  _Out_ TIMESPEC *Time
122  )
130 {
131 #ifndef STATS_HAS_HIGHRES_TIMER
132  *Time = __rdtsc();
133 #else
134  clock_gettime(CLOCK_MONOTONIC, Time);
135 #endif
136 }
137 
138 
139 static __forceinline void
141  _In_ TIMESPEC const *End,
142  _In_ TIMESPEC const *Start,
143  _Out_ TIMESPEC *Result
144  )
152 {
153 #ifdef STATS_HAS_HIGHRES_TIMER
154  if (__likely(End->tv_nsec > Start->tv_nsec))
155  {
156  Result->tv_sec = End->tv_sec - Start->tv_sec;
157  Result->tv_nsec = End->tv_nsec - Start->tv_nsec;
158  }
159  else
160  {
161  Result->tv_sec = End->tv_sec - Start->tv_sec - 1;
162  Result->tv_nsec = NSEC_PER_SEC + End->tv_nsec - Start->tv_nsec;
163  }
164 #else
165  *Result = *End - *Start;
166 #endif
167 }
168 
169 
170 static __forceinline void
172  void
173  )
179 {
180 #ifdef STATS_HAS_HIGHRES_TIMER
181  for (DWORD i = 0; i < ARRAYSIZE(gCounters); i++)
182  {
183  if (gCounters[i].StartEventId == gEventId)
184  {
185  gCounters[i].StatCalls++;
186  }
187  }
188 #endif
189 }
190 
191 
192 static __forceinline void
194  _Inout_ TIMESPEC *Time,
195  _In_ TIMESPEC const *Adder
196  )
203 {
204 #ifdef STATS_HAS_HIGHRES_TIMER
205  Time->tv_sec += Adder->tv_sec;
206  Time->tv_nsec += Adder->tv_nsec;
207 
208  if (Time->tv_nsec >= (INT64)NSEC_PER_SEC)
209  {
210  ++Time->tv_sec;
211  Time->tv_nsec -= NSEC_PER_SEC;
212  }
213 #else
214  *Time += *Adder;
215 #endif
216 }
217 
218 
219 void
221  void
222  )
226 {
227  LOG("[STATS] Introspection stats (totaling %lld events):\n", gEventId);
228 
229  for (DWORD i = 0; i < statsMaxCounter; i++)
230  {
231  // This is double the size of what we really need
232  char line[255];
233  STAT_COUNTER const *pCounter = &gCounters[i];
234 
235  if (0 == pCounter->TotalCount)
236  {
237  continue;
238  }
239 
240 #if defined(STATS_DISABLE_TIMER)
241  snprintf(line, sizeof(line), "%20s: %12llu times\n", IntStatGetName(i), pCounter->TotalCount);
242 
243 #elif defined(STATS_HAS_HIGHRES_TIMER)
244  double t = (double)(pCounter->Total.tv_sec * NSEC_PER_SEC +
245  pCounter->Total.tv_nsec) / (double)pCounter->TotalCount;
246  INT64 spe = 0;
247  INT64 nspe = (INT64)t % NSEC_PER_SEC;
248 
249  for (INT64 j = (INT64)t; j >= (INT64)NSEC_PER_SEC; j -= NSEC_PER_SEC)
250  {
251  ++spe;
252  }
253 
254  snprintf(line, sizeof(line),
255  "%25s: %8lld times - %4lu.%09lu (total) %4lld.%09lld (per exit) %4lld.%09lld (max)\n",
256  IntStatGetName(i), pCounter->TotalCount,
257  pCounter->Total.tv_sec, pCounter->Total.tv_nsec,
258  spe, nspe,
259  NSEC_TO_SEC(pCounter->Max), pCounter->Max % NSEC_PER_SEC);
260 #else
261  snprintf(line, sizeof(line),
262  "%25s: %20llu CPU ticks %12llu times - %4.6f (total) %4.12f (per exit) %4.12f (max)\n",
263  IntStatGetName(i),
264  pCounter->Total, pCounter->TotalCount,
265  pCounter->Total / (double)gGuest.TscSpeed,
266  pCounter->Total / (double)gGuest.TscSpeed / (double)pCounter->TotalCount,
267  pCounter->Max / (double)gGuest.TscSpeed);
268 #endif
269 
270  LOG("%s\n", line);
271  }
272 }
273 
274 
275 void
277  _In_ STAT_ID StatId
278  )
284 {
285  STAT_COUNTER *pCounter = &gCounters[StatId];
286 
287  pCounter->TotalCount = 0;
288 
289 #ifndef STATS_DISABLE_TIMER
290  pCounter->Max = 0;
291  pCounter->StartEventId = 0;
292 
293  memset(&pCounter->Start, 0, sizeof(pCounter->Start));
294 #endif
295 }
296 
297 
298 void
300  void
301  )
305 {
306  for (STAT_ID i = 0; i < statsMaxCounter; i++)
307  {
308  IntStatsReset(i);
309  }
310 }
311 
312 
313 #ifndef STATS_DISABLE_TIMER
314 
315 void
317  _In_ STAT_ID StatId
318  )
330 {
331  STAT_COUNTER *pCounter;
332 
334  {
335  return;
336  }
337 
338  pCounter = &gCounters[StatId];
339 
340  pCounter->StartEventId = gEventId;
341 
342  GetTime(&pCounter->Start);
343 
345 
346  pCounter->StatCalls = 0;
347 }
348 
349 
350 void
352  _In_ STAT_ID StatId
353  )
364 {
366  {
367  return;
368  }
369 
370  TIMESPEC total, end;
371  STAT_COUNTER *pCounter = &gCounters[StatId];
372 
373  pCounter->TotalCount++;
374 
375  if (pCounter->StartEventId != gEventId)
376  {
377  if (pCounter->StartEventId != 0)
378  {
379  ERROR("[ERROR] StartCount on event id %lld and stop on %lld for counter %d\n",
380  pCounter->StartEventId, gEventId, StatId);
381  }
382 
383  return;
384  }
385 
386  GetTime(&end);
387 
389 
390  DiffTime(&end, &pCounter->Start, &total);
391 
392 #ifndef STATS_HAS_HIGHRES_TIMER
393 
394  if (total > pCounter->Max)
395  {
396  pCounter->Max = total;
397  }
398 
399 #else
400 
401  QWORD totalNs = total.tv_sec * NSEC_PER_SEC + total.tv_nsec;
402  QWORD statCallNs = pCounter->StatCalls * gStatCallTimeNs;
403 
404  // Subtract the time spent in clock_gettime (by how many times it was called)
405  if (totalNs > statCallNs)
406  {
407  TIMESPEC t1;
408  TIMESPEC oldTotal = total;
409 
410  t1.tv_sec = statCallNs / NSEC_PER_SEC;
411  t1.tv_nsec = statCallNs - (t1.tv_sec * NSEC_PER_SEC);
412 
413  total.tv_nsec -= pCounter->StatCalls * gStatCallTimeNs;
414 
415  DiffTime(&oldTotal, &t1, &total);
416  }
417 
418  if (totalNs > pCounter->Max)
419  {
420  pCounter->Max = totalNs;
421  }
422 
423 #endif // STATS_HAS_HIGHRES_TIMER
424 
425  AddToTime(&pCounter->Total, &total);
426 
427  pCounter->StartEventId = 0;
428 }
429 
430 void
432  _In_ STAT_ID StatId
433  )
439 {
440  gCounters[StatId].StartEventId = 0;
441  gCounters[StatId].StatCalls = 0;
442 }
443 
444 #endif // STATS_DISABLE_TIMER
445 
446 
447 void
449  void
450  )
456 {
457 #ifdef STATS_HAS_HIGHRES_TIMER
458  const DWORD calibrationCalls = 10000;
459  TIMESPEC start, end, total;
460 
461  GetTime(&start);
462 
463  for (DWORD i = 0; i < calibrationCalls; i++)
464  {
465  GetTime(&total);
466  }
467 
468  GetTime(&end);
469 
470  DiffTime(&end, &start, &total);
471 
472  gStatCallTimeNs = (end.tv_nsec - start.tv_nsec) / calibrationCalls;
473 
474  // Allow a 10% error margin
475  gStatCallTimeNs -= (gStatCallTimeNs / 10);
476 
477  LOG("[DEBUG] Calibrated clock_gettime timer to %lld nanoseconds\n", gStatCallTimeNs);
478 #endif
479 }
Measures kernel mode exceptions checks.
Definition: stats.h:51
void IntStatStop(STAT_ID StatId)
Stops a stat measurement.
Definition: stats.c:351
STAT_COUNTER gCounters[statsMaxCounter]
The list of counters.
Definition: stats.c:21
Measures IntWinHandleException invocations done for DEP violations.
Definition: stats.h:76
#define __unlikely(x)
Definition: common.h:64
#define _Out_
Definition: intro_sal.h:22
Measures the integrity checks on the process security descriptor.
Definition: stats.h:108
Measures the security descriptor DPI protection information gathering.
Definition: stats.h:102
long long INT64
Definition: intro_types.h:45
#define NSEC_TO_SEC(nsec)
Definition: introdefs.h:97
Measures the pivoted stack DPI protection information gathering.
Definition: stats.h:87
Measures CR violation exits.
Definition: stats.h:30
#define _In_
Definition: intro_sal.h:21
Measures the debug flag DPI protection information gathering.
Definition: stats.h:86
Measures page table writes emulation.
Definition: stats.h:45
Measures user mode exceptions checks.
Definition: stats.h:50
Measures page table writes.
Definition: stats.h:44
Measures page tables integrity checks.
Definition: stats.h:59
static __pure const char * IntStatGetName(STAT_ID StatId)
Returns the name of a STAT_ID.
Definition: stats.c:30
Measures the instruction search done for the page table filtering agent.
Definition: stats.h:62
Measures XCR violation exits.
Definition: stats.h:32
Measures all EPT violations.
Definition: stats.h:18
#define ARRAYSIZE(A)
Definition: introdefs.h:101
Measures reads done from the kernel EAT.
Definition: stats.h:93
Measures the IntWinProcHandleCopyMemory detour handler.
Definition: stats.h:67
Measures page table writes done by the VAS monitor.
Definition: stats.h:56
#define __pure
Definition: introtypes.h:46
Measures the checks done on SharedUserData.
Definition: stats.h:104
Measures all the page table writes.
Definition: stats.h:46
Measures the stolen token flag DPI protection information gathering.
Definition: stats.h:88
Writes done from kernel mode over user mode.
Definition: stats.h:99
#define ERROR(fmt,...)
Definition: glue.h:62
Token writes.
Definition: stats.h:94
Measures the DTR violation exits.
Definition: stats.h:35
QWORD gEventId
The ID of the current event.
Definition: glue.c:55
Measures the cases in which the stack trace mechanism encounters a JMP after a CALL.
Definition: stats.h:81
Measures the thread start DPI protection information gathering.
Definition: stats.h:91
static void AddToTime(TIMESPEC *Time, TIMESPEC const *Adder)
Adds two time values.
Definition: stats.c:193
QWORD IntroActiveEventId
The event ID on which introcore became active.
Definition: guests.h:381
Measures the process creation checks.
Definition: stats.h:84
Measures the heap spray DPI protection information gathering.
Definition: stats.h:89
Measures the EPT violations for which the instruction does a read and a write.
Definition: stats.h:27
void IntStatsResetAll(void)
Resets all the stats.
Definition: stats.c:299
Measures the execution handling on SharedUserData page.
Definition: stats.h:106
Measures page table entries writes.
Definition: stats.h:47
Measures event injections.
Definition: stats.h:36
void IntStatsInit(void)
Initialization routine.
Definition: stats.c:448
#define LOG(fmt,...)
Definition: glue.h:61
Measures the handling of VMCALL exits.
Definition: stats.h:29
Measures the hook commits.
Definition: stats.h:42
Measures the execution of EPT violation handlers.
Definition: stats.h:26
Measures the INT3 exits generated by the page table filtering mechanism.
Definition: stats.h:60
#define _Inout_
Definition: intro_sal.h:20
void IntStatDiscard(STAT_ID StatId)
Discards the current measurement for a stat counter.
Definition: stats.c:431
Measures EPT violations generated while the guest was in kernel mode.
Definition: stats.h:22
unsigned long long QWORD
Definition: intro_types.h:53
A stats counter.
Definition: stats.h:133
Measures the IntWinVadHandleCommit detour handler.
Definition: stats.h:57
Measures the deletion of HOOK_REGION_DESCRIPTOR objects.
Definition: stats.h:39
Measures the stack trace mechanism for 32-bit execution contexts.
Definition: stats.h:78
void IntStatsDumpAll(void)
Prints all the non-zero stats.
Definition: stats.c:220
Measures the handling of memory reads in which a write protection policy exists.
Definition: stats.h:74
#define NSEC_PER_SEC
Definition: introdefs.h:93
Measures the decoding of instructions that generate EPT violations.
Definition: stats.h:24
void IntStatsReset(STAT_ID StatId)
Resets a stat.
Definition: stats.c:276
Measures the checks to see if the token has been changed when a token swap occurs.
Definition: stats.h:97
Measures EPT violations generated while the guest was in user mode.
Definition: stats.h:23
Measures the INT3 events.
Definition: stats.h:34
Measures page table writes that are actually relevant for Introcore.
Definition: stats.h:48
BOOLEAN UninitPrepared
Definition: guests.h:320
static void DiffTime(TIMESPEC const *End, TIMESPEC const *Start, TIMESPEC *Result)
Computes the delta between two time values.
Definition: stats.c:140
Measures the checks to see if the token has been changed when a write occurs over the token...
Definition: stats.h:96
#define __forceinline
Definition: introtypes.h:61
static void IncStatsCallsCount(void)
Computes the time GetTime was called for each counter that was started before this one and on this ev...
Definition: stats.c:171
Measures the timer events.
Definition: stats.h:33
Measures the VMCALL exists generated by the page table filtering agent.
Definition: stats.h:61
uint32_t DWORD
Definition: intro_types.h:49
QWORD TotalCount
The total number of times an event was measured.
Definition: stats.h:135
static uint64_t __rdtsc(void)
Definition: intrinsics.h:306
QWORD TIMESPEC
Definition: stats.h:127
Measures IntWinProcHandleCopyMemory invocations done for memory reads.
Definition: stats.h:69
Measures the self map entry validation.
Definition: stats.h:65
The number of valid stats IDs. Not a valid ID. Must always be the last entry in the enum...
Definition: stats.h:111
Measures the instruction search done for the SWAPGS protection.
Definition: stats.h:63
Measures MSR violation exits.
Definition: stats.h:31
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
Measures write EPT violations.
Definition: stats.h:20
enum _STAT_ID STAT_ID
Stat IDs.
Measures the handling of memory reads in which a read protection policy exists.
Definition: stats.h:72
#define __stats_case(x)
void IntStatStart(STAT_ID StatId)
Starts a stat measurement.
Definition: stats.c:316
QWORD TscSpeed
Number of ticks/second of this given guest. Should be the same as the global (physical) one...
Definition: guests.h:276
#define __likely(x)
Definition: common.h:63
Measures the look-up of EPT violation handlers.
Definition: stats.h:25
Measures the information gathering for the DPI mechanism.
Definition: stats.h:83
Measures exits on NtSetInformationProcess.
Definition: stats.h:100
static void GetTime(TIMESPEC *Time)
Returns the current time.
Definition: stats.c:120
Measures module load violation handling.
Definition: stats.h:37
Measures user mode crash handlers.
Definition: stats.h:54
Measures execute EPT violations.
Definition: stats.h:21
Measures glob-match exceptions.
Definition: stats.h:52
Measures read EPT violations.
Definition: stats.h:19
Measures the deletion of HOOK_GVA objects.
Definition: stats.h:40
Measures the token privileges DPI protection information gathering.
Definition: stats.h:90