fix floors measuring single lib instead all libs
This commit is contained in:
parent
22ab973f0c
commit
a9665e55f9
2 changed files with 58 additions and 14 deletions
|
|
@ -2124,17 +2124,30 @@ fn validate_live_cmd(
|
||||||
fn verify_live_cmd(prof: &GameProfile, pid: u32, dir: &Path, lib: &str) -> Result<OracleCounts> {
|
fn verify_live_cmd(prof: &GameProfile, pid: u32, dir: &Path, lib: &str) -> Result<OracleCounts> {
|
||||||
let img = load_lib(dir, lib)?;
|
let img = load_lib(dir, lib)?;
|
||||||
let classes = schema::enumerate_schema(&img);
|
let classes = schema::enumerate_schema(&img);
|
||||||
// The SAME floor `produce` applies, not merely non-empty: this half compares an offline read against a
|
// A floor, not merely non-empty: this half compares an offline read against a live one through the same
|
||||||
// live one through the same `CI_*` constants on the same bytes, so a reshape's survivors agree with
|
// `CI_*` constants on the same bytes, so a reshape's survivors agree with themselves at ~1.0 and a
|
||||||
// themselves at ~1.0 and a handful of classes looks like a clean run. See
|
// handful of classes looks like a clean run.
|
||||||
// `GameProfile::min_schema_classes`.
|
//
|
||||||
|
// It is `min_schema_classes_LIB`, because this enumerates ONE library while `produce`'s floor counts the
|
||||||
|
// union across all of them — see `GameProfile::min_schema_classes_lib`. Only `server_lib` has a
|
||||||
|
// calibrated count, so any other library is enumerated and reported rather than judged against a number
|
||||||
|
// that does not describe it.
|
||||||
|
if lib == prof.server_lib {
|
||||||
ensure!(
|
ensure!(
|
||||||
classes.len() >= prof.min_schema_classes,
|
classes.len() >= prof.min_schema_classes_lib,
|
||||||
"offline schema derivation found {} classes in {lib} (floor {}) — refusing to verify a schema \
|
"offline schema derivation found {} classes in {lib} (floor {}) — refusing to verify a schema \
|
||||||
whose class table collapsed",
|
whose class table collapsed",
|
||||||
classes.len(),
|
classes.len(),
|
||||||
prof.min_schema_classes
|
prof.min_schema_classes_lib
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"NOTE: {lib} is not {}, which is the only library with a calibrated class floor — \
|
||||||
|
enumerated {} classes, collapse check SKIPPED",
|
||||||
|
prof.server_lib,
|
||||||
|
classes.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let live = live::LiveProcess::attach(pid)?;
|
let live = live::LiveProcess::attach(pid)?;
|
||||||
let base = live
|
let base = live
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,16 @@ pub struct GameProfile {
|
||||||
/// offline/live layout comparison reads the same bytes through the same `CI_*` constants, so whatever
|
/// offline/live layout comparison reads the same bytes through the same `CI_*` constants, so whatever
|
||||||
/// survives a reshape agrees with itself.
|
/// survives a reshape agrees with itself.
|
||||||
pub min_schema_classes: usize,
|
pub min_schema_classes: usize,
|
||||||
|
/// Collapse floor for the schema CLASS table read from a SINGLE library — the live oracle's population.
|
||||||
|
///
|
||||||
|
/// Distinct from [`min_schema_classes`](Self::min_schema_classes), and the two may never be shared: that
|
||||||
|
/// one counts the union across every mapped library, this one counts `server_lib` alone, and the union is
|
||||||
|
/// roughly twice as large. A floor calibrated on the union rejects every healthy build when applied here,
|
||||||
|
/// because the honest single-library count sits below it by construction.
|
||||||
|
///
|
||||||
|
/// Calibrated the same way as its sibling — well under the observed count, a collapse detector rather
|
||||||
|
/// than a tight bound — and it only applies to `server_lib`, the one library whose count is calibrated.
|
||||||
|
pub min_schema_classes_lib: usize,
|
||||||
/// Collapse floor for the DERIVED function tiers — `core + high_confidence`.
|
/// Collapse floor for the DERIVED function tiers — `core + high_confidence`.
|
||||||
///
|
///
|
||||||
/// Every table read out of the binary has one of these; the tool's headline product did not, and the
|
/// Every table read out of the binary has one of these; the tool's headline product did not, and the
|
||||||
|
|
@ -252,9 +262,11 @@ pub const CS2: GameProfile = GameProfile {
|
||||||
// observed live: 271 of 300 bindings attributed across 24 classes
|
// observed live: 271 of 300 bindings attributed across 24 classes
|
||||||
min_vscript_classed: 150,
|
min_vscript_classed: 150,
|
||||||
min_schema_enums: 250,
|
min_schema_enums: 250,
|
||||||
// CS2 recovers 1,899. A floor at 1,200 is well clear of build-to-build drift and nowhere near
|
// CS2 recovers 1,899 across every mapped library. A floor at 1,200 is well clear of build-to-build
|
||||||
// the range a `SchemaClassInfoData_t` reshape would leave.
|
// drift and nowhere near the range a `SchemaClassInfoData_t` reshape would leave.
|
||||||
min_schema_classes: 1_200,
|
min_schema_classes: 1_200,
|
||||||
|
// libserver.so alone holds 852 of those; the live oracle reads that library only.
|
||||||
|
min_schema_classes_lib: 550,
|
||||||
// CS2 ships 1,086 core + 2,899 high-confidence = 3,985.
|
// CS2 ships 1,086 core + 2,899 high-confidence = 3,985.
|
||||||
min_core_functions: 2_500,
|
min_core_functions: 2_500,
|
||||||
game_key: "csgo",
|
game_key: "csgo",
|
||||||
|
|
@ -365,8 +377,10 @@ pub const DOTA: GameProfile = GameProfile {
|
||||||
// observed live: 1,638 of 1,841 bindings attributed across 63 classes
|
// observed live: 1,638 of 1,841 bindings attributed across 63 classes
|
||||||
min_vscript_classed: 900,
|
min_vscript_classed: 900,
|
||||||
min_schema_enums: 350,
|
min_schema_enums: 350,
|
||||||
// Dota recovers 2,962.
|
// Dota recovers 2,962 across every mapped library.
|
||||||
min_schema_classes: 2_000,
|
min_schema_classes: 2_000,
|
||||||
|
// libserver.so alone holds 1,916 of those; the live oracle reads that library only.
|
||||||
|
min_schema_classes_lib: 1_250,
|
||||||
// Dota ships 1,096 + 4,047 = 5,143.
|
// Dota ships 1,096 + 4,047 = 5,143.
|
||||||
min_core_functions: 3_000,
|
min_core_functions: 3_000,
|
||||||
game_key: "dota",
|
game_key: "dota",
|
||||||
|
|
@ -450,6 +464,23 @@ pub const DOTA: GameProfile = GameProfile {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
/// The two class floors count DIFFERENT populations — the all-library union and `server_lib` alone —
|
||||||
|
/// so a profile that gives them the same value has calibrated one of them against the other's
|
||||||
|
/// population, which rejects every healthy build on whichever site got the larger number.
|
||||||
|
#[test]
|
||||||
|
fn the_single_library_class_floor_is_strictly_below_the_all_library_one() {
|
||||||
|
for prof in [&CS2, &DOTA] {
|
||||||
|
assert!(
|
||||||
|
prof.min_schema_classes_lib < prof.min_schema_classes,
|
||||||
|
"{}: single-library floor {} must sit below the all-library floor {} — one library \
|
||||||
|
cannot hold more classes than every library",
|
||||||
|
prof.token,
|
||||||
|
prof.min_schema_classes_lib,
|
||||||
|
prof.min_schema_classes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cs2_launch_args_are_byte_identical_to_the_old_hand_synced_vec() {
|
fn cs2_launch_args_are_byte_identical_to_the_old_hand_synced_vec() {
|
||||||
// The exact arg vec the live launch requires for map="de_dust2", bots=9 — pins the LaunchSpec
|
// The exact arg vec the live launch requires for map="de_dust2", bots=9 — pins the LaunchSpec
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue