Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions .github/workflows/conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
# means nothing -- the same defect class as a check count that folds skipped checks into
# passes. The number it publishes is the README's, reproducible locally by anyone with the
# toolchain: `tools/conformance.sh`.
#
# TWO ARCHITECTURES, ONE BASELINE: the same job on x86_64 and on aarch64 (GitHub's arm64
# runner, free for public repositories; the fielded edge box is aarch64). Both are held to the
# same conformance/baseline-<revision>.yml, and the suite exits 1 on an unexpected failure and
# on a stale entry, so two green jobs mean the same scenarios fail, for the same reasons, on
# both: the architectures' results are equal by construction, not by a reader comparing them.

name: conformance

Expand All @@ -26,8 +32,12 @@ permissions:

jobs:
conformance:
name: Conformance - the two rows
runs-on: ubuntu-latest
name: Conformance - the two rows, ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1
Expand Down Expand Up @@ -57,6 +67,6 @@ jobs:
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: conformance-results
name: conformance-results-${{ runner.arch }}
path: ${{ runner.temp }}/conformance
if-no-files-found: error
36 changes: 28 additions & 8 deletions test/beam_mcp/connectome/tracer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ defmodule BeamMCP.Connectome.TracerTest do
Tracer.start(Keyword.merge([collector: collector, server: @server], opts))
end

# The barrier before a stop: every trace message the BEAM will deliver for what has run so
# far is in the tracer's mailbox, and the tracer has handled it. A `:sys.get_state/1` call
# alone waits only for what has already ARRIVED: trace delivery is "dislocated on the
# time-line compared to other events" (`erlang:trace_delivered/1`'s documentation), and the
# floor leg of gate run 39344186410 counted one of two calls (weight 1, want 2).
# `trace:delivered/2` is this same BIF for a session (kernel's trace.erl calls it), so the
# tracer's private session handle is not needed here.
defp handled do
ref = :erlang.trace_delivered(:all)

receive do
{:trace_delivered, :all, ^ref} -> :ok
after
5_000 -> flunk("the BEAM did not confirm trace delivery within 5 s")
end

_ = :sys.get_state(Tracer)
:ok
end

# The tracer's sessions alive in the node, as weak handles; nothing left behind is `[]`.
defp sessions, do: Enum.filter(:trace.session_info(:all), &match?({@session, _}, &1))

Expand Down Expand Up @@ -866,7 +886,7 @@ defmodule BeamMCP.Connectome.TracerTest do
# A call that is a message to the code server from this process, whether or not the
# module is already loaded (under cover every module is).
_ = :code.get_path()
_ = :sys.get_state(Tracer)
:ok = handled()
:ok = Tracer.stop()
Process.unregister(:tracer_test_sender6)
{:ok, g} = Observed.snapshot(c)
Expand All @@ -884,7 +904,7 @@ defmodule BeamMCP.Connectome.TracerTest do
Traced.wrapped(@marker)
# The barrier: a stop ends the tracer on the next message it handles, writing nothing
# after the flag, so the trace message must be handled before the stop is asked.
_ = :sys.get_state(Tracer)
:ok = handled()
:ok = Tracer.stop()

{:ok, %Graph{} = g} = Observed.snapshot(c)
Expand All @@ -901,7 +921,7 @@ defmodule BeamMCP.Connectome.TracerTest do
test "a dynamic call through apply/3 is the same edge as a static one", %{collector: c} do
{:ok, _} = start(c, modules: [Beta])
Traced.apply_wrapped(Beta, [@marker])
_ = :sys.get_state(Tracer)
:ok = handled()
:ok = Tracer.stop()

{:ok, g} = Observed.snapshot(c)
Expand All @@ -919,7 +939,7 @@ defmodule BeamMCP.Connectome.TracerTest do
{:ok, _} = start(c, modules: [Beta])
Traced.tail(1)
Alpha.run(1)
_ = :sys.get_state(Tracer)
:ok = handled()
:ok = Tracer.stop()

{:ok, g} = Observed.snapshot(c)
Expand Down Expand Up @@ -962,8 +982,8 @@ defmodule BeamMCP.Connectome.TracerTest do
assert_receive :got
assert_receive :got
# stop/0 ends the tracer on the next message it handles and discards what is still
# queued; a system message is answered in mailbox order, so this waits for both.
_ = :sys.get_state(Tracer)
# queued; handled/0 waits for both trace messages to arrive and be handled first.
:ok = handled()
:ok = Tracer.stop()
send(receiver, :done)
Process.unregister(:tracer_test_sender)
Expand Down Expand Up @@ -1023,7 +1043,7 @@ defmodule BeamMCP.Connectome.TracerTest do
send(pid, :something_else)
send({:tracer_test_sender3, node()}, :to_myself_by_name)
assert Tracer.running?()
_ = :sys.get_state(Tracer)
:ok = handled()
:ok = Tracer.stop()
Process.unregister(:tracer_test_sender3)

Expand All @@ -1040,7 +1060,7 @@ defmodule BeamMCP.Connectome.TracerTest do
anon = spawn_link(fn -> receive do: (_ -> :ok) end)
{:ok, _} = start(c, processes: [:tracer_test_sender2])
send(anon, @marker)
_ = :sys.get_state(Tracer)
:ok = handled()
:ok = Tracer.stop()
Process.unregister(:tracer_test_sender2)

Expand Down
Loading