@@ -151,21 +151,27 @@ def record_event_start(
151151 Args:
152152 event_id: Event type ID (constexpr)
153153 target_rank: Target rank for the operation
154- address: Memory address(es) - can be 1D or 2D block of pointers.
154+ address: Memory address(es) - 1D or 2D block of pointers.
155155 pid_m: Program ID in M dimension
156156 pid_n: Program ID in N dimension
157- mask: Optional mask tensor indicating valid elements.
157+ mask: Optional mask tensor indicating valid elements (1D or 2D) .
158158 """
159159 if not self .enabled :
160160 return tl .cast (0 , tl .int32 )
161161
162+ # Guard against runtime-disabled tracing: when the kernel is compiled
163+ # with tracing=True but the host context has tracing disabled, the
164+ # counter pointers are null and max_events is 0. Skip all work.
165+ if self .max_events <= 0 :
166+ return tl .cast (0 , tl .int32 )
167+
162168 event_idx = tl .atomic_add (self .counter , 1 )
163169 op_index = tl .atomic_add (self .op_index_counter , 1 )
164170
165171 # Calculate payload_size from mask and datatype
166172 if mask is not None :
167173 mask_i32 = tl .cast (mask , tl .int32 )
168- num_elements = gl .sum (mask_i32 , axis = 0 )
174+ num_elements = gl .sum (mask_i32 )
169175 elem_type = address .dtype .element_ty
170176 bitwidth = elem_type .primitive_bitwidth
171177 elem_size_bytes = bitwidth // 8
@@ -184,7 +190,7 @@ def record_event_start(
184190 tl .store (self .buf_cu_id + event_idx , device_utils .get_cu_id ())
185191 tl .store (self .buf_timestamp + event_idx , device_utils .read_realtime ())
186192 addr_i64 = tl .cast (address , tl .int64 )
187- tl .store (self .buf_address + event_idx , gl .min (addr_i64 , axis = 0 ))
193+ tl .store (self .buf_address + event_idx , gl .min (addr_i64 ))
188194 tl .store (self .buf_duration_cycles + event_idx , tl .cast (0 , tl .int64 ))
189195 tl .store (self .buf_op_index + event_idx , op_index )
190196 tl .store (self .buf_payload_size + event_idx , tl .cast (payload_size , tl .int32 ))
@@ -263,9 +269,16 @@ def initialize(context_tensor, tracing: gl.constexpr = False):
263269 heap_bases = context_tensor + 2 # Offset pointer to start at heap bases
264270
265271 if tracing :
266- # Extract tracing info: starts after heap_bases, then skip trace_enabled flag
272+ # Extract tracing info: starts after heap_bases, then skip trace_enabled flag.
267273 # Layout: [cur_rank, num_ranks, heap_base_0..N-1, trace_enabled, max_events,
268274 # trace_counter_ptr, op_index_counter_ptr, buf_event_id, ...(13 buffers)]
275+ #
276+ # When tracing is disabled at the host, the context tensor is padded with
277+ # zeros in the same positions (max_events=0, null pointers). On device,
278+ # the tracing helpers (e.g., record_event_start) first early-return when
279+ # max_events <= 0 and then guard all writes with a bounds check
280+ # (event_idx < max_events), so decoding potentially null pointers here is
281+ # safe as long as those invariants are preserved.
269282 trace_info_base = 2 + num_ranks + 1 # skip cur_rank, num_ranks, heap_bases, trace_enabled
270283 max_events = tl .cast (gl .load (context_tensor + trace_info_base + 0 ), tl .int32 )
271284 trace_counter_ptr = gl .load (context_tensor + trace_info_base + 1 )
@@ -1001,7 +1014,13 @@ def _build_device_context(self):
10011014 self .tracing .op_index_counter .data_ptr (),
10021015 ] + trace_buffer_ptrs
10031016 else :
1004- context_data += [0 ] # trace_enabled = 0 (false)
1017+ # trace_enabled = 0, then pad with zeros so a kernel compiled with
1018+ # tracing=True can safely decode the same layout without reading
1019+ # out of bounds. The zeros produce max_events=0 and null pointers,
1020+ # so the bounds check (event_idx < max_events) in record_event_start
1021+ # prevents any actual writes.
1022+ # Padding: 1 (trace_enabled) + 1 (max_events) + 2 (counters) + 13 (buffers) = 17
1023+ context_data += [0 ] * 17
10051024
10061025 self ._device_context = torch .tensor (context_data , dtype = torch .int64 , device = self .device )
10071026
0 commit comments