已合并
对齐Pytorch Profiler部分接口 #34217
xfeng创建于 4月23日
对齐Pytorch Profiler部分接口 #34217
已合并
xfeng创建于 4月23日
6 个文件变更+433-149
@@ -1,23 +1,27 @@
1+# Owner(s): ["oncall: profiler"]
1import os2import os
2import shutil3import shutil
3from unittest import mock4from unittest import mock
4 5 
5-import torch
6- 
7-from torch_npu.npu import Event
8-from torch_npu.profiler.analysis.prof_common_func._constant import Constant
9-from torch_npu.profiler.profiler_interface import _ProfInterface
10-from torch_npu.profiler.profiler_interface import _enable_event_record
11-from torch_npu.profiler.profiler_interface import _disable_event_record
12-from torch_npu.profiler._profiler_path_creator import ProfPathCreator
13-from torch_npu.profiler import supported_activities
14from torch_npu._C._profiler import ProfilerActivity6from torch_npu._C._profiler import ProfilerActivity
15-from torch_npu.profiler.analysis.prof_common_func._cann_package_manager import CannPackageManager7+from torch_npu.npu import Event
16-from torch_npu.testing.testcase import TestCase, run_tests8+from torch_npu.profiler import supported_activities
9+from torch_npu.profiler._profiler_path_creator import ProfPathCreator
10+from torch_npu.profiler.analysis.prof_common_func._cann_package_manager import (
11+ CannPackageManager,
12+)
13+from torch_npu.profiler.analysis.prof_common_func._constant import Constant
14+from torch_npu.profiler.profiler_interface import (
15+ _disable_event_record,
16+ _enable_event_record,
17+ _ProfInterface,
18+)
19+from torch_npu.testing.testcase import run_tests, TestCase
20+ 
21+import torch
17 22 
18 23 
19class TestActionController(TestCase):24class TestActionController(TestCase):
20- 
21 @classmethod25 @classmethod
22 def setUpClass(cls):26 def setUpClass(cls):
23 super().setUpClass()27 super().setUpClass()
@@ -34,18 +38,24 @@ class TestActionController(TestCase):
34 ProfPathCreator().init(dir_name=self.prof_dir)38 ProfPathCreator().init(dir_name=self.prof_dir)
35 39 
36 def test_init_trace(self):40 def test_init_trace(self):
41+ self.prof_if.custom_trace_id_callback = lambda: "trace_0"
37 with mock.patch(self.namespace + "._init_profiler") as mock_func:42 with mock.patch(self.namespace + "._init_profiler") as mock_func:
38 self.prof_if.init_trace()43 self.prof_if.init_trace()
39 self.assertEqual(1, mock_func.call_count)44 self.assertEqual(1, mock_func.call_count)
atomgit-bot
atomgit-botatomgit-bot4月24日

⚠️ 测试覆盖不足:callback 异常场景未覆盖

测试仅覆盖了正常设置 callback 并获取 trace_id 的场景,未覆盖:(1) callback 抛异常时是否优雅降级;(2) callback 返回非字符串时是否降级;(3) 不设置 callback 时默认生成 UUID 的场景。边界条件测试缺失可能导致生产环境问题。

Request Changes

参考依据

  • 类别: general
  • 置信度: 75%
