use std::collections::BTreeMap;
#[must_use]
pub fn thread_name_multiset(names: &[String]) -> BTreeMap<String, usize> {
let mut counts = BTreeMap::new();
for name in names {
*counts.entry(name.clone()).or_insert(0) += 1;
}
counts
}
#[cfg(target_os = "macos")]
#[must_use]
pub fn process_thread_names() -> Vec<String> {
use std::ffi::CStr;
unsafe extern "C" {
static mach_task_self_: libc::mach_port_t;
fn mach_port_deallocate(
task: libc::mach_port_t,
name: libc::mach_port_t,
) -> libc::kern_return_t;
}
let mut names = Vec::new();
unsafe {
let task = mach_task_self_;
let mut act_list: libc::thread_act_array_t = std::ptr::null_mut();
let mut count: libc::mach_msg_type_number_t = 0;
if libc::task_threads(task, &mut act_list, &mut count) != 0 {
return names;
}
for index in 0..count as usize {
let port = *act_list.add(index);
let pthread = libc::pthread_from_mach_thread_np(port);
if pthread != 0 {
let mut buffer = [0_i8; libc::MAXTHREADNAMESIZE];
if libc::pthread_getname_np(pthread, buffer.as_mut_ptr(), buffer.len()) == 0 {
let raw = CStr::from_ptr(buffer.as_ptr());
if let Ok(name) = raw.to_str()
&& !name.is_empty()
{
names.push(name.to_owned());
}
}
}
let _ = mach_port_deallocate(task, port);
}
let _ = libc::vm_deallocate(
task,
act_list as libc::vm_address_t,
count as usize * std::mem::size_of::<libc::thread_act_t>(),
);
}
names
}
#[cfg(target_os = "linux")]
#[must_use]
pub fn process_thread_names() -> Vec<String> {
let mut names = Vec::new();
let Ok(entries) = std::fs::read_dir("/proc/self/task") else {
return names;
};
for entry in entries.flatten() {
if let Ok(name) = std::fs::read_to_string(entry.path().join("comm")) {
let trimmed = name.trim_end_matches('\n');
if !trimmed.is_empty() {
names.push(trimmed.to_owned());
}
}
}
names
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
#[must_use]
pub fn process_thread_names() -> Vec<String> {
Vec::new()
}