-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support pkgimage caching and other improvements #5
base: main
Are you sure you want to change the base?
Conversation
Switch to embedding PROBES and friends within the probed module Add at-region macro for automatic start/stop Add UInt lib_id for probe-provided identifier, passed from start->stop Pass category, kind, and lib_id to probe Add more helpers for enabling/disabling/setting probes Add Preferences-based flag for default enabling probes
src/ProbeBase.jl
Outdated
end | ||
end | ||
@generated function probe_trigger(spec::TracepointSpec, category::Symbol, kind::Symbol, lib_id::Int64, arg) | ||
Targs = Expr(:tuple, :Symbol, :Symbol, :Int64, :Any) | ||
ex = Expr(:call, :ccall, :(spec.payload[]), :Int64, Targs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gbaraldi Do you know if this ccall
will be considered an "escape" for the purposes of alloc-opt?
The semantics of ccall are specifically non-escaping, but I'm assuming that we end up allocating this tuple on the heap just so that it can be boxed as an Any
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nicely done. The API feels pretty intuitive to me, and I'm a fan of the minimal feature-set!
The main open in my mind is what "two-phase" APIs will look like, where the event is allocated and then cached between the individual runs where the event is triggered.
Sounds like that is covered by #4. If you're curious, you can peek at topolarity/julia@fbc704b
for what a two-phase POC looked like when we were instrumenting Base with @KristofferC
Add fast ABI for zero-allocation tracepoints Add shareable semaphore for aliased tracepoints Load semaphore and probe just once in at-region Rename to Tracepoints.jl
Fix Fast ABI probe compilation Pass name as extra argument to Fast ABI Pass Int(0) as arg for Null (Nothing) ABI
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the latest improvements! This direction is feeling good so far 👍
FWIW, the ABI will probably also have to split on the various event types with different "temporal restrictions" (e.g. strict scoping vs. point-wise events) for the @tracepoint
and @region_start/finish
API's. It might be worth discussing today which temporal restrictions we want to support and how.
Also on another note - This requires iterating all tracepoints globally and calling set!
when you want to load a tracing library, right? How is that expected to be done, and how will a client library know what type of ccallable
to provide for a tracepoint depending on the metadata that was provided?
src/Tracepoints.jl
Outdated
if abi_type == :Nothing | ||
Targs = Expr(:tuple, :Symbol, :Symbol, :Int64, :Symbol, :Int) | ||
else | ||
Targs = Expr(:tuple, :Symbol, :Symbol, :Int64, :Symbol, abi_type, :Symbol) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about eagerly coercing types to their widest supported type?
e.g. UIntX would be converted to UInt64; Float16, Float32, Float64 would be converted to Float64; etc.
Might be helpful to keep the ABI a little bit smaller.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can understand doing that for UInt
/Int
types, but that feels concerning for floating point types, as the conversion can be somewhat lossy. Alternatively, we could sign/zero-extend to 64 bits (as we wouldn't lose information), but that does require undoing the extension on the probe end (but it does give us a more fixed-sized fast ABI, which I see as a plus).
src/Tracepoints.jl
Outdated
probe_trigger(spec, category, kind, lib_id, abi_type, arg, name) | ||
end | ||
end | ||
@generated function probe_trigger(spec::TracepointSpec, category::Symbol, kind::Symbol, lib_id::Int64, ::Val{abi_type}, arg, name) where {abi_type} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@generated function probe_trigger(spec::TracepointSpec, category::Symbol, kind::Symbol, lib_id::Int64, ::Val{abi_type}, arg, name) where {abi_type} | |
@generated function probe_trigger(spec::TracepointSpec, category::Symbol, kind::Symbol, lib_id::Int64, ::Val{abi_type}, arg::abi_type, name::Symbol) where {abi_type} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I previously committed this, but it actually doesn't work for me on Julia ~master, I get a MethodError
. This seems like maybe a Julia bug? Or maybe I'm doing it wrong.
read from a variable with the same name, but is instead hard-coded as `value` | ||
for both start and finish tracepoints. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
read from a variable with the same name, but is instead hard-coded as `value` | |
for both start and finish tracepoints. | |
read from a variable with the same name, but is instead evaluated at parse-time | |
and the result is hard-coded for both start and finish tracepoints. |
I find the different evaluation time for this syntax confusing. Why not just have it evaluate at runtime the same way the arg::T
syntax does?
It seems like there's no way, e.g., to pass a dynamically-constructed tuple (x,y)
to the API without copying it into a local with the intended name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not evaluated at parse-time - I admit hard-coded
is not what I meant, it's really just interpolated at runtime as you'd expect (if this is not true, please let me know!).
for both start and finish tracepoints. | ||
|
||
`(start_arg::ST, finish_arg::FT)` - This format is like `arg::T`, except that | ||
it allows specifying different values for the start and finish tracepoints. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a particular use case in mind for this? Additionally, when is this evaluated and where does the name(s) come from?
This feels a bit ambiguous-in-spirit with explicit tuple initializers that happen to include type assertions like
@region z::Any=(1::Int64, 0::Int64)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's convenient to have this for start-finish pairs in functions where the argument points to two different values at start and finish - the extra type specification is there since those values may also change types in a predictable manner. The alternative is to have to use @region_start/finish
, which I prefer not to use since it's easy to mess them up. Do you have another recommendation for syntax for this kind of need?
@region z::Any=(1::Int64, 0::Int64)
The macro should really only allow @region z=(1::Int64, 0::Int64)
(although I haven't tested that).
push!(ex.args, QuoteNode(abi_type)) | ||
if abi_type != :Any | ||
if abi_type == :Nothing | ||
push!(ex.args, :(0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why a special ABI where we always pass 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is here because it makes it easier to write probe functions (at least in Julia), as I can specify it as (..., abi_type, arg...)
and then slurp up the correct number of arguments (1 or 2) depending on abi_type
. If I don't pass something, then I'd need to wrap up abi_type
into the arguments being splatted in and then unwrap it, inspect it, and unwrap further. I could always define multiple probes, but it's just more annoying to have to do that just to support the Nothing
ABI.
end | ||
spec_abi = determine_abi(spec) | ||
spec_abi_sym = spec_abi == :Any ? :slow : :fast | ||
if abi != :all && spec_abi_sym != abi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In reality the :fast
ABI is a function of the actual arg type, so this only partially validates right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is only intended to validate that the probe supports any fast ABI, the slow ABI, or both as necessary. I'm hoping to avoid specifying which specific fast ABI subsets a probe supports, although I don't know how realistic that is - thoughts?
That's a good point - if we need to provide a different probe for each supported ABI type, then this current API doesn't give us enough control (or we have to do it manually, which is a pain). What kind of API would you propose for this? I'd still like to keep the current |
Allow at-region declaration in front of function definition Use Expr(:tryfinally) instead of try...finally Fix at-tracepoint/region_start/region_finish
Co-authored-by: Cody Tapscott <[email protected]>
4c53154
to
23463b9
Compare
Can we change the name of the One question: If we want to trace a distributed execution, how would it work? Do we need to run something like this? @everywhere Tracepoints.set!(MY_PROBE) |
Switch to embedding PROBES and friends within the probed module
Add at-region macro for automatic start/stop
Add Int lib_id for probe-provided identifier, passed from start->stop
Pass category, kind, and lib_id to probe
Add more helpers for enabling/disabling/setting probes
Add Preferences-based flag for default enabling probes
Todo:
NamedTuple
@region
tests(start_arg::ST, finish_arg::FT)
argument formatset!
/enable!
/disable!
__probebase_funcs__
is only for rooting purposes@region
Try allocating aAdd specialized no-alloc ABI withRef{T}
box at macro parse timeSymbol
flagPtr{Cvoid}
probesset!
of tracepoints of incompatible ABI