Skip to content

adding ze_validator to arginne-lcf/devel - #529

Open
myrepo1 wants to merge 2 commits into
argonne-lcf:develfrom
anlsys:first-half-merge
Open

adding ze_validator to arginne-lcf/devel#529
myrepo1 wants to merge 2 commits into
argonne-lcf:develfrom
anlsys:first-half-merge

Conversation

@myrepo1

@myrepo1 myrepo1 commented Aug 11, 2026

Copy link
Copy Markdown

Refactored the ze_validator:

  1. Split into multiple files
  2. Executable to profile device properties (part of the validator)
  3. Added multiple new detections

@TApplencourt

Copy link
Copy Markdown
Collaborator

+6,802-6,031. : ) I hope you didn't rebase correctly as I don't expect this PR to change the extract and ze_api.h

context['hostname'] = hostname
context['api'] = m[1]
#zeDriversInit or zeInit must be the first one to be called before any api calls
check_initialization(context,m)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check_init just need m[1], but you added m[1] into context just before... so why are you passing both context and m1..?

#puts "#{m[1]}"
on_exit(m, hostname, context, defi)
end
#ADDED: this event may have signaled something a deferred command list

@TApplencourt TApplencourt Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are all those "ADDED"?! What is the point of this word?!

hostname = e.stream.trace.get_environment_entry_value_by_name('hostname').value
context = e.get_common_context_field.value
#the event's own fields: input args at _entry, results at _exit
defi = e.payload_field.value

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why defi? What is the meeting of defi ? why not payload_filed?

@TApplencourt TApplencourt Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh found in to the top

