Timing Query Services

Time query services allow a virtual device to obtain information about how long the system, a virtual machine, or a thread has been executing, in milliseconds.

When the system creates a thread or virtual machine, it sets the execution time for the thread or virtual machine to zero. The system increases the execution time only when the thread or virtual machine actually runs. Therefore the execution does not reflect the length of time the thread or virtual machine has existed, but indicates the amount of time the thread or virtual machine has run. Note however that any code executed in the indicated thread or virtual machine contributes to the tally; it is not the case that one second of thread execution time or virtual machine execution time translates into one second of actual CPU time given to the application.

For each query service there are two variants, the standard form and the last updated form (for example, _GetThreadExecTime and _GetLastUpdatedThreadExecTime). The standard form returns the time to millisecond accuracy, whereas the last updated form returns the time only to an accuracy of approximately 50 milliseconds. The difference is that the standard form will ask the timer device to give the time to millisecond accuracy, and use the result to compute the value to return, whereas the last updated form returns the value most recently obtained by a standard form call, or by the timer device explicitly updating the system clock (which happens on every timer tick).

If the interval being measured is on the order of seconds or minutes, the last updated form is sufficient because a 50 milliseconds variation will not make a noticeable difference. If the interval being measured is less than one half second, you may be better off with the standard form.

Note also that all of the query services return 32-bit unsigned values. This value overflows every 49 1/2 days. If a virtual device is sensitive to rollover, it should schedule a time-out every 30 days.

Make certain to handle the boundary cases correctly. For example, the following code is incorrect when the system time is close to rollover.

VMMCall Get_System_Time
add    eax, 60000      ; Do it for one minute
mov    StopTime, eax
...
VMMCall Get_System_Time
cmp     eax, StopTime  ; Q: Time to stop?
jae     StopMe         ; Y: Stop doing it
 

If the operation starts less than one minute before a timer rollover, the operation will halt prematurely, because StopTime will contain a very small number due to addition overflow. Conversely, if the operation starts slightly earlier than one minute before timer rollover, it may never stop because StopTime will be extremely close to 0xFFFFFFFF. The correct way to handle the boundary cases is as follows:

VMMCall Get_System_Time
mov    StartTime, eax
...
VMMCall Get_System_Time
sub    eax, StartTime
cmp    eax, 60000      ; Q: Time to stop?
jae    StopMe          ; Y: Stop doing it