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  {
105  }
106 
107  return "<err_counter>";
108 
109 #undef __stats_case
110 }
111 
112 static __forceinline void
114  _Out_ TIMESPEC *Time
115  )
123 {
124 #ifndef STATS_HAS_HIGHRES_TIMER
125  *Time = __rdtsc();
126 #else
127  clock_gettime(CLOCK_MONOTONIC, Time);
128 #endif
129 }
130 
131 
132 static __forceinline void
134  _In_ TIMESPEC const *End,
135  _In_ TIMESPEC const *Start,
136  _Out_ TIMESPEC *Result
137  )
145 {
146 #ifdef STATS_HAS_HIGHRES_TIMER
147  if (__likely(End->tv_nsec > Start->tv_nsec))
148  {
149  Result->tv_sec = End->tv_sec - Start->tv_sec;
150  Result->tv_nsec = End->tv_nsec - Start->tv_nsec;
151  }
152  else
153  {
154  Result->tv_sec = End->tv_sec - Start->tv_sec - 1;
155  Result->tv_nsec = NSEC_PER_SEC + End->tv_nsec - Start->tv_nsec;
156  }
157 #else
158  *Result = *End - *Start;
159 #endif
160 }
161 
162 
163 static __forceinline void
165  void
166  )
172 {
173 #ifdef STATS_HAS_HIGHRES_TIMER
174  for (DWORD i = 0; i < ARRAYSIZE(gCounters); i++)
175  {
176  if (gCounters[i].StartEventId == gEventId)
177  {
178  gCounters[i].StatCalls++;
179  }
180  }
181 #endif
182 }
183 
184 
185 static __forceinline void
187  _Inout_ TIMESPEC *Time,
188  _In_ TIMESPEC const *Adder
189  )
196 {
197 #ifdef STATS_HAS_HIGHRES_TIMER
198  Time->tv_sec += Adder->tv_sec;
199  Time->tv_nsec += Adder->tv_nsec;
200 
201  if (Time->tv_nsec >= (INT64)NSEC_PER_SEC)
202  {
203  ++Time->tv_sec;
204  Time->tv_nsec -= NSEC_PER_SEC;
205  }
206 #else
207  *Time += *Adder;
208 #endif
209 }
210 
211 
212 void
214  void
215  )
219 {
220  LOG("[STATS] Introspection stats (totaling %lld events):\n", gEventId);
221 
222  for (DWORD i = 0; i < statsMaxCounter; i++)
223  {
224  // This is double the size of what we really need
225  char line[255];
226  STAT_COUNTER const *pCounter = &gCounters[i];
227 
228  if (0 == pCounter->TotalCount)
229  {
230  continue;
231  }
232 
233 #if defined(STATS_DISABLE_TIMER)
234  snprintf(line, sizeof(line), "%20s: %12llu times\n", IntStatGetName(i), pCounter->TotalCount);
235 
236 #elif defined(STATS_HAS_HIGHRES_TIMER)
237  double t = (double)(pCounter->Total.tv_sec * NSEC_PER_SEC +
238  pCounter->Total.tv_nsec) / (double)pCounter->TotalCount;
239  INT64 spe = 0;
240  INT64 nspe = (INT64)t % NSEC_PER_SEC;
241 
242  for (INT64 j = (INT64)t; j >= (INT64)NSEC_PER_SEC; j -= NSEC_PER_SEC)
243  {
244  ++spe;
245  }
246 
247  snprintf(line, sizeof(line),
248  "%25s: %8lld times - %4lu.%09lu (total) %4lld.%09lld (per exit) %4lld.%09lld (max)\n",
249  IntStatGetName(i), pCounter->TotalCount,
250  pCounter->Total.tv_sec, pCounter->Total.tv_nsec,
251  spe, nspe,
252  NSEC_TO_SEC(pCounter->Max), pCounter->Max % NSEC_PER_SEC);
253 #else
254  snprintf(line, sizeof(line),
255  "%25s: %20llu CPU ticks %12llu times - %4.6f (total) %4.12f (per exit) %4.12f (max)\n",
256  IntStatGetName(i),
257  pCounter->Total, pCounter->TotalCount,
258  pCounter->Total / (double)gGuest.TscSpeed,
259  pCounter->Total / (double)gGuest.TscSpeed / (double)pCounter->TotalCount,
260  pCounter->Max / (double)gGuest.TscSpeed);
261 #endif
262 
263  LOG("%s\n", line);
264  }
265 }
266 
267 
268 void
270  _In_ STAT_ID StatId
271  )
277 {
278  STAT_COUNTER *pCounter = &gCounters[StatId];
279 
280  pCounter->TotalCount = 0;
281 
282 #ifndef STATS_DISABLE_TIMER
283  pCounter->Max = 0;
284  pCounter->StartEventId = 0;
285 
286  memset(&pCounter->Start, 0, sizeof(pCounter->Start));
287 #endif
288 }
289 
290 
291 void
293  void
294  )
298 {
299  for (STAT_ID i = 0; i < statsMaxCounter; i++)
300  {
301  IntStatsReset(i);
302  }
303 }
304 
305 
306 #ifndef STATS_DISABLE_TIMER
307 
308 void
310  _In_ STAT_ID StatId
311  )
323 {
324  STAT_COUNTER *pCounter;
325 
327  {
328  return;
329  }
330 
331  pCounter = &gCounters[StatId];
332 
333  pCounter->StartEventId = gEventId;
334 
335  GetTime(&pCounter->Start);
336 
338 
339  pCounter->StatCalls = 0;
340 }
341 
342 
343 void
345  _In_ STAT_ID StatId
346  )
357 {
359  {
360  return;
361  }
362 
363  TIMESPEC total, end;
364  STAT_COUNTER *pCounter = &gCounters[StatId];
365 
366  pCounter->TotalCount++;
367 
368  if (pCounter->StartEventId != gEventId)
369  {
370  if (pCounter->StartEventId != 0)
371  {
372  ERROR("[ERROR] StartCount on event id %lld and stop on %lld for counter %d\n",
373  pCounter->StartEventId, gEventId, StatId);
374  }
375 
376  return;
377  }
378 
379  GetTime(&end);
380 
382 
383  DiffTime(&end, &pCounter->Start, &total);
384 
385 #ifndef STATS_HAS_HIGHRES_TIMER
386 
387  if (total > pCounter->Max)
388  {
389  pCounter->Max = total;
390  }
391 
392 #else
393 
394  QWORD totalNs = total.tv_sec * NSEC_PER_SEC + total.tv_nsec;
395  QWORD statCallNs = pCounter->StatCalls * gStatCallTimeNs;
396 
397  // Subtract the time spent in clock_gettime (by how many times it was called)
398  if (totalNs > statCallNs)
399  {
400  TIMESPEC t1;
401  TIMESPEC oldTotal = total;
402 
403  t1.tv_sec = statCallNs / NSEC_PER_SEC;
404  t1.tv_nsec = statCallNs - (t1.tv_sec * NSEC_PER_SEC);
405 
406  total.tv_nsec -= pCounter->StatCalls * gStatCallTimeNs;
407 
408  DiffTime(&oldTotal, &t1, &total);
409  }
410 
411  if (totalNs > pCounter->Max)
412  {
413  pCounter->Max = totalNs;
414  }
415 
416 #endif // STATS_HAS_HIGHRES_TIMER
417 
418  AddToTime(&pCounter->Total, &total);
419 
420  pCounter->StartEventId = 0;
421 }
422 
423 void
425  _In_ STAT_ID StatId
426  )
432 {
433  gCounters[StatId].StartEventId = 0;
434  gCounters[StatId].StatCalls = 0;
435 }
436 
437 #endif // STATS_DISABLE_TIMER
438 
439 
440 void
442  void
443  )
449 {
450 #ifdef STATS_HAS_HIGHRES_TIMER
451  const DWORD calibrationCalls = 10000;
452  TIMESPEC start, end, total;
453 
454  GetTime(&start);
455 
456  for (DWORD i = 0; i < calibrationCalls; i++)
457  {
458  GetTime(&total);
459  }
460 
461  GetTime(&end);
462 
463  DiffTime(&end, &start, &total);
464 
465  gStatCallTimeNs = (end.tv_nsec - start.tv_nsec) / calibrationCalls;
466 
467  // Allow a 10% error margin
468  gStatCallTimeNs -= (gStatCallTimeNs / 10);
469 
470  LOG("[DEBUG] Calibrated clock_gettime timer to %lld nanoseconds\n", gStatCallTimeNs);
471 #endif
472 }
Measures kernel mode exceptions checks.
Definition: stats.h:51
void IntStatStop(STAT_ID StatId)
Stops a stat measurement.
Definition: stats.c:344
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:47
#define _Out_
Definition: intro_sal.h:22
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 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:96
#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:186
QWORD IntroActiveEventId
The event ID on which introcore became active.
Definition: guests.h:377
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:292
Measures page table entries writes.
Definition: stats.h:47
Measures event injections.
Definition: stats.h:36
void IntStatsInit(void)
Initialization routine.
Definition: stats.c:441
#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:424
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:121
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:213
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:269
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:316
static void DiffTime(TIMESPEC const *End, TIMESPEC const *Start, TIMESPEC *Result)
Computes the delta between two time values.
Definition: stats.c:133
#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:164
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:123
static uint64_t __rdtsc(void)
Definition: intrinsics.h:306
QWORD TIMESPEC
Definition: stats.h:115
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:99
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:48
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:309
QWORD TscSpeed
Number of ticks/second of this given guest. Should be the same as the global (physical) one...
Definition: guests.h:272
#define __likely(x)
Definition: common.h:46
Measures the look-up of EPT violation handlers.
Definition: stats.h:25
Measures the information gathering for the DPI mechanism.
Definition: stats.h:83
static void GetTime(TIMESPEC *Time)
Returns the current time.
Definition: stats.c:113
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