已开启
feat(profiler): implement ACLprof backend for Kineto NPU activities #45722
denis_g创建于 9月2日
feat(profiler): implement ACLprof backend for Kineto NPU activities #45722
已开启
denis_g创建于 9月2日
共 15 个文件变更+1762-75
@@ -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)
@@ -0,0 +1,604 @@
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 <gmock/gmock.h>
17+#include <gtest/gtest.h>
18+#include <c10/util/Exception.h>
19+ 
20+#include <algorithm>
21+#include <cstdio>
22+#include <cstdlib>
23+#include <fstream>
24+#include <memory>
25+#include <set>
26+#include <stdexcept>
27+#include <string>
28+#include <unordered_map>
29+#include <vector>
30+#include <unistd.h>
31+ 
32+#include "ActivityBuffers.h"
33+#include "Config.h"
34+#include "GenericTraceActivity.h"
35+#include "output_base.h"
36+#include "torch_npu/csrc/profiler/aclprof_activity_profiler.h"
37+#include "torch_npu/csrc/profiler/aclprof_activity_profiler_internal.h"
38+#include "torch_npu/csrc/profiler/npu_activity_profiler.h"
39+ 
40+namespace torch_npu {
41+namespace profiler {
42+ 
43+class AclprofActivityProfilerTestPeer {
44+ public:
45+ static std::unique_ptr<AclprofActivityProfiler> create(std::shared_ptr<detail::AclprofApi> api) {
46+ return std::unique_ptr<AclprofActivityProfiler>(
47+ new AclprofActivityProfiler({libkineto::ActivityType::CONCURRENT_KERNEL}, std::move(api)));
48+ }
49+ 
50+ static void setEvents(AclprofActivityProfiler& profiler, std::vector<detail::AclprofEvent> events) {
51+ profiler.events_ = std::move(events);
52+ }
53+};
54+ 
55+namespace {
56+ 
57+class MockActivityLogger : public libkineto::ActivityLogger {
58+ public:
59+ MOCK_METHOD(void, handleDeviceInfo, (const libkineto::DeviceInfo&, int64_t), (override));
60+ MOCK_METHOD(void, handleResourceInfo, (const libkineto::ResourceInfo&, int64_t), (override));
61+ MOCK_METHOD(void, handleOverheadInfo, (const libkineto::ActivityLogger::OverheadInfo&, int64_t), (override));
62+ MOCK_METHOD(void, handleTraceSpan, (const libkineto::TraceSpan&), (override));
63+ MOCK_METHOD(void, handleActivity, (const libkineto::ITraceActivity&), (override));
64+ MOCK_METHOD(void, handleGenericActivity, (const libkineto::GenericTraceActivity&), (override));
65+ MOCK_METHOD(
66+ void,
67+ handleTraceStart,
68+ ((const std::unordered_map<std::string, std::string>&), const std::string&),
69+ (override));
70+ MOCK_METHOD(void, finalizeMemoryTrace, (const std::string&, const libkineto::Config&), (override));
71+ // The owning argument requires a complete ActivityBuffers type even with an empty body.
72+ void finalizeTrace(const libkineto::Config&, std::unique_ptr<libkineto::ActivityBuffers>, int64_t) override {}
73+};
74+ 
75+class CountingActivityLogger : public MockActivityLogger {
76+ public:
77+ void handleGenericActivity(const libkineto::GenericTraceActivity& activity) override {
78+ type = activity.type();
79+ stream = activity.resourceId();
80+ duration = activity.duration();
81+ streamMetadata = activity.getMetadataValue("streamId");
82+ taskType = activity.getMetadataValue("Task Type");
83+ names.push_back(activity.name());
84+ devices.push_back(activity.deviceId());
85+ }
86+ 
87+ libkineto::ActivityType type = libkineto::ActivityType::ENUM_COUNT;
88+ std::string streamMetadata;
89+ std::string taskType;
90+ std::vector<std::string> names;
91+ std::vector<int64_t> devices;
92+ int64_t stream = -1;
93+ int64_t duration = 0;
94+};
95+ 
96+std::vector<detail::AclprofEvent> parseTimeline(
97+ const std::string& contents,
98+ int64_t physicalDevice,
99+ std::vector<std::string>& errors) {
100+ char path[] = "/tmp/torch_npu_aclprof_timeline_XXXXXX";
@@ -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);
@@ -0,0 +1,557 @@
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/aclprof_activity_profiler.h"
17+#include "torch_npu/csrc/profiler/aclprof_activity_profiler_internal.h"
18+ 
19+#include <algorithm>
20+#include <cerrno>
21+#include <cctype>
22+#include <cmath>
23+#include <cstdlib>
24+#include <filesystem>
25+#include <fstream>
26+#include <iterator>
27+#include <limits>
28+#include <map>
29+#include <optional>
30+#include <regex>
31+#include <spawn.h>
32+#include <sys/wait.h>
33+#include <utility>
34+ 
35+#include <nlohmann/json.hpp>
36+ 
37+#include "GenericTraceActivity.h"
38+#include "libkineto.h"
39+#include "torch_npu/csrc/core/npu/NPUFunctions.h"
40+#include "torch_npu/csrc/core/npu/interface/AclInterface.h"
41+#include "torch_npu/csrc/framework/interface/AclInterface.h"
42+#include "torch_npu/csrc/framework/interface/MsProfilerInterface.h"
43+ 
44+extern char** environ;
45+ 
46+namespace torch_npu {
47+namespace profiler {
48+namespace {
49+ 
50+namespace fs = std::filesystem;
51+using json = nlohmann::json;
52+constexpr uint64_t kDataTypes = ACL_PROF_TASK_TIME | ACL_PROF_ACL_API;
53+ 
54+std::string text(const json& value) {
55+ if (value.is_string()) {
56+ return value.get<std::string>();
57+ }
58+ return value.is_null() ? "" : value.dump();
59+}
60+ 
61+std::optional<int64_t> integer(const json& object, std::initializer_list<const char*> keys) {
62+ if (!object.is_object()) {
63+ return std::nullopt;
64+ }
65+ for (const auto* key : keys) {
66+ const auto found = object.find(key);
67+ if (found == object.end()) {
68+ continue;
69+ }
70+ try {
71+ if (found->is_number_integer() || found->is_number_unsigned()) {
72+ return found->get<int64_t>();
73+ }
74+ if (found->is_string()) {
75+ size_t parsed = 0;
76+ const auto result = std::stoll(found->get<std::string>(), &parsed);
77+ if (parsed == found->get_ref<const std::string&>().size()) {
78+ return result;
79+ }
80+ }
81+ } catch (const std::exception&) {
82+ }
83+ }
84+ return std::nullopt;
85+}
86+ 
87+int64_t microsecondsToNanoseconds(const json& value) {
88+ try {
89+ const auto source = value.is_string() ? value.get<std::string>() : value.dump();
90+ size_t parsed = 0;
91+ const long double result = std::stold(source, &parsed) * 1000.0L;
92+ if (parsed == source.size() && std::isfinite(result) &&
93+ result <= static_cast<long double>(std::numeric_limits<int64_t>::max()) &&
94+ result >= static_cast<long double>(std::numeric_limits<int64_t>::min())) {
95+ return static_cast<int64_t>(std::llround(result));
96+ }
97+ } catch (const std::exception&) {
98+ }
99+ return 0;
100+}
@@ -0,0 +1,75 @@
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 <set>
21+#include <string>
22+#include <vector>
23+ 
24+#include "torch_npu/csrc/profiler/npu_activity_profiler.h"
25+ 
26+namespace torch_npu {
27+namespace profiler {
28+ 
29+namespace detail {
30+struct AclprofApi;
31+struct AclprofEvent;
32+} // namespace detail
33+ 
34+class AclprofActivityProfilerTestPeer;
35+ 
36+class TORCH_NPU_API AclprofActivityProfiler : public NPUActivityProfiler {
37+ public:
38+ explicit AclprofActivityProfiler(std::set<libkineto::ActivityType> activities);
39+ ~AclprofActivityProfiler() override;
40+ 
41+ const char* name() const override {
42+ return "aclprof";
43+ }
44+ void start() override;
45+ void stop() override;
46+ std::vector<std::string> errors() override;
47+ void processTrace(libkineto::ActivityLogger& logger) override;
48+ void processTrace(
49+ libkineto::ActivityLogger& logger,
50+ libkineto::getLinkedActivityCallback getLinkedActivity,
51+ int64_t startTime,
52+ int64_t endTime) override;
53+ std::unique_ptr<libkineto::DeviceInfo> getDeviceInfo() override;
54+ std::vector<libkineto::ResourceInfo> getResourceInfos() override;
55+ std::unique_ptr<libkineto::CpuTraceBuffer> getTraceBuffer() override;
56+ 
57+ private:
58+ AclprofActivityProfiler(std::set<libkineto::ActivityType> activities, std::shared_ptr<detail::AclprofApi> api);
59+ bool finishCollection(bool reportErrors);
60+ void cleanup();
61+ 
62+ friend class AclprofActivityProfilerTestPeer;
63+ std::set<libkineto::ActivityType> activities_;
64+ std::shared_ptr<detail::AclprofApi> api_;
65+ std::vector<detail::AclprofEvent> events_;
66+ std::vector<std::string> errors_;
67+ std::string root_;
68+ void* config_ = nullptr;
69+ int64_t device_ = -1;
70+ bool initialized_ = false;
71+ bool started_ = false;
72+};
73+ 
74+} // namespace profiler
75+} // namespace torch_npu
@@ -0,0 +1,37 @@
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+};
18+ 
19+// physicalDevice is the runtime-resolved physical device for this single-device session.
20+std::vector<AclprofEvent> parseAclprofTimeline(
21+ const std::string& path,
22+ int64_t physicalDevice,
23+ std::vector<std::string>& errors);
24+ 
25+struct AclprofApi {
26+ std::function<int32_t(int32_t*)> getDevice;
27+ std::function<int32_t(int32_t, int32_t*)> getPhysicalDevice;
28+ std::function<int32_t(const std::string&)> init;
29+ std::function<void*(uint32_t*)> createConfig;
30+ std::function<int32_t()> start;
31+ std::function<int32_t()> registerDeviceCallback;
32+ std::function<int32_t()> stop;
33+ std::function<int32_t()> destroyConfig;
34+ std::function<int32_t()> finalize;
35+};
36+ 
37+} // 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
@@ -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]