diff --git a/libkineto/src/plugin/xpupti/XpuptiActivityHandlers.cpp b/libkineto/src/plugin/xpupti/XpuptiActivityHandlers.cpp index f1d4f9397..eb6306b79 100644 --- a/libkineto/src/plugin/xpupti/XpuptiActivityHandlers.cpp +++ b/libkineto/src/plugin/xpupti/XpuptiActivityHandlers.cpp @@ -214,8 +214,7 @@ void XpuptiActivityProfilerSession::handleRuntimeKernelMemcpyMemsetActivities( if constexpr (handleRuntimeActivities) { trace_activity->device = activity->_process_id; trace_activity->resource = activity->_thread_id; - trace_activity->flow.start = - (correlateRuntimeOps_.count(trace_activity->name()) > 0); + trace_activity->flow.start = startsFlow(activityType); } else { trace_activity->device = getDeviceIdxFromUUID(activity->_device_uuid); trace_activity->resource = activity->_sycl_queue_id; diff --git a/libkineto/src/plugin/xpupti/XpuptiActivityProfilerSession.cpp b/libkineto/src/plugin/xpupti/XpuptiActivityProfilerSession.cpp index 5446b05ed..99de7d24e 100644 --- a/libkineto/src/plugin/xpupti/XpuptiActivityProfilerSession.cpp +++ b/libkineto/src/plugin/xpupti/XpuptiActivityProfilerSession.cpp @@ -19,28 +19,17 @@ namespace KINETO_NAMESPACE { -using namespace std::literals::string_view_literals; - uint32_t XpuptiActivityProfilerSession::iterationCount_ = 0; std::vector XpuptiActivityProfilerSession::deviceUUIDs_ = {}; -std::unordered_set - XpuptiActivityProfilerSession::correlateRuntimeOps_ = { - "piextUSMEnqueueFill"sv, - "urEnqueueUSMFill"sv, - "piextUSMEnqueueFill2D"sv, - "urEnqueueUSMFill2D"sv, - "piextUSMEnqueueMemcpy"sv, - "urEnqueueUSMMemcpy"sv, - "piextUSMEnqueueMemset"sv, - "piextUSMEnqueueMemcpy2D"sv, - "urEnqueueUSMMemcpy2D"sv, - "piextUSMEnqueueMemset2D"sv, - "piEnqueueKernelLaunch"sv, - "urEnqueueKernelLaunch"sv, - "piextEnqueueKernelLaunchCustom"sv, - "urEnqueueKernelLaunchCustomExp"sv, - "piextEnqueueCooperativeKernelLaunch"sv, - "urEnqueueCooperativeKernelLaunchExp"sv}; + +bool XpuptiActivityProfilerSession::startsFlow(ActivityType activityType) { + // Only host runtime records start the CPU->GPU flow. The runtime view is + // already filtered to work-submitting APIs via + // ptiViewEnableRuntimeApiClass(PTI_API_CLASS_GPU_OPERATION_CORE). Driver + // (XPU_DRIVER) records share the same correlation id as the runtime record + // and must not also start a flow, or the trace gets a duplicate flow start. + return activityType == ActivityType::XPU_RUNTIME; +} // =========== Session Constructor ============= // XpuptiActivityProfilerSession::XpuptiActivityProfilerSession( diff --git a/libkineto/src/plugin/xpupti/XpuptiActivityProfilerSession.h b/libkineto/src/plugin/xpupti/XpuptiActivityProfilerSession.h index 2cfa33542..c6579f790 100644 --- a/libkineto/src/plugin/xpupti/XpuptiActivityProfilerSession.h +++ b/libkineto/src/plugin/xpupti/XpuptiActivityProfilerSession.h @@ -18,7 +18,6 @@ #include #include #include -#include #include namespace KINETO_NAMESPACE { @@ -61,6 +60,12 @@ class XpuptiActivityProfilerSession : public libkineto::IActivityProfilerSession void pushUserCorrelationId(uint64_t id) override; void popUserCorrelationId() override; + // Whether a runtime/driver record starts a CPU->GPU flow arrow. Only host + // runtime (XPU_RUNTIME) records do; driver (XPU_DRIVER) records share the + // same correlation id and would otherwise create a duplicate flow start. + // Static so it can be unit-tested without real hardware. + static bool startsFlow(ActivityType activityType); + private: void checkTimestampOrder(const ITraceActivity* act1); void removeCorrelatedPtiActivities(const ITraceActivity* act1); @@ -94,7 +99,6 @@ class XpuptiActivityProfilerSession : public libkineto::IActivityProfilerSession protected: static uint32_t iterationCount_; static std::vector deviceUUIDs_; - static std::unordered_set correlateRuntimeOps_; int64_t captureWindowStartTime_{0}; int64_t captureWindowEndTime_{0}; diff --git a/libkineto/test/xpupti/CMakeLists.txt b/libkineto/test/xpupti/CMakeLists.txt index faa5751b8..dd8a0fcc3 100644 --- a/libkineto/test/xpupti/CMakeLists.txt +++ b/libkineto/test/xpupti/CMakeLists.txt @@ -19,6 +19,11 @@ add_executable(XpuptiScopeProfilerConfigTest XpuptiScopeProfilerConfigTest.cpp) target_link_libraries(XpuptiScopeProfilerConfigTest PRIVATE ${LINK_LIBRARIES}) gtest_discover_tests(XpuptiScopeProfilerConfigTest) +# Hardware-free test for the CPU->GPU flow-link allowlist. +add_executable(XpuptiFlowCorrelationTest XpuptiFlowCorrelationTest.cpp) +target_link_libraries(XpuptiFlowCorrelationTest PRIVATE ${LINK_LIBRARIES}) +gtest_discover_tests(XpuptiFlowCorrelationTest) + include(ExternalProject) function(make_test test_file) diff --git a/libkineto/test/xpupti/XpuptiFlowCorrelationTest.cpp b/libkineto/test/xpupti/XpuptiFlowCorrelationTest.cpp new file mode 100644 index 000000000..08cd4e953 --- /dev/null +++ b/libkineto/test/xpupti/XpuptiFlowCorrelationTest.cpp @@ -0,0 +1,41 @@ +//============================================================== +// Copyright (C) Intel Corporation +// +// SPDX-License-Identifier: MIT +// ============================================================= + +#include "src/plugin/xpupti/XpuptiActivityProfilerSession.h" + +#include + +namespace KN = KINETO_NAMESPACE; + +// Exercises the predicate that decides which records start a CPU->GPU ("ac2g") +// flow arrow. Runs without a GPU: only the static predicate is queried, no PTI +// collection is performed. + +TEST(XpuptiFlowCorrelationTest, RuntimeRecordsStartFlow) { + // Host runtime records (kernel launches, memcpy/fill enqueues) are the source + // of the CPU->GPU arrow. The runtime view is filtered to work-submitting APIs + // by ptiViewEnableRuntimeApiClass(PTI_API_CLASS_GPU_OPERATION_CORE). + EXPECT_TRUE(KN::XpuptiActivityProfilerSession::startsFlow( + KN::ActivityType::XPU_RUNTIME)); +} + +TEST(XpuptiFlowCorrelationTest, DriverRecordsDoNotStartFlow) { + // Driver (ze*) records share the runtime record's correlation id; if they + // also started a flow the trace would have a duplicate flow start for that + // id (Perfetto flow_duplicate_id). They must only be flow ends. + EXPECT_FALSE(KN::XpuptiActivityProfilerSession::startsFlow( + KN::ActivityType::XPU_DRIVER)); +} + +TEST(XpuptiFlowCorrelationTest, DeviceRecordsDoNotStartFlow) { + // GPU-side activities are flow destinations, never sources. + EXPECT_FALSE(KN::XpuptiActivityProfilerSession::startsFlow( + KN::ActivityType::CONCURRENT_KERNEL)); + EXPECT_FALSE(KN::XpuptiActivityProfilerSession::startsFlow( + KN::ActivityType::GPU_MEMCPY)); + EXPECT_FALSE(KN::XpuptiActivityProfilerSession::startsFlow( + KN::ActivityType::GPU_MEMSET)); +}