Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import itertools

from ._constant import Constant
from ._constant import convert_ns2us_str
from ._constant import convert_ns2us_float

__all__ = []
Expand All @@ -15,9 +16,12 @@ class TraceEventManager:
PID_OFFSET = 10
INDEX_OFFSET = 5

# Sequential counter for flow event IDs (avoids values > 2^53)
_flow_id_counter = itertools.count(1)

@classmethod
def create_x_event(cls, event: any, cat: str) -> dict:
return {"ph": "X", "name": event.name, "pid": event.pid, "tid": event.tid, "ts": convert_ns2us_str(event.ts),
return {"ph": "X", "name": event.name, "pid": event.pid, "tid": event.tid, "ts": convert_ns2us_float(event.ts),
"dur": convert_ns2us_float(event.dur), "cat": cat, "args": event.args}

@classmethod
Expand All @@ -38,18 +42,23 @@ def create_m_event(cls, pid: int, tid_dict: dict) -> list:
"args": {"sort_index": sort_index}}])
return event_list

@classmethod
def _next_flow_id(cls) -> int:
"""Return a monotonically increasing integer safe for JavaScript (under 2^53)."""
return next(cls._flow_id_counter)

@classmethod
def create_torch_to_npu_flow(cls, start_event: any, end_event: any) -> list:
flow_id = end_event.ts
flow_id = cls._next_flow_id()
return [{"ph": "s", "bp": "e", "name": "torch_to_npu", "id": flow_id, "pid": start_event.pid,
"tid": start_event.tid, "ts": convert_ns2us_str(start_event.ts), "cat": "async_npu"},
"tid": start_event.tid, "ts": convert_ns2us_float(start_event.ts), "cat": "async_npu"},
{"ph": "f", "bp": "e", "name": "torch_to_npu", "id": flow_id, "pid": end_event.pid,
"tid": end_event.tid, "ts": convert_ns2us_str(end_event.ts), "cat": "async_npu"}]
"tid": end_event.tid, "ts": convert_ns2us_float(end_event.ts), "cat": "async_npu"}]

@classmethod
def create_task_queue_flow(cls, ph: str, event: any) -> dict:
return {"ph": ph, "bp": "e", "name": "enqueue_to_dequeue", "id": event.corr_id, "pid": event.pid,
"tid": event.tid, "ts": convert_ns2us_str(event.ts), "cat": "async_task_queue"}
"tid": event.tid, "ts": convert_ns2us_float(event.ts), "cat": "async_task_queue"}

@classmethod
def create_fwd_flow(cls, event: any) -> list:
Expand All @@ -60,9 +69,9 @@ def create_fwd_flow(cls, event: any) -> list:
continue
flow_id = fwd_id
fwd_list.extend([{"ph": "s", "bp": "e", "name": "fwdbwd", "id": flow_id, "pid": node['start']['pid'],
"tid": node['start']['tid'], "ts": convert_ns2us_str(node['start']['ts']), "cat": "fwdbwd"},
"tid": node['start']['tid'], "ts": convert_ns2us_float(node['start']['ts']), "cat": "fwdbwd"},
{"ph": "f", "bp": "e", "name": "fwdbwd", "id": flow_id, "pid": node['end']['pid'],
"tid": node['end']['tid'], "ts": convert_ns2us_str(node['end']['ts']), "cat": "fwdbwd"}])
"tid": node['end']['tid'], "ts": convert_ns2us_float(node['end']['ts']), "cat": "fwdbwd"}])
return fwd_list

@classmethod
Expand Down
21 changes: 21 additions & 0 deletions torch_npu/profiler/analysis/prof_view/_trace_view_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,25 @@ def run(self, deps_data: dict):
self.logger.info("TraceViewParser finish.")
return Constant.SUCCESS, None

@staticmethod
def _normalize_timestamps(trace_data: list) -> None:
"""Convert absolute timestamps to relative (subtract min ts in-place)
and ensure ts/dur are numeric, compatible with Chrome Trace Format / Perfetto."""
if not trace_data:
return
# Find the minimum numeric ts among all events (metadata events like "M" have no ts)
min_ts = float("inf")
for event in trace_data:
ts = event.get("ts")
if ts is not None and isinstance(ts, (int, float)):
min_ts = min(min_ts, ts)
if min_ts == float("inf") or min_ts == 0:
return
for event in trace_data:
ts = event.get("ts")
if ts is not None and isinstance(ts, (int, float)):
event["ts"] = round(ts - min_ts, 3)

def generate_view(self) -> None:
if not ProfilerPathManager.get_cann_path(self._profiler_path):
self._trace_data = FwkFileParser(self._profiler_path).get_fwk_trace_data(
Expand All @@ -78,6 +97,8 @@ def generate_view(self) -> None:
self._prune_trace_by_level(msprof_timeline_data))
if self._torch_op_node:
self._trace_data.extend(self._get_flow_event(msprof_timeline_data))
# Normalize to relative timestamps for Perfetto / Chrome Trace Format compatibility
self._normalize_timestamps(self._trace_data)
if os.path.exists(self._temp_trace_file_path):
FileManager.append_trace_json_by_path(self._temp_trace_file_path, self._trace_data, self._trace_file_path)
else:
Expand Down