defi -- the decoded event payload ("definition").`

Definition... I will vote for payload ...

#fall through the `if` and are ignored.
m = e.name.match(/:(z.*)_(entry|exit)/)
if m
#hostname is trace-level metadata (recorded once per trace), while

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why it's important here to said that hostname is recorde one per trace? What does this add to the code to know that?

#hostname is trace-level metadata (recorded once per trace), while
#vpid/vtid come from LTTng's per-event common context. Merging them
#with the API name produces the `context` tuple that identifies
#"who is calling what", threaded through the entire validator.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

threaded through ? wath does that mean threaded through ? So much fancy english

print_leak_error(ctx, t, h) #it prints the type as well
}
}
# CHANGED: memory_allocation is now nested by context handle

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

???
Really? You think comment should refer old commit information? We have git-blame if we want to know how old version of the code is doing...

Please, review everything and ask yourself if the comment make any sense, and that every word of the comment add MEANINGFUL information, not slop who just add noise.

@@ -0,0 +1,29 @@
---
zeCommandListDestroy:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the point of the second argument, if everything is all the time command_list?
Can we add some with event maybe?

@TApplencourt

TApplencourt commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Oh and yeah, maybe a better name for the PR will help... Pull request into devel is not that supper meaningful

# earlier version of the model and explain WHY the change was necessary. They
# are kept because the reasoning (e.g. why memory is keyed by context) is not
# recoverable from the code alone.
# =============================================================================

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... Comment are here to explain context that are not "recoverable from the code"
It doesn't matter AT ALL if we was Added changed removed. Only the current version of the code mattern

Comment on lines +60 to +64
#One of these APIs must be called before any other calls
# The Level Zero spec requires the runtime to be initialized before any other
# entry point is used. Calling anything else first is undefined behavior, so
# StateObject#check_initialization watches for one of these appearing first.
INIT_API_NAMES = ['zeInit', 'zeInitDrivers']

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to rexplain the same thing into 20 difference place.
You explain the except behavior in check_initialization. I think we got it.

@sub_devices = []
@memory_allocations = Hash.new { |h, k| h[k] = {} }
@property_fetched = false
#This

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

???????

Comment on lines +239 to +253
# No need to create DeviceMemory class. Just create Memory with the specified type (device,shared,host)
# class DeviceMemory < Object
# @typename = 'memory_allocation_device'
# attr_reader :context
# attr_reader :size
# attr_reader :device

# def initialize(handle, context, size, device)
# super(handle,context,size,device)
# @context = context
# @size = size
# @device = device
# end
# end

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not commit commented code

# fill/kernel that still references its address range can be reported as a
# use-after-free instead of silently passing (unknown pointer).
attr_accessor :freed_by
def initialize(handle, context, size, owned_by, memtypestr="shared")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why by default it should be shared?

Comment on lines +227 to +229
# Device allocations must be explicitly made resident before the GPU can
# use them; host and shared memory is always accessible, so it starts
# resident and stays that way.

@TApplencourt TApplencourt Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What? Are you sure of that? Where does the spec said that? And you have a test who verify this?
AFAIK device allocation will automaticaly be made resident the first time they have been used

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a little complicated as the driver usually makes things resident and it depends on if the device supports page-faults (https://oneapi-src.github.io/level-zero-spec/level-zero/latest/core/PROG.html#device-residency). if the memory is passed as a kernel argument we don't need to explicitly make it resident but for things like indirect accesses we will.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so maybe not "Device allocations must be explicitly made resident" but "Device allocations must be resident" (but in many cases this happens under the hood)


def signaled?
@signaled
end

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just just use signaled? or signaled everywhere then? What does this new keyword add us?

Replay the ze_validator work on top of alcf/devel (ab4bc29) without the
accidental reverts of upstream that had accumulated on first-half-merge
(notably PR argonne-lcf#484 'H2yaml is now mandatory' and the Level Zero 1.18.2
header update, which commit 4c34a85 had rolled back).

Adds:
  ze_validator.in, ze_validator_zemodel.rb,
  ze_validator_function_entry_exit_callbacks.rb,
  ze_validator_entry_exit_helpers.rb, ze_validator_state_object.rb,
  ze_thread_safety.yaml, ze_deprecated.json, ze_device_property.cpp

Build glue:
  configure.ac generates backends/ze/ze_validator from ze_validator.in
  Makefile.am installs the validator script, builds ze_device_property
  with icpx, and ships the validator data files via data_DATA/EXTRA_DIST.
# exactly this.
#
# TYPICAL USE
# -----------

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TApplencourt I suggest we keep usage as documented here.
In the future, we want to add a new CLA on validation passes to run. e.g.: iprof --validate mpi,ze [...] -- that automatically sets the appropriate backends (mpi,ze) and run validators after the execution completed

@@ -0,0 +1,147 @@
// ze_device_property.cpp

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This program calls zeDeviceGetCommandQueueGroupProperties for each device a host can access, and maps the ordinal index to the compute/copy engines. This program gets executed at validation time by the validator, and the mapping will be saved as a ze_device_property.json file (to avoid subsequent calls to this program at later validation times). The resulting mapping gets used for checking submission of kernels to command lists and command queues that are associated with copy-only engines

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with the ordinal PR #526, is this still needed?

Comment thread backends/ze/Makefile.am

bin_SCRIPTS = \
tracer_ze.sh
tracer_ze.sh \

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For compiling the ze_device_property.cpp
Binary gets stored to build/ici/bin

@@ -0,0 +1,8 @@
{

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stores deprecated APIs. The first field inside the bracket is the version number the API got deprecated from (e.g., "1.10"), and the second field denotes recommended alternative (e.g., "zeInitDrivers").

@myrepo1 myrepo1 changed the title Pull request into devel adding ze_validator to arginne-lcf/devel Aug 12, 2026
# -> pump_deferred (a signal may have unblocked pending GPU work)
# ...and once the trace is exhausted, ze_validator.in calls #check_issues.
#
# TWO VOCABULARY NOTES THAT UNLOCK THE REST OF THE CODE

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comments can be simplified likely as well. Good to have overview but maybe a little less.

def immediate?
return !desc
end
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too many comments everywhere, here is a good example (4 lines of comments for 1 of code)
All these comments may run out-of-sync if edits are made (to Level Zero, or to the validator itself) -- removing them will ease maintenance.

Level Zero semantics is well-defined and the code should be self-contained: the spec is the doc

# are kept because the reasoning (e.g. why memory is keyed by context) is not
# recoverable from the code alone.
# =============================================================================
module ZEModel

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll join Thomas: too much comments.
Just replace these 50 lines of comments with 1 line, e.g.:

This file implements Level Zero objects (https://oneapi-src.github.io/level-zero-spec)

#one of them turns up a cycle
stuck.each { |u| break if dfs.call(u); }
report_deadlock_cycle(state, found) if found
end

@rpereira-dev rpereira-dev Aug 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest using a graph library instead of reimplementing those standard algorithm.
This: https://github.com/monora/rgl - can detect cycle: https://rubydoc.info/github/monora/rgl/RGL/Graph#acyclic%3F-instance_method

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Work for me, If using any dependency, they need to be packaged in spack or in https://github.com/argonne-lcf/THAPI-spack


#Find the memory allocation containing the ptr
def find_allocation_containing(allocations, ptr)
allocations.each_value.find { |m| m.base && m.base <= ptr && ptr < m.base + m.size }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I interpret right, it looks like find is O(N) (https://www.rubydoc.info/stdlib/core/Enumerable:find) so this will blow up for traces with a lot of memory allocations. Either an IntervalTree or a sorted map data type should help so this could go to O(N). This can be added in the future but I want to get it in the review so it's not forgotten (O(N) blows up fast and has bitten us before)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants