proof-cascade: the raw-reader pin counts the subscriptions it hands the reader (a laptop registers IOReport channels as it runs) #543
1 changed files with 91 additions and 48 deletions
|
|
@ -3800,47 +3800,59 @@ mod macos_ioreport {
|
|||
/// cumulative values of the CPU and GPU energy channels, in channel
|
||||
/// order.
|
||||
fn read(&self) -> Option<OracleRead> {
|
||||
unsafe {
|
||||
let smp = IOReportCreateSamples(self.sub, self.ch, std::ptr::null());
|
||||
if smp.is_null() {
|
||||
return None;
|
||||
}
|
||||
let key = CFStringCreateWithCString(
|
||||
std::ptr::null_mut(),
|
||||
c"IOReportChannels".as_ptr() as *const _,
|
||||
K_CFSTRING_ENCODING_UTF8,
|
||||
);
|
||||
let mut read = OracleRead { group: Group { energy_model: 0, all: 0 }, cpu: Vec::new(), gpu: Vec::new() };
|
||||
if !key.is_null() {
|
||||
let arr = CFDictionaryGetValue(smp, key as *const _) as CFArrayRef;
|
||||
CFRelease(key as CFTypeRef);
|
||||
if !arr.is_null() {
|
||||
for i in 0..CFArrayGetCount(arr) {
|
||||
let it = CFArrayGetValueAtIndex(arr, i);
|
||||
if it.is_null() {
|
||||
continue;
|
||||
}
|
||||
read.group.all += 1;
|
||||
let group = cfstring_to_string(IOReportChannelGetGroup(it)).unwrap_or_default();
|
||||
let name = cfstring_to_string(IOReportChannelGetChannelName(it)).unwrap_or_default();
|
||||
let em = group.eq_ignore_ascii_case("Energy Model");
|
||||
read.group.energy_model += usize::from(em);
|
||||
let rail = |group: String, name: String| OracleRail {
|
||||
group,
|
||||
name,
|
||||
value: IOReportSimpleGetIntegerValue(it, 0) as i64,
|
||||
};
|
||||
if name.ends_with("CPU Energy") {
|
||||
read.cpu.push(rail(group, name));
|
||||
} else if em && name == "GPU Energy" {
|
||||
read.gpu.push(rail(group, name));
|
||||
}
|
||||
// SAFETY: the subscription is live until drop.
|
||||
unsafe { oracle_read(self.sub, self.ch) }
|
||||
}
|
||||
}
|
||||
|
||||
/// The oracle's read of any live subscription, with its own sampling
|
||||
/// and parsing (not `energy_model_channels`): what `OracleSub::read`
|
||||
/// reads of its own, and what the raw-reader pin counts of the
|
||||
/// subscriptions it hands the reader.
|
||||
///
|
||||
/// # Safety
|
||||
/// `sub` and `ch` must be a live subscription and its channels.
|
||||
unsafe fn oracle_read(sub: IOReportSubscriptionRef, ch: CFMutableDictionaryRef) -> Option<OracleRead> {
|
||||
unsafe {
|
||||
let smp = IOReportCreateSamples(sub, ch, std::ptr::null());
|
||||
if smp.is_null() {
|
||||
return None;
|
||||
}
|
||||
let key = CFStringCreateWithCString(
|
||||
std::ptr::null_mut(),
|
||||
c"IOReportChannels".as_ptr() as *const _,
|
||||
K_CFSTRING_ENCODING_UTF8,
|
||||
);
|
||||
let mut read = OracleRead { group: Group { energy_model: 0, all: 0 }, cpu: Vec::new(), gpu: Vec::new() };
|
||||
if !key.is_null() {
|
||||
let arr = CFDictionaryGetValue(smp, key as *const _) as CFArrayRef;
|
||||
CFRelease(key as CFTypeRef);
|
||||
if !arr.is_null() {
|
||||
for i in 0..CFArrayGetCount(arr) {
|
||||
let it = CFArrayGetValueAtIndex(arr, i);
|
||||
if it.is_null() {
|
||||
continue;
|
||||
}
|
||||
read.group.all += 1;
|
||||
let group = cfstring_to_string(IOReportChannelGetGroup(it)).unwrap_or_default();
|
||||
let name = cfstring_to_string(IOReportChannelGetChannelName(it)).unwrap_or_default();
|
||||
let em = group.eq_ignore_ascii_case("Energy Model");
|
||||
read.group.energy_model += usize::from(em);
|
||||
let rail = |group: String, name: String| OracleRail {
|
||||
group,
|
||||
name,
|
||||
value: IOReportSimpleGetIntegerValue(it, 0) as i64,
|
||||
};
|
||||
if name.ends_with("CPU Energy") {
|
||||
read.cpu.push(rail(group, name));
|
||||
} else if em && name == "GPU Energy" {
|
||||
read.gpu.push(rail(group, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
CFRelease(smp);
|
||||
Some(read)
|
||||
}
|
||||
CFRelease(smp);
|
||||
Some(read)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4445,10 +4457,17 @@ mod macos_ioreport {
|
|||
/// The raw reader reads the subscription it is handed, every time.
|
||||
/// Handed the probe's own Energy Model subscription and an all-channel
|
||||
/// one (`open()`) in turn, it lists the group's channels, then every
|
||||
/// channel, then the group's again — as many as the oracle counts in
|
||||
/// each. A reader that hands back an earlier read (stale for the
|
||||
/// process) fails here whether or not any channel moves, which the GPU
|
||||
/// freshness pin cannot promise on a host whose GPU is idle.
|
||||
/// channel, then the group's again — as many as the oracle's own reader
|
||||
/// counts in each. A reader that hands back an earlier read (stale for
|
||||
/// the process) fails here whether or not any channel moves, which the
|
||||
/// GPU freshness pin cannot promise on a host whose GPU is idle.
|
||||
///
|
||||
/// The oracle counts the two subscriptions the reader is handed, not
|
||||
/// one of its own: an all-channel subscription holds the channels
|
||||
/// registered when it opened, and a laptop registers more as it runs
|
||||
/// (12,862 when the oracle's opened, 13,014 a moment later for
|
||||
/// `open()`'s, on an M5 Max), so a third subscription is no measure of
|
||||
/// these two.
|
||||
#[test]
|
||||
fn the_raw_reader_reads_the_subscription_it_is_given_every_time() {
|
||||
let Some(oracle) = OracleSub::open() else {
|
||||
|
|
@ -4456,10 +4475,17 @@ mod macos_ioreport {
|
|||
assert_ne!(ioreport_status(), IoReportStatus::Live, "Live with no IOReport");
|
||||
return;
|
||||
};
|
||||
let group = oracle.read().expect("the oracle's subscription yields a sample").group;
|
||||
let own = oracle.read().expect("the oracle's subscription yields a sample").group;
|
||||
let probe = EnergyModelSub::open().expect("the oracle reads IOReport; the probe's group subscription does not open");
|
||||
// SAFETY: open returns owned, non-null refs, released below.
|
||||
let (sub, ch) = unsafe { open() }.expect("the probe's group opens, a subscription to every channel does not");
|
||||
// SAFETY: both subscriptions are live until released below.
|
||||
let (want_g, want_a) = unsafe {
|
||||
(
|
||||
oracle_read(probe.sub, probe.ch).expect("the oracle reads the group subscription").group,
|
||||
oracle_read(sub, ch).expect("the oracle reads the all-channel subscription").group,
|
||||
)
|
||||
};
|
||||
let mut listed = Vec::new();
|
||||
for _ in 0..3 {
|
||||
// SAFETY: both subscriptions are live until released below.
|
||||
|
|
@ -4473,17 +4499,34 @@ mod macos_ioreport {
|
|||
CFRelease(ch as CFTypeRef);
|
||||
}
|
||||
eprintln!(
|
||||
"ioreport raw reader: (group, every channel) listed {listed:?}; the oracle counts ({}, {})",
|
||||
group.energy_model, group.all
|
||||
"ioreport raw reader: (group, every channel) listed {listed:?}; the oracle counts ({}, {}) in the \
|
||||
subscriptions handed over, ({}, {}) in its own",
|
||||
want_g.all, want_a.all, own.energy_model, own.all
|
||||
);
|
||||
assert_eq!(
|
||||
want_g.energy_model, want_g.all,
|
||||
"the probe's subscription holds {} channels, {} of them the Energy Model group's",
|
||||
want_g.all, want_g.energy_model
|
||||
);
|
||||
assert_eq!(
|
||||
want_a.energy_model, want_g.all,
|
||||
"the all-channel subscription holds {} Energy Model channels, the probe's {}",
|
||||
want_a.energy_model, want_g.all
|
||||
);
|
||||
assert!(
|
||||
want_g.all < want_a.all,
|
||||
"the group's {} channels are not fewer than every channel's {}",
|
||||
want_g.all,
|
||||
want_a.all
|
||||
);
|
||||
for (i, (g, a)) in listed.iter().enumerate() {
|
||||
assert_eq!(
|
||||
(*g, *a),
|
||||
(group.energy_model, group.all),
|
||||
(want_g.all, want_a.all),
|
||||
"read {i}: the raw reader listed {g} channels for the group and {a} for every channel; the oracle \
|
||||
counts {} and {}: it did not read the subscription it was handed",
|
||||
group.energy_model,
|
||||
group.all
|
||||
counts {} and {} in those subscriptions: it did not read the subscription it was handed",
|
||||
want_g.all,
|
||||
want_a.all
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue