Repository navigation
Modernize signposts and os_log: decodable strings, dynamic tracing, Instruments package - #87
Merged
Merged
Conversation
This was referenced Oct 18, 2026
Merged
maleadt
added a commit
to JuliaGPU/Metal.jl
that referenced
this pull request
Oct 18, 2026
Temporary, until ObjectiveC.jl 7.1 is released (JuliaInterop/ObjectiveC.jl#87).
maleadt
added a commit
to JuliaGPU/Metal.jl
that referenced
this pull request
Oct 18, 2026
Temporary, until ObjectiveC.jl 7.1 is released (JuliaInterop/ObjectiveC.jl#87).
Unified logging stores the name and format strings of a message as offsets into the image passed as dso, and resolves them from that image's on-disk __TEXT segment when decoding. We passed a heap copy of libjulia, so libtrace dropped every message. Instead, emit the strings as llvmcall constants in a non-inlined function specialized on them, so that they end up in the __cstring section of the package image that precompiles the call site, and use the linker's __dso_handle as the image handle. The signpost macros precompile these specializations while generating output, so that call sites that are only compiled at run time still find their strings in an image. Non-literal names, and call sites outside of package images, fall back to a generic "Julia" name with the name prepended to the message. This removes the last use of Libdl.
Signposts on a regular category are always enabled and end up in the system log, which is too expensive for hot paths like kernel launches. The DynamicTracing categories keep them disabled until a tool asks for them, but Instruments only does so for subsystems that an instrument names, so add instruments_package to build such an instrument. It plots the intervals and events of the given subsystems on a timeline, lists them, and summarizes the intervals by subsystem and name.
`@signpost_event` emitted events with OS_SIGNPOST_ID_NULL, which Apple's os_signpost_event_emit never does: Instruments records such events without an identifier. Use OS_SIGNPOST_ID_EXCLUSIVE instead, and drop signposts with a null or invalid ID, as the macros do. os_signpost_id_generate returns the null ID when signposts get disabled between the `is_signpost_enabled` check and the start of an interval.
os_log_type_enabled and os_signpost_enabled return a C bool, which we read as a Cint and converted with `Bool`. That relied on the callee extending the result, and added an InexactError path to every signpost call site.
The default stop message of `@signpost_interval` is "stop", not "end". Creating a logger calls os_log_create, which takes 40-60 ns: cheap, but several times the cost of a disabled signpost, so recommend hoisting it on hot paths. Also mention that the system truncates long messages.
maleadt
added a commit
to JuliaGPU/Metal.jl
that referenced
this pull request
Oct 18, 2026
Temporary, until ObjectiveC.jl 7.1 is released (JuliaInterop/ObjectiveC.jl#87).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The signposts and
os_logmessages fromObjectiveC.OSnever showed up in Instruments orlog show. Unified logging doesn't copy a message's name and format strings into the log. It records their offsets in the Mach-O image passed asdso, plus that image's UUID, and the decoder reads the strings back from the image's__TEXTsegment on disk. We passed a heap copy of libjulia's file contents asdso, so libtrace dropped every message.This PR makes those strings static, and makes signposts cheap enough for hot paths like kernel launches in Metal.jl.
Static strings. The name and format strings are emitted as private
unnamed_addrLLVM constants (viallvmcall) in a@noinlinefunction specialized on them. When that specialization is compiled into a package image, the constants end up in the image's__TEXT,__cstringsection, which the decoder reads like Apple's own__oslogstring(pkgimages drop custom sections, so we can't use that one). A weak hidden__dso_handlereference, which the linker resolves to the image's Mach-O header, provides the matchingdso.@signpost_intervaland@signpost_eventprecompile that specialization when they are expanded during precompilation, so call sites that are only compiled at run time still find their strings in the image of the package that contains them. When there is no image (a name that isn't a string literal, or code outside a package image), signposts are named"Julia"with the actual name prepended to the message. If ObjectiveC.jl itself isn't precompiled, they borrow a%sfrom libobjc, whose strings are always decodable.Dynamic tracing.
OS.CATEGORY_DYNAMIC_TRACING(andCATEGORY_DYNAMIC_STACK_TRACING,CATEGORY_POINTS_OF_INTEREST) are Apple's categories for signposts that stay disabled until a tool asks for them. A disabled@signpost_intervalthen costs oneos_signpost_enabledcheck and builds no message: about 16 ns on an M1, the same as Apple's C macros. Enabled intervals cost about 250 ns, also the same as C, and allocate nothing. Instruments only enables these categories for subsystems that an instrument names, soOS.instruments_package(subsystems)builds such an instrument with Xcode'sinstrumentbuilder. It plots the intervals and events of those subsystems on a timeline, lists them, and summarizes the intervals by subsystem and name. Use it withxctrace record --package $path --instrument "Julia Signposts", or open the package in Instruments.app.Alignment with Apple's macros. Events now use
OS_SIGNPOST_ID_EXCLUSIVEinstead of the null ID, and signposts with a null or invalid ID are dropped, asos_signpost_emit_with_typedoes. The enabled checks are declared as returningBool, matching the C prototypes. The docs now say thatOSLogconstruction (os_log_create, 40–60 ns) should be hoisted out of hot paths, and fix the documented default stop message.API changes are additive.
interval_begin/interval_end/os_signpost_emit_with_typegained methods that take the static strings, and their name and message arguments are no longer restricted toString. The now-unused Libdl dependency is dropped, and the version is bumped to 7.1.0 so that Metal.jl can depend on the release.Verified with
xctrace recordon macOS 27 (M1): without this PR the os-signpost table contains no Julia signposts at all. With it, a precompiled package emitting DynamicTracing intervals and events in three subsystems, recorded with a package built for two of them, shows exactly the intervals and events of those two, with their names and messages. The tests check that the strings of a call site in another package end up in that package's image. The mechanism also works injuliacexecutables and libraries (with and without--trim=safe): the strings land in the binary's__cstring, andlog showdecodes them.