Skip to content

Commit

Permalink
Display counter for undesired allocations (debug builds only)
Browse files Browse the repository at this point in the history
  • Loading branch information
helgoboss committed Nov 16, 2023
1 parent 1c21347 commit e26c76e
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 28 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions allocator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -19,6 +20,12 @@ thread_local! {
static ALLOC_PERMIT_COUNT: Cell<u32> = 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, F: FnOnce() -> T>(func: F) -> T {
// no-op
Expand Down Expand Up @@ -196,10 +203,11 @@ impl<I, D> HelgobossAllocator<I, D> {
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()
);
Expand Down
21 changes: 13 additions & 8 deletions main/src/application/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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()
Expand Down
14 changes: 14 additions & 0 deletions main/src/domain/control_surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub struct RealearnControlSurfaceMiddleware<EH: DomainEventHandler> {
reaper_config_change_detector: ReaperConfigChangeDetector,
control_surface_event_sender: SenderToNormalThread<ControlSurfaceEvent<'static>>,
control_surface_event_receiver: crossbeam_channel::Receiver<ControlSurfaceEvent<'static>>,
last_undesired_allocation_count: u32,
}

pub enum RealearnControlSurfaceMainTask<EH: DomainEventHandler> {
Expand Down Expand Up @@ -214,11 +215,24 @@ impl<EH: DomainEventHandler> RealearnControlSurfaceMiddleware<EH> {
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
Expand Down
3 changes: 2 additions & 1 deletion main/src/domain/eventing.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::domain::{
Compartment, CompoundMappingTarget, ControlLogContext, ControlLogEntry, MappingId,
Compartment, CompoundMappingTarget, ControlLogContext, ControlLogEntry, InfoEvent, MappingId,
MessageCaptureResult, PluginParamIndex, PluginParams, ProjectionFeedbackValue,
QualifiedMappingId, RawParamValue,
};
Expand All @@ -22,6 +22,7 @@ pub enum DomainEvent<'a> {
},
UpdatedAllParameters(PluginParams),
TargetValueChanged(TargetValueChangedEvent<'a>),
Info(&'a InfoEvent),
ProjectionFeedback(ProjectionFeedbackValue),
MappingMatched(MappingMatchedEvent),
TargetControlled(TargetControlEvent),
Expand Down
4 changes: 4 additions & 0 deletions main/src/domain/info_event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[derive(Debug)]
pub enum InfoEvent {
UndesiredAllocationCountChanged,
}
30 changes: 18 additions & 12 deletions main/src/domain/main_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1802,6 +1802,12 @@ impl<EH: DomainEventHandler> MainProcessor<EH> {
&& 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.
Expand Down
3 changes: 3 additions & 0 deletions main/src/domain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,6 @@ pub use control_event::*;

mod lua_support;
pub use lua_support::*;

mod info_event;
pub use info_event::*;
2 changes: 2 additions & 0 deletions main/src/domain/real_time_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
Expand Down
30 changes: 26 additions & 4 deletions main/src/infrastructure/ui/main_panel.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::infrastructure::ui::{
bindings::root, util, AppInstance, HeaderPanel, IndependentPanelManager, MappingRowsPanel,
bindings::root, util, HeaderPanel, IndependentPanelManager, MappingRowsPanel,
SharedAppInstance, SharedIndependentPanelManager, SharedMainState,
};

Expand All @@ -15,15 +15,16 @@ 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::{
send_projection_feedback_to_subscribed_clients, send_updated_controller_routing,
};
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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -325,6 +335,14 @@ impl MainPanel {
self.handle_affected_own(affected);
}

fn handle_info_event(self: SharedView<Self>, event: &InfoEvent) {
match event {
InfoEvent::UndesiredAllocationCountChanged => {
self.invalidate_version_text();
}
}
}

fn handle_affected_own(self: SharedView<Self>, affected: Affected<SessionProp>) {
use Affected::*;
use SessionProp::*;
Expand Down Expand Up @@ -564,6 +582,10 @@ impl SessionUi for Weak<MainPanel> {
// 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<MainPanel>) -> Rc<MainPanel> {
Expand Down
2 changes: 1 addition & 1 deletion playtime-clip-engine

0 comments on commit e26c76e

Please sign in to comment.