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
4 changes: 2 additions & 2 deletions docs/profiling.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ Stall Barrier inst 0.75

After profiling, a zip file is saved to your current directory:
```
profile_20260113_031052_run0.zip
profile_20260113_031052_result0_profile0.zip
```

This contains a `.ncu-rep` file (the full Nsight Compute report):
```
$ unzip -l profile_20260113_031052_run0.zip
$ unzip -l profile_20260113_031052_result0_profile0.zip
Length Date Time Name
--------- ---------- ----- ----
2178383 01-13-2026 03:10 profile.ncu-rep
Expand Down
82 changes: 74 additions & 8 deletions src/service/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::{anyhow, Result};
use base64::Engine;
use chrono::Utc;
use chrono::{DateTime, Utc};
use reqwest::header::{HeaderMap, HeaderValue};
use reqwest::multipart::{Form, Part};
use reqwest::Client;
Expand Down Expand Up @@ -650,7 +650,7 @@ pub async fn submit_solution<P: AsRef<Path>>(
{
for (key, run_data) in runs.iter() {
if key.starts_with("profile") {
handle_profile_result(cb, run_data, i);
handle_profile_result(cb, run_data, i, key);
}
}
}
Expand Down Expand Up @@ -754,7 +754,12 @@ pub async fn submit_solution<P: AsRef<Path>>(

/// Handle profile mode results by decoding and displaying profile data,
/// and saving trace files to the current directory.
fn handle_profile_result(cb: &(dyn Fn(String) + Send + Sync), run_data: &Value, run_idx: usize) {
fn handle_profile_result(
cb: &(dyn Fn(String) + Send + Sync),
run_data: &Value,
result_idx: usize,
run_key: &str,
) {
// 1. Get profiler type and display it
if let Some(profile) = run_data.get("profile") {
let profiler = profile
Expand Down Expand Up @@ -814,11 +819,9 @@ fn handle_profile_result(cb: &(dyn Fn(String) + Send + Sync), run_data: &Value,
if !trace_b64.is_empty() {
match base64::engine::general_purpose::STANDARD.decode(trace_b64) {
Ok(trace_data) => {
// Generate unique filename with timestamp and run index
let timestamp = Utc::now().format("%Y%m%d_%H%M%S");
let filename = format!("profile_{}_run{}.zip", timestamp, run_idx);
match std::fs::write(&filename, &trace_data) {
Ok(_) => cb(format!("\nSaved profile trace to: {}", filename)),
match write_profile_trace_file(&trace_data, Utc::now(), result_idx, run_key)
{
Ok(filename) => cb(format!("\nSaved profile trace to: {}", filename)),
Err(e) => cb(format!("Failed to save trace file: {}", e)),
}
}
Expand All @@ -836,9 +839,54 @@ fn handle_profile_result(cb: &(dyn Fn(String) + Send + Sync), run_data: &Value,
}
}

fn sanitize_profile_run_key(run_key: &str) -> String {
let sanitized: String = run_key
.chars()
.map(|ch| {
if ch.is_ascii_alphanumeric() || ch == '_' || ch == '-' {
ch
} else {
'_'
}
})
.collect();

if sanitized.is_empty() {
"profile".to_string()
} else {
sanitized
}
}

fn build_profile_trace_filename(
timestamp: DateTime<Utc>,
result_idx: usize,
run_key: &str,
) -> String {
let run_key = sanitize_profile_run_key(run_key);
format!(
"profile_{}_result{}_{}.zip",
timestamp.format("%Y%m%d_%H%M%S"),
result_idx,
run_key
)
}

fn write_profile_trace_file(
trace_data: &[u8],
timestamp: DateTime<Utc>,
result_idx: usize,
run_key: &str,
) -> std::io::Result<String> {
let filename = build_profile_trace_filename(timestamp, result_idx, run_key);
std::fs::write(&filename, trace_data)?;
Ok(filename)
}

#[cfg(test)]
mod tests {
use super::*;
use chrono::TimeZone;

#[test]
fn test_create_client_without_cli_id() {
Expand Down Expand Up @@ -932,4 +980,22 @@ mod tests {
std::env::set_var("POPCORN_API_URL", val);
}
}

#[test]
fn test_build_profile_trace_filename_uses_result_index_and_run_key() {
let timestamp = Utc.with_ymd_and_hms(2026, 3, 27, 9, 38, 46).single().unwrap();

let filename = build_profile_trace_filename(timestamp, 0, "profile3");

assert_eq!(filename, "profile_20260327_093846_result0_profile3.zip");
}

#[test]
fn test_build_profile_trace_filename_sanitizes_run_key() {
let timestamp = Utc.with_ymd_and_hms(2026, 3, 27, 9, 38, 46).single().unwrap();

let filename = build_profile_trace_filename(timestamp, 1, "profile:1/a b");

assert_eq!(filename, "profile_20260327_093846_result1_profile_1_a_b.zip");
}
}