use super::*;
pub fn on_exec(thr: &Thread) {
if PERF_TASK_ACTIVE.load(Ordering::Acquire) == 0 {
return;
}
let now = now_ns();
thr.perf_context().with_counters(|counters| {
for ptc in counters.iter() {
if ptc.run_state.lock().is_stopping() {
continue;
}
if ptc.enable_on_exec && !ptc.enabled.swap(true, Ordering::AcqRel) {
ptc.enabled_at_ns.store(now, Ordering::Release);
}
}
});
perf_sched_in(thr);
}
pub(in crate::perf) fn visible_tgid(
ptc: &PerTaskCounter,
identity: &PidIdentity,
) -> Option<TgidNumber> {
identity
.visible_number_in(ptc.observer)
.map(TgidNumber::from)
}
pub(in crate::perf) fn visible_tid(
ptc: &PerTaskCounter,
identity: &PidIdentity,
) -> Option<TidNumber> {
identity
.visible_number_in(ptc.observer)
.map(TidNumber::from)
}
pub(in crate::perf) fn sideband_target(
ptc: &PerTaskCounter,
thread: &Thread,
) -> Option<SidebandTarget> {
if !(ptc.want_comm || ptc.want_mmap2 || ptc.want_task) {
return None;
}
let ring = ptc.output.lock().effective()?.0;
let pid = visible_tgid(ptc, &thread.proc_data.identity())?;
let tid = visible_tid(ptc, &thread.pid_identity())?;
Some(SidebandTarget {
ring,
sample_type: ptc.sample_type,
sample_id_all: ptc.sample_id_all,
id: ptc.sample_id.load(Ordering::Relaxed),
pid,
tid,
})
}
pub fn on_task_exit(thr: &Thread) {
let counters = thr.perf_context().close_and_snapshot();
if counters.is_empty() {
return;
}
for ptc in &counters {
if ptc.want_task
&& let Some(target) = sideband_target(ptc, thr)
{
let pid = target.pid;
let tid = target.tid;
let parent = thr.proc_data.proc.parent().and_then(|parent| {
let number = parent.identity().visible_number_in(ptc.observer)?;
Some((TgidNumber::from(number), TidNumber::from(number)))
});
sideband::emit_exit(
&target,
pid,
parent.map(|(pid, _)| pid),
tid,
parent.map(|(_, tid)| tid),
);
}
}
release_task_counters(thr, &counters);
}
pub(crate) fn on_scheduler_task_exit(thr: &Thread) {
let counters = thr.perf_context().close_and_snapshot();
release_task_counters(thr, &counters);
}
fn release_task_counters(thr: &Thread, counters: &[Arc<PerTaskCounter>]) {
for ptc in counters {
if let Err(error) = free_hw(ptc) {
warn!(
"perf: task-exit failed to quiesce counter {:?} on tid {}: {error}",
ptc.counter,
thr.tid()
);
}
}
}
pub(crate) fn free_hw(ptc: &Arc<PerTaskCounter>) -> crate::StarryResult<()> {
if ptc.resources_released() {
return Ok(());
}
let close_action = ptc.run_state.lock().begin_close();
let stop_result = match close_action {
PmuCloseAction::AlreadyClosed | PmuCloseAction::Complete => Ok(()),
PmuCloseAction::Stop(lease) => cpu_worker::stop_task_counter(Arc::clone(ptc), lease),
};
stop_result?;
let Some(resource_claim) = ptc.resources.claim() else {
return Ok(());
};
ptc.publish_rdpmc_inactive();
if !ptc.is_family_root() {
if let Some(family) = ptc.family() {
family.retire_child(ptc);
}
ptc.clear_family_output();
}
crate::perf::hw_allocation::free_counter(ptc.counter);
if resource_claim == PmuResourceClaim::Published {
let previous = PERF_TASK_ACTIVE.fetch_sub(1, Ordering::AcqRel);
assert!(previous > 0, "task perf active count underflow");
}
Ok(())
}