blob: 3e2c6e84e26c2f3abbfaa42c53509567c320d11d [file] [edit]
// M1 regression: calling __llvm_profile_hip_collect_device_data() before any
// kernel has been launched must not poison the later atexit drain. The early
// call sees "no instrumented code object loaded yet" (a transient no-op) and
// must not latch the drain as completed; otherwise the post-launch atexit
// pass produces no device .profraw and we silently lose device counters.
//
// REQUIRES: hip, amdgpu
// Guards the Linux introspection drain's DrainCompleted latch; the Windows
// host-shadow drain has no such latch (it tracks per-TU Processed flags).
// UNSUPPORTED: windows
// RUN: rm -rf %t.dir && mkdir -p %t.dir
// RUN: %clang -x hip --offload-arch=%amdgpu_arch -fno-gpu-rdc \
// RUN: -fprofile-instr-generate -fcoverage-mapping %s -o %t.dir/a.out \
// RUN: -L%hip_lib_path -lamdhip64
// RUN: env LLVM_PROFILE_FILE=%t.dir/host.%%p.profraw \
// RUN: LD_LIBRARY_PATH=%hip_lib_path:$LD_LIBRARY_PATH \
// RUN: %t.dir/a.out
// Both the host profraw and at least one device profraw (gfx-prefixed) must
// have been produced, despite the early collection attempt.
// RUN: ls %t.dir/host.*.profraw
// RUN: ls %t.dir/gfx*.profraw
// And the merged profile must contain the device kernel that was launched
// *after* the early collect.
// RUN: llvm-profdata merge %t.dir/*.profraw -o %t.dir/a.profdata
// RUN: llvm-profdata show --all-functions %t.dir/a.profdata \
// RUN: | FileCheck %s
#include <hip/hip_runtime.h>
// Declared by libclang_rt.profile-<host arch>.a; we call it directly to
// simulate any caller that drains device counters at an arbitrary point in
// the program lifetime (e.g. a per-iteration profile dump).
extern "C" int __llvm_profile_hip_collect_device_data(void);
__global__ void post_collect_kernel(int *p) {
if (*p > 0)
*p += 1;
else
*p -= 1;
}
int main() {
// (1) Early collection -- runs before any kernel launch. The drainer
// finds either no GPU agents, no loaded segments, or no instrumented
// bounds table, and returns 0 without latching DrainCompleted.
(void)__llvm_profile_hip_collect_device_data();
// (2) Now launch a kernel. HIP loads the device code object that carries
// the __llvm_profile_sections bounds table, executes our kernel, and
// populates the device-side counters.
int *d = nullptr;
if (hipMalloc(&d, sizeof(int)) != hipSuccess)
return 2;
int h = 5;
(void)hipMemcpy(d, &h, sizeof(int), hipMemcpyHostToDevice);
post_collect_kernel<<<1, 1>>>(d);
(void)hipMemcpy(&h, d, sizeof(int), hipMemcpyDeviceToHost);
(void)hipFree(d);
// (3) Exit normally. The atexit drain runs and -- because step (1) did
// not latch DrainCompleted -- it walks the (now loaded) code object,
// finds __llvm_profile_sections, and emits the device .profraw.
return h > 0 ? 0 : 1;
}
// CHECK-DAG: post_collect_kernel
// CHECK-DAG: main