diff --git a/.gitignore b/.gitignore index a1a0c48..a89f44f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /tl_out *.log !/tests/inputs/*.log +.claude/ \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index a43b7ee..38b3239 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,6 +124,7 @@ fn add_file_output( compile_directory: &mut Vec, output_count: &mut i32, vllm_state: &vllm::VllmState, + subgraph_name: &Option, ) { let is_stack_traces = is_stack_traces_file(&filename); let maybe_content = if is_stack_traces { @@ -158,6 +159,8 @@ fn add_file_output( number: *output_count, suffix: suffix, readable_url, + starts_group: false, + group_name: subgraph_name.clone().unwrap_or_default(), }); *output_count += 1; } @@ -240,6 +243,7 @@ fn run_parser<'t>( compile_directory, output_count, vllm_state, + &e.subgraph_name, ); } ParserOutput::GlobalFile(filename, out) => { @@ -250,6 +254,7 @@ fn run_parser<'t>( compile_directory, output_count, vllm_state, + &e.subgraph_name, ); } ParserOutput::PayloadFile(raw_filename) => { @@ -264,6 +269,7 @@ fn run_parser<'t>( compile_directory, output_count, vllm_state, + &e.subgraph_name, ); } ParserOutput::PayloadReformatFile(raw_filename, formatter) => { @@ -280,6 +286,7 @@ fn run_parser<'t>( compile_directory, output_count, vllm_state, + &e.subgraph_name, ); } Err(err) => { @@ -301,6 +308,8 @@ fn run_parser<'t>( number: *output_count, suffix: "".to_string(), readable_url: None, + starts_group: false, + group_name: e.subgraph_name.clone().unwrap_or_default(), }); *output_count += 1; } @@ -857,6 +866,7 @@ pub fn parse_path(path: &PathBuf, config: &ParseConfig) -> anyhow::Result { @@ -867,6 +877,7 @@ pub fn parse_path(path: &PathBuf, config: &ParseConfig) -> anyhow::Result { @@ -881,6 +892,7 @@ pub fn parse_path(path: &PathBuf, config: &ParseConfig) -> anyhow::Result { @@ -897,6 +909,7 @@ pub fn parse_path(path: &PathBuf, config: &ParseConfig) -> anyhow::Result { @@ -918,6 +931,8 @@ pub fn parse_path(path: &PathBuf, config: &ParseConfig) -> anyhow::Result anyhow::Result = directory .iter() .map(|(x, _)| { diff --git a/src/parsers.rs b/src/parsers.rs index 65b8493..250beb5 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -593,6 +593,8 @@ impl StructuredLogParser for CompilationMetricsParser<'_> { number: o.number.clone(), suffix: o.suffix.clone(), readable_url: o.readable_url.as_ref().map(|u| remove_prefix(u)), + starts_group: o.starts_group, + group_name: o.group_name.clone(), }) .collect(); let extra_metrics: Vec = m diff --git a/src/templates.rs b/src/templates.rs index a59e2a8..1d91986 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -195,6 +195,7 @@ Build products below:
  • {compile_directory.0}
      {{ for path_idx in compile_directory.1 }} + {{ if path_idx.starts_group }}
    • {{ if path_idx.group_name }}Subgraph Name: {path_idx.group_name}{{ endif }}
    • {{ endif }}
    • {path_idx.name}{{ if path_idx.readable_url }} (readable_html){{ endif }} {path_idx.suffix} ({path_idx.number})
    • {{ endfor }}
    diff --git a/src/types.rs b/src/types.rs index 9cb6cef..a948099 100644 --- a/src/types.rs +++ b/src/types.rs @@ -636,6 +636,10 @@ pub struct OutputFile { pub suffix: String, /// URL to a human-readable HTML version of inductor_provenance_tracking_kernel_stack_traces.json pub readable_url: Option, + #[serde(default)] + pub starts_group: bool, + #[serde(default)] + pub group_name: String, } #[derive(Debug, Serialize)] @@ -773,6 +777,7 @@ pub struct Envelope { pub rank: Option, #[serde(flatten)] pub compile_id: Option, + pub subgraph_name: Option, #[serde(default)] pub has_payload: Option, pub stack: Option, diff --git a/tests/inputs/subgraph_name.log b/tests/inputs/subgraph_name.log new file mode 100644 index 0000000..edbe36d --- /dev/null +++ b/tests/inputs/subgraph_name.log @@ -0,0 +1,6 @@ +V0429 16:46:26.856000 3930665 /torch/_logging/structured.py:28] {"artifact": {"name": "dynamo_output_graph", "encoding": "string"}, "frame_id": 0, "frame_compile_id": 0, "attempt": 0, "has_payload": "8b4a69a00ac3b2850d14ad15b66d77ed"} + test payload no subgraph +V0429 16:46:27.745000 3930665 /torch/_inductor/compile_fx.py:1313] {"artifact": {"name": "fx_graph_runnable", "encoding": "string"}, "frame_id": 0, "frame_compile_id": 0, "attempt": 0, "subgraph_name": "repeated_subgraph0", "has_payload": "dcffdffe5e74fe67eaf327b0453e379a"} + test payload subgraph A +V0429 16:46:29.604000 3930665 /torch/_inductor/compile_fx.py:1313] {"artifact": {"name": "fx_graph_runnable", "encoding": "string"}, "frame_id": 0, "frame_compile_id": 0, "attempt": 0, "subgraph_name": "partitioned_bw_subgraph_1_0", "has_payload": "13e1309b0f53ad9d570fda42091e0569"} + test payload subgraph B diff --git a/tests/integration_test.rs b/tests/integration_test.rs index a33dba9..9a1b0e2 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -3345,3 +3345,34 @@ fn test_cli_no_overwrite_fails() -> Result<(), Box> { Ok(()) } + +#[test] +fn test_subgraph_name_grouping() { + let path = Path::new("tests/inputs/subgraph_name.log").to_path_buf(); + let config = ParseConfig::default(); + let output = tlparse::parse_path(&path, &config); + assert!(output.is_ok()); + let map: HashMap = output.unwrap().into_iter().collect(); + + // All files should be flat in -_0_0_0/ (no subgraph subdirectories) + assert!(prefix_exists(&map, "-_0_0_0/dynamo_output_graph")); + assert!(prefix_exists(&map, "-_0_0_0/fx_graph_runnable")); + + // No files should be nested under subgraph subdirectories + assert!( + !map.keys() + .any(|k| k.to_str().unwrap_or("").contains("repeated_subgraph0/")), + "files should not be in subgraph subdirectories" + ); + + // Index should contain subgraph group headers + let index = &map[&PathBuf::from("index.html")]; + assert!( + index.contains("Subgraph Name: repeated_subgraph0"), + "index should contain repeated_subgraph0 group header" + ); + assert!( + index.contains("Subgraph Name: partitioned_bw_subgraph_1_0"), + "index should contain partitioned_bw_subgraph_1_0 group header" + ); +}