已开启
feat(profiler): add CPU-to-NPU correlation for ACLprof #46050
feat(profiler): add CPU-to-NPU correlation for ACLprof #46050
已开启
denis_g创建于 9月8日
共 17 个文件变更+2605-77
@@ -416,7 +416,7 @@ if (DEFINED BUILD_FXRT)
416 add_subdirectory(${PROJECT_SOURCE_DIR}/fxrt/fxrt)416 add_subdirectory(${PROJECT_SOURCE_DIR}/fxrt/fxrt)
417endif()417endif()
418 418 
419-if (DEFINED BUILD_GTEST)419+if (BUILD_GTEST)
420 enable_testing()420 enable_testing()
421 SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/gtest)421 SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/gtest)
422 add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/googletest)422 add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/googletest)
@@ -426,8 +426,20 @@ if (DEFINED BUILD_GTEST)
426 add_subdirectory(${PROJECT_SOURCE_DIR}/test/cpp/api)426 add_subdirectory(${PROJECT_SOURCE_DIR}/test/cpp/api)
427 add_executable(test_api ${TORCH_API_TEST_SOURCES})427 add_executable(test_api ${TORCH_API_TEST_SOURCES})
428 428 
429+ find_path(KINETO_TEST_INCLUDE_DIR ActivityBuffers.h
430+ HINTS "${PYTORCH_INSTALL_DIR}/include/kineto"
431+ NO_DEFAULT_PATH
432+ DOC "Kineto internal headers matching installed PyTorch (libkineto/src)")
433+ if(NOT EXISTS "${KINETO_TEST_INCLUDE_DIR}/ActivityBuffers.h")
434+ message(FATAL_ERROR
435+ "test_api requires ActivityBuffers.h. Set KINETO_TEST_INCLUDE_DIR to "
436+ "libkineto/src from the Kineto version used to build installed PyTorch.")
437+ endif()
438+ target_include_directories(test_api PRIVATE "${KINETO_TEST_INCLUDE_DIR}")
439+ target_compile_definitions(test_api PRIVATE KINETO_NAMESPACE=libkineto)
440+ 
429 target_link_libraries(test_api PUBLIC torch_npu)441 target_link_libraries(test_api PUBLIC torch_npu)
430- target_link_libraries(test_api PUBLIC gtest_main gtest)442+ target_link_libraries(test_api PUBLIC gtest_main gtest gmock)
431endif()443endif()
432 444 
433if (DEFINED BUILD_LIBTORCH)445if (DEFINED BUILD_LIBTORCH)
@@ -1,2 +1,5 @@
1set(TORCH_API_TEST_DIR "${PROJECT_SOURCE_DIR}/test/cpp/api")1set(TORCH_API_TEST_DIR "${PROJECT_SOURCE_DIR}/test/cpp/api")
2-set(TORCH_API_TEST_SOURCES ${TORCH_API_TEST_DIR}/modules.cpp PARENT_SCOPE)2+set(TORCH_API_TEST_SOURCES
3+ ${TORCH_API_TEST_DIR}/modules.cpp
4+ ${TORCH_API_TEST_DIR}/profiler.cpp
5+ PARENT_SCOPE)
@@ -1,16 +1,107 @@
1+import json
2+import pickle
1import unittest3import unittest
2-from unittest.mock import patch4+from unittest.mock import PropertyMock, patch
3 5 
6+from torch._C._profiler import _ExperimentalConfig as KinetoExperimentalConfig
7+from torch_npu.profiler import _NPUExperimentalConfig
4from torch_npu.profiler.experimental_config import supported_ai_core_metrics8from torch_npu.profiler.experimental_config import supported_ai_core_metrics
5from torch_npu.profiler.experimental_config import supported_profiler_level9from torch_npu.profiler.experimental_config import supported_profiler_level
6from torch_npu.profiler.experimental_config import supported_export_type10from torch_npu.profiler.experimental_config import supported_export_type
7from torch_npu.profiler.experimental_config import _ExperimentalConfig11from torch_npu.profiler.experimental_config import _ExperimentalConfig
12+from torch_npu.profiler.experimental_config import ExportType, ProfilerLevel
8from torch_npu._C._profiler import _ExperimentalConfig as Cpp_ExperimentalConfig13from torch_npu._C._profiler import _ExperimentalConfig as Cpp_ExperimentalConfig
9from torch_npu.testing.testcase import TestCase, run_tests14from torch_npu.testing.testcase import TestCase, run_tests
10from torch_npu.profiler.analysis.prof_common_func._constant import Constant15from torch_npu.profiler.analysis.prof_common_func._constant import Constant
11from torch_npu.profiler.analysis.prof_common_func._cann_package_manager import CannPackageManager16from torch_npu.profiler.analysis.prof_common_func._cann_package_manager import CannPackageManager
12 17 
13 18 
19+class TestNPUExperimentalConfig(unittest.TestCase):
20+ def test_backend_only_compatibility(self):
21+ self.assertEqual(str(_NPUExperimentalConfig()), '{"backend":"mspti"}')
22+ for backend in ["mspti", "aclprof", "invalid", None, 1]:
23+ with self.subTest(backend=backend):
24+ config = _NPUExperimentalConfig(backend=backend)
25+ self.assertEqual(str(config), json.dumps({"backend": backend}, separators=(",", ":")))
26+ self.assertEqual(config.__getstate__(), KinetoExperimentalConfig(
27+ custom_profiler_config=str(config)).__getstate__())
28+ 
29+ def test_configuration_combinations(self):
30+ for backend in [None, "mspti", "aclprof"]:
31+ for options in [{"profiler_level": ProfilerLevel.Level1}, {"export_type": ExportType.Db},
32+ {"profiler_level": ProfilerLevel.Level1, "export_type": [ExportType.Db]}]:
33+ with self.subTest(backend=backend, options=options):
34+ kwargs = {} if backend is None else {"backend": backend}
35+ data = json.loads(str(_NPUExperimentalConfig(**kwargs, **options)))
36+ expected = {"backend": backend or "mspti", **options}
37+ if "export_type" in options:
38+ expected["export_type"] = [ExportType.Db]
39+ self.assertEqual(data, expected)
40+ 
41+ def test_supported_levels(self):
42+ for level in supported_profiler_level():
43+ with self.subTest(level=level):
44+ self.assertEqual(json.loads(str(_NPUExperimentalConfig(profiler_level=level)))["profiler_level"], level)
45+ 
46+ def test_export_normalization_and_determinism(self):
47+ scalar = _NPUExperimentalConfig(export_type=ExportType.Db)
48+ self.assertEqual(str(scalar), str(_NPUExperimentalConfig(export_type=[ExportType.Db])))
49+ exports = [ExportType.Text, ExportType.Db, ExportType.Text]
50+ config = _NPUExperimentalConfig(export_type=exports)
51+ self.assertEqual(str(config), '{"backend":"mspti","export_type":["db","text"]}')
52+ exports.clear()
53+ self.assertEqual(str(config), str(_NPUExperimentalConfig(export_type=[ExportType.Db, ExportType.Text])))
54+ self.assertEqual(str(_NPUExperimentalConfig(profiler_level=None, export_type=None)),
55+ str(_NPUExperimentalConfig()))
56+ 
57+ def test_invalid_profiler_level(self):
58+ for level in ["invalid", "Level3", "", 1, True, 1.0, [], {}]:
59+ with self.subTest(level=level), self.assertRaisesRegex(ValueError, "profiler_level"):
60+ _NPUExperimentalConfig(profiler_level=level)
61+ 
62+ def test_invalid_export_type(self):
63+ for exports in ["invalid", "db,text", "", 1, True, [], {}, (ExportType.Db,), [ExportType.Db, 1], [[]]]:
64+ with self.subTest(exports=exports), self.assertRaisesRegex(ValueError, "export_type"):
65+ _NPUExperimentalConfig(export_type=exports)
66+ 
67+ def test_standard_channel_and_pickle_roundtrip(self):
68+ config = _NPUExperimentalConfig(backend="aclprof", profiler_level=ProfilerLevel.Level1,
69+ export_type=ExportType.Db, verbose=True)
70+ payload = str(config)
71+ self.assertEqual(payload, '{"backend":"aclprof","profiler_level":"Level1","export_type":["db"]}')
72+ standard = KinetoExperimentalConfig(custom_profiler_config=payload, verbose=True)
73+ self.assertEqual(config.__getstate__(), standard.__getstate__())
74+ for value in [config, standard]:
75+ restored = pickle.loads(pickle.dumps(value))
76+ self.assertEqual(restored.__getstate__(), value.__getstate__())
77+ self.assertEqual(str(pickle.loads(pickle.dumps(config))), payload)
78+ # Newer PyTorch versions append unrelated fields after the custom config.
79+ with patch.object(_NPUExperimentalConfig, "__getstate__", return_value=(*config.__getstate__(), False, False)):
80+ self.assertEqual(str(config), payload)
81+ 
82+ def test_str_uses_direct_attribute_without_serializing(self):
83+ config = _NPUExperimentalConfig()
84+ payload = '{"backend":"aclprof"}'
85+ with patch.object(_NPUExperimentalConfig, "custom_profiler_config", payload, create=True), \
86+ patch.object(_NPUExperimentalConfig, "__getstate__", side_effect=AssertionError("unexpected fallback")) as state:
87+ self.assertEqual(str(config), payload)
88+ state.assert_not_called()
89+ 
90+ def test_str_falls_back_when_attribute_is_unavailable(self):
91+ config = _NPUExperimentalConfig(backend="aclprof")
92+ payload = '{"backend":"aclprof"}'
93+ with patch.object(_NPUExperimentalConfig, "custom_profiler_config", create=True,
94+ new_callable=PropertyMock, side_effect=AttributeError):
95+ with patch.object(_NPUExperimentalConfig, "__getstate__", return_value=config.__getstate__()) as state:
96+ self.assertEqual(str(config), payload)
97+ state.assert_called_once_with()
98+ self.assertEqual(str(pickle.loads(pickle.dumps(config))), payload)
99+ 
100+ def test_json_escaping(self):
101+ backend = 'aclprof",\n"profiler_level":"Level2'
102+ self.assertEqual(json.loads(str(_NPUExperimentalConfig(backend=backend))), {"backend": backend})
103+ 
104+ 
14class TestExperimentalConfig(TestCase):105class TestExperimentalConfig(TestCase):
15 106 
16 @classmethod107 @classmethod
@@ -47,6 +138,12 @@ class TestExperimentalConfig(TestCase):
47 experimental_config = _ExperimentalConfig()138 experimental_config = _ExperimentalConfig()
48 self.assertTrue(isinstance(experimental_config(), Cpp_ExperimentalConfig))139 self.assertTrue(isinstance(experimental_config(), Cpp_ExperimentalConfig))
49 140 
141+ def test_kineto_backend_config(self):
142+ standard_config = pickle.dumps(KinetoExperimentalConfig())
143+ aclprof_config = pickle.dumps(_NPUExperimentalConfig(backend="aclprof"))
144+ self.assertNotIn(b'{"backend":"aclprof"}', standard_config)
145+ self.assertIn(b'{"backend":"aclprof"}', aclprof_config)
146+ 
50 def test_mstx_domain_switches_will_reset_when_msproftx_and_mstx_not_enabled(self):147 def test_mstx_domain_switches_will_reset_when_msproftx_and_mstx_not_enabled(self):
51 experimental_config = _ExperimentalConfig(msprof_tx=False,148 experimental_config = _ExperimentalConfig(msprof_tx=False,
52 mstx=False,149 mstx=False,
@@ -136,6 +136,7 @@ TORCH_NPU_LOAD_FUNC(aclrtMemset)
136TORCH_NPU_LOAD_FUNC(aclrtMemsetAsync)136TORCH_NPU_LOAD_FUNC(aclrtMemsetAsync)
137TORCH_NPU_LOAD_FUNC(aclmdlRICaptureThreadExchangeMode)137TORCH_NPU_LOAD_FUNC(aclmdlRICaptureThreadExchangeMode)
138TORCH_NPU_LOAD_FUNC(aclrtGetLogicDevIdByUserDevId)138TORCH_NPU_LOAD_FUNC(aclrtGetLogicDevIdByUserDevId)
139+TORCH_NPU_LOAD_FUNC(aclrtGetPhyDevIdByUserDevId)
139TORCH_NPU_LOAD_FUNC(aclrtBinaryLoadFromData)140TORCH_NPU_LOAD_FUNC(aclrtBinaryLoadFromData)
140TORCH_NPU_LOAD_FUNC(aclrtBinaryGetFunction)141TORCH_NPU_LOAD_FUNC(aclrtBinaryGetFunction)
141TORCH_NPU_LOAD_FUNC(aclrtBinaryUnLoad)142TORCH_NPU_LOAD_FUNC(aclrtBinaryUnLoad)
@@ -1943,6 +1944,16 @@ aclError AclrtGetLogicDevIdByUserDevId(const int32_t userDevid, int32_t* const l
1943 TORCH_CHECK(func, "Failed to find function aclrtGetLogicDevIdByUserDevId", PTA_ERROR(ErrCode::NOT_FOUND));1944 TORCH_CHECK(func, "Failed to find function aclrtGetLogicDevIdByUserDevId", PTA_ERROR(ErrCode::NOT_FOUND));
1944 return func(userDevid, logicDevId);1945 return func(userDevid, logicDevId);
1945}1946}
1947+ 
1948+aclError AclrtGetPhyDevIdByUserDevId(const int32_t userDevId, int32_t* const phyDevId) {
1949+ typedef aclError (*AclrtGetPhyDevIdByUserDevId)(const int32_t, int32_t* const);
1950+ static AclrtGetPhyDevIdByUserDevId func = nullptr;
1951+ if (func == nullptr) {
1952+ func = (AclrtGetPhyDevIdByUserDevId)TORCH_NPU_GET_FUNC(aclrtGetPhyDevIdByUserDevId);
1953+ }
1954+ TORCH_CHECK(func, "Failed to find function aclrtGetPhyDevIdByUserDevId", PTA_ERROR(ErrCode::NOT_FOUND));
1955+ return func(userDevId, phyDevId);
1956+}
1946bool IsExistAclrtLaunchKernelWithHostArgs() {1957bool IsExistAclrtLaunchKernelWithHostArgs() {
1947 using Func = decltype(&aclrtLaunchKernelWithHostArgs);1958 using Func = decltype(&aclrtLaunchKernelWithHostArgs);
1948 static Func func = (Func)TORCH_NPU_GET_FUNC(aclrtLaunchKernelWithHostArgs);1959 static Func func = (Func)TORCH_NPU_GET_FUNC(aclrtLaunchKernelWithHostArgs);
@@ -442,6 +442,8 @@ aclError AclrtGetDeviceInfo(uint32_t deviceId, aclrtDevAttr attr, int64_t* value
442 442 
443aclError AclrtGetLogicDevIdByUserDevId(const int32_t userDevid, int32_t* const logicDevId);443aclError AclrtGetLogicDevIdByUserDevId(const int32_t userDevid, int32_t* const logicDevId);
444 444 
445+aclError AclrtGetPhyDevIdByUserDevId(const int32_t userDevId, int32_t* const phyDevId);
446+ 
445aclError AclrtMemSet(void* devPtr, size_t maxCount, int32_t value, size_t count);447aclError AclrtMemSet(void* devPtr, size_t maxCount, int32_t value, size_t count);
446 448 
447aclError AclrtMemSetAsync(void* devPtr, size_t maxCount, int32_t value, size_t count, aclrtStream stream);449aclError AclrtMemSetAsync(void* devPtr, size_t maxCount, int32_t value, size_t count, aclrtStream stream);
@@ -17,6 +17,7 @@
17#include "torch_npu/csrc/framework/utils/OpAdapter.h"17#include "torch_npu/csrc/framework/utils/OpAdapter.h"
18#include "torch_npu/csrc/framework/utils/OpPreparation.h"18#include "torch_npu/csrc/framework/utils/OpPreparation.h"
19#ifndef BUILD_LIBTORCH19#ifndef BUILD_LIBTORCH
20+#include "torch_npu/csrc/profiler/aclprof_activity_profiler.h"
20#include "torch_npu/csrc/profiler/npu_profiler.h"21#include "torch_npu/csrc/profiler/npu_profiler.h"
21#endif22#endif
22 23 
@@ -275,7 +276,8 @@ void NpuUtils::ProfReportMarkDataToNpuProfiler(uint32_t category, const std::str
275 if (data.empty()) {276 if (data.empty()) {
276 return;277 return;
277 }278 }
278- if (torch_npu::profiler::profDataReportEnable().load(std::memory_order_relaxed)) {279+ if (torch_npu::profiler::profDataReportEnable().load(std::memory_order_relaxed) ||
280+ torch_npu::profiler::aclprofTaskQueueCaptureEnabled()) {
279 torch_npu::profiler::reportMarkDataToNpuProfiler(category, data, correlation_id);281 torch_npu::profiler::reportMarkDataToNpuProfiler(category, data, correlation_id);
280 }282 }
281}283}
@@ -304,7 +306,8 @@ void NpuUtils::ProfReportMarkDataToNpuProfiler(uint32_t category, void* data, si
304 if (C10_UNLIKELY(!data)) {306 if (C10_UNLIKELY(!data)) {
305 return;307 return;
306 }308 }
307- if (torch_npu::profiler::profDataReportEnable().load(std::memory_order_relaxed)) {309+ if (torch_npu::profiler::profDataReportEnable().load(std::memory_order_relaxed) ||
310+ torch_npu::profiler::aclprofTaskQueueCaptureEnabled()) {
308 static const std::map<int64_t, DqueueCall> DEQUEUE_CALL_FUNC_MAP{311 static const std::map<int64_t, DqueueCall> DEQUEUE_CALL_FUNC_MAP{
309 {c10_npu::queue::COMPILE_AND_EXECUTE, &DqueueCompileExcute},312 {c10_npu::queue::COMPILE_AND_EXECUTE, &DqueueCompileExcute},
310 {c10_npu::queue::EXECUTE_OPAPI, &DqueueCompileExcuteOpApi},313 {c10_npu::queue::EXECUTE_OPAPI, &DqueueCompileExcuteOpApi},
@@ -0,0 +1,124 @@
1+// Copyright (c) 2026 Huawei Technologies Co., Ltd
2+// All rights reserved.
3+//
4+// Licensed under the BSD 3-Clause License (the "License");
5+// you may not use this file except in compliance with the License.
6+// You may obtain a copy of the License at
7+//
8+// https://opensource.org/licenses/BSD-3-Clause
9+//
10+// Unless required by applicable law or agreed to in writing, software
11+// distributed under the License is distributed on an "AS IS" BASIS,
12+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+// See the License for the specific language governing permissions and
14+// limitations under the License.
15+ 
16+#pragma once
17+ 
18+#include <cstdint>
19+#include <memory>
20+#include <mutex>
21+#include <optional>
22+#include <set>
23+#include <string>
24+#include <unordered_map>
25+#include <vector>
26+ 
27+#include "torch_npu/csrc/profiler/npu_activity_profiler.h"
28+ 
29+namespace torch_npu {
30+namespace profiler {
31+ 
32+namespace detail {
33+struct AclprofApi;
34+struct AclprofEvent;
35+} // namespace detail
36+ 
37+class AclprofActivityProfilerTestPeer;
38+ 
39+TORCH_NPU_API bool aclprofTaskQueueCaptureEnabled();
40+TORCH_NPU_API void recordAclprofTaskQueueEvent(
41+ uint32_t category,
42+ uint64_t taskQueueCorrelation,
43+ int64_t monotonicTimestampNs,
44+ int64_t pid,
45+ int64_t tid,
46+ const std::string& name);
47+ 
48+class TORCH_NPU_API AclprofActivityProfiler : public NPUActivityProfiler {
49+ public:
50+ explicit AclprofActivityProfiler(std::set<libkineto::ActivityType> activities);
51+ ~AclprofActivityProfiler() override;
52+ 
53+ const char* name() const override {
54+ return "aclprof";
55+ }
56+ void start() override;
57+ void stop() override;
58+ std::vector<std::string> errors() override;
59+ void processTrace(libkineto::ActivityLogger& logger) override;
60+ void processTrace(
61+ libkineto::ActivityLogger& logger,
62+ libkineto::getLinkedActivityCallback getLinkedActivity,
63+ int64_t startTime,
64+ int64_t endTime) override;
65+ std::unique_ptr<libkineto::DeviceInfo> getDeviceInfo() override;
66+ std::vector<libkineto::ResourceInfo> getResourceInfos() override;
67+ std::unique_ptr<libkineto::CpuTraceBuffer> getTraceBuffer() override;
68+ void pushCorrelationId(uint64_t id) override;
69+ void popCorrelationId() override;
70+ 
71+ private:
72+ struct TaskQueueMark {
73+ uint32_t category;
74+ uint64_t taskQueueCorrelation;
75+ int64_t timestampNs;
76+ int64_t tid;
77+ std::string name;
78+ std::optional<int32_t> kinetoCorrelation;
79+ };
80+ 
81+ AclprofActivityProfiler(std::set<libkineto::ActivityType> activities, std::shared_ptr<detail::AclprofApi> api);
82+ bool finishCollection(bool reportErrors);
83+ void cleanup();
84+ void resetCorrelationState();
85+ void beginCorrelationCapture();
86+ void endCorrelationCapture();
87+ void recordTaskQueueEvent(
88+ uint32_t category,
89+ uint64_t taskQueueCorrelation,
90+ int64_t monotonicTimestampNs,
91+ int64_t pid,
92+ int64_t tid,
93+ const std::string& name);
94+ void loadTaskQueueClockOffset(const std::string& profile);
95+ void resolveCorrelations();
96+ 
97+ friend class AclprofActivityProfilerTestPeer;
98+ friend void recordAclprofTaskQueueEvent(
99+ uint32_t category,
100+ uint64_t taskQueueCorrelation,
101+ int64_t monotonicTimestampNs,
102+ int64_t pid,
103+ int64_t tid,
104+ const std::string& name);
105+ std::set<libkineto::ActivityType> activities_;
106+ std::shared_ptr<detail::AclprofApi> api_;
107+ std::vector<detail::AclprofEvent> events_;
108+ std::vector<std::string> errors_;
109+ std::string root_;
110+ void* config_ = nullptr;
111+ int64_t device_ = -1;
112+ std::mutex correlationMutex_;
113+ std::unordered_map<int64_t, std::vector<std::optional<int32_t>>> activeCorrelations_;
114+ std::vector<TaskQueueMark> taskQueueMarks_;
115+ std::optional<int64_t> taskQueueClockOffsetNs_;
116+ std::vector<std::optional<int32_t>> linkedCorrelations_;
117+ bool initialized_ = false;
118+ bool started_ = false;
119+ bool captureCorrelations_ = false;
120+ bool correlationsResolved_ = false;
121+};
122+ 
123+} // namespace profiler
124+} // namespace torch_npu
@@ -0,0 +1,42 @@
1+#pragma once
2+ 
3+#include <cstdint>
4+#include <functional>
5+#include <string>
6+#include <vector>
7+ 
8+namespace torch_npu::profiler::detail {
9+ 
10+struct AclprofEvent {
11+ std::string name;
12+ std::string taskType;
13+ int64_t startNs = 0;
14+ int64_t endNs = 0;
15+ int64_t device = 0;
16+ int64_t stream = 0;
17+ int64_t correlation = 0;
18+ uint64_t hostLaunchId = 0;
19+ int64_t flowStartNs = 0;
20+ int64_t flowStartPid = -1;
21+ int64_t flowStartTid = -1;
22+};
23+ 
24+// physicalDevice is the runtime-resolved physical device for this single-device session.
25+std::vector<AclprofEvent> parseAclprofTimeline(
26+ const std::string& path,
27+ int64_t physicalDevice,
28+ std::vector<std::string>& errors);
29+ 
30+struct AclprofApi {
31+ std::function<int32_t(int32_t*)> getDevice;
32+ std::function<int32_t(int32_t, int32_t*)> getPhysicalDevice;
33+ std::function<int32_t(const std::string&)> init;
34+ std::function<void*(uint32_t*)> createConfig;
35+ std::function<int32_t()> start;
36+ std::function<int32_t()> registerDeviceCallback;
37+ std::function<int32_t()> stop;
38+ std::function<int32_t()> destroyConfig;
39+ std::function<int32_t()> finalize;
40+};
41+ 
42+} // namespace torch_npu::profiler::detail
@@ -19,7 +19,6 @@
19#include "torch_npu/csrc/framework/interface/MsptiInterface.h"19#include "torch_npu/csrc/framework/interface/MsptiInterface.h"
20#include "torch_npu/csrc/core/npu/npu_log.h"20#include "torch_npu/csrc/core/npu/npu_log.h"
21#include "torch_npu/csrc/toolkit/profiler/common/utils.h"21#include "torch_npu/csrc/toolkit/profiler/common/utils.h"
22-#include <torch/csrc/profiler/standalone/privateuse1_profiler.h>
23 22 
24namespace torch_npu {23namespace torch_npu {
25namespace profiler {24namespace profiler {
@@ -47,12 +46,6 @@ bool g_sessionActive = false;
47 46 
48int64_t g_corrCounter = 1LL << 30; // 1,073,741,824 (< 2^32 - 1)47int64_t g_corrCounter = 1LL << 30; // 1,073,741,824 (< 2^32 - 1)
49 48 
50-bool privateUse1Requested(const std::set<libkineto::ActivityType>& types) {
51- using AT = libkineto::ActivityType;
52- return types.count(AT::CONCURRENT_KERNEL) != 0 || types.count(AT::GPU_MEMCPY) != 0 ||
53- types.count(AT::GPU_MEMSET) != 0 || types.count(AT::GPU_USER_ANNOTATION) != 0;
54-}
55- 
56std::vector<msptiActivityKind> computeEnabledKinds(const std::set<libkineto::ActivityType>& acts) {49std::vector<msptiActivityKind> computeEnabledKinds(const std::set<libkineto::ActivityType>& acts) {
57 using AT = libkineto::ActivityType;50 using AT = libkineto::ActivityType;
58 std::vector<msptiActivityKind> ks;51 std::vector<msptiActivityKind> ks;
@@ -426,38 +419,5 @@ std::unique_ptr<libkineto::CpuTraceBuffer> MsptiActivityProfiler::getTraceBuffer
426 return nullptr;419 return nullptr;
427}420}
428 421 
429-std::unique_ptr<libkineto::IActivityProfilerSession> MsptiActivityProfilerPoc::configure(
430- const std::set<libkineto::ActivityType>& types,
431- const libkineto::Config&) {
432- if (!privateUse1Requested(types)) {
433- return nullptr;
434- }
435- return std::make_unique<MsptiActivityProfiler>(types);
436-}
437- 
438-std::unique_ptr<libkineto::IActivityProfilerSession> MsptiActivityProfilerPoc::configure(
439- int64_t,
440- int64_t,
441- const std::set<libkineto::ActivityType>& types,
442- const libkineto::Config& cfg) {
443- return configure(types, cfg);
444-}
445- 
446-// ------------------------------ Registration -------------------------------
447-// Prefer the upstream macro (stores the factory in PrivateUse1ProfilerRegistry);
448-#ifdef REGISTER_PRIVATEUSE1_PROFILER
449-REGISTER_PRIVATEUSE1_PROFILER(MsptiActivityProfilerPoc);
450-#else
451-namespace {
452-struct MsptiAutoRegister {
453- MsptiAutoRegister() {
454- libkineto::api().registerProfilerFactory(
455- []() -> std::unique_ptr<libkineto::IActivityProfiler> { return std::make_unique<MsptiActivityProfilerPoc>(); });
456- }
457-};
458-static MsptiAutoRegister g_mspti_auto_register;
459-} // namespace
460-#endif
461- 
462} // namespace profiler422} // namespace profiler
463} // namespace torch_npu423} // namespace torch_npu
@@ -1,17 +1,12 @@
1#pragma once1#pragma once
2-// mspti NPU activity-profiler session + libkineto bridge.2+// MSPTI-specific NPU activity-profiler backend.
3-//
4-// MsptiActivityProfilerPoc (libkineto::IActivityProfiler) registers for
5-// ProfilerActivity.PrivateUse1; configure() returns a MsptiActivityProfiler,
6-// which is the session libkineto drives. The session enables mspti kinds from
7-// the requested activity-type set and collects NPU kernel and host API records.
8 3 
9#include <memory>4#include <memory>
10#include <set>5#include <set>
11#include <string>6#include <string>
12#include <vector>7#include <vector>
13 8 
14-#include "IActivityProfiler.h"9+#include "torch_npu/csrc/profiler/npu_activity_profiler.h"
15 10 
16namespace torch_npu {11namespace torch_npu {
17namespace profiler {12namespace profiler {
@@ -31,10 +26,13 @@ struct MsptiEvent {
31 int64_t streamId = 0;26 int64_t streamId = 0;
32};27};
33 28 
34-class MsptiActivityProfiler : public libkineto::IActivityProfilerSession {29+class MsptiActivityProfiler : public NPUActivityProfiler {
35 public:30 public:
36 explicit MsptiActivityProfiler(std::set<libkineto::ActivityType> activities) : activities_(std::move(activities)) {}31 explicit MsptiActivityProfiler(std::set<libkineto::ActivityType> activities) : activities_(std::move(activities)) {}
37 32 
33+ const char* name() const override {
34+ return "mspti";
35+ }
38 void start() override;36 void start() override;
39 void stop() override;37 void stop() override;
40 std::vector<std::string> errors() override;38 std::vector<std::string> errors() override;
@@ -56,27 +54,5 @@ class MsptiActivityProfiler : public libkineto::IActivityProfilerSession {
56 std::vector<std::string> errors_;54 std::vector<std::string> errors_;
57};55};
58 56 
59-class MsptiActivityProfilerPoc : public libkineto::IActivityProfiler {
60- public:
61- const std::string& name() const override {
62- return name_;
63- }
64- const std::set<libkineto::ActivityType>& availableActivities() const override {
65- return avail_;
66- }
67- std::unique_ptr<libkineto::IActivityProfilerSession> configure(
68- const std::set<libkineto::ActivityType>& activity_types,
69- const libkineto::Config& config) override;
70- std::unique_ptr<libkineto::IActivityProfilerSession> configure(
71- int64_t ts_ms,
72- int64_t duration_ms,
73- const std::set<libkineto::ActivityType>& activity_types,
74- const libkineto::Config& config) override;
75- 
76- private:
77- std::string name_{"mspti"};
78- std::set<libkineto::ActivityType> avail_{libkineto::ActivityType::CONCURRENT_KERNEL};
79-};
80- 
81} // namespace profiler57} // namespace profiler
82} // namespace torch_npu58} // namespace torch_npu
@@ -0,0 +1,169 @@
1+// Copyright (c) 2026 Huawei Technologies Co., Ltd
2+// All rights reserved.
3+//
4+// Licensed under the BSD 3-Clause License (the "License");
5+// you may not use this file except in compliance with the License.
6+// You may obtain a copy of the License at
7+//
8+// https://opensource.org/licenses/BSD-3-Clause
9+//
10+// Unless required by applicable law or agreed to in writing, software
11+// distributed under the License is distributed on an "AS IS" BASIS,
12+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+// See the License for the specific language governing permissions and
14+// limitations under the License.
15+ 
16+#include "torch_npu/csrc/profiler/npu_activity_profiler.h"
17+ 
18+#include <set>
19+#include <utility>
20+ 
21+#include <nlohmann/json.hpp>
22+#include <c10/util/Exception.h>
23+ 
24+#include "libkineto.h"
25+#include "torch_npu/csrc/profiler/aclprof_activity_profiler.h"
26+#include "torch_npu/csrc/profiler/mspti_activity_profiler.h"
27+#include <torch/csrc/profiler/standalone/privateuse1_profiler.h>
28+ 
29+namespace torch_npu {
30+namespace profiler {
31+namespace {
32+ 
33+bool privateUse1Requested(const std::set<libkineto::ActivityType>& types) {
34+ using AT = libkineto::ActivityType;
35+ return types.count(AT::CONCURRENT_KERNEL) != 0 || types.count(AT::GPU_MEMCPY) != 0 ||
36+ types.count(AT::GPU_MEMSET) != 0 || types.count(AT::GPU_USER_ANNOTATION) != 0;
37+}
38+ 
39+NPUProfilerConfig parseExperimentalConfig(const std::string& customConfig) {
40+ NPUProfilerConfig result;
41+ const auto config = nlohmann::json::parse(customConfig, nullptr, false);
42+ if (!config.is_object()) {
43+ return result;
44+ }
45+ const auto backend = config.find("backend");
46+ if (backend != config.end() && backend->is_string() && backend->get<std::string>() == "aclprof") {
47+ result.backend = "aclprof";
48+ }
49+ const auto level = config.find("profiler_level");
50+ if (level != config.end()) {
51+ TORCH_CHECK(
52+ level->is_string() &&
53+ (*level == "Level0" || *level == "Level1" || *level == "Level2" || *level == "Level_none"),
54+ "Invalid NPU profiler_level");
55+ result.profilerLevel = level->get<std::string>();
56+ }
57+ const auto exports = config.find("export_type");
58+ if (exports != config.end()) {
59+ TORCH_CHECK(exports->is_array() && !exports->empty(), "Invalid NPU export_type: expected a nonempty array");
60+ std::set<std::string> values;
61+ for (const auto& value : *exports) {
62+ TORCH_CHECK(value.is_string() && (value == "db" || value == "text"), "Invalid NPU export_type");
63+ values.insert(value.get<std::string>());
64+ }
65+ result.exportTypes.assign(values.begin(), values.end());
66+ }
67+ return result;
68+}
69+ 
70+} // namespace
71+ 
72+std::unique_ptr<NPUActivityProfiler> NPUActivityProfilerFactory::create(
73+ const std::set<libkineto::ActivityType>& activities,
74+ const libkineto::Config& config) {
75+ if (!privateUse1Requested(activities)) {
76+ return nullptr;
77+ }
78+ auto options = parseExperimentalConfig(config.getCustomConfig());
79+ std::unique_ptr<NPUActivityProfiler> profiler;
80+ if (options.backend == "aclprof") {
81+ profiler = std::make_unique<AclprofActivityProfiler>(activities);
82+ } else {
83+ profiler = std::make_unique<MsptiActivityProfiler>(activities);
84+ }
85+ profiler->config_ = std::move(options);
86+ return profiler;
87+}
88+ 
89+void PrivateUse1ProfilerManager::start() {
90+ profiler_->start();
91+ status_ = profiler_->status();
92+}
93+ 
94+void PrivateUse1ProfilerManager::stop() {
95+ profiler_->stop();
96+ status_ = profiler_->status();
97+}
98+ 
99+std::vector<std::string> PrivateUse1ProfilerManager::errors() {
100+ return profiler_->errors();
101+}
102+ 
103+void PrivateUse1ProfilerManager::processTrace(libkineto::ActivityLogger& logger) {
104+ profiler_->processTrace(logger);
105+}
106+ 
107+void PrivateUse1ProfilerManager::processTrace(
108+ libkineto::ActivityLogger& logger,
109+ libkineto::getLinkedActivityCallback getLinkedActivity,
110+ int64_t startTime,
111+ int64_t endTime) {
112+ profiler_->processTrace(logger, std::move(getLinkedActivity), startTime, endTime);
113+}
114+ 
115+std::unique_ptr<libkineto::DeviceInfo> PrivateUse1ProfilerManager::getDeviceInfo() {
116+ return profiler_->getDeviceInfo();
117+}
118+ 
119+std::vector<libkineto::ResourceInfo> PrivateUse1ProfilerManager::getResourceInfos() {
120+ return profiler_->getResourceInfos();
121+}
122+ 
123+std::unique_ptr<libkineto::CpuTraceBuffer> PrivateUse1ProfilerManager::getTraceBuffer() {
124+ return profiler_->getTraceBuffer();
125+}
126+ 
127+void PrivateUse1ProfilerManager::pushCorrelationId(uint64_t id) {
128+ profiler_->pushCorrelationId(id);
129+}
130+ 
131+void PrivateUse1ProfilerManager::popCorrelationId() {
132+ profiler_->popCorrelationId();
133+}
134+ 
135+std::unique_ptr<libkineto::IActivityProfilerSession> NPUActivityProfilerPlugin::configure(
136+ const std::set<libkineto::ActivityType>& activityTypes,
137+ const libkineto::Config& config) {
138+ auto profiler = NPUActivityProfilerFactory::create(activityTypes, config);
139+ if (profiler == nullptr) {
140+ return nullptr;
141+ }
142+ return std::make_unique<PrivateUse1ProfilerManager>(std::move(profiler));
143+}
144+ 
145+std::unique_ptr<libkineto::IActivityProfilerSession> NPUActivityProfilerPlugin::configure(
146+ int64_t,
147+ int64_t,
148+ const std::set<libkineto::ActivityType>& activityTypes,
149+ const libkineto::Config& config) {
150+ return configure(activityTypes, config);
151+}
152+ 
153+#ifdef REGISTER_PRIVATEUSE1_PROFILER
154+REGISTER_PRIVATEUSE1_PROFILER(NPUActivityProfilerPlugin);
155+#else
156+namespace {
157+struct NPUProfilerAutoRegister {
158+ NPUProfilerAutoRegister() {
159+ libkineto::api().registerProfilerFactory([]() -> std::unique_ptr<libkineto::IActivityProfiler> {
160+ return std::make_unique<NPUActivityProfilerPlugin>();
161+ });
162+ }
163+};
164+static NPUProfilerAutoRegister g_npu_profiler_auto_register;
165+} // namespace
166+#endif
167+ 
168+} // namespace profiler
169+} // namespace torch_npu
@@ -0,0 +1,126 @@
1+// Copyright (c) 2026 Huawei Technologies Co., Ltd
2+// All rights reserved.
3+//
4+// Licensed under the BSD 3-Clause License (the "License");
5+// you may not use this file except in compliance with the License.
6+// You may obtain a copy of the License at
7+//
8+// https://opensource.org/licenses/BSD-3-Clause
9+//
10+// Unless required by applicable law or agreed to in writing, software
11+// distributed under the License is distributed on an "AS IS" BASIS,
12+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+// See the License for the specific language governing permissions and
14+// limitations under the License.
15+ 
16+#pragma once
17+ 
18+#include <memory>
19+#include <set>
20+#include <string>
21+#include <utility>
22+#include <vector>
23+ 
24+#include "IActivityProfiler.h"
25+#include "torch_npu/csrc/core/npu/NPUMacros.h"
26+ 
27+namespace torch_npu {
28+namespace profiler {
29+ 
30+struct NPUProfilerConfig {
31+ std::string backend = "mspti";
32+ // Empty fields mean unspecified; neither field affects backend selection.
33+ std::string profilerLevel;
34+ std::vector<std::string> exportTypes;
35+};
36+ 
37+class NPUActivityProfiler {
38+ public:
39+ virtual ~NPUActivityProfiler() = default;
40+ 
41+ virtual const char* name() const = 0;
42+ virtual void start() = 0;
43+ virtual void stop() = 0;
44+ virtual std::vector<std::string> errors() = 0;
45+ virtual void processTrace(libkineto::ActivityLogger& logger) = 0;
46+ virtual void processTrace(
47+ libkineto::ActivityLogger& logger,
48+ libkineto::getLinkedActivityCallback getLinkedActivity,
49+ int64_t startTime,
50+ int64_t endTime) = 0;
51+ virtual std::unique_ptr<libkineto::DeviceInfo> getDeviceInfo() = 0;
52+ virtual std::vector<libkineto::ResourceInfo> getResourceInfos() = 0;
53+ virtual std::unique_ptr<libkineto::CpuTraceBuffer> getTraceBuffer() = 0;
54+ virtual void pushCorrelationId(uint64_t) {}
55+ virtual void popCorrelationId() {}
56+ 
57+ libkineto::TraceStatus status() const {
58+ return status_;
59+ }
60+ 
61+ const NPUProfilerConfig& experimentalConfig() const {
62+ return config_;
63+ }
64+ 
65+ protected:
66+ libkineto::TraceStatus status_ = libkineto::TraceStatus::READY;
67+ 
68+ private:
69+ friend class NPUActivityProfilerFactory;
70+ NPUProfilerConfig config_;
71+};
72+ 
73+class TORCH_NPU_API NPUActivityProfilerFactory {
74+ public:
75+ static std::unique_ptr<NPUActivityProfiler> create(
76+ const std::set<libkineto::ActivityType>& activities,
77+ const libkineto::Config& config);
78+};
79+ 
80+class TORCH_NPU_API PrivateUse1ProfilerManager : public libkineto::IActivityProfilerSession {
81+ public:
82+ explicit PrivateUse1ProfilerManager(std::unique_ptr<NPUActivityProfiler> profiler) : profiler_(std::move(profiler)) {}
83+ 
84+ void start() override;
85+ void stop() override;
86+ std::vector<std::string> errors() override;
87+ void processTrace(libkineto::ActivityLogger& logger) override;
88+ void processTrace(
89+ libkineto::ActivityLogger& logger,
90+ libkineto::getLinkedActivityCallback getLinkedActivity,
91+ int64_t startTime,
92+ int64_t endTime) override;
93+ std::unique_ptr<libkineto::DeviceInfo> getDeviceInfo() override;
94+ std::vector<libkineto::ResourceInfo> getResourceInfos() override;
95+ std::unique_ptr<libkineto::CpuTraceBuffer> getTraceBuffer() override;
96+ void pushCorrelationId(uint64_t id) override;
97+ void popCorrelationId() override;
98+ 
99+ private:
100+ std::unique_ptr<NPUActivityProfiler> profiler_;
101+};
102+ 
103+class TORCH_NPU_API NPUActivityProfilerPlugin : public libkineto::IActivityProfiler {
104+ public:
105+ const std::string& name() const override {
106+ return name_;
107+ }
108+ const std::set<libkineto::ActivityType>& availableActivities() const override {
109+ return avail_;
110+ }
111+ std::unique_ptr<libkineto::IActivityProfilerSession> configure(
112+ const std::set<libkineto::ActivityType>& activityTypes,
113+ const libkineto::Config& config) override;
114+ std::unique_ptr<libkineto::IActivityProfilerSession> configure(
115+ int64_t tsMs,
116+ int64_t durationMs,
117+ const std::set<libkineto::ActivityType>& activityTypes,
118+ const libkineto::Config& config) override;
119+ 
120+ private:
121+ std::string name_{"mspti"};
122+ std::set<libkineto::ActivityType> avail_{libkineto::ActivityType::CONCURRENT_KERNEL};
123+};
124+ 
125+} // namespace profiler
126+} // namespace torch_npu
@@ -8,6 +8,7 @@
8#include "torch_npu/csrc/core/npu/npu_log.h"8#include "torch_npu/csrc/core/npu/npu_log.h"
9#include "torch_npu/csrc/core/npu/NPUException.h"9#include "torch_npu/csrc/core/npu/NPUException.h"
10#include "torch_npu/csrc/core/npu/interface/AclInterface.h"10#include "torch_npu/csrc/core/npu/interface/AclInterface.h"
11+#include "torch_npu/csrc/profiler/aclprof_activity_profiler.h"
11#include "torch_npu/csrc/profiler/npu_profiler.h"12#include "torch_npu/csrc/profiler/npu_profiler.h"
12 13 
13#include "torch_npu/csrc/toolkit/profiler/common/utils.h"14#include "torch_npu/csrc/toolkit/profiler/common/utils.h"
@@ -386,6 +387,16 @@ void finalizeNpuProfiler() {
386}387}
387 388 
388void reportMarkDataToNpuProfiler(uint32_t category, const std::string& msg, uint64_t correlation_id) {389void reportMarkDataToNpuProfiler(uint32_t category, const std::string& msg, uint64_t correlation_id) {
390+ recordAclprofTaskQueueEvent(
391+ category,
392+ correlation_id,
393+ static_cast<int64_t>(Utils::GetClockMonotonicRawNs()),
394+ static_cast<int64_t>(Utils::GetPid()),
395+ static_cast<int64_t>(Utils::GetTid()),
396+ msg);
397+ if (!profDataReportEnable().load(std::memory_order_relaxed)) {
398+ return;
399+ }
389 ProfilerMgr::GetInstance()->UploadWithLock(std::make_unique<torch_npu::toolkit::profiler::OpMarkData>(400 ProfilerMgr::GetInstance()->UploadWithLock(std::make_unique<torch_npu::toolkit::profiler::OpMarkData>(
390 static_cast<int64_t>(Utils::GetClockTime()), category, correlation_id, Utils::GetTid(), Utils::GetPid(), msg));401 static_cast<int64_t>(Utils::GetClockTime()), category, correlation_id, Utils::GetTid(), Utils::GetPid(), msg));
391}402}
@@ -9,8 +9,10 @@ from .scheduler import Schedule as schedule
9from .scheduler import ProfilerAction9from .scheduler import ProfilerAction
10from .experimental_config import _ExperimentalConfig, supported_profiler_level, supported_ai_core_metrics, \10from .experimental_config import _ExperimentalConfig, supported_profiler_level, supported_ai_core_metrics, \
11 supported_export_type, ProfilerLevel, AiCMetrics, ExportType, HostSystem11 supported_export_type, ProfilerLevel, AiCMetrics, ExportType, HostSystem
12+from ._kineto_config import _NPUExperimentalConfig
12from ._non_intrusive_profile import _NonIntrusiveProfile13from ._non_intrusive_profile import _NonIntrusiveProfile
13 14 
14__all__ = ["profile", "ProfilerActivity", "supported_activities", "tensorboard_trace_handler", "schedule",15__all__ = ["profile", "ProfilerActivity", "supported_activities", "tensorboard_trace_handler", "schedule",
15- "ProfilerAction", "_ExperimentalConfig", "supported_profiler_level", "supported_ai_core_metrics",16+ "ProfilerAction", "_ExperimentalConfig", "supported_profiler_level",
17+ "supported_ai_core_metrics",
16 "supported_export_type", "ProfilerLevel", "AiCMetrics", "ExportType", "HostSystem"]18 "supported_export_type", "ProfilerLevel", "AiCMetrics", "ExportType", "HostSystem"]
@@ -0,0 +1,56 @@
1+# Copyright (c) 2026 Huawei Technologies Co., Ltd
2+# All rights reserved.
3+#
4+# Licensed under the BSD 3-Clause License (the "License");
5+# you may not use this file except in compliance with the License.
6+# You may obtain a copy of the License at
7+#
8+# https://opensource.org/licenses/BSD-3-Clause
9+#
10+# Unless required by applicable law or agreed to in writing, software
11+# distributed under the License is distributed on an "AS IS" BASIS,
12+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+# See the License for the specific language governing permissions and
14+# limitations under the License.
15+ 
16+import json
17+ 
18+from torch._C._profiler import _ExperimentalConfig as ExperimentalConfig
19+ 
20+from .experimental_config import supported_export_type, supported_profiler_level
21+ 
22+ 
23+class _NPUExperimentalConfig(ExperimentalConfig):
24+ """Ascend-specific options for ``torch.profiler.profile``.
25+ 
26+ Only ``backend="aclprof"`` selects the ACLprof stub. ``profiler_level``
27+ and ``export_type`` are retained for future implementations; they do not
28+ enable collection or export. ``None`` leaves these options unspecified.
29+ Exports accept an ExportType string or a nonempty list of those strings.
30+ Invalid levels/exports raise ValueError; invalid backends fall back to MSPTI.
31+ """
32+ 
33+ def __init__(self, *, backend: str = "mspti", profiler_level=None, export_type=None, **kwargs):
34+ config = {"backend": backend}
35+ if profiler_level is not None:
36+ if not isinstance(profiler_level, str) or profiler_level not in supported_profiler_level():
37+ raise ValueError("profiler_level must be a supported ProfilerLevel value")
38+ config["profiler_level"] = profiler_level
39+ if export_type is not None:
40+ exports = [export_type] if isinstance(export_type, str) else export_type
41+ if not isinstance(exports, list) or not exports or any(
42+ not isinstance(value, str) or value not in supported_export_type() for value in exports
43+ ):
44+ raise ValueError("export_type must be an ExportType value or a nonempty list of them")
45+ config["export_type"] = sorted(set(exports))
46+ super().__init__(
47+ custom_profiler_config=json.dumps(config, separators=(",", ":")),
48+ **kwargs,
49+ )
50+ 
51+ def __str__(self):
52+ # Older PyTorch builds expose this field only through their pickle state.
53+ custom_config = getattr(self, "custom_profiler_config", None)
54+ if custom_config is not None:
55+ return custom_config
56+ return self.__getstate__()[11]