Description
hey i fixed a bunch of memory leaks in here
there is a last leak i'm facing:
leaks stdout: Process: leak [58086]
Path: /Users/USER/Documents/*/leak
Load Address: 0x102f40000
Identifier: leak
Version: 0
Code Type: ARM64
Platform: macOS
Parent Process: bash [40171]
Date/Time: 2024-08-30 11:09:49.548 +0200
Launch Time: 2024-08-30 11:09:48.532 +0200
OS Version: macOS 14.5 (23F79)
Report Version: 7
Analysis Tool: /Applications/Xcode.app/Contents/Developer/usr/bin/leaks
Analysis Tool Version: Xcode 15.4 (15F31d)
Physical footprint: 5249K
Physical footprint (peak): 5249K
Idle exit: untracked
----
leaks Report Version: 4.0, multi-line stacks
Process 58086: 8540 nodes malloced for 948 KB
Process 58086: 1 leak for 224 total leaked bytes.
STACK OF 1 INSTANCE OF 'ROOT LEAK: <SCStreamConfiguration>':
23 dyld 0x19fa6e0e0 start + 2360
22 leak 0x102f4ae1c main + 36
21 leak 0x102f5469c std::rt::lang_start::hbf253debbfb5da83 + 84 rt.rs:158
20 leak 0x102fbabe8 std::rt::lang_start_internal::h27a134f18d582a1e + 640
19 leak 0x102f546d0 std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::hd7f53490fb601a3d + 28 rt.rs:159
18 leak 0x102f45964 std::sys_common::backtrace::__rust_begin_short_backtrace::hbd676431ce47ec6b + 24 backtrace.rs:161
17 leak 0x102f50344 core::ops::function::FnOnce::call_once::h87f13f6285a6f5bc + 20 function.rs:250
16 leak 0x102f4ad38 leak::main::h76222d92e030bf7f + 2548 leak.rs:79
15 leak 0x102f4dd0c screencapturekit::sc_stream::SCStream::new::hc1a712a51f90081e + 68 sc_stream.rs:0
14 leak 0x102f4dc20 _$LT$T$u20$as$u20$core..convert..Into$LT$U$GT$$GT$::into::he8db2d20efc619ba + 12 mod.rs:760
13 leak 0x102f57804 screencapturekit::sc_stream_configuration::_$LT$impl$u20$core..convert..From$LT$screencapturekit..sc_stream_configuration..SCStreamConfiguration$GT$$u20$for$u20$objc_id..id..Id$LT$screencapturekit_sys..stream_configuration..UnsafeStreamConfigurationRef$GT$$GT$::from::hf24e8a374332729a + 48
12 leak 0x102f577cc _$LT$T$u20$as$u20$core..convert..Into$LT$U$GT$$GT$::into::hfbac22df0571c6a9 + 12
11 leak 0x102f78ab8 screencapturekit_sys::stream_configuration::_$LT$impl$u20$core..convert..From$LT$screencapturekit_sys..stream_configuration..UnsafeStreamConfiguration$GT$$u20$for$u20$objc_id..id..Id$LT$screencapturekit_sys..stream_configuration..UnsafeStreamConfigurationRef$GT$$GT$::from::h303bbc276092c257 + 564
10 leak 0x102f6f9b0 objc::message::platform::send_unverified::hecceebaf7d334a9f + 136
9 leak 0x102f98ce4 objc::exception::try::hd989aeece3baa958 + 12
8 leak 0x102f68248 objc_exception::try::hbe9fd473da645d90 + 72
7 leak 0x102f63318 objc_exception::try_no_ret::h19840b29b3cee5c9 + 144
6 leak 0x102fa4944 RustObjCExceptionTryCatch + 36
5 leak 0x102f66810 objc_exception::try_no_ret::try_objc_execute_closure::hfc53324dcf5dbd7e + 76
4 leak 0x102f69c6c objc_exception::try::_$u7b$$u7b$closure$u7d$$u7d$::ha391d7eab5a0b2df + 44
3 leak 0x102f713f8 objc::message::platform::send_unverified::_$u7b$$u7b$closure$u7d$$u7d$::h56aa810409fb6d0b + 60
2 leak 0x102f8f5e0 _$LT$$LP$$RP$$u20$as$u20$objc..message..MessageArguments$GT$::invoke::h9cfcb104498431d2 + 72
1 libobjc.A.dylib 0x19fa25d84 _objc_rootAllocWithZone + 44
0 libsystem_malloc.dylib 0x19fc30d1c _malloc_zone_calloc_instrumented_or_legacy + 240
====
1 (224 bytes) ROOT LEAK: <SCStreamConfiguration 0x11e730130> [224]
theory of the problem
• Improper release of Objective-C object in Rust-to-ObjC conversion
• Retain cycle between Rust and Objective-C objects
• Exception thrown during object initialization, bypassing normal cleanup
• Autorelease pool not properly set up or drained
• Incorrect implementation of Drop trait for Rust wrapper
• Memory management mismatch between Rust's ownership model and ObjC's reference counting
• Unhandled edge case in exception handling code
• Thread-safety issue causing object to be retained on another thread
• Incorrect use of unsafe code in FFI layer
• Hidden retain in Apple's SCStreamConfiguration implementation
things i tried to fix
- using autoreleasepool
- impl my own autoreleasepool
- using verify message
- drop manually
- impl drop for unsafestreamconf
- print stuff everywhere
- count retain
- use panic unwind rust feat
- commenting out properties (still leak on the
init
msg_send) - other stuff
anything else i should try?
how to reproduce
add this to examples/leak.rs
use std::process::Command;
use screencapturekit::{
cm_sample_buffer::CMSampleBuffer,
sc_content_filter::{InitParams, SCContentFilter},
sc_error_handler::StreamErrorHandler,
sc_output_handler::{SCStreamOutputType, StreamOutput},
sc_shareable_content::SCShareableContent,
sc_stream::SCStream,
sc_stream_configuration::{PixelFormat, SCStreamConfiguration},
sc_types::base::CMTime,
};
use screencapturekit_sys::{
content_filter::{UnsafeContentFilter, UnsafeInitParams},
shareable_content::UnsafeSCShareableContent,
};
pub struct Capturer {}
impl Capturer {
pub fn new() -> Self {
println!("Capturer initialized");
Capturer {}
}
}
impl StreamErrorHandler for Capturer {
fn on_error(&self) {
eprintln!("ERROR!");
}
}
impl StreamOutput for Capturer {
fn did_output_sample_buffer(&self, _sample: CMSampleBuffer, _of_type: SCStreamOutputType) {
println!("New frame recvd");
}
}
fn main() {
println!("Starting");
for _ in 0..1 {
// Repeat the process multiple times to amplify leaks
// Create SCShareableContent and SCContentFilter
let display = SCShareableContent::current().displays.pop().unwrap();
let windows = SCShareableContent::current().windows;
// Create multiple filters
let _filter1 = SCContentFilter::new(InitParams::DisplayExcludingWindows(
display.clone(),
windows,
));
let _filter2 = SCContentFilter::new(InitParams::Display(display.clone()));
let _filter3 =
SCContentFilter::new(InitParams::DisplayExcludingWindows(display.clone(), vec![]));
// Create multiple configurations
let _config1 = SCStreamConfiguration {
width: 1920,
height: 1080,
..Default::default()
};
let _config2 = SCStreamConfiguration {
width: 1280,
height: 720,
..Default::default()
};
// Create and immediately drop streams
let init_params = InitParams::Display(display);
let filter = SCContentFilter::new(init_params);
let mut sc_stream = SCStream::new(filter, _config1, Capturer {});
let output = Capturer {};
sc_stream.add_output(output, SCStreamOutputType::Screen);
// Force drop of sc_stream
drop(sc_stream);
}
// Get the current process ID
let pid = std::process::id();
// Run the 'leaks' command
let output = Command::new("leaks")
.args(&[pid.to_string()])
.output()
.expect("Failed to execute leaks command");
// Check the output for leaks
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
println!("leaks stdout: {}", stdout);
println!("leaks stderr: {}", stderr);
}
RUST_BACKTRACE=1 MallocStackLogging=1 cargo run --example leak
thank you
related issue: