From 4c0ba4f90422a816eec8749cf93d910868433961 Mon Sep 17 00:00:00 2001 From: Vladimir Vukicevic Date: Tue, 23 Apr 2024 16:58:08 +0200 Subject: [PATCH] Make work --- samply/src/linux_shared/process.rs | 12 ++- samply/src/linux_shared/processes.rs | 11 ++- samply/src/windows/etw_gecko.rs | 127 +++++++++++++++------------ samply/src/windows/profiler.rs | 106 ++++++++++++---------- 4 files changed, 149 insertions(+), 107 deletions(-) diff --git a/samply/src/linux_shared/process.rs b/samply/src/linux_shared/process.rs index ad830f61..7d1622a3 100644 --- a/samply/src/linux_shared/process.rs +++ b/samply/src/linux_shared/process.rs @@ -1,6 +1,7 @@ use std::path::{Path, PathBuf}; use framehop::Unwinder; +use rangemap::RangeSet; use fxprof_processed_profile::{ CategoryHandle, CounterHandle, LibraryHandle, MarkerTiming, ProcessHandle, Profile, ThreadHandle, Timestamp, @@ -167,7 +168,7 @@ where profile: &mut Profile, jit_category_manager: &mut JitCategoryManager, timestamp_converter: &TimestampConverter, - ) -> (ProcessSampleData, Option<(String, ProcessRecyclingData)>) { + ) -> (ProcessSampleData, Option<(String, ProcessRecyclingData)>, Option>) { self.unwinder = U::default(); let perf_map_mappings = if !self.unresolved_samples.is_empty() { @@ -189,13 +190,18 @@ where timestamp_converter, ); + let mut marker_ranges: Option> = None; let mut marker_spans = Vec::new(); for (thread_handle, marker_file_path, fallback_dir) in self.marker_file_paths { - if let Ok(marker_spans_from_this_file) = get_markers( + if let Ok((marker_spans_from_this_file, maybe_ranges)) = get_markers( &marker_file_path, + None, fallback_dir.as_deref(), *timestamp_converter, ) { + if let Some(ranges) = maybe_ranges { + marker_ranges.get_or_insert_with(|| RangeSet::new()).extend(ranges); + } marker_spans.extend(marker_spans_from_this_file.into_iter().map(|span| { MarkerSpanOnThread { thread_handle, @@ -235,7 +241,7 @@ where None }; - (process_sample_data, process_recycling_data) + (process_sample_data, process_recycling_data, marker_ranges) } #[allow(clippy::too_many_arguments)] diff --git a/samply/src/linux_shared/processes.rs b/samply/src/linux_shared/processes.rs index 56c25335..cf83aeb9 100644 --- a/samply/src/linux_shared/processes.rs +++ b/samply/src/linux_shared/processes.rs @@ -3,6 +3,7 @@ use fxprof_processed_profile::{CategoryColor, Profile, Timestamp}; use std::collections::hash_map::Entry; use std::collections::HashMap; +use rangemap::RangeSet; use super::process::Process; @@ -148,7 +149,7 @@ where process.notify_dead(time, profile); - let (process_sample_data, process_recycling_data) = + let (process_sample_data, process_recycling_data, _process_range_data) = process.finish(profile, jit_category_manager, timestamp_converter); if !process_sample_data.is_empty() { self.process_sample_datas.push(process_sample_data); @@ -201,13 +202,18 @@ where jit_category_manager: &mut JitCategoryManager, timestamp_converter: &TimestampConverter, ) { + let mut full_marker_ranges = None; + // Gather the ProcessSampleData from any processes which are still alive at the end of profiling. for process in self.processes_by_pid.into_values() { - let (process_sample_data, _process_recycling_data) = + let (process_sample_data, _process_recycling_data, process_range_data) = process.finish(profile, jit_category_manager, timestamp_converter); if !process_sample_data.is_empty() { self.process_sample_datas.push(process_sample_data); } + if let Some(process_range_data) = process_range_data { + full_marker_ranges.get_or_insert_with(|| RangeSet::new()).extend(process_range_data); + } } let user_category = profile.add_category("User", CategoryColor::Yellow).into(); @@ -221,6 +227,7 @@ where &mut stack_frame_scratch_buf, unresolved_stacks, event_names, + full_marker_ranges.as_ref(), ); } } diff --git a/samply/src/windows/etw_gecko.rs b/samply/src/windows/etw_gecko.rs index f498d835..b2f8bd6e 100644 --- a/samply/src/windows/etw_gecko.rs +++ b/samply/src/windows/etw_gecko.rs @@ -3,6 +3,7 @@ use std::process::ExitStatus; use super::context_switch::{OffCpuSampleGroup, ThreadContextSwitchData}; use super::etw_reader::{GUID, open_trace, parser::{Parser, TryParse, Address}, print_property, schema::SchemaLocator, write_property}; +use super::{etw_reader, winutils}; use crate::shared::lib_mappings::{LibMappingOpQueue, LibMappingOp, LibMappingAdd}; use serde_json::{Value, json, to_writer}; use fxprof_processed_profile::{debugid, CategoryColor, CategoryHandle, CategoryPairHandle, CounterHandle, CpuDelta, FrameFlags, FrameInfo, LibraryHandle, LibraryInfo, MarkerDynamicField, MarkerFieldFormat, MarkerLocation, MarkerSchema, MarkerSchemaField, MarkerTiming, ProcessHandle, Profile, ProfilerMarker, ReferenceTimestamp, SamplingInterval, Symbol, SymbolTable, ThreadHandle, Timestamp}; @@ -15,7 +16,7 @@ use crate::shared::stack_converter::StackConverter; use crate::shared::lib_mappings::LibMappingInfo; use crate::shared::types::{StackFrame, StackMode}; use crate::shared::unresolved_samples::{UnresolvedSamples, UnresolvedStacks}; -use crate::shared::process_sample_data::ProcessSampleData; +use crate::shared::process_sample_data::{MarkerSpanOnThread, ProcessSampleData, SimpleMarker}; use super::{context_switch::ContextSwitchHandler}; use crate::shared::{jit_function_add_marker::JitFunctionAddMarker, marker_file::get_markers, process_sample_data::UserTimingMarker, timestamp_converter::TimestampConverter}; @@ -114,13 +115,12 @@ impl ProcessState { } } -pub fn start_recording( - process_launch_props: ProcessLaunchProps, +pub fn profile_pid_from_etl_file( + pid: u32, recording_props: RecordingProps, profile_creation_props: ProfileCreationProps, - server_props: Option, -) -> Result { -fn main() { + etl_file: &Path, +) { let profile_start_instant = Timestamp::from_nanos_since_reference(0); let profile_start_system = SystemTime::now(); @@ -133,31 +133,23 @@ fn main() { let mut libs: HashMap = HashMap::new(); let start = Instant::now(); - let mut pargs = pico_args::Arguments::from_env(); - let merge_threads = pargs.contains("--merge-threads"); - let include_idle = pargs.contains("--idle"); - let demand_zero_faults = pargs.contains("--demand-zero-faults"); - let marker_file: Option = pargs.opt_value_from_str("--marker-file").unwrap(); - let marker_prefix: Option = pargs.opt_value_from_str("--filter-by-marker-prefix").unwrap(); - - let trace_file: String = pargs.free_from_str().unwrap(); + let merge_threads = profile_creation_props.reuse_threads; // --merge-threads? + let include_idle = false; //pargs.contains("--idle"); + let demand_zero_faults = false; //pargs.contains("--demand-zero-faults"); + let marker_file: Option = None; //pargs.opt_value_from_str("--marker-file").unwrap(); + let marker_prefix: Option = None; //pargs.opt_value_from_str("--filter-by-marker-prefix").unwrap(); let mut process_targets = HashSet::new(); - let mut process_target_name = None; - if let Ok(process_filter) = pargs.free_from_str::() { - if let Ok(process_id) = process_filter.parse() { - process_targets.insert(process_id); - } else { - println!("targeting {}", process_filter); - process_target_name = Some(process_filter); - } - } else { - println!("No process specified"); - std::process::exit(1); + if pid != 0 { + process_targets.insert(pid); } - - let command_name = process_target_name.as_deref().unwrap_or("firefox"); - let mut profile = Profile::new(command_name, ReferenceTimestamp::from_system_time(profile_start_system), SamplingInterval::from_nanos(122100)); // 8192Hz + let all_processes = process_targets.is_empty(); + + let process_target_name: Option<&str> = None; + + let mut profile = Profile::new(&profile_creation_props.profile_name, + ReferenceTimestamp::from_system_time(profile_start_system), + SamplingInterval::from_nanos(122100)); // 8192Hz // only with the higher recording rate? let user_category: CategoryPairHandle = profile.add_category("User", fxprof_processed_profile::CategoryColor::Yellow).into(); let kernel_category: CategoryPairHandle = profile.add_category("Kernel", fxprof_processed_profile::CategoryColor::Orange).into(); @@ -190,7 +182,7 @@ fn main() { let mut event_timestamps_are_qpc = false; let mut categories = HashMap::::new(); - let result = open_trace(Path::new(&trace_file), |e| { + let result = open_trace(&etl_file, |e| { event_count += 1; let s = schema_locator.event_schema(e); if let Ok(s) = s { @@ -265,13 +257,13 @@ fn main() { "MSNT_SystemTrace/Thread/Start" | "MSNT_SystemTrace/Thread/DCStart" => { let timestamp = e.EventHeader.TimeStamp as u64; - let timestamp = timestamp_converter.convert_raw(timestamp); + let timestamp = timestamp_converter.convert_time(timestamp); let mut parser = Parser::create(&s); let thread_id: u32 = parser.parse("TThreadId"); let process_id: u32 = parser.parse("ProcessId"); //assert_eq!(process_id,s.process_id()); - //println!("thread_name pid: {} tid: {} name: {:?}", process_id, thread_id, thread_name); + eprintln!("thread_name pid: {} tid: {}", process_id, thread_id); if !process_targets.contains(&process_id) { return; @@ -335,7 +327,7 @@ fn main() { "MSNT_SystemTrace/Thread/End" | "MSNT_SystemTrace/Thread/DCEnd" => { let timestamp = e.EventHeader.TimeStamp as u64; - let timestamp = timestamp_converter.convert_raw(timestamp); + let timestamp = timestamp_converter.convert_time(timestamp); let mut parser = Parser::create(&s); let thread_id: u32 = parser.parse("TThreadId"); @@ -352,16 +344,23 @@ fn main() { } "MSNT_SystemTrace/Process/Start" | "MSNT_SystemTrace/Process/DCStart" => { + let mut parser = Parser::create(&s); + let process_id: u32 = parser.parse("ProcessId"); + let parent_id: u32 = parser.parse("ParentId"); + + eprintln!("process {} parent {}", process_id, parent_id); + if all_processes || process_targets.contains(&parent_id) { + process_targets.insert(process_id); + } + if let Some(process_target_name) = &process_target_name { let timestamp = e.EventHeader.TimeStamp as u64; - let timestamp = timestamp_converter.convert_raw(timestamp); - let mut parser = Parser::create(&s); + let timestamp = timestamp_converter.convert_time(timestamp); let image_file_name: String = parser.parse("ImageFileName"); println!("process start {}", image_file_name); - let process_id: u32 = parser.parse("ProcessId"); if image_file_name.contains(process_target_name) { process_targets.insert(process_id); println!("tracing {}", process_id); @@ -374,6 +373,8 @@ fn main() { } } } + // TODO End -- remove from process_targets + "MSNT_SystemTrace/StackWalk/Stack" => { let mut parser = Parser::create(&s); @@ -433,7 +434,7 @@ fn main() { // the pending stack with matching timestamp. let mut add_sample = |thread: &ThreadState, process: &mut ProcessState, timestamp: u64, cpu_delta: CpuDelta, weight: i32, stack: Vec| { - let profile_timestamp = timestamp_converter.convert_raw(timestamp); + let profile_timestamp = timestamp_converter.convert_time(timestamp); let stack_index = unresolved_stacks.convert(stack.into_iter().rev()); let extra_label_frame = if let Some(global_thread) = global_thread { let thread_name = thread.merge_name.as_ref().map(|x| strip_thread_numbers(x).to_owned()).unwrap_or_else(|| format!("thread {}", thread.thread_id)); @@ -502,7 +503,7 @@ fn main() { _ => "Other" }; let timestamp = e.EventHeader.TimeStamp as u64; - let timestamp = timestamp_converter.convert_raw(timestamp); + let timestamp = timestamp_converter.convert_time(timestamp); frames.push(FrameInfo { frame: fxprof_processed_profile::Frame::Label(profile.intern_string(&thread_name)), @@ -542,7 +543,7 @@ fn main() { _ => "Other" }; let timestamp = e.EventHeader.TimeStamp as u64; - let timestamp = timestamp_converter.convert_raw(timestamp); + let timestamp = timestamp_converter.convert_time(timestamp); frames.push(FrameInfo { frame: fxprof_processed_profile::Frame::Label(profile.intern_string(&thread_name)), @@ -567,7 +568,7 @@ fn main() { } let mut parser = Parser::create(&s); let timestamp = e.EventHeader.TimeStamp as u64; - let timestamp = timestamp_converter.convert_raw(timestamp); + let timestamp = timestamp_converter.convert_time(timestamp); let thread_id = e.EventHeader.ThreadId; let counter = match memory_usage.entry(e.EventHeader.ProcessId) { Entry::Occupied(e) => e.into_mut(), @@ -598,7 +599,7 @@ fn main() { text += ", " } - profile.add_marker(thread.handle, CategoryHandle::OTHER, "VirtualFree", TextMarker(text), timing) + profile.add_marker(thread.handle, CategoryHandle::OTHER, "VirtualFree", SimpleMarker(text), timing) } "MSNT_SystemTrace/PageFault/VirtualAlloc" => { if !process_targets.contains(&e.EventHeader.ProcessId) { @@ -606,7 +607,7 @@ fn main() { } let mut parser = Parser::create(&s); let timestamp = e.EventHeader.TimeStamp as u64; - let timestamp = timestamp_converter.convert_raw(timestamp); + let timestamp = timestamp_converter.convert_time(timestamp); let thread_id = e.EventHeader.ThreadId; let counter = match memory_usage.entry(e.EventHeader.ProcessId) { Entry::Occupied(e) => e.into_mut(), @@ -635,7 +636,7 @@ fn main() { //println!("{}.{} VirtualAlloc({}) = {}", e.EventHeader.ProcessId, thread_id, region_size, counter.value); profile.add_counter_sample(counter.counter, timestamp, region_size as f64, 1); - profile.add_marker(thread.handle, CategoryHandle::OTHER, "VirtualAlloc", TextMarker(text), timing) + profile.add_marker(thread.handle, CategoryHandle::OTHER, "VirtualAlloc", SimpleMarker(text), timing) } "KernelTraceControl/ImageID/" => { @@ -735,7 +736,7 @@ fn main() { } "Microsoft-Windows-DxgKrnl/VSyncDPC/Info " => { let timestamp = e.EventHeader.TimeStamp as u64; - let timestamp = timestamp_converter.convert_raw(timestamp); + let timestamp = timestamp_converter.convert_time(timestamp); #[derive(Debug, Clone)] pub struct VSyncMarker; @@ -826,7 +827,7 @@ fn main() { process_jit_info.next_relative_address += method_size as u32; let timestamp = e.EventHeader.TimeStamp as u64; - let timestamp = timestamp_converter.convert_raw(timestamp); + let timestamp = timestamp_converter.convert_time(timestamp); if let Some(main_thread) = process.main_thread_handle { profile.add_marker( @@ -912,10 +913,10 @@ fn main() { } }; let timing = match phase { - PHASE_INSTANT => MarkerTiming::Instant(timestamp_converter.convert_raw(instant_time_qpc)), - PHASE_INTERVAL => MarkerTiming::Interval(timestamp_converter.convert_raw(start_time_qpc), timestamp_converter.convert_raw(end_time_qpc)), - PHASE_INTERVAL_START => MarkerTiming::IntervalStart(timestamp_converter.convert_raw(start_time_qpc)), - PHASE_INTERVAL_END => MarkerTiming::IntervalEnd(timestamp_converter.convert_raw(end_time_qpc)), + PHASE_INSTANT => MarkerTiming::Instant(timestamp_converter.convert_time(instant_time_qpc)), + PHASE_INTERVAL => MarkerTiming::Interval(timestamp_converter.convert_time(start_time_qpc), timestamp_converter.convert_time(end_time_qpc)), + PHASE_INTERVAL_START => MarkerTiming::IntervalStart(timestamp_converter.convert_time(start_time_qpc)), + PHASE_INTERVAL_END => MarkerTiming::IntervalEnd(timestamp_converter.convert_time(end_time_qpc)), _ => panic!("Unexpected marker phase {phase}"), }; @@ -924,9 +925,9 @@ fn main() { profile.add_marker(thread.handle, CategoryHandle::OTHER, "UserTiming", UserTimingMarker(name), timing); } else if marker_name == "SimpleMarker" || marker_name == "Text" || marker_name == "tracing" { let marker_name: String = parser.try_parse("MarkerName").unwrap(); - profile.add_marker(thread.handle, CategoryHandle::OTHER, &marker_name, TextMarker(text.clone()), timing); + profile.add_marker(thread.handle, CategoryHandle::OTHER, &marker_name, SimpleMarker(text.clone()), timing); } else { - profile.add_marker(thread.handle, CategoryHandle::OTHER, marker_name, TextMarker(text.clone()), timing); + profile.add_marker(thread.handle, CategoryHandle::OTHER, marker_name, SimpleMarker(text.clone()), timing); } } else if let Some(marker_name) = s.name().strip_prefix("Google.Chrome/").and_then(|s| s.strip_suffix("/")) { // a bitfield of keywords @@ -1020,13 +1021,13 @@ fn main() { if keyword == KeywordNames::blink_user_timing { profile.add_marker(thread.handle, CategoryHandle::OTHER, "UserTiming", UserTimingMarker(marker_name.to_owned()), timing); } else { - profile.add_marker(thread.handle, CategoryHandle::OTHER, marker_name, TextMarker(text.clone()), timing); + profile.add_marker(thread.handle, CategoryHandle::OTHER, marker_name, SimpleMarker(text.clone()), timing); } } else { let mut parser = Parser::create(&s); let timestamp = e.EventHeader.TimeStamp as u64; - let timestamp = timestamp_converter.convert_raw(timestamp); + let timestamp = timestamp_converter.convert_time(timestamp); let thread_id = e.EventHeader.ThreadId; let thread = match threads.entry(thread_id) { Entry::Occupied(e) => e.into_mut(), @@ -1053,7 +1054,7 @@ fn main() { } }; - profile.add_marker(thread.handle, category, s.name().split_once("/").unwrap().1, TextMarker(text), timing) + profile.add_marker(thread.handle, category, s.name().split_once("/").unwrap().1, SimpleMarker(text), timing) } //println!("unhandled {}", s.name()) } @@ -1069,8 +1070,9 @@ fn main() { let (marker_spans, sample_ranges) = match marker_file { Some(marker_file) => get_markers( - &marker_file, + &Path::new(&marker_file), marker_prefix.as_deref(), + None, // extra_dir? timestamp_converter, ) .expect("Could not get markers"), @@ -1094,8 +1096,21 @@ fn main() { }, None => Vec::new(), }; - let process_sample_data = ProcessSampleData::new(unresolved_samples, regular_lib_mapping_ops, jitdump_lib_mapping_op_queues, None, main_thread_handle.unwrap_or_else(|| panic!("process no main thread {:?}", process_id))); - process_sample_data.flush_samples_to_profile(&mut profile, user_category, kernel_category, &mut stack_frame_scratch_buf, &mut unresolved_stacks, &[], &marker_spans, sample_ranges.as_ref()) + // TODO proper threads, not main thread + let marker_spans_on_thread = marker_spans.iter().map(|marker_span| { + MarkerSpanOnThread { + thread_handle: main_thread_handle.unwrap(), + name: marker_span.name.clone(), + start_time: marker_span.start_time, + end_time: marker_span.end_time, + } + }).collect(); + + let process_sample_data = ProcessSampleData::new(unresolved_samples, regular_lib_mapping_ops, + jitdump_lib_mapping_op_queues, + None, marker_spans_on_thread); + //main_thread_handle.unwrap_or_else(|| panic!("process no main thread {:?}", process_id))); + process_sample_data.flush_samples_to_profile(&mut profile, user_category, kernel_category, &mut stack_frame_scratch_buf, &mut unresolved_stacks, &[], sample_ranges.as_ref()) } /*if merge_threads { diff --git a/samply/src/windows/profiler.rs b/samply/src/windows/profiler.rs index ef541981..c4320e4d 100644 --- a/samply/src/windows/profiler.rs +++ b/samply/src/windows/profiler.rs @@ -37,7 +37,7 @@ use windows::{ use super::etw::*; -use crate::windows::winutils; +use crate::windows::{etw_gecko, winutils}; // Hello intrepid explorer! You may be in this code because you'd like to extend something, // or are trying to figure out how various ETW things work. It's not the easiest API! @@ -106,57 +106,72 @@ pub fn start_recording( let profile = Arc::new(Mutex::new(profile)); let mut context = ProfileContext::new(profile.clone(), rt.handle().clone()); - context.add_kernel_drivers(); - context.start_xperf(&recording_props.output_file); - - // Run the command. - // !!!FIXME!!! We are in an elevated context right now. Running this will run - // the command as Administrator, which is almost definitely not what the - // user wanted. We could drop privileges before running the command, but - // I think what we need to do is have the _initial_ samply session stick - // around and act as the command executor, passing us the pids it spawns. - // That way the command will get executed in exactly the context the user intended. - for _ in 0..process_launch_props.iteration_count { - let mut child = std::process::Command::new(&process_launch_props.command_name); - child.args(&process_launch_props.args); - child.envs(process_launch_props.env_vars.iter().map(|(k, v)| (k, v))); - let mut child = child.spawn().unwrap(); - - context.add_interesting_pid(child.id()); - - let exit_status = child.wait().unwrap(); - if !exit_status.success() { - eprintln!("Child process exited with {:?}", exit_status); + let mut main_pid = 0; + + let (etl_file, existing_etl) = if !process_launch_props.command_name.to_str().unwrap().ends_with(".etl") { + // Start xperf. + context.start_xperf(&recording_props.output_file); + + // Run the command. + // !!!FIXME!!! We are in an elevated context right now. Running this will run + // the command as Administrator, which is almost definitely not what the + // user wanted. We could drop privileges before running the command, but + // I think what we need to do is have the _initial_ samply session stick + // around and act as the command executor, passing us the pids it spawns. + // That way the command will get executed in exactly the context the user intended. + for _ in 0..process_launch_props.iteration_count { + let mut child = std::process::Command::new(&process_launch_props.command_name); + child.args(&process_launch_props.args); + child.envs(process_launch_props.env_vars.iter().map(|(k, v)| (k, v))); + let mut child = child.spawn().unwrap(); + + main_pid = child.id(); + context.add_interesting_pid(child.id()); + + let exit_status = child.wait().unwrap(); + if !exit_status.success() { + eprintln!("Child process exited with {:?}", exit_status); + } } - } + context.stop_xperf(); - context.stop_xperf(); + (context.etl_file.clone().unwrap(), false) + } else { + eprintln!("Existing ETL"); + (PathBuf::from(&process_launch_props.command_name), true) + }; eprintln!("Processing ETL trace..."); - let etl_file = context.etl_file.clone().unwrap(); - let (trace, handle) = FileTrace::new(etl_file.clone(), move |ev, sl| { - trace_callback(ev, sl, &mut context); - }) - .start() - .unwrap(); - // TODO: grab the header info, so that we can pull out StartTime (and PerfFreq). Not really important. - // QueryTraceProcessingHandle(handle, EtwQueryLogFileHeader, None, 0, &ptr to TRACE_LOGFILE_HEADER) + let output_file = recording_props.output_file.clone(); + + let old_processing = false; + if old_processing { + let (trace, handle) = FileTrace::new(etl_file.clone(), move |ev, sl| { + trace_callback(ev, sl, &mut context); + }) + .start() + .unwrap(); - FileTrace::process_from_handle(handle).unwrap(); + // TODO: grab the header info, so that we can pull out StartTime (and PerfFreq). Not really important. + // QueryTraceProcessingHandle(handle, EtwQueryLogFileHeader, None, 0, &ptr to TRACE_LOGFILE_HEADER) - let n_events = trace.events_handled(); - eprintln!("Read {} events from file", n_events); + FileTrace::process_from_handle(handle).unwrap(); + + let n_events = trace.events_handled(); + eprintln!("Read {} events from file", n_events); + } else { + etw_gecko::profile_pid_from_etl_file(main_pid, recording_props, profile_creation_props, &Path::new(&etl_file)); + } // delete etl_file - std::fs::remove_file(&etl_file) - .expect(format!("Failed to delete ETL file {:?}", etl_file.to_str().unwrap()).as_str()); + if !existing_etl { + std::fs::remove_file(&etl_file).expect(format!("Failed to delete ETL file {:?}", etl_file.to_str().unwrap()).as_str()); + } // write the profile to a json file - let output_file = recording_props.output_file.clone(); - let file = File::create(&output_file).unwrap(); let writer = BufWriter::new(file); to_writer(writer, profile.lock().unwrap().deref_mut()).expect("Couldn't write JSON"); @@ -337,15 +352,14 @@ impl ProfileContext { // extension. let etl_file = format!("{}.etl", output_file.to_str().unwrap()); let mut xperf = std::process::Command::new("xperf"); - xperf.arg("-on"); - xperf.arg("PROC_THREAD+LOADER+PROFILE"); - xperf.arg("-stackwalk"); - xperf.arg("profile"); // Virtualised ARM64 Windows crashes out on PROFILE tracing, and that's what I'm developing // on, so these are hacky args to get me a useful profile that I can work with. - //xperf.arg("PROC_THREAD+LOADER+CSWITCH+SYSCALL+VIRT_ALLOC+OB_HANDLE"); - //xperf.arg("-stackwalk"); - //xperf.arg("VirtualAlloc+VirtualFree+HandleCreate+HandleClose"); + xperf.arg("-on"); + //xperf.arg("PROC_THREAD+LOADER+PROFILE"); + xperf.arg("PROC_THREAD+LOADER+CSWITCH+SYSCALL+VIRT_ALLOC+OB_HANDLE"); + xperf.arg("-stackwalk"); + //xperf.arg("profile"); + xperf.arg("VirtualAlloc+VirtualFree+HandleCreate+HandleClose"); xperf.arg("-f"); xperf.arg(&etl_file);