From e26c76e46da59152f4a21dedcb93b01b81ce317d Mon Sep 17 00:00:00 2001 From: Benjamin Klum Date: Thu, 16 Nov 2023 10:42:00 +0100 Subject: [PATCH] Display counter for undesired allocations (debug builds only) --- Cargo.lock | 7 ++++++ allocator/src/lib.rs | 12 ++++++++-- main/src/application/session.rs | 21 ++++++++++------- main/src/domain/control_surface.rs | 14 +++++++++++ main/src/domain/eventing.rs | 3 ++- main/src/domain/info_event.rs | 4 ++++ main/src/domain/main_processor.rs | 30 ++++++++++++++---------- main/src/domain/mod.rs | 3 +++ main/src/domain/real_time_processor.rs | 2 ++ main/src/infrastructure/ui/main_panel.rs | 30 ++++++++++++++++++++---- playtime-clip-engine | 2 +- 11 files changed, 100 insertions(+), 28 deletions(-) create mode 100644 main/src/domain/info_event.rs diff --git a/Cargo.lock b/Cargo.lock index bab13cbb2..ae069c464 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2301,6 +2301,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "glidesort" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2e102e6eb644d3e0b186fc161e4460417880a0a0b87d235f2e5b8fb30f2e9e0" + [[package]] name = "glob" version = "0.3.1" @@ -4032,6 +4038,7 @@ dependencies = [ "envcrypt", "erased-serde", "futures", + "glidesort", "helgoboss-allocator", "helgoboss-learn", "helgoboss-license-api", diff --git a/allocator/src/lib.rs b/allocator/src/lib.rs index a8788be6c..112f6f6d1 100644 --- a/allocator/src/lib.rs +++ b/allocator/src/lib.rs @@ -8,6 +8,7 @@ //! good testability but also nice otherwise as long as set_alloc_error_hook() is still unstable) use std::alloc::{GlobalAlloc, Layout, System}; use std::cell::Cell; +use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::mpsc::{Receiver, SyncSender, TrySendError}; use std::sync::{mpsc, OnceLock}; use std::thread; @@ -19,6 +20,12 @@ thread_local! { static ALLOC_PERMIT_COUNT: Cell = Cell::new(0); } +static UNDESIRED_ALLOCATION_COUNTER: AtomicU32 = AtomicU32::new(0); + +pub fn undesired_allocation_count() -> u32 { + UNDESIRED_ALLOCATION_COUNTER.load(Ordering::Relaxed) +} + #[cfg(not(debug_assertions))] pub fn assert_no_alloc T>(func: F) -> T { // no-op @@ -196,10 +203,11 @@ impl HelgobossAllocator { let forbid_count = ALLOC_FORBID_COUNT.with(|f| f.get()); let permit_count = ALLOC_PERMIT_COUNT.with(|p| p.get()); if forbid_count > 0 && permit_count == 0 { - // Comment out if you want to ignore it TODO-high + UNDESIRED_ALLOCATION_COUNTER.fetch_add(1, Ordering::Relaxed); + // Comment out if you want to ignore it permit_alloc(|| { eprintln!( - "Memory allocation of {} bytes failed from:\n{:?}", + "Undesired memory allocation of {} bytes from:\n{:?}", layout.size(), backtrace::Backtrace::new() ); diff --git a/main/src/application/session.rs b/main/src/application/session.rs index ed8845cbb..fb5543f7b 100644 --- a/main/src/application/session.rs +++ b/main/src/application/session.rs @@ -12,14 +12,14 @@ use crate::domain::{ CompartmentParamIndex, CompartmentParams, CompoundMappingSource, ControlContext, ControlInput, DomainEvent, DomainEventHandler, ExtendedProcessorContext, FeedbackAudioHookTask, FeedbackOutput, FeedbackRealTimeTask, FinalSourceFeedbackValue, GroupId, GroupKey, - IncomingCompoundSourceValue, InputDescriptor, InstanceContainer, InstanceId, InstanceState, - LastTouchedTargetFilter, MainMapping, MappingId, MappingKey, MappingMatchedEvent, - MessageCaptureEvent, MidiControlInput, NormalMainTask, NormalRealTimeTask, OscFeedbackTask, - ParamSetting, PluginParams, ProcessorContext, ProjectionFeedbackValue, QualifiedMappingId, - RealearnControlSurfaceMainTask, RealearnTarget, ReaperTarget, ReaperTargetType, - SharedInstanceState, StayActiveWhenProjectInBackground, Tag, TargetControlEvent, - TargetTouchEvent, TargetValueChangedEvent, VirtualControlElementId, VirtualFx, VirtualSource, - VirtualSourceValue, + IncomingCompoundSourceValue, InfoEvent, InputDescriptor, InstanceContainer, InstanceId, + InstanceState, LastTouchedTargetFilter, MainMapping, MappingId, MappingKey, + MappingMatchedEvent, MessageCaptureEvent, MidiControlInput, NormalMainTask, NormalRealTimeTask, + OscFeedbackTask, ParamSetting, PluginParams, ProcessorContext, ProjectionFeedbackValue, + QualifiedMappingId, RealearnControlSurfaceMainTask, RealearnTarget, ReaperTarget, + ReaperTargetType, SharedInstanceState, StayActiveWhenProjectInBackground, Tag, + TargetControlEvent, TargetTouchEvent, TargetValueChangedEvent, VirtualControlElementId, + VirtualFx, VirtualSource, VirtualSourceValue, }; use base::{Global, NamedChannelSender, SenderToNormalThread, SenderToRealTimeThread}; use derivative::Derivative; @@ -71,6 +71,7 @@ pub trait SessionUi { ); fn mapping_matched(&self, event: MappingMatchedEvent); fn target_controlled(&self, event: TargetControlEvent); + fn handle_info_event(&self, event: &InfoEvent); fn handle_affected( &self, session: &Session, @@ -2560,6 +2561,10 @@ impl DomainEventHandler for WeakSession { let session = self.upgrade().ok_or("session not existing anymore")?; use DomainEvent::*; match event { + Info(evt) => { + let s = session.try_borrow()?; + s.ui.handle_info_event(evt); + } ConditionsChanged => { let s = session.try_borrow()?; s.ui.conditions_changed() diff --git a/main/src/domain/control_surface.rs b/main/src/domain/control_surface.rs index fe6892fd3..acf4c0b99 100644 --- a/main/src/domain/control_surface.rs +++ b/main/src/domain/control_surface.rs @@ -64,6 +64,7 @@ pub struct RealearnControlSurfaceMiddleware { reaper_config_change_detector: ReaperConfigChangeDetector, control_surface_event_sender: SenderToNormalThread>, control_surface_event_receiver: crossbeam_channel::Receiver>, + last_undesired_allocation_count: u32, } pub enum RealearnControlSurfaceMainTask { @@ -214,11 +215,24 @@ impl RealearnControlSurfaceMiddleware { reaper_config_change_detector: Default::default(), control_surface_event_sender, control_surface_event_receiver, + last_undesired_allocation_count: 0, } } fn run_internal(&mut self) { let timestamp = ControlEventTimestamp::now(); + #[cfg(debug_assertions)] + { + let current_undesired_allocation_count = + helgoboss_allocator::undesired_allocation_count(); + if current_undesired_allocation_count != self.last_undesired_allocation_count { + self.last_undesired_allocation_count = current_undesired_allocation_count; + let event = &crate::domain::InfoEvent::UndesiredAllocationCountChanged; + for p in self.main_processors.borrow_mut().iter_mut() { + p.process_info_event(event); + } + } + } // Poll for change events that don't support notification. At the moment we execute this // only once per run() invocation to save resources. As a consequence, we can't detect // whether the changes were caused by ReaLearn targets or direct interaction with REAPER diff --git a/main/src/domain/eventing.rs b/main/src/domain/eventing.rs index c268cc43e..a403e3593 100644 --- a/main/src/domain/eventing.rs +++ b/main/src/domain/eventing.rs @@ -1,5 +1,5 @@ use crate::domain::{ - Compartment, CompoundMappingTarget, ControlLogContext, ControlLogEntry, MappingId, + Compartment, CompoundMappingTarget, ControlLogContext, ControlLogEntry, InfoEvent, MappingId, MessageCaptureResult, PluginParamIndex, PluginParams, ProjectionFeedbackValue, QualifiedMappingId, RawParamValue, }; @@ -22,6 +22,7 @@ pub enum DomainEvent<'a> { }, UpdatedAllParameters(PluginParams), TargetValueChanged(TargetValueChangedEvent<'a>), + Info(&'a InfoEvent), ProjectionFeedback(ProjectionFeedbackValue), MappingMatched(MappingMatchedEvent), TargetControlled(TargetControlEvent), diff --git a/main/src/domain/info_event.rs b/main/src/domain/info_event.rs new file mode 100644 index 000000000..849cc8357 --- /dev/null +++ b/main/src/domain/info_event.rs @@ -0,0 +1,4 @@ +#[derive(Debug)] +pub enum InfoEvent { + UndesiredAllocationCountChanged, +} diff --git a/main/src/domain/main_processor.rs b/main/src/domain/main_processor.rs index e43f57e29..719d7717d 100644 --- a/main/src/domain/main_processor.rs +++ b/main/src/domain/main_processor.rs @@ -7,18 +7,18 @@ use crate::domain::{ ExtendedProcessorContext, FeedbackAudioHookTask, FeedbackCollector, FeedbackDestinations, FeedbackOutput, FeedbackRealTimeTask, FeedbackResolution, FeedbackSendBehavior, FinalRealFeedbackValue, FinalSourceFeedbackValue, GlobalControlAndFeedbackState, GroupId, - HitInstructionContext, HitInstructionResponse, InstanceContainer, InstanceOrchestrationEvent, - InstanceStateChanged, IoUpdatedEvent, KeyMessage, MainMapping, MainSourceMessage, - MappingActivationEffect, MappingControlResult, MappingId, MappingInfo, MessageCaptureEvent, - MessageCaptureResult, MidiControlInput, MidiDestination, MidiScanResult, NormalRealTimeTask, - OrderedMappingIdSet, OrderedMappingMap, OscDeviceId, OscFeedbackTask, PluginParamIndex, - PluginParams, ProcessorContext, ProjectOptions, ProjectionFeedbackValue, QualifiedMappingId, - QualifiedSource, RawParamValue, RealTimeMappingUpdate, RealTimeTargetUpdate, - RealearnMonitoringFxParameterValueChangedEvent, RealearnParameterChangePayload, - ReaperConfigChange, ReaperMessage, ReaperSourceFeedbackValue, ReaperTarget, - SharedInstanceState, SourceReleasedEvent, SpecificCompoundFeedbackValue, TargetControlEvent, - TargetValueChangedEvent, UpdatedSingleMappingOnStateEvent, VirtualControlElement, - VirtualSourceValue, + HitInstructionContext, HitInstructionResponse, InfoEvent, InstanceContainer, + InstanceOrchestrationEvent, InstanceStateChanged, IoUpdatedEvent, KeyMessage, MainMapping, + MainSourceMessage, MappingActivationEffect, MappingControlResult, MappingId, MappingInfo, + MessageCaptureEvent, MessageCaptureResult, MidiControlInput, MidiDestination, MidiScanResult, + NormalRealTimeTask, OrderedMappingIdSet, OrderedMappingMap, OscDeviceId, OscFeedbackTask, + PluginParamIndex, PluginParams, ProcessorContext, ProjectOptions, ProjectionFeedbackValue, + QualifiedMappingId, QualifiedSource, RawParamValue, RealTimeMappingUpdate, + RealTimeTargetUpdate, RealearnMonitoringFxParameterValueChangedEvent, + RealearnParameterChangePayload, ReaperConfigChange, ReaperMessage, ReaperSourceFeedbackValue, + ReaperTarget, SharedInstanceState, SourceReleasedEvent, SpecificCompoundFeedbackValue, + TargetControlEvent, TargetValueChangedEvent, UpdatedSingleMappingOnStateEvent, + VirtualControlElement, VirtualSourceValue, }; use derive_more::Display; use enum_map::EnumMap; @@ -1802,6 +1802,12 @@ impl MainProcessor { && self.basics.settings.control_input == ControlInput::Osc(*device_id) } + pub fn process_info_event(&mut self, evt: &InfoEvent) { + self.basics + .event_handler + .handle_event_ignoring_error(DomainEvent::Info(evt)); + } + pub fn process_reaper_message(&mut self, evt: ControlEvent<&ReaperMessage>) { // First process internally. // Convenience: Send all feedback whenever a MIDI device is connected. diff --git a/main/src/domain/mod.rs b/main/src/domain/mod.rs index 6ecd3b9f6..bf1e43cb6 100644 --- a/main/src/domain/mod.rs +++ b/main/src/domain/mod.rs @@ -140,3 +140,6 @@ pub use control_event::*; mod lua_support; pub use lua_support::*; + +mod info_event; +pub use info_event::*; diff --git a/main/src/domain/real_time_processor.rs b/main/src/domain/real_time_processor.rs index 19c7c51c3..e9910bacd 100644 --- a/main/src/domain/real_time_processor.rs +++ b/main/src/domain/real_time_processor.rs @@ -278,6 +278,8 @@ impl RealTimeProcessor { ); } // Clear existing mappings (without deallocating) + // TODO-high-ms2 If there's a mapping with a FlexibleMidiSourceScript and this + // contains Lua code, the Lua Drop handler allocates! (lua.rs line 1739) self.mappings[compartment].clear(); // Set new mappings self.mappings[compartment].extend(mappings.into_iter().map(|m| (m.id(), m))); diff --git a/main/src/infrastructure/ui/main_panel.rs b/main/src/infrastructure/ui/main_panel.rs index 96ddae7e3..e1eb72402 100644 --- a/main/src/infrastructure/ui/main_panel.rs +++ b/main/src/infrastructure/ui/main_panel.rs @@ -1,5 +1,5 @@ use crate::infrastructure::ui::{ - bindings::root, util, AppInstance, HeaderPanel, IndependentPanelManager, MappingRowsPanel, + bindings::root, util, HeaderPanel, IndependentPanelManager, MappingRowsPanel, SharedAppInstance, SharedIndependentPanelManager, SharedMainState, }; @@ -15,8 +15,8 @@ use crate::application::{ use crate::base::when; use crate::domain::ui_util::format_tags_as_csv; use crate::domain::{ - Compartment, MappingId, MappingMatchedEvent, ProjectionFeedbackValue, QualifiedMappingId, - TargetControlEvent, TargetValueChangedEvent, + Compartment, InfoEvent, MappingId, MappingMatchedEvent, ProjectionFeedbackValue, + QualifiedMappingId, TargetControlEvent, TargetValueChangedEvent, }; use crate::infrastructure::plugin::{App, RealearnPluginParameters}; use crate::infrastructure::server::http::{ @@ -24,6 +24,7 @@ use crate::infrastructure::server::http::{ }; use crate::infrastructure::ui::util::{header_panel_height, parse_tags_from_csv}; use base::SoundPlayer; +use helgoboss_allocator::undesired_allocation_count; use rxrust::prelude::*; use std::rc::{Rc, Weak}; use std::sync; @@ -238,9 +239,18 @@ impl MainPanel { } fn invalidate_version_text(&self) { + use std::fmt::Write; + let mut text = format!("ReaLearn {}", App::detailed_version_label()); + if cfg!(debug_assertions) { + let _ = write!( + &mut text, + " | Undesired allocations: {}", + undesired_allocation_count() + ); + } self.view .require_control(root::ID_MAIN_PANEL_VERSION_TEXT) - .set_text(format!("ReaLearn {}", App::detailed_version_label())); + .set_text(text); } fn invalidate_all_controls(&self) { @@ -325,6 +335,14 @@ impl MainPanel { self.handle_affected_own(affected); } + fn handle_info_event(self: SharedView, event: &InfoEvent) { + match event { + InfoEvent::UndesiredAllocationCountChanged => { + self.invalidate_version_text(); + } + } + } + fn handle_affected_own(self: SharedView, affected: Affected) { use Affected::*; use SessionProp::*; @@ -564,6 +582,10 @@ impl SessionUi for Weak { // Update primary GUI upgrade_panel(self).handle_affected(affected, initiator); } + + fn handle_info_event(&self, event: &InfoEvent) { + upgrade_panel(self).handle_info_event(event); + } } fn upgrade_panel(panel: &Weak) -> Rc { diff --git a/playtime-clip-engine b/playtime-clip-engine index 39f2634a5..3125f7f28 160000 --- a/playtime-clip-engine +++ b/playtime-clip-engine @@ -1 +1 @@ -Subproject commit 39f2634a5be0358bae6b71e2eec48faffd784624 +Subproject commit 3125f7f2873ded93d65428758c9f07770a711b18