ykjit / yk

yk packages
https://ykjit.github.io/yk/
Other
29 stars 7 forks source link

More generally support asynchronous interruptions #593

Open vext01 opened 1 year ago

vext01 commented 1 year ago

At any time, the process being traced could be interrupted by an asynchronous event (signal handler, context switch etc.), transferring execution elsewhere. In a PT packetstream, this is indicated by a FUP (flow update) packet.

At the time of writing, I'm about to add support for the simplest of such cases: where execution is diverted to some untraceable code external to the "interpreter code" for a while, before eventually returning back to the interpreter where we were interrupted. In this case we will simply ignore the interruption (we simply look for a [FUP, TIP.PGD, TIP.PGE] sequence with no other control flow events mixed in and ignore it)

But in general, a FUP could go anywhere, including back into the interpreter itself. Arguably it's never(?) useful to copy interruptions over into the trace IR and have those compiled-in to the executable trace. However, in order to exclude the interruption from the trace, we would need to ability to identify the end of the interruption. I'm not sure how hard that is in general.

vext01 commented 1 year ago

What makes generally handling FUP hard is that our decoder is eager and will try to get as far in the control flow as it can without asking the packet stream for more packets.

For example, if we have three IR blocks A, B and C that codegen to three MIR blocks at address ranges [0, 9], [10, 19] and [20, 29]. If A unconditionally jumps to B, and B unconditionally jumps to C, then the PT decoder is able to infer this statically without asking for packets.

Our decoder is an iterator. In the above scenario, it would have yielded block A, B and C in turn (back to the consumer, which is the trace compiler). But upon finding where to go after C, we may encounter a FUP packet telling us that there was an interrupt at address 3 (in the middle of A). This is problematic, as by the time we see this, the iterator has already lied to the consumer!

I wonder if we need some kind of "peeking" strategy, where the iterator always has the next control flow event buffered in case it's a FUP... If it is a FUP, it can check the address of the interruption and know which block is interrupted, before jumping the gun an yielding incorrect blocks.

I can also foresee a situation where TNT decisions of the interruption are misinterpreted as decisions in the "regular" control flow path...

ltratt commented 1 year ago

Just because the interface we expose is an iterator doesn't mean the underlying thing needs to iterate one element at a time. It can search arbitrarily far forwards so long as it provides a simple "one element a a time" API to the user.

vext01 commented 1 year ago

indeed.

vext01 commented 1 year ago

From the top of my head, libipt peeks one "control flow event" ahead.