likedislike
40 self.assertTrue(os.path.exists(self.prof_dir))45 self.assertTrue(os.path.exists(self.prof_dir))
46+ self.assertEqual("trace_0", self.prof_if.trace_id)
41 47 
42 def test_start_trace(self):48 def test_start_trace(self):
43- with mock.patch(self.namespace + ".NpuProfilerConfig", return_value="config_obj"), \49+ with (
44- mock.patch(self.namespace + "._get_syscnt_enable", return_value=True), \50+ mock.patch(
45- mock.patch(self.namespace + "._get_freq", return_value=100), \51+ self.namespace + ".NpuProfilerConfig", return_value="config_obj"
46- mock.patch(self.namespace + "._get_syscnt", return_value=10000), \52+ ),
47- mock.patch(self.namespace + "._get_monotonic", return_value=20000), \53+ mock.patch(self.namespace + "._get_syscnt_enable", return_value=True),
48- mock.patch(self.namespace + "._start_profiler") as mock_start:54+ mock.patch(self.namespace + "._get_freq", return_value=100),
55+ mock.patch(self.namespace + "._get_syscnt", return_value=10000),
56+ mock.patch(self.namespace + "._get_monotonic", return_value=20000),
57+ mock.patch(self.namespace + "._start_profiler") as mock_start,
58+ ):
49 self.prof_if.start_trace()59 self.prof_if.start_trace()
50 self.assertEqual(True, self.prof_if.syscnt_enable)60 self.assertEqual(True, self.prof_if.syscnt_enable)
51 self.assertEqual(100, self.prof_if.freq)61 self.assertEqual(100, self.prof_if.freq)
@@ -57,11 +67,13 @@ class TestActionController(TestCase):
57 with mock.patch(self.namespace + "._stop_profiler") as mock_stop:67 with mock.patch(self.namespace + "._stop_profiler") as mock_stop:
58 self.prof_if.stop_trace()68 self.prof_if.stop_trace()
59 mock_stop.assert_called_once()69 mock_stop.assert_called_once()
60- 70+ 
61 def test_finalize_trace(self):71 def test_finalize_trace(self):
62- with mock.patch(self.namespace + "._init_profiler"), \72+ with (
63- mock.patch(self.namespace + "._finalize_profiler") as mock_finalize:73+ mock.patch(self.namespace + "._init_profiler"),
64- self.prof_if.metadata = {"key":"val"}74+ mock.patch(self.namespace + "._finalize_profiler") as mock_finalize,
75+ ):
76+ self.prof_if.metadata = {"key": "val"}
65 self.prof_if.init_trace()77 self.prof_if.init_trace()
66 self.prof_if.finalize_trace()78 self.prof_if.finalize_trace()
67 mock_finalize.assert_called_once()79 mock_finalize.assert_called_once()
@@ -69,7 +81,9 @@ class TestActionController(TestCase):
69 self.assertTrue(self._check_metadata_json(self.prof_if.prof_path))81 self.assertTrue(self._check_metadata_json(self.prof_if.prof_path))
70 82 
71 def test_analyse(self):83 def test_analyse(self):
72- with mock.patch("torch_npu.profiler.analysis._npu_profiler.NpuProfiler.analyse") as mock_analyse:84+ with mock.patch(
85+ "torch_npu.profiler.analysis._npu_profiler.NpuProfiler.analyse"
86+ ) as mock_analyse:
73 self.prof_if.analyse()87 self.prof_if.analyse()
74 mock_analyse.assert_called_once()88 mock_analyse.assert_called_once()
75 89 
@@ -79,6 +93,49 @@ class TestActionController(TestCase):
79 self.assertTrue(ProfilerActivity.CPU in activities)93 self.assertTrue(ProfilerActivity.CPU in activities)
80 self.assertTrue(ProfilerActivity.NPU in activities)94 self.assertTrue(ProfilerActivity.NPU in activities)
81 95 
96+ def test_create_trace_id_should_return_default_when_callback_is_none(self):
97+ self.prof_if.custom_trace_id_callback = None
98+ trace_id = self.prof_if.create_trace_id()
99+ self.assertIsInstance(trace_id, str)
100+ self.assertEqual(32, len(trace_id))
101+ 
102+ def test_create_trace_id_should_return_callback_result_when_callback_is_valid(self):
103+ self.prof_if.custom_trace_id_callback = lambda: "custom_trace_id"
104+ trace_id = self.prof_if.create_trace_id()
105+ self.assertEqual("custom_trace_id", trace_id)
106+ 
107+ def test_create_trace_id_should_return_default_when_callback_is_not_callable(self):
108+ self.prof_if.custom_trace_id_callback = "not_callable"
109+ trace_id = self.prof_if.create_trace_id()
110+ self.assertIsInstance(trace_id, str)
111+ self.assertEqual(32, len(trace_id))
112+ 
113+ def test_create_trace_id_should_return_default_when_callback_returns_non_string(
114+ self,
115+ ):
116+ self.prof_if.custom_trace_id_callback = lambda: 12345
117+ trace_id = self.prof_if.create_trace_id()
118+ self.assertIsInstance(trace_id, str)
119+ self.assertEqual(32, len(trace_id))
120+ 
121+ def test_create_trace_id_should_return_default_when_callback_raises_exception(self):
122+ def bad_callback():
123+ raise RuntimeError("callback error")
124+ 
125+ self.prof_if.custom_trace_id_callback = bad_callback
126+ trace_id = self.prof_if.create_trace_id()
127+ self.assertIsInstance(trace_id, str)
128+ self.assertEqual(32, len(trace_id))
129+ 
130+ def test_create_trace_id_should_return_default_when_callback_returns_too_long_string(
131+ self,
132+ ):
133+ long_str = "a" * (self.prof_if.MAX_TRACE_ID_LEN + 1)
134+ self.prof_if.custom_trace_id_callback = lambda: long_str
135+ trace_id = self.prof_if.create_trace_id()
136+ self.assertIsInstance(trace_id, str)
137+ self.assertEqual(32, len(trace_id))
138+ 
82 def test_event_record_should_have_return_true_attr_when_enable_record(self):139 def test_event_record_should_have_return_true_attr_when_enable_record(self):
83 _enable_event_record()140 _enable_event_record()
84 self.assertTrue(hasattr(Event.record, "origin_func"))141 self.assertTrue(hasattr(Event.record, "origin_func"))
@@ -96,13 +153,15 @@ class TestActionController(TestCase):
96 def _check_profiler_info_json(self, prof_path: str) -> bool:153 def _check_profiler_info_json(self, prof_path: str) -> bool:
97 if torch.distributed.is_available() and torch.distributed.is_initialized():154 if torch.distributed.is_available() and torch.distributed.is_initialized():
98 rank_id = torch.distributed.get_rank()155 rank_id = torch.distributed.get_rank()
99- path = os.path.join(os.path.realpath(prof_path), f'profiler_info_{rank_id}.json')156+ path = os.path.join(
157+ os.path.realpath(prof_path), f"profiler_info_{rank_id}.json"
158+ )
100 else:159 else:
101- path = os.path.join(os.path.realpath(prof_path), 'profiler_info.json')160+ path = os.path.join(os.path.realpath(prof_path), "profiler_info.json")
102 return os.path.exists(path)161 return os.path.exists(path)
103 162 
104 def _check_metadata_json(self, prof_path: str) -> bool:163 def _check_metadata_json(self, prof_path: str) -> bool:
105- path = os.path.join(os.path.realpath(prof_path), 'profiler_metadata.json')164+ path = os.path.join(os.path.realpath(prof_path), "profiler_metadata.json")
106 return os.path.exists(path)165 return os.path.exists(path)
107 166 
108 def _check_params(self):167 def _check_params(self):
@@ -1,48 +1,66 @@
1-import torch_npu1+# Owner(s): ["oncall: profiler"]
2-from torch_npu.profiler import ProfilerAction2+from torch_npu.profiler import ProfilerAction, schedule
3-from torch_npu.profiler import schedule3+from torch_npu.testing.testcase import run_tests, TestCase
4-from torch_npu.testing.testcase import TestCase, run_tests
5 4 
6 5 
7class TestScheduler(TestCase):6class TestScheduler(TestCase):
8- 
9 def setUp(self):7 def setUp(self):
10 self.schedule_list = [8 self.schedule_list = [
11 # wait, active, warmup, repeat, skip_first, [step, expected result]9 # wait, active, warmup, repeat, skip_first, [step, expected result]
12 [10 [
13- 0, 0, 0, 0, 0,11+ 0,
12+ 0,
13+ 0,
14+ 0,
15+ 0,
14 [0, ProfilerAction.RECORD_AND_SAVE],16 [0, ProfilerAction.RECORD_AND_SAVE],
15 [1, ProfilerAction.RECORD_AND_SAVE],17 [1, ProfilerAction.RECORD_AND_SAVE],
16- [10, ProfilerAction.RECORD_AND_SAVE]18+ [10, ProfilerAction.RECORD_AND_SAVE],
17 ],19 ],
18 [20 [
19- -1, -1, -1, -1, -1,21+ -1,
22+ -1,
23+ -1,
24+ -1,
25+ -1,
20 [0, ProfilerAction.RECORD_AND_SAVE],26 [0, ProfilerAction.RECORD_AND_SAVE],
21 [1, ProfilerAction.RECORD_AND_SAVE],27 [1, ProfilerAction.RECORD_AND_SAVE],
22- [10, ProfilerAction.RECORD_AND_SAVE]28+ [10, ProfilerAction.RECORD_AND_SAVE],
23 ],29 ],
24 [30 [
25- 2, 2, 2, 0, 2,31+ 2,
32+ 2,
33+ 2,
34+ 0,
35+ 2,
26 [0, ProfilerAction.NONE],36 [0, ProfilerAction.NONE],
27 [1, ProfilerAction.NONE],37 [1, ProfilerAction.NONE],
28 [3, ProfilerAction.NONE],38 [3, ProfilerAction.NONE],
29 [5, ProfilerAction.WARMUP],39 [5, ProfilerAction.WARMUP],
30 [6, ProfilerAction.RECORD],40 [6, ProfilerAction.RECORD],
31 [7, ProfilerAction.RECORD_AND_SAVE],41 [7, ProfilerAction.RECORD_AND_SAVE],
32- [13, ProfilerAction.RECORD_AND_SAVE]42+ [13, ProfilerAction.RECORD_AND_SAVE],
33 ],43 ],
34 [44 [
35- 2, 2, 2, 1, 2,45+ 2,
46+ 2,
47+ 2,
48+ 1,
49+ 2,
36 [0, ProfilerAction.NONE],50 [0, ProfilerAction.NONE],
37 [1, ProfilerAction.NONE],51 [1, ProfilerAction.NONE],
38 [3, ProfilerAction.NONE],52 [3, ProfilerAction.NONE],
39 [5, ProfilerAction.WARMUP],53 [5, ProfilerAction.WARMUP],
40 [6, ProfilerAction.RECORD],54 [6, ProfilerAction.RECORD],
41 [7, ProfilerAction.RECORD_AND_SAVE],55 [7, ProfilerAction.RECORD_AND_SAVE],
42- [13, ProfilerAction.NONE]56+ [13, ProfilerAction.NONE],
43 ],57 ],
44 [58 [
45- 2, 2, 2, 4, 2,59+ 2,
60+ 2,
61+ 2,
62+ 4,
63+ 2,
46 [0, ProfilerAction.NONE],64 [0, ProfilerAction.NONE],
47 [1, ProfilerAction.NONE],65 [1, ProfilerAction.NONE],
48 [3, ProfilerAction.NONE],66 [3, ProfilerAction.NONE],
@@ -63,14 +81,87 @@ class TestScheduler(TestCase):
63 active=samples[1],81 active=samples[1],
64 warmup=samples[2],82 warmup=samples[2],
65 repeat=samples[3],83 repeat=samples[3],
66- skip_first=samples[4]84+ skip_first=samples[4],
67 )85 )
68- test_pair = samples[self.test_pair_idx:]86+ test_pair = samples[self.test_pair_idx :]
69 self.assertTrue(sche_inst.active >= 1)87 self.assertTrue(sche_inst.active >= 1)
70 for step, expect_result in test_pair:88 for step, expect_result in test_pair:
71 result = sche_inst(step)89 result = sche_inst(step)
72 self.assertEqual(result, expect_result)90 self.assertEqual(result, expect_result)
73 91 
92+ def test_skip_first_wait_should_works_when_non_zero(self):
93+ test_schedule = schedule(
94+ skip_first=1, wait=2, warmup=1, active=2, repeat=2, skip_first_wait=1
95+ )
96+ test_schedule_expected_outputs = [
97+ # repeat No. 1 begin
98+ # skip first 1
99+ ProfilerAction.NONE,
100+ # warmup 1
101+ ProfilerAction.WARMUP,
102+ # active 1 begin
103+ ProfilerAction.RECORD,
104+ ProfilerAction.RECORD_AND_SAVE,
105+ # active 1 end
106+ # repeat No. 1 end
107+ # ---
108+ # repeat No. 2 begin
109+ # wait 2
110+ ProfilerAction.NONE,
111+ ProfilerAction.NONE,
112+ # warmup 1
113+ ProfilerAction.WARMUP,
114+ # active 2 begin
115+ ProfilerAction.RECORD,
116+ ProfilerAction.RECORD_AND_SAVE,
117+ # active 2 end
118+ # repeat No. 2 end
119+ ProfilerAction.NONE,
120+ ProfilerAction.NONE,
121+ ProfilerAction.NONE,
122+ ProfilerAction.NONE,
123+ ]
124+ for step in range(len(test_schedule_expected_outputs)):
125+ self.assertEqual(test_schedule(step), test_schedule_expected_outputs[step])
126+ 
127+ def test_skip_first_wait_should_be_reset_when_invalid(self):
128+ test_schedule = schedule(
129+ skip_first=1, wait=2, warmup=1, active=2, repeat=2, skip_first_wait=0.5
130+ )
131+ test_schedule_expected_outputs = [
132+ # skip first 1
133+ ProfilerAction.NONE,
134+ # repeat No. 1 begin
135+ # wait 2
136+ ProfilerAction.NONE,
137+ ProfilerAction.NONE,
138+ # warmup 1
139+ ProfilerAction.WARMUP,
140+ # active 1 begin
141+ ProfilerAction.RECORD,
142+ ProfilerAction.RECORD_AND_SAVE,
143+ # active 1 end
144+ # repeat No. 1 end
145+ # ---
146+ # repeat No. 2 begin
147+ # wait 2
148+ ProfilerAction.NONE,
149+ ProfilerAction.NONE,
150+ # warmup 1
151+ ProfilerAction.WARMUP,
152+ # active 2 begin
153+ ProfilerAction.RECORD,
154+ ProfilerAction.RECORD_AND_SAVE,
155+ # active 2 end
156+ # repeat No. 2 end
157+ ProfilerAction.NONE,
158+ ProfilerAction.NONE,
159+ ProfilerAction.NONE,
160+ ProfilerAction.NONE,
161+ ]
162+ for step in range(len(test_schedule_expected_outputs)):
163+ self.assertEqual(test_schedule(step), test_schedule_expected_outputs[step])
164+ 
74 165 
75if __name__ == "__main__":166if __name__ == "__main__":
76- run_tests()167+ run_tests()
@@ -1683,7 +1683,7 @@
1683 "signature": "(profiler_level: int = 'Level0', aic_metrics: int = 'ACL_AICORE_PIPE_UTILIZATION', l2_cache: bool = False, msprof_tx: bool = False, mstx: bool = False, data_simplification: bool = True, record_op_args: bool = False, op_attr: bool = False, gc_detect_threshold: float = None, export_type: Union[str, list] = 'text', host_sys: list = None, sys_io: bool = False, sys_interconnection: bool = False, mstx_domain_include: list = None, mstx_domain_exclude: list = None)"1683 "signature": "(profiler_level: int = 'Level0', aic_metrics: int = 'ACL_AICORE_PIPE_UTILIZATION', l2_cache: bool = False, msprof_tx: bool = False, mstx: bool = False, data_simplification: bool = True, record_op_args: bool = False, op_attr: bool = False, gc_detect_threshold: float = None, export_type: Union[str, list] = 'text', host_sys: list = None, sys_io: bool = False, sys_interconnection: bool = False, mstx_domain_include: list = None, mstx_domain_exclude: list = None)"
1684 },1684 },
1685 "torch_npu.profiler.profile": {1685 "torch_npu.profiler.profile": {
1686- "signature": "(*, activities: Optional[Iterable[torch_npu._C._profiler.ProfilerActivity]] = None, schedule: Optional[Callable[[int], torch_npu.profiler.scheduler.ProfilerAction]] = None, on_trace_ready: Optional[Callable[..., Any]] = None, record_shapes: bool = False, profile_memory: bool = False, with_stack: bool = False, with_flops: bool = False, with_modules: bool = False, experimental_config: Optional[torch_npu.profiler.experimental_config._ExperimentalConfig] = None, use_cuda: Optional[bool] = None)"1686+ "signature": "(*, activities: Optional[collections.abc.Iterable[torch_npu._C._profiler.ProfilerActivity]] = None, schedule: Optional[collections.abc.Callable[[int], torch_npu.profiler.scheduler.ProfilerAction]] = None, on_trace_ready: Optional[collections.abc.Callable[..., Any]] = None, record_shapes: bool = False, profile_memory: bool = False, with_stack: bool = False, with_flops: bool = False, with_modules: bool = False, experimental_config: Optional[torch_npu.profiler.experimental_config._ExperimentalConfig] = None, custom_trace_id_callback: Optional[collections.abc.Callable[[], str]] = None, use_cuda: Optional[bool] = None)"
1687 },1687 },
1688 "torch_npu.profiler.profile.start": {1688 "torch_npu.profiler.profile.start": {
1689 "signature": "(self)"1689 "signature": "(self)"
@@ -1694,6 +1694,12 @@
1694 "torch_npu.profiler.profile.step": {1694 "torch_npu.profiler.profile.step": {
1695 "signature": "(self)"1695 "signature": "(self)"
1696 },1696 },
1697+ "torch_npu.profiler.profile.get_trace_id": {
1698+ "signature": "(self) -> str"
1699+ },
1700+ "torch_npu.profiler.profile.set_custom_trace_id_callback": {
1701+ "signature": "(self, callback: collections.abc.Callable[[], str]) -> None"
1702+ },
1697 "torch_npu.profiler.profile.enable_profiler_in_child_thread": {1703 "torch_npu.profiler.profile.enable_profiler_in_child_thread": {
1698 "signature": "(record_shapes: bool = False, profile_memory: bool = False, with_stack: bool = False, with_flops: bool = False, with_modules: bool = False)"1704 "signature": "(record_shapes: bool = False, profile_memory: bool = False, with_stack: bool = False, with_flops: bool = False, with_modules: bool = False)"
1699 },1705 },
@@ -1701,7 +1707,7 @@
1701 "signature": "()"1707 "signature": "()"
1702 },1708 },
1703 "torch_npu.profiler.schedule": {1709 "torch_npu.profiler.schedule": {
1704- "signature": "(wait: int, active: int, warmup: int = 0, repeat: int = 0, skip_first: int = 0) -> None"1710+ "signature": "(wait: int, active: int, warmup: int = 0, repeat: int = 0, skip_first: int = 0, skip_first_wait: int = 0) -> None"
1705 },1711 },
1706 "torch_npu.profiler.supported_activities": {1712 "torch_npu.profiler.supported_activities": {
1707 "signature": "()"1713 "signature": "()"
@@ -1716,7 +1722,7 @@
1716 "signature": "()"1722 "signature": "()"
1717 },1723 },
1718 "torch_npu.profiler.tensorboard_trace_handler": {1724 "torch_npu.profiler.tensorboard_trace_handler": {
1719- "signature": "(dir_name: str = None, worker_name: str = None, analyse_flag: bool = True, async_mode: bool = False)"1725+ "signature": "(dir_name: Optional[str] = None, worker_name: Optional[str] = None, analyse_flag: bool = True, async_mode: bool = False)"
1720 },1726 },
1721 "torch_npu.profiler.dynamic_profile.init": {1727 "torch_npu.profiler.dynamic_profile.init": {
1722 "signature": "(path: str)"1728 "signature": "(path: str)"
@@ -1749,10 +1755,10 @@
1749 "signature": "()"1755 "signature": "()"
1750 },1756 },
1751 "torch_npu.profiler.profiler.analyse": {1757 "torch_npu.profiler.profiler.analyse": {
1752- "signature": "(profiler_path: str, max_process_number: int = 96, export_type: Union[str, list] = None)"1758+ "signature": "(profiler_path: str, max_process_number: int = 320, export_type: Union[str, list, NoneType] = None)"
1753 },1759 },
1754 "torch_npu.profiler.profiler.profile": {1760 "torch_npu.profiler.profiler.profile": {
1755- "signature": "(*, activities: Optional[Iterable[torch_npu._C._profiler.ProfilerActivity]] = None, schedule: Optional[Callable[[int], torch_npu.profiler.scheduler.ProfilerAction]] = None, on_trace_ready: Optional[Callable[..., Any]] = None, record_shapes: bool = False, profile_memory: bool = False, with_stack: bool = False, with_flops: bool = False, with_modules: bool = False, experimental_config: Optional[torch_npu.profiler.experimental_config._ExperimentalConfig] = None, use_cuda: Optional[bool] = None)"1761+ "signature": "(*, activities: Optional[collections.abc.Iterable[torch_npu._C._profiler.ProfilerActivity]] = None, schedule: Optional[collections.abc.Callable[[int], torch_npu.profiler.scheduler.ProfilerAction]] = None, on_trace_ready: Optional[collections.abc.Callable[..., Any]] = None, record_shapes: bool = False, profile_memory: bool = False, with_stack: bool = False, with_flops: bool = False, with_modules: bool = False, experimental_config: Optional[torch_npu.profiler.experimental_config._ExperimentalConfig] = None, custom_trace_id_callback: Optional[collections.abc.Callable[[], str]] = None, use_cuda: Optional[bool] = None)"
1756 },1762 },
1757 "torch_npu.profiler.profiler.profile.start": {1763 "torch_npu.profiler.profiler.profile.start": {
1758 "signature": "(self)"1764 "signature": "(self)"
@@ -1763,6 +1769,12 @@
1763 "torch_npu.profiler.profiler.profile.step": {1769 "torch_npu.profiler.profiler.profile.step": {
1764 "signature": "(self)"1770 "signature": "(self)"
1765 },1771 },
1772+ "torch_npu.profiler.profiler.profile.get_trace_id": {
1773+ "signature": "(self) -> str"
1774+ },
1775+ "torch_npu.profiler.profiler.profile.set_custom_trace_id_callback": {
1776+ "signature": "(self, callback: collections.abc.Callable[[], str]) -> None"
1777+ },
1766 "torch_npu.profiler.profiler.profile.enable_profiler_in_child_thread": {1778 "torch_npu.profiler.profiler.profile.enable_profiler_in_child_thread": {
1767 "signature": "(record_shapes: bool = False, profile_memory: bool = False, with_stack: bool = False, with_flops: bool = False, with_modules: bool = False)"1779 "signature": "(record_shapes: bool = False, profile_memory: bool = False, with_stack: bool = False, with_flops: bool = False, with_modules: bool = False)"
1768 },1780 },
@@ -1773,7 +1785,7 @@
1773 "signature": "()"1785 "signature": "()"
1774 },1786 },
1775 "torch_npu.profiler.profiler.tensorboard_trace_handler": {1787 "torch_npu.profiler.profiler.tensorboard_trace_handler": {
1776- "signature": "(dir_name: str = None, worker_name: str = None, analyse_flag: bool = True, async_mode: bool = False)"1788+ "signature": "(dir_name: Optional[str] = None, worker_name: Optional[str] = None, analyse_flag: bool = True, async_mode: bool = False)"
1777 },1789 },
1778 "torch_npu.profiler.profiler_interface.supported_activities": {1790 "torch_npu.profiler.profiler_interface.supported_activities": {
1779 "signature": "()"1791 "signature": "()"
@@ -1782,7 +1794,7 @@
1782 "signature": "(value, names=None, *, module=None, qualname=None, type=None, start=1)"1794 "signature": "(value, names=None, *, module=None, qualname=None, type=None, start=1)"
1783 },1795 },
1784 "torch_npu.profiler.scheduler.Schedule": {1796 "torch_npu.profiler.scheduler.Schedule": {
1785- "signature": "(wait: int, active: int, warmup: int = 0, repeat: int = 0, skip_first: int = 0) -> None"1797+ "signature": "(wait: int, active: int, warmup: int = 0, repeat: int = 0, skip_first: int = 0, skip_first_wait: int = 0) -> None"
1786 },1798 },
1787 "torch_npu.testing.common_distributed.TestSkip": {1799 "torch_npu.testing.common_distributed.TestSkip": {
1788 "signature": "(exit_code, message)"1800 "signature": "(exit_code, message)"
@@ -1,50 +1,48 @@
1-import os.path1+# ruff: noqa: UP045, UP007
2import json2import json
3+import os.path
4+from collections.abc import Callable, Iterable
3from sys import getsizeof5from sys import getsizeof
4-from typing import Optional, Iterable, Callable, Any, Union6+from typing import Any, Optional, Union
5 7 
6import torch.autograd.profiler as prof8import torch.autograd.profiler as prof
7import torch_npu.npu9import torch_npu.npu
8from torch_npu._C._profiler import (10from torch_npu._C._profiler import (
9- _enable_profiler_in_child_thread,
10 _disable_profiler_in_child_thread,11 _disable_profiler_in_child_thread,
12+ _enable_profiler_in_child_thread,
11 _ExperimentalConfig as C_ExperimentalConfig,13 _ExperimentalConfig as C_ExperimentalConfig,
14+ NpuProfilerConfig,
12 ProfilerActivity,15 ProfilerActivity,
13- NpuProfilerConfig
14)16)
15from torch_npu.utils._error_code import ErrCode, prof_error17from torch_npu.utils._error_code import ErrCode, prof_error
16 18 
17-from .experimental_config import _ExperimentalConfig
18-from ._profiler_path_creator import ProfPathCreator
19-from .profiler_interface import _ProfInterface, supported_activities
20-from ._profiler_action_controller import ProfActionController
21-from .scheduler import _default_schedule_fn, ProfilerAction
22-from .analysis.prof_common_func._constant import Constant
23-from .analysis.prof_common_func._constant import print_warn_msg
24-from .analysis.prof_common_func._utils import no_exception_func
25-from .analysis._npu_profiler import NpuProfiler
26-from .analysis.prof_common_func._path_manager import ProfilerPathManager
27from ..utils._path_manager import PathManager19from ..utils._path_manager import PathManager
20+from ._profiler_action_controller import ProfActionController
21+from ._profiler_path_creator import ProfPathCreator
22+from .analysis._npu_profiler import NpuProfiler
23+from .analysis.prof_common_func._constant import Constant, print_warn_msg
24+from .analysis.prof_common_func._path_manager import ProfilerPathManager
25+from .analysis.prof_common_func._utils import no_exception_func
26+from .experimental_config import _ExperimentalConfig
27+from .profiler_interface import _ProfInterface, supported_activities
28+from .scheduler import _default_schedule_fn, ProfilerAction
28 29 
29-__all__ = [30+ 
30- 'supported_activities',31+__all__ = ["supported_activities", "analyse", "tensorboard_trace_handler", "profile"]
31- 'analyse',
32- 'tensorboard_trace_handler',
33- 'profile'
34-]
35 32 
36 33 
37class _KinetoProfile:34class _KinetoProfile:
38 def __init__(35 def __init__(
39- self,36+ self,
40- *,37+ *,
41- activities: Optional[Iterable[ProfilerActivity]] = None,38+ activities: Optional[Iterable[ProfilerActivity]] = None,
42- record_shapes: bool = False,39+ record_shapes: bool = False,
43- profile_memory: bool = False,40+ profile_memory: bool = False,
44- with_stack: bool = False,41+ with_stack: bool = False,
45- with_flops: bool = False,42+ with_flops: bool = False,
46- with_modules: bool = False,43+ with_modules: bool = False,
47- experimental_config: Optional[_ExperimentalConfig] = None,44+ experimental_config: Optional[_ExperimentalConfig] = None,
45+ custom_trace_id_callback: Optional[Callable[[], str]] = None,
48 ):46 ):
49 self.metadata = {}47 self.metadata = {}
50 self.prof_if = _ProfInterface(48 self.prof_if = _ProfInterface(
@@ -55,7 +53,8 @@ class _KinetoProfile:
55 with_flops=with_flops,53 with_flops=with_flops,
56 with_modules=with_modules,54 with_modules=with_modules,
57 experimental_config=experimental_config,55 experimental_config=experimental_config,
58- metadata=self.metadata56+ metadata=self.metadata,
57+ custom_trace_id_callback=custom_trace_id_callback,
59 )58 )
60 self.max_meta_size = 50 * 102459 self.max_meta_size = 50 * 1024
61 self.max_str_len = 409660 self.max_str_len = 4096
@@ -81,7 +80,10 @@ class _KinetoProfile:
81 PathManager.check_input_file_path(output_path)80 PathManager.check_input_file_path(output_path)
82 file_name = os.path.basename(output_path)81 file_name = os.path.basename(output_path)
83 if not file_name.endswith(".json"):82 if not file_name.endswith(".json"):
84- raise RuntimeError("Invalid parameter output_path, which must be a json file." + prof_error(ErrCode.VALUE))83+ raise RuntimeError(
84+ "Invalid parameter output_path, which must be a json file."
85+ + prof_error(ErrCode.VALUE)
86+ )
85 if not self.prof_if.prof_path:87 if not self.prof_if.prof_path:
86 print_warn_msg("Invalid profiling path.")88 print_warn_msg("Invalid profiling path.")
87 return89 return
@@ -90,14 +92,16 @@ class _KinetoProfile:
90 @no_exception_func()92 @no_exception_func()
91 def add_metadata(self, key: str, value: str):93 def add_metadata(self, key: str, value: str):
92 if not isinstance(key, str) or not isinstance(value, str):94 if not isinstance(key, str) or not isinstance(value, str):
93- print_warn_msg("The key and value of metadata must be string. Skip this metadata.")95+ print_warn_msg(
96+ "The key and value of metadata must be string. Skip this metadata."
97+ )
94 return98 return
95 if not self._check_str_valid(key) or not self._check_str_valid(value):99 if not self._check_str_valid(key) or not self._check_str_valid(value):
96 print_warn_msg("Invalid input key or value. Skip this metadata.")100 print_warn_msg("Invalid input key or value. Skip this metadata.")
97 return101 return
98 add_size = getsizeof(key) + getsizeof(value)102 add_size = getsizeof(key) + getsizeof(value)
99 if getsizeof(self.metadata) + add_size < self.max_meta_size:103 if getsizeof(self.metadata) + add_size < self.max_meta_size:
100- if key in self.metadata.keys():104+ if key in self.metadata:
101 print_warn_msg(f"{key} is already saved as metadata, override it.")105 print_warn_msg(f"{key} is already saved as metadata, override it.")
102 self.metadata[key] = value106 self.metadata[key] = value
103 else:107 else:
@@ -106,7 +110,9 @@ class _KinetoProfile:
106 @no_exception_func()110 @no_exception_func()
107 def add_metadata_json(self, key: str, value: str):111 def add_metadata_json(self, key: str, value: str):
108 if not isinstance(key, str) or not isinstance(value, str):112 if not isinstance(key, str) or not isinstance(value, str):
109- print_warn_msg("The key and value of metadata must be string. Skip this metadata.")113+ print_warn_msg(
114+ "The key and value of metadata must be string. Skip this metadata."
115+ )
110 return116 return
111 if not self._check_str_valid(key) or not self._check_str_valid(value):117 if not self._check_str_valid(key) or not self._check_str_valid(value):
112 print_warn_msg("Invalid input key or value. Skip this metadata.")118 print_warn_msg("Invalid input key or value. Skip this metadata.")
@@ -114,11 +120,13 @@ class _KinetoProfile:
114 add_size = getsizeof(key) + getsizeof(value)120 add_size = getsizeof(key) + getsizeof(value)
115 if getsizeof(self.metadata) + add_size < self.max_meta_size:121 if getsizeof(self.metadata) + add_size < self.max_meta_size:
116 try:122 try:
117- if key in self.metadata.keys():123+ if key in self.metadata:
118 print_warn_msg(f"{key} is already saved as metadata, override it.")124 print_warn_msg(f"{key} is already saved as metadata, override it.")
119 self.metadata[key] = json.loads(value)125 self.metadata[key] = json.loads(value)
120 except ValueError:126 except ValueError:
121- print_warn_msg("The metadata value must be json format string. Skip this metadata")127+ print_warn_msg(
128+ "The metadata value must be json format string. Skip this metadata"
129+ )
122 else:130 else:
123 print_warn_msg("Too many metadata added. Skip this metadata")131 print_warn_msg("Too many metadata added. Skip this metadata")
124 132 
@@ -130,8 +138,10 @@ class _KinetoProfile:
130 print_warn_msg("Function export_stacks() requires with_stack=True.")138 print_warn_msg("Function export_stacks() requires with_stack=True.")
131 return139 return
132 if metric not in self._support_export_stacks_metrics():140 if metric not in self._support_export_stacks_metrics():
133- print_warn_msg("Metric should be self_cpu_time_total or self_npu_time_total."141+ print_warn_msg(
134- "Here it is presumed to be self_cpu_time_total.")142+ "Metric should be self_cpu_time_total or self_npu_time_total."
143+ "Here it is presumed to be self_cpu_time_total."
144+ )
135 metric = Constant.METRIC_CPU_TIME145 metric = Constant.METRIC_CPU_TIME
136 if not self.prof_if.prof_path:146 if not self.prof_if.prof_path:
137 print_warn_msg("Invalid profiling path.")147 print_warn_msg("Invalid profiling path.")
@@ -139,7 +149,9 @@ class _KinetoProfile:
139 self.prof_if.analyse(Constant.EXPORT_STACK, output_path, metric=metric)149 self.prof_if.analyse(Constant.EXPORT_STACK, output_path, metric=metric)
140 150 
141 @no_exception_func()151 @no_exception_func()
142- def export_memory_timeline(self, output_path: str, device: Optional[str] = None) -> None:152+ def export_memory_timeline(
153+ self, output_path: str, device: Optional[str] = None
154+ ) -> None:
143 if device is None:155 if device is None:
144 device = "npu:0" if torch_npu.npu.is_available() else "cpu"156 device = "npu:0" if torch_npu.npu.is_available() else "cpu"
145 missing = []157 missing = []
@@ -156,7 +168,9 @@ class _KinetoProfile:
156 if not self.prof_if.prof_path:168 if not self.prof_if.prof_path:
157 print_warn_msg("Invalid profiling path.")169 print_warn_msg("Invalid profiling path.")
158 return170 return
159- self.prof_if.analyse(Constant.EXPORT_MEMORY_TIMELINE, output_path, device=device)171+ self.prof_if.analyse(
172+ Constant.EXPORT_MEMORY_TIMELINE, output_path, device=device
173+ )
160 174 
161 def _check_str_valid(self, input_str: str):175 def _check_str_valid(self, input_str: str):
162 if len(input_str) > self.max_str_len:176 if len(input_str) > self.max_str_len:
@@ -168,8 +182,12 @@ class _KinetoProfile:
168 182 
169 183 
170@no_exception_func()184@no_exception_func()
171-def tensorboard_trace_handler(dir_name: str = None, worker_name: str = None,185+def tensorboard_trace_handler(
172- analyse_flag: bool = True, async_mode: bool = False):186+ dir_name: Optional[str] = None,
187+ worker_name: Optional[str] = None,
188+ analyse_flag: bool = True,
189+ async_mode: bool = False,
190+):
173 ProfPathCreator().init(worker_name=worker_name, dir_name=dir_name)191 ProfPathCreator().init(worker_name=worker_name, dir_name=dir_name)
174 if not isinstance(analyse_flag, bool):192 if not isinstance(analyse_flag, bool):
175 print_warn_msg("analyse_flag is not bool, set by default.")193 print_warn_msg("analyse_flag is not bool, set by default.")
@@ -187,27 +205,30 @@ def tensorboard_trace_handler(dir_name: str = None, worker_name: str = None,
187 205 
188class profile(_KinetoProfile):206class profile(_KinetoProfile):
189 def __init__(207 def __init__(
190- self,208+ self,
191- *,209+ *,
192- activities: Optional[Iterable[ProfilerActivity]] = None,210+ activities: Optional[Iterable[ProfilerActivity]] = None,
193- schedule: Optional[Callable[[int], ProfilerAction]] = None,211+ schedule: Optional[Callable[[int], ProfilerAction]] = None,
194- on_trace_ready: Optional[Callable[..., Any]] = None,212+ on_trace_ready: Optional[Callable[..., Any]] = None,
195- record_shapes: bool = False,213+ record_shapes: bool = False,
196- profile_memory: bool = False,214+ profile_memory: bool = False,
197- with_stack: bool = False,215+ with_stack: bool = False,
198- with_flops: bool = False,216+ with_flops: bool = False,
199- with_modules: bool = False,217+ with_modules: bool = False,
200- experimental_config: Optional[_ExperimentalConfig] = None,218+ experimental_config: Optional[_ExperimentalConfig] = None,
201- # deprecated:219+ custom_trace_id_callback: Optional[Callable[[], str]] = None,
202- use_cuda: Optional[bool] = None,220+ # deprecated:
221+ use_cuda: Optional[bool] = None,
203 ):222 ):
204- super().__init__(activities=activities,223+ super().__init__(
205- record_shapes=record_shapes,224+ activities=activities,
206- profile_memory=profile_memory,225+ record_shapes=record_shapes,
207- with_stack=with_stack,226+ profile_memory=profile_memory,
208- with_flops=with_flops,227+ with_stack=with_stack,
209- with_modules=with_modules,228+ with_flops=with_flops,
210- experimental_config=experimental_config)229+ with_modules=with_modules,
230+ experimental_config=experimental_config,
231+ )
211 activities_set = set(activities) if activities else supported_activities()232 activities_set = set(activities) if activities else supported_activities()
212 if schedule and isinstance(schedule, Callable):233 if schedule and isinstance(schedule, Callable):
213 self.schedule = schedule234 self.schedule = schedule
@@ -230,7 +251,8 @@ class profile(_KinetoProfile):
230 with_modules=with_modules,251 with_modules=with_modules,
231 experimental_config=experimental_config,252 experimental_config=experimental_config,
232 schedule=self.schedule,253 schedule=self.schedule,
233- metadata=self.metadata254+ metadata=self.metadata,
255+ custom_trace_id_callback=custom_trace_id_callback,
234 )256 )
235 self.on_trace_ready = on_trace_ready257 self.on_trace_ready = on_trace_ready
236 self.step_num = 0258 self.step_num = 0
@@ -240,7 +262,9 @@ class profile(_KinetoProfile):
240 if use_cuda is not None:262 if use_cuda is not None:
241 print_warn_msg("This is npu environment, use_cuda is invalid")263 print_warn_msg("This is npu environment, use_cuda is invalid")
242 self.stopped = False264 self.stopped = False
243- self.action_controller = ProfActionController(self, self.prof_if, self.on_trace_ready)265+ self.action_controller = ProfActionController(
266+ self, self.prof_if, self.on_trace_ready
267+ )
244 268 
245 @no_exception_func()269 @no_exception_func()
246 def __enter__(self):270 def __enter__(self):
@@ -253,7 +277,7 @@ class profile(_KinetoProfile):
253 277 
254 @no_exception_func()278 @no_exception_func()
255 def __del__(self):279 def __del__(self):
256- if self.stopped == False:280+ if not self.stopped:
257 self.stop()281 self.stop()
258 282 
259 @no_exception_func()283 @no_exception_func()
@@ -267,7 +291,9 @@ class profile(_KinetoProfile):
267 ProfPathCreator().init(export_only_mode=True)291 ProfPathCreator().init(export_only_mode=True)
268 self.action_controller.transit_action(ProfilerAction.NONE, self.current_action)292 self.action_controller.transit_action(ProfilerAction.NONE, self.current_action)
269 if self.record_steps:293 if self.record_steps:
270- self.step_rec_fn = prof.record_function("ProfilerStep#" + str(self.step_num + self._step_num_offset))294+ self.step_rec_fn = prof.record_function(
295+ "ProfilerStep#" + str(self.step_num + self._step_num_offset)
296+ )
271 self.step_rec_fn.__enter__()297 self.step_rec_fn.__enter__()
272 298 
273 @no_exception_func()299 @no_exception_func()
@@ -289,31 +315,51 @@ class profile(_KinetoProfile):
289 self.current_action = self.schedule(self.step_num)315 self.current_action = self.schedule(self.step_num)
290 self.action_controller.transit_action(prev_action, self.current_action)316 self.action_controller.transit_action(prev_action, self.current_action)
291 if self.record_steps:317 if self.record_steps:
292- self.step_rec_fn = prof.record_function("ProfilerStep#" + str(self.step_num + self._step_num_offset))318+ self.step_rec_fn = prof.record_function(
319+ "ProfilerStep#" + str(self.step_num + self._step_num_offset)
320+ )
293 self.step_rec_fn.__enter__()321 self.step_rec_fn.__enter__()
294 322 
323+ @no_exception_func()
324+ def set_custom_trace_id_callback(self, callback: Callable[[], str]) -> None:
atomgit-bot
atomgit-botatomgit-bot4月24日

🚨 set_custom_trace_id_callback 不会更新已生成的 trace_id

trace_idinit_trace() 调用时生成一次。如果用户后续通过 set_custom_trace_id_callback() 设置新的 callback,trace_id 不会重新生成,导致 get_trace_id() 返回的仍是旧值。这违反了用户预期:设置 callback 后,后续获取的 trace_id 应该由新 callback 生成。

参考依据

  • 类别: bug
  • 置信度: 90%
likedislike
xfeng
xfeng
4月24日 评论:
325+ self.prof_if.custom_trace_id_callback = callback
326+ 
327+ @no_exception_func()
328+ def get_trace_id(self) -> str:
329+ return self.prof_if.trace_id
330+ 
295 @classmethod331 @classmethod
296 @no_exception_func()332 @no_exception_func()
297- def enable_profiler_in_child_thread(cls,333+ def enable_profiler_in_child_thread(
298- record_shapes: bool = False,334+ cls,
299- profile_memory: bool = False,335+ record_shapes: bool = False,
300- with_stack: bool = False,336+ profile_memory: bool = False,
301- with_flops: bool = False,337+ with_stack: bool = False,
302- with_modules: bool = False):338+ with_flops: bool = False,
339+ with_modules: bool = False,
340+ ):
303 params = {341 params = {
304- 'record_shapes': record_shapes,342+ "record_shapes": record_shapes,
305- 'profile_memory': profile_memory,343+ "profile_memory": profile_memory,
306- 'with_stack': with_stack,344+ "with_stack": with_stack,
307- 'with_flops': with_flops,345+ "with_flops": with_flops,
308- 'with_modules': with_modules346+ "with_modules": with_modules,
309 }347 }
310 for param_name, param_value in params.items():348 for param_name, param_value in params.items():
311 if not isinstance(param_value, bool):349 if not isinstance(param_value, bool):
312- print_warn_msg(f"{param_name} in enable_profiler_in_child_thread is not bool, reset it to False.")350+ print_warn_msg(
351+ f"{param_name} in enable_profiler_in_child_thread is not bool, reset it to False."
352+ )
313 params[param_name] = False353 params[param_name] = False
314- npu_prof_config = NpuProfilerConfig('', params['record_shapes'], params['profile_memory'], 354+ npu_prof_config = NpuProfilerConfig(
315- params['with_stack'], params['with_flops'], params['with_modules'], 355+ "",
316- C_ExperimentalConfig())356+ params["record_shapes"],
357+ params["profile_memory"],
358+ params["with_stack"],
359+ params["with_flops"],
360+ params["with_modules"],
361+ C_ExperimentalConfig(),
362+ )
317 _enable_profiler_in_child_thread(npu_prof_config)363 _enable_profiler_in_child_thread(npu_prof_config)
318 364 
319 @classmethod365 @classmethod
@@ -324,24 +370,37 @@ class profile(_KinetoProfile):
324 370 
325 371 
326@no_exception_func()372@no_exception_func()
327-def analyse(profiler_path: str, max_process_number: int = Constant.DEFAULT_PROCESS_NUMBER,373+def analyse(
328- export_type: Union[str, list] = None):374+ profiler_path: str,
375+ max_process_number: int = Constant.DEFAULT_PROCESS_NUMBER,
376+ export_type: Optional[Union[str, list]] = None,
377+):
329 if not isinstance(max_process_number, int) or max_process_number <= 0:378 if not isinstance(max_process_number, int) or max_process_number <= 0:
330 max_process_number = Constant.DEFAULT_PROCESS_NUMBER379 max_process_number = Constant.DEFAULT_PROCESS_NUMBER
331 print_warn_msg("Invalid max_process_number, reset it to default!")380 print_warn_msg("Invalid max_process_number, reset it to default!")
332 if max_process_number > os.cpu_count():381 if max_process_number > os.cpu_count():
333 max_process_number = os.cpu_count()382 max_process_number = os.cpu_count()
334- print_warn_msg("max_process_number exceeds the number of cpu cores, reset it to the number of cpu cores!")383+ print_warn_msg(
384+ "max_process_number exceeds the number of cpu cores, reset it to the number of cpu cores!"
385+ )
335 if export_type is not None:386 if export_type is not None:
336 if isinstance(export_type, str):387 if isinstance(export_type, str):
337 export_type = [export_type]388 export_type = [export_type]
338 elif isinstance(export_type, list):389 elif isinstance(export_type, list):
339 export_type = list(set(export_type))390 export_type = list(set(export_type))
340 else:391 else:
341- print_warn_msg(f"Invalid parameter export_type: {export_type}, reset it to None.")392+ print_warn_msg(
393+ f"Invalid parameter export_type: {export_type}, reset it to None."
394+ )
342 export_type = None395 export_type = None
343 if export_type is not None:396 if export_type is not None:
344- if not export_type or not all(_type in [Constant.Text, Constant.Db] for _type in export_type):397+ if not export_type or not all(
345- print_warn_msg(f"Invalid parameter export_type: {export_type}, reset it to None.")398+ _type in [Constant.Text, Constant.Db] for _type in export_type
399+ ):
400+ print_warn_msg(
401+ f"Invalid parameter export_type: {export_type}, reset it to None."
402+ )
346 export_type = None403 export_type = None
347- NpuProfiler.analyse(profiler_path, max_process_number=max_process_number, export_type=export_type)404+ NpuProfiler.analyse(
405+ profiler_path, max_process_number=max_process_number, export_type=export_type
406+ )
@@ -2,6 +2,7 @@
2import functools2import functools
3import os3import os
4import time4import time
5+import uuid
5from collections.abc import Callable, Iterable6from collections.abc import Callable, Iterable
6from typing import Optional7from typing import Optional
7 8 
@@ -70,18 +71,21 @@ def _disable_event_record():
70 71 
71class _ProfInterface:72class _ProfInterface:
72 PARALLEL_GROUP_KEY = "parallel_group_info"73 PARALLEL_GROUP_KEY = "parallel_group_info"
74+ TRACE_ID_KEY = "trace_id"
75+ MAX_TRACE_ID_LEN = 1024
73 76 
74 def __init__(77 def __init__(
75 self,78 self,
76- activities: Iterable[ProfilerActivity] | None = None,79+ activities: Optional[Iterable[ProfilerActivity]] = None,
77 record_shapes: bool = False,80 record_shapes: bool = False,
78 profile_memory: bool = False,81 profile_memory: bool = False,
79 with_stack: bool = False,82 with_stack: bool = False,
80 with_flops: bool = False,83 with_flops: bool = False,
81 with_modules: bool = False,84 with_modules: bool = False,
82- schedule: Callable[[int], ProfilerAction] | None = None,85+ schedule: Optional[Callable[[int], ProfilerAction]] = None,
83 metadata: Optional[dict] = None,86 metadata: Optional[dict] = None,
84- experimental_config: _ExperimentalConfig | None = None,87+ experimental_config: Optional[_ExperimentalConfig] = None,
88+ custom_trace_id_callback: Optional[Callable[[], str]] = None,
85 ) -> None:89 ) -> None:
86 self._is_env_valid = check_msprof_env()90 self._is_env_valid = check_msprof_env()
87 self.prof_path = ""91 self.prof_path = ""
@@ -100,6 +104,8 @@ class _ProfInterface:
100 self.experimental_config = experimental_config104 self.experimental_config = experimental_config
101 self.schedule = schedule105 self.schedule = schedule
102 self.metadata = metadata106 self.metadata = metadata
107+ self.custom_trace_id_callback = custom_trace_id_callback
108+ self.trace_id = ""
103 self.gc_detector = None109 self.gc_detector = None
104 self._check_params()110 self._check_params()
105 111 
@@ -112,6 +118,7 @@ class _ProfInterface:
112 ProfPathCreator().create_prof_dir()118 ProfPathCreator().create_prof_dir()
113 self.prof_path = ProfPathCreator().get_prof_dir()119 self.prof_path = ProfPathCreator().get_prof_dir()
114 _init_profiler(self.prof_path, self.activities)120 _init_profiler(self.prof_path, self.activities)
121+ self.trace_id = self.create_trace_id()
115 122 
116 def warmup_trace(self):123 def warmup_trace(self):
117 if not self._is_env_valid:124 if not self._is_env_valid:
@@ -201,6 +208,32 @@ class _ProfInterface:
201 self.gc_detector.stop()208 self.gc_detector.stop()
202 self.gc_detector = None209 self.gc_detector = None
203 210 
211+ def default_trace_id(self):
212+ # Generate a UUID
213+ uuid_raw = uuid.uuid4()
214+ return f"{uuid_raw.int:032X}"
215+ 
216+ def create_trace_id(self):
217+ if not self.custom_trace_id_callback:
218+ return self.default_trace_id()
219+ if not isinstance(self.custom_trace_id_callback, Callable):
220+ print_warn_msg(
221+ "Parameter custom_trace_id_callback is not callable, reset it to default."
222+ )
223+ return self.default_trace_id()
224+ try:
225+ trace_id = self.custom_trace_id_callback()
226+ if isinstance(trace_id, str) and len(trace_id) <= self.MAX_TRACE_ID_LEN:
227+ return trace_id
228+ print_warn_msg(
229+ f"Parameter custom_trace_id_callback should return str(max length: {self.MAX_TRACE_ID_LEN}), reset it to default."
230+ )
231+ except Exception as e:
232+ print_warn_msg(
233+ f"Parameter custom_trace_id_callback raised an exception: {e}, reset it to default."
234+ )
235+ return self.default_trace_id()
236+ 
atomgit-bot
atomgit-botatomgit-bot4月24日

⚠️ custom_trace_id_callback 返回非字符串值或抛异常时缺乏处理(同一段代码另有1个问题)

create_trace_id() 调用 custom_trace_id_callback() 时,如果 callback 返回非字符串类型(如 None、int)或抛出异常,代码没有防护。这可能导致后续 _add_trace_id_to_metadata() 写入无效数据或程序崩溃。

参考依据

  • 类别: general
  • 置信度: 80%
likedislike
xfeng
xfeng
4月24日 评论:
204 def _check_params(self):237 def _check_params(self):
205 for activity in self.activities:238 for activity in self.activities:
206 if activity in supported_activities():239 if activity in supported_activities():
@@ -319,6 +352,7 @@ class _ProfInterface:
319 if Constant.Text in self.experimental_config.export_type:352 if Constant.Text in self.experimental_config.export_type:
320 self.metadata.update(collect_env_vars())353 self.metadata.update(collect_env_vars())
321 self._add_group_info_to_metadata()354 self._add_group_info_to_metadata()
355+ self._add_trace_id_to_metadata()
322 if not self.metadata:356 if not self.metadata:
323 return357 return
324 if not ProfPathCreator().is_prof_inited:358 if not ProfPathCreator().is_prof_inited:
@@ -372,6 +406,9 @@ class _ProfInterface:
372 except Exception as err:406 except Exception as err:
373 print_warn_msg(f"Failed to get parallel group info, Exception: {str(err)}.")407 print_warn_msg(f"Failed to get parallel group info, Exception: {str(err)}.")
374 408 
409+ def _add_trace_id_to_metadata(self):
410+ self.metadata.update({self.TRACE_ID_KEY: self.trace_id})
411+ 
375 412 
376@no_exception_func(set())413@no_exception_func(set())
377def supported_activities():414def supported_activities():
@@ -1,12 +1,11 @@
1from enum import Enum1from enum import Enum
2 2 
3from torch_npu.utils._error_code import ErrCode, prof_error3from torch_npu.utils._error_code import ErrCode, prof_error
4+ 
4from .analysis.prof_common_func._constant import print_warn_msg5from .analysis.prof_common_func._constant import print_warn_msg
5 6 
6-__all__ = [7+ 
7- 'ProfilerAction',8+__all__ = ["ProfilerAction", "Schedule"]
8- 'Schedule'
9-]
10 9 
11 10 
12class ProfilerAction(Enum):11class ProfilerAction(Enum):
@@ -23,22 +22,44 @@ class Schedule:
23 ``active`` steps and then repeat the cycle starting with ``wait`` steps. The optional number22 ``active`` steps and then repeat the cycle starting with ``wait`` steps. The optional number
24 of cycles is specified with the ``repeat`` parameter, the zero value means that23 of cycles is specified with the ``repeat`` parameter, the zero value means that
25 the cycles will continue until the profiling is finished.24 the cycles will continue until the profiling is finished.
25+ 
26+ The ``skip_first_wait`` parameter controls whether the first ``wait`` stage should be skipped.
27+ This can be useful if a user wants to wait longer than ``skip_first`` between cycles, but not
28+ for the first profile. For example, if ``skip_first`` is 10 and ``wait`` is 20, the first cycle will
29+ wait 10 + 20 = 30 steps before warmup if ``skip_first_wait`` is zero, but will wait only 10
30+ steps if ``skip_first_wait`` is non-zero. All subsequent cycles will then wait 20 steps between the
31+ last active and warmup.
26 """32 """
27- def __init__(self, wait: int, active: int, warmup: int = 0, repeat: int = 0, skip_first: int = 0) -> None:33+ 
34+ def __init__(
35+ self,
36+ wait: int,
37+ active: int,
38+ warmup: int = 0,
39+ repeat: int = 0,
40+ skip_first: int = 0,
41+ skip_first_wait: int = 0,
42+ ) -> None:
28 self.wait = wait43 self.wait = wait
29 self.active = active44 self.active = active
30 self.warmup = warmup45 self.warmup = warmup
31 self.repeat = repeat46 self.repeat = repeat
32 self.skip_first = skip_first47 self.skip_first = skip_first
48+ self.skip_first_wait = skip_first_wait
33 self._check_params()49 self._check_params()
34 50 
35 def __call__(self, step: int) -> ProfilerAction:51 def __call__(self, step: int) -> ProfilerAction:
36 if step < 0:52 if step < 0:
37- raise ValueError("Invalid parameter step, which must be not less than 0." + prof_error(ErrCode.VALUE))53+ raise ValueError(
54+ "Invalid parameter step, which must be not less than 0."
55+ + prof_error(ErrCode.VALUE)
56+ )
38 if step < self.skip_first:57 if step < self.skip_first:
39 return ProfilerAction.NONE58 return ProfilerAction.NONE
40 else:59 else:
41 step -= self.skip_first60 step -= self.skip_first
61+ if self.skip_first_wait != 0:
62+ step += self.wait
42 num_steps = self.wait + self.warmup + self.active63 num_steps = self.wait + self.warmup + self.active
43 if self.repeat > 0 and step / num_steps >= self.repeat:64 if self.repeat > 0 and step / num_steps >= self.repeat:
44 return ProfilerAction.NONE65 return ProfilerAction.NONE
@@ -70,9 +91,14 @@ class Schedule:
70 if not isinstance(self.skip_first, int) or self.skip_first < 0:91 if not isinstance(self.skip_first, int) or self.skip_first < 0:
71 print_warn_msg("Invalid parameter skip_first, reset it to 0.")92 print_warn_msg("Invalid parameter skip_first, reset it to 0.")
72 self.skip_first = 093 self.skip_first = 0
94+ if not isinstance(self.skip_first_wait, int):
95+ print_warn_msg("Invalid parameter skip_first_wait, reset it to 0.")
96+ self.skip_first_wait = 0
73 97 
74 if self.warmup == 0:98 if self.warmup == 0:
75- print_warn_msg("Profiler won't be using warmup, this can skew profiler results")99+ print_warn_msg(
100+ "Profiler won't be using warmup, this can skew profiler results"
101+ )
76 102 
77 103 
78def _default_schedule_fn(_: int) -> ProfilerAction:104def _default_schedule_fn(_: int) -> ProfilerAction: