Add markers to event stream
This commit is contained in:
		
							parent
							
								
									ed06004a60
								
							
						
					
					
						commit
						5e23df4723
					
				| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
use std::{ffi::c_void, ptr, slice};
 | 
					use std::{borrow::Cow, ffi::c_void, ptr, slice};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use anyhow::{Result, anyhow};
 | 
					use anyhow::{Result, anyhow};
 | 
				
			||||||
use bitflags::bitflags;
 | 
					use bitflags::bitflags;
 | 
				
			||||||
| 
						 | 
					@ -191,6 +191,9 @@ extern "C" fn on_frame(sim: *mut VB) -> c_int {
 | 
				
			||||||
    // There is no way for the userdata to be null or otherwise invalid.
 | 
					    // There is no way for the userdata to be null or otherwise invalid.
 | 
				
			||||||
    let data: &mut VBState = unsafe { &mut *vb_get_user_data(sim).cast() };
 | 
					    let data: &mut VBState = unsafe { &mut *vb_get_user_data(sim).cast() };
 | 
				
			||||||
    data.frame_seen = true;
 | 
					    data.frame_seen = true;
 | 
				
			||||||
 | 
					    if data.monitor.enabled {
 | 
				
			||||||
 | 
					        data.monitor.event = Some(SimEvent::Marker(Cow::Borrowed("Frame Drawn")));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    1
 | 
					    1
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -328,6 +331,7 @@ pub enum SimEvent {
 | 
				
			||||||
    Halt,
 | 
					    Halt,
 | 
				
			||||||
    Interrupt(u16, u32),
 | 
					    Interrupt(u16, u32),
 | 
				
			||||||
    Reti,
 | 
					    Reti,
 | 
				
			||||||
 | 
					    Marker(Cow<'static, str>),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct EventMonitor {
 | 
					struct EventMonitor {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -213,10 +213,22 @@ impl ProfilerSession {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn track_event(&mut self, event: SimEvent) -> Result<()> {
 | 
					    fn track_event(&mut self, event: SimEvent) -> Result<()> {
 | 
				
			||||||
        if let Some(program) = &mut self.program {
 | 
					        let Some(program) = &mut self.program else {
 | 
				
			||||||
            program.track_event(event)?;
 | 
					            return Ok(());
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        match event {
 | 
				
			||||||
 | 
					            SimEvent::Call(address) => program.track_call(address),
 | 
				
			||||||
 | 
					            SimEvent::Return => program.track_return(),
 | 
				
			||||||
 | 
					            SimEvent::Halt => program.track_halt(),
 | 
				
			||||||
 | 
					            SimEvent::Interrupt(code, address) => program.track_interrupt(code, address),
 | 
				
			||||||
 | 
					            SimEvent::Reti => program.track_reti(),
 | 
				
			||||||
 | 
					            SimEvent::Marker(name) => {
 | 
				
			||||||
 | 
					                if let Some(recording) = &mut self.recording {
 | 
				
			||||||
 | 
					                    recording.track_marker(name);
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                Ok(())
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn start_recording(&mut self) {
 | 
					    fn start_recording(&mut self) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,9 @@
 | 
				
			||||||
use std::collections::HashMap;
 | 
					use std::{borrow::Cow, collections::HashMap};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use fxprof_processed_profile::{
 | 
					use fxprof_processed_profile::{
 | 
				
			||||||
    CategoryHandle, CpuDelta, Frame, FrameFlags, FrameInfo, ProcessHandle, Profile,
 | 
					    CategoryHandle, CpuDelta, Frame, FrameFlags, FrameInfo, MarkerTiming, ProcessHandle, Profile,
 | 
				
			||||||
    ReferenceTimestamp, SamplingInterval, StackHandle, ThreadHandle, Timestamp,
 | 
					    ReferenceTimestamp, SamplingInterval, StackHandle, StaticSchemaMarker, StringHandle,
 | 
				
			||||||
 | 
					    ThreadHandle, Timestamp,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::profiler::state::{ProgramState, RESET_CODE, StackFrame};
 | 
					use crate::profiler::state::{ProgramState, RESET_CODE, StackFrame};
 | 
				
			||||||
| 
						 | 
					@ -72,6 +73,15 @@ impl Recording {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn track_marker(&mut self, name: Cow<'static, str>) {
 | 
				
			||||||
 | 
					        let Some(thread) = self.threads.get(&RESET_CODE) else {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let timing = MarkerTiming::Instant(Timestamp::from_nanos_since_reference(self.now * 50));
 | 
				
			||||||
 | 
					        let marker = SimpleMarker(name);
 | 
				
			||||||
 | 
					        self.profile.add_marker(*thread, timing, marker);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn finish(self) -> Vec<u8> {
 | 
					    pub fn finish(self) -> Vec<u8> {
 | 
				
			||||||
        serde_json::to_vec(&self.profile).expect("could not serialize profile")
 | 
					        serde_json::to_vec(&self.profile).expect("could not serialize profile")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -92,6 +102,29 @@ impl Recording {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SimpleMarker(Cow<'static, str>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl StaticSchemaMarker for SimpleMarker {
 | 
				
			||||||
 | 
					    const UNIQUE_MARKER_TYPE_NAME: &'static str = "Simple";
 | 
				
			||||||
 | 
					    const FIELDS: &'static [fxprof_processed_profile::StaticSchemaMarkerField] = &[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn name(&self, profile: &mut Profile) -> StringHandle {
 | 
				
			||||||
 | 
					        profile.intern_string(&self.0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn category(&self, _profile: &mut Profile) -> CategoryHandle {
 | 
				
			||||||
 | 
					        CategoryHandle::OTHER
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn string_field_value(&self, _field_index: u32) -> StringHandle {
 | 
				
			||||||
 | 
					        unreachable!()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn number_field_value(&self, _field_index: u32) -> f64 {
 | 
				
			||||||
 | 
					        unreachable!()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn thread_name_for_code(code: u16) -> std::borrow::Cow<'static, str> {
 | 
					fn thread_name_for_code(code: u16) -> std::borrow::Cow<'static, str> {
 | 
				
			||||||
    match code {
 | 
					    match code {
 | 
				
			||||||
        RESET_CODE => "Main".into(),
 | 
					        RESET_CODE => "Main".into(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ use std::{collections::HashMap, path::PathBuf};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use anyhow::{Result, bail};
 | 
					use anyhow::{Result, bail};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{emulator::SimEvent, profiler::symbols::SymbolFile};
 | 
					use crate::profiler::symbols::SymbolFile;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct ProgramState {
 | 
					pub struct ProgramState {
 | 
				
			||||||
    symbol_file: SymbolFile,
 | 
					    symbol_file: SymbolFile,
 | 
				
			||||||
| 
						 | 
					@ -42,62 +42,66 @@ impl ProgramState {
 | 
				
			||||||
        Some((*code, call_stack))
 | 
					        Some((*code, call_stack))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn track_event(&mut self, event: SimEvent) -> Result<()> {
 | 
					    pub fn track_call(&mut self, address: u32) -> Result<()> {
 | 
				
			||||||
        match event {
 | 
					        let Some(code) = self.context_stack.last() else {
 | 
				
			||||||
            SimEvent::Call(address) => {
 | 
					            bail!("How did we call anything when we're halted?");
 | 
				
			||||||
                let Some(code) = self.context_stack.last() else {
 | 
					        };
 | 
				
			||||||
                    bail!("How did we call anything when we're halted?");
 | 
					        let Some(stack) = self.call_stacks.get_mut(code) else {
 | 
				
			||||||
                };
 | 
					            bail!("missing stack {code:04x}");
 | 
				
			||||||
                let Some(stack) = self.call_stacks.get_mut(code) else {
 | 
					        };
 | 
				
			||||||
                    bail!("missing stack {code:04x}");
 | 
					        stack.push(StackFrame { address });
 | 
				
			||||||
                };
 | 
					        Ok(())
 | 
				
			||||||
                stack.push(StackFrame { address });
 | 
					    }
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            SimEvent::Return => {
 | 
					 | 
				
			||||||
                let Some(code) = self.context_stack.last() else {
 | 
					 | 
				
			||||||
                    bail!("how did we return when we're halted?");
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                let Some(stack) = self.call_stacks.get_mut(code) else {
 | 
					 | 
				
			||||||
                    bail!("missing stack {code:04x}");
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                if stack.pop().is_none() {
 | 
					 | 
				
			||||||
                    bail!("returned from {code:04x} but stack was empty");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if stack.is_empty() {
 | 
					 | 
				
			||||||
                    bail!("returned to oblivion");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            SimEvent::Halt => {
 | 
					 | 
				
			||||||
                let Some(RESET_CODE) = self.context_stack.pop() else {
 | 
					 | 
				
			||||||
                    bail!("halted when not in an interrupt");
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            SimEvent::Interrupt(code, address) => {
 | 
					 | 
				
			||||||
                // if the CPU was halted before, wake it up now
 | 
					 | 
				
			||||||
                if self.context_stack.is_empty() {
 | 
					 | 
				
			||||||
                    self.context_stack.push(RESET_CODE);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                self.context_stack.push(code);
 | 
					    pub fn track_return(&mut self) -> Result<()> {
 | 
				
			||||||
                if self
 | 
					        let Some(code) = self.context_stack.last() else {
 | 
				
			||||||
                    .call_stacks
 | 
					            bail!("how did we return when we're halted?");
 | 
				
			||||||
                    .insert(code, vec![StackFrame { address }])
 | 
					        };
 | 
				
			||||||
                    .is_some()
 | 
					        let Some(stack) = self.call_stacks.get_mut(code) else {
 | 
				
			||||||
                {
 | 
					            bail!("missing stack {code:04x}");
 | 
				
			||||||
                    bail!("{code:04x} fired twice");
 | 
					        };
 | 
				
			||||||
                }
 | 
					        if stack.pop().is_none() {
 | 
				
			||||||
            }
 | 
					            bail!("returned from {code:04x} but stack was empty");
 | 
				
			||||||
            SimEvent::Reti => {
 | 
					        }
 | 
				
			||||||
                let Some(code) = self.context_stack.pop() else {
 | 
					        if stack.is_empty() {
 | 
				
			||||||
                    bail!("RETI when halted");
 | 
					            bail!("returned to oblivion");
 | 
				
			||||||
                };
 | 
					        }
 | 
				
			||||||
                if code == RESET_CODE {
 | 
					        Ok(())
 | 
				
			||||||
                    bail!("RETI when not in interrupt");
 | 
					    }
 | 
				
			||||||
                }
 | 
					
 | 
				
			||||||
                if self.call_stacks.remove(&code).is_none() {
 | 
					    pub fn track_halt(&mut self) -> Result<()> {
 | 
				
			||||||
                    bail!("{code:04x} popped but never called");
 | 
					        let Some(RESET_CODE) = self.context_stack.pop() else {
 | 
				
			||||||
                }
 | 
					            bail!("halted when not in an interrupt");
 | 
				
			||||||
            }
 | 
					        };
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn track_interrupt(&mut self, code: u16, address: u32) -> Result<()> {
 | 
				
			||||||
 | 
					        // if the CPU was halted before, wake it up now
 | 
				
			||||||
 | 
					        if self.context_stack.is_empty() {
 | 
				
			||||||
 | 
					            self.context_stack.push(RESET_CODE);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.context_stack.push(code);
 | 
				
			||||||
 | 
					        if self
 | 
				
			||||||
 | 
					            .call_stacks
 | 
				
			||||||
 | 
					            .insert(code, vec![StackFrame { address }])
 | 
				
			||||||
 | 
					            .is_some()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            bail!("{code:04x} fired twice");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn track_reti(&mut self) -> Result<()> {
 | 
				
			||||||
 | 
					        let Some(code) = self.context_stack.pop() else {
 | 
				
			||||||
 | 
					            bail!("RETI when halted");
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        if code == RESET_CODE {
 | 
				
			||||||
 | 
					            bail!("RETI when not in interrupt");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if self.call_stacks.remove(&code).is_none() {
 | 
				
			||||||
 | 
					            bail!("{code:04x} popped but never called");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue