2828# Log scan: optional activation (Megatron prints TORCH_FR_DUMP_TEMP_FILE=...)
2929# ---------------------------------------------------------------------------
3030
31- FR_DUMP_PATH_LOG_LINE_PATTERN = re .compile (r"TORCH_FR_DUMP_TEMP_FILE=(\S+)" )
31+ FR_DUMP_PATH_ENV = "TORCH_FR_DUMP_TEMP_FILE"
32+ FR_DUMP_PATH_LOG_LINE_PATTERN = re .compile (rf"{ FR_DUMP_PATH_ENV } =(\S+)" )
3233FR_DUMP_PATH_LOG_SCAN_LINES = 1000
3334
3435
@@ -57,44 +58,111 @@ def fr_path_resolvable_for_collective_analyzer(fr_path: str) -> bool:
5758 return _fr_traces_exist_for_prefix (fr_path )
5859
5960
61+ def _validated_torch_fr_dump_prefix (
62+ prefix : str ,
63+ * ,
64+ allowed_root : Optional [str ] = None ,
65+ source : str = FR_DUMP_PATH_ENV ,
66+ ) -> Optional [str ]:
67+ """Return ``prefix`` when it is a usable ``TORCH_FR_DUMP_TEMP_FILE`` path prefix."""
68+ prefix = prefix .strip ().strip ("'\" " )
69+ if not prefix :
70+ return None
71+ if os .path .isdir (prefix ):
72+ logger .warning (
73+ "%s=%r is a directory, not a path prefix; env var is misconfigured — "
74+ "skipping FR analysis" ,
75+ source ,
76+ prefix ,
77+ )
78+ return None
79+ if not _fr_traces_exist_for_prefix (prefix ):
80+ logger .debug (
81+ "%s prefix %r has no matching trace files; skipping FR analysis" ,
82+ source ,
83+ prefix ,
84+ )
85+ return None
86+ if allowed_root is not None and not path_is_under_allowed_root (prefix , allowed_root ):
87+ logger .warning (
88+ "%s prefix %r is outside allowed_root %r; skipping FR analysis" ,
89+ source ,
90+ prefix ,
91+ allowed_root ,
92+ )
93+ return None
94+ return prefix
95+
96+
97+ def _valid_checkpoints_dir (
98+ directory : str ,
99+ * ,
100+ allowed_root : Optional [str ] = None ,
101+ source : str ,
102+ ) -> Optional [str ]:
103+ """Return ``directory`` when it exists, contains FR traces, and passes path policy."""
104+ if not os .path .isdir (directory ):
105+ return None
106+ if not _fr_traces_exist_in_dir (directory ):
107+ logger .debug (
108+ "%s checkpoints dir %s has no _dump_* traces; skipping candidate" ,
109+ source ,
110+ directory ,
111+ )
112+ return None
113+ if allowed_root is not None and not path_is_under_allowed_root (directory , allowed_root ):
114+ logger .warning (
115+ "%s FR checkpoints path %r is outside allowed_root %r; skipping candidate" ,
116+ source ,
117+ directory ,
118+ allowed_root ,
119+ )
120+ return None
121+ logger .debug (
122+ "FR dump path inferred from %s: checkpoints=%s" ,
123+ source ,
124+ directory ,
125+ )
126+ return directory
127+
128+
60129def _infer_checkpoints_dir_from_log_path (
61130 log_path : str , allowed_root : Optional [str ] = None
62131) -> Optional [str ]:
63- """If ``log_path`` lives under a ``.../logs/...`` tree, return sibling ``.../checkpoints`` when
64- it exists **and** contains at least one ``_dump_*`` trace file.
132+ """Infer common shared-filesystem FR dump directories from ``log_path``.
65133
66- Many training runs use ``<run>/logs/`` for Slurm step logs and ``<run>/checkpoints/`` for FR dumps
67- (``_dump_<rank>``). The log line ``TORCH_FR_DUMP_TEMP_FILE=`` often points at a container-local path
68- that attrsvc cannot read; the shared run directory is derivable from the log file path.
134+ Resolution order:
135+ 1. ``<log_dir>/checkpoints`` for flat per-cycle logs, e.g.
136+ ``/mnt/logs/test_job_cycle0.log`` -> ``/mnt/logs/checkpoints``.
137+ 2. ``<run>/checkpoints`` when ``log_path`` is under a ``.../logs/...`` tree, e.g.
138+ ``<run>/logs/slurm/job.log`` -> ``<run>/checkpoints``.
139+
140+ These shared path candidates are fallback locations when the explicit
141+ ``TORCH_FR_DUMP_TEMP_FILE`` prefix is absent or not resolvable by attrsvc.
69142 """
70143 try :
71- d = os .path .dirname (os .path .abspath (log_path ))
144+ log_dir = os .path .dirname (os .path .abspath (log_path ))
72145 except (OSError , ValueError ):
73146 return None
147+ local_checkpoints = _valid_checkpoints_dir (
148+ os .path .join (log_dir , "checkpoints" ),
149+ allowed_root = allowed_root ,
150+ source = "log directory" ,
151+ )
152+ if local_checkpoints is not None :
153+ return local_checkpoints
154+ d = log_dir
74155 while True :
75156 if os .path .basename (d ) == "logs" :
76157 run_root = os .path .dirname (d )
77158 cand = os .path .join (run_root , "checkpoints" )
78- if os .path .isdir (cand ):
79- if not _fr_traces_exist_in_dir (cand ):
80- logger .debug (
81- "Inferred checkpoints dir %s has no _dump_* traces; skipping FR analysis" ,
82- cand ,
83- )
84- return None
85- if allowed_root is not None and not path_is_under_allowed_root (cand , allowed_root ):
86- logger .warning (
87- "Inferred FR checkpoints path %r is outside allowed_root %r; skipping FR analysis" ,
88- cand ,
89- allowed_root ,
90- )
91- return None
92- logger .debug (
93- "FR dump path inferred from log layout: log_path=%s -> checkpoints=%s" ,
94- log_path ,
95- cand ,
96- )
97- return cand
159+ sibling_checkpoints = _valid_checkpoints_dir (
160+ cand ,
161+ allowed_root = allowed_root ,
162+ source = "logs sibling" ,
163+ )
164+ if sibling_checkpoints is not None :
165+ return sibling_checkpoints
98166 parent = os .path .dirname (d )
99167 if parent == d :
100168 break
@@ -122,32 +190,11 @@ def _read_torch_fr_dump_from_log(
122190 break
123191 m = FR_DUMP_PATH_LOG_LINE_PATTERN .search (line )
124192 if m :
125- prefix = m .group (1 )
126- if os .path .isdir (prefix ):
127- logger .warning (
128- "TORCH_FR_DUMP_TEMP_FILE=%r is a directory, not a path prefix; "
129- "env var is misconfigured — skipping FR analysis" ,
130- prefix ,
131- )
132- return None
133- if not _fr_traces_exist_for_prefix (prefix ):
134- logger .debug (
135- "TORCH_FR_DUMP_TEMP_FILE prefix %r has no matching trace files; "
136- "skipping FR analysis" ,
137- prefix ,
138- )
139- return None
140- if allowed_root is not None and not path_is_under_allowed_root (
141- prefix , allowed_root
142- ):
143- logger .warning (
144- "TORCH_FR_DUMP_TEMP_FILE prefix %r is outside allowed_root %r; "
145- "skipping FR analysis" ,
146- prefix ,
147- allowed_root ,
148- )
149- return None
150- return prefix
193+ return _validated_torch_fr_dump_prefix (
194+ m .group (1 ),
195+ allowed_root = allowed_root ,
196+ source = f"log { FR_DUMP_PATH_ENV } " ,
197+ )
151198 except OSError :
152199 pass
153200 return None
@@ -156,20 +203,24 @@ def _read_torch_fr_dump_from_log(
156203def extract_fr_dump_path (log_path : str , allowed_root : Optional [str ] = None ) -> Optional [str ]:
157204 """Resolve FR dump path for attrsvc.
158205
159- Prefer ``<run>/checkpoints`` (directory containing ``_dump_*`` files) when ``log_path`` is
160- under ``<run>/logs/`` (shared filesystem). Otherwise scan the log for
161- ``TORCH_FR_DUMP_TEMP_FILE=`` (a path prefix; may be container-local).
206+ Apply discovery rules in order:
207+ 1. ``TORCH_FR_DUMP_TEMP_FILE=`` scanned from the log (a path prefix).
208+ 2. ``<log_dir>/checkpoints`` for flat per-cycle logs.
209+ 3. ``<run>/checkpoints`` when ``log_path`` is under ``<run>/logs/...``.
162210
163211 When ``allowed_root`` is set, inferred directories and ``TORCH_FR_DUMP_TEMP_FILE`` prefixes must
164212 resolve under that root (same containment as log path validation); otherwise discovery returns
165213 ``None`` so log-injected paths cannot bypass the service path policy.
166214
167215 Returns ``None`` if no valid FR traces are found — analysis should not be triggered.
168216 """
217+ from_log = _read_torch_fr_dump_from_log (log_path , allowed_root = allowed_root )
218+ if from_log is not None :
219+ return from_log
169220 inferred = _infer_checkpoints_dir_from_log_path (log_path , allowed_root = allowed_root )
170221 if inferred is not None :
171222 return inferred
172- return _read_torch_fr_dump_from_log ( log_path , allowed_root = allowed_root )
223+ return None
173224
174225
175226# ---------------------------------------------------------------------------
0 commit comments