Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tons of memory leaks #52

Closed
louis030195 opened this issue Aug 30, 2024 · 1 comment
Closed

tons of memory leaks #52

louis030195 opened this issue Aug 30, 2024 · 1 comment

Comments

@louis030195
Copy link

louis030195 commented Aug 30, 2024

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:

@1313
Copy link
Collaborator

1313 commented Aug 31, 2024

Great findings! This project has not gotten much love and I've moved on to doing a rewrite using Core Foundation, which simplifies memory management.

See pr: #27.

But I would still recommend using something like cidre or just building the screencapturekit bindings for audio yourself.

But going forward, I'll update the readme for this crate to make it more clear that it is not production ready. And see where things are when I get time to complete the PR.

Looking at cidr, it's taking a much more systematic approach. And by using its building blocks it should be fairly simple to add any missing parts (audio capture).

Thanks again for your effort here.

@1313 1313 closed this as not planned Won't fix, can't repro, duplicate, stale Sep 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants