pf2 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +2 -1
- data/ext/pf2/src/backtrace.rs +1 -0
- data/ext/pf2/src/lib.rs +2 -0
- data/ext/pf2/src/ruby_internal_apis.rs +47 -0
- data/ext/pf2/src/signal_scheduler/configuration.rs +1 -1
- data/ext/pf2/src/signal_scheduler/timer_installer.rs +58 -112
- data/ext/pf2/src/signal_scheduler.rs +3 -3
- data/lib/pf2/version.rb +1 -1
- metadata +3 -6
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: d576aec57e600cc77194fabb8c1b7b3c782c4ab76a03ec3b0f24581eb4c317ae
         | 
| 4 | 
            +
              data.tar.gz: a290d23df802601977622f1afe39f1f2d6cd92a9350b25d203e97c3b6da6db16
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: a8d72fa4ccb3395f0fdd9203885c1e0cb8a00429597a6dd756e80fb70e0d8092c084992bd9595ee21789a4aaf2550e2284b6d3f81b448dc330088b325d822d85
         | 
| 7 | 
            +
              data.tar.gz: 8f93c98574ef7077336232bb1b51a4af1ac4ba2561914a299209a5c2879fe9af3e8998c8df264836f698fea05c8d028f7ae985de3c18e9889f05568ec2740bc1
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,5 +1,14 @@ | |
| 1 1 | 
             
            ## [Unreleased]
         | 
| 2 2 |  | 
| 3 | 
            +
            ### Added
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            - New option: `track_all_threads`
         | 
| 6 | 
            +
              - When true, all Threads will be tracked regardless of the `threads` option.
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            ### Removed
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            - The `track_new_threads` option was removed in favor of the `track_all_threads` option.
         | 
| 11 | 
            +
             | 
| 3 12 |  | 
| 4 13 | 
             
            ## [0.3.0] - 2024-02-05
         | 
| 5 14 |  | 
    
        data/README.md
    CHANGED
    
    | @@ -8,6 +8,7 @@ Notable Capabilites | |
| 8 8 |  | 
| 9 9 | 
             
            - Can accurately track multiple Ruby Threads' activity
         | 
| 10 10 | 
             
            - Sampling interval can be set based on per-Thread CPU usage
         | 
| 11 | 
            +
            - Can record native (C-level) stack traces side-by-side with Ruby traces
         | 
| 11 12 |  | 
| 12 13 | 
             
            Usage
         | 
| 13 14 | 
             
            --------
         | 
| @@ -57,7 +58,7 @@ Pf2.start( | |
| 57 58 | 
             
              threads: [],            # Array<Thread>: A list of Ruby Threads to be tracked (default: `Thread.list`)
         | 
| 58 59 | 
             
              time_mode: :cpu,        # `:cpu` or `:wall`: The sampling timer's mode
         | 
| 59 60 | 
             
                                      # (default: `:cpu` for SignalScheduler, `:wall` for TimerThreadScheduler)
         | 
| 60 | 
            -
               | 
| 61 | 
            +
              track_all_threads: true # Boolean: Whether to track all Threads regardless of `threads` option
         | 
| 61 62 | 
             
                                      # (default: false)
         | 
| 62 63 | 
             
            )
         | 
| 63 64 | 
             
            ```
         | 
    
        data/ext/pf2/src/backtrace.rs
    CHANGED
    
    
    
        data/ext/pf2/src/lib.rs
    CHANGED
    
    
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            #![allow(non_snake_case)]
         | 
| 2 | 
            +
            #![allow(non_camel_case_types)]
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            use libc::{clockid_t, pthread_getcpuclockid, pthread_t};
         | 
| 5 | 
            +
            use rb_sys::{rb_check_typeddata, rb_data_type_struct, RTypedData, VALUE};
         | 
| 6 | 
            +
            use std::ffi::{c_char, c_int};
         | 
| 7 | 
            +
            use std::mem::MaybeUninit;
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            // Types and structs from Ruby 3.4.0.
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            type rb_nativethread_id_t = libc::pthread_t;
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            #[repr(C)]
         | 
| 14 | 
            +
            struct rb_native_thread {
         | 
| 15 | 
            +
                _padding_serial: [c_char; 4], // rb_atomic_t
         | 
| 16 | 
            +
                _padding_vm: *mut c_int,      // struct rb_vm_struct
         | 
| 17 | 
            +
                thread_id: rb_nativethread_id_t,
         | 
| 18 | 
            +
                // ...
         | 
| 19 | 
            +
            }
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            #[repr(C)]
         | 
| 22 | 
            +
            struct rb_thread_struct {
         | 
| 23 | 
            +
                _padding_lt_node: [c_char; 16], // struct ccan_list_node
         | 
| 24 | 
            +
                _padding_self: VALUE,
         | 
| 25 | 
            +
                _padding_ractor: *mut c_int, // rb_ractor_t
         | 
| 26 | 
            +
                _padding_vm: *mut c_int,     // rb_vm_t
         | 
| 27 | 
            +
                nt: *mut rb_native_thread,
         | 
| 28 | 
            +
                // ...
         | 
| 29 | 
            +
            }
         | 
| 30 | 
            +
            type rb_thread_t = rb_thread_struct;
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            /// Reimplementation of the internal RTYPEDDATA_TYPE macro.
         | 
| 33 | 
            +
            unsafe fn RTYPEDDATA_TYPE(obj: VALUE) -> *const rb_data_type_struct {
         | 
| 34 | 
            +
                let typed: *mut RTypedData = obj as *mut RTypedData;
         | 
| 35 | 
            +
                (*typed).type_
         | 
| 36 | 
            +
            }
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            unsafe fn rb_thread_ptr(thread: VALUE) -> *mut rb_thread_t {
         | 
| 39 | 
            +
                unsafe { rb_check_typeddata(thread, RTYPEDDATA_TYPE(thread)) as *mut rb_thread_t }
         | 
| 40 | 
            +
            }
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            pub unsafe fn rb_thread_getcpuclockid(thread: VALUE) -> clockid_t {
         | 
| 43 | 
            +
                let mut cid: clockid_t = MaybeUninit::zeroed().assume_init();
         | 
| 44 | 
            +
                let pthread_id: pthread_t = (*(*rb_thread_ptr(thread)).nt).thread_id;
         | 
| 45 | 
            +
                pthread_getcpuclockid(pthread_id, &mut cid as *mut clockid_t);
         | 
| 46 | 
            +
                cid
         | 
| 47 | 
            +
            }
         | 
| @@ -1,31 +1,28 @@ | |
| 1 | 
            -
            use std::collections:: | 
| 1 | 
            +
            use std::collections::HashSet;
         | 
| 2 2 | 
             
            use std::ffi::c_void;
         | 
| 3 3 | 
             
            use std::mem;
         | 
| 4 4 | 
             
            use std::mem::ManuallyDrop;
         | 
| 5 5 | 
             
            use std::ptr::null_mut;
         | 
| 6 | 
            +
            use std::sync::Arc;
         | 
| 6 7 | 
             
            use std::sync::{Mutex, RwLock};
         | 
| 7 | 
            -
            use std::{collections::HashSet, sync::Arc};
         | 
| 8 8 |  | 
| 9 9 | 
             
            use rb_sys::*;
         | 
| 10 10 |  | 
| 11 | 
            -
            use crate::signal_scheduler::SignalHandlerArgs;
         | 
| 12 | 
            -
             | 
| 13 11 | 
             
            use super::configuration::Configuration;
         | 
| 14 12 | 
             
            use crate::profile::Profile;
         | 
| 13 | 
            +
            use crate::ruby_internal_apis::rb_thread_getcpuclockid;
         | 
| 14 | 
            +
            use crate::signal_scheduler::{cstr, SignalHandlerArgs};
         | 
| 15 15 |  | 
| 16 | 
            -
             | 
| 17 | 
            -
            // but we're not doing so since (1) Ruby does not expose the pthread_self() of a Ruby Thread
         | 
| 18 | 
            -
            // (which is actually stored in th->nt->thread_id), and (2) pthread_getcpuclockid(3) is not portable
         | 
| 19 | 
            -
            // in the first place (e.g. not available on macOS).
         | 
| 16 | 
            +
            #[derive(Debug)]
         | 
| 20 17 | 
             
            pub struct TimerInstaller {
         | 
| 21 | 
            -
                 | 
| 18 | 
            +
                inner: Box<Mutex<Inner>>,
         | 
| 22 19 | 
             
            }
         | 
| 23 20 |  | 
| 24 | 
            -
             | 
| 21 | 
            +
            #[derive(Debug)]
         | 
| 22 | 
            +
            struct Inner {
         | 
| 25 23 | 
             
                configuration: Configuration,
         | 
| 26 | 
            -
                 | 
| 27 | 
            -
                 | 
| 28 | 
            -
                profile: Arc<RwLock<Profile>>,
         | 
| 24 | 
            +
                pub profile: Arc<RwLock<Profile>>,
         | 
| 25 | 
            +
                known_threads: HashSet<VALUE>,
         | 
| 29 26 | 
             
            }
         | 
| 30 27 |  | 
| 31 28 | 
             
            impl TimerInstaller {
         | 
| @@ -35,153 +32,102 @@ impl TimerInstaller { | |
| 35 32 | 
             
                    configuration: Configuration,
         | 
| 36 33 | 
             
                    profile: Arc<RwLock<Profile>>,
         | 
| 37 34 | 
             
                ) {
         | 
| 38 | 
            -
                    let  | 
| 39 | 
            -
                         | 
| 35 | 
            +
                    let installer = Self {
         | 
| 36 | 
            +
                        inner: Box::new(Mutex::new(Inner {
         | 
| 40 37 | 
             
                            configuration: configuration.clone(),
         | 
| 41 | 
            -
                            registered_pthread_ids: HashSet::new(),
         | 
| 42 | 
            -
                            kernel_thread_id_to_ruby_thread_map: HashMap::new(),
         | 
| 43 38 | 
             
                            profile,
         | 
| 39 | 
            +
                            known_threads: HashSet::new(),
         | 
| 44 40 | 
             
                        })),
         | 
| 45 41 | 
             
                    };
         | 
| 46 42 |  | 
| 47 | 
            -
                    let  | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
                             | 
| 51 | 
            -
                             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
                        // Spawn a no-op Thread to fire the event hook
         | 
| 55 | 
            -
                        // (at least 2 Ruby Threads must be active for the RESUMED hook to be fired)
         | 
| 56 | 
            -
                        rb_thread_create(Some(Self::do_nothing), null_mut());
         | 
| 57 | 
            -
                    };
         | 
| 43 | 
            +
                    if let Ok(mut inner) = installer.inner.try_lock() {
         | 
| 44 | 
            +
                        for ruby_thread in configuration.target_ruby_threads.iter() {
         | 
| 45 | 
            +
                            let ruby_thread: VALUE = *ruby_thread;
         | 
| 46 | 
            +
                            inner.known_threads.insert(ruby_thread);
         | 
| 47 | 
            +
                            inner.register_timer_to_ruby_thread(ruby_thread);
         | 
| 48 | 
            +
                        }
         | 
| 49 | 
            +
                    }
         | 
| 58 50 |  | 
| 59 | 
            -
                    if configuration. | 
| 51 | 
            +
                    if configuration.track_all_threads {
         | 
| 52 | 
            +
                        let ptr = Box::into_raw(installer.inner);
         | 
| 60 53 | 
             
                        unsafe {
         | 
| 54 | 
            +
                            // TODO: Clean up this hook when the profiling session ends
         | 
| 61 55 | 
             
                            rb_internal_thread_add_event_hook(
         | 
| 62 | 
            -
                                Some(Self:: | 
| 63 | 
            -
                                 | 
| 56 | 
            +
                                Some(Self::on_thread_resume),
         | 
| 57 | 
            +
                                RUBY_INTERNAL_THREAD_EVENT_RESUMED,
         | 
| 64 58 | 
             
                                ptr as *mut c_void,
         | 
| 65 59 | 
             
                            );
         | 
| 66 60 | 
             
                        };
         | 
| 67 61 | 
             
                    }
         | 
| 68 62 | 
             
                }
         | 
| 69 63 |  | 
| 70 | 
            -
                 | 
| 71 | 
            -
                    Qnil.into()
         | 
| 72 | 
            -
                }
         | 
| 73 | 
            -
             | 
| 74 | 
            -
                // Thread resume callback
         | 
| 64 | 
            +
                // Thread start callback
         | 
| 75 65 | 
             
                unsafe extern "C" fn on_thread_resume(
         | 
| 76 66 | 
             
                    _flag: rb_event_flag_t,
         | 
| 77 67 | 
             
                    data: *const rb_internal_thread_event_data,
         | 
| 78 68 | 
             
                    custom_data: *mut c_void,
         | 
| 79 69 | 
             
                ) {
         | 
| 80 | 
            -
                     | 
| 81 | 
            -
                    let internal =
         | 
| 82 | 
            -
                        unsafe { ManuallyDrop::new(Box::from_raw(custom_data as *mut Mutex<Internal>)) };
         | 
| 83 | 
            -
                    let mut internal = internal.lock().unwrap();
         | 
| 84 | 
            -
             | 
| 85 | 
            -
                    // Check if the current thread is a target Ruby Thread
         | 
| 86 | 
            -
                    let current_ruby_thread: VALUE = unsafe { (*data).thread };
         | 
| 87 | 
            -
                    if !internal
         | 
| 88 | 
            -
                        .configuration
         | 
| 89 | 
            -
                        .target_ruby_threads
         | 
| 90 | 
            -
                        .contains(¤t_ruby_thread)
         | 
| 91 | 
            -
                    {
         | 
| 92 | 
            -
                        return;
         | 
| 93 | 
            -
                    }
         | 
| 70 | 
            +
                    let ruby_thread: VALUE = unsafe { (*data).thread };
         | 
| 94 71 |  | 
| 95 | 
            -
                    //  | 
| 96 | 
            -
                    let  | 
| 97 | 
            -
                     | 
| 98 | 
            -
                        .registered_pthread_ids
         | 
| 99 | 
            -
                        .contains(¤t_pthread_id)
         | 
| 100 | 
            -
                    {
         | 
| 101 | 
            -
                        return;
         | 
| 102 | 
            -
                    }
         | 
| 103 | 
            -
             | 
| 104 | 
            -
                    // Record the pthread ID of the current thread
         | 
| 105 | 
            -
                    internal.registered_pthread_ids.insert(current_pthread_id);
         | 
| 106 | 
            -
                    // Keep a mapping from kernel thread ID to Ruby Thread
         | 
| 107 | 
            -
                    internal
         | 
| 108 | 
            -
                        .kernel_thread_id_to_ruby_thread_map
         | 
| 109 | 
            -
                        .insert(unsafe { libc::gettid() }, current_ruby_thread);
         | 
| 110 | 
            -
             | 
| 111 | 
            -
                    Self::register_timer_to_current_thread(
         | 
| 112 | 
            -
                        &internal.configuration,
         | 
| 113 | 
            -
                        &internal.profile,
         | 
| 114 | 
            -
                        &internal.kernel_thread_id_to_ruby_thread_map,
         | 
| 115 | 
            -
                    );
         | 
| 116 | 
            -
             | 
| 117 | 
            -
                    // TODO: Remove the hook when all threads have been registered
         | 
| 118 | 
            -
                }
         | 
| 72 | 
            +
                    // A pointer to Box<Inner> is passed as custom_data
         | 
| 73 | 
            +
                    let inner = unsafe { ManuallyDrop::new(Box::from_raw(custom_data as *mut Mutex<Inner>)) };
         | 
| 74 | 
            +
                    let mut inner = inner.lock().unwrap();
         | 
| 119 75 |  | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
                     | 
| 125 | 
            -
                ) {
         | 
| 126 | 
            -
                    // The SignalScheduler (as a Ruby obj) should be passed as custom_data
         | 
| 127 | 
            -
                    let internal =
         | 
| 128 | 
            -
                        unsafe { ManuallyDrop::new(Box::from_raw(custom_data as *mut Mutex<Internal>)) };
         | 
| 129 | 
            -
                    let mut internal = internal.lock().unwrap();
         | 
| 130 | 
            -
             | 
| 131 | 
            -
                    let current_ruby_thread: VALUE = unsafe { (*data).thread };
         | 
| 132 | 
            -
                    internal
         | 
| 133 | 
            -
                        .configuration
         | 
| 134 | 
            -
                        .target_ruby_threads
         | 
| 135 | 
            -
                        .insert(current_ruby_thread);
         | 
| 76 | 
            +
                    if !inner.known_threads.contains(&ruby_thread) {
         | 
| 77 | 
            +
                        inner.known_threads.insert(ruby_thread);
         | 
| 78 | 
            +
                        // Install a timer for the thread
         | 
| 79 | 
            +
                        inner.register_timer_to_ruby_thread(ruby_thread);
         | 
| 80 | 
            +
                    }
         | 
| 136 81 | 
             
                }
         | 
| 82 | 
            +
            }
         | 
| 137 83 |  | 
| 138 | 
            -
             | 
| 139 | 
            -
                fn  | 
| 140 | 
            -
                    configuration: &Configuration,
         | 
| 141 | 
            -
                    profile: &Arc<RwLock<Profile>>,
         | 
| 142 | 
            -
                    kernel_thread_id_to_ruby_thread_map: &HashMap<libc::pid_t, VALUE>,
         | 
| 143 | 
            -
                ) {
         | 
| 144 | 
            -
                    let current_pthread_id = unsafe { libc::pthread_self() };
         | 
| 145 | 
            -
                    let context_ruby_thread: VALUE = unsafe {
         | 
| 146 | 
            -
                        *(kernel_thread_id_to_ruby_thread_map
         | 
| 147 | 
            -
                            .get(&(libc::gettid()))
         | 
| 148 | 
            -
                            .unwrap())
         | 
| 149 | 
            -
                    };
         | 
| 150 | 
            -
             | 
| 84 | 
            +
            impl Inner {
         | 
| 85 | 
            +
                fn register_timer_to_ruby_thread(&self, ruby_thread: VALUE) {
         | 
| 151 86 | 
             
                    // NOTE: This Box is never dropped
         | 
| 152 87 | 
             
                    let signal_handler_args = Box::new(SignalHandlerArgs {
         | 
| 153 | 
            -
                        profile: Arc::clone(profile),
         | 
| 154 | 
            -
                        context_ruby_thread,
         | 
| 88 | 
            +
                        profile: Arc::clone(&self.profile),
         | 
| 89 | 
            +
                        context_ruby_thread: ruby_thread,
         | 
| 155 90 | 
             
                    });
         | 
| 156 91 |  | 
| 92 | 
            +
                    // rb_funcall deadlocks when called within a THREAD_EVENT_STARTED hook
         | 
| 93 | 
            +
                    let kernel_thread_id: i32 = i32::try_from(unsafe {
         | 
| 94 | 
            +
                        rb_num2int(rb_funcall(
         | 
| 95 | 
            +
                            ruby_thread,
         | 
| 96 | 
            +
                            rb_intern(cstr!("native_thread_id")), // kernel thread ID
         | 
| 97 | 
            +
                            0,
         | 
| 98 | 
            +
                        ))
         | 
| 99 | 
            +
                    })
         | 
| 100 | 
            +
                    .unwrap();
         | 
| 101 | 
            +
             | 
| 157 102 | 
             
                    // Create a signal event
         | 
| 158 103 | 
             
                    let mut sigevent: libc::sigevent = unsafe { mem::zeroed() };
         | 
| 159 104 | 
             
                    // Note: SIGEV_THREAD_ID is Linux-specific. In other platforms, we would need to
         | 
| 160 | 
            -
                    // " | 
| 105 | 
            +
                    // "trampoline" the signal as any pthread can receive the signal.
         | 
| 161 106 | 
             
                    sigevent.sigev_notify = libc::SIGEV_THREAD_ID;
         | 
| 162 | 
            -
                    sigevent.sigev_notify_thread_id =
         | 
| 163 | 
            -
                        unsafe { libc::syscall(libc::SYS_gettid).try_into().unwrap() }; // The kernel thread ID
         | 
| 107 | 
            +
                    sigevent.sigev_notify_thread_id = kernel_thread_id;
         | 
| 164 108 | 
             
                    sigevent.sigev_signo = libc::SIGALRM;
         | 
| 165 109 | 
             
                    // Pass required args to the signal handler
         | 
| 166 110 | 
             
                    sigevent.sigev_value.sival_ptr = Box::into_raw(signal_handler_args) as *mut c_void;
         | 
| 167 111 |  | 
| 168 112 | 
             
                    // Create and configure timer to fire every _interval_ ms of CPU time
         | 
| 169 113 | 
             
                    let mut timer: libc::timer_t = unsafe { mem::zeroed() };
         | 
| 170 | 
            -
                    let clockid = match configuration.time_mode {
         | 
| 171 | 
            -
                        crate::signal_scheduler::TimeMode::CpuTime =>  | 
| 114 | 
            +
                    let clockid = match self.configuration.time_mode {
         | 
| 115 | 
            +
                        crate::signal_scheduler::TimeMode::CpuTime => unsafe {
         | 
| 116 | 
            +
                            rb_thread_getcpuclockid(ruby_thread)
         | 
| 117 | 
            +
                        },
         | 
| 172 118 | 
             
                        crate::signal_scheduler::TimeMode::WallTime => libc::CLOCK_MONOTONIC,
         | 
| 173 119 | 
             
                    };
         | 
| 174 120 | 
             
                    let err = unsafe { libc::timer_create(clockid, &mut sigevent, &mut timer) };
         | 
| 175 121 | 
             
                    if err != 0 {
         | 
| 176 122 | 
             
                        panic!("timer_create failed: {}", err);
         | 
| 177 123 | 
             
                    }
         | 
| 178 | 
            -
                    let itimerspec = Self::duration_to_itimerspec(&configuration.interval);
         | 
| 124 | 
            +
                    let itimerspec = Self::duration_to_itimerspec(&self.configuration.interval);
         | 
| 179 125 | 
             
                    let err = unsafe { libc::timer_settime(timer, 0, &itimerspec, null_mut()) };
         | 
| 180 126 | 
             
                    if err != 0 {
         | 
| 181 127 | 
             
                        panic!("timer_settime failed: {}", err);
         | 
| 182 128 | 
             
                    }
         | 
| 183 129 |  | 
| 184 | 
            -
                    log::debug!("timer registered for thread {}",  | 
| 130 | 
            +
                    log::debug!("timer registered for thread {}", ruby_thread);
         | 
| 185 131 | 
             
                }
         | 
| 186 132 |  | 
| 187 133 | 
             
                fn duration_to_itimerspec(duration: &std::time::Duration) -> libc::itimerspec {
         | 
| @@ -56,7 +56,7 @@ impl SignalScheduler { | |
| 56 56 | 
             
                                rb_intern(cstr!("interval_ms")),
         | 
| 57 57 | 
             
                                rb_intern(cstr!("threads")),
         | 
| 58 58 | 
             
                                rb_intern(cstr!("time_mode")),
         | 
| 59 | 
            -
                                rb_intern(cstr!(" | 
| 59 | 
            +
                                rb_intern(cstr!("track_all_threads")),
         | 
| 60 60 | 
             
                            ]
         | 
| 61 61 | 
             
                            .as_mut_ptr(),
         | 
| 62 62 | 
             
                            0,
         | 
| @@ -99,7 +99,7 @@ impl SignalScheduler { | |
| 99 99 | 
             
                    } else {
         | 
| 100 100 | 
             
                        configuration::TimeMode::CpuTime
         | 
| 101 101 | 
             
                    };
         | 
| 102 | 
            -
                    let  | 
| 102 | 
            +
                    let track_all_threads: bool = if kwargs_values[3] != Qundef as VALUE {
         | 
| 103 103 | 
             
                        RTEST(kwargs_values[3])
         | 
| 104 104 | 
             
                    } else {
         | 
| 105 105 | 
             
                        false
         | 
| @@ -117,7 +117,7 @@ impl SignalScheduler { | |
| 117 117 | 
             
                        interval,
         | 
| 118 118 | 
             
                        target_ruby_threads,
         | 
| 119 119 | 
             
                        time_mode,
         | 
| 120 | 
            -
                         | 
| 120 | 
            +
                        track_all_threads,
         | 
| 121 121 | 
             
                    });
         | 
| 122 122 |  | 
| 123 123 | 
             
                    Qnil.into()
         | 
    
        data/lib/pf2/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,13 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: pf2
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.4.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Daisuke Aritomo
         | 
| 8 | 
            -
            autorequire:
         | 
| 9 8 | 
             
            bindir: exe
         | 
| 10 9 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2024- | 
| 10 | 
            +
            date: 2024-03-22 00:00:00.000000000 Z
         | 
| 12 11 | 
             
            dependencies:
         | 
| 13 12 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 13 | 
             
              name: rake-compiler
         | 
| @@ -52,7 +51,6 @@ dependencies: | |
| 52 51 | 
             
                - - ">="
         | 
| 53 52 | 
             
                  - !ruby/object:Gem::Version
         | 
| 54 53 | 
             
                    version: '0'
         | 
| 55 | 
            -
            description:
         | 
| 56 54 | 
             
            email:
         | 
| 57 55 | 
             
            - osyoyu@osyoyu.com
         | 
| 58 56 | 
             
            executables:
         | 
| @@ -156,6 +154,7 @@ files: | |
| 156 154 | 
             
            - ext/pf2/src/profile_serializer.rs
         | 
| 157 155 | 
             
            - ext/pf2/src/ringbuffer.rs
         | 
| 158 156 | 
             
            - ext/pf2/src/ruby_init.rs
         | 
| 157 | 
            +
            - ext/pf2/src/ruby_internal_apis.rs
         | 
| 159 158 | 
             
            - ext/pf2/src/sample.rs
         | 
| 160 159 | 
             
            - ext/pf2/src/siginfo_t.c
         | 
| 161 160 | 
             
            - ext/pf2/src/signal_scheduler.rs
         | 
| @@ -175,7 +174,6 @@ metadata: | |
| 175 174 | 
             
              homepage_uri: https://github.com/osyoyu/pf2
         | 
| 176 175 | 
             
              source_code_uri: https://github.com/osyoyu/pf2
         | 
| 177 176 | 
             
              changelog_uri: https://github.com/osyoyu/pf2/blob/master/CHANGELOG.md
         | 
| 178 | 
            -
            post_install_message:
         | 
| 179 177 | 
             
            rdoc_options: []
         | 
| 180 178 | 
             
            require_paths:
         | 
| 181 179 | 
             
            - lib
         | 
| @@ -191,7 +189,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 191 189 | 
             
                  version: '0'
         | 
| 192 190 | 
             
            requirements: []
         | 
| 193 191 | 
             
            rubygems_version: 3.6.0.dev
         | 
| 194 | 
            -
            signing_key:
         | 
| 195 192 | 
             
            specification_version: 4
         | 
| 196 193 | 
             
            summary: Yet another Ruby profiler
         | 
| 197 194 | 
             
            test_files: []
         |