Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 945c44c475 | |||
| e58219668c | |||
| 69dcc431ec | |||
| 3e136d7367 | |||
| 2cf7c1d248 | |||
| 0c0209f9a6 | |||
| af71fb11ba | |||
| 564f109143 | |||
| 6737e367b4 | |||
| b5986c61d9 | |||
| 235196beb2 | |||
| ed60970fc2 | |||
| 10adbd57b6 | |||
| 331cde68a7 | |||
| 4a04806d22 | |||
| 75057d8c69 | |||
| 20ad56f3f4 | |||
| 52d1373e81 | |||
| 04d5513af2 | |||
| d7891372f2 | |||
| 54a4ff2149 | |||
| ab35ddd3c2 | |||
| c6c05a1001 | |||
| f2a8ce1305 | |||
| dc2a60f8fc | |||
| 67bc61236a | |||
| 230cef4ef0 | |||
| 215e018a48 | |||
| 35a4612630 | |||
| 923bc737a3 | |||
| ef21571feb | |||
| 12dffb9254 | |||
| 6f793d2521 | |||
| a1f5028fe6 | |||
| cd60e469a1 | |||
| a7efb39685 | |||
| b57595cb4a | |||
| 484f3d3d2d | |||
| 5575e014e7 | |||
| bfc61b745b | |||
| b218321bda | |||
| a8ae19b224 | |||
| aa9af0dcff | |||
| ff44167926 | |||
| 8d75ac8c6c | |||
| 88ee30e0e4 | |||
| 02ea81e1cf | |||
| 47a29dee2b | |||
| b9a03b4274 | |||
| ef8c2d1919 | |||
| 682b73bfe8 | |||
| 642aefe35a | |||
| 7b353d7bae | |||
| bd3143b7d7 | |||
| 16111a5eee |
@@ -242,7 +242,7 @@
|
||||
<PackageVersion Include="ZB.MOM.WW.Secrets.Replicator.AkkaDotNet" Version="0.6.2" />
|
||||
<PackageVersion Include="ZB.MOM.WW.Audit" Version="0.1.0" />
|
||||
<PackageVersion Include="ZB.MOM.WW.Theme" Version="0.5.0" />
|
||||
<PackageVersion Include="ZB.MOM.WW.HistorianGateway.Client" Version="0.4.0" />
|
||||
<PackageVersion Include="ZB.MOM.WW.HistorianGateway.Contracts" Version="0.4.0" />
|
||||
<PackageVersion Include="ZB.MOM.WW.HistorianGateway.Client" Version="1.0.0" />
|
||||
<PackageVersion Include="ZB.MOM.WW.HistorianGateway.Contracts" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+9
-6
@@ -337,7 +337,7 @@ and is registered the same way for every backend.
|
||||
|
||||
> **Cross-repo follow-up — a multi-tag batch `WriteLiveValues` on the gateway contract.** The drain's
|
||||
> unit of work is one gateway round-trip **per historian tag**, because
|
||||
> `ZB.MOM.WW.HistorianGateway.Client` **0.4.0** exposes no RPC that writes values for more than one tag
|
||||
> `ZB.MOM.WW.HistorianGateway.Client` **1.0.0** exposes no RPC that writes values for more than one tag
|
||||
> in a call. Bounded parallelism (`DrainConcurrency`) is therefore the ceiling: it overlaps the
|
||||
> round-trips but cannot remove them, so a wide tag set still pays `⌈tags / concurrency⌉` network
|
||||
> hops per drain pass. Collapsing that to a single hop needs a **batch/multi-tag `WriteLiveValues`** on
|
||||
@@ -345,8 +345,11 @@ and is registered the same way for every backend.
|
||||
> lands, raising `DrainBatchSize` widens each hop's payload but not the hop count — which is why the
|
||||
> concurrency window, not the batch width, is what governs drain throughput today.
|
||||
|
||||
The HistorianGateway **Client and Contracts packages are pinned together at 0.4.0**
|
||||
([historiangw#119](https://gitea.dohertylan.com/dohertj2/historiangw/issues/119)). The removed
|
||||
The HistorianGateway **Client and Contracts packages are pinned together at 1.0.0**
|
||||
([historiangw#131](https://gitea.dohertylan.com/dohertj2/historiangw/issues/131)). The adapter maps
|
||||
`AllowUntrustedServerCertificate` directly and only for TLS: the default verifies the server, while an
|
||||
explicit opt-out applies only when `UseTls=true`. Stale certificate options are ignored on plaintext h2c.
|
||||
This follows the earlier [0.4.0 update](https://gitea.dohertylan.com/dohertj2/historiangw/issues/119). The removed
|
||||
`ReadBlocks` RPC and `ReadBlocksAsync` wrapper were unused here. A partially imported CA PEM bundle now
|
||||
fails client creation instead of accepting a narrower certificate pin; repair the configured bundle if
|
||||
startup reports `ArgumentException` for `CaCertificatePath`.
|
||||
@@ -1848,9 +1851,9 @@ value at **10:00**, not 09:00, and no point ever falls before the window. The in
|
||||
`(start − (k+1)·interval, start − k·interval]`, the earliest one truncated, each stamped with its later edge.
|
||||
|
||||
**SDK parity (#677).** `SdkAggregateParityTests` (unit, `Sql.Common.Tests`) and
|
||||
`ProcessedConformance.Processed_reads_match_the_SDK_aggregate_calculators` (both SQL backends, through the real SQL) run the pinned SDK's
|
||||
own calculators (`AverageAggregateCalculator`, `MinMaxAggregateCalculator`, `CountAggregateCalculator`, the base `AggregateCalculator`
|
||||
for Interpolative, `StatusAggregateCalculator` for PercentGood/Bad) over the same samples, forward and reverse, and compare every
|
||||
`ProcessedConformance.Processed_reads_match_the_SDK_aggregate_calculators` (both SQL backends, through the real SQL) use the preserved
|
||||
1.5.378.182 calculator source through `CompatibilityAggregateFactory`, not the new SDK 2 algorithms, over the same samples,
|
||||
forward and reverse, and compare every
|
||||
interval's timestamp, value, status code and Partial bit. The per-sample comparison covers Average, Minimum, Maximum, Count, Range,
|
||||
Start, End, Delta and Minimum/MaximumActualTime; the time-weighted one TimeAverage, TimeAverage2, Total, Total2, Interpolative and
|
||||
PercentGood/Bad (#680), including datasets whose data ends inside the window and with an interval wholly inside an outage; reverse
|
||||
|
||||
@@ -4,7 +4,7 @@ Execution started 2026-11-10 for [#782](https://gitea.dohertylan.com/dohertj2/lm
|
||||
|
||||
## Baseline and authority
|
||||
|
||||
- Baseline `83ed880e4471344deaf008da9bd028c7e170bdc7` equals fetched `origin/master`; original checkout clean. Dedicated managed worktree, branch `codex/opcua-sdk-2`.
|
||||
- Baseline `83ed880e4471344deaf008da9bd028c7e170bdc7` equaled `origin/master` when execution started; original checkout was clean. Master subsequently advanced to the independent HistorianGateway package update `16111a5e`, merged into the candidate without content conflicts. Dedicated managed worktree, branch `codex/opcua-sdk-2`.
|
||||
- Target remains exact stable SDK `2.0.1`, upstream `07e62f72500cc0923a2bab940653c8c1116fa286`. Tagged source and restored assemblies control API decisions.
|
||||
- Baseline packages are already `1.5.378.182`, not the plan preparation checkout's `.106`. #751 reflection helper exists. AggregateManager application configuration override already exists; preserve it.
|
||||
- #782 previously parked the major upgrade; the user's current explicit execution instruction supersedes that scheduling decision. No deployment authorization overrides the Wonder-box remote-execution hold.
|
||||
@@ -65,11 +65,11 @@ No optional ManagedSession/DI/AOT/cluster redesign, trust relaxation, silent war
|
||||
## Gates
|
||||
|
||||
- G0 baseline/design: accepted for migration, with corrections and external/trust-matrix gaps recorded below.
|
||||
- G1 restored 2.0 graph / break inventory: passed for lane execution; compilation convergence pending.
|
||||
- G2 compilation convergence: pending.
|
||||
- G1 restored 2.0 graph / break inventory: passed; compilation convergence subsequently passed G2.
|
||||
- G2 compilation convergence: passed; whole-solution Release builds have zero warnings/errors and temporary migration assets are removed.
|
||||
- G3 behavioral/security/interoperability: pending.
|
||||
- G4 lifecycle/performance/publish: pending.
|
||||
- Astra security/ownership and release reviews: pending.
|
||||
- G4 lifecycle/performance/publish: failed/open for the canonical RSS gate (#799); scoped client lifecycle/publish results are recorded below.
|
||||
- Astra security/ownership source findings: closed for the reviewed identity, history, audit and browse changes; final release review remains pending.
|
||||
|
||||
No upgraded artifact is approved for release.
|
||||
|
||||
@@ -129,4 +129,423 @@ Restore succeeds without downgrade suppression or source changes. The first comp
|
||||
|
||||
This is the exposed first wave, not the solution-wide final break count: server overrides, removed collections, immutable/null/Variant APIs, certificate manager, and async identity construction block downstream compilation. Log and exact diagnostics: `artifacts/opcua2/d9984028/sdk2-spike/{build.log,diagnostics.txt}`. Later diagnostics are inventoried by lane until G2 convergence; no compile failure is claimed as behavioral success.
|
||||
|
||||
Restored project graphs contain no mixed Foundation SDK versions: every Foundation package (including analyzer-only development transitive GDS assets) is 2.0.1. Host graph resolves Bcl.TimeProvider 10.0.12, RateLimiting 10.0.12, Http.Resilience 10.10.0. G1 is satisfied for lane execution; full build/tests remain required at G2/G3. Frozen boundary contract is above; production and unit paths are split by plan ownership, integration/historian oracle edits coordinator-only.
|
||||
Restored project graphs contain no mixed Foundation SDK versions: every Foundation package (including analyzer-only development transitive GDS assets) is 2.0.1. Host graph resolves Bcl.TimeProvider 10.0.12 and Http.Resilience 10.10.0; client assets resolve RateLimiting 10.0.12, while the Host shared framework prunes that separate package. G1 is satisfied for lane execution; full build/tests remain required at G2/G3. Frozen boundary contract is above; production and unit paths are split by plan ownership, integration/historian oracle edits coordinator-only.
|
||||
|
||||
|
||||
## Integrated implementation and source reviews
|
||||
|
||||
The implementation is on `codex/opcua-sdk-2`, not deployed or merged to master.
|
||||
Master's independently delivered HistorianGateway client/contracts 0.4.0 change
|
||||
`16111a5e` is included, and master ancestry was merged at `88ee30e0` without a
|
||||
content change. historiangw#119 is closed; historiangw#78 deployment is separate.
|
||||
|
||||
The server, client and driver lanes were integrated and validated in isolated
|
||||
worktrees. Astra reviewed the design, aggregate compatibility architecture,
|
||||
identity/peer provenance, certificate ownership, production history continuation
|
||||
store, write conversions/revert ownership, alarm compatibility, and filtered event
|
||||
and audit delivery. No source review is represented as release approval.
|
||||
|
||||
- Identity registry admission is installed before listeners and remains until
|
||||
shutdown. Rejected/disabled credentials cannot fall through to a default identity.
|
||||
SDK role augmentation preserves the application's principal and key ID.
|
||||
- Activation uses public transport peer evidence, scoped across asynchronous work
|
||||
and restored in `finally`. Unknown peers retain the existing process-wide budget;
|
||||
this application has no CIDR admission or reverse-connect policy to invent.
|
||||
- Immutable SDK values are normalized only at existing application boundaries.
|
||||
Scalar numeric leniency, checked one-dimensional conversions, pass-through arrays,
|
||||
byte/ByteString rank, null/empty and detached buffers remain pinned. Matrices remain
|
||||
unsupported. Public SDK-facing client DTOs remain SDK-facing.
|
||||
- Session history tokens are consumed/released through the real SDK store, with
|
||||
exact node/realm, read kind, topology epoch and current authority checks. Existing
|
||||
canonical-query semantics are retained; no undocumented filter fingerprint was
|
||||
introduced.
|
||||
- SDK2 alarm early AlreadyAcked/AlreadyConfirmed checks are adapted only for the
|
||||
current nonempty trunk EventId, preserving application authorization and routing.
|
||||
Write reverts bind the actual node instance and committed attempt, preventing a
|
||||
late failure from undoing a newer write/publish or replacement node.
|
||||
- Audit delivery uses a private, revocable operation context for one exact source
|
||||
and type. Both existing permission callbacks can veto; no global role/node
|
||||
permission array is widened. Ordinary SDK automatic audits remain denied. The
|
||||
encrypted wire test proves exactly one application audit to the Read audience
|
||||
and none to the denied audience.
|
||||
|
||||
A cross-version wire test found an additional SDK2 creation change: `Create`
|
||||
reset an alarm's preassigned reference to the null NodeId, hiding it from its raw
|
||||
parent's hierarchical browse. Restoring HasComponent **after** Create fixed all
|
||||
three supported alarm types in both realms (`fa823378`, integrated `41e65f6a`).
|
||||
All 12 actual-node/wire regressions failed first, then passed. Full server units
|
||||
at that fix: **1115 passed, zero failures, one existing skip**. Astra approved
|
||||
its source scope and the matching separate-process results.
|
||||
|
||||
## Preserved aggregate algorithms and independent oracles
|
||||
|
||||
The owner explicitly chose current application results over changed SDK2
|
||||
aggregate semantics. A private source adapter contains the eight original MIT
|
||||
algorithm/factory files from 1.5.378.182 with original source hashes. It is not an
|
||||
old SDK runtime. A fresh SDK AggregateManager registers the same 37 factories
|
||||
exactly once; calling the base registration first would duplicate diagnostic nodes.
|
||||
|
||||
The separately pinned original-SDK generator produced **6,144 vectors**: the
|
||||
original 4,144 plus 2,000 numeric-width, nonfinite/conversion, tie/picosecond and
|
||||
configuration edge cases. Fixture SHA-256:
|
||||
`2ec2a1ac97f05d199c18da55157f8bbe0276853c3ba73ebf1b509c535c5a7dd8`.
|
||||
Original SDK assembly SHA-256:
|
||||
`d8d9706dea577858325730cd89896826ed82f7bfcf9503ccd0260311663a69f4`.
|
||||
|
||||
All cases pass both directly and through the booted production aggregate manager.
|
||||
Values, CLR types, all status bits, source/server time and picoseconds, exceptions,
|
||||
queue acceptance and incremental/partial results are compared. SDK2 FILETIME zero
|
||||
is explicitly observed as the original CLR unset sentinel; no calculation or
|
||||
expected output was regenerated from the candidate.
|
||||
|
||||
The SQL application's PercentGood policy already differs from SDK1.5.378.182.
|
||||
The same ten of 275 parity tests fail on the original and candidate with identical
|
||||
names/messages. Independent application results and regression expectations stay
|
||||
unchanged; [#796](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/796)
|
||||
tracks that baseline debt. It is not a green test run.
|
||||
|
||||
## Local compatibility and security evidence
|
||||
|
||||
- Full wire integration reached **187 passed, zero failures/skips** in the second
|
||||
CI-equivalent run, excluding the separately gated E2E soak category. It includes
|
||||
all original contracts plus production continuation, trusted-channel, audit,
|
||||
hierarchy and current authority regressions.
|
||||
- Focused final security probes: **17/17**, zero skips. Seven trusted cases cover
|
||||
explicit DER trust, Sign and SignAndEncrypt, invalid credentials, an untrusted
|
||||
client, same-session reactivation, and concurrent IPv4/IPv6 attempts correlated
|
||||
with their own peer before/after an await barrier. Eighteen earlier continuation
|
||||
cases cover raw/modified/events with alias, replacement, rebuild, grant/key
|
||||
revocation and expiry, proving no backend dispatch or saved-data disclosure.
|
||||
- Native current-trunk retry tests use a narrow raw-device grant with zero global
|
||||
roles, then revoke/expire authority and require no route/state/EventId/event
|
||||
change. Scripted retry tests explicitly use the existing cluster grant.
|
||||
A separate real-session original/candidate probe preserves the opaque scripted
|
||||
ID's equipment-scope denial and broad-grant positive. This **preexisting**
|
||||
production feature gap remains [#797](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/797);
|
||||
a slash-qualified synthetic ID is not a valid UI-authored workaround.
|
||||
- Target certificate manager/endpoint identity tests: **7/7** (valid, expired,
|
||||
revoked, missing issuer, issuer usage, hostname and URI). These are manager/API
|
||||
checks, not seven network handshakes.
|
||||
- Corrected a preexisting client creation-policy bug: 2048 meant months, not RSA
|
||||
bits. New certificates use the SDK's 12-month default; existing longer-valid
|
||||
certificates and identical private-key bytes are reused. A real-factory red
|
||||
lifetime case became green; all **324 Shared tests passed**. RSA minimum remains
|
||||
the SDK's separate 2048-bit default.
|
||||
- Separate-process original→candidate, candidate→original, candidate→candidate,
|
||||
and original→original Shared legs plus candidate driver/actual CLI legs:
|
||||
**62/62 passed**. Strict alarm identities remain asserted. Shared/server/driver
|
||||
use explicit mutual DER trust and encrypted API-key sessions; CLI's existing
|
||||
autoaccept is disclosed and is not rejection-policy evidence. The same harness
|
||||
first exposed the hierarchy defect at **60/62**. Commands, artifact hashes,
|
||||
policies and outcomes are checked in under
|
||||
`scripts/validation/opcua-sdk-compatibility/evidence/2026-11-10/`.
|
||||
- The actual unchanged ScadaBridge adapter, SDK1.5.378.106, passed **9 None/API-key
|
||||
cases against each server** (18 total). Strict encrypted connections fail on
|
||||
both original/candidate because its application certificate is unconfigured:
|
||||
[ScadaBridge#121](https://gitea.dohertylan.com/dohertj2/ScadaBridge/issues/121).
|
||||
That is a retained failure, not a secure interoperability pass. Reproducible
|
||||
isolated consumer receipts are in `scripts/validation/scadabridge-sdk-compatibility/`.
|
||||
- Real local SQL Server: **195/195** on each version. QuestDB10.0.1: **233 passed,
|
||||
zero failed, one existing opt-in race-stress skip** on each version. Case maps
|
||||
match exactly. Only task databases/prefixes and the task QuestDB container/volume
|
||||
were removed. See [backend receipts](evidence/2026-11-10-opcua2/backend-matrix.md).
|
||||
This is not real AVEVA evidence.
|
||||
|
||||
## Solution, performance and package evidence
|
||||
|
||||
Whole-solution Release builds pass with **zero warnings/errors**, including all
|
||||
inventory consumers. All restored production Foundation packages are 2.0.1; no
|
||||
MigrationAnalyzer reference/runtime asset remains. The only old SDK is isolated
|
||||
in the explicitly original-SDK validation process outside the solution.
|
||||
|
||||
The first full CI-equivalent run executed **18,459** cases with execution guard
|
||||
passed (floor12,000), but failed203: 192 from an incorrect test-oracle reflection
|
||||
port (repaired and **554/554 Core.Abstractions tests passed**), the ten known
|
||||
PercentGood baseline failures, and a ScriptWorker250ms timing assertion.
|
||||
The latter also fails independently on untouched baseline83ed880e (880.13ms),
|
||||
so no threshold/filter/skip was changed; it is
|
||||
[#798](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/798).
|
||||
The fresh second full solution run produced **63 TRX files, 19,121 total,
|
||||
18,486 executed, 18,475 passed, 11 failed and 635 existing skipped cases**.
|
||||
Its test exit is **1**, execution guard **0** (floor12,000). The exact eleven
|
||||
failures are the same ten PercentGood cases (#796) and one timing case (#798);
|
||||
there are no additional failure classes. This is baseline-matching evidence,
|
||||
**not a green solution run**. The command/logs are retained under ignored
|
||||
`artifacts/opcua2/88ee30e0/solution-tests2/`;
|
||||
[versioned counters and exact failures](evidence/2026-11-10-opcua2/solution-tests2.json)
|
||||
retain the nonzero status. Fixture-backed gates remain separate.
|
||||
|
||||
Three alternating warmed lock measurements per version show medians17→15ms for
|
||||
10k-tag rebuild and81→80ms for50k-node removal, with equal visit counts.
|
||||
See [bounded subset and host-load caveats](evidence/2026-11-10-opcua2/lock-benchmarks.md).
|
||||
The separate 72,000-call warmed native-process experiment records median p95
|
||||
changes +14.95% scalar read, +1.58% scalar write, -16.28% array read, -17.24%
|
||||
array write, plus lower allocated bytes/workload operation. It retains the
|
||||
scalar-read outlier, small-workload scope, host-load and JIT-mode caveats. Neither
|
||||
these samples nor the short cold-tiered measurements certify full G4.
|
||||
|
||||
Four self-contained publishes (Linux ARM64 and Windows x64 Host/CLI) pass with
|
||||
runtime10.0.14, unique SDK DLL basenames, native RID validation and file hashes.
|
||||
Actual Linux Host passes two start/stop cycles with unchanged certificate identity,
|
||||
healthy web checks, real HMAC-key positive/negative cases and strict peer negatives.
|
||||
Console/rolling logs contain zero task-secret occurrences. See
|
||||
[publish/runtime receipts](evidence/2026-11-10-opcua2/publish-smoke.md).
|
||||
Windows service/runtime installation remains
|
||||
[#800](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/800), not inferred from
|
||||
cross-publishing. Approved live AVEVA/consumer/pair validation remains
|
||||
[#801](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/801).
|
||||
|
||||
## Resource finding and long-run scope
|
||||
|
||||
The Mac SDK-server restart soak failed twice around17 minutes at the unchanged
|
||||
initial-RSS+128MiB guard. One run completed250 cycles/62 restarts; managed memory,
|
||||
FDs, cached stores and logical collections remained bounded, but process RSS did
|
||||
not meet its limit. **No two-hour pass is claimed.**
|
||||
[#799](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/799) retains the failed
|
||||
TRX/JSONL and active investigation. Explicit SDK wrapper tracking found created=
|
||||
disposed and no outstanding/unreachable-undisposed wrapper after cleanup;
|
||||
controlled private PFX imports/SDK copies accumulate native Apple Security
|
||||
objects, unlike shared-handle/public-only controls. These controls do not attribute the entire RSS increase or isolate the server
|
||||
from the co-located SDK client. Astra found no safe narrow public copy-policy
|
||||
setting in exact2.0.1. Switching channel/session factories would change lifecycle
|
||||
semantics and requires separate design. No unproven production patch or resource-
|
||||
budget waiver was applied.
|
||||
|
||||
The original Mac PKI-specific descriptor zero was invalid because `/var` and
|
||||
`/private/var` were compared literally. Corrected canonical, directory-bounded
|
||||
instrumentation proves0→1→0 for an open test file and reaps a timed-out probe.
|
||||
The original logical/global-FD evidence remains usable; its PKI-zero claim does
|
||||
not. A corrected90-second smoke is explicitly not two-hour evidence.
|
||||
|
||||
The same-cap self-contained **Linux ARM64 server/node-manager workload also
|
||||
failed**, at **248 cycles / 61 restarts / 903.508 seconds**: RSS353,931,264
|
||||
exceeded351,850,496 bytes (cycle3 baseline+128MiB). Managed memory~20.5MB,
|
||||
descriptors189, PKI descriptors0, cached stores2 and logical collections remained
|
||||
bounded. Runtime10.0.14, SDK 2.0.1; test/container/launcher fail. This broadens
|
||||
#799 beyond macOS; no complete/two-hour pass exists.
|
||||
[Selected Linux failed-run receipts](evidence/2026-11-10-opcua2/server-linux-soak-failed.json)
|
||||
retain hashes, unchanged limits, positive descriptor controls and failure.
|
||||
|
||||
The independent root-owned actual production Shared/driver/live-reinitialize/
|
||||
browser lifecycle run on Linux **passed after 7,200.984 seconds**, with 5,420 cycles,
|
||||
10,840 rounds per product, 10,840 additional live driver reinitializations and
|
||||
43,360 verified certificate-manager disposals. All 543 samples had zero weak product
|
||||
survivors and zero open PKI descriptors. Final managed memory was 3,988,520 bytes,
|
||||
RSS 86,102,016 bytes and global FDs 148. Own certificates remained unchanged; the
|
||||
manager disposal positive control passed. Container/test exit 0 and the real
|
||||
monotonic duration guard passed. [Full counter/hash receipts](../../scripts/validation/opcua-client-lifecycle/evidence/2026-11-10-full/verification.json)
|
||||
record source `c25561c6`, exact SDK 2.0.1 and Linux ARM64 runtime 10.0.3. The later
|
||||
HG 1.0 consumer change is independently tested and does not relabel these artifacts.
|
||||
This is a completed client lifecycle gate, **not an RSS performance-budget or full
|
||||
G4 pass**. Its workload does not replace the failed canonical server soak.
|
||||
G4 and release approval remain open.
|
||||
|
||||
|
||||
## Final integrated source review and diagnostic instrumentation
|
||||
|
||||
Astra (`gpt-6-astra`, xhigh) reviewed integrated source `aa9af0dc` read-only and
|
||||
reported **no new must-fix migration source/security finding**. It rechecked
|
||||
identity registry/rejection, scoped public peer evidence, restricted identity
|
||||
behavior, actual continuation ownership/node/topology binding, temporary audit
|
||||
capability, exact alarm retry guards, write/node attempt ownership and client/
|
||||
driver generation/disposal. Its independently inspected security receipt is
|
||||
17/17 with zero skips. This is source/security review, **not release approval**:
|
||||
Astra retains #799 and explicitly withholds G4/release approval on both platforms.
|
||||
The reviewed production tree remains unchanged by later diagnostic commits.
|
||||
|
||||
Test-only instrumentation now writes the complete captured resource tuple before
|
||||
resource assertions and includes GC committed/heap/fragmentation, pinned/finalizer,
|
||||
JIT method/IL, thread and timer counters. Explicit bounded profile mode preserves
|
||||
the first original budget rejection, marks subsequent samples rejected, and
|
||||
rethrows it before completion; full/diagnostic modes still fail immediately.
|
||||
The full7200-second minimum and every original cap are unchanged. Both descriptor
|
||||
positive/timeout controls passed again, **2/2**, at integrated `b57595cb`.
|
||||
Historical failed-run hashes and source revisions remain exact; they are not
|
||||
relabeled as results from this newer instrumentation.
|
||||
|
||||
The pushed implementation is reviewable as
|
||||
[draft PR#802](https://gitea.dohertylan.com/dohertj2/lmxopcua/pulls/802).
|
||||
It is explicitly held from merge/release, and #782 remains open.
|
||||
|
||||
|
||||
## Completed cross-platform RSS investigation
|
||||
|
||||
The [reproducers, report](../../scripts/validation/opcua-lifecycle-memory/README.md)
|
||||
and [hashed evidence](../../scripts/validation/opcua-lifecycle-memory/evidence.json)
|
||||
preserve four failed canonical workload/profile attempts and the unchanged caps.
|
||||
A 30-minute Linux **diagnostic subset** completed on both versions: candidate
|
||||
768 cycles/192 restarts, original736/184. Candidate retained managed memory was
|
||||
19.25 MB, final RSS334.18 MB, with19,272 certificate wrappers created and disposed
|
||||
and none outstanding. It has **no full-gate pass** and omits canonical history,
|
||||
revocation, topology surgery and network-stall work. The original subset's growing
|
||||
managed/timer footprint cannot provide a favorable steady-state performance
|
||||
comparison denominator.
|
||||
|
||||
EventPipe attributed a large late step to6,690 distinct generated standard-node
|
||||
factory methods receiving Tier1 compilation (49,141,484 cumulative native method
|
||||
bytes). A later180-second interval had zero SDK/factory JIT starts and zero lost
|
||||
events. These code-event sums are not RSS/PSS allocation measurements. This
|
||||
supports a finite compilation burst in the subset; full-workload saturation has
|
||||
not been established. macOS native Apple Security object retention continues
|
||||
independently. No narrow application workaround, factory swap, runtime flag,
|
||||
resource threshold change or acceptance-baseline reset was adopted.
|
||||
|
||||
Astra reviewed the packaged sources/hashes/raw-receipt consistency/sanitization
|
||||
at integrated `6f793d25` and found no must-fix report/source issue. It approved
|
||||
only a **prospective diagnostic saturation design**, recorded in the report:
|
||||
384 fixed canonical cycles/96 restarts plus one settling cycle within1,800 seconds;
|
||||
fixed traced windows321–352 and353–384; zero generated-factory compilations,
|
||||
new SDK assembly loads and lost events; ownership checks active throughout.
|
||||
That experiment had not run at that review; its later diagnostic result is recorded below and cannot pass G4. Matched full acceptance requires
|
||||
an original-SDK adapter for the identical canonical workload and at least7,200
|
||||
seconds **after** a prospectively reviewed fixed warmup. Historical failures and
|
||||
macOS native retention remain open in #799. No release approval was given.
|
||||
|
||||
|
||||
## Included HistorianGateway 1.0 consumer change
|
||||
|
||||
The user's added [historiangw#131](https://gitea.dohertylan.com/dohertj2/historiangw/issues/131)
|
||||
was delivered independently on OtOpcUa master `230cef4e`, then merged into the
|
||||
SDK2 branch at `67bc6123`. Both real published Client/Contracts packages are 1.0.0;
|
||||
the adapter maps `AllowUntrustedServerCertificate = UseTls && configuredOptOut`
|
||||
and retains strict TLS defaults and h2c certificate-option filtering. Astra
|
||||
approved source/security and corrected real-wire tests. Full master Release
|
||||
build: 0 warnings/errors; Gateway 523 passed/6 existing live skips, Runtime1,002
|
||||
passed/3 existing retired-fixture skips, zero failures, execution guards passed.
|
||||
The three new wire tests prove typed TLS rejection before RPC, explicit opt-out
|
||||
success and h2c success. The issue was closed with a verification comment after
|
||||
master push; [immutable package/test receipt](evidence/2026-11-10-historiangw-131.json).
|
||||
Independent actual-package SDK2 Gateway verification also passed 523/0/6.
|
||||
|
||||
The integrated SDK2/HG 1.0 solution built with 0 warnings/errors in 46.91 seconds.
|
||||
CI3 and the corrected follow-up run are recorded below; every command preserves
|
||||
test/guard exits and the 12,000 execution floor. CI2 remains historical pre-HG 1.0 evidence.
|
||||
|
||||
All four self-contained Host/CLI publishes and actual Linux Host two-start/stop
|
||||
checks were repeated on the HG 1.0 graph at `f2a8ce13`: all passed. Both Host
|
||||
artifacts contain the exact published 1.0.0 Client/Contracts DLLs; the Foundation
|
||||
SDK family remains 2.0.1. Six strict HMAC/peer cases, two Good published-CLI reads,
|
||||
preserved certificate identity and zero task-secret matches passed. See the
|
||||
[fresh publication/runtime report](evidence/2026-11-10-opcua2/publish-smoke-hg100.md).
|
||||
Windows actual install/runtime #800 and live/paired-environment #801 remain pending.
|
||||
|
||||
|
||||
## CI3 investigation and corrected final verification
|
||||
|
||||
The post-HG 1.0 full CI3 run at `f2a8ce13` produced **63 TRX, 19,124 total,
|
||||
18,489 executed, 18,472 passed, 17 failed and 635 existing skipped** results.
|
||||
Test exit 1 and execution guard 0 are retained in
|
||||
[the complete summary](evidence/2026-11-10-opcua2/solution-tests3.json).
|
||||
Eleven failures match the established baseline issues #796/#798. Five browser
|
||||
failures came from a task simulator with differing advertised/internal and host
|
||||
ports. Matching those ports restores browser 5/5 and driver 5/5 with no source
|
||||
change. Actual SDK1.5.378.182 browser binaries also fail the different-port
|
||||
advertisement with the same DNS error: [baseline NAT parity](evidence/2026-11-10-opcua2/browser-nat-parity.md).
|
||||
The preexisting browser capability gap is [#806](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/806),
|
||||
not a new SDK2 behavior or a security-policy workaround.
|
||||
|
||||
The final failure was a preexisting Runtime test-ordering race: an EF-presence
|
||||
wait captured the first apply before its later NodeAdded announcement. A gated
|
||||
controlled reproduction proves 1→2 with one EF and one announcement. The narrow
|
||||
repair awaits correlated clean completion replies for both rebuilds, retaining
|
||||
all idempotence assertions and existing budgets. Astra reviewed source causality
|
||||
and RED/GREEN proof. SDK2 and independent SDK1.5 owning Runtime suites each pass
|
||||
**1,002 tests, zero failures and 3 existing skips**. The test-only fix landed on
|
||||
master `52d1373e`, merged at `75057d8c`; [#805](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/805)
|
||||
closed with validation. [Exact repair evidence](evidence/2026-11-10-opcua2/rebuild-test-barrier.md).
|
||||
No product code, expected result, timing budget or skip was changed.
|
||||
|
||||
The complete solution rebuilt at `75057d8c` with **zero warnings/errors**, 26.61 s.
|
||||
Fresh full CI4 with the corrected simulator and completion-barrier repair completed
|
||||
at `75057d8c`: **63 TRX, 19,124 total, 18,489 executed, 18,478 passed, 11 failed,
|
||||
635 existing skips**. Test exit **1**, execution guard **0** at floor12,000.
|
||||
All eleven remaining failures are the established ten PercentGood cases (#796)
|
||||
and one ScriptWorker timing assertion (#798). The fixture and rebuild-ordering
|
||||
cases pass in this same full run, with no additional failure classes or new skips.
|
||||
[Versioned counters, exact failures and per-project hashes](evidence/2026-11-10-opcua2/solution-tests4.json)
|
||||
retain the nonzero status. CI3 remains available; this baseline-matching result
|
||||
is **not a green solution run**. Task-owned simulator was removed after completion.
|
||||
|
||||
## Canonical candidate generated-factory saturation result
|
||||
|
||||
The exact immutable bundle reviewed by Astra completed **384 canonical cycles,
|
||||
96 restarts and one settling cycle in 1,425.242 seconds**, inside the fixed 1,800-second
|
||||
cutoff. Both fixed trace windows and the continuous attached interval had zero
|
||||
SDK generated-factory JIT/load events, zero new SDK assembly loads and zero lost
|
||||
events. The observer detached/drained before settling; shutdown ownership checks
|
||||
passed. This meets only the selected generated-factory readiness criterion.
|
||||
|
||||
The unchanged cold cap rejected at cycle 248: RSS 362,409,984 >358,838,272 bytes.
|
||||
The original assertion was rethrown; test/container/launcher exit 1, no ordinary
|
||||
completion or full-duration flag. Peak RSS 394,956,800 bytes; final managed memory
|
||||
19,974,280 bytes. Allocation StackTrace tracking was enabled, so this is additional
|
||||
instrumented diagnostic overhead and cannot serve as uninstrumented performance
|
||||
or acceptance evidence. [Scoped report and hashed receipts](evidence/2026-11-10-opcua2/saturation-candidate.md).
|
||||
#799 and G4 remain failed/open. A matched original-SDK canonical adapter is being
|
||||
prepared before any further acceptance-protocol review; no warmed replacement
|
||||
baseline, budget waiver, factory/runtime setting or production patch was adopted.
|
||||
|
||||
|
||||
## Original canonical adapter limitation and final review boundary
|
||||
|
||||
The [original-SDK canonical adapter](../../scripts/validation/opcua-lifecycle-memory/baseline-canonical/README.md)
|
||||
compiles against the unchanged `83ed880e` publications with zero warnings/errors;
|
||||
its package/input/source hashes are preserved. A short normal-cycle control
|
||||
completed encrypted sessions, typed writes/readbacks, all ten history calls,
|
||||
revocation, surgery/rebuild, stalled-read recovery, subscription cleanup and both
|
||||
client closes. The retained closed Session object still held one browse and six
|
||||
history points while SessionManager had zero active sessions; the copied assertion
|
||||
fails with exit1, including after a separate fixed250ms observation. Exact1.5
|
||||
binary inspection shows Close removes diagnostics, while Dispose clears those
|
||||
lists. Held-object state does not prove live server retention or an unbounded leak.
|
||||
|
||||
A separate active-session restart control cleared the observable session/
|
||||
subscription/item/CP counts, then returned **exit2: unsupported ownership parity**.
|
||||
Original ApplicationInstance/CertificateValidator have no SDK2 manager-disposal
|
||||
observable; cached store references1→1 are not file-handle counts. Unsupported
|
||||
metrics were not fabricated as zeros. This is not a passing four-cycle sequence,
|
||||
a matched process-memory host, an accepted RSS denominator or a long-run result.
|
||||
Astra independently verified208 reference hashes, all short-run projections and
|
||||
the exact immutable close/dispose behavior. The diagnostic package is reviewable;
|
||||
shared semantic/token/weak-root/resource-owner checks remain prerequisites to a
|
||||
new acceptance procedure.
|
||||
|
||||
Astra's final integrated verdict is **source/security review closed, release HOLD**.
|
||||
It independently verified all63 final CI4 TRX hashes/counters and exact baseline
|
||||
failure attribution, the two-hour client receipts and428-file recursive manifest,
|
||||
the candidate saturation result and original failure preservation, and the
|
||||
original adapter's explicit limitations. No new must-fix migration source/security
|
||||
finding remains. **CI remains red on11 baseline cases; G3 external evidence and
|
||||
G4 resource/performance acceptance remain unresolved.** #799, #800, #801 and
|
||||
ScadaBridge#121 stay open. Neither an SDK version substitution, production crypto/
|
||||
session-factory workaround, resource-cap waiver nor warmed acceptance baseline
|
||||
was approved. SDK2 remains on draft PR#802; it is not merged to master or deployed.
|
||||
|
||||
## Follow-on #799 close ownership and native attribution
|
||||
|
||||
The [candidate bounded close control](evidence/2026-11-10-opcua2/close-ownership-candidate.md)
|
||||
at `564f1091` passed with one abandoned browse and six abandoned history points.
|
||||
Old authentication returns exactly BadSessionIdInvalid; old continuation tokens
|
||||
are rejected without a history-backend read while fresh-session positives work.
|
||||
All 23 old-object weak targets collect with the same host still running; all 13
|
||||
application/server/manager/core/store targets collect after normal shutdown.
|
||||
SDK2's stronger `(0,0)` closed-list check, two manager-disposal checks, empty
|
||||
caches and zero PKI descriptors remain enforced. Astra verified the actual TRX.
|
||||
Six affected history tests also pass; their original launcher mistakenly expected
|
||||
seven, and the retained false guard receipt has a separate independently reviewed
|
||||
six-test source/TRX correction. No failed test was relabelled or rerun for that fix.
|
||||
|
||||
The [fixed macOS store-versus-registry control](../../scripts/validation/opcua-lifecycle-memory/native-retention/README.md)
|
||||
completed its execution/ownership checks, **not native-retention acceptance**.
|
||||
Two hundred configured-store acquisitions leave AppleDatabase inventory 3→203;
|
||||
registry AddRefs remain 3→3. After normal disposal and five seconds idle, both
|
||||
arms have zero SDK certificate owners, runtime-tracked temporary keychains,
|
||||
temporary-keychain files, keychain descriptors and PKI descriptors. The installed
|
||||
runtime calls CFRelease after native deletion; a missing managed release is not
|
||||
indicated. Native ownership/delete status, complete RSS attribution and a supported
|
||||
mitigation remain unresolved. Registry substitution would change deliberate
|
||||
configured-store replacement semantics and was not adopted.
|
||||
|
||||
These results leave the original cold RSS failures and #799/G4 unchanged. The
|
||||
[prospective resolution procedure](../../scripts/validation/opcua-lifecycle-memory/RESOLUTION.md)
|
||||
is explicitly a design draft, not approval to launch a warmed acceptance run.
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
This operator runbook accompanies [#782](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/782),
|
||||
the [implementation plan](2026-11-10-opcua-sdk-2-upgrade.md) and
|
||||
[execution evidence](2026-11-10-opcua-sdk-2-evidence.md). Gitea is the single
|
||||
status system. This draft was checked against source `a21104d2`; it does not
|
||||
approve an artifact or record a deployment. The coordinator must attach final
|
||||
commit-specific evidence in #782 before advancing a gate. All placeholders below
|
||||
are **pending evidence**, not passing results.
|
||||
status system. This runbook was reconciled with SDK2/HG1.0 checkout `f2a8ce13` and the
|
||||
commit-specific local receipts linked below. It records local validation, not
|
||||
operator release approval or a fleet deployment. Receipt revisions vary; use each
|
||||
report's full source/artifact identity rather than treating every result as a test
|
||||
of this documentation commit. Open failures and operator gates still prevent a
|
||||
release go decision.
|
||||
|
||||
The target is exact OPC Foundation SDK **2.0.1**; the known-good SDK baseline is
|
||||
**1.5.378.182** (application baseline `83ed880e4471344deaf008da9bd028c7e170bdc7`).
|
||||
@@ -19,7 +21,7 @@ Package downgrades alone cannot reverse the application/API migration.
|
||||
No WinRM, psbridge or replacement remote-exec channel may be used until the user
|
||||
clears a channel. UNC/SMB file copying and permitted SQL access do not authorize
|
||||
service control, PowerShell execution or deployment on those hosts. Steps requiring
|
||||
a command on a held host remain pending. No deployment was performed for this draft.
|
||||
a command on a held host remain pending. No fleet deployment was performed.
|
||||
|
||||
Run the service commands below only from an approved local operator session on an
|
||||
authorized Windows node. Record the node, account, channel and maintenance window
|
||||
@@ -50,25 +52,76 @@ also touches gateway/retired sidecar paths outside this SDK rollout.
|
||||
SDK FILETIME-zero maps to the existing CLR unset-time sentinel at history boundaries.
|
||||
- No new historian backend, companion specification, PubSub, ManagedSession, AOT or
|
||||
cluster rolling-upgrade capability is claimed. HistorianGateway Client/Contracts
|
||||
**0.4.0** is already on gateway master `16111a5e` (historiangw#119 closed);
|
||||
the real gateway gate historiangw#78 remains separate from package alignment.
|
||||
**1.0.0** is on OtOpcUa master `230cef4e` and merged into this candidate
|
||||
(historiangw#131 closed after published-package/TLS verification). The preceding
|
||||
0.4.0 alignment closed #119. The live gateway gate historiangw#78 remains separate.
|
||||
- Certificate creation correction `bd3143b7` uses the SDK's **12-month** default
|
||||
instead of treating 2048 as months. The separate RSA minimum stays 2048 bits;
|
||||
existing certificates/private-key bytes and PKI identity are retained.
|
||||
|
||||
## Release record and preflight
|
||||
|
||||
Fill this record in #782 with links to immutable logs/manifests; do not paste keys,
|
||||
passwords, connection strings, private certificates or unredacted environment dumps.
|
||||
The following local results have recorded evidence. They do not substitute for
|
||||
unexecuted operator or duration gates. Keep secrets out of logs and release records.
|
||||
|
||||
| Required evidence | Record to supply before release |
|
||||
| Local validation | Recorded result and receipt |
|
||||
|---|---|
|
||||
| Candidate source and graph | Pending: full commit, clean/dirty state, SDK/runtime/OS, resolved package graph showing exact 2.0.1 |
|
||||
| Known-good recovery set | Pending: deployed application version, full binary/config archive SHA-256, protected PKI backup location and permission record |
|
||||
| Candidate artifacts | Pending: Windows and Linux publish directories/archives, RID/self-contained choice, SHA-256 manifest, dependencies and script-worker contents |
|
||||
| G2 compilation | Pending: restore/build logs and warning/error counts at the final SHA |
|
||||
| G3 behavior/security | Pending: project TRX/counts, required case results, positive and negative authorization, audit privacy, CP lifecycle and 6,144-vector comparison |
|
||||
| Interoperability | Pending: baseline/candidate client-server pairs, actual ScadaBridge adapter, upstream driver/browser products and mixed-version cluster gate |
|
||||
| Historian | Pending: configured SqlServer/QuestDB/Gateway capability-specific results; historiangw#78 link/evidence for live gateway |
|
||||
| G4 resources/performance | Pending: baseline comparison, at least three measured runs, two-hour soak, Windows/Linux start-stop-twice smoke with preserved PKI |
|
||||
| Review/operations | Pending: independent security/release review, authorized channel/window, named operator, canary/rollback results and decision |
|
||||
| G2 compilation and graph | Whole-solution Release builds pass with **0 warnings/errors**; production Foundation packages are exact 2.0.1, with no runtime migration analyzer. [Build/package evidence](2026-11-10-opcua-sdk-2-evidence.md#solution-performance-and-package-evidence). |
|
||||
| Security and application contracts | **17/17** final security probes; **187** wire cases; **324** Shared tests; **1115** server units with one existing skip. Seven certificate-manager/API checks are separate from network handshakes. [Case scope and provenance](2026-11-10-opcua-sdk-2-evidence.md#local-compatibility-and-security-evidence). |
|
||||
| Original aggregate results | **6,144 vectors pass directly and through the production manager**. Fixture SHA-256 `2ec2a1ac97f05d199c18da55157f8bbe0276853c3ba73ebf1b509c535c5a7dd8`; [original-SDK provenance and comparison scope](2026-11-10-opcua-sdk-2-evidence.md#preserved-aggregate-algorithms-and-independent-oracles). |
|
||||
| Separate-process compatibility | **62/62** baseline/candidate Shared, driver and actual CLI cases pass. Strict mutual DER trust/encrypted API-key coverage is separate from CLI autoaccept. [Matrix receipts](../../scripts/validation/opcua-sdk-compatibility/evidence/2026-11-10/). |
|
||||
| Actual ScadaBridge adapter | **18 None/API-key cases pass**, nine against each server. Encrypted attempts fail against both due to the preexisting own-certificate configuration gap, [ScadaBridge#121](https://gitea.dohertylan.com/dohertj2/ScadaBridge/issues/121); secure interoperability is blocked. [Consumer receipts](../../scripts/validation/scadabridge-sdk-compatibility/). |
|
||||
| Local historian backends | SQL Server **195/195** on each version; QuestDB **233 passed, 0 failed, 1 existing opt-in race-stress skip** on each. Case outcomes match. [Backend matrix](evidence/2026-11-10-opcua2/backend-matrix.md). This is not live AVEVA evidence. |
|
||||
| Bounded performance subset | Three warmed measurements per version: median rebuild **17→15 ms**, removal **81→80 ms**, equal visit counts. [Benchmark scope/load caveats](evidence/2026-11-10-opcua2/lock-benchmarks.md); these are not full wire-throughput, p95 or steady-RSS approval. |
|
||||
| Publish/package and Linux smoke | **Four** self-contained Linux ARM64/Windows x64 Host/CLI publishes pass. Actual Linux full Host passes **two start/stop cycles**, strict security/health checks and unchanged PKI. [Fresh HG1.0 artifact hashes and runtime receipts](evidence/2026-11-10-opcua2/publish-smoke-hg100.md). Both Hosts contain Client/Contracts1.0.0; Foundation remains2.0.1. Windows execution is still pending. |
|
||||
| Production client lifecycle | **7,200.984 seconds,5,420 cycles,43,360 verified manager disposals**, zero weak product survivors and PKI descriptors across543 samples. [Exact source/runtime/counter receipts](../../scripts/validation/opcua-client-lifecycle/evidence/2026-11-10-full/verification.json). This is a client lifecycle pass, not a server RSS or full G4 pass. |
|
||||
| Source review | Independent reviews covered identity, ownership, aggregate preservation, hierarchy and filtered audit paths. [Review scope](2026-11-10-opcua-sdk-2-evidence.md#integrated-implementation-and-source-reviews). Source review is not release approval. |
|
||||
|
||||
The final corrected CI-equivalent run at `75057d8c` completed with **63 TRX,
|
||||
19,124 total, 18,489 executed, 18,478 passed, 11 failed and 635 existing skips**.
|
||||
Test exit **1**; execution guard **0** at floor12,000. It is not green: the eleven
|
||||
failures are ten PercentGood cases and one ShadowMode250ms assertion, independently
|
||||
reproduced on baseline. [Final counters/failures/project hashes](evidence/2026-11-10-opcua2/solution-tests4.json).
|
||||
CI3's extra five simulator-advertisement failures were corrected by a directly
|
||||
reachable matching-port fixture; the baseline Runtime test-ordering race was fixed
|
||||
through correlated completion barriers (#805, master52d1373e). Both corrections
|
||||
pass in the final full run. The retained CI3 report and original SDK1.5 NAT parity
|
||||
are in the [main evidence](2026-11-10-opcua-sdk-2-evidence.md#ci3-investigation-and-corrected-final-verification);
|
||||
preexisting browser NAT behavior remains #806.
|
||||
The ten PercentGood failures are tracked in
|
||||
[#796](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/796), and the separately
|
||||
reproduced baseline ScriptWorker timing failure is
|
||||
[#798](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/798).
|
||||
The opaque scripted-alarm narrow-scope denial is a separate preexisting functionality
|
||||
gap, [#797](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/797), not a new
|
||||
SDK incompatibility or an approved synthetic-ID workaround.
|
||||
|
||||
**The server resource gate failed.** The Mac restart soak failed twice around
|
||||
17 minutes at the unchanged RSS cap. The same-cap self-contained Linux server
|
||||
workload also failed at **248 cycles / 903.5 seconds**: RSS **353,931,264 bytes**
|
||||
exceeded **351,850,496 bytes**. Its retained result is
|
||||
`/tmp/opcua2-server-linux-full-20261110/task/results/soak.trx`.
|
||||
[#799](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/799) owns investigation;
|
||||
no cap waiver or two-hour server pass is recorded. The distinct actual-production
|
||||
Shared/driver/reinitialize/browser Linux lifecycle run passed its actual two-hour
|
||||
duration and cleanup checks, as recorded above; it does not replace this failed
|
||||
server workload. See [long-run scope](2026-11-10-opcua-sdk-2-evidence.md#resource-finding-and-long-run-scope)
|
||||
for instrumentation limitations and final-result updates.
|
||||
The fixed candidate saturation diagnostic later met its selected generated-factory
|
||||
criterion, but retained the cold RSS rejection and exit1. The original-SDK adapter
|
||||
records partial wire coverage and unsupported ownership/process-memory parity;
|
||||
it supplies no approved performance denominator. See [scoped saturation result](evidence/2026-11-10-opcua2/saturation-candidate.md)
|
||||
and [original adapter limitations](../../scripts/validation/opcua-lifecycle-memory/baseline-canonical/README.md).
|
||||
Astra's final source/security review is closed; release remains on HOLD.
|
||||
|
||||
|
||||
| Operator release field | Still required before its release decision |
|
||||
|---|---|
|
||||
| Recovery set and candidate selection | Actual deployed known-good application/config archive hashes, protected PKI/permission backup, selected complete candidate artifact and matching source/manifest. Local artifact manifests above are available; a fleet recovery set is not inferred. |
|
||||
| Windows runtime/service | Approved-node installation, service account/ACL checks, preserved-PKI two-start/stop and protocol checks: [#800](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/800). Cross-publishing does not satisfy this. |
|
||||
| Live AVEVA/consumer/pair | Licensed gateway/backend, remaining secure consumer work and mixed-version Akka/config/failover validation: [#801](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/801), with historiangw#78 and ScadaBridge#121 linked as applicable. No rolling-safe claim. |
|
||||
| Resource/performance acceptance | Resolve #799, record completed required-duration runs and remaining representative load/budget evidence; no shortened run or changed cap substitutes. |
|
||||
| Rollout/rollback authorization and results | Named operator, cleared channel, environment/window, actual schema/serialization assessment, canary/rollback checks and final review/go decision in #782. The wonder-box hold remains binding. |
|
||||
|
||||
Before staging:
|
||||
|
||||
@@ -277,8 +330,14 @@ thresholds or count absent environments as passed.
|
||||
|
||||
## Results handoff
|
||||
|
||||
Pending: coordinator to attach the final SHA, artifact hashes, TRX counts/skips,
|
||||
interoperability/performance/runtime evidence, reviews and per-environment operator
|
||||
records in #782. This document makes no claim of a passing build, test, ScadaBridge,
|
||||
Windows/Linux smoke, live backend, soak or deployment. Required inaccessible steps
|
||||
remain open gates unless formally separated into linked issues with accurate scope.
|
||||
Local build, security/wire, aggregate, backend, compatibility, publish and Linux
|
||||
smoke evidence is linked above with its actual scope and revisions. The server
|
||||
RSS gate is failed (#799), full CI retains the 11 baseline failures (#796/#798),
|
||||
the client lifecycle run completed two hours with cleanup checks passed, secure
|
||||
ScadaBridge interoperability is blocked (ScadaBridge#121), and
|
||||
Windows/live AVEVA/pair/operator gates remain pending (#800/#801). No fleet rollout
|
||||
or rollback has been executed. The final post-HG1.0 corrected full run retains
|
||||
only the eleven independently reproduced baseline failures; earlier failed runs
|
||||
remain recorded. The coordinator records remaining decisions in #782 and the main
|
||||
evidence; none is inferred from a
|
||||
passing subset or cross-published artifact.
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"issue": "https://gitea.dohertylan.com/dohertj2/historiangw/issues/131",
|
||||
"consumerBase": "16111a5e",
|
||||
"opcUaSdk": "1.5.378.182 (independent of SDK2 migration)",
|
||||
"build": {
|
||||
"command": "dotnet build ZB.MOM.WW.OtOpcUa.slnx -c Release -m:1",
|
||||
"exit": 0,
|
||||
"warnings": 0,
|
||||
"errors": 0,
|
||||
"seconds": 37.33,
|
||||
"logSha256": "cb4f1ee96bf05254e5c707179c1e81d99d8267e28c5117a84afa0b0c853fa329"
|
||||
},
|
||||
"packages": [
|
||||
{
|
||||
"package": "ZB.MOM.WW.HistorianGateway.Client/1.0.0",
|
||||
"type": "package",
|
||||
"source": "https://gitea.dohertylan.com/api/packages/dohertj2/nuget/index.json",
|
||||
"repositoryCommit": "ea9f21c850fb618767e12690924f9966f1e433ac",
|
||||
"sha256": {
|
||||
"lib/net10.0/ZB.MOM.WW.HistorianGateway.Client.dll": "b89a02e0d228ec549c4dfb755694f7f67b573a6077f06ff2781165e884fc5e12",
|
||||
"zb.mom.ww.historiangateway.client.1.0.0.nupkg": "0a3f2836727671c07c0c393e705cc9737d2ed44258213a51fb4acf0d0ab7129e"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "ZB.MOM.WW.HistorianGateway.Contracts/1.0.0",
|
||||
"type": "package",
|
||||
"source": "https://gitea.dohertylan.com/api/packages/dohertj2/nuget/index.json",
|
||||
"repositoryCommit": "ea9f21c850fb618767e12690924f9966f1e433ac",
|
||||
"sha256": {
|
||||
"lib/net10.0/ZB.MOM.WW.HistorianGateway.Contracts.dll": "e60d3d4650be5d251982f7847404daf1b666f61d56e2b0ccb844af8677e97743",
|
||||
"zb.mom.ww.historiangateway.contracts.1.0.0.nupkg": "83ffd12bcbbf919c973ba522560e10aa93a4cb8c686a3232030a2cfa5f00995e"
|
||||
}
|
||||
}
|
||||
],
|
||||
"sourceOverrides": false,
|
||||
"tests": [
|
||||
{
|
||||
"project": "gateway",
|
||||
"testExit": 0,
|
||||
"executionGuardExit": 0,
|
||||
"counters": {
|
||||
"total": "529",
|
||||
"executed": "523",
|
||||
"passed": "523",
|
||||
"failed": "0",
|
||||
"error": "0",
|
||||
"timeout": "0",
|
||||
"aborted": "0",
|
||||
"inconclusive": "0",
|
||||
"passedButRunAborted": "0",
|
||||
"notRunnable": "0",
|
||||
"notExecuted": "0",
|
||||
"disconnected": "0",
|
||||
"warning": "0",
|
||||
"completed": "0",
|
||||
"inProgress": "0",
|
||||
"pending": "0"
|
||||
},
|
||||
"outcomes": {
|
||||
"Passed": 523,
|
||||
"Failed": 0,
|
||||
"NotExecuted": 6
|
||||
},
|
||||
"requiredTlsWireTests": [
|
||||
{
|
||||
"name": "ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests.HistorianGatewayTlsPolicyWireTests.Explicit_tls_opt_out_reaches_the_same_self_signed_server_shape",
|
||||
"outcome": "Passed"
|
||||
},
|
||||
{
|
||||
"name": "ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests.HistorianGatewayTlsPolicyWireTests.Default_tls_rejects_an_untrusted_server_before_the_rpc",
|
||||
"outcome": "Passed"
|
||||
},
|
||||
{
|
||||
"name": "ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests.HistorianGatewayTlsPolicyWireTests.Plaintext_ignores_stale_tls_opt_out_and_ca_settings_and_reaches_the_rpc",
|
||||
"outcome": "Passed"
|
||||
}
|
||||
],
|
||||
"trxSha256": "68086e66da0942214c0be805366b47c443f633d90f217c91e07ec685e8aea9d9"
|
||||
},
|
||||
{
|
||||
"project": "runtime",
|
||||
"testExit": 0,
|
||||
"executionGuardExit": 0,
|
||||
"counters": {
|
||||
"total": "1005",
|
||||
"executed": "1002",
|
||||
"passed": "1002",
|
||||
"failed": "0",
|
||||
"error": "0",
|
||||
"timeout": "0",
|
||||
"aborted": "0",
|
||||
"inconclusive": "0",
|
||||
"passedButRunAborted": "0",
|
||||
"notRunnable": "0",
|
||||
"notExecuted": "0",
|
||||
"disconnected": "0",
|
||||
"warning": "0",
|
||||
"completed": "0",
|
||||
"inProgress": "0",
|
||||
"pending": "0"
|
||||
},
|
||||
"outcomes": {
|
||||
"Passed": 1002,
|
||||
"Failed": 0,
|
||||
"NotExecuted": 3
|
||||
},
|
||||
"requiredTlsWireTests": [],
|
||||
"trxSha256": "c12308fd650be0646beba044a4b3301dc2aefc43097b2da667d0eb5da408fc65"
|
||||
}
|
||||
],
|
||||
"review": "Astra approved source/security mapping and corrected real-wire tests; actual published package verification complete.",
|
||||
"deploymentPerformed": false
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
# Local historian backend matrix — 2026-11-10
|
||||
|
||||
Candidate a8e1082d69a112ea0e16788f023067979698527a (SDK 2.0.1) compared with baseline 83ed880e4471344deaf008da9bd028c7e170bdc7 (SDK 1.5.378.182). This is the existing direct backend integration test matrix over real local SQL Server and QuestDB. It is not an OPC UA wire test, AVEVA validation, or stress/soak approval. No source edits, expected-value changes, new skips, suppressions, issue updates, pushes, or commits were made for this task.
|
||||
|
||||
| Version | Backend | Passed | Failed | Skipped | Build / test / guard exit |
|
||||
|---|---|---:|---:|---:|---|
|
||||
| candidate | SqlServer | 195 | 0 | 0 | 0 / 0 / 0 |
|
||||
| candidate | QuestDb | 233 | 0 | 1 | 0 / 0 / 0 |
|
||||
| baseline | SqlServer | 195 | 0 | 0 | 0 / 0 / 0 |
|
||||
| baseline | QuestDb | 233 | 0 | 1 | 0 / 0 / 0 |
|
||||
|
||||
Every invocation was unfiltered, used a fresh SHA/project run directory and TRX, preserved the dotnet test exit status, and ran the repository MIN_EXECUTED=1 guard. Project builds were serial within each worktree; test invocations were serial against the same isolated fixture endpoints. Exact commands, UTC start/end, SDK dependency versions and test/SDK binary SHA-256 hashes are in each result.json.
|
||||
|
||||
Candidate positive coverage includes, on each backend, 13 raw-read, 14 AtTime, 11 modified-value, 7 event-read, 12 update, 64 processed-read, 6 bounding-value and 4 quality-contract conformance cases, plus backend-specific schema, write, provisioning, retention (SQL), tombstone/index/TTL (QuestDB), and alarm-writer tests.
|
||||
|
||||
SqlServer: baseline/candidate display names and outcomes identical = True across 195 discovered cases. Baseline failures: 0; candidate failures: 0.
|
||||
QuestDb: baseline/candidate display names and outcomes identical = True across 234 discovered cases. Baseline failures: 0; candidate failures: 0.
|
||||
|
||||
No PercentGood failure appeared in these selected integration suites. Their empty failure sets match where both versions have completed; there is no failing message pair to compare. This does not resolve or replace the separate standalone SDK-oracle PercentGood issue #796.
|
||||
|
||||
The existing QuestDB LiveWriteReadRaceStressTests.Per_sample_reads_of_a_tag_being_written_never_fault_and_never_return_a_valueless_non_bad_result case skips because HISTORIAN_QUESTDB_STRESS_SECONDS is unset. It was not enabled, shortened, reclassified, or replaced by a normal passing case. No five-million-row workload or two-hour stress/soak was run. Those gates remain pending.
|
||||
|
||||
## Fixture ownership and exact images
|
||||
|
||||
SQL Server: existing otopcua-dev-sql-1, endpoint 127.0.0.1:14330. Its documented local dev credential was read from docs/Historian.md and supplied only via environment; the password and full secret-bearing connection string were not printed or included in the report. SqlHistorianFixture was verified before execution: it forces master only for CREATE/DROP, generates OtOpcUaHistorian_T_<GUID>, directs all test schemas into that new database, clears its pools, and drops only its generated name. The fixture source is unchanged between baseline and candidate. Five preexisting OtOpcUaHistorian_T_GUID databases were recorded and were never dropped by this task.
|
||||
|
||||
SQL container ID: `74ea75e5904c647f18e89b8b14c9ac385d7149c0484fbf13f6c16ec00cf3e24f`; image ID: `sha256:a2fbff321505724b8a271a214d4dec020a8e7a745c83eede0b9d762b51a9ab34`.
|
||||
|
||||
QuestDB: new task-owned otopcua-sdk2-questdb, loopback-only PG port 32770 and HTTP port 32771, new storage volume `otopcua-sdk2-questdb-data-20261110t170141z`. No existing QuestDB service or volume was used. Connection settings use ServerCompatibilityMode=NoTypeLoading and Maximum Pool Size=16; the fixture applies its existing one-second idle pruning. QuestDbFixture assigns unique t<12 hex>_ prefixes, validates them, locates owned tables with starts_with (not wildcard LIKE), and drops only those prefixes. The same unchanged fixture source and image were used by both versions.
|
||||
|
||||
QuestDB image: `questdb/questdb:10.0.1`; immutable image ID `sha256:41ceca901724ad10a0035da0cb0c1f99f88bf3692abdc9090baabdfff1d215f9`; digest `questdb/questdb@sha256:67eaed863ebb2383227919ea9a5499a4a39ceca3862c20b974c84a10e11cdf89`.
|
||||
|
||||
## Run evidence
|
||||
|
||||
| Version / backend | UTC start | UTC end | Directory |
|
||||
|---|---|---|---|
|
||||
| candidate SqlServer | 2026-11-10T17:03:02.101289+00:00 | 2026-11-10T17:03:52.950418+00:00 | `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/a8e1082d/backend-matrix-20261110T170141Z/SqlServer` |
|
||||
| candidate QuestDb | 2026-11-10T17:03:58.392642+00:00 | 2026-11-10T17:11:13.275488+00:00 | `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/a8e1082d/backend-matrix-20261110T170141Z/QuestDb` |
|
||||
| baseline SqlServer | 2026-11-10T17:11:19.005717+00:00 | 2026-11-10T17:12:11.817250+00:00 | `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/83ed880e/backend-matrix-20261110T170141Z/SqlServer` |
|
||||
| baseline QuestDb | 2026-11-10T17:12:16.867323+00:00 | 2026-11-10T17:19:37.746023+00:00 | `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/83ed880e/backend-matrix-20261110T170141Z/QuestDb` |
|
||||
|
||||
Session index / fixture metadata / aggregate results / exact failure-set comparison:
|
||||
|
||||
/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/a8e1082d/backend-matrix-20261110T170141Z
|
||||
|
||||
Each project directory contains build.log, test.log, a fresh TRX, guard.log and result.json. Candidate directories also contain class-counters.json. The session has fixture-metadata.json and readiness/WAL snapshots; cleanup.json and questdb-container.log are written after final cleanup.
|
||||
|
||||
## Cleanup
|
||||
|
||||
Completed at 2026-11-10T17:19:44.751608+00:00. SQL GUID-database inventory unchanged: True; SQL container ID unchanged: True. QuestDB tables remaining after all tests: 0. Task-owned container absent: True; task-owned volume absent: True. Only the captured task container ID and its verified task-labelled volume were removed. No other container, volume or database was removed.
|
||||
|
||||
## Reproduction and remaining gates
|
||||
|
||||
Completed scripts (kept in /tmp; they create new timestamped evidence directories and protect ownership):
|
||||
|
||||
```bash
|
||||
python3 /tmp/opcua2-backend-setup.py
|
||||
python3 /tmp/opcua2-backend-matrix.py
|
||||
python3 /tmp/opcua2-backend-cleanup.py
|
||||
python3 /tmp/opcua2-backend-report.py
|
||||
```
|
||||
|
||||
For each version/backend the actual commands were dotnet build <owning-project.csproj> -c Release, then dotnet test <owning-project.csproj> -c Release --no-build --no-restore --logger trx --results-directory <fresh-directory>, with only the appropriate real connection environment variable set. Guard: MIN_EXECUTED=1 bash scripts/ci/assert-not-all-skipped.sh <fresh-directory>/*.trx. The orchestration preserves nonzero test and guard statuses instead of overwriting them. SQL connection acquisition is from the documented local dev recipe without echo; QuestDB endpoints are obtained from the newly created task container.
|
||||
|
||||
AVEVA/HistorianGateway live read/write/alarm/provisioning and licensed backend validation remain external and pending. The separate SDK-oracle issue #796, wire/security interoperability, five-million-row workload, two-hour stress/soak and release/performance gates are not completed by these local backend runs.
|
||||
@@ -0,0 +1,23 @@
|
||||
# SDK1.5 Browser different-port NAT parity
|
||||
|
||||
Read-only application validation using existing Release Browser integration DLLs in `/Users/dohertj2/.codex/worktrees/historiangw-client-040/OtOpcUa` (current HEAD `52d1373e8129b7f4f0ea2e35f86a8a5d0ed1de36`). No repository edits, build, or restore. DLL dependency graph confirms all five OPCFoundation packages `1.5.378.182`; exact DLL hashes/package versions are in `metadata.json`.
|
||||
|
||||
One task-owned fixture was started from cached `mcr.microsoft.com/iotedge/opc-plc:2.14.10` with `--pn=50000 --ut --aa --alm`, hostname `otopcua-sdk1-browser-nat-parity`, and localhost ephemeral mapping `127.0.0.1:32773 -> 50000`. Image ID/digest and Docker configuration are recorded in `container-metadata.txt` and `image-digest.txt`. No shared fixture, volume, or port was changed.
|
||||
|
||||
All five original SDK1.5 Browser integration cases failed: **0 passed, 5 failed, 0 skipped**; test exit1, execution guard exit0. Each failed with `System.Net.Sockets.SocketException: nodename nor servname provided, or not known` in the SDK1.5 `DefaultSessionFactory.CreateChannelAsync/CreateAsync` path. `browser-sdk1-nat.trx` and `test.log` contain the actual failures.
|
||||
|
||||
A standalone scratch discovery probe referencing those exact baseline `Opc.Ua.*` DLLs called public `DiscoveryClient.CreateAsync` and `GetEndpointsAsync` against localhost:32773. It succeeded (exit0) and captured all seven advertised endpoints at `opc.tcp://otopcua-sdk1-browser-nat-parity:50000/`, including SecurityPolicy.None/MessageSecurityMode.None. See `get-endpoints.log` and `discovery-probe/{Program.cs,probe.csproj}`. The advertised hostname/port are therefore the actual discovery response, not inferred from configuration alone.
|
||||
|
||||
This establishes the same browser failure on SDK1.5 when discovery advertises an unreachable Docker hostname/internal port. It supports classification as a preexisting NAT/advertisement limitation exposed by the test fixture mapping, rather than an SDK2-only regression. Root's matching-port SDK2 fixture pass is separate evidence.
|
||||
|
||||
Command:
|
||||
|
||||
```sh
|
||||
OPCUA_SIM_ENDPOINT=opc.tcp://127.0.0.1:32773 dotnet test tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests.csproj -c Release --no-build --no-restore --logger 'trx;LogFileName=browser-sdk1-nat.trx' --results-directory /tmp/opcua-browser-sdk1-nat-parity
|
||||
MIN_EXECUTED=1 bash scripts/ci/assert-not-all-skipped.sh /tmp/opcua-browser-sdk1-nat-parity/browser-sdk1-nat.trx
|
||||
```
|
||||
|
||||
Cleanup removed only `otopcua-sdk1-browser-nat-parity`; cleanup exit0 and exact-name absence recorded in `cleanup.log`. No source/issue changes or commits.
|
||||
|
||||
|
||||
The baseline browser limitation remains open in [#806](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/806). This migration retains its behavior; the fixture is configured as a directly reachable endpoint with identical host/internal/advertised ports. Focused SDK2 browser5/5 and driver5/5 passed after that correction, with no source changes.
|
||||
@@ -0,0 +1,200 @@
|
||||
{
|
||||
"issues": {
|
||||
"ordering": "#805 closed; master52d1373e",
|
||||
"nat": "#806 open; SDK1.5 parity confirms preexisting limitation"
|
||||
},
|
||||
"evidence": {
|
||||
"controlledRed": {
|
||||
"sha256": "986e32c042f8b8525af44647f52d2defbf759ab122eb7494a05d77a0e1e4fbf9",
|
||||
"counters": {
|
||||
"total": "1",
|
||||
"executed": "1",
|
||||
"passed": "0",
|
||||
"failed": "1",
|
||||
"error": "0",
|
||||
"timeout": "0",
|
||||
"aborted": "0",
|
||||
"inconclusive": "0",
|
||||
"passedButRunAborted": "0",
|
||||
"notRunnable": "0",
|
||||
"notExecuted": "0",
|
||||
"disconnected": "0",
|
||||
"warning": "0",
|
||||
"completed": "0",
|
||||
"inProgress": "0",
|
||||
"pending": "0"
|
||||
},
|
||||
"outcomes": {
|
||||
"Passed": 0,
|
||||
"Failed": 1,
|
||||
"NotExecuted": 0
|
||||
}
|
||||
},
|
||||
"controlledGreen": {
|
||||
"sha256": "cbc3bf65bc7ad915f8b8697c260181f3cc01321a4904137dcca6fd4ac09b047d",
|
||||
"counters": {
|
||||
"total": "1",
|
||||
"executed": "1",
|
||||
"passed": "1",
|
||||
"failed": "0",
|
||||
"error": "0",
|
||||
"timeout": "0",
|
||||
"aborted": "0",
|
||||
"inconclusive": "0",
|
||||
"passedButRunAborted": "0",
|
||||
"notRunnable": "0",
|
||||
"notExecuted": "0",
|
||||
"disconnected": "0",
|
||||
"warning": "0",
|
||||
"completed": "0",
|
||||
"inProgress": "0",
|
||||
"pending": "0"
|
||||
},
|
||||
"outcomes": {
|
||||
"Passed": 1,
|
||||
"Failed": 0,
|
||||
"NotExecuted": 0
|
||||
}
|
||||
},
|
||||
"sdk2Runtime": {
|
||||
"sha256": "238ecd1b10df3d9e173d428f2760c35b5e293b4db67f3e9a2a46d2d9988a27f2",
|
||||
"counters": {
|
||||
"total": "1005",
|
||||
"executed": "1002",
|
||||
"passed": "1002",
|
||||
"failed": "0",
|
||||
"error": "0",
|
||||
"timeout": "0",
|
||||
"aborted": "0",
|
||||
"inconclusive": "0",
|
||||
"passedButRunAborted": "0",
|
||||
"notRunnable": "0",
|
||||
"notExecuted": "0",
|
||||
"disconnected": "0",
|
||||
"warning": "0",
|
||||
"completed": "0",
|
||||
"inProgress": "0",
|
||||
"pending": "0"
|
||||
},
|
||||
"outcomes": {
|
||||
"Passed": 1002,
|
||||
"Failed": 0,
|
||||
"NotExecuted": 3
|
||||
}
|
||||
},
|
||||
"sdk1Runtime": {
|
||||
"sha256": "e6b378d324a91d08159b59f675d10af2207a745afc6e0c846564c39ac144b58a",
|
||||
"counters": {
|
||||
"total": "1005",
|
||||
"executed": "1002",
|
||||
"passed": "1002",
|
||||
"failed": "0",
|
||||
"error": "0",
|
||||
"timeout": "0",
|
||||
"aborted": "0",
|
||||
"inconclusive": "0",
|
||||
"passedButRunAborted": "0",
|
||||
"notRunnable": "0",
|
||||
"notExecuted": "0",
|
||||
"disconnected": "0",
|
||||
"warning": "0",
|
||||
"completed": "0",
|
||||
"inProgress": "0",
|
||||
"pending": "0"
|
||||
},
|
||||
"outcomes": {
|
||||
"Passed": 1002,
|
||||
"Failed": 0,
|
||||
"NotExecuted": 3
|
||||
}
|
||||
},
|
||||
"browserCorrectedFixture": {
|
||||
"sha256": "ed9db0d9545fef17e8b43429a962a36936b551c2de03535be4b7983343d42ecd",
|
||||
"counters": {
|
||||
"total": "5",
|
||||
"executed": "5",
|
||||
"passed": "5",
|
||||
"failed": "0",
|
||||
"error": "0",
|
||||
"timeout": "0",
|
||||
"aborted": "0",
|
||||
"inconclusive": "0",
|
||||
"passedButRunAborted": "0",
|
||||
"notRunnable": "0",
|
||||
"notExecuted": "0",
|
||||
"disconnected": "0",
|
||||
"warning": "0",
|
||||
"completed": "0",
|
||||
"inProgress": "0",
|
||||
"pending": "0"
|
||||
},
|
||||
"outcomes": {
|
||||
"Passed": 5,
|
||||
"Failed": 0,
|
||||
"NotExecuted": 0
|
||||
}
|
||||
},
|
||||
"driverCorrectedFixture": {
|
||||
"sha256": "dc2ac55ca296474d767b29ddca2feaa027cec5793ab6ac6a28a10c98dfb2cb56",
|
||||
"counters": {
|
||||
"total": "5",
|
||||
"executed": "5",
|
||||
"passed": "5",
|
||||
"failed": "0",
|
||||
"error": "0",
|
||||
"timeout": "0",
|
||||
"aborted": "0",
|
||||
"inconclusive": "0",
|
||||
"passedButRunAborted": "0",
|
||||
"notRunnable": "0",
|
||||
"notExecuted": "0",
|
||||
"disconnected": "0",
|
||||
"warning": "0",
|
||||
"completed": "0",
|
||||
"inProgress": "0",
|
||||
"pending": "0"
|
||||
},
|
||||
"outcomes": {
|
||||
"Passed": 5,
|
||||
"Failed": 0,
|
||||
"NotExecuted": 0
|
||||
}
|
||||
},
|
||||
"sdk1BrowserNatBaseline": {
|
||||
"sha256": "7111ac68dd46bb1931b0e8c3b4bbb3b85588dbcd94758aec5fcb3e6e205b0487",
|
||||
"counters": {
|
||||
"total": "5",
|
||||
"executed": "5",
|
||||
"passed": "0",
|
||||
"failed": "5",
|
||||
"error": "0",
|
||||
"timeout": "0",
|
||||
"aborted": "0",
|
||||
"inconclusive": "0",
|
||||
"passedButRunAborted": "0",
|
||||
"notRunnable": "0",
|
||||
"notExecuted": "0",
|
||||
"disconnected": "0",
|
||||
"warning": "0",
|
||||
"completed": "0",
|
||||
"inProgress": "0",
|
||||
"pending": "0"
|
||||
},
|
||||
"outcomes": {
|
||||
"Passed": 0,
|
||||
"Failed": 5,
|
||||
"NotExecuted": 0
|
||||
}
|
||||
},
|
||||
"actorMasterCandidateSourceDiff": {
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"bytes": 0
|
||||
},
|
||||
"sdk1GetEndpoints": {
|
||||
"sha256": "c4400f696ce3a8e8cdc49a4dedc06e4969584cfb47a6ada28c28bf91f75189f7",
|
||||
"bytes": 858
|
||||
}
|
||||
},
|
||||
"expectedRedFailureRetained": true,
|
||||
"newProductChanges": false
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
# Candidate close and weak-root control
|
||||
|
||||
The final bounded control at `564f10914357fc34db303b2faf030907663e6779`
|
||||
passed: one test executed, one passed, no skips; runner elapsed 10.082 seconds.
|
||||
It creates one abandoned browse point and six abandoned history points through
|
||||
the same encrypted canonical fixture helpers. Before close, the retained server
|
||||
session has exactly `(1,6)` points; SDK2's stronger post-close `(0,0)` check remains.
|
||||
|
||||
The old authentication token returns exactly `BadSessionIdInvalid` (`0x80250000`).
|
||||
A fresh session rejects both old continuation tokens with
|
||||
`BadContinuationPointInvalid`; invalid history does not call the historian.
|
||||
Fresh reads and the session's own browse/history roundtrips succeed around the
|
||||
denials, so a generally broken channel cannot satisfy this control.
|
||||
|
||||
After the session helper and its completed async state machine leave the isolated
|
||||
boundary, **all 23 old client/session/point/value/state targets are collectible
|
||||
while the same server is still running**, in each of five fixed GC observations.
|
||||
After normal host/application disposal and the fixture boundary, all 13 observed
|
||||
application/configuration/manager/validation-core/store/server/node-manager/
|
||||
backend targets are collectible in every round. Independent retained/collectible
|
||||
observer controls behave as expected. Both SDK2 certificate managers are disposed,
|
||||
cached stores are empty and actual PKI descriptors are zero.
|
||||
|
||||
Earlier one-history-point control receipts remain recorded: `b5986c61` passed
|
||||
with the original two-status auth whitelist; `6737e367` passed after narrowing to
|
||||
the observed exact status. Six affected history tests also passed at `6737e367`.
|
||||
Their launcher mistakenly expected seven tests. The original false guard receipt
|
||||
is retained; a separate source inventory and TRX review confirms four session
|
||||
history tests plus two event-paging tests, all executed/passed. No seventh source
|
||||
test is missing and the tests were not rerun to correct that bookkeeping error.
|
||||
|
||||
[Machine-readable observations, commands and raw hashes](close-ownership-candidate.json)
|
||||
retain all four executions. The current build has zero warnings/errors. Astra
|
||||
reviewed the design and actual code before each diagnostic execution; it verified
|
||||
all three close-control TRX files, the aligned 23/13 weak-target result and the
|
||||
six-test inventory correction independently.
|
||||
|
||||
This establishes bounded candidate close semantics and managed collectibility.
|
||||
It does **not** establish full canonical operation/host parity with the original
|
||||
standalone adapter, a steady RSS denominator, a resource-budget waiver, macOS
|
||||
native-retention resolution or G4 acceptance. No production code changed.
|
||||
@@ -0,0 +1,60 @@
|
||||
# OPC UA SDK lock benchmark diagnostic — 2026-11-10
|
||||
|
||||
Compared baseline `83ed880e4471344deaf008da9bd028c7e170bdc7` (SDK 1.5.378.182) with candidate `a8e1082d69a112ea0e16788f023067979698527a` (SDK 2.0.1). Both Release project builds passed with 0 warnings/errors. No source edits or new commits were made for this evidence task.
|
||||
|
||||
Same Mac16,7, 14 logical/physical CPU cores, 48 GiB RAM, .NET SDK 10.0.403. OS details and exact assembly SHA-256 hashes are in metadata.json. All runs used OTOPCUA_BENCH=1 and the existing NodeManagerLockTimeBenchmarkTests class. Scenario sizes and assertions are identical; the only benchmark source differences are SDK-required OperationContext.RequestLifetime.None and IAsyncNodeManager interface changes. Each test boots a private server with its own temporary PKI and probed port. No containers or global fixture were used.
|
||||
|
||||
One warmup invocation per version was followed by three recorded invocations per version, alternating baseline/candidate with 25 seconds between processes. Each invocation is a fresh process; warmup primes host/file caches, not persistent in-process JIT state. Default xUnit test order varied. Warmups are excluded from the medians.
|
||||
|
||||
| Recorded metric | Baseline runs | SDK 2 runs | Baseline median | SDK 2 median | Change |
|
||||
|---|---|---|---:|---:|---:|
|
||||
| Rebuild 10k-tag folder | 18, 15, 17 | 15, 15, 14 | 17 ms | 15 ms | -11.76% |
|
||||
| Remove 200 equipment / 50k nodes | 81, 50, 86 | 80, 68, 80 | 81 ms | 80 ms | -1.23% |
|
||||
| Visits for 10 removals / 50 conditions | 10, 10, 10 | 10, 10, 10 | 10 visits | 10 visits | +0.00% |
|
||||
| OS command maximum resident set size | 385.17, 136.20, 136.30 | 136.39, 136.81, 136.80 | 136.30 MiB | 136.80 MiB | +0.37% |
|
||||
|
||||
The median teardown timings do not exceed the provisional +15% latency-growth reference. Their inverse-time local teardown-rate proxies change by +13.33% (rebuild) and +1.25% (removal), so neither shows a >10% drop in this subset. These derived rates are not wire throughput, and three samples do not establish p95 latency. Command maximum-RSS median rose 0.37%, below the +15% reference, but it does not measure steady server RSS. No change to budgets or acceptance decision is implied.
|
||||
|
||||
All eight invocations passed all three cases: 24 passed, 0 failed, 0 skipped (18 recorded case executions plus 6 warmup). Every test exit and MIN_EXECUTED=3 guard exit was 0; aggregate orchestrator exit was 0. The original 5000 ms opt-in teardown assertions remained enforced; the largest recorded operation was 86 ms. The strict, hardware-independent visit counter was exactly 10 in all eight invocations.
|
||||
|
||||
Host-load caveat: other solution testing/build activity shared this host. Initial preparation saw load averages 19.78/30.02/23.48 on 14 cores; per-run before/after uptime snapshots are retained in result.json. Recorded one-minute load ranged 5.58–10.90; five-minute load ranged 15.26–20.32 and fifteen-minute load 18.45–20.56. BENCH uses integer-millisecond Stopwatch output. Timing spread, quantization and changing xUnit ordering preclude a stable performance claim. OS /usr/bin/time -l maximum RSS is command-invocation rusage, not isolated teardown allocation or steady server RSS; baseline run1 is an outlier and is included unchanged. GC/allocation counters are not exposed by the existing harness and were not measured. This subset does not complete wire throughput, p50/p95/p99 latency, allocation, steady RSS, history-page throughput, load, soak, publish, or resource-lifecycle gates.
|
||||
|
||||
## Recorded invocations
|
||||
|
||||
| Version / phase | UTC start | UTC end | Test / guard exit | Evidence directory |
|
||||
|---|---|---|---|---|
|
||||
| baseline warmup | 2026-11-10T16:54:37.980941+00:00 | 2026-11-10T16:54:42.012296+00:00 | 0 / 0 | `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/83ed880e/lock-bench-20261110T165437Z/warmup` |
|
||||
| candidate warmup | 2026-11-10T16:55:07.020267+00:00 | 2026-11-10T16:55:11.610491+00:00 | 0 / 0 | `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/a8e1082d/lock-bench-20261110T165437Z/warmup` |
|
||||
| baseline run1 | 2026-11-10T16:55:36.615335+00:00 | 2026-11-10T16:55:40.023724+00:00 | 0 / 0 | `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/83ed880e/lock-bench-20261110T165437Z/run1` |
|
||||
| candidate run1 | 2026-11-10T16:56:05.032437+00:00 | 2026-11-10T16:56:09.352092+00:00 | 0 / 0 | `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/a8e1082d/lock-bench-20261110T165437Z/run1` |
|
||||
| baseline run2 | 2026-11-10T16:56:34.361326+00:00 | 2026-11-10T16:56:37.712286+00:00 | 0 / 0 | `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/83ed880e/lock-bench-20261110T165437Z/run2` |
|
||||
| candidate run2 | 2026-11-10T16:57:02.720388+00:00 | 2026-11-10T16:57:06.894343+00:00 | 0 / 0 | `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/a8e1082d/lock-bench-20261110T165437Z/run2` |
|
||||
| baseline run3 | 2026-11-10T16:57:31.903230+00:00 | 2026-11-10T16:57:35.306953+00:00 | 0 / 0 | `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/83ed880e/lock-bench-20261110T165437Z/run3` |
|
||||
| candidate run3 | 2026-11-10T16:58:00.315244+00:00 | 2026-11-10T16:58:04.720985+00:00 | 0 / 0 | `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/a8e1082d/lock-bench-20261110T165437Z/run3` |
|
||||
|
||||
Each directory contains a fresh TRX (BENCH stdout attached to individual test results), test.log, resource.log, guard.log, and result.json. Session metadata/results/summary:
|
||||
|
||||
/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/a8e1082d/lock-bench-20261110T165437Z
|
||||
|
||||
Reproduction (in each version worktree; choose a new, nonexistent absolute result directory each invocation):
|
||||
|
||||
```bash
|
||||
OPC_RUN_DIR=/absolute/fresh/result/directory
|
||||
test ! -e "$OPC_RUN_DIR" || exit 2
|
||||
mkdir -p "$OPC_RUN_DIR" || exit
|
||||
opc_test_status=0
|
||||
OTOPCUA_BENCH=1 /usr/bin/time -l dotnet test \
|
||||
tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests.csproj \
|
||||
-c Release --no-build --no-restore \
|
||||
--filter FullyQualifiedName~NodeManagerLockTimeBenchmarkTests \
|
||||
--logger trx --logger "console;verbosity=detailed" \
|
||||
--results-directory "$OPC_RUN_DIR" \
|
||||
> "$OPC_RUN_DIR/test.log" 2> "$OPC_RUN_DIR/resource.log" || opc_test_status=$?
|
||||
opc_guard_status=0
|
||||
MIN_EXECUTED=3 bash scripts/ci/assert-not-all-skipped.sh "$OPC_RUN_DIR"/*.trx \
|
||||
> "$OPC_RUN_DIR/guard.log" 2>&1 || opc_guard_status=$?
|
||||
if [ "$opc_test_status" -ne 0 ]; then exit "$opc_test_status"; fi
|
||||
exit "$opc_guard_status"
|
||||
```
|
||||
|
||||
Exact completed orchestration script: /tmp/opcua2-lock-bench.py. It records environment, SDK dependencies, assembly hashes, start/end UTC, host load, metrics, counts and statuses, and alternates the eight invocations with 25-second gaps.
|
||||
@@ -0,0 +1,86 @@
|
||||
# Updated SDK 2 / HistorianGateway 1.0.0 publish smoke — 2026-11-10
|
||||
|
||||
**Passed:** four fresh self-contained Host/CLI publishes, static RID/dependency inspection, and the actual Linux ARM64 Host two-start/stop runtime smoke on the updated HistorianGateway 1.0.0 graph. Windows runtime/service installation remains pending under #800. This report does not complete G4.
|
||||
|
||||
All four artifacts and the unchanged existing probe were built from SDK 2 source `f2a8ce13059b266e53448739a6be0a32732f58d5` in a clean isolated checkout. The only committed change in this lane is this report; no runner, production source, package policy, existing fleet, sibling repository or issue was changed. The original smoke runner from `3a11087b` was reused without edits.
|
||||
|
||||
## Published graph and artifacts
|
||||
|
||||
Build environment: .NET SDK **10.0.403**, self-contained runtime **10.0.14**. Every publish used Release, the explicit RID, `--self-contained true`, `PublishSingleFile=false`, `PublishTrimmed=false`, `-m:1`, fresh output and external task-owned `--artifacts-path` directories. Four application publishes and the probe publish exited **0**, with no build warning/error diagnostics. Publishing was sequential while the coordinator ran its separate test lane. No Native AOT was attempted.
|
||||
|
||||
Both actual Host publish trees contain `ZB.MOM.WW.HistorianGateway.Client/1.0.0` and `ZB.MOM.WW.HistorianGateway.Contracts/1.0.0` in their `.deps.json` package graphs. The shipped DLLs report assembly/file version **1.0.0.0** and informational version **1.0.0+ea9f21c850fb618767e12690924f9966f1e433ac**. Their bytes match the actual published-package proof in [the HG 131 results](../../../../scripts/validation/historiangw-131/RESULTS.md). CLI does not depend on the HistorianGateway packages; it was rebuilt from the same updated source graph.
|
||||
|
||||
| Shipped DLL, identical in Linux and Windows Host artifacts | SHA-256 |
|
||||
|---|---|
|
||||
| `ZB.MOM.WW.HistorianGateway.Client.dll` | `b89a02e0d228ec549c4dfb755694f7f67b573a6077f06ff2781165e884fc5e12` |
|
||||
| `ZB.MOM.WW.HistorianGateway.Contracts.dll` | `e60d3d4650be5d251982f7847404daf1b666f61d56e2b0ccb844af8677e97743` |
|
||||
|
||||
Every OPC Foundation package dependency is **2.0.1** and every inspected SDK assembly is **2.0.1.0**. Each SDK basename occurs once per application tree; there is no old SDK or analyzer runtime artifact. Assembly metadata was read without executing the assemblies, using `PEReader`/`MetadataReader`.
|
||||
|
||||
| Artifact | Files / bytes | SDK assemblies | Native entries |
|
||||
|---|---:|---:|---:|
|
||||
| linux-arm64/host | 1385 / 356614102 | 8 | 34 |
|
||||
| linux-arm64/cli | 236 / 111341043 | 7 | 16 |
|
||||
| win-x64/host | 1390 / 342187531 | 8 | 37 |
|
||||
| win-x64/cli | 236 / 103181233 | 7 | 16 |
|
||||
|
||||
All inspected Linux native entries are ELF64 AArch64; all Windows native entries are x86-64 PE. Apphosts and bundled runtime libraries match their RID. Runtimeconfig `includedFrameworks` records Microsoft.NETCore.App 10.0.14, plus Microsoft.AspNetCore.App 10.0.14 for Host. Both Host artifacts include the separately self-contained script worker; the smoke used the default in-process script runner and does not exercise out-of-process scripting.
|
||||
|
||||
| Artifact | Apphost SHA-256 | Full-file manifest SHA-256 |
|
||||
|---|---|---|
|
||||
| linux-arm64/host | `d97f318f268246852603166a19d66c2da788a75a7f9124b2242e6c794985dcbb` | `be1c2f45587950007863b38050d3b48e593a82ce156816369461ba072ebd4c27` |
|
||||
| linux-arm64/cli | `b4840db34c07ec7f0fa8cbc220c5552fac87527d3b222c4e47260c68c7eedb19` | `0441fe823afd7d69e1a100786ae4b988445f228b3028976bfddc411de8984b0d` |
|
||||
| win-x64/host | `bf247acd5446a5b2ded0f90dab2b33662f08e8623fb87bd1b752d1aff38671d7` | `72ef93087da85c0dac80535915a7541e3cf8044a740eac1391ebdc173718dbc0` |
|
||||
| win-x64/cli | `949efc2cd8e71638596c3ac37dd526558513d9b09a6777d546f1d207ee691e01` | `45611b8b71673d75b7bcb7484322c16bee36cb2efc4895ca6054421c31680531` |
|
||||
|
||||
Full fresh manifests are `linux-arm64-host-sha256.json`, `linux-arm64-cli-sha256.json`, `win-x64-host-sha256.json` and `win-x64-cli-sha256.json` in the evidence root below. They exclude runtime logs/private state. `publish-inventory.json`, `native-inventory.json` and `assembly-metadata.json` preserve versions, architecture, paths and hashes. Shared SDK assembly bytes agree across all artifacts that contain each module:
|
||||
|
||||
| SDK module, version 2.0.1.0 | SHA-256 |
|
||||
|---|---|
|
||||
| `Opc.Ua.Client` | `620da406fabdbc75d11f9827ea7e39d2d3c762471bd2264b0d11f1d6cd9247cd` |
|
||||
| `Opc.Ua.Configuration` | `6ed6a594c93c3ff35d1a16539a9fe87c1e2640549cc2f48ed6bae333dc8bf0e7` |
|
||||
| `Opc.Ua.Core` | `9d0857aa6b7f3c49037d4ea557b4da8edf07e146069464481783e66c5c9cd35a` |
|
||||
| `Opc.Ua.Core.Schema` | `51cfad48657d95d38074bb88be29aea6f5b361a07397247d58838d066b99772e` |
|
||||
| `Opc.Ua.Core.Types` | `a7dd141b8dabe8848ff28c2fb8e16a85a0508e6a49e6b1a1c1e267175b99af96` |
|
||||
| `Opc.Ua.Security.Certificates` | `7f4969aceb2f0db5328c6882cb4b117342f3b18905c38acdba600683860c4fd0` |
|
||||
| `Opc.Ua.Server` | `930e714a2f6e15d393840bcdc95e1afa8012bc740c3738a655f958142721864e` |
|
||||
| `Opc.Ua.Types` | `c542f69882f4e8a4ebd5ccc28a3881d6967c7ffb6dcc3c56dc6c776f39c2ede1` |
|
||||
|
||||
## Actual Linux Host runtime
|
||||
|
||||
The existing runner completed at **2026-11-10T19:47:49.570167+00:00**. Image: `mcr.microsoft.com/dotnet/sdk:10.0.103-noble`, immutable ID `sha256:b04611ee9e1b7d36797012ed921962c57189cb4dbb3dff12afb8e158110f293b`, digest `mcr.microsoft.com/dotnet/sdk@sha256:e362a8dbcd691522456da26a5198b8f3ca1d7641c95624fadc5e3e82678bd08a`, Linux arm64. The real published self-contained `OtOpcUa.Host` was the container entrypoint; the SDK image supplied only the probe framework.
|
||||
|
||||
The fixture used a fresh task-owned LocalDb/secret store and GUID application PKI, a single local Akka driver-role node, `FetchAndCache`, an unreachable loopback central endpoint, and a seeded deployment containing one raw folder and a real peppered HMAC API key. It used the production authentication and cache-restoration paths. All containers had `--network none`, no published host ports and task labels. The same application, state and PKI were started and stopped twice.
|
||||
|
||||
| Executed check | Result |
|
||||
|---|---|
|
||||
| Readiness/liveness/active health on each boot | Six requests passed; readiness/active Healthy, Akka Up |
|
||||
| Trusted peer, Basic256Sha256 SignAndEncrypt, real HMAC API key | Two Good reads; strict peer autoaccept false |
|
||||
| Wrong API-key secret | Two `BadIdentityTokenRejected` denials |
|
||||
| Untrusted server | `BadCertificateUntrusted` |
|
||||
| Untrusted client | `BadSecurityChecksFailed` |
|
||||
| Actual published CLI help and encrypted reads | Help exit 0; two Good ServerStatus.State reads, exit 0 |
|
||||
| Stop / restart / stop | Both stops exit 0, no OOM kill; served state restored on both boots |
|
||||
| Execution-count guards | Exactly two boots and six strict SDK cases; overall runtime exit 0 |
|
||||
|
||||
The CLI retains its existing autoaccept behavior and has no strict-validation flag. Its reads are separate from the strict SDK peer checks. The closed-port CLI preparation returned the expected nonzero status and was not counted as a successful connection. This smoke verifies the updated Host package contents and OPC UA startup/security paths; it does not call a HistorianGateway or live AVEVA backend.
|
||||
|
||||
Application DER SHA-256 on both boots: `7d8deca04ba8f73005c477f2aedde7a55ab3fecdba36faa49d57648e77bd254a`. Preserved thumbprint on both boots: `sha1 Fingerprint=4A:1D:8C:4F:22:01:F2:B7:7E:C0:99:92:54:24:8F:CC:AD:A9:E5:5E`. The certificate bytes were unchanged.
|
||||
|
||||
Raw application/probe stdout/stderr and appended rolling logs were scanned before redacted copies were saved. Task secret matches were **0** in raw and saved output; the appended Serilog selflog was **0 bytes**. The mode-0600 environment file was removed. Authentication/certificate denials remained diagnosable.
|
||||
|
||||
The task container `340ffd5844fe6d71b00cdc8dbbddfaf4bd109e420912264637c9b93728fee16d` was removed after its second clean stop. No matching smoke container remains and no existing fleet or store was modified.
|
||||
|
||||
## Evidence and reproduction
|
||||
|
||||
Evidence root: `/tmp/opcua2-publish-hg100-20261110`.
|
||||
Runtime logs, health responses, command exits and summary: `/tmp/opcua2-publish-hg100-20261110/runtime-c4244a56c29942e68f259a6522331632`.
|
||||
The exact six build/runtime invocations, source revision and exits are in `validation-receipt.json` (SHA-256 `2858beab92c3ef19df894c54886b4da97d27e734f980fc02dfe63f2246c885e7`). Runtime `summary.json` SHA-256: `bf81953127c4cd95ccbb6b4d2dbbf266cd8890ebbd667f2de0e588d9e0f89e7d`.
|
||||
|
||||
The four per-publish logs, `probe-publish.log`, `linux-runtime.log`, manifest files and inventory JSON files remain under the evidence root. The private state directory is not a distributable artifact. The read-only inventory helpers used for this run are retained at `/tmp/opcua2-publish-hg100-inventory.py` and `/tmp/opcua2-publish-hg100-metadata/`; application code was not loaded by these inspectors.
|
||||
|
||||
Reproduce using [the existing publish-smoke recipe](../../../../scripts/validation/opcua-publish-smoke/README.md), selecting a fresh task directory and adding `--artifacts-path "$task_output/build-$rid"` to the application publishes and `--artifacts-path "$task_output/build-probe"` to the probe publish. Exact commands for this completed run are in the receipt. No existing runner fix was required.
|
||||
|
||||
**Pending #800:** Windows service installation, account/ACL behavior and actual Windows two-start/stop runtime validation remain an operator gate on an approved environment. Cross-publishing and PE inspection do not satisfy that gate. No WinRM, psbridge, replacement remote-execution channel, Windows service action or deployment was used.
|
||||
|
||||
Backend/AVEVA interoperability, load/performance, failover and the existing server lifecycle/RSS findings remain separate gates. This publish smoke does not waive them.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Actual Host / CLI publish and runtime smoke — 2026-11-10
|
||||
|
||||
Linux ARM64 actual full Host start/stop smoke PASSED. Windows x64 cross-publish and static inspection PASSED. Windows runtime/service installation remains a formal pending operator gate; this report does not complete Gate G4.
|
||||
|
||||
Reproducible probe commit: `3a11087b37728b4a97c2884d55f2d7d9949923b9`. Only four new files under scripts/validation/opcua-publish-smoke were added. No production, existing documentation, package policy, fleet, sister-repository or issue changes were made by this lane. Root commits 2d75cecb and bd3143b7 were merged into the isolated checkout before the applicable publishes.
|
||||
|
||||
## Published artifacts
|
||||
|
||||
The repository install recipe documents dotnet publish -c Release -r win-x64 --self-contained. The four commands used that setting, with PublishSingleFile=false and PublishTrimmed=false explicitly retained, one MSBuild node and new output folders. The Linux RID matches the cached ARM64 container. No Native AOT was attempted. All four actual publish exits were zero.
|
||||
|
||||
| RID / app | Source revision at publish | Files / bytes | SDK assemblies | Native inventory entries |
|
||||
|---|---|---:|---:|---:|
|
||||
| linux-arm64/host | `d31560cd360144132f1433a04fcb5acb25dfa3fd` | 1388 / 356618849 | 8 | 32 |
|
||||
| linux-arm64/cli | `8a1028c4e2f308a29bcc4a504596df6d667554f8` | 236 / 111341495 | 7 | 15 |
|
||||
| win-x64/host | `8a1028c4e2f308a29bcc4a504596df6d667554f8` | 1390 / 342188283 | 8 | 37 |
|
||||
| win-x64/cli | `8a1028c4e2f308a29bcc4a504596df6d667554f8` | 236 / 103181689 | 7 | 16 |
|
||||
|
||||
Linux Host was published before the root client-factory-only correction; it has no Client.Shared dependency. Both CLI publishes and Windows Host include the merged bd3143b7 correction. Host production source is unchanged across these two publish revisions. The exact per-artifact source, command and SHA-256 manifest are retained in validation-receipt.json.
|
||||
|
||||
Every OPCFoundation dependency is 2.0.1. Each SDK assembly basename appears once per application publish tree; no old SDK copy or migration analyzer runtime artifact was found. All inspected Linux native entries are AArch64 ELF and Windows entries are x86-64 PE; apphosts and bundled runtimes match their RID. Both Host publishes contain the separately self-contained script-worker. Runtimeconfig includedFrameworks records Microsoft.NETCore.App 10.0.14, plus Microsoft.AspNetCore.App 10.0.14 on Host. This validates packaging and the in-process default only; the out-of-process script execution path was not exercised here.
|
||||
|
||||
| Artifact | Apphost SHA-256 | Full-file-manifest SHA-256 |
|
||||
|---|---|---|
|
||||
| linux-arm64/host | `d97f318f268246852603166a19d66c2da788a75a7f9124b2242e6c794985dcbb` | `32ec603b22ba7f7df62904bc738e9193e0c1275fcc7a9fb5c65b9aa5d65c0f5e` |
|
||||
| linux-arm64/cli | `b4840db34c07ec7f0fa8cbc220c5552fac87527d3b222c4e47260c68c7eedb19` | `992bc65bcf74c0d57aa6d789085abdfcd2290b0496931a445a11e801de2312a6` |
|
||||
| win-x64/host | `7896ec7a0db88dd00d893ea9a55bc22e40849e7c789af7eea79a4514ed587972` | `b42baf99acbbe0646c487fbaf193f9f4fea66adfd393a41b841b55ba95d53aa0` |
|
||||
| win-x64/cli | `d880d4c83448f9ee5a2ea79c3573c1b2301b5fc17c277cf072153a589543bc5d` | `28a9872354539d600659c4af463e24c7ba40136c4ab67a5cb40c36b5d9575060` |
|
||||
|
||||
Complete paths, sizes and SHA-256 values are in the four *-sha256.json manifests. Managed SDK/package inventory is in publish-inventory.json; native paths, file-format/architecture and hashes are in native-inventory.json. Runtime-generated logs and private PKI/state are excluded from shipped-artifact manifests.
|
||||
|
||||
## Actual Linux runtime
|
||||
|
||||
Image: mcr.microsoft.com/dotnet/sdk:10.0.103-noble; cached immutable ID `sha256:b04611ee9e1b7d36797012ed921962c57189cb4dbb3dff12afb8e158110f293b`, architecture `arm64`, digest `mcr.microsoft.com/dotnet/sdk@sha256:e362a8dbcd691522456da26a5198b8f3ca1d7641c95624fadc5e3e82678bd08a`. The published self-contained OtOpcUa.Host executable was the container command; the SDK image supplies only the portable probe framework. No ProcessHost server substitute was used.
|
||||
|
||||
The real Host ran Cluster:Roles=driver, ConfigSource:Mode=FetchAndCache, a self-only otopcua Akka seed, and an unreachable loopback CentralFetchEndpoint. No ConfigDb was registered. A task-only LocalDb deployment cache was seeded through public production AddOtOpcUaLocalDb / IDeploymentArtifactCache APIs, using the public DeploymentId formatter. The artifact contains one raw folder and a real peppered HMAC API key with read/browse/subscribe grants. Production API-key authentication was used. PKI, LocalDb and encrypted secrets SQLite lived in one fresh GUID state directory. Random secrets were environment-only; none are included in this report or recorded arguments.
|
||||
|
||||
All preparation and runtime containers used --network none and task labels. HTTP, UA and Akka ports were randomly chosen within that private namespace; none were published to the host. The same task-owned container, application executable, LocalDb and GUID PKI were started and stopped twice.
|
||||
|
||||
| Executed check | Actual result |
|
||||
|---|---|
|
||||
| /healthz, /health/ready and /health/active on each boot | 6 requests passed; ready/active Healthy and Akka Up |
|
||||
| Strict trusted Basic256Sha256 SignAndEncrypt + real API-key session / ServerStatus.State read | 2 passed, Good quality, peer autoaccept false |
|
||||
| Wrong secret for the same real key | 2 denied with BadIdentityTokenRejected |
|
||||
| Untrusted server certificate | Denied with BadCertificateUntrusted |
|
||||
| Untrusted client certificate | Denied with BadSecurityChecksFailed |
|
||||
| Actual published CLI --help | Exit 0 |
|
||||
| Actual published CLI encrypted API-key ServerStatus.State reads | 2 passed, Good quality, exit 0 |
|
||||
| Actual Host stop / restart / stop | Both stops exit 0; Running=false, OOMKilled=false |
|
||||
| Cache restore | Production restored-served-state log appears on both boots |
|
||||
| Required execution guard | Exactly 2 boots and 6 strict SDK probe cases executed; overall exit 0 |
|
||||
|
||||
The CLI retains its existing AutoAcceptCertificates=true behavior and has no strict-validation switch. Its successful reads are separate from the strict SDK peer-validation checks. Its matching application-name/URI certificate was prepared through the public SDK API, then trusted by the server before first boot. A closed-port CLI setup preflight intentionally returned nonzero; it is not counted as a passing connection case.
|
||||
|
||||
Application certificate SHA-256 on BOTH boots: `19000d33d71bb11c92f282724a3515b9119783e070df06667b05f49e41413640`. Preserved thumbprint on BOTH boots: `sha1 Fingerprint=DE:CB:33:B0:DE:32:9B:FB:40:FF:A8:5C:66:48:CE:44:01:2A:48:F8`. The public certificate bytes and thumbprint were unchanged; no rotation was performed.
|
||||
|
||||
Raw process stdout/stderr and this run’s appended rolling script/application/selflog bytes were scanned before sanitized copies were saved. Exact task API-key/secret, pepper, fetch key and master-key matches: zero. Successful-run appended Serilog selflog: 0 bytes. Negative authentication and certificate results remained diagnosable. The private environment file was mode 0600 and removed at the end.
|
||||
|
||||
Final successful runtime directory: `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/publish-smoke/20261110T172336Z/runtime-8e794a04a90d4b769fd94cd330aba544`. summary.json, commands.json, health JSON, individual protocol/CLI logs, full Host logs and appended rolling-file slices contain the actual results, UTC times, statuses and exits. The task-labelled container `f725df5c31d907db6626a70446294006e9e18d6dd7efb3bc9f1f385267faf7f2` was removed after its second clean stop. No task containers remain; no existing fleet container, volume, database or PKI was modified.
|
||||
|
||||
Earlier fresh setup attempts exited nonzero and remain in sibling runtime-* directories. They exposed fixture mistakes: closed-port CLI certificate preparation, an unnecessary split cluster role, the liveness route spelling (/healthz is correct), and a hyphenated deployment identifier. Those were corrected only in the new smoke harness. The application’s configuration validator failed closed, and valid configuration subsequently passed. No SDK regression was attributed to those setup errors.
|
||||
|
||||
## Reproduction and pending operator gate
|
||||
|
||||
The committed scripts/validation/opcua-publish-smoke/README.md contains exact four-publish, probe-build and runtime commands. The runtime script requires a fresh private state directory per invocation, preserves each command exit, enforces the two-boot/six-case count and raw-log checks, and removes only its captured task-labelled container. Build/publish commands and immutable hashes for this completed run are in validation-receipt.json.
|
||||
|
||||
Evidence root: `/Users/dohertj2/.codex/worktrees/opcua2-test-convergence/OtOpcUa/artifacts/opcua2/publish-smoke/20261110T172336Z`.
|
||||
|
||||
**PENDING — Windows runtime / installation:** cross-publishing PE artifacts on macOS does not execute Windows service registration, startup, endpoint negotiation or shutdown. An operator must perform scripts/install/Install-Services.ps1 and the two-start/stop, preserved-PKI health/UA checks on an approved Windows environment. The Wonder-box remote-execution hold remains binding: no WinRM, psbridge or replacement remote-execution channel was used or is authorized by this smoke. No deployment was performed.
|
||||
|
||||
Performance/load, full driver/native transport operation, Windows service ACL/account behavior, backend/live AVEVA, failover and the two-hour lifecycle soak remain separate gates.
|
||||
@@ -0,0 +1,43 @@
|
||||
# CI3 Runtime rebuild idempotence assertion investigation
|
||||
|
||||
Base: `04d5513af2620f277a56acbb19ae466623fc054f` (SDK2, actual HG1.0), branch `codex/opcua2-rebuild-test-barrier`. SDK `10.0.403`. No production edits, budget changes, expected result changes, skips, or issue actions.
|
||||
|
||||
## Cause and classification evidence
|
||||
|
||||
`RecordingSink.EnsureFolder` queues `EF:eq-1` before the actor finishes materialisation and calls `AnnounceAddedNodes`, which queues `NA:line-1`. The old test waited only for EF, captured a possibly incomplete first-call count, then slept after sending the second request. Thus it could attribute the first apply's trailing announcement to the second apply. `FinishRebuild` sends the public `RebuildCompleted` outcome after `CompleteAddressSpaceApply` and deferred writes, so it supplies the needed completion barrier.
|
||||
|
||||
An independent source comparison against landed SDK1.5 master `230cef4e` found no actor/test changes: `git diff 230cef4e 04d5513a -- src/Server/ZB.MOM.WW.OtOpcUa.Runtime/OpcUa/OpcUaPublishActor.cs tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/OpcUa/OpcUaPublishActorRebuildTests.cs` has zero bytes (`master-candidate-source.diff`). This supports a preexisting test-ordering failure, not an SDK2 production idempotence regression.
|
||||
|
||||
## Controlled reproduction
|
||||
|
||||
Temporary test-only `AfterEnsureFolder` gate blocked the first apply immediately after EF was recorded. The original EF-presence wait and snapshot therefore captured exactly one call. The second request was queued, the gate released, and both apply-completion counters awaited before the unchanged count assertion. The exact CI failure reproduced: expected 1, actual 2. Diagnostic records `completed calls=EF:eq-1,NA:line-1; completions=2`. There was only one EF and one announcement: no repeated materialisation.
|
||||
|
||||
- RED: `controlled-red/controlled-red-diagnostic.trx`, `test-diagnostic.log`, `controlled-red-source.cs`; build exit0, test exit1, guard exit0; 1 failed/0 passed/0 skipped (expected reproduction).
|
||||
- GREEN: same first-apply gate with explicit count1 while blocked; release, then await first correlated clean `RebuildCompleted` before snapshot and second correlated clean reply before assertion. `controlled-green/controlled-green.trx`, `controlled-green-source.cs`; build0/test0/guard0; 1 passed/0 failed/0 skipped.
|
||||
- Temporary gates are absent from final source. Only the target test changes: both requests ask for outcomes, both replies must match correlation and be clean, original EF/no-full-rebuild/unchanged-call-count assertions remain, and fixed sleep is removed. Existing `PresenceBudget` and cancellation token are retained.
|
||||
|
||||
## Reproduction commands
|
||||
|
||||
From the owned worktree, test project is `tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/ZB.MOM.WW.OtOpcUa.Runtime.Tests.csproj`.
|
||||
|
||||
```sh
|
||||
dotnet build "$PROJECT" -c Release --no-restore
|
||||
dotnet test "$PROJECT" -c Release --no-build --no-restore --filter 'FullyQualifiedName~Second_rebuild_with_same_artifact_is_empty_plan_no_op' --logger 'trx;LogFileName=focused.trx' --results-directory "$RESULTS"
|
||||
# Preserve dotnet exit before guard; guard does not replace test status.
|
||||
MIN_EXECUTED=1 bash scripts/ci/assert-not-all-skipped.sh "$RESULTS/focused.trx"
|
||||
# Owning suite omits --filter and uses its own fresh results directory/TRX.
|
||||
```
|
||||
|
||||
## Final validation
|
||||
|
||||
Commit: `b5f51406ffc7dcf5b40baa52156ea85e862ab5b4` (one test file; worktree clean).
|
||||
|
||||
Final owning project build: exit0, 0 warnings, 0 errors, 2.97s (`final/build.log`, `final/build-status.txt`). Full unfiltered Runtime suite: 1,005 total, 1,002 passed, 0 failed, 3 existing skips, duration1m25s. Test exit0, nonzero execution guard exit0 (`final/runtime-final.trx`, `final/test.log`, `final/guard.log`, `final/status.txt`). Target test is passed in that full-suite TRX. DLL SHA256 is recorded in `final/runtime-dll.sha256`.
|
||||
|
||||
The three existing skips are the retired-v3 artifact/composer parity tests `Composer_and_artifact_agree_on_equipment_node_device_host`, `ParseComposition_scoped_flags_cross_cluster_orphan_equipment`, and `Driverbound_equipment_kept_when_driver_in_cluster`. No skips were added or changed. No remaining diagnostics from this work.
|
||||
|
||||
Host concurrently ran coordinator validation; this task makes no performance claim. Root owns broader CI/fixture classification and issue writes.
|
||||
|
||||
|
||||
|
||||
Coordinator landing: master `52d1373e`, merged into SDK2 at `75057d8c`; [#805](https://gitea.dohertylan.com/dohertj2/lmxopcua/issues/805) closed after independent SDK1.5 master Runtime validation:1,002passed/0failed/3existing skips, test/guard exits0. No product source changed. Fresh full CI4 follows this repair and fixture correction; it is not assumed green from the owning-suite pass.
|
||||
@@ -0,0 +1,352 @@
|
||||
{
|
||||
"scope": "Instrumented candidate-only canonical saturation diagnostic. No acceptance protocol or baseline change.",
|
||||
"allocationTracking": {
|
||||
"enabled": true,
|
||||
"environment": "OPCUA_CERTIFICATE_LEAK_TRACKING=1",
|
||||
"overhead": "Exact SDK captures a StackTrace and tracker entry per certificate wrapper. Not an uninstrumented performance/acceptance baseline."
|
||||
},
|
||||
"bundle": {
|
||||
"sourceRevision": "2738d7cc8675298ceb38ed19ca58a9f643036ff1",
|
||||
"receiptSha256": "e4eed693c80baa29266f58cee4473b1181bc6de8f4c460843bd6b1993bcf7179",
|
||||
"sdk": "2.0.1",
|
||||
"gateway": "1.0.0",
|
||||
"runtime": "self-contained Linux ARM64 10.0.14 for target and observer",
|
||||
"sha256": "e4eed693c80baa29266f58cee4473b1181bc6de8f4c460843bd6b1993bcf7179"
|
||||
},
|
||||
"run": {
|
||||
"acceptance": false,
|
||||
"containerExitStatus": 1,
|
||||
"wallSeconds": 1429.7275004169205,
|
||||
"sourceRevision": "2738d7cc8675298ceb38ed19ca58a9f643036ff1",
|
||||
"receiptSha256": "e4eed693c80baa29266f58cee4473b1181bc6de8f4c460843bd6b1993bcf7179",
|
||||
"descriptorTests": {
|
||||
"executed": 2,
|
||||
"skipped": 0
|
||||
},
|
||||
"experimentTests": {
|
||||
"executed": 1,
|
||||
"skipped": 0
|
||||
},
|
||||
"diagnosticOutcome": {
|
||||
"kind": "saturation-result",
|
||||
"utc": "2026-11-10T20:30:58.5817344+00:00",
|
||||
"canonicalCycles": 384,
|
||||
"settlingCycles": 1,
|
||||
"restarts": 96,
|
||||
"phaseSeconds": 1425.2416632,
|
||||
"readiness": true,
|
||||
"coldBudgetRejected": true,
|
||||
"shutdownOwnershipVerified": true,
|
||||
"acceptance": false
|
||||
},
|
||||
"protocolCompleted": true
|
||||
},
|
||||
"start": {
|
||||
"kind": "start",
|
||||
"utc": "2026-11-10T20:07:13.3303503+00:00",
|
||||
"processId": 200,
|
||||
"mode": "saturation",
|
||||
"requestedSeconds": 1800,
|
||||
"minimumFullSeconds": 7200,
|
||||
"build": "2738d7cc8675298ceb38ed19ca58a9f643036ff1",
|
||||
"sdk": "2.0.1+07e62f72500cc0923a2bab940653c8c1116fa286",
|
||||
"diagnosticInstrumentation": "SDK2.0.1 CP lists and certificate-validation cached stores; read-only",
|
||||
"limits": {
|
||||
"retainedManagedGrowthBytes": 67108864,
|
||||
"rssGrowthBytes": 134217728,
|
||||
"fdGrowth": 32
|
||||
},
|
||||
"coverage": "encrypted sessions; scalar/Int16[] Raw+UNS; subscription removal; browse abandonment; history release/cross-session/close; surgical changes/rebuild; revocation; paused TCP read/recovery; same-PKI restart"
|
||||
},
|
||||
"coldRejection": {
|
||||
"kind": "budget-rejected",
|
||||
"cycle": 248,
|
||||
"seconds": 913.8748808,
|
||||
"resources": {
|
||||
"Cycle": 248,
|
||||
"Seconds": 913.830416,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 193,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 19961096,
|
||||
"RssBytes": 362409984,
|
||||
"AllocatedBytes": 5662357112,
|
||||
"Gen0": 619,
|
||||
"Gen1": 496,
|
||||
"Gen2": 496,
|
||||
"GcCommittedBytes": 48226304,
|
||||
"GcHeapBytes": 22015464,
|
||||
"GcFragmentedBytes": 2054368,
|
||||
"PinnedObjects": 1,
|
||||
"PendingFinalizers": 17,
|
||||
"JitMethods": 75848,
|
||||
"JitIlBytes": 11437192,
|
||||
"ActiveTimers": 16,
|
||||
"Threads": 21
|
||||
},
|
||||
"baseline": {
|
||||
"Cycle": 3,
|
||||
"Seconds": 7.6451075,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 191,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 19878208,
|
||||
"RssBytes": 224620544,
|
||||
"AllocatedBytes": 86490920,
|
||||
"Gen0": 7,
|
||||
"Gen1": 6,
|
||||
"Gen2": 6,
|
||||
"GcCommittedBytes": 41967616,
|
||||
"GcHeapBytes": 20716064,
|
||||
"GcFragmentedBytes": 837856,
|
||||
"PinnedObjects": 1,
|
||||
"PendingFinalizers": 16,
|
||||
"JitMethods": 35741,
|
||||
"JitIlBytes": 4635402,
|
||||
"ActiveTimers": 18,
|
||||
"Threads": 27
|
||||
},
|
||||
"failure": "Shouldly.ShouldAssertException: 362409984L\r\n should be less than or equal to\r\n358838272L\r\n but was not\n at ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests.Sdk2LifecycleSoakTests.Repeated_lifecycles_return_resources_to_a_steady_baseline() in tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/Sdk2LifecycleSoakTests.cs:line 133"
|
||||
},
|
||||
"observer": {
|
||||
"protocol": "sdk2-saturation-v1",
|
||||
"runId": "545b3093-5eab-4348-a72d-926b9d1da7da",
|
||||
"targetPid": 200,
|
||||
"observerPid": 109,
|
||||
"active": false,
|
||||
"drained": true,
|
||||
"markersValid": true,
|
||||
"eventsLost": 0,
|
||||
"sdkFactoryJit": 0,
|
||||
"sdkFactoryLoads": 0,
|
||||
"sdkAssemblyLoads": 0,
|
||||
"windows": [
|
||||
{
|
||||
"name": "window1",
|
||||
"firstCycle": 321,
|
||||
"lastCycle": 352,
|
||||
"startRestarts": 80,
|
||||
"endRestarts": 88,
|
||||
"sdkFactoryJit": 0,
|
||||
"sdkFactoryLoads": 0,
|
||||
"sdkAssemblyLoads": 0
|
||||
},
|
||||
{
|
||||
"name": "window2",
|
||||
"firstCycle": 353,
|
||||
"lastCycle": 384,
|
||||
"startRestarts": 88,
|
||||
"endRestarts": 96,
|
||||
"sdkFactoryJit": 0,
|
||||
"sdkFactoryLoads": 0,
|
||||
"sdkAssemblyLoads": 0
|
||||
}
|
||||
],
|
||||
"otherJit": 287,
|
||||
"loadedNativeBytes": 60760,
|
||||
"runtimeControlJit": 0,
|
||||
"configuration": {
|
||||
"runtimeProvider": "Microsoft-Windows-DotNETRuntime",
|
||||
"level": "Verbose",
|
||||
"keywords": "0x18",
|
||||
"markerProvider": "OtOpcUa-Sdk2-Saturation",
|
||||
"circularBufferMiB": 16,
|
||||
"requestRundown": false
|
||||
},
|
||||
"acceptance": false
|
||||
},
|
||||
"tests": {
|
||||
"descriptor-controls": {
|
||||
"counters": {
|
||||
"total": "2",
|
||||
"executed": "2",
|
||||
"passed": "2",
|
||||
"failed": "0",
|
||||
"error": "0",
|
||||
"timeout": "0",
|
||||
"aborted": "0",
|
||||
"inconclusive": "0",
|
||||
"passedButRunAborted": "0",
|
||||
"notRunnable": "0",
|
||||
"notExecuted": "0",
|
||||
"disconnected": "0",
|
||||
"warning": "0",
|
||||
"completed": "0",
|
||||
"inProgress": "0",
|
||||
"pending": "0"
|
||||
},
|
||||
"sha256": "0de2cd05f9f1dbcaee3cff33e3e625723f5dc19b8f2dbcf3fbf1376792bc489c",
|
||||
"errors": []
|
||||
},
|
||||
"saturation": {
|
||||
"counters": {
|
||||
"total": "1",
|
||||
"executed": "1",
|
||||
"passed": "0",
|
||||
"failed": "1",
|
||||
"error": "0",
|
||||
"timeout": "0",
|
||||
"aborted": "0",
|
||||
"inconclusive": "0",
|
||||
"passedButRunAborted": "0",
|
||||
"notRunnable": "0",
|
||||
"notExecuted": "0",
|
||||
"disconnected": "0",
|
||||
"warning": "0",
|
||||
"completed": "0",
|
||||
"inProgress": "0",
|
||||
"pending": "0"
|
||||
},
|
||||
"sha256": "1b5cba9e1b3c348d0dcbb79ba4c715d0e837602c78afc2c221c64c2e776e825b",
|
||||
"errors": [
|
||||
"Shouldly.ShouldAssertException : 362409984L\r\n should be less than or equal to\r\n358838272L\r\n but was not"
|
||||
]
|
||||
}
|
||||
},
|
||||
"firstSample": {
|
||||
"Cycle": 1,
|
||||
"Seconds": 2.7631819,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 191,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 19275376,
|
||||
"RssBytes": 222228480,
|
||||
"AllocatedBytes": 62054576,
|
||||
"Gen0": 3,
|
||||
"Gen1": 2,
|
||||
"Gen2": 2,
|
||||
"GcCommittedBytes": 47521792,
|
||||
"GcHeapBytes": 20204744,
|
||||
"GcFragmentedBytes": 929368,
|
||||
"PinnedObjects": 1,
|
||||
"PendingFinalizers": 19,
|
||||
"JitMethods": 29401,
|
||||
"JitIlBytes": 4142214,
|
||||
"ActiveTimers": 12,
|
||||
"Threads": 27
|
||||
},
|
||||
"baselineSample": {
|
||||
"Cycle": 3,
|
||||
"Seconds": 7.6451075,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 191,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 19878208,
|
||||
"RssBytes": 224620544,
|
||||
"AllocatedBytes": 86490920,
|
||||
"Gen0": 7,
|
||||
"Gen1": 6,
|
||||
"Gen2": 6,
|
||||
"GcCommittedBytes": 41967616,
|
||||
"GcHeapBytes": 20716064,
|
||||
"GcFragmentedBytes": 837856,
|
||||
"PinnedObjects": 1,
|
||||
"PendingFinalizers": 16,
|
||||
"JitMethods": 35741,
|
||||
"JitIlBytes": 4635402,
|
||||
"ActiveTimers": 18,
|
||||
"Threads": 27
|
||||
},
|
||||
"lastSample": {
|
||||
"Cycle": 385,
|
||||
"Seconds": 1425.221077,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 200,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 19974280,
|
||||
"RssBytes": 367771648,
|
||||
"AllocatedBytes": 8790684304,
|
||||
"Gen0": 963,
|
||||
"Gen1": 770,
|
||||
"Gen2": 770,
|
||||
"GcCommittedBytes": 46555136,
|
||||
"GcHeapBytes": 20348120,
|
||||
"GcFragmentedBytes": 373840,
|
||||
"PinnedObjects": 1,
|
||||
"PendingFinalizers": 27,
|
||||
"JitMethods": 80193,
|
||||
"JitIlBytes": 12208985,
|
||||
"ActiveTimers": 15,
|
||||
"Threads": 22
|
||||
},
|
||||
"peakResources": {
|
||||
"ManagedBytes": 20501376,
|
||||
"RssBytes": 394956800,
|
||||
"Descriptors": 201,
|
||||
"PkiDescriptors": 0,
|
||||
"JitMethods": 80193,
|
||||
"GcCommittedBytes": 48549888
|
||||
},
|
||||
"evidenceSha256": {
|
||||
"run-receipt.json": "842eacf845af019df087c69c55bb527d83c6e0d6f42057c8dd63d65eb5f86511",
|
||||
"resources.jsonl": "eab640b684908d6978ca323c919d40b8ec6496a483484e3f86a396511e28dbc3",
|
||||
"control/drained.json": "5a61b090086f379f3cc96b1efe328225efdafb027795a57f7636acfbf2bc5594",
|
||||
"results/descriptor-controls.trx": "0de2cd05f9f1dbcaee3cff33e3e625723f5dc19b8f2dbcf3fbf1376792bc489c",
|
||||
"results/saturation.trx": "1b5cba9e1b3c348d0dcbb79ba4c715d0e837602c78afc2c221c64c2e776e825b",
|
||||
"test-exit-status.txt": "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865",
|
||||
"observer-exit-status.txt": "9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa",
|
||||
"target-runtime.txt": "1287d31fb4a49b24e82c346e2b9d2a4ef956d151e08f0497c3aef78617d2a012",
|
||||
"observer-runtime.txt": "38c764d4f77a4bfa9c27905c5f8feb1076d30ea626f28d0f188e4bf86bb0c456"
|
||||
},
|
||||
"noOrdinaryCompleteRecord": true,
|
||||
"privatePkiAndRawTraceExcluded": true,
|
||||
"sanitization": {
|
||||
"checkoutPrefixRedacted": true,
|
||||
"rawResourceHashPreserved": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# Candidate canonical saturation diagnostic
|
||||
|
||||
The prospectively fixed experiment completed 384 canonical cycles / 96 restarts
|
||||
plus one settling cycle in **1,425.242 seconds**, within the 1,800-second cutoff.
|
||||
Both traced windows, 321–352 and 353–384, recorded **zero generated-factory JIT/load
|
||||
events, zero new SDK assembly loads and zero lost events**; the continuous attached
|
||||
interval also had zero such events. Other JIT starts: 287. Summed CLR `MethodSize`
|
||||
values: 60,760 bytes; this is not an RSS/PSS measurement. The observer was active
|
||||
before cycle 321 and stopped/drained before cycle 385. Shutdown ownership checks
|
||||
passed.
|
||||
|
||||
**The cold resource failure remains:** cycle 248 RSS 362,409,984 exceeded
|
||||
358,838,272 bytes (cycle 3 + 128 MiB). The first assertion was retained and rethrown;
|
||||
test/container/launcher exit 1, one failed executed lifecycle test, no ordinary
|
||||
completion/full-duration result. Descriptor controls passed 2/2. Readiness=true
|
||||
describes the selected generated-factory criterion on the canonical workload,
|
||||
**not general runtime/performance readiness, G4 acceptance or #799 closure**.
|
||||
|
||||
Source `2738d7cc`, exact SDK 2.0.1 and HG 1.0.0; both participants use self-contained
|
||||
Linux ARM64 runtime 10.0.14. Astra approved the exact immutable 1,275-file bundle
|
||||
before launch and independently verified 1,064 module identities. The production/
|
||||
central-package source tree matched the candidate. Outer output permissions were
|
||||
0700; private PKI and raw trace stay unversioned.
|
||||
|
||||
`OPCUA_CERTIFICATE_LEAK_TRACKING=1` was enabled before startup. Exact SDK tracking
|
||||
captures an allocation StackTrace and tracker entry per wrapper. This additional
|
||||
diagnostic overhead prevents reuse as an uninstrumented performance/acceptance
|
||||
baseline. Caps and measurement baseline were unchanged.
|
||||
|
||||
[Hashed result, windows, failure and runtime receipts](saturation-candidate.json)
|
||||
preserve cold/warm/peak observations. A matched original-SDK canonical adapter,
|
||||
prospectively reviewed acceptance procedure and at least 7,200 seconds after fixed
|
||||
warmup are still required before a replacement resource gate could be considered.
|
||||
Continued macOS native certificate-object accumulation remains separate and
|
||||
unresolved. No production workaround or acceptance waiver was applied.
|
||||
@@ -0,0 +1,361 @@
|
||||
{
|
||||
"issue": "#799",
|
||||
"status": "failed",
|
||||
"scope": "In-process SDK client and production server/node-manager lifecycle; not standalone server attribution",
|
||||
"source": {
|
||||
"launcherSourceRevision": "38098ce142ab8c382825aa754e0758071dd3a46b",
|
||||
"publishedSourceRevision": "74c938f37cbb9cf08b6ca72e4effc511120dc274",
|
||||
"productionEquivalentTo": "7b353d7bae5311a2f0a33c0a82c88d2c5b49d76f",
|
||||
"productionDiffEmpty": true,
|
||||
"canonicalDescriptorProbeCommit": "642aefe3",
|
||||
"testSourceSha256": "e695783e05787d94bf5d167b25b1c48b446b800c28259f023c28ed6012d737d1",
|
||||
"publishCommand": "reused self-contained artifact: /private/tmp/opcua2-server-linux-diagnostic-20261110a/app",
|
||||
"appPath": "/private/tmp/opcua2-server-linux-diagnostic-20261110a/app",
|
||||
"apphost": {
|
||||
"format": "ELF64 AArch64",
|
||||
"sha256": "97531b8005df9dc8b129d2b22ba2049cf572d5d970046f7e8644ec632e860032"
|
||||
},
|
||||
"runtimeConfig": {
|
||||
"runtimeOptions": {
|
||||
"tfm": "net10.0",
|
||||
"includedFrameworks": [
|
||||
{
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.14"
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.AspNetCore.App",
|
||||
"version": "10.0.14"
|
||||
}
|
||||
],
|
||||
"configProperties": {
|
||||
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
|
||||
"System.Reflection.NullabilityInfoContext.IsSupported": true,
|
||||
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"sdkDependencies": [
|
||||
"OPCFoundation.NetStandard.Opc.Ua.Client/2.0.1",
|
||||
"OPCFoundation.NetStandard.Opc.Ua.Configuration/2.0.1",
|
||||
"OPCFoundation.NetStandard.Opc.Ua.Core.Schema/2.0.1",
|
||||
"OPCFoundation.NetStandard.Opc.Ua.Core.Types/2.0.1",
|
||||
"OPCFoundation.NetStandard.Opc.Ua.Core/2.0.1",
|
||||
"OPCFoundation.NetStandard.Opc.Ua.Security.Certificates/2.0.1",
|
||||
"OPCFoundation.NetStandard.Opc.Ua.Server/2.0.1",
|
||||
"OPCFoundation.NetStandard.Opc.Ua.Types/2.0.1"
|
||||
],
|
||||
"image": {
|
||||
"Id": "sha256:b04611ee9e1b7d36797012ed921962c57189cb4dbb3dff12afb8e158110f293b",
|
||||
"RepoDigests": [
|
||||
"mcr.microsoft.com/dotnet/sdk@sha256:e362a8dbcd691522456da26a5198b8f3ca1d7641c95624fadc5e3e82678bd08a"
|
||||
],
|
||||
"Architecture": "arm64",
|
||||
"Os": "linux"
|
||||
},
|
||||
"hostDotnet": ".NET SDK:\n Version: 10.0.403\n Commit: 5a2162bea6\n Workload version: 10.0.400-manifests.a00bebb6\n MSBuild version: 18.9.15+5a2162bea\n\nRuntime Environment:\n OS Name: Mac OS X\n OS Version: 26.5\n OS Platform: Darwin\n RID: osx-arm64\n Base Path: /opt/homebrew/Cellar/dotnet/10.0.403/libexec/sdk/10.0.403/\n\n.NET workloads installed:\nThere are no installed workloads to display.\nConfigured to use workload sets when installing new manifests.\nNo workload sets are installed. Run \"dotnet workload restore\" to install a workload set.\n\nHost:\n Version: 10.0.14\n Architecture: arm64\n Commit: 0427b9abbe\n\n.NET SDKs installed:\n 10.0.403 [/opt/homebrew/Cellar/dotnet/10.0.403/libexec/sdk]\n\n.NET runtimes installed:\n Microsoft.AspNetCore.App 10.0.14 [/opt/homebrew/Cellar/dotnet/10.0.403/libexec/shared/Microsoft.AspNetCore.App]\n Microsoft.NETCore.App 10.0.14 [/opt/homebrew/Cellar/dotnet/10.0.403/libexec/shared/Microsoft.NETCore.App]\n\nOther architectures found:\n None\n\nEnvironment variables:\n DOTNET_ROOT [/opt/homebrew/Cellar/dotnet/10.0.403/libexec]\n\nglobal.json file:\n /Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/global.json\n\nLearn more:\n https://aka.ms/dotnet/info\n\nDownload .NET:\n https://aka.ms/dotnet/download\n",
|
||||
"network": "none",
|
||||
"mode": "full",
|
||||
"durationSeconds": 7200,
|
||||
"startedUtc": "2026-11-10T18:10:21Z"
|
||||
},
|
||||
"launcherResult": {
|
||||
"status": "failed",
|
||||
"containerExitCode": 1,
|
||||
"containerElapsedSeconds": 907.5196822080761,
|
||||
"validationError": "TRX expected exact passed/executed count 1: /private/tmp/opcua2-server-linux-full-20261110/task/results/soak.trx"
|
||||
},
|
||||
"start": {
|
||||
"kind": "start",
|
||||
"utc": "2026-11-10T18:10:24.882222+00:00",
|
||||
"processId": 192,
|
||||
"mode": "full",
|
||||
"requestedSeconds": 7200,
|
||||
"minimumFullSeconds": 7200,
|
||||
"build": "38098ce142ab8c382825aa754e0758071dd3a46b",
|
||||
"sdk": "2.0.1+07e62f72500cc0923a2bab940653c8c1116fa286",
|
||||
"diagnosticInstrumentation": "SDK2.0.1 CP lists and certificate-validation cached stores; read-only",
|
||||
"limits": {
|
||||
"retainedManagedGrowthBytes": 67108864,
|
||||
"rssGrowthBytes": 134217728,
|
||||
"fdGrowth": 32
|
||||
},
|
||||
"coverage": "encrypted sessions; scalar/Int16[] Raw+UNS; subscription removal; browse abandonment; history release/cross-session/close; surgical changes/rebuild; revocation; paused TCP read/recovery; same-PKI restart"
|
||||
},
|
||||
"failed": {
|
||||
"kind": "failed",
|
||||
"cycles": 248,
|
||||
"restarts": 61,
|
||||
"seconds": 903.5081762,
|
||||
"exceptionType": "Shouldly.ShouldAssertException"
|
||||
},
|
||||
"test": {
|
||||
"executionId": "b3f13597-f945-44bf-9134-cb0282f6455c",
|
||||
"testId": "3a3639bf-5159-da35-0b1a-0a6c89f03e2c",
|
||||
"testName": "ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests.Sdk2LifecycleSoakTests.Repeated_lifecycles_return_resources_to_a_steady_baseline",
|
||||
"computerName": "b0dfc9462250",
|
||||
"duration": "00:15:04.9982391",
|
||||
"startTime": "2026-11-10T18:10:23.4089949+00:00",
|
||||
"endTime": "2026-11-10T18:25:28.4141750+00:00",
|
||||
"testType": "13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b",
|
||||
"outcome": "Failed",
|
||||
"testListId": "8c84fa94-04c1-424b-9868-57a2d4851a1d",
|
||||
"relativeResultsDirectory": "b3f13597-f945-44bf-9134-cb0282f6455c"
|
||||
},
|
||||
"error": "Shouldly.ShouldAssertException : 353931264L\r\n should be less than or equal to\r\n351850496L\r\n but was not",
|
||||
"counters": {
|
||||
"total": "1",
|
||||
"executed": "1",
|
||||
"passed": "0",
|
||||
"failed": "1",
|
||||
"error": "0",
|
||||
"timeout": "0",
|
||||
"aborted": "0",
|
||||
"inconclusive": "0",
|
||||
"passedButRunAborted": "0",
|
||||
"notRunnable": "0",
|
||||
"notExecuted": "0",
|
||||
"disconnected": "0",
|
||||
"warning": "0",
|
||||
"completed": "0",
|
||||
"inProgress": "0",
|
||||
"pending": "0"
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"Cycle": 1,
|
||||
"Seconds": 2.6707062,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 187,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 19829880,
|
||||
"RssBytes": 212717568,
|
||||
"AllocatedBytes": 45694176,
|
||||
"Gen0": 3,
|
||||
"Gen1": 2,
|
||||
"Gen2": 2
|
||||
},
|
||||
{
|
||||
"Cycle": 3,
|
||||
"Seconds": 7.5077403,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 187,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 20362800,
|
||||
"RssBytes": 217632768,
|
||||
"AllocatedBytes": 52216848,
|
||||
"Gen0": 7,
|
||||
"Gen1": 6,
|
||||
"Gen2": 6
|
||||
},
|
||||
{
|
||||
"Cycle": 25,
|
||||
"Seconds": 90.9064734,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 189,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 20360784,
|
||||
"RssBytes": 269185024,
|
||||
"AllocatedBytes": 349530024,
|
||||
"Gen0": 57,
|
||||
"Gen1": 50,
|
||||
"Gen2": 50
|
||||
},
|
||||
{
|
||||
"Cycle": 50,
|
||||
"Seconds": 181.3146113,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 189,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 20444424,
|
||||
"RssBytes": 230686720,
|
||||
"AllocatedBytes": 652625024,
|
||||
"Gen0": 113,
|
||||
"Gen1": 100,
|
||||
"Gen2": 100
|
||||
},
|
||||
{
|
||||
"Cycle": 100,
|
||||
"Seconds": 361.5237015,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 190,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 20637848,
|
||||
"RssBytes": 229531648,
|
||||
"AllocatedBytes": 1261146560,
|
||||
"Gen0": 225,
|
||||
"Gen1": 200,
|
||||
"Gen2": 200
|
||||
},
|
||||
{
|
||||
"Cycle": 150,
|
||||
"Seconds": 546.7687542,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 189,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 20388720,
|
||||
"RssBytes": 281825280,
|
||||
"AllocatedBytes": 1907065880,
|
||||
"Gen0": 338,
|
||||
"Gen1": 300,
|
||||
"Gen2": 300
|
||||
},
|
||||
{
|
||||
"Cycle": 200,
|
||||
"Seconds": 727.6899225,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 189,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 20678608,
|
||||
"RssBytes": 269180928,
|
||||
"AllocatedBytes": 2516228704,
|
||||
"Gen0": 450,
|
||||
"Gen1": 400,
|
||||
"Gen2": 400
|
||||
},
|
||||
{
|
||||
"Cycle": 245,
|
||||
"Seconds": 896.2277082,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 189,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 20292328,
|
||||
"RssBytes": 341528576,
|
||||
"AllocatedBytes": 3109239976,
|
||||
"Gen0": 552,
|
||||
"Gen1": 490,
|
||||
"Gen2": 490
|
||||
},
|
||||
{
|
||||
"Cycle": 247,
|
||||
"Seconds": 901.025049,
|
||||
"Sessions": 0,
|
||||
"Subscriptions": 0,
|
||||
"Items": 0,
|
||||
"BrowsePoints": 0,
|
||||
"HistoryPoints": 0,
|
||||
"LiveDataItems": 0,
|
||||
"LiveEventItems": 0,
|
||||
"MonitoredNodes": 0,
|
||||
"Variables": 19,
|
||||
"Folders": 4,
|
||||
"Conditions": 1,
|
||||
"CachedCertificateStores": 2,
|
||||
"Descriptors": 189,
|
||||
"PkiDescriptors": 0,
|
||||
"ManagedBytes": 20547624,
|
||||
"RssBytes": 349171712,
|
||||
"AllocatedBytes": 3115447904,
|
||||
"Gen0": 556,
|
||||
"Gen1": 494,
|
||||
"Gen2": 494
|
||||
}
|
||||
],
|
||||
"maximumRecordedQuiescent": {
|
||||
"ManagedBytes": 20890032,
|
||||
"RssBytes": 349171712,
|
||||
"Descriptors": 191,
|
||||
"PkiDescriptors": 0
|
||||
},
|
||||
"evidenceSha256": {
|
||||
"result.json": "cb3d4dc9c3dcd80392e66f21d6b6e36907d8438c550a1723dced57a0968e7e89",
|
||||
"source.json": "5bd909d1f289d38039b996ed110b450cd3914a6a69d96c7f3aa83b016629497d",
|
||||
"task/soak.jsonl": "1d24d32382b5be6ebca2615389929818dd6c9874b7787cb91482ceaa5d3b9cd3",
|
||||
"task/results/soak.trx": "798c97c3158ec35d9dbae754e98f57a7bf403ae10b81473f98a52a89c2401265",
|
||||
"task/results/descriptor-controls.trx": "99ca030ff7542e2dc43959d8cf9a2f129352db9c6a44d5d011c6e10fa9327f01"
|
||||
},
|
||||
"limitations": [
|
||||
"No complete row; no two-hour pass.",
|
||||
"Failed sample248 RSS is reported in TRX before JSONL quiescent emission.",
|
||||
"No resource budget or production workaround changed.",
|
||||
"Private PKI and full task output are deliberately excluded."
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+7
-6
@@ -301,12 +301,13 @@ guards in front of the store, bound from the same `Security:ApiKeys` section
|
||||
**Per-client accounting and its limits (#751).** `IOpcUaUserAuthenticator` has an endpoint-carrying
|
||||
`AuthenticateUserNameAsync(username, password, remoteEndpoint, ct)` overload (default: ignore the endpoint
|
||||
and delegate), which the server calls and `ApiKeyOpcUaUserAuthenticator` forwards to
|
||||
`IApiKeyStore.FindAsync(..., remoteAddress, ...)`. The OPC UA SDK (1.5.378) exposes no client address on any
|
||||
public surface, so `SecureChannelRemoteEndpoint` reads it from the TCP listener's private channel map by
|
||||
reflection, keyed by the session's secure channel id. It is fail-soft: an unresolvable address (an SDK upgrade
|
||||
that renamed the members, or a client re-activating over a new channel) is charged to the process-wide bucket
|
||||
only, which therefore stays as the backstop. `ApiKeyMissBudgetPerEndpointTests` (OpcUaServer.IntegrationTests)
|
||||
pins the resolution over the wire so such an upgrade fails a test. At most `BoundedApiKeyStore.MaxTrackedEndpoints`
|
||||
`IApiKeyStore.FindAsync(..., remoteAddress, ...)`. SDK 2.0.1 supplies the public transport-observed
|
||||
`SecureChannelContext.PeerAddress` on session activation. `OtOpcUaSdkServer` scopes it to the current
|
||||
asynchronous activation and restores the prior context in `finally`; `OpcUaApplicationHost` adapts it to
|
||||
the existing IP-budget seam with port zero (no peer port is exposed). The former private-channel-map
|
||||
reflection helper is removed. An unknown address is charged to the process-wide bucket only, which
|
||||
therefore stays as the backstop. `ApiKeyMissBudgetPerEndpointTests` (OpcUaServer.IntegrationTests)
|
||||
pins per-client accounting over the wire; concurrent IPv4/IPv6 activation probes check peer isolation. At most `BoundedApiKeyStore.MaxTrackedEndpoints`
|
||||
(4096) addresses are tracked; when full, idle (refilled) buckets are pruned and a new address beyond that is
|
||||
charged process-wide only — never handed a fresh budget. Clients behind one NAT address share a bucket. Neither
|
||||
guard applies to a driver-only node: its `ArtifactApiKeyStore` is two in-memory dictionary probes and is never
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
**Landed:** OtOpcUa master `230cef4e` consumes the published Client/Contracts1.0.0;
|
||||
historiangw#131 is closed with verification. The change is merged into this SDK2
|
||||
branch. The patch below is a historical/replay input; the validator recognizes
|
||||
an already migrated checkout. [Master verification](../../../docs/plans/evidence/2026-11-10-historiangw-131.json)
|
||||
records the whole-solution zero-warning build,523 Gateway passes,1,002 Runtime
|
||||
passes and package hashes. SDK2 resource/release gates remain separate.
|
||||
|
||||
# HistorianGateway consumer TLS migration — issue 131
|
||||
|
||||
`consumer-1.0.0.patch` is a reviewable consumer change, kept separate from the
|
||||
buildable application in this checkout. Real paired Client/Contracts 1.0.0
|
||||
packages are now published and the isolated owning-project proof passed (RESULTS.md).
|
||||
The application in this checkout remains pinned to published 0.4.0 until the patch
|
||||
is applied by the coordinator.
|
||||
|
||||
The patch changes both central package pins to 1.0.0 and maps the new option as:
|
||||
|
||||
```csharp
|
||||
AllowUntrustedServerCertificate = options.UseTls && options.AllowUntrustedServerCertificate,
|
||||
```
|
||||
|
||||
The old inverse-mapping and obsolete h2c-history comments are removed. The existing
|
||||
h2c construction test now includes a stale opt-out as well as a stale CA path.
|
||||
Three new bounded local wire tests verify behavior through the real adapter and
|
||||
published client API, without reflection or replacing the transport handler:
|
||||
|
||||
- TLS with normal defaults rejects a self-signed server before the metadata RPC.
|
||||
- Explicit opt-out permits that self-signed server shape and executes one RPC.
|
||||
- Plaintext with stale opt-out and CA settings executes one RPC, proving TLS-only
|
||||
settings were not forwarded to the client's validating options.
|
||||
|
||||
The local server uses an ephemeral loopback port and a fresh short-lived
|
||||
self-signed certificate. RPC completion is bounded to 15 seconds; the consumer
|
||||
call timeout is two seconds. No gateway, deployment, or global trust store is used.
|
||||
|
||||
Before applying the patch, query the live Gitea NuGet version indexes for BOTH
|
||||
packages and require actual stable 1.0.0. Source `main` having the new API is not a
|
||||
package release. At initial inspection, both feed indexes listed exactly
|
||||
0.4.0, 0.3.0, 0.2.0, 0.1.0, and the new API was only in Unreleased at HG commit
|
||||
`e4321915ebc5e0704a68ecad21b85c1cc1c80d6b`.
|
||||
|
||||
```bash
|
||||
git apply --check scripts/validation/historiangw-131/consumer-1.0.0.patch
|
||||
git apply scripts/validation/historiangw-131/consumer-1.0.0.patch
|
||||
# From the consumer checkout, using a fresh run/results directory:
|
||||
hg131_results=/absolute/fresh/results
|
||||
test ! -e "$hg131_results" || exit 2
|
||||
mkdir -p "$hg131_results" || exit
|
||||
hg131_test_status=0
|
||||
dotnet test tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests \
|
||||
-c Release --logger trx --results-directory "$hg131_results" || hg131_test_status=$?
|
||||
hg131_guard_status=0
|
||||
MIN_EXECUTED=1 bash scripts/ci/assert-not-all-skipped.sh "$hg131_results"/*.trx \
|
||||
|| hg131_guard_status=$?
|
||||
if [ "$hg131_test_status" -ne 0 ]; then exit "$hg131_test_status"; fi
|
||||
exit "$hg131_guard_status"
|
||||
```
|
||||
|
||||
Published-package verification must use the real 1.0.0 packages, preserve all test
|
||||
and guard exits, run the full owning project, and complete the consumer solution
|
||||
build/review before issue 131 is closed. The patch is transport-only and can be
|
||||
applied independently of the OPC UA SDK 2 rollout.
|
||||
|
||||
For isolated validation, use a fresh output directory OUTSIDE both repositories
|
||||
so the archived HG projects do not inherit the consumer's central package policy:
|
||||
|
||||
```bash
|
||||
python3 scripts/validation/historiangw-131/validate.py \
|
||||
--consumer-root "$PWD" \
|
||||
--hg-root /Users/dohertj2/Desktop/HistorianGateway \
|
||||
--output /tmp/historiangw-131-fresh-validation
|
||||
```
|
||||
|
||||
The script checks both live feed indexes. If both actual stable 1.0.0 packages are
|
||||
available, it validates them in an archived consumer copy. Otherwise it archives
|
||||
exact HG commit e432191 and temporarily replaces ONLY the archived Gateway csproj's
|
||||
two package references with source project references. This equivalent source
|
||||
validation does not create or publish any fake package/version. Restore uses an
|
||||
independent copy-on-write/copy cache, isolated HTTP cache and CLI home. Neither
|
||||
user checkout is built or edited. Full owning-suite TRX, required TLS failure
|
||||
classification, command exits and execution guard are retained under the task
|
||||
output. Published mode additionally checks package assets/provenance, absence of
|
||||
source overrides, and exact package-to-test-bin DLL hashes. Never treat source
|
||||
validation as stable published-package verification.
|
||||
@@ -0,0 +1,98 @@
|
||||
# HG issue 131 — actual published 1.0.0 verification
|
||||
|
||||
Both real stable **Client 1.0.0** and **Contracts 1.0.0** were observed in the live
|
||||
Gitea indexes at 2026-11-10 19:35:54 UTC and restored into a fresh isolated cache.
|
||||
The future consumer patch was applied only to a separate SDK 2 consumer archive.
|
||||
Application files in this working branch remain on 0.4.0, unchanged.
|
||||
|
||||
- Full Gateway owning project: **529 total, 523 passed, 0 failed, 6 existing live skips**.
|
||||
- Restore, test and repository execution-count guard exits: **0 / 0 / 0**.
|
||||
- All **3 real TLS/h2c wire cases passed** against the published packages.
|
||||
- Strict default rejected with AuthenticationException **UntrustedRoot**, before RPC.
|
||||
- Explicit opt-out and plaintext/stale-TLS-settings cases each completed one RPC.
|
||||
- Both assets entries are **type=package, version=1.0.0**, from the actual Gitea
|
||||
source. No source ProjectReference override: the archived Gateway csproj is
|
||||
byte-identical to the normal consumer csproj.
|
||||
- The only package folder is the task-isolated cache. Both copied test-bin DLL
|
||||
hashes equal the package DLL hashes. No package was packed or published here.
|
||||
|
||||
Actual package RepositoryCommit for BOTH packages:
|
||||
`ea9f21c850fb618767e12690924f9966f1e433ac`.
|
||||
Consumer archive base: `4ea379fa92e571492ff9f49889a5eb91e3ad1e1a`.
|
||||
Applied patch SHA-256: `9452ee82b2bace7828a384b9f6048e9c931e8b4ec2078e0ee1baa175d0652fac`.
|
||||
|
||||
| Package | NuGet package SHA-256 | Consumed DLL SHA-256 |
|
||||
|---|---|---|
|
||||
| ZB.MOM.WW.HistorianGateway.Client 1.0.0 | `0a3f2836727671c07c0c393e705cc9737d2ed44258213a51fb4acf0d0ab7129e` | `b89a02e0d228ec549c4dfb755694f7f67b573a6077f06ff2781165e884fc5e12` |
|
||||
| ZB.MOM.WW.HistorianGateway.Contracts 1.0.0 | `83ffd12bcbbf919c973ba522560e10aa93a4cb8c686a3232030a2cfa5f00995e` | `e60d3d4650be5d251982f7847404daf1b666f61d56e2b0ccb844af8677e97743` |
|
||||
|
||||
Proof metadata/source/hash/readback checks:
|
||||
`/tmp/historiangw-131-published-20261110T193638Z/published-package-proof.json`.
|
||||
Full run metadata (including typed TLS error and package readback):
|
||||
`/tmp/historiangw-131-published-20261110T193638Z/metadata.json`.
|
||||
Fresh actual-package TRX: `/tmp/historiangw-131-published-20261110T193638Z/test-results`.
|
||||
Restore/test/guard logs: `/tmp/historiangw-131-published-20261110T193638Z`.
|
||||
|
||||
This completes the isolated SDK 2 owning-project proof. The coordinator separately
|
||||
owns the consumer-master full solution build, Astra review, landing/push and issue
|
||||
131 comment/closure. No issue action, package publication, or HG user-checkout
|
||||
mutation was performed by this validation lane. The live AVEVA fixture skips do
|
||||
not substitute for the three executed local wire tests.
|
||||
|
||||
---
|
||||
|
||||
# Earlier archived-source validation
|
||||
|
||||
Future consumer patch validated against exact archived HG source commit
|
||||
`e4321915ebc5e0704a68ecad21b85c1cc1c80d6b`. Consumer archive base:
|
||||
`18c22cf7eb042126226e540d9b979907328055c3`. Patch SHA-256:
|
||||
`9452ee82b2bace7828a384b9f6048e9c931e8b4ec2078e0ee1baa175d0652fac`.
|
||||
|
||||
## Scope and isolation
|
||||
|
||||
Mode: **unreleased source project references**, not published-package verification.
|
||||
At that earlier source-validation time, both feed indexes still listed only
|
||||
0.4.0, 0.3.0, 0.2.0, 0.1.0. The published-package run below supersedes that pending
|
||||
release-availability status.
|
||||
|
||||
The user's HG checkout was only read by git archive. Only the disposable archived
|
||||
consumer Gateway csproj had its two package references replaced by project
|
||||
references into the exact archived HG source. No package was packed or published;
|
||||
no fake 1.0.0 was inserted into any feed/cache. Restore used an independent copied
|
||||
NuGet cache, isolated HTTP cache and CLI home. The normal consumer files remain
|
||||
on published 0.4.0.
|
||||
|
||||
## Actual execution
|
||||
|
||||
- Full Gateway owning project: **529 total, 523 passed, 0 failed, 6 existing live skips**.
|
||||
- Test exit **0**; repository nonzero-execution guard exit **0**.
|
||||
- All three new local wire tests passed; no new skip or expected application-result change.
|
||||
- Strict default: base HistorianGatewayException -> RpcException Status Internal ->
|
||||
HttpRequestException -> AuthenticationException, certificate-chain **UntrustedRoot**.
|
||||
The test explicitly requires the typed TLS authentication failure; RPC calls = **0**.
|
||||
- Explicit TLS opt-out: actual metadata RPC succeeds; calls = **1**.
|
||||
- Plaintext with stale opt-out and nonexistent CA path: actual metadata RPC succeeds;
|
||||
calls = **1**.
|
||||
|
||||
The first source run compiled and executed but failed one new assertion because
|
||||
it predicted the narrower HistorianGatewayUnavailableException type. The actual
|
||||
TLS exception is the public base HistorianGatewayException; the corrected assertion
|
||||
retains the required AuthenticationException graph and zero-RPC check. The fresh
|
||||
second unfiltered run above passed. The six skipped cases are the existing AVEVA
|
||||
live fixture tests; they are not a substitute for TLS wire coverage.
|
||||
|
||||
Completed UTC: `2026-11-10T19:33:17.272468+00:00`.
|
||||
Actual metadata, typed TLS exception output and binary hashes:
|
||||
`/tmp/historiangw-131-validation-20261110T193106Z/metadata-final.json`.
|
||||
Fresh second-run TRX: `/tmp/historiangw-131-validation-20261110T193106Z/test-results2`.
|
||||
Test log: `/tmp/historiangw-131-validation-20261110T193106Z/test2.log`.
|
||||
Initial failed run and restore logs are retained in the same task directory.
|
||||
|
||||
Archived-source Client DLL SHA-256:
|
||||
`87e995a43c63debdd8f648a8feb8d19d9110b45eb7b2b6906159077d90109862`.
|
||||
Archived-source Contracts DLL SHA-256:
|
||||
`ebaaa43ffc485288054eb864a59ec59264d8d9c4ea6871319fd7995192370751`.
|
||||
|
||||
That earlier run consumed source only. Actual stable-package proof is recorded
|
||||
above. Consumer-master solution build/review/landing and issue handling are owned
|
||||
by the coordinator; no issue comment/close was performed here.
|
||||
@@ -0,0 +1,226 @@
|
||||
diff --git a/Directory.Packages.props b/Directory.Packages.props
|
||||
--- a/Directory.Packages.props
|
||||
+++ b/Directory.Packages.props
|
||||
@@ -242,7 +242,7 @@
|
||||
<PackageVersion Include="ZB.MOM.WW.Secrets.Replicator.AkkaDotNet" Version="0.6.2" />
|
||||
<PackageVersion Include="ZB.MOM.WW.Audit" Version="0.1.0" />
|
||||
<PackageVersion Include="ZB.MOM.WW.Theme" Version="0.5.0" />
|
||||
- <PackageVersion Include="ZB.MOM.WW.HistorianGateway.Client" Version="0.4.0" />
|
||||
- <PackageVersion Include="ZB.MOM.WW.HistorianGateway.Contracts" Version="0.4.0" />
|
||||
+ <PackageVersion Include="ZB.MOM.WW.HistorianGateway.Client" Version="1.0.0" />
|
||||
+ <PackageVersion Include="ZB.MOM.WW.HistorianGateway.Contracts" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
\ No newline at end of file
|
||||
diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/HistorianGatewayClientAdapter.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/HistorianGatewayClientAdapter.cs
|
||||
--- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/HistorianGatewayClientAdapter.cs
|
||||
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/HistorianGatewayClientAdapter.cs
|
||||
@@ -57,23 +57,15 @@
|
||||
$"ServerHistorian:Endpoint must be an absolute http(s) URI (e.g. https://host:5222); got '{options.Endpoint}'.");
|
||||
}
|
||||
|
||||
- // TLS-only options must stay at their defaults on a plaintext h2c connection: the client rejects
|
||||
- // each of them outright when UseTls=false ("<name> is a TLS-only option and requires
|
||||
- // UseTls=true"). Forwarding them unconditionally made h2c UNREACHABLE — AllowUntrustedServer-
|
||||
- // Certificate defaults to false, so RequireCertificateValidation was always sent as true and
|
||||
- // every http:// deployment crashed at startup, even though the scheme is documented as the
|
||||
- // supported way to select h2c. There is no certificate to have a posture about here.
|
||||
+ // Certificate settings apply only to TLS. The client rejects an explicit opt-out or a
|
||||
+ // CA path on h2c, where there is no certificate to validate.
|
||||
var clientOptions = new HistorianGatewayClientOptions
|
||||
{
|
||||
Endpoint = endpointUri,
|
||||
ApiKey = options.ApiKey,
|
||||
UseTls = options.UseTls,
|
||||
CaCertificatePath = options.UseTls ? options.CaCertificatePath : null,
|
||||
- // INVERTED mapping (TLS only): ServerHistorianOptions.AllowUntrustedServerCertificate
|
||||
- // (opt-in to accept a self-signed cert) is the negation of the client's
|
||||
- // RequireCertificateValidation. Allowing an untrusted cert == not requiring validation; a
|
||||
- // pinned CaCertificatePath always verifies.
|
||||
- RequireCertificateValidation = options.UseTls && !options.AllowUntrustedServerCertificate,
|
||||
+ AllowUntrustedServerCertificate = options.UseTls && options.AllowUntrustedServerCertificate,
|
||||
DefaultCallTimeout = options.CallTimeout,
|
||||
LoggerFactory = loggerFactory,
|
||||
};
|
||||
diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/HistorianGatewayClientAdapterTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/HistorianGatewayClientAdapterTests.cs
|
||||
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/HistorianGatewayClientAdapterTests.cs
|
||||
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/HistorianGatewayClientAdapterTests.cs
|
||||
@@ -27,23 +27,6 @@
|
||||
/// A plaintext <c>h2c</c> gateway (an <c>http://</c> endpoint with <c>UseTls=false</c>) must
|
||||
/// construct, using nothing but documented defaults.
|
||||
/// </summary>
|
||||
- /// <remarks>
|
||||
- /// <para>
|
||||
- /// Found by the LocalDb Phase 2 live gate, on the third consecutive crash-loop of the rig.
|
||||
- /// <c>docs/Historian.md</c> and CLAUDE.md both document <c>http://</c> as a supported
|
||||
- /// transport ("scheme selects transport: https = TLS, http = h2c"), but it was in fact
|
||||
- /// <b>unreachable</b>: this factory forwarded the TLS-only options unconditionally, and
|
||||
- /// <see cref="ServerHistorianOptions.AllowUntrustedServerCertificate"/> defaults to
|
||||
- /// <see langword="false"/>, so it always sent <c>RequireCertificateValidation=true</c> —
|
||||
- /// which the client rejects outright when <c>UseTls=false</c>
|
||||
- /// ("RequireCertificateValidation is a TLS-only option and requires UseTls=true").
|
||||
- /// </para>
|
||||
- /// <para>
|
||||
- /// So <b>every</b> h2c deployment crashed at startup, and the only way to avoid it was to
|
||||
- /// set <c>AllowUntrustedServerCertificate=true</c> — asserting a certificate posture for a
|
||||
- /// connection that has no certificate at all.
|
||||
- /// </para>
|
||||
- /// </remarks>
|
||||
[Fact]
|
||||
public void Adapter_constructs_for_plaintext_h2c_endpoint()
|
||||
{
|
||||
@@ -71,6 +54,7 @@
|
||||
ApiKey = "histgw_x_y",
|
||||
UseTls = false,
|
||||
CaCertificatePath = "/etc/ssl/certs/leftover-from-a-tls-config.pem",
|
||||
+ AllowUntrustedServerCertificate = true,
|
||||
};
|
||||
|
||||
using var adapter = HistorianGatewayClientAdapter.Create(opts, NullLoggerFactory.Instance);
|
||||
diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/HistorianGatewayTlsPolicyWireTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/HistorianGatewayTlsPolicyWireTests.cs
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/HistorianGatewayTlsPolicyWireTests.cs
|
||||
@@ -0,0 +1,142 @@
|
||||
+using System.Net;
|
||||
+using System.Security.Authentication;
|
||||
+using System.Security.Cryptography;
|
||||
+using System.Security.Cryptography.X509Certificates;
|
||||
+using Grpc.Core;
|
||||
+using Microsoft.AspNetCore.Builder;
|
||||
+using Microsoft.AspNetCore.Hosting;
|
||||
+using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
+using Microsoft.Extensions.DependencyInjection;
|
||||
+using Microsoft.Extensions.Logging;
|
||||
+using Microsoft.Extensions.Logging.Abstractions;
|
||||
+using Shouldly;
|
||||
+using Xunit;
|
||||
+using ZB.MOM.WW.HistorianGateway.Client;
|
||||
+using ZB.MOM.WW.HistorianGateway.Contracts.Grpc;
|
||||
+using ZB.MOM.WW.OtOpcUa.Runtime.Historian;
|
||||
+
|
||||
+namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests;
|
||||
+
|
||||
+/// <summary>historiangw#131: verify the mapped TLS posture through an actual local gRPC connection.</summary>
|
||||
+public sealed class HistorianGatewayTlsPolicyWireTests(ITestOutputHelper output)
|
||||
+{
|
||||
+ private static CancellationToken Ct => TestContext.Current.CancellationToken;
|
||||
+
|
||||
+ [Fact]
|
||||
+ public async Task Default_tls_rejects_an_untrusted_server_before_the_rpc()
|
||||
+ {
|
||||
+ await using var server = await Server.StartAsync(tls: true);
|
||||
+ using var adapter = HistorianGatewayClientAdapter.Create(server.Options(), NullLoggerFactory.Instance);
|
||||
+
|
||||
+ var error = await Should.ThrowAsync<HistorianGatewayException>(
|
||||
+ () => adapter.TagExistsAsync("T", Ct).WaitAsync(TimeSpan.FromSeconds(15), Ct));
|
||||
+ output.WriteLine($"TLS rejection: {error}");
|
||||
+ ContainsTlsAuthenticationFailure(error).ShouldBeTrue("the failure must be TLS authentication, not an arbitrary connection failure");
|
||||
+ server.MetadataCalls.ShouldBe(0);
|
||||
+ }
|
||||
+
|
||||
+ [Fact]
|
||||
+ public async Task Explicit_tls_opt_out_reaches_the_same_self_signed_server_shape()
|
||||
+ {
|
||||
+ await using var server = await Server.StartAsync(tls: true);
|
||||
+ var options = server.Options(allowUntrusted: true);
|
||||
+ using var adapter = HistorianGatewayClientAdapter.Create(options, NullLoggerFactory.Instance);
|
||||
+
|
||||
+ (await adapter.TagExistsAsync("T", Ct).WaitAsync(TimeSpan.FromSeconds(15), Ct)).ShouldBeTrue();
|
||||
+ server.MetadataCalls.ShouldBe(1);
|
||||
+ }
|
||||
+
|
||||
+ [Fact]
|
||||
+ public async Task Plaintext_ignores_stale_tls_opt_out_and_ca_settings_and_reaches_the_rpc()
|
||||
+ {
|
||||
+ await using var server = await Server.StartAsync(tls: false);
|
||||
+ var options = server.Options(allowUntrusted: true, caPath: "/nonexistent/historiangw-131-stale-ca.pem");
|
||||
+ using var adapter = HistorianGatewayClientAdapter.Create(options, NullLoggerFactory.Instance);
|
||||
+
|
||||
+ (await adapter.TagExistsAsync("T", Ct).WaitAsync(TimeSpan.FromSeconds(15), Ct)).ShouldBeTrue();
|
||||
+ server.MetadataCalls.ShouldBe(1);
|
||||
+ }
|
||||
+
|
||||
+ private static bool ContainsTlsAuthenticationFailure(Exception error)
|
||||
+ {
|
||||
+ var pending = new Stack<Exception>();
|
||||
+ var seen = new HashSet<Exception>(ReferenceEqualityComparer.Instance);
|
||||
+ pending.Push(error);
|
||||
+ while (pending.TryPop(out var current))
|
||||
+ {
|
||||
+ if (!seen.Add(current)) continue;
|
||||
+ if (current is AuthenticationException) return true;
|
||||
+ if (current.InnerException is { } inner) pending.Push(inner);
|
||||
+ if (current is RpcException { Status.DebugException: { } debug }) pending.Push(debug);
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ private sealed class TagsService : HistorianTags.HistorianTagsBase
|
||||
+ {
|
||||
+ public int Calls;
|
||||
+ public override Task<GetTagMetadataReply> GetTagMetadata(GetTagMetadataRequest request, ServerCallContext context)
|
||||
+ {
|
||||
+ Interlocked.Increment(ref Calls);
|
||||
+ return Task.FromResult(new GetTagMetadataReply
|
||||
+ { Metadata = new HistorianTagMetadata { Name = request.Tag } });
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private sealed class Server(WebApplication app, TagsService service, string endpoint,
|
||||
+ RSA? key, X509Certificate2? certificate) : IAsyncDisposable
|
||||
+ {
|
||||
+ public int MetadataCalls => Volatile.Read(ref service.Calls);
|
||||
+ public ServerHistorianOptions Options(bool allowUntrusted = false, string? caPath = null) => new()
|
||||
+ {
|
||||
+ Endpoint = endpoint, ApiKey = "histgw_131_test", UseTls = certificate is not null,
|
||||
+ CallTimeout = TimeSpan.FromSeconds(2),
|
||||
+ AllowUntrustedServerCertificate = allowUntrusted, CaCertificatePath = caPath,
|
||||
+ };
|
||||
+
|
||||
+ public static async Task<Server> StartAsync(bool tls)
|
||||
+ {
|
||||
+ RSA? key = null;
|
||||
+ X509Certificate2? certificate = null;
|
||||
+ if (tls)
|
||||
+ {
|
||||
+ key = RSA.Create(2048);
|
||||
+ var request = new CertificateRequest("CN=localhost", key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
+ request.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, true));
|
||||
+ request.CertificateExtensions.Add(new X509KeyUsageExtension(
|
||||
+ X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment, true));
|
||||
+ var names = new SubjectAlternativeNameBuilder();
|
||||
+ names.AddDnsName("localhost"); names.AddIpAddress(IPAddress.Loopback);
|
||||
+ request.CertificateExtensions.Add(names.Build());
|
||||
+ certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow.AddMinutes(-5), DateTimeOffset.UtcNow.AddDays(1));
|
||||
+ }
|
||||
+ var service = new TagsService();
|
||||
+ var builder = WebApplication.CreateBuilder();
|
||||
+ builder.Logging.ClearProviders();
|
||||
+ builder.WebHost.ConfigureKestrel(options => options.Listen(IPAddress.Loopback, 0, listen =>
|
||||
+ {
|
||||
+ listen.Protocols = HttpProtocols.Http2;
|
||||
+ if (certificate is not null) listen.UseHttps(certificate);
|
||||
+ }));
|
||||
+ builder.Services.AddGrpc(); builder.Services.AddSingleton(service);
|
||||
+ var app = builder.Build();
|
||||
+ app.MapGrpcService<TagsService>();
|
||||
+ try
|
||||
+ {
|
||||
+ await app.StartAsync(Ct);
|
||||
+ return new Server(app, service, app.Urls.Single(), key, certificate);
|
||||
+ }
|
||||
+ catch
|
||||
+ {
|
||||
+ await app.DisposeAsync(); certificate?.Dispose(); key?.Dispose();
|
||||
+ throw;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public async ValueTask DisposeAsync()
|
||||
+ {
|
||||
+ try { await app.StopAsync(Ct); }
|
||||
+ finally { await app.DisposeAsync(); certificate?.Dispose(); key?.Dispose(); }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate the future patch in archives, using published 1.0.0 or exact HG source project references."""
|
||||
import argparse, datetime, hashlib, io, json, os, shutil, subprocess, tarfile, urllib.request
|
||||
import xml.etree.ElementTree as ET
|
||||
from pathlib import Path
|
||||
|
||||
def verify_published_packages(root, consumer, cache, project):
|
||||
relative = Path('src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.csproj')
|
||||
assert (consumer/relative).read_bytes() == (root/relative).read_bytes(), 'Unexpected source-project override'
|
||||
assets = json.loads((consumer/project.parent/'obj/project.assets.json').read_text())
|
||||
assert all(Path(p).resolve() == cache.resolve() for p in assets['packageFolders'])
|
||||
proof = {'no_source_project_reference_override': True, 'packages': {}}
|
||||
for name in ['Client', 'Contracts']:
|
||||
ident = 'ZB.MOM.WW.HistorianGateway.' + name
|
||||
library = assets['libraries'][ident + '/1.0.0']
|
||||
assert library['type'] == 'package'
|
||||
package = cache/ident.lower()/'1.0.0'
|
||||
provenance = json.loads((package/'.nupkg.metadata').read_text())
|
||||
assert provenance['source'].startswith('https://gitea.dohertylan.com/api/packages/dohertj2/nuget')
|
||||
dll = package/'lib/net10.0'/(ident + '.dll')
|
||||
copied = consumer/project.parent/'bin/Release/net10.0'/(ident + '.dll')
|
||||
digest = hashlib.sha256(dll.read_bytes()).hexdigest()
|
||||
assert digest == hashlib.sha256(copied.read_bytes()).hexdigest()
|
||||
nuspec = ET.parse(package/(ident.lower() + '.nuspec'))
|
||||
assert nuspec.findtext('.//{*}version') == '1.0.0'
|
||||
repository = nuspec.find('.//{*}repository')
|
||||
proof['packages'][ident] = {'version': '1.0.0', 'asset_type': 'package',
|
||||
'source': provenance['source'], 'dll_sha256': digest,
|
||||
'nupkg_sha256': hashlib.sha256((package/(ident.lower() + '.1.0.0.nupkg')).read_bytes()).hexdigest(),
|
||||
'repository': repository.attrib if repository is not None else None,
|
||||
'test_bin_dll_matches_package': True}
|
||||
return proof
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--consumer-root', type=Path, required=True)
|
||||
parser.add_argument('--hg-root', type=Path, required=True)
|
||||
parser.add_argument('--hg-commit', default='e4321915ebc5e0704a68ecad21b85c1cc1c80d6b')
|
||||
parser.add_argument('--output', type=Path, required=True)
|
||||
a = parser.parse_args(); root = a.consumer_root.resolve(); out = a.output.resolve()
|
||||
if out.is_relative_to(root) or out.is_relative_to(a.hg_root.resolve()):
|
||||
raise ValueError('Use an output directory outside both checkouts so archived HG does not inherit consumer central-package settings.')
|
||||
out.mkdir(parents=True, exist_ok=False)
|
||||
patch = Path(__file__).with_name('consumer-1.0.0.patch').resolve()
|
||||
project = Path('tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests.csproj')
|
||||
sha = subprocess.check_output(['git','-C',str(root),'rev-parse','HEAD'],text=True).strip()
|
||||
hg_sha = subprocess.check_output(['git','-C',str(a.hg_root),'rev-parse',a.hg_commit],text=True).strip()
|
||||
def archive(repo, rev, target):
|
||||
target.mkdir()
|
||||
data = subprocess.check_output(['git','-C',str(repo),'archive',rev])
|
||||
with tarfile.open(fileobj=io.BytesIO(data)) as tar: tar.extractall(target, filter='data')
|
||||
|
||||
versions = {}
|
||||
for name in ['client','contracts']:
|
||||
url = 'https://gitea.dohertylan.com/api/packages/dohertj2/nuget/package/zb.mom.ww.historiangateway.' + name + '/index.json'
|
||||
versions[name] = json.load(urllib.request.urlopen(url,timeout=20))['versions']
|
||||
published = all('1.0.0' in v for v in versions.values())
|
||||
metadata = {'utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'consumer_commit':sha,'hg_snapshot':hg_sha,
|
||||
'mode':'published-1.0.0' if published else 'unreleased-source-project-reference',
|
||||
'feed_versions':versions,'patch_sha256':hashlib.sha256(patch.read_bytes()).hexdigest(),
|
||||
'published_package_verification':published,'normal_package_cache_modified':False}
|
||||
(out/'metadata.json').write_text(json.dumps(metadata,indent=2))
|
||||
consumer = out/'consumer'; archive(root,sha,consumer)
|
||||
subprocess.run(['git','init','-q',str(consumer)],check=True)
|
||||
check = subprocess.run(['git','-C',str(consumer),'apply','--check',str(patch)],capture_output=True,text=True)
|
||||
if check.returncode == 0:
|
||||
subprocess.run(['git','-C',str(consumer),'apply',str(patch)],check=True)
|
||||
metadata['consumer_patch_state'] = 'applied-to-archive'
|
||||
else:
|
||||
reverse = subprocess.run(['git','-C',str(consumer),'apply','--reverse','--check',str(patch)],capture_output=True,text=True)
|
||||
if reverse.returncode:
|
||||
raise RuntimeError('Consumer neither accepts the migration patch nor contains it completely: ' + check.stderr)
|
||||
metadata['consumer_patch_state'] = 'already-present-in-consumer'
|
||||
|
||||
if not published:
|
||||
hg = out/'historiangw-source'; archive(a.hg_root,hg_sha,hg)
|
||||
adapter = consumer/'src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.csproj'
|
||||
tree = ET.parse(adapter)
|
||||
removed = 0
|
||||
for group in tree.getroot().findall('ItemGroup'):
|
||||
for item in list(group):
|
||||
if item.tag=='PackageReference' and item.get('Include') in ['ZB.MOM.WW.HistorianGateway.Client','ZB.MOM.WW.HistorianGateway.Contracts']:
|
||||
group.remove(item);removed += 1
|
||||
assert removed==2
|
||||
group = ET.SubElement(tree.getroot(),'ItemGroup')
|
||||
for relative in ['clients/dotnet/ZB.MOM.WW.HistorianGateway.Client/ZB.MOM.WW.HistorianGateway.Client.csproj',
|
||||
'src/ZB.MOM.WW.HistorianGateway.Contracts/ZB.MOM.WW.HistorianGateway.Contracts.csproj']:
|
||||
ET.SubElement(group,'ProjectReference',Include=str(hg/relative))
|
||||
tree.write(adapter,encoding='unicode')
|
||||
metadata['temporary_override']='Only archived Gateway csproj: two package references replaced by exact archived HG project references; no package packed or published.'
|
||||
# Copy cached dependencies independently, preferably APFS copy-on-write, into the isolated package root.
|
||||
cache = out/'packages'; cache.mkdir()
|
||||
assets = json.loads((root/project.parent/'obj/project.assets.json').read_text())
|
||||
normal = Path(next(iter(assets['packageFolders'])))
|
||||
for library in assets['libraries'].values():
|
||||
if library.get('type')!='package':continue
|
||||
relative = library['path']; source=normal/relative; target=cache/relative
|
||||
target.parent.mkdir(parents=True,exist_ok=True)
|
||||
if target.exists():continue
|
||||
r=subprocess.run(['cp','-cR',str(source),str(target)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
|
||||
if r.returncode: shutil.copytree(source,target)
|
||||
config = ET.parse(root/'NuGet.config')
|
||||
for source in config.getroot().find('packageSources'):
|
||||
if source.get('key')=='local-mxgw':
|
||||
local = root/'nuget-packages'
|
||||
if not local.exists():local=out/'empty-local';local.mkdir()
|
||||
source.set('value',str(local))
|
||||
config.write(out/'NuGet.config',encoding='unicode')
|
||||
env=dict(os.environ,NUGET_PACKAGES=str(cache),NUGET_HTTP_CACHE_PATH=str(out/'http-cache'),DOTNET_CLI_HOME=str(out/'dotnet-home'),
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE='1',DOTNET_GENERATE_ASPNET_CERTIFICATE='false',DOTNET_CLI_TELEMETRY_OPTOUT='1')
|
||||
commands=[]
|
||||
def run(args,name):
|
||||
with (out/name).open('w') as log:r=subprocess.run(args,cwd=consumer,env=env,stdout=log,stderr=subprocess.STDOUT)
|
||||
commands.append({'argv':args,'exit':r.returncode,'log':name})
|
||||
(out/'commands.json').write_text(json.dumps(commands,indent=2));print(name,'exit',r.returncode,flush=True)
|
||||
return r.returncode
|
||||
restore = run(['dotnet','restore',str(project),'--configfile',str(out/'NuGet.config'),'--packages',str(cache)],'restore.log')
|
||||
metadata['restore_exit']=restore
|
||||
if restore:
|
||||
(out/'metadata.json').write_text(json.dumps(metadata,indent=2));raise SystemExit(restore)
|
||||
results=out/'test-results'
|
||||
test = run(['dotnet','test',str(project),'-c','Release','--no-restore','--logger','trx','--results-directory',str(results)],'test.log')
|
||||
guard_env=env.copy();guard_env['MIN_EXECUTED']='1'
|
||||
with (out/'guard.log').open('w') as log:
|
||||
guard=subprocess.run(['bash',str(consumer/'scripts/ci/assert-not-all-skipped.sh'),*[str(p) for p in results.glob('*.trx')]],env=guard_env,stdout=log,stderr=subprocess.STDOUT)
|
||||
metadata.update({'test_exit':test,'guard_exit':guard.returncode})
|
||||
ns={'t':'http://microsoft.com/schemas/VisualStudio/TeamTest/2010'}
|
||||
metadata['results']=[]
|
||||
for trx in results.glob('*.trx'):
|
||||
xml=ET.parse(trx);metadata['results'].append(xml.find('.//t:Counters',ns).attrib)
|
||||
metadata['tls_cases']=[{'name':r.get('testName'),'outcome':r.get('outcome'),'output':'\n'.join(p.text or '' for p in r.findall('.//t:StdOut',ns)),
|
||||
'error':r.findtext('.//t:Message','',ns)} for r in xml.findall('.//t:UnitTestResult',ns) if 'HistorianGatewayTlsPolicyWireTests' in r.get('testName','')]
|
||||
if published:
|
||||
metadata['package_proof'] = verify_published_packages(root, consumer, cache, project)
|
||||
(out/'metadata.json').write_text(json.dumps(metadata,indent=2));print(json.dumps(metadata,indent=2),flush=True)
|
||||
raise SystemExit(test if test else guard.returncode)
|
||||
@@ -0,0 +1 @@
|
||||
__pycache__/
|
||||
@@ -0,0 +1,249 @@
|
||||
#pragma warning disable OTOPCUA0001
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Configuration;
|
||||
using ZB.MOM.WW.OtOpcUa.Client.Shared;
|
||||
using ZB.MOM.WW.OtOpcUa.Client.Shared.Models;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser;
|
||||
using ZB.MOM.WW.OtOpcUa.OpcUaServer;
|
||||
using WriteRequest = ZB.MOM.WW.OtOpcUa.Core.Abstractions.WriteRequest;
|
||||
using SharedSecurityMode = ZB.MOM.WW.OtOpcUa.Client.Shared.Models.SecurityMode;
|
||||
|
||||
internal static partial class ProcessHost
|
||||
{
|
||||
static object ServerCounts(OtOpcUaSdkServer server)
|
||||
{
|
||||
var sessions = server.CurrentInstance.SessionManager.GetSessions();
|
||||
var subs = server.CurrentInstance.SubscriptionManager.GetSubscriptions();
|
||||
return new { utc = DateTime.UtcNow, sessions = sessions.Count, subscriptions = subs.Count,
|
||||
items = subs.Sum(s => s.MonitoredItemCount), serverProcess = Environment.ProcessId };
|
||||
}
|
||||
}
|
||||
|
||||
internal static class Lifecycle
|
||||
{
|
||||
static string Root = "";
|
||||
static string Password => Environment.GetEnvironmentVariable("OPCUA_MATRIX_PASSWORD") ?? throw new InvalidOperationException("Missing fixture credential");
|
||||
static readonly JsonSerializerOptions Json = new() { WriteIndented = true };
|
||||
static int managerChecks, sharedPasses, driverPasses, browserPasses;
|
||||
static readonly List<WeakReference> Weak = [];
|
||||
static void Require(bool pass, string message) { if (!pass) throw new InvalidOperationException(message); }
|
||||
static object? FieldOrNull(object owner, string name)
|
||||
{
|
||||
var field = owner.GetType().GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)
|
||||
?? throw new InvalidOperationException("Diagnostic layout changed: " + owner.GetType().FullName + "." + name);
|
||||
return field.GetValue(owner);
|
||||
}
|
||||
static object Field(object owner, string name) => FieldOrNull(owner, name) ?? throw new InvalidOperationException("Diagnostic owner null: " + name);
|
||||
static void Empty(object owner, string name) => Require(Field(owner, name) is ICollection items && items.Count == 0, "Retained collection: " + name);
|
||||
static bool Disposed(object manager)
|
||||
{
|
||||
Require(manager.GetType().FullName == "Opc.Ua.CertificateManager", "Unexpected SDK certificate manager type");
|
||||
Require(manager.GetType().Assembly.GetName().Version?.ToString() == "2.0.1.0", "Diagnostic requires SDK 2.0.1.0");
|
||||
var field = manager.GetType().GetField("m_disposed", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
Require(field?.FieldType == typeof(bool), "SDK 2.0.1 CertificateManager.m_disposed bool layout changed");
|
||||
return (bool)field!.GetValue(manager)!;
|
||||
}
|
||||
static object SharedManager(OpcUaClientService shared) => Field(Field(shared, "_session"), "_certificateManager");
|
||||
static object AppManager(object owner) => ((ApplicationInstance)Field(owner, "_ownedApplication")).ApplicationConfiguration!.CertificateManager;
|
||||
static void CheckManager(object manager, bool disposed)
|
||||
{
|
||||
Require(Disposed(manager) == disposed, "Certificate manager disposal observation mismatch");
|
||||
if (disposed) managerChecks++;
|
||||
}
|
||||
static ConnectionSettings Settings(string endpoint, string? password = null) => new()
|
||||
{
|
||||
EndpointUrl = endpoint, Username = "apikey", Password = password ?? Password,
|
||||
SecurityMode = SharedSecurityMode.SignAndEncrypt, AutoAcceptCertificates = false,
|
||||
CertificateStorePath = Path.Combine(Root, "data", "shared-pki"), SessionTimeoutSeconds = 30
|
||||
};
|
||||
static OpcUaClientDriverOptions Options(string endpoint) => new()
|
||||
{
|
||||
EndpointUrl = endpoint, SecurityPolicy = OpcUaSecurityPolicy.Basic256Sha256,
|
||||
SecurityMode = OpcUaSecurityMode.SignAndEncrypt, AuthType = OpcUaAuthType.Username,
|
||||
Username = "apikey", Password = Password, AutoAcceptCertificates = false,
|
||||
SessionName = "ClientLifecycle", ApplicationUri = "urn:localhost:ClientLifecycle",
|
||||
RawTags = [new("scalar", "{\"nodeId\":\"nsu=https://zb.com/otopcua/raw;s=Matrix/dev/Scalar\"}", true),
|
||||
new("vector", "{\"nodeId\":\"nsu=https://zb.com/otopcua/raw;s=Matrix/dev/Vector\"}", true)]
|
||||
};
|
||||
static async Task Wait(Func<bool> condition, CancellationToken ct)
|
||||
{
|
||||
while (!condition()) await Task.Delay(25, ct);
|
||||
}
|
||||
static object? Clr(DataValue value) => ZB.MOM.WW.OtOpcUa.Client.Shared.Helpers.ValueConverter.ToClrObject(value.WrappedValue);
|
||||
static async Task Prepare()
|
||||
{
|
||||
// Public connection failures create and release the same production applications as a live attempt.
|
||||
// No production private method is invoked and no SDK state is written by diagnostics.
|
||||
using var ct = new CancellationTokenSource(TimeSpan.FromSeconds(15));
|
||||
using (var shared = new OpcUaClientService())
|
||||
{ try { await shared.ConnectAsync(Settings("opc.tcp://127.0.0.1:1"), ct.Token); } catch { } }
|
||||
await using (var driver = new OpcUaClientDriver(Options("opc.tcp://127.0.0.1:1"), "lifecycle"))
|
||||
{ try { await driver.InitializeAsync("{}", ct.Token); } catch { } }
|
||||
var browser = new OpcUaClientDriverBrowser();
|
||||
try { await using var session = await browser.OpenAsync(JsonSerializer.Serialize(Options("opc.tcp://127.0.0.1:1")), ct.Token); } catch { }
|
||||
foreach (var folder in new[] { "data/shared-pki", "data/OtOpcUa/pki", "data/OtOpcUa/adminui-browse-pki" })
|
||||
Require(Directory.GetFiles(Path.Combine(Root, folder, "own", "certs")).Length == 1, "Expected one reusable application DER certificate: " + folder);
|
||||
}
|
||||
static async Task Cycle(string endpoint, int cycle)
|
||||
{
|
||||
using var budget = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||
var ct = budget.Token;
|
||||
using var shared = new OpcUaClientService(); Weak.Add(new(shared));
|
||||
await using var driver = new OpcUaClientDriver(Options(endpoint), "lifecycle"); Weak.Add(new(driver));
|
||||
var browser = new OpcUaClientDriverBrowser(); Weak.Add(new(browser));
|
||||
// Two connections on the same Shared service and two initializations on the same driver.
|
||||
for (var pass = 0; pass < 2; pass++)
|
||||
{
|
||||
var seen = new ConcurrentQueue<int>();
|
||||
EventHandler<DataChangedEventArgs> handler = (_, e) => { if (Clr(e.Value) is int n) seen.Enqueue(n); };
|
||||
shared.DataChanged += handler;
|
||||
var info = await shared.ConnectAsync(Settings(endpoint), ct);
|
||||
Require(info.SecurityMode == "SignAndEncrypt" && info.SecurityPolicyUri == SecurityPolicies.Basic256Sha256, "Shared policy downgrade");
|
||||
object? manager = SharedManager(shared); CheckManager(manager, false);
|
||||
var nss = (string[])Clr(await shared.ReadValueAsync(VariableIds.Server_NamespaceArray, ct))!;
|
||||
var ns = (ushort)Array.IndexOf(nss, "https://zb.com/otopcua/raw"); Require(ns > 0, "Raw namespace missing");
|
||||
var scalar = new NodeId("Matrix/dev/Scalar", ns); var vector = new NodeId("Matrix/dev/Vector", ns);
|
||||
await shared.SubscribeAsync(scalar, 100, ct); await shared.SubscribeAsync(vector, 100, ct);
|
||||
var target = cycle * 10 + pass;
|
||||
Require(StatusCode.IsGood(await shared.WriteValueAsync(scalar, target, ct)), "Shared scalar write failed");
|
||||
Require(StatusCode.IsGood(await shared.WriteValueAsync(vector, new[] { target, 2, 3 }, ct)), "Shared vector write failed");
|
||||
await Wait(() => seen.Contains(target), ct);
|
||||
Require(Equals(Clr(await shared.ReadValueAsync(scalar, ct)), target), "Shared scalar roundtrip mismatch");
|
||||
Require(((int[])Clr(await shared.ReadValueAsync(vector, ct))!).SequenceEqual(new[] { target, 2, 3 }), "Shared vector mismatch");
|
||||
Require((await shared.BrowseAsync(new NodeId("Matrix/dev", ns), ct)).Count > 0, "Shared browse empty");
|
||||
await shared.UnsubscribeAsync(scalar, ct); await shared.UnsubscribeAsync(vector, ct);
|
||||
await shared.DisconnectAsync(ct);
|
||||
Require(!shared.IsConnected && shared.CurrentConnectionInfo is null, "Shared public connection state retained");
|
||||
Require(FieldOrNull(shared, "_session") is null, "Shared session retained"); Empty(shared, "_activeDataSubscriptions");
|
||||
CheckManager(manager, true); manager = null; shared.DataChanged -= handler; sharedPasses++;
|
||||
|
||||
if (pass == 0) await driver.InitializeAsync("{}", ct);
|
||||
else await driver.ReinitializeAsync("{}", ct);
|
||||
Require(driver.GetHealth().State == DriverState.Healthy, "Driver initialize health");
|
||||
manager = AppManager(driver); CheckManager(manager, false);
|
||||
var values = new ConcurrentQueue<int>();
|
||||
EventHandler<DataChangeEventArgs> driverHandler = (_, e) => { if (e.Snapshot.Value is int n) values.Enqueue(n); };
|
||||
driver.OnDataChange += driverHandler;
|
||||
var sub = await driver.SubscribeAsync(["scalar", "vector"], TimeSpan.FromMilliseconds(100), ct);
|
||||
target++;
|
||||
var writes = await driver.WriteAsync([new WriteRequest("scalar", target), new WriteRequest("vector", new[] { target, 4, 5 })], ct);
|
||||
Require(writes.Count == 2 && writes.All(w => w.StatusCode == 0), "Driver write failed");
|
||||
await Wait(() => values.Contains(target), ct);
|
||||
var reads = await driver.ReadAsync(["scalar", "vector"], ct);
|
||||
Require(reads.Count == 2 && reads.All(r => r.StatusCode == 0) && Equals(reads[0].Value, target)
|
||||
&& ((int[])reads[1].Value!).SequenceEqual(new[] { target, 4, 5 }), "Driver roundtrip mismatch");
|
||||
await driver.UnsubscribeAsync(sub, ct); sub = null!;
|
||||
// Reinitialize a live connection with an active replacement subscription, as the watchdog does.
|
||||
sub = await driver.SubscribeAsync(["scalar", "vector"], TimeSpan.FromMilliseconds(100), ct);
|
||||
await driver.ReinitializeAsync("{}", ct); sub = null!;
|
||||
CheckManager(manager, true); manager = null;
|
||||
manager = AppManager(driver); CheckManager(manager, false);
|
||||
Require(driver.GetHealth().State == DriverState.Healthy, "Live reinitialize failed");
|
||||
var reinitialized = await driver.ReadAsync(["scalar"], ct);
|
||||
Require(reinitialized.Count == 1 && reinitialized[0].StatusCode == 0 && Equals(reinitialized[0].Value, target), "Live reinitialize read failed");
|
||||
await driver.ShutdownAsync(ct); driver.OnDataChange -= driverHandler;
|
||||
Require(driver.GetHealth().State == DriverState.Unknown, "Driver shutdown public state retained");
|
||||
Require(FieldOrNull(driver, "<Session>k__BackingField") is null, "Driver session retained");
|
||||
Empty(driver, "_subscriptions"); Empty(driver, "_alarmSubscriptions");
|
||||
CheckManager(manager, true); manager = null; driverPasses++;
|
||||
|
||||
var browse = await browser.OpenAsync(JsonSerializer.Serialize(Options(endpoint)), ct); Weak.Add(new(browse));
|
||||
manager = AppManager(browse); CheckManager(manager, false);
|
||||
Require((await browse.RootAsync(ct)).Count > 0, "Browser root empty");
|
||||
await browse.DisposeAsync().AsTask().WaitAsync(ct); CheckManager(manager, true); manager = null; browserPasses++;
|
||||
}
|
||||
}
|
||||
static object Resources(int cycle, double elapsed)
|
||||
{
|
||||
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
|
||||
var survivors = Weak.Count(w => w.IsAlive); Weak.RemoveAll(w => !w.IsAlive);
|
||||
Require(survivors == 0, "Disposed production client/driver/browser object retained after full GC");
|
||||
var fd = Directory.GetFiles("/proc/self/fd");
|
||||
var owned = 0; var pki = 0;
|
||||
foreach (var path in fd)
|
||||
{
|
||||
var target = new FileInfo(path).LinkTarget;
|
||||
if (target is null || !Path.IsPathFullyQualified(target)) continue;
|
||||
target = Path.GetFullPath(target);
|
||||
if (target.StartsWith(Path.GetFullPath(Root) + Path.DirectorySeparatorChar, StringComparison.Ordinal)) owned++;
|
||||
if (target.StartsWith(Path.Combine(Path.GetFullPath(Root), "data") + Path.DirectorySeparatorChar, StringComparison.Ordinal)) pki++;
|
||||
}
|
||||
using var p = Process.GetCurrentProcess();
|
||||
return new { cycle, elapsedSeconds = elapsed, utc = DateTime.UtcNow, p.WorkingSet64, p.PrivateMemorySize64,
|
||||
managedBytes = GC.GetTotalMemory(false), allocatedBytes = GC.GetTotalAllocatedBytes(),
|
||||
gc = new[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) },
|
||||
fdCount = fd.Length, ownedRootFdCount = owned, ownedPkiFdCount = pki, survivingProductionObjects = survivors,
|
||||
managerDisposalsVerified = managerChecks, sharedPasses, driverPasses, browserPasses };
|
||||
}
|
||||
static async Task Run(string endpoint, int seconds, bool full)
|
||||
{
|
||||
Require(!full || seconds >= 7200, "Full soak requires >=7200 requested seconds");
|
||||
// Deliberate owned-manager positive control proves the exact-tag read-only diagnostic sees both states.
|
||||
var control = CertificateManagerFactory.Create(new SecurityConfiguration(), DefaultTelemetry.Create(_ => { }));
|
||||
Require(!Disposed(control), "Disposal diagnostic control false-state failure");
|
||||
await control.DisposeAsync(); Require(Disposed(control), "Disposal diagnostic control true-state failure");
|
||||
control = null!;
|
||||
var originalCerts = CertificateHashes();
|
||||
var elapsed = Stopwatch.StartNew(); var cycle = 0;
|
||||
File.AppendAllText(Path.Combine(Root, "resources.jsonl"), JsonSerializer.Serialize(Resources(0, 0)) + "\n");
|
||||
while (elapsed.Elapsed.TotalSeconds < seconds)
|
||||
{
|
||||
await Cycle(endpoint, ++cycle).WaitAsync(TimeSpan.FromSeconds(65));
|
||||
// The async cycle has returned, dropping its strong manager/session/item/event references.
|
||||
await Task.Delay(300);
|
||||
var serverPath = Path.Combine(Root, "server", "live-resources.json");
|
||||
var settledOk = false;
|
||||
for (var attempt = 0; attempt < 30; attempt++)
|
||||
{
|
||||
using var settled = JsonDocument.Parse(await File.ReadAllTextAsync(serverPath));
|
||||
settledOk = new[] { "sessions", "subscriptions", "items" }.All(name => settled.RootElement.GetProperty(name).GetInt32() == 0);
|
||||
if (settledOk) break;
|
||||
await Task.Delay(100);
|
||||
}
|
||||
Require(settledOk, "Server sessions/subscriptions/items retained after bounded settling");
|
||||
if (cycle % 10 == 0 || elapsed.Elapsed.TotalSeconds >= seconds)
|
||||
File.AppendAllText(Path.Combine(Root, "resources.jsonl"), JsonSerializer.Serialize(Resources(cycle, elapsed.Elapsed.TotalSeconds)) + "\n");
|
||||
}
|
||||
Require(elapsed.Elapsed.TotalSeconds >= seconds && (!full || elapsed.Elapsed.TotalSeconds >= 7200), "Real elapsed duration gate failed");
|
||||
Require(managerChecks == cycle * 8 && sharedPasses == cycle * 2 && driverPasses == cycle * 2 && browserPasses == cycle * 2, "Incomplete lifecycle accounting");
|
||||
Require(CertificateHashes().SequenceEqual(originalCerts), "Application certificate unexpectedly rotated or PKI changed");
|
||||
var summary = new { status = "passed", diagnosticPositiveControl = "exact SDK2 manager false-before/true-after DisposeAsync", applicationCertificateHashes = originalCerts, requestedSeconds = seconds, elapsedSeconds = elapsed.Elapsed.TotalSeconds,
|
||||
fullTwoHourLifecycle = full && elapsed.Elapsed.TotalSeconds >= 7200, cycles = cycle, sharedPasses, driverPasses, browserPasses,
|
||||
managerDisposalsVerified = managerChecks, finalResources = Resources(cycle, elapsed.Elapsed.TotalSeconds),
|
||||
scope = "Production Shared connect/disconnect/reconnect; production driver Initialize/Reinitialize/Shutdown/dispose; production browser open/browse/dispose; scalar and Int32[3] read/write/subscribe/unsubscribe; strict mutual trusted DER/Basic256Sha256 SignAndEncrypt/API-key fixture. No network stalls, revocation, surgical edits/rebuild, CP abandonment or Windows/AVEVA/performance G4 claim." };
|
||||
File.WriteAllText(Path.Combine(Root, "summary.json"), JsonSerializer.Serialize(summary, Json));
|
||||
}
|
||||
static string[] CertificateHashes() => Directory.GetFiles(Path.Combine(Root, "data"), "*.der", SearchOption.AllDirectories)
|
||||
.Where(p => p.Contains(Path.DirectorySeparatorChar + "own" + Path.DirectorySeparatorChar + "certs" + Path.DirectorySeparatorChar, StringComparison.Ordinal))
|
||||
.Order().Select(p => Path.GetRelativePath(Root, p) + ":" + Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(p))).ToLowerInvariant()).ToArray();
|
||||
public static async Task<int> Main(string[] args)
|
||||
{
|
||||
if (args[0] == "server") return await ProcessHost.HelperMain(args);
|
||||
Root = Path.GetFullPath(args[1]); Directory.CreateDirectory(Root);
|
||||
try
|
||||
{
|
||||
var local = Path.GetFullPath(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData));
|
||||
Require(local == Path.Combine(Root, "data"), "XDG task PKI isolation failed");
|
||||
if (args[0] == "prepare") { await Prepare(); return 0; }
|
||||
Require(args[0] == "run", "Unknown mode");
|
||||
await Run(args[2], int.Parse(args[3]), args[4] == "full");
|
||||
var sdk = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name!.StartsWith("Opc.Ua", StringComparison.Ordinal))
|
||||
.Select(a => new { name = a.GetName().Name, version = a.GetName().Version?.ToString(), sha256 = Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(a.Location))).ToLowerInvariant() });
|
||||
File.WriteAllText(Path.Combine(Root, "sdk-assemblies.json"), JsonSerializer.Serialize(sdk, Json));
|
||||
return 0;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
File.WriteAllText(Path.Combine(Root, "failure.txt"), error.ToString().Replace(Password, "<fixture-secret>"));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup><OutputType>Exe</OutputType><StartupObject>Lifecycle</StartupObject><DefineConstants>SDK2</DefineConstants></PropertyGroup>
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<ProjectReference Include="../../../src/Server/ZB.MOM.WW.OtOpcUa.Host/ZB.MOM.WW.OtOpcUa.Host.csproj" />
|
||||
<ProjectReference Include="../../../src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/ZB.MOM.WW.OtOpcUa.Client.Shared.csproj" />
|
||||
<ProjectReference Include="../../../src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.csproj" />
|
||||
<ProjectReference Include="../../../src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.csproj" />
|
||||
</ItemGroup></Project>
|
||||
@@ -0,0 +1,360 @@
|
||||
// Copy of compatibility ProcessHost at 7b353d7b; lifecycle-owned changes only.
|
||||
#pragma warning disable OTOPCUA0001 // This wire harness deliberately exercises public driver capabilities directly.
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Client;
|
||||
using ZB.MOM.WW.OtOpcUa.Client.Shared;
|
||||
using ZB.MOM.WW.OtOpcUa.Client.Shared.Models;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Authorization;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient;
|
||||
using ZB.MOM.WW.OtOpcUa.OpcUaServer;
|
||||
using ZB.MOM.WW.OtOpcUa.OpcUaServer.Security;
|
||||
using AlarmEventArgs = ZB.MOM.WW.OtOpcUa.Client.Shared.Models.AlarmEventArgs;
|
||||
using WriteRequest = ZB.MOM.WW.OtOpcUa.Core.Abstractions.WriteRequest;
|
||||
using SharedSecurityMode = ZB.MOM.WW.OtOpcUa.Client.Shared.Models.SecurityMode;
|
||||
using CoreHistory = ZB.MOM.WW.OtOpcUa.Core.Abstractions.HistoryReadResult;
|
||||
|
||||
internal static partial class ProcessHost
|
||||
{
|
||||
static readonly DateTime Epoch = new(2026, 10, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
const string RawUri = "https://zb.com/otopcua/raw", UnsUri = "https://zb.com/otopcua/uns";
|
||||
const string Folder = "Matrix/dev", Scalar = Folder + "/Scalar", Vector = Folder + "/Vector", Alarm = Folder + "/Alarm";
|
||||
static readonly CancellationToken Ct = new CancellationTokenSource(TimeSpan.FromSeconds(int.Parse(Environment.GetEnvironmentVariable("OPCUA_LIFECYCLE_SERVER_SECONDS") ?? "240"))).Token;
|
||||
static string Password => Environment.GetEnvironmentVariable("OPCUA_MATRIX_PASSWORD") ?? throw new InvalidOperationException("Missing ephemeral credential");
|
||||
static readonly List<object> Cases = [];
|
||||
static readonly List<object> Observations = [];
|
||||
static int Failures;
|
||||
static string Root = "";
|
||||
static object? Clr(DataValue v)
|
||||
{
|
||||
#if SDK2
|
||||
return ZB.MOM.WW.OtOpcUa.Client.Shared.Helpers.ValueConverter.ToClrObject(v.WrappedValue);
|
||||
#else
|
||||
return v.Value;
|
||||
#endif
|
||||
}
|
||||
static DateTime SourceTime(DataValue value)
|
||||
{
|
||||
#if SDK2
|
||||
return value.SourceTimestamp.ToDateTime();
|
||||
#else
|
||||
return value.SourceTimestamp;
|
||||
#endif
|
||||
}
|
||||
static DateTime ServerTime(DataValue value)
|
||||
{
|
||||
#if SDK2
|
||||
return value.ServerTimestamp.ToDateTime();
|
||||
#else
|
||||
return value.ServerTimestamp;
|
||||
#endif
|
||||
}
|
||||
static void Require(bool yes, string message) { if (!yes) throw new InvalidOperationException(message); }
|
||||
static async Task Case(string name, Func<Task> action)
|
||||
{
|
||||
try { await action(); Cases.Add(new { name, status = "passed" }); }
|
||||
catch (Exception e) { Failures++; Cases.Add(new { name, status = "failed", error = e.ToString() }); }
|
||||
}
|
||||
static async Task Wait(Func<bool> predicate)
|
||||
{
|
||||
for (int i = 0; i < 200; i++) { if (predicate()) return; await Task.Delay(50, Ct); }
|
||||
Require(false, "Notification deadline exceeded");
|
||||
}
|
||||
static ConnectionSettings Settings(string endpoint) => new()
|
||||
{
|
||||
EndpointUrl = endpoint,
|
||||
Username = "apikey",
|
||||
Password = Password,
|
||||
SecurityMode = SharedSecurityMode.SignAndEncrypt,
|
||||
AutoAcceptCertificates = false,
|
||||
CertificateStorePath = Environment.GetEnvironmentVariable("OPCUA_MATRIX_CLIENT_PKI") ?? Path.Combine(Root, "client-pki")
|
||||
};
|
||||
public static async Task<int> HelperMain(string[] args)
|
||||
{
|
||||
Root = Path.GetFullPath(args[1]); Directory.CreateDirectory(Root);
|
||||
try
|
||||
{
|
||||
switch (args[0])
|
||||
{
|
||||
case "local-data-path": Console.WriteLine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)); return 0;
|
||||
case "driver": await Driver(args[2]); break;
|
||||
case "server": await Serve(); return 0;
|
||||
case "client": await Client(args[2]); break;
|
||||
case "performance": await Performance(args[2]); break;
|
||||
default: throw new ArgumentException("Expected prepare/server/client");
|
||||
}
|
||||
}
|
||||
catch (Exception e) { Failures++; Cases.Add(new { name = "process", status = "failed", error = e.ToString() }); }
|
||||
File.WriteAllText(Path.Combine(Root, "result.json"), JsonSerializer.Serialize(new { sdk = typeof(NodeId).Assembly.GetName().Version?.ToString(), process = Environment.ProcessId, cases = Cases, observations = Observations, failures = Failures, loadedSdkAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name!.StartsWith("Opc.Ua", StringComparison.Ordinal)).Select(a => new { name = a.GetName().Name, version = a.GetName().Version?.ToString(), path = a.Location }) }, new JsonSerializerOptions { WriteIndented = true }));
|
||||
return Failures == 0 ? 0 : 1;
|
||||
}
|
||||
static async Task Serve()
|
||||
{
|
||||
var options = new OpcUaApplicationHostOptions
|
||||
{
|
||||
ApplicationName = "OtOpcUa.Matrix",
|
||||
ApplicationUri = "urn:OtOpcUa.Matrix.ClientLifecycle",
|
||||
PublicHostname = "127.0.0.1",
|
||||
PkiStoreRoot = Path.Combine(Root, "server-pki"),
|
||||
AllowAnonymous = false,
|
||||
AutoAcceptUntrustedClientCertificates = false,
|
||||
EnabledSecurityProfiles = Environment.GetEnvironmentVariable("OPCUA_MATRIX_SERVER_SECURITY") == "none"
|
||||
? [OpcUaSecurityProfile.None] : [OpcUaSecurityProfile.Basic256Sha256SignAndEncrypt]
|
||||
};
|
||||
using (var listener = new TcpListener(IPAddress.Loopback, 0)) { listener.Start(); options.OpcUaPort = ((IPEndPoint)listener.LocalEndpoint).Port; }
|
||||
var server = new OtOpcUaSdkServer();
|
||||
await using var host = new OpcUaApplicationHost(options, NullLogger<OpcUaApplicationHost>.Instance, new Auth());
|
||||
await host.StartAsync(server, Ct);
|
||||
var nm = server.NodeManager!; var sink = new SdkAddressSpaceSink(nm);
|
||||
var authority = new DataPlaneAuthorityState();
|
||||
authority.Apply("matrix", "rev1", [new ApiKey { ApiKeyId = "matrix", Name = "matrix", SecretHash = "fixture", Principal = "matrix", Enabled = true, CreatedUtc = DateTime.UtcNow.AddDays(-1) }],
|
||||
[new NodeAcl { NodeAclId = "all", ClusterId = "matrix", Principal = "matrix", ScopeKind = NodeAclScopeKind.Cluster, PermissionFlags = (NodePermissions)int.MaxValue }], new Dictionary<AclScopeRef, IReadOnlyList<NodeAclPath>>());
|
||||
nm.DataPlaneAuthority = authority;
|
||||
nm.Authorizer = new DataPlaneAuthorizer(authority, new TriePermissionEvaluator(authority.Tries), true, NullLogger.Instance, TimeProvider.System);
|
||||
var history = new Source(Root); nm.HistorianDataSource = history;
|
||||
sink.EnsureFolder(Folder, null, "dev", AddressSpaceRealm.Raw);
|
||||
sink.EnsureFolder("Eq", null, "Eq", AddressSpaceRealm.Uns);
|
||||
foreach (var realm in new[] { AddressSpaceRealm.Raw, AddressSpaceRealm.Uns })
|
||||
{
|
||||
var scalar = realm == AddressSpaceRealm.Raw ? Scalar : "Eq/Scalar";
|
||||
var vector = realm == AddressSpaceRealm.Raw ? Vector : "Eq/Vector";
|
||||
var parent = realm == AddressSpaceRealm.Raw ? Folder : "Eq";
|
||||
sink.EnsureVariable(scalar, parent, "Scalar", "Int32", true, realm, "matrix.scalar");
|
||||
sink.EnsureVariable(vector, parent, "Vector", "Int32", true, realm, isArray: true, arrayLength: 3);
|
||||
sink.WriteValue(scalar, 17, 0, Epoch, realm); sink.WriteValue(vector, new[] { 1, 2, 3 }, 0, Epoch, realm);
|
||||
}
|
||||
sink.AddReference("Eq/Scalar", AddressSpaceRealm.Uns, Scalar, AddressSpaceRealm.Raw);
|
||||
sink.MaterialiseAlarmCondition(Alarm, Folder, "Alarm", "OffNormalAlarm", 800, AddressSpaceRealm.Raw, isNative: true);
|
||||
sink.WireAlarmNotifiers(Alarm, AddressSpaceRealm.Raw, ["Eq"], AddressSpaceRealm.Uns);
|
||||
sink.EnsureVariable(Folder + "/UnsetTimestamp", Folder, "UnsetTimestamp", "Int32", false, AddressSpaceRealm.Raw);
|
||||
sink.WriteValue(Folder + "/UnsetTimestamp", 3, 0, DateTime.MinValue, AddressSpaceRealm.Raw);
|
||||
nm.NodeWriteGateway = new Gateway();
|
||||
var state = new AlarmConditionSnapshot(true, false, true, true, AlarmShelvingKind.Unshelved, 800, "matrix-alarm");
|
||||
nm.NativeAlarmAckRouter = _ => Task.Run(() => { state = state with { Acknowledged = true }; sink.WriteAlarmCondition(Alarm, state, DateTime.UtcNow, AddressSpaceRealm.Raw); });
|
||||
File.WriteAllText(Path.Combine(Root, "endpoint.txt"), $"opc.tcp://127.0.0.1:{options.OpcUaPort}/OtOpcUa");
|
||||
File.WriteAllText(Path.Combine(Root, "ready.json"), JsonSerializer.Serialize(new { endpoint = $"opc.tcp://127.0.0.1:{options.OpcUaPort}/OtOpcUa", process = Environment.ProcessId, sdk = typeof(NodeId).Assembly.GetName().Version?.ToString(), rawUri = RawUri, unsUri = UnsUri }));
|
||||
int tick = 0;
|
||||
while (!File.Exists(Path.Combine(Root, "stop")))
|
||||
{
|
||||
sink.WriteAlarmCondition(Alarm, state with { Message = ++tick % 2 == 0 ? "matrix-alarm" : "matrix-tick" }, DateTime.UtcNow, AddressSpaceRealm.Raw);
|
||||
var counts = ServerCounts(server);
|
||||
File.WriteAllText(Path.Combine(Root, "live-resources.tmp"), JsonSerializer.Serialize(counts));
|
||||
File.Move(Path.Combine(Root, "live-resources.tmp"), Path.Combine(Root, "live-resources.json"), true);
|
||||
await Task.Delay(250, Ct);
|
||||
}
|
||||
using var process = System.Diagnostics.Process.GetCurrentProcess();
|
||||
File.WriteAllText(Path.Combine(Root, "server-resources.json"), JsonSerializer.Serialize(new { process.WorkingSet64, process.PrivateMemorySize64, process.HandleCount, threads=process.Threads.Count, cpuMilliseconds=process.TotalProcessorTime.TotalMilliseconds, allocatedBytes=GC.GetTotalAllocatedBytes(), collections=new[]{GC.CollectionCount(0),GC.CollectionCount(1),GC.CollectionCount(2)} }, new JsonSerializerOptions {WriteIndented=true}));
|
||||
}
|
||||
static async Task Client(string endpoint)
|
||||
{
|
||||
using var client = new OpcUaClientService();
|
||||
await Case("trusted-SignAndEncrypt-API-key", async () => { var info = await client.ConnectAsync(Settings(endpoint), Ct); Require(info.SecurityMode == "SignAndEncrypt" && info.SecurityPolicyUri == SecurityPolicies.Basic256Sha256, "Unexpected negotiated policy"); Observations.Add(new { info.SecurityMode, info.SecurityPolicyUri }); });
|
||||
if (!client.IsConnected) return;
|
||||
var namespaces = (string[])Clr(await client.ReadValueAsync(VariableIds.Server_NamespaceArray, Ct))!;
|
||||
var raw = (ushort)Array.IndexOf(namespaces, RawUri); var uns = (ushort)Array.IndexOf(namespaces, UnsUri);
|
||||
NodeId ScalarId(ushort ns) => new(ns == raw ? Scalar : "Eq/Scalar", ns);
|
||||
NodeId VectorId(ushort ns) => new(ns == raw ? Vector : "Eq/Vector", ns);
|
||||
await Case("raw-UNS-URI-browse", async () =>
|
||||
{
|
||||
Require(raw > 0 && uns > 0, "Missing namespace URI");
|
||||
var rawChildren = await client.BrowseAsync(new NodeId(Folder, raw), Ct);
|
||||
var unsChildren = await client.BrowseAsync(new NodeId("Eq", uns), Ct);
|
||||
Observations.Add(new { rawChildren, unsChildren });
|
||||
// Supplement the production Shared results with unfiltered wire-reference metadata.
|
||||
var adapter = typeof(OpcUaClientService).GetField("_session", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(client)!;
|
||||
var sdkSession = (Opc.Ua.Client.Session)adapter.GetType().GetField("_session", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(adapter)!;
|
||||
var (_, _, rawReferences) = await sdkSession.BrowseAsync(null, null, new NodeId(Folder, raw), 0u,
|
||||
BrowseDirection.Forward, ReferenceTypeIds.References, true, 0u);
|
||||
Observations.Add(new { rawReferences = (rawReferences.ToArray() ?? []).Select(reference => new { nodeId = reference.NodeId.ToString(), browseName = reference.BrowseName.ToString(), nodeClass = reference.NodeClass.ToString(), typeDefinition = reference.TypeDefinition.ToString(), referenceType = reference.ReferenceTypeId.ToString(), reference.IsForward }).ToArray() });
|
||||
var expectedRaw = new[] { Scalar, Vector, Alarm, Folder + "/UnsetTimestamp" }.Select(id => new NodeId(id, raw).ToString());
|
||||
Require(expectedRaw.All(id => rawChildren.Any(child => child.NodeId == id)), "Missing raw children: " + string.Join(",", expectedRaw.Where(id => !rawChildren.Any(child => child.NodeId == id))));
|
||||
Require(new[] { "Eq/Scalar", "Eq/Vector" }.Select(id => new NodeId(id, uns).ToString()).All(id => unsChildren.Any(child => child.NodeId == id)), "Missing UNS scalar/array children");
|
||||
});
|
||||
await Case("initial-quality-explicit-source-and-server-timestamps", async () => { var initial = await client.ReadValueAsync(ScalarId(raw), Ct); Require(StatusCode.IsGood(initial.StatusCode), "Bad initial quality"); Require(SourceTime(initial) == Epoch && ServerTime(initial) > Epoch, "Lost explicit initial timestamps"); Observations.Add(new { sourceTimestamp = SourceTime(initial).ToString("O"), serverTimestamp = ServerTime(initial).ToString("O"), contract = "Source supplied as 2026-10-01T00:00:00Z; server timestamp observed without normalization or historian algorithm changes" }); });
|
||||
foreach (var ns in new[] { raw, uns })
|
||||
{
|
||||
await Case($"scalar-read-write-ns-{ns}", async () => { Require(Clr(await client.ReadValueAsync(ScalarId(ns), Ct)) is int, "Scalar lost Int32 type"); Require(StatusCode.IsGood(await client.WriteValueAsync(ScalarId(ns), 42, Ct)), "Scalar write failed"); Require((int)Clr(await client.ReadValueAsync(ScalarId(ns), Ct))! == 42, "Scalar round trip failed"); });
|
||||
await Case($"typed-array-read-write-ns-{ns}", async () => { Require(Clr(await client.ReadValueAsync(VectorId(ns), Ct)) is int[], "Array lost Int32[] type"); Require(StatusCode.IsGood(await client.WriteValueAsync(VectorId(ns), new[] { 7, 8, 9 }, Ct)), "Array write failed"); Require(((int[])Clr(await client.ReadValueAsync(VectorId(ns), Ct))!).SequenceEqual(new[] { 7, 8, 9 }), "Array round trip failed"); });
|
||||
}
|
||||
await Case("monitor-quality-timestamps", async () => { var seen = new ConcurrentQueue<DataValue>(); client.DataChanged += (_, e) => seen.Enqueue(e.Value); await client.SubscribeAsync(ScalarId(raw), 100, Ct); await client.WriteValueAsync(ScalarId(raw), 73, Ct); await Wait(() => seen.Any(v => Equals(Clr(v), 73))); var v = seen.Last(v => Equals(Clr(v), 73)); Require(StatusCode.IsGood(v.StatusCode) && SourceTime(v) > Epoch && ServerTime(v) > Epoch, "Bad monitor quality or missing timestamps"); Observations.Add(new { sourceTimestamp = SourceTime(v).ToString("O"), serverTimestamp = ServerTime(v).ToString("O"), status = v.StatusCode.ToString() }); await client.UnsubscribeAsync(ScalarId(raw), Ct); });
|
||||
await Case("event-filter-and-native-ack", async () => { var alarms = new ConcurrentQueue<AlarmEventArgs>(); client.AlarmEvent += (_, a) => alarms.Enqueue(a); await client.SubscribeAlarmsAsync(ObjectIds.Server, 100, Ct); await Wait(() => alarms.Any(a => a.Message == "matrix-alarm" && a.ActiveState && a.Severity == 800)); var alarm = alarms.Last(a => a.Message == "matrix-alarm"); Require(alarm.EventId?.Length > 0, "Missing event ID"); Require(StatusCode.IsGood(await client.AcknowledgeAlarmAsync(new NodeId(Alarm, raw).ToString(), alarm.EventId!, "matrix-ack", Ct)), "Ack refused"); await Wait(() => alarms.Any(a => a.Message == "matrix-alarm" && a.AckedState)); await client.UnsubscribeAlarmsAsync(Ct); });
|
||||
await Case("raw-history-CP-paging", async () => { var values = await client.HistoryReadRawAsync(ScalarId(raw), Epoch, Epoch.AddMinutes(1), 100, Ct); Require(values.Count == 25, "Expected 25 raw samples, got " + values.Count); Require(values.Select(v => Convert.ToInt32(Clr(v))).SequenceEqual(Enumerable.Range(0, 25)), "Raw page loss/duplicates/order"); });
|
||||
await Case("event-history-CP-paging", async () =>
|
||||
{
|
||||
var filter = new EventFilter();
|
||||
#if SDK2
|
||||
filter.SelectClauses += new SimpleAttributeOperand(ObjectTypeIds.BaseEventType, new QualifiedName("EventId")) { AttributeId = Attributes.Value };
|
||||
#else
|
||||
filter.SelectClauses.Add(new SimpleAttributeOperand {TypeDefinitionId=ObjectTypeIds.BaseEventType,BrowsePath=new QualifiedNameCollection {new QualifiedName("EventId")},AttributeId=Attributes.Value});
|
||||
#endif
|
||||
var events = await client.HistoryReadEventsAsync(new NodeId(Folder, raw), Epoch, Epoch.AddMinutes(1), filter, 100, Ct); Require(events.Count == 25, "Expected 25 event samples, got " + events.Count);
|
||||
#if SDK2
|
||||
var ids = events.Select(e => System.Text.Encoding.UTF8.GetString((byte[])ZB.MOM.WW.OtOpcUa.Client.Shared.Helpers.ValueConverter.ToClrObject(e.Fields[0])!));
|
||||
#else
|
||||
var ids=events.Select(e=>System.Text.Encoding.UTF8.GetString((byte[])e.Fields[0].Value));
|
||||
#endif
|
||||
Require(ids.SequenceEqual(Enumerable.Range(0, 25).Select(i => $"e{i:00}")), "Event history loss/duplicates/order across timestamp ties");
|
||||
});
|
||||
await Case("event-history-where-filter-positive-and-negative", async () =>
|
||||
{
|
||||
foreach (var type in new[] { ObjectTypeIds.BaseEventType, ObjectTypeIds.AuditEventType })
|
||||
{
|
||||
var filter = new EventFilter();
|
||||
#if SDK2
|
||||
filter.SelectClauses += new SimpleAttributeOperand(ObjectTypeIds.BaseEventType, new QualifiedName("EventId")) { AttributeId = Attributes.Value };
|
||||
filter.WhereClause.Push(FilterOperator.OfType, Variant.From(type));
|
||||
#else
|
||||
filter.SelectClauses.Add(new SimpleAttributeOperand(ObjectTypeIds.BaseEventType,new QualifiedName("EventId")){AttributeId=Attributes.Value});
|
||||
filter.WhereClause.Push(FilterOperator.OfType,new LiteralOperand(new Variant(type)));
|
||||
#endif
|
||||
var events = await client.HistoryReadEventsAsync(new NodeId(Folder, raw), Epoch, Epoch.AddMinutes(1), filter, 100, Ct);
|
||||
Require(events.Count == (type == ObjectTypeIds.BaseEventType ? 25 : 0), $"Where filter for {type} expected {(type == ObjectTypeIds.BaseEventType ? 25 : 0)} got {events.Count}");
|
||||
}
|
||||
});
|
||||
var unset = await client.ReadValueAsync(new NodeId(Folder + "/UnsetTimestamp", raw), Ct);
|
||||
Observations.Add(new { sourceTimestamp = SourceTime(unset).ToString("O"), serverTimestamp = ServerTime(unset).ToString("O"), timestampObservation = "Fixture source supplied DateTime.MinValue (unset). These are unmodified application/SDK wire observations; no historian algorithm or expected timestamp was changed." });
|
||||
await Benchmark(client, ScalarId(raw), VectorId(raw));
|
||||
await client.DisconnectAsync(Ct);
|
||||
}
|
||||
static OpcUaClientDriverOptions DriverOptions(string endpoint) => new()
|
||||
{
|
||||
EndpointUrl = endpoint,
|
||||
SecurityPolicy = OpcUaSecurityPolicy.Basic256Sha256,
|
||||
SecurityMode = OpcUaSecurityMode.SignAndEncrypt,
|
||||
AuthType = OpcUaAuthType.Username,
|
||||
Username = "apikey",
|
||||
Password = Password,
|
||||
AutoAcceptCertificates = false,
|
||||
RawTags = [new("scalar",JsonSerializer.Serialize(new {nodeId=$"nsu={RawUri};s={Scalar}"}),true),
|
||||
new("vector",JsonSerializer.Serialize(new {nodeId=$"nsu={RawUri};s={Vector}"}),true),
|
||||
new("uns",JsonSerializer.Serialize(new {nodeId=$"nsu={UnsUri};s=Eq/Scalar"}),true)],
|
||||
SessionName = "MatrixDriver",
|
||||
ApplicationUri = "urn:localhost:MatrixDriver"
|
||||
};
|
||||
static string IsolatedLocalData()
|
||||
{
|
||||
var local = Path.GetFullPath(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData));
|
||||
Require(local.StartsWith(Path.GetFullPath(Root) + Path.DirectorySeparatorChar, StringComparison.Ordinal), "Driver/CLI canonical PKI must be in task-owned root: " + local);
|
||||
return local;
|
||||
}
|
||||
static async Task Driver(string endpoint)
|
||||
{
|
||||
IsolatedLocalData();
|
||||
await using var driver = new OpcUaClientDriver(DriverOptions(endpoint), "matrix");
|
||||
await Case("driver-trusted-encrypted-API-key-connect", () => driver.InitializeAsync("{}", Ct));
|
||||
await Case("driver-raw-UNS-URI-scalar-and-array-read", async () =>
|
||||
{
|
||||
var values = await driver.ReadAsync(["scalar", "vector", "uns"], Ct);
|
||||
Require(values.Count == 3 && values.All(x => x.StatusCode == 0), "Read failure");
|
||||
Require(values[0].Value is int && values[1].Value is int[] && values[2].Value is int, "Lost scalar/array type");
|
||||
});
|
||||
await Case("driver-scalar-array-write-roundtrip", async () =>
|
||||
{
|
||||
var writes = await driver.WriteAsync([new WriteRequest("scalar", 91), new WriteRequest("vector", new[] { 11, 12, 13 })], Ct);
|
||||
Require(writes.All(x => x.StatusCode == 0), "Write failed");
|
||||
var values = await driver.ReadAsync(["scalar", "vector"], Ct);
|
||||
Require(Equals(values[0].Value, 91) && ((int[])values[1].Value!).SequenceEqual(new[] { 11, 12, 13 }), "Write round trip mismatch");
|
||||
});
|
||||
await Case("driver-monitor-quality-timestamps", async () =>
|
||||
{
|
||||
var seen = new ConcurrentQueue<DataValueSnapshot>();
|
||||
driver.OnDataChange += (_, e) => seen.Enqueue(e.Snapshot);
|
||||
var subscription = await driver.SubscribeAsync(["scalar"], TimeSpan.FromMilliseconds(100), Ct);
|
||||
await driver.WriteAsync([new WriteRequest("scalar", 92)], Ct);
|
||||
await Wait(() => seen.Any(x => Equals(x.Value, 92)));
|
||||
var value = seen.Last(x => Equals(x.Value, 92));
|
||||
Require(value.StatusCode == 0 && value.SourceTimestampUtc.HasValue && value.ServerTimestampUtc > Epoch, "Missing monitor quality/timestamps");
|
||||
Observations.Add(new { value.SourceTimestampUtc, value.ServerTimestampUtc, value.StatusCode });
|
||||
await driver.UnsubscribeAsync(subscription, Ct);
|
||||
});
|
||||
Observations.Add(new { capability = "driver-history", status = "not-applicable", evidence = "Driver has no IHistoryProvider/HistoryRead capability; .claude/rules/drivers.md #655. Shared/CLI own history." });
|
||||
await driver.ShutdownAsync(Ct);
|
||||
}
|
||||
static async Task Performance(string endpoint)
|
||||
{
|
||||
using var client = new OpcUaClientService();
|
||||
var info = await client.ConnectAsync(Settings(endpoint), Ct);
|
||||
Require(info.SecurityMode == "SignAndEncrypt" && info.SecurityPolicyUri == SecurityPolicies.Basic256Sha256, "Unexpected benchmark endpoint policy");
|
||||
var namespaces = (string[])Clr(await client.ReadValueAsync(VariableIds.Server_NamespaceArray, Ct))!;
|
||||
var raw = (ushort)Array.IndexOf(namespaces, RawUri);
|
||||
Require(raw > 0, "Missing benchmark raw namespace");
|
||||
Observations.Add(new {info.SecurityMode, info.SecurityPolicyUri, tieredCompilation=Environment.GetEnvironmentVariable("DOTNET_TieredCompilation"), scope="Two scalar/Int32[3] variables; zero data monitors during measurement; five fixture variables and one native condition"});
|
||||
await Case("warmed-production-client-read-write-measurement", () => Benchmark(client, new NodeId(Scalar, raw), new NodeId(Vector, raw)));
|
||||
await client.DisconnectAsync(Ct);
|
||||
}
|
||||
static async Task Benchmark(OpcUaClientService client, NodeId scalar, NodeId vector)
|
||||
{
|
||||
// Local diagnostic subset; excludes reconnect, history and load.
|
||||
var rounds = int.Parse(Environment.GetEnvironmentVariable("OPCUA_MATRIX_PERF_ROUNDS") ?? "3");
|
||||
var warmups = int.Parse(Environment.GetEnvironmentVariable("OPCUA_MATRIX_PERF_WARMUPS") ?? "10");
|
||||
var measured = int.Parse(Environment.GetEnvironmentVariable("OPCUA_MATRIX_PERF_MEASURED") ?? "50");
|
||||
Require(rounds > 0 && warmups >= 0 && measured > 0, "Invalid performance sample counts");
|
||||
for (int round = 1; round <= rounds; round++)
|
||||
{
|
||||
for (int warmup = 0; warmup < warmups; warmup++)
|
||||
{
|
||||
await client.ReadValueAsync(scalar, Ct); await client.WriteValueAsync(scalar, warmup, Ct);
|
||||
await client.ReadValueAsync(vector, Ct); await client.WriteValueAsync(vector, new[] { warmup, warmup + 1, warmup + 2 }, Ct);
|
||||
}
|
||||
var samples = new Dictionary<string, List<double>>();
|
||||
var before = System.Diagnostics.Process.GetCurrentProcess(); var cpu = before.TotalProcessorTime;
|
||||
var allocated = GC.GetTotalAllocatedBytes(); var gc = new[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) };
|
||||
foreach (var operation in new[] { "scalar-read", "scalar-write", "array-read", "array-write" })
|
||||
{
|
||||
var times = new List<double>();
|
||||
for (int i = 0; i < measured; i++)
|
||||
{
|
||||
var start = System.Diagnostics.Stopwatch.GetTimestamp();
|
||||
if (operation == "scalar-read") await client.ReadValueAsync(scalar, Ct);
|
||||
else if (operation == "scalar-write") Require(StatusCode.IsGood(await client.WriteValueAsync(scalar, i, Ct)), "Benchmark scalar write");
|
||||
else if (operation == "array-read") await client.ReadValueAsync(vector, Ct);
|
||||
else Require(StatusCode.IsGood(await client.WriteValueAsync(vector, new[] { i, i + 1, i + 2 }, Ct)), "Benchmark array write");
|
||||
times.Add(System.Diagnostics.Stopwatch.GetElapsedTime(start).TotalMilliseconds);
|
||||
}
|
||||
samples[operation] = times;
|
||||
}
|
||||
before.Refresh();
|
||||
Observations.Add(new
|
||||
{
|
||||
benchmarkRound = round,
|
||||
warmupOperationsPerKind = 10,
|
||||
operationsPerKind = 50,
|
||||
latencyMilliseconds = samples.ToDictionary(x => x.Key, x => new { p50 = x.Value.Order().ElementAt((int)Math.Ceiling(measured * 0.50) - 1), p95 = x.Value.Order().ElementAt((int)Math.Ceiling(measured * 0.95) - 1), p99 = x.Value.Order().ElementAt((int)Math.Ceiling(measured * 0.99) - 1), throughputOperationsPerSecond = measured * 1000.0 / x.Value.Sum() }),
|
||||
clientCpuMilliseconds = (before.TotalProcessorTime - cpu).TotalMilliseconds,
|
||||
clientAllocatedBytes = GC.GetTotalAllocatedBytes() - allocated, clientAllocatedBytesPerOperation = (GC.GetTotalAllocatedBytes() - allocated) / (4.0 * measured),
|
||||
clientWorkingSetBytes = before.WorkingSet64,
|
||||
clientPrivateBytes = before.PrivateMemorySize64,
|
||||
clientThreads = before.Threads.Count,
|
||||
clientHandles = before.HandleCount,
|
||||
collections = new[] { GC.CollectionCount(0) - gc[0], GC.CollectionCount(1) - gc[1], GC.CollectionCount(2) - gc[2] }
|
||||
});
|
||||
}
|
||||
}
|
||||
sealed class Auth : IOpcUaUserAuthenticator
|
||||
{
|
||||
public Task<OpcUaUserAuthResult> AuthenticateUserNameAsync(string username, string password, CancellationToken ct) => Task.FromResult(username == "apikey" && password == Password ? OpcUaUserAuthResult.AllowKey("matrix", [OpcUaDataPlaneRoles.WriteOperate, OpcUaDataPlaneRoles.AlarmAck], "matrix") : OpcUaUserAuthResult.Deny("Invalid ephemeral fixture key"));
|
||||
}
|
||||
sealed class Gateway : IOpcUaNodeWriteGateway { public Task<NodeWriteOutcome> WriteAsync(string id, object? value, AddressSpaceRealm realm, CancellationToken ct) => Task.FromResult(new NodeWriteOutcome(true, null)); }
|
||||
sealed class Source(string root) : IHistorianDataSource
|
||||
{
|
||||
void Log(string operation, int rows) => File.AppendAllText(Path.Combine(root, "history-requests.jsonl"), JsonSerializer.Serialize(new { operation, rows }) + "\n");
|
||||
public int MaxRawValuesPerRead => 7;
|
||||
public Task<CoreHistory> ReadRawAsync(string r, DateTime s, DateTime e, uint m, CancellationToken ct)
|
||||
{ var all = Enumerable.Range(0, 25).Select(i => new DataValueSnapshot(i, 0, Epoch.AddSeconds(i), Epoch.AddSeconds(i))).Where(v => v.SourceTimestampUtc >= s && v.SourceTimestampUtc < e).ToList(); var rows = all.Take((int)(m == 0 ? 7 : m)).ToArray(); Log("raw", rows.Length); return Task.FromResult(new CoreHistory(rows, all.Count > rows.Length ? [1] : null)); }
|
||||
public Task<HistoricalEventsResult> ReadEventsAsync(string? source, DateTime s, DateTime e, int max, CancellationToken ct)
|
||||
{ var all = Enumerable.Range(0, 25).Select(i => new HistoricalEvent($"e{i:00}", "matrix", Epoch.AddSeconds(i / 2), Epoch, "matrix-history", 500)).Where(v => v.EventTimeUtc >= s && v.EventTimeUtc < e).ToList(); var rows = all.Take(Math.Min(7, max <= 0 ? 7 : max)).ToArray(); Log("events", rows.Length); return Task.FromResult(new HistoricalEventsResult(rows, all.Count > rows.Length ? [1] : null)); }
|
||||
public Task<CoreHistory> ReadProcessedAsync(string r, DateTime s, DateTime e, TimeSpan i, HistoryAggregateType a, CancellationToken ct) => NullHistorianDataSource.Instance.ReadProcessedAsync(r, s, e, i, a, ct);
|
||||
public Task<CoreHistory> ReadAtTimeAsync(string r, IReadOnlyList<DateTime> t, CancellationToken ct) => NullHistorianDataSource.Instance.ReadAtTimeAsync(r, t, ct);
|
||||
public HistorianHealthSnapshot GetHealthSnapshot() => NullHistorianDataSource.Instance.GetHealthSnapshot();
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
# Production OPC UA client lifecycle diagnostic
|
||||
|
||||
This standalone harness runs the unchanged production `OpcUaClientService`,
|
||||
`OpcUaClientDriver`, and `OpcUaClientDriverBrowser` in one persistent Linux client
|
||||
process against a separate process using the production SDK server and node
|
||||
manager. Both processes load OPC Foundation SDK 2.0.1. The server fixture is a
|
||||
local copy of the compatibility harness at commit `7b353d7b`; only this directory's
|
||||
copy has configurable lifetime and atomic public resource snapshots.
|
||||
|
||||
Run a short diagnostic from a checkout containing the SDK 2.0.1 changes:
|
||||
|
||||
```sh
|
||||
python3 scripts/validation/opcua-client-lifecycle/run.py \
|
||||
--duration 90 --output /tmp/otopcua-client-lifecycle-diagnostic-unique
|
||||
```
|
||||
|
||||
The coordinator must launch and own the full process separately:
|
||||
|
||||
```sh
|
||||
python3 scripts/validation/opcua-client-lifecycle/run.py \
|
||||
--full --duration 7200 --output /tmp/otopcua-client-lifecycle-full-unique
|
||||
```
|
||||
|
||||
The output path must not exist. `--full` rejects a duration below 7200 seconds;
|
||||
`fullTwoHourLifecycle` is true only after the client monotonic clock has actually
|
||||
elapsed at least 7200 seconds and every final check completes. A diagnostic pass
|
||||
cannot satisfy that gate. The outer process also has a duration plus 300-second
|
||||
hard deadline and forcibly removes its own container on timeout.
|
||||
|
||||
The launcher publishes this standalone project, then uses the locally cached
|
||||
official `mcr.microsoft.com/dotnet/sdk:10.0.103-noble` Linux arm64 image with
|
||||
`--network none`, no host ports and fresh task-owned PKI. `XDG_DATA_HOME` is the
|
||||
only storage environment override; it never changes `HOME`. A generated
|
||||
credential stays in the child process environment. The output root is mode 0700
|
||||
and contains generated private keys: **never commit the entire output directory**.
|
||||
Commit only the selected redacted JSON/log receipts under `evidence/`.
|
||||
|
||||
Preparation invokes bounded failing connections through each product's public
|
||||
API to create its own application certificate. It does not reflectively invoke
|
||||
configuration builders or replace the store. Before the live session, public DER
|
||||
certificates are exchanged explicitly among the three clients and server.
|
||||
Autoaccept is disabled on both ends. Negotiation must be Basic256Sha256
|
||||
SignAndEncrypt and API-key-shaped credentials must pass the fixture authenticator
|
||||
and production authority/ACL path. Existing own DER hashes must remain unchanged
|
||||
across all loop iterations.
|
||||
|
||||
Every cycle performs two Shared connect/read/write/subscribe/unsubscribe/
|
||||
disconnect rounds on the same object. It performs two driver initialize/read/
|
||||
write/subscribe/unsubscribe rounds, each followed by a live reinitialize with an
|
||||
active subscription, then shutdown. It opens, browses and disposes two production
|
||||
AdminUI browser sessions. Scalar Int32 and Int32[3] writes must roundtrip exactly,
|
||||
and both Shared and driver scalar data notifications must arrive.
|
||||
|
||||
The external harness reads ownership fields for diagnostics only. Fields are
|
||||
looked up by exact name, with missing/null layouts failing the run. It does not
|
||||
write reflected fields or change SDK state. For SDK 2.0.1.0 only, each captured
|
||||
`CertificateManager.m_disposed` boolean must be false while connected and true
|
||||
after release. An independently created, deliberately disposed SDK manager proves
|
||||
the before/after probe. All strong manager references are dropped each pass.
|
||||
Eight released managers are checked per cycle: two Shared, four driver (including
|
||||
live reinitialization), and two browser. Production session and data subscription
|
||||
collections must clear, and public Shared connection information and driver health
|
||||
must return to disconnected/Unknown. Collected weak references to disposed client,
|
||||
driver, browser and browse-session objects must have no survivors at samples.
|
||||
|
||||
After every cycle the server's public session/subscription/monitored-item counts
|
||||
must return to zero within a bounded settle period. Resource samples capture GC,
|
||||
managed allocation/retained bytes, RSS/private bytes, global FDs and task/PKI FDs.
|
||||
Owned-path FD matching canonicalizes absolute paths and uses a path-separator
|
||||
boundary. Sampling starts cold, then every ten cycles and at completion. RSS and
|
||||
FD observations require interpretation after warmup; they are not performance
|
||||
budgets or an automatic G4 acceptance decision.
|
||||
|
||||
Artifacts are `result.json`, `source.json`, `command.json`, `build.log`,
|
||||
`container.log`, and `task/{summary.json,resources.jsonl,sdk-assemblies.json,
|
||||
client.log,server.log,prepare.log,server/live-resources.json}`. A failure additionally
|
||||
writes a redacted `task/failure.txt`. The server uses a fixed application URI and
|
||||
the three client certificate stores are reused for the entire process lifetime.
|
||||
|
||||
This is a small local lifecycle gate, not the entire Task 12 gate. It does not
|
||||
cover certificate revocation, network stalls, node surgical edits/rebuild,
|
||||
continuation-point abandonment/churn, representative driver fanout/load,
|
||||
Windows install/publish, ScadaBridge or AVEVA interoperability. It makes no
|
||||
baseline/candidate performance or full G4 claim. The coordinator's separate server
|
||||
lifecycle harness supplies complementary SDK session/CP/server mutation coverage.
|
||||
|
||||
The checked-in 2026-11-10 diagnostic completed **91.104 seconds**, 68 cycles,
|
||||
136 Shared rounds, 136 driver rounds (plus 136 live driver reinitializations),
|
||||
136 browser sessions, and 544 verified released certificate managers. The final
|
||||
sample had zero surviving weak product objects and zero owned PKI FDs; server
|
||||
sessions/subscriptions/items returned to zero after every cycle. Warm samples
|
||||
showed 146–148 total FDs; the final managed heap was 4,737,536 bytes and RSS was
|
||||
181,018,624 bytes. The deliberate manager before/after control passed and
|
||||
`--full --duration 90` exited 2 before creating artifacts. This is diagnostic
|
||||
coverage only; the two-hour gate was pending at that point. The completed full
|
||||
run is recorded below. Receipts are under `evidence/2026-11-10-diagnostic/`.
|
||||
|
||||
|
||||
The coordinator-owned full run completed on 2026-11-10 with **exit 0 and
|
||||
7,200.984 seconds of actual client observation**: 5,420 cycles; 10,840 Shared
|
||||
rounds, 10,840 driver rounds plus 10,840 live reinitializations, 10,840 browser
|
||||
sessions, and **43,360 verified manager disposals**. All samples report zero weak
|
||||
product-object survivors and zero open PKI descriptors; each cycle returned server
|
||||
public session/subscription/item counts to zero. Final retained managed memory was
|
||||
3,988,520 bytes and RSS 86,102,016 bytes, with 148 global FDs. Own certificate hashes
|
||||
were unchanged. The manager before/after positive control passed. Selected counter
|
||||
histories, loaded SDK hashes, artifact manifest and verification hashes are in
|
||||
`evidence/2026-11-10-full/`; private PKI and credentials are excluded.
|
||||
|
||||
This is a **completed two-hour client lifecycle gate**, not a performance-budget
|
||||
or complete G4 pass. Its source is `c25561c6`, exact SDK 2.0.1, framework-dependent
|
||||
Linux ARM64 on runtime 10.0.3 with the pinned image ID recorded in the receipt.
|
||||
The later HG 1.0 consumer mapping is independently tested and does not retroactively
|
||||
change these immutable artifact/source records. The failed canonical server RSS
|
||||
workload remains separate in #799.
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
# Disposable network-isolated container only. All PKI is owned by this fresh task root.
|
||||
set -euo pipefail
|
||||
mkdir -p /task/data /task/server
|
||||
export XDG_DATA_HOME=/task/data
|
||||
export OPCUA_LIFECYCLE_SERVER_SECONDS=$((LIFECYCLE_SECONDS + 300))
|
||||
dotnet /app/Lifecycle.dll prepare /task > /task/prepare.log 2>&1
|
||||
mkdir -p /task/server/server-pki/trusted/certs
|
||||
for pki in /task/data/shared-pki /task/data/OtOpcUa/pki /task/data/OtOpcUa/adminui-browse-pki; do
|
||||
cp "$pki"/own/certs/* /task/server/server-pki/trusted/certs/
|
||||
done
|
||||
dotnet /app/Lifecycle.dll server /task/server > /task/server.log 2>&1 &
|
||||
server_pid=$!
|
||||
cleanup() {
|
||||
touch /task/server/stop
|
||||
for attempt in $(seq 1 150); do
|
||||
kill -0 "$server_pid" 2>/dev/null || break
|
||||
sleep 0.1
|
||||
done
|
||||
kill "$server_pid" 2>/dev/null || true
|
||||
wait "$server_pid" || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
for attempt in $(seq 1 600); do
|
||||
test ! -f /task/server/ready.json || break
|
||||
kill -0 "$server_pid" || exit 1
|
||||
sleep 0.1
|
||||
done
|
||||
test -f /task/server/ready.json
|
||||
for pki in /task/data/shared-pki /task/data/OtOpcUa/pki /task/data/OtOpcUa/adminui-browse-pki; do
|
||||
mkdir -p "$pki/trusted/certs"
|
||||
cp /task/server/server-pki/own/certs/* "$pki/trusted/certs/"
|
||||
done
|
||||
endpoint=$(cat /task/server/endpoint.txt)
|
||||
dotnet /app/Lifecycle.dll run /task "$endpoint" "$LIFECYCLE_SECONDS" "$LIFECYCLE_MODE" > /task/client.log 2>&1
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
Determining projects to restore...
|
||||
All projects are up-to-date for restore.
|
||||
ZB.MOM.WW.OtOpcUa.Analyzers -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Tooling/ZB.MOM.WW.OtOpcUa.Analyzers/bin/Release/netstandard2.0/ZB.MOM.WW.OtOpcUa.Analyzers.dll
|
||||
ZB.MOM.WW.OtOpcUa.Core.Abstractions -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Abstractions.dll
|
||||
ZB.MOM.WW.OtOpcUa.Commons -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Core/ZB.MOM.WW.OtOpcUa.Commons/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Commons.dll
|
||||
ZB.MOM.WW.OtOpcUa.Cluster -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Cluster.dll
|
||||
ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions.dll
|
||||
ZB.MOM.WW.OtOpcUa.Core.Scripting -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.dll
|
||||
ZB.MOM.WW.OtOpcUa.Core.VirtualTags -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts.dll
|
||||
ZB.MOM.WW.OtOpcUa.Configuration -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Configuration.dll
|
||||
ZB.MOM.WW.OtOpcUa.Core -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Core/ZB.MOM.WW.OtOpcUa.Core/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.dll
|
||||
ZB.MOM.WW.OtOpcUa.Core.Scripting.Protocol -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Protocol/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.Protocol.dll
|
||||
ZB.MOM.WW.OtOpcUa.Core.Scripting.OutOfProcess -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.OutOfProcess/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.OutOfProcess.dll
|
||||
ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.dll
|
||||
ZB.MOM.WW.OtOpcUa.Security -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Server/ZB.MOM.WW.OtOpcUa.Security/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Security.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Modbus.Contracts -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Contracts/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Contracts.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.S7.Contracts -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Contracts/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.S7.Contracts.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.AbCip.Contracts -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Contracts/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Contracts.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Contracts -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Contracts/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Contracts.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Contracts -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Contracts/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Contracts.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Contracts -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Contracts/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Contracts.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Calculation.Contracts -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Calculation.Contracts/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Calculation.Contracts.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts.dll
|
||||
ZB.MOM.WW.OtOpcUa.ControlPlane -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.ControlPlane.dll
|
||||
ZB.MOM.WW.OtOpcUa.OpcUaServer -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.OpcUaServer.dll
|
||||
ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian.dll
|
||||
ZB.MOM.WW.OtOpcUa.Runtime -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Runtime.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Contracts -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Contracts/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Contracts.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Session -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Session/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Session.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Sql.Browser -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Browser/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Browser.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Transport -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Transport/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Transport.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Browser -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Browser/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Browser.dll
|
||||
ZB.MOM.WW.OtOpcUa.AdminUI -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.AdminUI.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.AbCip -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbCip.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.AbLegacy -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Calculation -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Calculation/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Calculation.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.FOCAS -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Galaxy -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Modbus -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Mqtt -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Mqtt/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.MTConnect -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.S7 -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.S7.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.Sql -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.dll
|
||||
ZB.MOM.WW.OtOpcUa.Driver.TwinCAT -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.dll
|
||||
ZB.MOM.WW.OtOpcUa.ScriptWorker -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Server/ZB.MOM.WW.OtOpcUa.ScriptWorker/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.ScriptWorker.dll
|
||||
ZB.MOM.WW.OtOpcUa.Host -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Server/ZB.MOM.WW.OtOpcUa.Host/bin/Release/net10.0/OtOpcUa.Host.dll
|
||||
ZB.MOM.WW.OtOpcUa.Client.Shared -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Client.Shared.dll
|
||||
Lifecycle -> /Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-client-lifecycle/bin/Release/net10.0/Lifecycle.dll
|
||||
Lifecycle -> /private/tmp/opcua2-production-client-diagnostic-20261110c/app/
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"argv": [
|
||||
"docker",
|
||||
"run",
|
||||
"--rm",
|
||||
"--name",
|
||||
"otopcua-client-lifecycle-a2c271bbeb54",
|
||||
"--network",
|
||||
"none",
|
||||
"-e",
|
||||
"OPCUA_MATRIX_PASSWORD",
|
||||
"-e",
|
||||
"LIFECYCLE_SECONDS",
|
||||
"-e",
|
||||
"LIFECYCLE_MODE",
|
||||
"-v",
|
||||
"/private/tmp/opcua2-production-client-diagnostic-20261110c/app:/app:ro",
|
||||
"-v",
|
||||
"/private/tmp/opcua2-production-client-diagnostic-20261110c/task:/task",
|
||||
"mcr.microsoft.com/dotnet/sdk:10.0.103-noble",
|
||||
"bash",
|
||||
"/task/container.sh"
|
||||
],
|
||||
"timeoutSeconds": 390
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
usage: run.py [-h] --output OUTPUT [--duration DURATION] [--full]
|
||||
[--image IMAGE]
|
||||
run.py: error: --full requires duration >=7200; diagnostic duration must be positive
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{"cycle":0,"elapsedSeconds":0,"utc":"2026-11-10T17:39:34.5140163Z","WorkingSet64":50884608,"PrivateMemorySize64":87019520,"managedBytes":243384,"allocatedBytes":307688,"gc":[2,2,2],"fdCount":66,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":0,"sharedPasses":0,"driverPasses":0,"browserPasses":0}
|
||||
{"cycle":10,"elapsedSeconds":13.7816334,"utc":"2026-11-10T17:39:48.307566Z","WorkingSet64":168873984,"PrivateMemorySize64":210952192,"managedBytes":4622440,"allocatedBytes":53385048,"gc":[5,4,4],"fdCount":146,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":80,"sharedPasses":20,"driverPasses":20,"browserPasses":20}
|
||||
{"cycle":20,"elapsedSeconds":26.955914,"utc":"2026-11-10T17:40:01.4729188Z","WorkingSet64":181080064,"PrivateMemorySize64":230060032,"managedBytes":4716120,"allocatedBytes":100286768,"gc":[8,6,6],"fdCount":146,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":160,"sharedPasses":40,"driverPasses":40,"browserPasses":40}
|
||||
{"cycle":30,"elapsedSeconds":40.3288294,"utc":"2026-11-10T17:40:14.8512936Z","WorkingSet64":181923840,"PrivateMemorySize64":230674432,"managedBytes":4624608,"allocatedBytes":146998480,"gc":[11,8,8],"fdCount":146,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":240,"sharedPasses":60,"driverPasses":60,"browserPasses":60}
|
||||
{"cycle":40,"elapsedSeconds":53.6437668,"utc":"2026-11-10T17:40:28.1646671Z","WorkingSet64":183074816,"PrivateMemorySize64":232931328,"managedBytes":4503968,"allocatedBytes":193703712,"gc":[14,10,10],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":320,"sharedPasses":80,"driverPasses":80,"browserPasses":80}
|
||||
{"cycle":50,"elapsedSeconds":66.9338459,"utc":"2026-11-10T17:40:41.4571518Z","WorkingSet64":184758272,"PrivateMemorySize64":232972288,"managedBytes":4503736,"allocatedBytes":240424880,"gc":[17,12,12],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":400,"sharedPasses":100,"driverPasses":100,"browserPasses":100}
|
||||
{"cycle":60,"elapsedSeconds":80.254683,"utc":"2026-11-10T17:40:54.7806201Z","WorkingSet64":180539392,"PrivateMemorySize64":233132032,"managedBytes":4504072,"allocatedBytes":287112440,"gc":[20,14,14],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":480,"sharedPasses":120,"driverPasses":120,"browserPasses":120}
|
||||
{"cycle":68,"elapsedSeconds":91.0947059,"utc":"2026-11-10T17:41:05.614513Z","WorkingSet64":180760576,"PrivateMemorySize64":233250816,"managedBytes":4739896,"allocatedBytes":324761520,"gc":[23,16,16],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":544,"sharedPasses":136,"driverPasses":136,"browserPasses":136}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"exitCode": 0,
|
||||
"containerElapsedSeconds": 93.73282008292153,
|
||||
"summary": {
|
||||
"status": "passed",
|
||||
"diagnosticPositiveControl": "exact SDK2 manager false-before/true-after DisposeAsync",
|
||||
"applicationCertificateHashes": [
|
||||
"data/OtOpcUa/adminui-browse-pki/own/certs/OtOpcUa AdminUI Browse [14579B861300836A2A96549E307DB4123778B0C5].der:89e21ac01a5582dd04df77add2fad19495c695b69f4a48a05d2c71283325503b",
|
||||
"data/OtOpcUa/pki/own/certs/ClientLifecycle [AF2D40052FCDDC9A8C09686E6E8E0F17CFF5A979].der:e9bdc63bc2cc0b7901389f958b2fd6bfaf5369f439dc996e152668a3fa3c381c",
|
||||
"data/shared-pki/own/certs/OtOpcUaClient [91C87A8D0AC11919E0AFD44457BF932F3587F1B0].der:b4f2018a43f161e18d25f7b8bb80590ccec13afcf7b9189f6d97e10a68b55602"
|
||||
],
|
||||
"requestedSeconds": 90,
|
||||
"elapsedSeconds": 91.1041217,
|
||||
"fullTwoHourLifecycle": false,
|
||||
"cycles": 68,
|
||||
"sharedPasses": 136,
|
||||
"driverPasses": 136,
|
||||
"browserPasses": 136,
|
||||
"managerDisposalsVerified": 544,
|
||||
"finalResources": {
|
||||
"cycle": 68,
|
||||
"elapsedSeconds": 91.1041218,
|
||||
"utc": "2026-11-10T17:41:05.6187359Z",
|
||||
"WorkingSet64": 181018624,
|
||||
"PrivateMemorySize64": 233250816,
|
||||
"managedBytes": 4737536,
|
||||
"allocatedBytes": 324891240,
|
||||
"gc": [
|
||||
25,
|
||||
18,
|
||||
18
|
||||
],
|
||||
"fdCount": 148,
|
||||
"ownedRootFdCount": 4,
|
||||
"ownedPkiFdCount": 0,
|
||||
"survivingProductionObjects": 0,
|
||||
"managerDisposalsVerified": 544,
|
||||
"sharedPasses": 136,
|
||||
"driverPasses": 136,
|
||||
"browserPasses": 136
|
||||
},
|
||||
"scope": "Production Shared connect/disconnect/reconnect; production driver Initialize/Reinitialize/Shutdown/dispose; production browser open/browse/dispose; scalar and Int32[3] read/write/subscribe/unsubscribe; strict mutual trusted DER/Basic256Sha256 SignAndEncrypt/API-key fixture. No network stalls, revocation, surgical edits/rebuild, CP abandonment or Windows/AVEVA/performance G4 claim."
|
||||
},
|
||||
"applicationCertificates": {
|
||||
"data/OtOpcUa/adminui-browse-pki/own/certs/OtOpcUa AdminUI Browse [14579B861300836A2A96549E307DB4123778B0C5].der": "89e21ac01a5582dd04df77add2fad19495c695b69f4a48a05d2c71283325503b",
|
||||
"data/OtOpcUa/pki/own/certs/ClientLifecycle [AF2D40052FCDDC9A8C09686E6E8E0F17CFF5A979].der": "e9bdc63bc2cc0b7901389f958b2fd6bfaf5369f439dc996e152668a3fa3c381c",
|
||||
"data/shared-pki/own/certs/OtOpcUaClient [91C87A8D0AC11919E0AFD44457BF932F3587F1B0].der": "b4f2018a43f161e18d25f7b8bb80590ccec13afcf7b9189f6d97e10a68b55602",
|
||||
"server/server-pki/own/certs/OtOpcUa.Matrix [29412ADF65A3E370B5CDCABE9386217B43E1C1C1].der": "c3beb0f1c0536635c7ceb0dcfc42c6220d78d4a38c00ef199c137a6ba36bb04c"
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
[
|
||||
{
|
||||
"name": "Opc.Ua.Core",
|
||||
"version": "2.0.1.0",
|
||||
"sha256": "9d0857aa6b7f3c49037d4ea557b4da8edf07e146069464481783e66c5c9cd35a"
|
||||
},
|
||||
{
|
||||
"name": "Opc.Ua.Types",
|
||||
"version": "2.0.1.0",
|
||||
"sha256": "c542f69882f4e8a4ebd5ccc28a3881d6967c7ffb6dcc3c56dc6c776f39c2ede1"
|
||||
},
|
||||
{
|
||||
"name": "Opc.Ua.Security.Certificates",
|
||||
"version": "2.0.1.0",
|
||||
"sha256": "7f4969aceb2f0db5328c6882cb4b117342f3b18905c38acdba600683860c4fd0"
|
||||
},
|
||||
{
|
||||
"name": "Opc.Ua.Core.Types",
|
||||
"version": "2.0.1.0",
|
||||
"sha256": "a7dd141b8dabe8848ff28c2fb8e16a85a0508e6a49e6b1a1c1e267175b99af96"
|
||||
},
|
||||
{
|
||||
"name": "Opc.Ua.Client",
|
||||
"version": "2.0.1.0",
|
||||
"sha256": "620da406fabdbc75d11f9827ea7e39d2d3c762471bd2264b0d11f1d6cd9247cd"
|
||||
},
|
||||
{
|
||||
"name": "Opc.Ua.Configuration",
|
||||
"version": "2.0.1.0",
|
||||
"sha256": "6ed6a594c93c3ff35d1a16539a9fe87c1e2640549cc2f48ed6bae333dc8bf0e7"
|
||||
}
|
||||
]
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"utc":"2026-11-10T17:41:05.3943073Z","sessions":0,"subscriptions":0,"items":0,"serverProcess":26}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"endpoint":"opc.tcp://127.0.0.1:36237/OtOpcUa","process":26,"sdk":"2.0.1.0","rawUri":"https://zb.com/otopcua/raw","unsUri":"https://zb.com/otopcua/uns"}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"WorkingSet64": 232546304,
|
||||
"PrivateMemorySize64": 263188480,
|
||||
"HandleCount": 151,
|
||||
"threads": 17,
|
||||
"cpuMilliseconds": 11661.139,
|
||||
"allocatedBytes": 334834272,
|
||||
"collections": [
|
||||
9,
|
||||
1,
|
||||
0
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"commit": "7b353d7bae5311a2f0a33c0a82c88d2c5b49d76f",
|
||||
"root": "/Users/dohertj2/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa",
|
||||
"durationSeconds": 90,
|
||||
"mode": "diagnostic",
|
||||
"image": {
|
||||
"Id": "sha256:b04611ee9e1b7d36797012ed921962c57189cb4dbb3dff12afb8e158110f293b",
|
||||
"RepoDigests": [
|
||||
"mcr.microsoft.com/dotnet/sdk@sha256:e362a8dbcd691522456da26a5198b8f3ca1d7641c95624fadc5e3e82678bd08a"
|
||||
],
|
||||
"Architecture": "arm64",
|
||||
"Os": "linux"
|
||||
},
|
||||
"network": "none",
|
||||
"startedUtc": "2026-11-10T17:39:26Z",
|
||||
"scope": "actual unchanged production Shared/OpcUaClientDriver/AdminUI browser in a repeated same-process loop; SDK 2.0.1 only"
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"status": "passed",
|
||||
"diagnosticPositiveControl": "exact SDK2 manager false-before/true-after DisposeAsync",
|
||||
"applicationCertificateHashes": [
|
||||
"data/OtOpcUa/adminui-browse-pki/own/certs/OtOpcUa AdminUI Browse [14579B861300836A2A96549E307DB4123778B0C5].der:89e21ac01a5582dd04df77add2fad19495c695b69f4a48a05d2c71283325503b",
|
||||
"data/OtOpcUa/pki/own/certs/ClientLifecycle [AF2D40052FCDDC9A8C09686E6E8E0F17CFF5A979].der:e9bdc63bc2cc0b7901389f958b2fd6bfaf5369f439dc996e152668a3fa3c381c",
|
||||
"data/shared-pki/own/certs/OtOpcUaClient [91C87A8D0AC11919E0AFD44457BF932F3587F1B0].der:b4f2018a43f161e18d25f7b8bb80590ccec13afcf7b9189f6d97e10a68b55602"
|
||||
],
|
||||
"requestedSeconds": 90,
|
||||
"elapsedSeconds": 91.1041217,
|
||||
"fullTwoHourLifecycle": false,
|
||||
"cycles": 68,
|
||||
"sharedPasses": 136,
|
||||
"driverPasses": 136,
|
||||
"browserPasses": 136,
|
||||
"managerDisposalsVerified": 544,
|
||||
"finalResources": {
|
||||
"cycle": 68,
|
||||
"elapsedSeconds": 91.1041218,
|
||||
"utc": "2026-11-10T17:41:05.6187359Z",
|
||||
"WorkingSet64": 181018624,
|
||||
"PrivateMemorySize64": 233250816,
|
||||
"managedBytes": 4737536,
|
||||
"allocatedBytes": 324891240,
|
||||
"gc": [
|
||||
25,
|
||||
18,
|
||||
18
|
||||
],
|
||||
"fdCount": 148,
|
||||
"ownedRootFdCount": 4,
|
||||
"ownedPkiFdCount": 0,
|
||||
"survivingProductionObjects": 0,
|
||||
"managerDisposalsVerified": 544,
|
||||
"sharedPasses": 136,
|
||||
"driverPasses": 136,
|
||||
"browserPasses": 136
|
||||
},
|
||||
"scope": "Production Shared connect/disconnect/reconnect; production driver Initialize/Reinitialize/Shutdown/dispose; production browser open/browse/dispose; scalar and Int32[3] read/write/subscribe/unsubscribe; strict mutual trusted DER/Basic256Sha256 SignAndEncrypt/API-key fixture. No network stalls, revocation, surgical edits/rebuild, CP abandonment or Windows/AVEVA/performance G4 claim."
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"diagnosticExitCode": 0,
|
||||
"fullDurationGuardExitCode": 2,
|
||||
"rawArtifactRoot": "/private/tmp/opcua2-production-client-diagnostic-20261110c",
|
||||
"shortRunDoesNotMeetTwoHourGate": true,
|
||||
"harnessFiles": [
|
||||
{
|
||||
"path": "scripts/validation/opcua-client-lifecycle/.gitignore",
|
||||
"sha256": "32dae3052f331ee34d628ef535709b301259a45df7c7522c4d35dcf49873f00b"
|
||||
},
|
||||
{
|
||||
"path": "scripts/validation/opcua-client-lifecycle/Lifecycle.cs",
|
||||
"sha256": "609b73b55c4ad2ed7dcd70f42dbc702cc233cd2f9ace8c686f8fe1cbb8471f3a"
|
||||
},
|
||||
{
|
||||
"path": "scripts/validation/opcua-client-lifecycle/Lifecycle.csproj",
|
||||
"sha256": "379ce48ae014f4a52c9e9096a3fce5046689475a08408f279e430463f5f19085"
|
||||
},
|
||||
{
|
||||
"path": "scripts/validation/opcua-client-lifecycle/ProcessHost.cs",
|
||||
"sha256": "89b6f518c9706753e7f62bda4f8312e3390cf8d0ced99d847e8df52d935720d6"
|
||||
},
|
||||
{
|
||||
"path": "scripts/validation/opcua-client-lifecycle/README.md",
|
||||
"sha256": "05cc5b39f58abd7381b3eec51e0fe18bc6de6f525430810ef21b4679fe1e5d20"
|
||||
},
|
||||
{
|
||||
"path": "scripts/validation/opcua-client-lifecycle/container.sh",
|
||||
"sha256": "899cea38d6a7e21448308dea8931a3d2fe0986d736eaf96e4769ef1637ed5aae"
|
||||
},
|
||||
{
|
||||
"path": "scripts/validation/opcua-client-lifecycle/run.py",
|
||||
"sha256": "8da86e2215fcaa883a9212b6dde0e897795efd5d75307af6318345b61c965ad8"
|
||||
}
|
||||
]
|
||||
}
|
||||
+2142
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,543 @@
|
||||
{"cycle":0,"elapsedSeconds":0,"utc":"2026-11-10T17:44:45.1152841Z","WorkingSet64":50864128,"PrivateMemorySize64":87015424,"managedBytes":243368,"allocatedBytes":307672,"gc":[2,2,2],"fdCount":66,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":0,"sharedPasses":0,"driverPasses":0,"browserPasses":0}
|
||||
{"cycle":10,"elapsedSeconds":13.7695273,"utc":"2026-11-10T17:44:58.8996774Z","WorkingSet64":169111552,"PrivateMemorySize64":210857984,"managedBytes":4700728,"allocatedBytes":53463728,"gc":[5,4,4],"fdCount":146,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":80,"sharedPasses":20,"driverPasses":20,"browserPasses":20}
|
||||
{"cycle":20,"elapsedSeconds":26.9702703,"utc":"2026-11-10T17:45:12.0945318Z","WorkingSet64":180752384,"PrivateMemorySize64":220975104,"managedBytes":4635880,"allocatedBytes":100195000,"gc":[8,6,6],"fdCount":146,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":160,"sharedPasses":40,"driverPasses":40,"browserPasses":40}
|
||||
{"cycle":30,"elapsedSeconds":40.1833927,"utc":"2026-11-10T17:45:25.3056621Z","WorkingSet64":181284864,"PrivateMemorySize64":221257728,"managedBytes":4635304,"allocatedBytes":146914520,"gc":[11,8,8],"fdCount":146,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":240,"sharedPasses":60,"driverPasses":60,"browserPasses":60}
|
||||
{"cycle":40,"elapsedSeconds":53.432775,"utc":"2026-11-10T17:45:38.5592881Z","WorkingSet64":182726656,"PrivateMemorySize64":223703040,"managedBytes":4651128,"allocatedBytes":193645336,"gc":[14,10,10],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":320,"sharedPasses":80,"driverPasses":80,"browserPasses":80}
|
||||
{"cycle":50,"elapsedSeconds":66.6594264,"utc":"2026-11-10T17:45:51.7879085Z","WorkingSet64":184299520,"PrivateMemorySize64":223821824,"managedBytes":4512664,"allocatedBytes":240338112,"gc":[17,12,12],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":400,"sharedPasses":100,"driverPasses":100,"browserPasses":100}
|
||||
{"cycle":60,"elapsedSeconds":79.9231462,"utc":"2026-11-10T17:46:05.0408096Z","WorkingSet64":181075968,"PrivateMemorySize64":223862784,"managedBytes":4512712,"allocatedBytes":287040456,"gc":[20,14,14],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":480,"sharedPasses":120,"driverPasses":120,"browserPasses":120}
|
||||
{"cycle":70,"elapsedSeconds":93.1568589,"utc":"2026-11-10T17:46:18.2833867Z","WorkingSet64":181616640,"PrivateMemorySize64":225923072,"managedBytes":4513064,"allocatedBytes":333750808,"gc":[23,16,16],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":560,"sharedPasses":140,"driverPasses":140,"browserPasses":140}
|
||||
{"cycle":80,"elapsedSeconds":106.4112548,"utc":"2026-11-10T17:46:31.5394096Z","WorkingSet64":182919168,"PrivateMemorySize64":226041856,"managedBytes":4513088,"allocatedBytes":380449960,"gc":[26,18,18],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":640,"sharedPasses":160,"driverPasses":160,"browserPasses":160}
|
||||
{"cycle":90,"elapsedSeconds":119.5485812,"utc":"2026-11-10T17:46:44.6748037Z","WorkingSet64":182964224,"PrivateMemorySize64":226074624,"managedBytes":4514752,"allocatedBytes":427140400,"gc":[29,20,20],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":720,"sharedPasses":180,"driverPasses":180,"browserPasses":180}
|
||||
{"cycle":100,"elapsedSeconds":132.7817233,"utc":"2026-11-10T17:46:57.905311Z","WorkingSet64":141791232,"PrivateMemorySize64":226054144,"managedBytes":4513568,"allocatedBytes":473846752,"gc":[32,22,22],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":800,"sharedPasses":200,"driverPasses":200,"browserPasses":200}
|
||||
{"cycle":110,"elapsedSeconds":146.0640883,"utc":"2026-11-10T17:47:11.1836839Z","WorkingSet64":141873152,"PrivateMemorySize64":226054144,"managedBytes":4512456,"allocatedBytes":520551464,"gc":[35,24,24],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":880,"sharedPasses":220,"driverPasses":220,"browserPasses":220}
|
||||
{"cycle":120,"elapsedSeconds":159.240507,"utc":"2026-11-10T17:47:24.357642Z","WorkingSet64":142090240,"PrivateMemorySize64":226074624,"managedBytes":4578864,"allocatedBytes":567333592,"gc":[38,26,26],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":960,"sharedPasses":240,"driverPasses":240,"browserPasses":240}
|
||||
{"cycle":130,"elapsedSeconds":172.3592901,"utc":"2026-11-10T17:47:37.4808822Z","WorkingSet64":140406784,"PrivateMemorySize64":226164736,"managedBytes":4627496,"allocatedBytes":614112792,"gc":[41,28,28],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":1040,"sharedPasses":260,"driverPasses":260,"browserPasses":260}
|
||||
{"cycle":140,"elapsedSeconds":185.6255455,"utc":"2026-11-10T17:47:50.7528131Z","WorkingSet64":140435456,"PrivateMemorySize64":226197504,"managedBytes":4635744,"allocatedBytes":660820104,"gc":[44,30,30],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":1120,"sharedPasses":280,"driverPasses":280,"browserPasses":280}
|
||||
{"cycle":150,"elapsedSeconds":198.8420396,"utc":"2026-11-10T17:48:03.9622808Z","WorkingSet64":140603392,"PrivateMemorySize64":226217984,"managedBytes":4526896,"allocatedBytes":707533128,"gc":[47,32,32],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":1200,"sharedPasses":300,"driverPasses":300,"browserPasses":300}
|
||||
{"cycle":160,"elapsedSeconds":212.0768585,"utc":"2026-11-10T17:48:17.2025904Z","WorkingSet64":135364608,"PrivateMemorySize64":226217984,"managedBytes":4525584,"allocatedBytes":754256240,"gc":[50,34,34],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":1280,"sharedPasses":320,"driverPasses":320,"browserPasses":320}
|
||||
{"cycle":170,"elapsedSeconds":225.286241,"utc":"2026-11-10T17:48:30.4084106Z","WorkingSet64":136687616,"PrivateMemorySize64":226197504,"managedBytes":4626624,"allocatedBytes":801068648,"gc":[53,36,36],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":1360,"sharedPasses":340,"driverPasses":340,"browserPasses":340}
|
||||
{"cycle":180,"elapsedSeconds":238.5631894,"utc":"2026-11-10T17:48:43.68748Z","WorkingSet64":136933376,"PrivateMemorySize64":226217984,"managedBytes":4509904,"allocatedBytes":847775352,"gc":[56,38,38],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":1440,"sharedPasses":360,"driverPasses":360,"browserPasses":360}
|
||||
{"cycle":190,"elapsedSeconds":252.0181175,"utc":"2026-11-10T17:48:57.1406338Z","WorkingSet64":136933376,"PrivateMemorySize64":226197504,"managedBytes":4560984,"allocatedBytes":894536040,"gc":[59,40,40],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":1520,"sharedPasses":380,"driverPasses":380,"browserPasses":380}
|
||||
{"cycle":200,"elapsedSeconds":265.2363293,"utc":"2026-11-10T17:49:10.3618643Z","WorkingSet64":136785920,"PrivateMemorySize64":226177024,"managedBytes":4513696,"allocatedBytes":941251664,"gc":[62,42,42],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":1600,"sharedPasses":400,"driverPasses":400,"browserPasses":400}
|
||||
{"cycle":210,"elapsedSeconds":278.4993127,"utc":"2026-11-10T17:49:23.6257057Z","WorkingSet64":136945664,"PrivateMemorySize64":226254848,"managedBytes":4513856,"allocatedBytes":987976336,"gc":[65,44,44],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":1680,"sharedPasses":420,"driverPasses":420,"browserPasses":420}
|
||||
{"cycle":220,"elapsedSeconds":291.8139272,"utc":"2026-11-10T17:49:36.945777Z","WorkingSet64":136957952,"PrivateMemorySize64":226254848,"managedBytes":4513000,"allocatedBytes":1034681632,"gc":[68,46,46],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":1760,"sharedPasses":440,"driverPasses":440,"browserPasses":440}
|
||||
{"cycle":230,"elapsedSeconds":305.2256216,"utc":"2026-11-10T17:49:50.3539024Z","WorkingSet64":139825152,"PrivateMemorySize64":227258368,"managedBytes":4513704,"allocatedBytes":1082343824,"gc":[71,48,48],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":1840,"sharedPasses":460,"driverPasses":460,"browserPasses":460}
|
||||
{"cycle":240,"elapsedSeconds":318.7210006,"utc":"2026-11-10T17:50:03.8442729Z","WorkingSet64":140492800,"PrivateMemorySize64":227385344,"managedBytes":4513552,"allocatedBytes":1129039640,"gc":[74,50,50],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":1920,"sharedPasses":480,"driverPasses":480,"browserPasses":480}
|
||||
{"cycle":250,"elapsedSeconds":332.1604191,"utc":"2026-11-10T17:50:17.2844081Z","WorkingSet64":140492800,"PrivateMemorySize64":227385344,"managedBytes":4515080,"allocatedBytes":1175757536,"gc":[77,52,52],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":2000,"sharedPasses":500,"driverPasses":500,"browserPasses":500}
|
||||
{"cycle":260,"elapsedSeconds":345.4675957,"utc":"2026-11-10T17:50:30.5895307Z","WorkingSet64":139522048,"PrivateMemorySize64":227418112,"managedBytes":4514784,"allocatedBytes":1222459552,"gc":[80,54,54],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":2080,"sharedPasses":520,"driverPasses":520,"browserPasses":520}
|
||||
{"cycle":270,"elapsedSeconds":358.769662,"utc":"2026-11-10T17:50:43.8952358Z","WorkingSet64":139931648,"PrivateMemorySize64":227463168,"managedBytes":4651760,"allocatedBytes":1269298688,"gc":[83,56,56],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":2160,"sharedPasses":540,"driverPasses":540,"browserPasses":540}
|
||||
{"cycle":280,"elapsedSeconds":372.1252106,"utc":"2026-11-10T17:50:57.2428981Z","WorkingSet64":140324864,"PrivateMemorySize64":235986944,"managedBytes":4570704,"allocatedBytes":1315990496,"gc":[86,58,58],"fdCount":150,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":2240,"sharedPasses":560,"driverPasses":560,"browserPasses":560}
|
||||
{"cycle":290,"elapsedSeconds":385.4714765,"utc":"2026-11-10T17:51:10.5985393Z","WorkingSet64":140390400,"PrivateMemorySize64":236068864,"managedBytes":4634576,"allocatedBytes":1362782352,"gc":[89,60,60],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":2320,"sharedPasses":580,"driverPasses":580,"browserPasses":580}
|
||||
{"cycle":300,"elapsedSeconds":398.7827818,"utc":"2026-11-10T17:51:23.9097834Z","WorkingSet64":139005952,"PrivateMemorySize64":236068864,"managedBytes":4652400,"allocatedBytes":1409489328,"gc":[92,62,62],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":2400,"sharedPasses":600,"driverPasses":600,"browserPasses":600}
|
||||
{"cycle":310,"elapsedSeconds":412.2199569,"utc":"2026-11-10T17:51:37.3401855Z","WorkingSet64":143712256,"PrivateMemorySize64":237314048,"managedBytes":4527064,"allocatedBytes":1456208880,"gc":[95,64,64],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":2480,"sharedPasses":620,"driverPasses":620,"browserPasses":620}
|
||||
{"cycle":320,"elapsedSeconds":425.5244319,"utc":"2026-11-10T17:51:50.6490664Z","WorkingSet64":144027648,"PrivateMemorySize64":237314048,"managedBytes":4569400,"allocatedBytes":1502963424,"gc":[98,66,66],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":2560,"sharedPasses":640,"driverPasses":640,"browserPasses":640}
|
||||
{"cycle":330,"elapsedSeconds":438.932473,"utc":"2026-11-10T17:52:04.0498994Z","WorkingSet64":141996032,"PrivateMemorySize64":237293568,"managedBytes":4562136,"allocatedBytes":1549665464,"gc":[101,68,68],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":2640,"sharedPasses":660,"driverPasses":660,"browserPasses":660}
|
||||
{"cycle":340,"elapsedSeconds":452.164949,"utc":"2026-11-10T17:52:17.2886751Z","WorkingSet64":141996032,"PrivateMemorySize64":237273088,"managedBytes":4512880,"allocatedBytes":1596374080,"gc":[104,70,70],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":2720,"sharedPasses":680,"driverPasses":680,"browserPasses":680}
|
||||
{"cycle":350,"elapsedSeconds":465.404458,"utc":"2026-11-10T17:52:30.528482Z","WorkingSet64":141996032,"PrivateMemorySize64":237293568,"managedBytes":4588024,"allocatedBytes":1643160240,"gc":[107,72,72],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":2800,"sharedPasses":700,"driverPasses":700,"browserPasses":700}
|
||||
{"cycle":360,"elapsedSeconds":478.6179186,"utc":"2026-11-10T17:52:43.7430573Z","WorkingSet64":142000128,"PrivateMemorySize64":237277184,"managedBytes":4513000,"allocatedBytes":1689870792,"gc":[110,74,74],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":2880,"sharedPasses":720,"driverPasses":720,"browserPasses":720}
|
||||
{"cycle":370,"elapsedSeconds":491.876047,"utc":"2026-11-10T17:52:57.0080243Z","WorkingSet64":139350016,"PrivateMemorySize64":237277184,"managedBytes":4513576,"allocatedBytes":1736584040,"gc":[113,76,76],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":2960,"sharedPasses":740,"driverPasses":740,"browserPasses":740}
|
||||
{"cycle":380,"elapsedSeconds":505.1403319,"utc":"2026-11-10T17:53:10.259606Z","WorkingSet64":139354112,"PrivateMemorySize64":237297664,"managedBytes":4544344,"allocatedBytes":1783307464,"gc":[116,78,78],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":3040,"sharedPasses":760,"driverPasses":760,"browserPasses":760}
|
||||
{"cycle":390,"elapsedSeconds":518.3707659,"utc":"2026-11-10T17:53:23.4942486Z","WorkingSet64":135155712,"PrivateMemorySize64":233123840,"managedBytes":4536360,"allocatedBytes":1830117816,"gc":[119,80,80],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":3120,"sharedPasses":780,"driverPasses":780,"browserPasses":780}
|
||||
{"cycle":400,"elapsedSeconds":531.586656,"utc":"2026-11-10T17:53:36.7128434Z","WorkingSet64":134750208,"PrivateMemorySize64":237146112,"managedBytes":4561408,"allocatedBytes":1876833480,"gc":[122,82,82],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":3200,"sharedPasses":800,"driverPasses":800,"browserPasses":800}
|
||||
{"cycle":410,"elapsedSeconds":544.8171966,"utc":"2026-11-10T17:53:49.9469169Z","WorkingSet64":134901760,"PrivateMemorySize64":237297664,"managedBytes":4502384,"allocatedBytes":1923530968,"gc":[125,84,84],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":3280,"sharedPasses":820,"driverPasses":820,"browserPasses":820}
|
||||
{"cycle":420,"elapsedSeconds":558.0481703,"utc":"2026-11-10T17:54:03.1707399Z","WorkingSet64":134901760,"PrivateMemorySize64":237297664,"managedBytes":4547616,"allocatedBytes":1970293824,"gc":[128,86,86],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":3360,"sharedPasses":840,"driverPasses":840,"browserPasses":840}
|
||||
{"cycle":430,"elapsedSeconds":571.2732878,"utc":"2026-11-10T17:54:16.3976731Z","WorkingSet64":134008832,"PrivateMemorySize64":237309952,"managedBytes":4561616,"allocatedBytes":2017029208,"gc":[131,88,88],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":3440,"sharedPasses":860,"driverPasses":860,"browserPasses":860}
|
||||
{"cycle":440,"elapsedSeconds":584.4426453,"utc":"2026-11-10T17:54:29.563521Z","WorkingSet64":134336512,"PrivateMemorySize64":237289472,"managedBytes":4512936,"allocatedBytes":2063758408,"gc":[134,90,90],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":3520,"sharedPasses":880,"driverPasses":880,"browserPasses":880}
|
||||
{"cycle":450,"elapsedSeconds":597.6619216,"utc":"2026-11-10T17:54:42.786555Z","WorkingSet64":134467584,"PrivateMemorySize64":237330432,"managedBytes":4637512,"allocatedBytes":2110581488,"gc":[137,92,92],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":3600,"sharedPasses":900,"driverPasses":900,"browserPasses":900}
|
||||
{"cycle":460,"elapsedSeconds":610.9003191,"utc":"2026-11-10T17:54:56.0332862Z","WorkingSet64":138235904,"PrivateMemorySize64":237772800,"managedBytes":4635240,"allocatedBytes":2158245944,"gc":[140,94,94],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":3680,"sharedPasses":920,"driverPasses":920,"browserPasses":920}
|
||||
{"cycle":470,"elapsedSeconds":624.0978198,"utc":"2026-11-10T17:55:09.2278067Z","WorkingSet64":137936896,"PrivateMemorySize64":237772800,"managedBytes":4652232,"allocatedBytes":2204953080,"gc":[143,96,96],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":3760,"sharedPasses":940,"driverPasses":940,"browserPasses":940}
|
||||
{"cycle":480,"elapsedSeconds":637.3568307,"utc":"2026-11-10T17:55:22.4794072Z","WorkingSet64":137936896,"PrivateMemorySize64":237752320,"managedBytes":4644040,"allocatedBytes":2251661136,"gc":[146,98,98],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":3840,"sharedPasses":960,"driverPasses":960,"browserPasses":960}
|
||||
{"cycle":490,"elapsedSeconds":650.5736842,"utc":"2026-11-10T17:55:35.6980521Z","WorkingSet64":137936896,"PrivateMemorySize64":237731840,"managedBytes":4579352,"allocatedBytes":2298383520,"gc":[149,100,100],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":3920,"sharedPasses":980,"driverPasses":980,"browserPasses":980}
|
||||
{"cycle":500,"elapsedSeconds":663.8366705,"utc":"2026-11-10T17:55:48.9564326Z","WorkingSet64":137097216,"PrivateMemorySize64":237731840,"managedBytes":4577824,"allocatedBytes":2345077424,"gc":[152,102,102],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":4000,"sharedPasses":1000,"driverPasses":1000,"browserPasses":1000}
|
||||
{"cycle":510,"elapsedSeconds":677.065381,"utc":"2026-11-10T17:56:02.1904476Z","WorkingSet64":137097216,"PrivateMemorySize64":237731840,"managedBytes":4578392,"allocatedBytes":2391771280,"gc":[155,104,104],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":4080,"sharedPasses":1020,"driverPasses":1020,"browserPasses":1020}
|
||||
{"cycle":520,"elapsedSeconds":690.3065052,"utc":"2026-11-10T17:56:15.4309025Z","WorkingSet64":137097216,"PrivateMemorySize64":237731840,"managedBytes":4578232,"allocatedBytes":2438474944,"gc":[158,106,106],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":4160,"sharedPasses":1040,"driverPasses":1040,"browserPasses":1040}
|
||||
{"cycle":530,"elapsedSeconds":703.4412912,"utc":"2026-11-10T17:56:28.5628852Z","WorkingSet64":137101312,"PrivateMemorySize64":237772800,"managedBytes":4634600,"allocatedBytes":2485236424,"gc":[161,108,108],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":4240,"sharedPasses":1060,"driverPasses":1060,"browserPasses":1060}
|
||||
{"cycle":540,"elapsedSeconds":716.6648568,"utc":"2026-11-10T17:56:41.7843237Z","WorkingSet64":132005888,"PrivateMemorySize64":237752320,"managedBytes":4626344,"allocatedBytes":2531930208,"gc":[164,110,110],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":4320,"sharedPasses":1080,"driverPasses":1080,"browserPasses":1080}
|
||||
{"cycle":550,"elapsedSeconds":729.8734274,"utc":"2026-11-10T17:56:54.9987459Z","WorkingSet64":132448256,"PrivateMemorySize64":237772800,"managedBytes":4702632,"allocatedBytes":2578769528,"gc":[167,112,112],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":4400,"sharedPasses":1100,"driverPasses":1100,"browserPasses":1100}
|
||||
{"cycle":560,"elapsedSeconds":743.4933317,"utc":"2026-11-10T17:57:08.6107867Z","WorkingSet64":132493312,"PrivateMemorySize64":237772800,"managedBytes":4683496,"allocatedBytes":2625488256,"gc":[170,114,114],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":4480,"sharedPasses":1120,"driverPasses":1120,"browserPasses":1120}
|
||||
{"cycle":570,"elapsedSeconds":756.652478,"utc":"2026-11-10T17:57:21.7778734Z","WorkingSet64":133828608,"PrivateMemorySize64":237772800,"managedBytes":4652704,"allocatedBytes":2672259440,"gc":[173,116,116],"fdCount":150,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":4560,"sharedPasses":1140,"driverPasses":1140,"browserPasses":1140}
|
||||
{"cycle":580,"elapsedSeconds":769.9148879,"utc":"2026-11-10T17:57:35.0416181Z","WorkingSet64":134316032,"PrivateMemorySize64":237740032,"managedBytes":4514344,"allocatedBytes":2718962144,"gc":[176,118,118],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":4640,"sharedPasses":1160,"driverPasses":1160,"browserPasses":1160}
|
||||
{"cycle":590,"elapsedSeconds":783.1432993,"utc":"2026-11-10T17:57:48.2637812Z","WorkingSet64":135409664,"PrivateMemorySize64":237780992,"managedBytes":4635536,"allocatedBytes":2765793352,"gc":[179,120,120],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":4720,"sharedPasses":1180,"driverPasses":1180,"browserPasses":1180}
|
||||
{"cycle":600,"elapsedSeconds":796.3965915,"utc":"2026-11-10T17:58:01.5236438Z","WorkingSet64":136241152,"PrivateMemorySize64":238190592,"managedBytes":4636328,"allocatedBytes":2812497264,"gc":[182,122,122],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":4800,"sharedPasses":1200,"driverPasses":1200,"browserPasses":1200}
|
||||
{"cycle":610,"elapsedSeconds":809.6451522,"utc":"2026-11-10T17:58:14.7728315Z","WorkingSet64":140668928,"PrivateMemorySize64":244002816,"managedBytes":4644960,"allocatedBytes":2859208272,"gc":[185,124,124],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":4880,"sharedPasses":1220,"driverPasses":1220,"browserPasses":1220}
|
||||
{"cycle":620,"elapsedSeconds":822.8248692,"utc":"2026-11-10T17:58:27.9516206Z","WorkingSet64":140668928,"PrivateMemorySize64":243982336,"managedBytes":4580712,"allocatedBytes":2905911480,"gc":[188,126,126],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":4960,"sharedPasses":1240,"driverPasses":1240,"browserPasses":1240}
|
||||
{"cycle":630,"elapsedSeconds":836.1274925,"utc":"2026-11-10T17:58:41.2503909Z","WorkingSet64":140668928,"PrivateMemorySize64":244023296,"managedBytes":4767544,"allocatedBytes":2952795848,"gc":[191,128,128],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":5040,"sharedPasses":1260,"driverPasses":1260,"browserPasses":1260}
|
||||
{"cycle":640,"elapsedSeconds":849.3561003,"utc":"2026-11-10T17:58:54.485024Z","WorkingSet64":140599296,"PrivateMemorySize64":244023296,"managedBytes":4767584,"allocatedBytes":2999516072,"gc":[194,130,130],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":5120,"sharedPasses":1280,"driverPasses":1280,"browserPasses":1280}
|
||||
{"cycle":650,"elapsedSeconds":862.6303684,"utc":"2026-11-10T17:59:07.7554554Z","WorkingSet64":140640256,"PrivateMemorySize64":244002816,"managedBytes":4628280,"allocatedBytes":3046210632,"gc":[197,132,132],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":5200,"sharedPasses":1300,"driverPasses":1300,"browserPasses":1300}
|
||||
{"cycle":660,"elapsedSeconds":875.9574451,"utc":"2026-11-10T17:59:21.0839627Z","WorkingSet64":140705792,"PrivateMemorySize64":244002816,"managedBytes":4645496,"allocatedBytes":3092929440,"gc":[200,134,134],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":5280,"sharedPasses":1320,"driverPasses":1320,"browserPasses":1320}
|
||||
{"cycle":670,"elapsedSeconds":889.3266363,"utc":"2026-11-10T17:59:34.4479619Z","WorkingSet64":138076160,"PrivateMemorySize64":244002816,"managedBytes":4645128,"allocatedBytes":3139625752,"gc":[203,136,136],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":5360,"sharedPasses":1340,"driverPasses":1340,"browserPasses":1340}
|
||||
{"cycle":680,"elapsedSeconds":902.5676055,"utc":"2026-11-10T17:59:47.6941683Z","WorkingSet64":138649600,"PrivateMemorySize64":244002816,"managedBytes":4644224,"allocatedBytes":3187309400,"gc":[206,138,138],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":5440,"sharedPasses":1360,"driverPasses":1360,"browserPasses":1360}
|
||||
{"cycle":690,"elapsedSeconds":915.8708291,"utc":"2026-11-10T18:00:00.9905254Z","WorkingSet64":138649600,"PrivateMemorySize64":244002816,"managedBytes":4627224,"allocatedBytes":3234022344,"gc":[209,140,140],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":5520,"sharedPasses":1380,"driverPasses":1380,"browserPasses":1380}
|
||||
{"cycle":700,"elapsedSeconds":929.1957975,"utc":"2026-11-10T18:00:14.3130841Z","WorkingSet64":138649600,"PrivateMemorySize64":244002816,"managedBytes":4619800,"allocatedBytes":3280726800,"gc":[212,142,142],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":5600,"sharedPasses":1400,"driverPasses":1400,"browserPasses":1400}
|
||||
{"cycle":710,"elapsedSeconds":942.4586542,"utc":"2026-11-10T18:00:27.586475Z","WorkingSet64":135946240,"PrivateMemorySize64":244002816,"managedBytes":4628968,"allocatedBytes":3327444728,"gc":[215,144,144],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":5680,"sharedPasses":1420,"driverPasses":1420,"browserPasses":1420}
|
||||
{"cycle":720,"elapsedSeconds":955.7505247,"utc":"2026-11-10T18:00:40.8724218Z","WorkingSet64":135958528,"PrivateMemorySize64":244023296,"managedBytes":4628968,"allocatedBytes":3374155568,"gc":[218,146,146],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":5760,"sharedPasses":1440,"driverPasses":1440,"browserPasses":1440}
|
||||
{"cycle":730,"elapsedSeconds":969.0838894,"utc":"2026-11-10T18:00:54.2049363Z","WorkingSet64":136417280,"PrivateMemorySize64":244023296,"managedBytes":4516824,"allocatedBytes":3420859040,"gc":[221,148,148],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":5840,"sharedPasses":1460,"driverPasses":1460,"browserPasses":1460}
|
||||
{"cycle":740,"elapsedSeconds":982.3943025,"utc":"2026-11-10T18:01:07.5128133Z","WorkingSet64":134074368,"PrivateMemorySize64":244023296,"managedBytes":4629808,"allocatedBytes":3467677680,"gc":[224,150,150],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":5920,"sharedPasses":1480,"driverPasses":1480,"browserPasses":1480}
|
||||
{"cycle":750,"elapsedSeconds":995.6763152,"utc":"2026-11-10T18:01:20.7968671Z","WorkingSet64":134213632,"PrivateMemorySize64":244158464,"managedBytes":4707312,"allocatedBytes":3514541584,"gc":[227,152,152],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":6000,"sharedPasses":1500,"driverPasses":1500,"browserPasses":1500}
|
||||
{"cycle":760,"elapsedSeconds":1008.9962837,"utc":"2026-11-10T18:01:34.1196181Z","WorkingSet64":134389760,"PrivateMemorySize64":244310016,"managedBytes":4611496,"allocatedBytes":3561239752,"gc":[230,154,154],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":6080,"sharedPasses":1520,"driverPasses":1520,"browserPasses":1520}
|
||||
{"cycle":770,"elapsedSeconds":1022.3235328,"utc":"2026-11-10T18:01:47.4463324Z","WorkingSet64":133943296,"PrivateMemorySize64":244477952,"managedBytes":4525248,"allocatedBytes":3607968032,"gc":[233,156,156],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":6160,"sharedPasses":1540,"driverPasses":1540,"browserPasses":1540}
|
||||
{"cycle":780,"elapsedSeconds":1035.6194261,"utc":"2026-11-10T18:02:00.7433573Z","WorkingSet64":134066176,"PrivateMemorySize64":244477952,"managedBytes":4525144,"allocatedBytes":3654681832,"gc":[236,158,158],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":6240,"sharedPasses":1560,"driverPasses":1560,"browserPasses":1560}
|
||||
{"cycle":790,"elapsedSeconds":1048.9445312,"utc":"2026-11-10T18:02:14.0619801Z","WorkingSet64":134283264,"PrivateMemorySize64":244641792,"managedBytes":4525200,"allocatedBytes":3701398480,"gc":[239,160,160],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":6320,"sharedPasses":1580,"driverPasses":1580,"browserPasses":1580}
|
||||
{"cycle":800,"elapsedSeconds":1062.5263877,"utc":"2026-11-10T18:02:27.6482991Z","WorkingSet64":135147520,"PrivateMemorySize64":244621312,"managedBytes":4652720,"allocatedBytes":3748232504,"gc":[242,162,162],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":6400,"sharedPasses":1600,"driverPasses":1600,"browserPasses":1600}
|
||||
{"cycle":810,"elapsedSeconds":1075.9020195,"utc":"2026-11-10T18:02:41.0260759Z","WorkingSet64":134098944,"PrivateMemorySize64":244645888,"managedBytes":4644840,"allocatedBytes":3794926744,"gc":[245,164,164],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":6480,"sharedPasses":1620,"driverPasses":1620,"browserPasses":1620}
|
||||
{"cycle":820,"elapsedSeconds":1089.2480793,"utc":"2026-11-10T18:02:54.3736896Z","WorkingSet64":134152192,"PrivateMemorySize64":244645888,"managedBytes":4626696,"allocatedBytes":3841641744,"gc":[248,166,166],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":6560,"sharedPasses":1640,"driverPasses":1640,"browserPasses":1640}
|
||||
{"cycle":830,"elapsedSeconds":1102.5923236,"utc":"2026-11-10T18:03:07.7177768Z","WorkingSet64":134152192,"PrivateMemorySize64":244645888,"managedBytes":4627632,"allocatedBytes":3888364424,"gc":[251,168,168],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":6640,"sharedPasses":1660,"driverPasses":1660,"browserPasses":1660}
|
||||
{"cycle":840,"elapsedSeconds":1115.893989,"utc":"2026-11-10T18:03:21.0117452Z","WorkingSet64":133316608,"PrivateMemorySize64":244690944,"managedBytes":4687752,"allocatedBytes":3935138320,"gc":[254,170,170],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":6720,"sharedPasses":1680,"driverPasses":1680,"browserPasses":1680}
|
||||
{"cycle":850,"elapsedSeconds":1129.1850949,"utc":"2026-11-10T18:03:34.3027344Z","WorkingSet64":133316608,"PrivateMemorySize64":244649984,"managedBytes":4514696,"allocatedBytes":3981852688,"gc":[257,172,172],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":6800,"sharedPasses":1700,"driverPasses":1700,"browserPasses":1700}
|
||||
{"cycle":860,"elapsedSeconds":1143.2609207,"utc":"2026-11-10T18:03:48.3843547Z","WorkingSet64":133566464,"PrivateMemorySize64":244674560,"managedBytes":4512416,"allocatedBytes":4028560504,"gc":[260,174,174],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":6880,"sharedPasses":1720,"driverPasses":1720,"browserPasses":1720}
|
||||
{"cycle":870,"elapsedSeconds":1156.5442235,"utc":"2026-11-10T18:04:01.6618093Z","WorkingSet64":133566464,"PrivateMemorySize64":244715520,"managedBytes":4701456,"allocatedBytes":4075436064,"gc":[263,176,176],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":6960,"sharedPasses":1740,"driverPasses":1740,"browserPasses":1740}
|
||||
{"cycle":880,"elapsedSeconds":1169.8395465,"utc":"2026-11-10T18:04:14.9592413Z","WorkingSet64":133902336,"PrivateMemorySize64":244695040,"managedBytes":4644888,"allocatedBytes":4122145768,"gc":[266,178,178],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":7040,"sharedPasses":1760,"driverPasses":1760,"browserPasses":1760}
|
||||
{"cycle":890,"elapsedSeconds":1183.0837681,"utc":"2026-11-10T18:04:28.2057113Z","WorkingSet64":134070272,"PrivateMemorySize64":244695040,"managedBytes":4584936,"allocatedBytes":4168868520,"gc":[269,180,180],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":7120,"sharedPasses":1780,"driverPasses":1780,"browserPasses":1780}
|
||||
{"cycle":900,"elapsedSeconds":1196.4124674,"utc":"2026-11-10T18:04:41.5380118Z","WorkingSet64":134070272,"PrivateMemorySize64":244695040,"managedBytes":4644976,"allocatedBytes":4215662424,"gc":[272,182,182],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":7200,"sharedPasses":1800,"driverPasses":1800,"browserPasses":1800}
|
||||
{"cycle":910,"elapsedSeconds":1210.8633534,"utc":"2026-11-10T18:04:55.9862313Z","WorkingSet64":140054528,"PrivateMemorySize64":246149120,"managedBytes":4643720,"allocatedBytes":4263326776,"gc":[275,184,184],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":7280,"sharedPasses":1820,"driverPasses":1820,"browserPasses":1820}
|
||||
{"cycle":920,"elapsedSeconds":1224.1300185,"utc":"2026-11-10T18:05:09.2476755Z","WorkingSet64":140230656,"PrivateMemorySize64":246317056,"managedBytes":4652544,"allocatedBytes":4310033600,"gc":[278,186,186],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":7360,"sharedPasses":1840,"driverPasses":1840,"browserPasses":1840}
|
||||
{"cycle":930,"elapsedSeconds":1237.4599224,"utc":"2026-11-10T18:05:22.5775977Z","WorkingSet64":140271616,"PrivateMemorySize64":246317056,"managedBytes":4576384,"allocatedBytes":4356724040,"gc":[281,188,188],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":7440,"sharedPasses":1860,"driverPasses":1860,"browserPasses":1860}
|
||||
{"cycle":940,"elapsedSeconds":1250.6471697,"utc":"2026-11-10T18:05:35.7646176Z","WorkingSet64":139784192,"PrivateMemorySize64":246317056,"managedBytes":4644992,"allocatedBytes":4403505944,"gc":[284,190,190],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":7520,"sharedPasses":1880,"driverPasses":1880,"browserPasses":1880}
|
||||
{"cycle":950,"elapsedSeconds":1263.968135,"utc":"2026-11-10T18:05:49.0858797Z","WorkingSet64":139841536,"PrivateMemorySize64":246317056,"managedBytes":4637856,"allocatedBytes":4450208048,"gc":[287,192,192],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":7600,"sharedPasses":1900,"driverPasses":1900,"browserPasses":1900}
|
||||
{"cycle":960,"elapsedSeconds":1278.7734837,"utc":"2026-11-10T18:06:03.8909298Z","WorkingSet64":140120064,"PrivateMemorySize64":246337536,"managedBytes":4645120,"allocatedBytes":4496918472,"gc":[290,194,194],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":7680,"sharedPasses":1920,"driverPasses":1920,"browserPasses":1920}
|
||||
{"cycle":970,"elapsedSeconds":1291.9503059,"utc":"2026-11-10T18:06:17.07437Z","WorkingSet64":139649024,"PrivateMemorySize64":246317056,"managedBytes":4644960,"allocatedBytes":4543641400,"gc":[293,196,196],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":7760,"sharedPasses":1940,"driverPasses":1940,"browserPasses":1940}
|
||||
{"cycle":980,"elapsedSeconds":1305.1370692,"utc":"2026-11-10T18:06:30.2607127Z","WorkingSet64":139743232,"PrivateMemorySize64":246317056,"managedBytes":4644840,"allocatedBytes":4590341832,"gc":[296,198,198],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":7840,"sharedPasses":1960,"driverPasses":1960,"browserPasses":1960}
|
||||
{"cycle":990,"elapsedSeconds":1318.5113041,"utc":"2026-11-10T18:06:43.6340267Z","WorkingSet64":139743232,"PrivateMemorySize64":246317056,"managedBytes":4644672,"allocatedBytes":4637044696,"gc":[299,200,200],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":7920,"sharedPasses":1980,"driverPasses":1980,"browserPasses":1980}
|
||||
{"cycle":1000,"elapsedSeconds":1331.8291343,"utc":"2026-11-10T18:06:56.9531769Z","WorkingSet64":145399808,"PrivateMemorySize64":246841344,"managedBytes":4655328,"allocatedBytes":4683910576,"gc":[302,202,202],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":8000,"sharedPasses":2000,"driverPasses":2000,"browserPasses":2000}
|
||||
{"cycle":1010,"elapsedSeconds":1346.5536167,"utc":"2026-11-10T18:07:11.6758977Z","WorkingSet64":142626816,"PrivateMemorySize64":246841344,"managedBytes":4720744,"allocatedBytes":4730691520,"gc":[305,204,204],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":8080,"sharedPasses":2020,"driverPasses":2020,"browserPasses":2020}
|
||||
{"cycle":1020,"elapsedSeconds":1359.7287206,"utc":"2026-11-10T18:07:24.8528482Z","WorkingSet64":143069184,"PrivateMemorySize64":255459328,"managedBytes":4744384,"allocatedBytes":4777455496,"gc":[308,206,206],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":8160,"sharedPasses":2040,"driverPasses":2040,"browserPasses":2040}
|
||||
{"cycle":1030,"elapsedSeconds":1373.0594056,"utc":"2026-11-10T18:07:38.179103Z","WorkingSet64":143056896,"PrivateMemorySize64":247009280,"managedBytes":4516048,"allocatedBytes":4824197776,"gc":[311,208,208],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":8240,"sharedPasses":2060,"driverPasses":2060,"browserPasses":2060}
|
||||
{"cycle":1040,"elapsedSeconds":1386.3945337,"utc":"2026-11-10T18:07:51.5392663Z","WorkingSet64":141619200,"PrivateMemorySize64":247029760,"managedBytes":4581064,"allocatedBytes":4870989312,"gc":[314,210,210],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":8320,"sharedPasses":2080,"driverPasses":2080,"browserPasses":2080}
|
||||
{"cycle":1050,"elapsedSeconds":1399.7240136,"utc":"2026-11-10T18:08:04.8487418Z","WorkingSet64":141619200,"PrivateMemorySize64":247009280,"managedBytes":4516312,"allocatedBytes":4917708640,"gc":[317,212,212],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":8400,"sharedPasses":2100,"driverPasses":2100,"browserPasses":2100}
|
||||
{"cycle":1060,"elapsedSeconds":1414.4014588,"utc":"2026-11-10T18:08:19.5229664Z","WorkingSet64":141619200,"PrivateMemorySize64":247029760,"managedBytes":4646848,"allocatedBytes":4964536176,"gc":[320,214,214],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":8480,"sharedPasses":2120,"driverPasses":2120,"browserPasses":2120}
|
||||
{"cycle":1070,"elapsedSeconds":1427.7593602,"utc":"2026-11-10T18:08:32.8823181Z","WorkingSet64":138493952,"PrivateMemorySize64":247029760,"managedBytes":4647848,"allocatedBytes":5011244288,"gc":[323,216,216],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":8560,"sharedPasses":2140,"driverPasses":2140,"browserPasses":2140}
|
||||
{"cycle":1080,"elapsedSeconds":1441.1025344,"utc":"2026-11-10T18:08:46.2275674Z","WorkingSet64":138579968,"PrivateMemorySize64":247029760,"managedBytes":4629840,"allocatedBytes":5057967016,"gc":[326,218,218],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":8640,"sharedPasses":2160,"driverPasses":2160,"browserPasses":2160}
|
||||
{"cycle":1090,"elapsedSeconds":1454.4184261,"utc":"2026-11-10T18:08:59.5424563Z","WorkingSet64":138579968,"PrivateMemorySize64":247029760,"managedBytes":4514248,"allocatedBytes":5104663168,"gc":[329,220,220],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":8720,"sharedPasses":2180,"driverPasses":2180,"browserPasses":2180}
|
||||
{"cycle":1100,"elapsedSeconds":1467.7018179,"utc":"2026-11-10T18:09:12.819323Z","WorkingSet64":140947456,"PrivateMemorySize64":247029760,"managedBytes":4565624,"allocatedBytes":5151409632,"gc":[332,222,222],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":8800,"sharedPasses":2200,"driverPasses":2200,"browserPasses":2200}
|
||||
{"cycle":1110,"elapsedSeconds":1481.0477388,"utc":"2026-11-10T18:09:26.1698485Z","WorkingSet64":137891840,"PrivateMemorySize64":247029760,"managedBytes":4630816,"allocatedBytes":5198224288,"gc":[335,224,224],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":8880,"sharedPasses":2220,"driverPasses":2220,"browserPasses":2220}
|
||||
{"cycle":1120,"elapsedSeconds":1494.3852703,"utc":"2026-11-10T18:09:39.5051633Z","WorkingSet64":137891840,"PrivateMemorySize64":247029760,"managedBytes":4517056,"allocatedBytes":5244950888,"gc":[338,226,226],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":8960,"sharedPasses":2240,"driverPasses":2240,"browserPasses":2240}
|
||||
{"cycle":1130,"elapsedSeconds":1507.7647938,"utc":"2026-11-10T18:09:52.885106Z","WorkingSet64":138997760,"PrivateMemorySize64":247029760,"managedBytes":4650240,"allocatedBytes":5292768256,"gc":[341,228,228],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":9040,"sharedPasses":2260,"driverPasses":2260,"browserPasses":2260}
|
||||
{"cycle":1140,"elapsedSeconds":1521.0713321,"utc":"2026-11-10T18:10:06.1916721Z","WorkingSet64":137912320,"PrivateMemorySize64":247029760,"managedBytes":4509544,"allocatedBytes":5339450904,"gc":[344,230,230],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":9120,"sharedPasses":2280,"driverPasses":2280,"browserPasses":2280}
|
||||
{"cycle":1150,"elapsedSeconds":1534.435738,"utc":"2026-11-10T18:10:19.5605195Z","WorkingSet64":138235904,"PrivateMemorySize64":247009280,"managedBytes":4516472,"allocatedBytes":5386173568,"gc":[347,232,232],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":9200,"sharedPasses":2300,"driverPasses":2300,"browserPasses":2300}
|
||||
{"cycle":1160,"elapsedSeconds":1547.5446634,"utc":"2026-11-10T18:10:32.6620821Z","WorkingSet64":138452992,"PrivateMemorySize64":247009280,"managedBytes":4508384,"allocatedBytes":5432887232,"gc":[350,234,234],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":9280,"sharedPasses":2320,"driverPasses":2320,"browserPasses":2320}
|
||||
{"cycle":1170,"elapsedSeconds":1560.888704,"utc":"2026-11-10T18:10:46.0179428Z","WorkingSet64":138399744,"PrivateMemorySize64":247029760,"managedBytes":4630488,"allocatedBytes":5479735384,"gc":[353,236,236],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":9360,"sharedPasses":2340,"driverPasses":2340,"browserPasses":2340}
|
||||
{"cycle":1180,"elapsedSeconds":1574.2072805,"utc":"2026-11-10T18:10:59.3255877Z","WorkingSet64":138596352,"PrivateMemorySize64":247029760,"managedBytes":4631992,"allocatedBytes":5526436568,"gc":[356,238,238],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":9440,"sharedPasses":2360,"driverPasses":2360,"browserPasses":2360}
|
||||
{"cycle":1190,"elapsedSeconds":1587.5311377,"utc":"2026-11-10T18:11:12.6501734Z","WorkingSet64":139821056,"PrivateMemorySize64":247009280,"managedBytes":4516296,"allocatedBytes":5573144160,"gc":[359,240,240],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":9520,"sharedPasses":2380,"driverPasses":2380,"browserPasses":2380}
|
||||
{"cycle":1200,"elapsedSeconds":1600.8550938,"utc":"2026-11-10T18:11:25.9739753Z","WorkingSet64":139821056,"PrivateMemorySize64":247009280,"managedBytes":4516664,"allocatedBytes":5619855784,"gc":[362,242,242],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":9600,"sharedPasses":2400,"driverPasses":2400,"browserPasses":2400}
|
||||
{"cycle":1210,"elapsedSeconds":1614.1373595,"utc":"2026-11-10T18:11:39.2552215Z","WorkingSet64":137674752,"PrivateMemorySize64":247136256,"managedBytes":4736656,"allocatedBytes":5666803624,"gc":[365,244,244],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":9680,"sharedPasses":2420,"driverPasses":2420,"browserPasses":2420}
|
||||
{"cycle":1220,"elapsedSeconds":1627.4900653,"utc":"2026-11-10T18:11:52.6144638Z","WorkingSet64":137674752,"PrivateMemorySize64":247074816,"managedBytes":4516624,"allocatedBytes":5713537352,"gc":[368,246,246],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":9760,"sharedPasses":2440,"driverPasses":2440,"browserPasses":2440}
|
||||
{"cycle":1230,"elapsedSeconds":1640.8110675,"utc":"2026-11-10T18:12:05.9285163Z","WorkingSet64":137711616,"PrivateMemorySize64":247074816,"managedBytes":4516928,"allocatedBytes":5760231648,"gc":[371,248,248],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":9840,"sharedPasses":2460,"driverPasses":2460,"browserPasses":2460}
|
||||
{"cycle":1240,"elapsedSeconds":1654.1099118,"utc":"2026-11-10T18:12:19.2306735Z","WorkingSet64":136536064,"PrivateMemorySize64":247095296,"managedBytes":4574048,"allocatedBytes":5806975552,"gc":[374,250,250],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":9920,"sharedPasses":2480,"driverPasses":2480,"browserPasses":2480}
|
||||
{"cycle":1250,"elapsedSeconds":1667.3634254,"utc":"2026-11-10T18:12:32.4815789Z","WorkingSet64":136585216,"PrivateMemorySize64":247095296,"managedBytes":4564816,"allocatedBytes":5853684944,"gc":[377,252,252],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":10000,"sharedPasses":2500,"driverPasses":2500,"browserPasses":2500}
|
||||
{"cycle":1260,"elapsedSeconds":1680.6600475,"utc":"2026-11-10T18:12:45.7831188Z","WorkingSet64":137273344,"PrivateMemorySize64":247115776,"managedBytes":4530104,"allocatedBytes":5900390488,"gc":[380,254,254],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":10080,"sharedPasses":2520,"driverPasses":2520,"browserPasses":2520}
|
||||
{"cycle":1270,"elapsedSeconds":1693.9695244,"utc":"2026-11-10T18:12:59.095321Z","WorkingSet64":137273344,"PrivateMemorySize64":247095296,"managedBytes":4556272,"allocatedBytes":5947116888,"gc":[383,256,256],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":10160,"sharedPasses":2540,"driverPasses":2540,"browserPasses":2540}
|
||||
{"cycle":1280,"elapsedSeconds":1707.2398061,"utc":"2026-11-10T18:13:12.3625522Z","WorkingSet64":137027584,"PrivateMemorySize64":247095296,"managedBytes":4647376,"allocatedBytes":5993901016,"gc":[386,258,258],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":10240,"sharedPasses":2560,"driverPasses":2560,"browserPasses":2560}
|
||||
{"cycle":1290,"elapsedSeconds":1720.5002753,"utc":"2026-11-10T18:13:25.6278775Z","WorkingSet64":137027584,"PrivateMemorySize64":247095296,"managedBytes":4646368,"allocatedBytes":6040626600,"gc":[389,260,260],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":10320,"sharedPasses":2580,"driverPasses":2580,"browserPasses":2580}
|
||||
{"cycle":1300,"elapsedSeconds":1733.7836483,"utc":"2026-11-10T18:13:38.9012015Z","WorkingSet64":138080256,"PrivateMemorySize64":247095296,"managedBytes":4630144,"allocatedBytes":6087350664,"gc":[392,262,262],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":10400,"sharedPasses":2600,"driverPasses":2600,"browserPasses":2600}
|
||||
{"cycle":1310,"elapsedSeconds":1747.1154746,"utc":"2026-11-10T18:13:52.2332268Z","WorkingSet64":136679424,"PrivateMemorySize64":247095296,"managedBytes":4538432,"allocatedBytes":6134076880,"gc":[395,264,264],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":10480,"sharedPasses":2620,"driverPasses":2620,"browserPasses":2620}
|
||||
{"cycle":1320,"elapsedSeconds":1760.4065997,"utc":"2026-11-10T18:14:05.5242815Z","WorkingSet64":136843264,"PrivateMemorySize64":247095296,"managedBytes":4648144,"allocatedBytes":6180893088,"gc":[398,266,266],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":10560,"sharedPasses":2640,"driverPasses":2640,"browserPasses":2640}
|
||||
{"cycle":1330,"elapsedSeconds":1773.5803288,"utc":"2026-11-10T18:14:18.7020362Z","WorkingSet64":136957952,"PrivateMemorySize64":247095296,"managedBytes":4631176,"allocatedBytes":6227619776,"gc":[401,268,268],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":10640,"sharedPasses":2660,"driverPasses":2660,"browserPasses":2660}
|
||||
{"cycle":1340,"elapsedSeconds":1786.9233177,"utc":"2026-11-10T18:14:32.0412017Z","WorkingSet64":136998912,"PrivateMemorySize64":247095296,"managedBytes":4522856,"allocatedBytes":6274339168,"gc":[404,270,270],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":10720,"sharedPasses":2680,"driverPasses":2680,"browserPasses":2680}
|
||||
{"cycle":1350,"elapsedSeconds":1800.2281551,"utc":"2026-11-10T18:14:45.3512335Z","WorkingSet64":137056256,"PrivateMemorySize64":247095296,"managedBytes":4566328,"allocatedBytes":6321085872,"gc":[407,272,272],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":10800,"sharedPasses":2700,"driverPasses":2700,"browserPasses":2700}
|
||||
{"cycle":1360,"elapsedSeconds":1813.5769478,"utc":"2026-11-10T18:14:58.6947676Z","WorkingSet64":138088448,"PrivateMemorySize64":247095296,"managedBytes":4638440,"allocatedBytes":6368827328,"gc":[410,274,274],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":10880,"sharedPasses":2720,"driverPasses":2720,"browserPasses":2720}
|
||||
{"cycle":1370,"elapsedSeconds":1826.8670103,"utc":"2026-11-10T18:15:11.990283Z","WorkingSet64":138088448,"PrivateMemorySize64":247074816,"managedBytes":4516656,"allocatedBytes":6415549376,"gc":[413,276,276],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":10960,"sharedPasses":2740,"driverPasses":2740,"browserPasses":2740}
|
||||
{"cycle":1380,"elapsedSeconds":1840.3092567,"utc":"2026-11-10T18:15:25.4338969Z","WorkingSet64":136617984,"PrivateMemorySize64":247074816,"managedBytes":4517688,"allocatedBytes":6462240480,"gc":[416,278,278],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":11040,"sharedPasses":2760,"driverPasses":2760,"browserPasses":2760}
|
||||
{"cycle":1390,"elapsedSeconds":1853.9394438,"utc":"2026-11-10T18:15:39.0664869Z","WorkingSet64":136683520,"PrivateMemorySize64":247115776,"managedBytes":4720288,"allocatedBytes":6509119544,"gc":[419,280,280],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":11120,"sharedPasses":2780,"driverPasses":2780,"browserPasses":2780}
|
||||
{"cycle":1400,"elapsedSeconds":1867.2304519,"utc":"2026-11-10T18:15:52.3545296Z","WorkingSet64":136683520,"PrivateMemorySize64":247095296,"managedBytes":4565472,"allocatedBytes":6555810712,"gc":[422,282,282],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":11200,"sharedPasses":2800,"driverPasses":2800,"browserPasses":2800}
|
||||
{"cycle":1410,"elapsedSeconds":1880.5473124,"utc":"2026-11-10T18:16:05.6704664Z","WorkingSet64":136499200,"PrivateMemorySize64":247095296,"managedBytes":4517688,"allocatedBytes":6602519160,"gc":[425,284,284],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":11280,"sharedPasses":2820,"driverPasses":2820,"browserPasses":2820}
|
||||
{"cycle":1420,"elapsedSeconds":1893.8702526,"utc":"2026-11-10T18:16:18.9954993Z","WorkingSet64":136552448,"PrivateMemorySize64":247095296,"managedBytes":4631536,"allocatedBytes":6649345896,"gc":[428,286,286],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":11360,"sharedPasses":2840,"driverPasses":2840,"browserPasses":2840}
|
||||
{"cycle":1430,"elapsedSeconds":1907.146463,"utc":"2026-11-10T18:16:32.2737756Z","WorkingSet64":136560640,"PrivateMemorySize64":247095296,"managedBytes":4631424,"allocatedBytes":6696047192,"gc":[431,288,288],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":11440,"sharedPasses":2860,"driverPasses":2860,"browserPasses":2860}
|
||||
{"cycle":1440,"elapsedSeconds":1920.4070682,"utc":"2026-11-10T18:16:45.5295848Z","WorkingSet64":134692864,"PrivateMemorySize64":247115776,"managedBytes":4655832,"allocatedBytes":6742830296,"gc":[434,290,290],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":11520,"sharedPasses":2880,"driverPasses":2880,"browserPasses":2880}
|
||||
{"cycle":1450,"elapsedSeconds":1933.6768004,"utc":"2026-11-10T18:16:58.7942646Z","WorkingSet64":134692864,"PrivateMemorySize64":247115776,"managedBytes":4621528,"allocatedBytes":6789528608,"gc":[437,292,292],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":11600,"sharedPasses":2900,"driverPasses":2900,"browserPasses":2900}
|
||||
{"cycle":1460,"elapsedSeconds":1946.8110388,"utc":"2026-11-10T18:17:11.935328Z","WorkingSet64":137175040,"PrivateMemorySize64":247136256,"managedBytes":4596816,"allocatedBytes":6836289016,"gc":[440,294,294],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":11680,"sharedPasses":2920,"driverPasses":2920,"browserPasses":2920}
|
||||
{"cycle":1470,"elapsedSeconds":1960.2422696,"utc":"2026-11-10T18:17:25.3601895Z","WorkingSet64":137175040,"PrivateMemorySize64":247095296,"managedBytes":4580840,"allocatedBytes":6882995992,"gc":[443,296,296],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":11760,"sharedPasses":2940,"driverPasses":2940,"browserPasses":2940}
|
||||
{"cycle":1480,"elapsedSeconds":1973.4582607,"utc":"2026-11-10T18:17:38.5772935Z","WorkingSet64":137056256,"PrivateMemorySize64":247074816,"managedBytes":4516656,"allocatedBytes":6929697568,"gc":[446,298,298],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":11840,"sharedPasses":2960,"driverPasses":2960,"browserPasses":2960}
|
||||
{"cycle":1490,"elapsedSeconds":1986.7176141,"utc":"2026-11-10T18:17:51.8424507Z","WorkingSet64":138252288,"PrivateMemorySize64":247095296,"managedBytes":4565920,"allocatedBytes":6976443296,"gc":[449,300,300],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":11920,"sharedPasses":2980,"driverPasses":2980,"browserPasses":2980}
|
||||
{"cycle":1500,"elapsedSeconds":1999.9966044,"utc":"2026-11-10T18:18:05.1254937Z","WorkingSet64":138428416,"PrivateMemorySize64":247095296,"managedBytes":4549504,"allocatedBytes":7023142600,"gc":[452,302,302],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":12000,"sharedPasses":3000,"driverPasses":3000,"browserPasses":3000}
|
||||
{"cycle":1510,"elapsedSeconds":2013.2249533,"utc":"2026-11-10T18:18:18.3471073Z","WorkingSet64":137465856,"PrivateMemorySize64":247074816,"managedBytes":4508888,"allocatedBytes":7069864088,"gc":[455,304,304],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":12080,"sharedPasses":3020,"driverPasses":3020,"browserPasses":3020}
|
||||
{"cycle":1520,"elapsedSeconds":2026.4953922,"utc":"2026-11-10T18:18:31.6147466Z","WorkingSet64":138018816,"PrivateMemorySize64":247095296,"managedBytes":4646720,"allocatedBytes":7116708424,"gc":[458,306,306],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":12160,"sharedPasses":3040,"driverPasses":3040,"browserPasses":3040}
|
||||
{"cycle":1530,"elapsedSeconds":2039.7154802,"utc":"2026-11-10T18:18:44.8355938Z","WorkingSet64":138018816,"PrivateMemorySize64":247115776,"managedBytes":4639832,"allocatedBytes":7163431624,"gc":[461,308,308],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":12240,"sharedPasses":3060,"driverPasses":3060,"browserPasses":3060}
|
||||
{"cycle":1540,"elapsedSeconds":2052.9307959,"utc":"2026-11-10T18:18:58.048379Z","WorkingSet64":138080256,"PrivateMemorySize64":247115776,"managedBytes":4596336,"allocatedBytes":7210151480,"gc":[464,310,310],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":12320,"sharedPasses":3080,"driverPasses":3080,"browserPasses":3080}
|
||||
{"cycle":1550,"elapsedSeconds":2066.1629511,"utc":"2026-11-10T18:19:11.2861941Z","WorkingSet64":136790016,"PrivateMemorySize64":247074816,"managedBytes":4582816,"allocatedBytes":7256841728,"gc":[467,312,312],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":12400,"sharedPasses":3100,"driverPasses":3100,"browserPasses":3100}
|
||||
{"cycle":1560,"elapsedSeconds":2079.2794502,"utc":"2026-11-10T18:19:24.398575Z","WorkingSet64":136790016,"PrivateMemorySize64":247074816,"managedBytes":4582712,"allocatedBytes":7303526936,"gc":[470,314,314],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":12480,"sharedPasses":3120,"driverPasses":3120,"browserPasses":3120}
|
||||
{"cycle":1570,"elapsedSeconds":2092.5482995,"utc":"2026-11-10T18:19:37.6719344Z","WorkingSet64":136790016,"PrivateMemorySize64":247074816,"managedBytes":4583824,"allocatedBytes":7350219296,"gc":[473,316,316],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":12560,"sharedPasses":3140,"driverPasses":3140,"browserPasses":3140}
|
||||
{"cycle":1580,"elapsedSeconds":2105.8147467,"utc":"2026-11-10T18:19:50.9423122Z","WorkingSet64":139313152,"PrivateMemorySize64":247074816,"managedBytes":4582048,"allocatedBytes":7397889688,"gc":[476,318,318],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":12640,"sharedPasses":3160,"driverPasses":3160,"browserPasses":3160}
|
||||
{"cycle":1590,"elapsedSeconds":2119.0615168,"utc":"2026-11-10T18:20:04.1819379Z","WorkingSet64":139313152,"PrivateMemorySize64":247074816,"managedBytes":4582016,"allocatedBytes":7444610984,"gc":[479,320,320],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":12720,"sharedPasses":3180,"driverPasses":3180,"browserPasses":3180}
|
||||
{"cycle":1600,"elapsedSeconds":2132.293745,"utc":"2026-11-10T18:20:17.4119578Z","WorkingSet64":139325440,"PrivateMemorySize64":247095296,"managedBytes":4614896,"allocatedBytes":7491375976,"gc":[482,322,322],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":12800,"sharedPasses":3200,"driverPasses":3200,"browserPasses":3200}
|
||||
{"cycle":1610,"elapsedSeconds":2145.5361456,"utc":"2026-11-10T18:20:30.6539337Z","WorkingSet64":138514432,"PrivateMemorySize64":247095296,"managedBytes":4647528,"allocatedBytes":7538120008,"gc":[485,324,324],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":12880,"sharedPasses":3220,"driverPasses":3220,"browserPasses":3220}
|
||||
{"cycle":1620,"elapsedSeconds":2158.737018,"utc":"2026-11-10T18:20:43.8589913Z","WorkingSet64":138514432,"PrivateMemorySize64":247095296,"managedBytes":4647592,"allocatedBytes":7584840152,"gc":[488,326,326],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":12960,"sharedPasses":3240,"driverPasses":3240,"browserPasses":3240}
|
||||
{"cycle":1630,"elapsedSeconds":2171.8600863,"utc":"2026-11-10T18:20:56.9811391Z","WorkingSet64":138514432,"PrivateMemorySize64":247095296,"managedBytes":4631144,"allocatedBytes":7631578368,"gc":[491,328,328],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":13040,"sharedPasses":3260,"driverPasses":3260,"browserPasses":3260}
|
||||
{"cycle":1640,"elapsedSeconds":2185.1105072,"utc":"2026-11-10T18:21:10.2337275Z","WorkingSet64":138829824,"PrivateMemorySize64":247115776,"managedBytes":4639544,"allocatedBytes":7678288784,"gc":[494,330,330],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":13120,"sharedPasses":3280,"driverPasses":3280,"browserPasses":3280}
|
||||
{"cycle":1650,"elapsedSeconds":2198.3165558,"utc":"2026-11-10T18:21:23.4452832Z","WorkingSet64":137834496,"PrivateMemorySize64":247095296,"managedBytes":4630216,"allocatedBytes":7724967608,"gc":[497,332,332],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":13200,"sharedPasses":3300,"driverPasses":3300,"browserPasses":3300}
|
||||
{"cycle":1660,"elapsedSeconds":2211.5554794,"utc":"2026-11-10T18:21:36.6819109Z","WorkingSet64":137834496,"PrivateMemorySize64":247095296,"managedBytes":4630472,"allocatedBytes":7771680552,"gc":[500,334,334],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":13280,"sharedPasses":3320,"driverPasses":3320,"browserPasses":3320}
|
||||
{"cycle":1670,"elapsedSeconds":2224.7569182,"utc":"2026-11-10T18:21:49.8838303Z","WorkingSet64":137834496,"PrivateMemorySize64":247095296,"managedBytes":4648440,"allocatedBytes":7818418288,"gc":[503,336,336],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":13360,"sharedPasses":3340,"driverPasses":3340,"browserPasses":3340}
|
||||
{"cycle":1680,"elapsedSeconds":2237.8689779,"utc":"2026-11-10T18:22:03.0006017Z","WorkingSet64":136896512,"PrivateMemorySize64":247095296,"managedBytes":4646680,"allocatedBytes":7865134736,"gc":[506,338,338],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":13440,"sharedPasses":3360,"driverPasses":3360,"browserPasses":3360}
|
||||
{"cycle":1690,"elapsedSeconds":2251.1051878,"utc":"2026-11-10T18:22:16.2299041Z","WorkingSet64":136962048,"PrivateMemorySize64":247095296,"managedBytes":4630720,"allocatedBytes":7911833728,"gc":[509,340,340],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":13520,"sharedPasses":3380,"driverPasses":3380,"browserPasses":3380}
|
||||
{"cycle":1700,"elapsedSeconds":2264.3999716,"utc":"2026-11-10T18:22:29.5239101Z","WorkingSet64":136962048,"PrivateMemorySize64":247095296,"managedBytes":4647160,"allocatedBytes":7958559744,"gc":[512,342,342],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":13600,"sharedPasses":3400,"driverPasses":3400,"browserPasses":3400}
|
||||
{"cycle":1710,"elapsedSeconds":2277.6834886,"utc":"2026-11-10T18:22:42.8022829Z","WorkingSet64":137003008,"PrivateMemorySize64":247095296,"managedBytes":4647104,"allocatedBytes":8005274288,"gc":[515,344,344],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":13680,"sharedPasses":3420,"driverPasses":3420,"browserPasses":3420}
|
||||
{"cycle":1720,"elapsedSeconds":2290.9457052,"utc":"2026-11-10T18:22:56.0743607Z","WorkingSet64":137302016,"PrivateMemorySize64":247115776,"managedBytes":4656016,"allocatedBytes":8051971648,"gc":[518,346,346],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":13760,"sharedPasses":3440,"driverPasses":3440,"browserPasses":3440}
|
||||
{"cycle":1730,"elapsedSeconds":2304.2362097,"utc":"2026-11-10T18:23:09.3534475Z","WorkingSet64":137314304,"PrivateMemorySize64":247119872,"managedBytes":4647592,"allocatedBytes":8098675856,"gc":[521,348,348],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":13840,"sharedPasses":3460,"driverPasses":3460,"browserPasses":3460}
|
||||
{"cycle":1740,"elapsedSeconds":2317.524939,"utc":"2026-11-10T18:23:22.6419748Z","WorkingSet64":137314304,"PrivateMemorySize64":247119872,"managedBytes":4647504,"allocatedBytes":8145511144,"gc":[524,350,350],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":13920,"sharedPasses":3480,"driverPasses":3480,"browserPasses":3480}
|
||||
{"cycle":1750,"elapsedSeconds":2330.8427595,"utc":"2026-11-10T18:23:35.9687964Z","WorkingSet64":135757824,"PrivateMemorySize64":247119872,"managedBytes":4646680,"allocatedBytes":8192212584,"gc":[527,352,352],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":14000,"sharedPasses":3500,"driverPasses":3500,"browserPasses":3500}
|
||||
{"cycle":1760,"elapsedSeconds":2344.1646584,"utc":"2026-11-10T18:23:49.2885992Z","WorkingSet64":136843264,"PrivateMemorySize64":247119872,"managedBytes":4515968,"allocatedBytes":8238919152,"gc":[530,354,354],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":14080,"sharedPasses":3520,"driverPasses":3520,"browserPasses":3520}
|
||||
{"cycle":1770,"elapsedSeconds":2357.53567,"utc":"2026-11-10T18:24:02.6642915Z","WorkingSet64":136843264,"PrivateMemorySize64":247099392,"managedBytes":4500584,"allocatedBytes":8285601912,"gc":[533,356,356],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":14160,"sharedPasses":3540,"driverPasses":3540,"browserPasses":3540}
|
||||
{"cycle":1780,"elapsedSeconds":2370.832052,"utc":"2026-11-10T18:24:15.9519118Z","WorkingSet64":136511488,"PrivateMemorySize64":247119872,"managedBytes":4614720,"allocatedBytes":8332417608,"gc":[536,358,358],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":14240,"sharedPasses":3560,"driverPasses":3560,"browserPasses":3560}
|
||||
{"cycle":1790,"elapsedSeconds":2384.0386193,"utc":"2026-11-10T18:24:29.1629364Z","WorkingSet64":136511488,"PrivateMemorySize64":247119872,"managedBytes":4631640,"allocatedBytes":8379137872,"gc":[539,360,360],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":14320,"sharedPasses":3580,"driverPasses":3580,"browserPasses":3580}
|
||||
{"cycle":1800,"elapsedSeconds":2397.3494585,"utc":"2026-11-10T18:24:42.4747031Z","WorkingSet64":136511488,"PrivateMemorySize64":247119872,"managedBytes":4520824,"allocatedBytes":8425838320,"gc":[542,362,362],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":14400,"sharedPasses":3600,"driverPasses":3600,"browserPasses":3600}
|
||||
{"cycle":1810,"elapsedSeconds":2410.699165,"utc":"2026-11-10T18:24:55.8166934Z","WorkingSet64":138088448,"PrivateMemorySize64":247787520,"managedBytes":4522072,"allocatedBytes":8473495664,"gc":[545,364,364],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":14480,"sharedPasses":3620,"driverPasses":3620,"browserPasses":3620}
|
||||
{"cycle":1820,"elapsedSeconds":2424.0325256,"utc":"2026-11-10T18:25:09.1499308Z","WorkingSet64":136626176,"PrivateMemorySize64":247787520,"managedBytes":4624928,"allocatedBytes":8520306600,"gc":[548,366,366],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":14560,"sharedPasses":3640,"driverPasses":3640,"browserPasses":3640}
|
||||
{"cycle":1830,"elapsedSeconds":2437.1159714,"utc":"2026-11-10T18:25:22.2332942Z","WorkingSet64":137408512,"PrivateMemorySize64":247787520,"managedBytes":4640544,"allocatedBytes":8567007904,"gc":[551,368,368],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":14640,"sharedPasses":3660,"driverPasses":3660,"browserPasses":3660}
|
||||
{"cycle":1840,"elapsedSeconds":2450.4753988,"utc":"2026-11-10T18:25:35.6000775Z","WorkingSet64":137519104,"PrivateMemorySize64":247808000,"managedBytes":4655224,"allocatedBytes":8613728360,"gc":[554,370,370],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":14720,"sharedPasses":3680,"driverPasses":3680,"browserPasses":3680}
|
||||
{"cycle":1850,"elapsedSeconds":2463.8130385,"utc":"2026-11-10T18:25:48.9359084Z","WorkingSet64":137895936,"PrivateMemorySize64":247767040,"managedBytes":4516904,"allocatedBytes":8660447192,"gc":[557,372,372],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":14800,"sharedPasses":3700,"driverPasses":3700,"browserPasses":3700}
|
||||
{"cycle":1860,"elapsedSeconds":2477.0625223,"utc":"2026-11-10T18:26:02.1885177Z","WorkingSet64":137990144,"PrivateMemorySize64":247787520,"managedBytes":4581728,"allocatedBytes":8707236648,"gc":[560,374,374],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":14880,"sharedPasses":3720,"driverPasses":3720,"browserPasses":3720}
|
||||
{"cycle":1870,"elapsedSeconds":2490.4390181,"utc":"2026-11-10T18:26:15.5674593Z","WorkingSet64":137990144,"PrivateMemorySize64":247787520,"managedBytes":4630152,"allocatedBytes":8753998360,"gc":[563,376,376],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":14960,"sharedPasses":3740,"driverPasses":3740,"browserPasses":3740}
|
||||
{"cycle":1880,"elapsedSeconds":2503.7854531,"utc":"2026-11-10T18:26:28.9025999Z","WorkingSet64":138797056,"PrivateMemorySize64":247787520,"managedBytes":4639424,"allocatedBytes":8800701288,"gc":[566,378,378],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":15040,"sharedPasses":3760,"driverPasses":3760,"browserPasses":3760}
|
||||
{"cycle":1890,"elapsedSeconds":2517.0796801,"utc":"2026-11-10T18:26:42.2012909Z","WorkingSet64":138649600,"PrivateMemorySize64":247787520,"managedBytes":4646672,"allocatedBytes":8847401880,"gc":[569,380,380],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":15120,"sharedPasses":3780,"driverPasses":3780,"browserPasses":3780}
|
||||
{"cycle":1900,"elapsedSeconds":2530.4471528,"utc":"2026-11-10T18:26:55.5646282Z","WorkingSet64":138711040,"PrivateMemorySize64":247767040,"managedBytes":4582280,"allocatedBytes":8894102608,"gc":[572,382,382],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":15200,"sharedPasses":3800,"driverPasses":3800,"browserPasses":3800}
|
||||
{"cycle":1910,"elapsedSeconds":2543.804551,"utc":"2026-11-10T18:27:08.9219429Z","WorkingSet64":138711040,"PrivateMemorySize64":247787520,"managedBytes":4649488,"allocatedBytes":8940861936,"gc":[575,384,384],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":15280,"sharedPasses":3820,"driverPasses":3820,"browserPasses":3820}
|
||||
{"cycle":1920,"elapsedSeconds":2557.1152703,"utc":"2026-11-10T18:27:22.2374334Z","WorkingSet64":138645504,"PrivateMemorySize64":247767040,"managedBytes":4582824,"allocatedBytes":8987563808,"gc":[578,386,386],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":15360,"sharedPasses":3840,"driverPasses":3840,"browserPasses":3840}
|
||||
{"cycle":1930,"elapsedSeconds":2570.4020941,"utc":"2026-11-10T18:27:35.5302264Z","WorkingSet64":138645504,"PrivateMemorySize64":247787520,"managedBytes":4646376,"allocatedBytes":9034315952,"gc":[581,388,388],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":15440,"sharedPasses":3860,"driverPasses":3860,"browserPasses":3860}
|
||||
{"cycle":1940,"elapsedSeconds":2583.7163414,"utc":"2026-11-10T18:27:48.8370152Z","WorkingSet64":138711040,"PrivateMemorySize64":247808000,"managedBytes":4637640,"allocatedBytes":9081049264,"gc":[584,390,390],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":15520,"sharedPasses":3880,"driverPasses":3880,"browserPasses":3880}
|
||||
{"cycle":1950,"elapsedSeconds":2597.1614868,"utc":"2026-11-10T18:28:02.2907674Z","WorkingSet64":135892992,"PrivateMemorySize64":247787520,"managedBytes":4646624,"allocatedBytes":9127878592,"gc":[587,392,392],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":15600,"sharedPasses":3900,"driverPasses":3900,"browserPasses":3900}
|
||||
{"cycle":1960,"elapsedSeconds":2610.5638849,"utc":"2026-11-10T18:28:15.6838969Z","WorkingSet64":135892992,"PrivateMemorySize64":247787520,"managedBytes":4646904,"allocatedBytes":9174566264,"gc":[590,394,394],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":15680,"sharedPasses":3920,"driverPasses":3920,"browserPasses":3920}
|
||||
{"cycle":1970,"elapsedSeconds":2623.8831112,"utc":"2026-11-10T18:28:29.0093064Z","WorkingSet64":135892992,"PrivateMemorySize64":247787520,"managedBytes":4631608,"allocatedBytes":9221285464,"gc":[593,396,396],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":15760,"sharedPasses":3940,"driverPasses":3940,"browserPasses":3940}
|
||||
{"cycle":1980,"elapsedSeconds":2637.2218407,"utc":"2026-11-10T18:28:42.3512126Z","WorkingSet64":135921664,"PrivateMemorySize64":247808000,"managedBytes":4703232,"allocatedBytes":9268046432,"gc":[596,398,398],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":15840,"sharedPasses":3960,"driverPasses":3960,"browserPasses":3960}
|
||||
{"cycle":1990,"elapsedSeconds":2650.539745,"utc":"2026-11-10T18:28:55.6668257Z","WorkingSet64":135913472,"PrivateMemorySize64":247767040,"managedBytes":4581872,"allocatedBytes":9314781216,"gc":[599,400,400],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":15920,"sharedPasses":3980,"driverPasses":3980,"browserPasses":3980}
|
||||
{"cycle":2000,"elapsedSeconds":2663.8441782,"utc":"2026-11-10T18:29:08.9695541Z","WorkingSet64":135913472,"PrivateMemorySize64":247767040,"managedBytes":4582392,"allocatedBytes":9361474560,"gc":[602,402,402],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":16000,"sharedPasses":4000,"driverPasses":4000,"browserPasses":4000}
|
||||
{"cycle":2010,"elapsedSeconds":2677.1740281,"utc":"2026-11-10T18:29:22.2948528Z","WorkingSet64":138665984,"PrivateMemorySize64":247808000,"managedBytes":4686240,"allocatedBytes":9408277472,"gc":[605,404,404],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":16080,"sharedPasses":4020,"driverPasses":4020,"browserPasses":4020}
|
||||
{"cycle":2020,"elapsedSeconds":2690.4681673,"utc":"2026-11-10T18:29:35.5947545Z","WorkingSet64":137682944,"PrivateMemorySize64":247787520,"managedBytes":4630056,"allocatedBytes":9454979536,"gc":[608,406,406],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":16160,"sharedPasses":4040,"driverPasses":4040,"browserPasses":4040}
|
||||
{"cycle":2030,"elapsedSeconds":2703.7962157,"utc":"2026-11-10T18:29:48.9258312Z","WorkingSet64":138342400,"PrivateMemorySize64":247951360,"managedBytes":4647752,"allocatedBytes":9502665528,"gc":[611,408,408],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":16240,"sharedPasses":4060,"driverPasses":4060,"browserPasses":4060}
|
||||
{"cycle":2040,"elapsedSeconds":2717.1308802,"utc":"2026-11-10T18:30:02.2532544Z","WorkingSet64":138731520,"PrivateMemorySize64":248270848,"managedBytes":4521888,"allocatedBytes":9549349232,"gc":[614,410,410],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":16320,"sharedPasses":4080,"driverPasses":4080,"browserPasses":4080}
|
||||
{"cycle":2050,"elapsedSeconds":2730.3370295,"utc":"2026-11-10T18:30:15.4646329Z","WorkingSet64":138731520,"PrivateMemorySize64":248270848,"managedBytes":4565408,"allocatedBytes":9596105064,"gc":[617,412,412],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":16400,"sharedPasses":4100,"driverPasses":4100,"browserPasses":4100}
|
||||
{"cycle":2060,"elapsedSeconds":2743.6617215,"utc":"2026-11-10T18:30:28.7886336Z","WorkingSet64":138350592,"PrivateMemorySize64":248270848,"managedBytes":4581448,"allocatedBytes":9642823376,"gc":[620,414,414],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":16480,"sharedPasses":4120,"driverPasses":4120,"browserPasses":4120}
|
||||
{"cycle":2070,"elapsedSeconds":2756.9877393,"utc":"2026-11-10T18:30:42.1124635Z","WorkingSet64":140828672,"PrivateMemorySize64":248291328,"managedBytes":4573664,"allocatedBytes":9689559584,"gc":[623,416,416],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":16560,"sharedPasses":4140,"driverPasses":4140,"browserPasses":4140}
|
||||
{"cycle":2080,"elapsedSeconds":2770.3042485,"utc":"2026-11-10T18:30:55.429929Z","WorkingSet64":140828672,"PrivateMemorySize64":248270848,"managedBytes":4582080,"allocatedBytes":9736275368,"gc":[626,418,418],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":16640,"sharedPasses":4160,"driverPasses":4160,"browserPasses":4160}
|
||||
{"cycle":2090,"elapsedSeconds":2783.6840022,"utc":"2026-11-10T18:31:08.8030717Z","WorkingSet64":140148736,"PrivateMemorySize64":248291328,"managedBytes":4564544,"allocatedBytes":9783022480,"gc":[629,420,420],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":16720,"sharedPasses":4180,"driverPasses":4180,"browserPasses":4180}
|
||||
{"cycle":2100,"elapsedSeconds":2797.1979142,"utc":"2026-11-10T18:31:22.315798Z","WorkingSet64":140251136,"PrivateMemorySize64":248291328,"managedBytes":4778032,"allocatedBytes":9829933448,"gc":[632,422,422],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":16800,"sharedPasses":4200,"driverPasses":4200,"browserPasses":4200}
|
||||
{"cycle":2110,"elapsedSeconds":2810.2095128,"utc":"2026-11-10T18:31:35.3268782Z","WorkingSet64":140251136,"PrivateMemorySize64":248270848,"managedBytes":4614528,"allocatedBytes":9876638648,"gc":[635,424,424],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":16880,"sharedPasses":4220,"driverPasses":4220,"browserPasses":4220}
|
||||
{"cycle":2120,"elapsedSeconds":2823.4268007,"utc":"2026-11-10T18:31:48.5578323Z","WorkingSet64":139083776,"PrivateMemorySize64":248270848,"managedBytes":4522416,"allocatedBytes":9923346424,"gc":[638,426,426],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":16960,"sharedPasses":4240,"driverPasses":4240,"browserPasses":4240}
|
||||
{"cycle":2130,"elapsedSeconds":2836.7069192,"utc":"2026-11-10T18:32:01.8244195Z","WorkingSet64":139083776,"PrivateMemorySize64":248270848,"managedBytes":4514056,"allocatedBytes":9970035088,"gc":[641,428,428],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":17040,"sharedPasses":4260,"driverPasses":4260,"browserPasses":4260}
|
||||
{"cycle":2140,"elapsedSeconds":2850.0267526,"utc":"2026-11-10T18:32:15.1454111Z","WorkingSet64":139657216,"PrivateMemorySize64":248270848,"managedBytes":4647824,"allocatedBytes":10016869552,"gc":[644,430,430],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":17120,"sharedPasses":4280,"driverPasses":4280,"browserPasses":4280}
|
||||
{"cycle":2150,"elapsedSeconds":2863.3821078,"utc":"2026-11-10T18:32:28.5143232Z","WorkingSet64":139657216,"PrivateMemorySize64":248270848,"managedBytes":4631064,"allocatedBytes":10063589400,"gc":[647,432,432],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":17200,"sharedPasses":4300,"driverPasses":4300,"browserPasses":4300}
|
||||
{"cycle":2160,"elapsedSeconds":2876.6553751,"utc":"2026-11-10T18:32:41.785683Z","WorkingSet64":139042816,"PrivateMemorySize64":248377344,"managedBytes":4752624,"allocatedBytes":10110429368,"gc":[650,434,434],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":17280,"sharedPasses":4320,"driverPasses":4320,"browserPasses":4320}
|
||||
{"cycle":2170,"elapsedSeconds":2889.9759578,"utc":"2026-11-10T18:32:55.1026875Z","WorkingSet64":139096064,"PrivateMemorySize64":248356864,"managedBytes":4627528,"allocatedBytes":10157130152,"gc":[653,436,436],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":17360,"sharedPasses":4340,"driverPasses":4340,"browserPasses":4340}
|
||||
{"cycle":2180,"elapsedSeconds":2903.2588842,"utc":"2026-11-10T18:33:08.3853269Z","WorkingSet64":139132928,"PrivateMemorySize64":248315904,"managedBytes":4516080,"allocatedBytes":10203887920,"gc":[656,438,438],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":17440,"sharedPasses":4360,"driverPasses":4360,"browserPasses":4360}
|
||||
{"cycle":2190,"elapsedSeconds":2916.5802093,"utc":"2026-11-10T18:33:21.7089622Z","WorkingSet64":138510336,"PrivateMemorySize64":248336384,"managedBytes":4567544,"allocatedBytes":10250662816,"gc":[659,440,440],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":17520,"sharedPasses":4380,"driverPasses":4380,"browserPasses":4380}
|
||||
{"cycle":2200,"elapsedSeconds":2929.8721086,"utc":"2026-11-10T18:33:34.9894493Z","WorkingSet64":138752000,"PrivateMemorySize64":248356864,"managedBytes":4558456,"allocatedBytes":10297378040,"gc":[662,442,442],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":17600,"sharedPasses":4400,"driverPasses":4400,"browserPasses":4400}
|
||||
{"cycle":2210,"elapsedSeconds":2943.146189,"utc":"2026-11-10T18:33:48.2642691Z","WorkingSet64":138752000,"PrivateMemorySize64":248356864,"managedBytes":4657320,"allocatedBytes":10344182648,"gc":[665,444,444],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":17680,"sharedPasses":4420,"driverPasses":4420,"browserPasses":4420}
|
||||
{"cycle":2220,"elapsedSeconds":2956.5000095,"utc":"2026-11-10T18:34:01.6211071Z","WorkingSet64":138293248,"PrivateMemorySize64":248356864,"managedBytes":4639480,"allocatedBytes":10390881776,"gc":[668,446,446],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":17760,"sharedPasses":4440,"driverPasses":4440,"browserPasses":4440}
|
||||
{"cycle":2230,"elapsedSeconds":2969.8096515,"utc":"2026-11-10T18:34:14.927071Z","WorkingSet64":138350592,"PrivateMemorySize64":248356864,"managedBytes":4631056,"allocatedBytes":10437697368,"gc":[671,448,448],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":17840,"sharedPasses":4460,"driverPasses":4460,"browserPasses":4460}
|
||||
{"cycle":2240,"elapsedSeconds":2983.0934272,"utc":"2026-11-10T18:34:28.2142358Z","WorkingSet64":139091968,"PrivateMemorySize64":248336384,"managedBytes":4647200,"allocatedBytes":10484410360,"gc":[674,450,450],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":17920,"sharedPasses":4480,"driverPasses":4480,"browserPasses":4480}
|
||||
{"cycle":2250,"elapsedSeconds":2996.4042566,"utc":"2026-11-10T18:34:41.5215897Z","WorkingSet64":139091968,"PrivateMemorySize64":248336384,"managedBytes":4655024,"allocatedBytes":10531117040,"gc":[677,452,452],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":18000,"sharedPasses":4500,"driverPasses":4500,"browserPasses":4500}
|
||||
{"cycle":2260,"elapsedSeconds":3009.7364308,"utc":"2026-11-10T18:34:54.8647018Z","WorkingSet64":138768384,"PrivateMemorySize64":248336384,"managedBytes":4612304,"allocatedBytes":10578820008,"gc":[680,454,454],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":18080,"sharedPasses":4520,"driverPasses":4520,"browserPasses":4520}
|
||||
{"cycle":2270,"elapsedSeconds":3022.8462638,"utc":"2026-11-10T18:35:07.9738308Z","WorkingSet64":138768384,"PrivateMemorySize64":248336384,"managedBytes":4648096,"allocatedBytes":10625559056,"gc":[683,456,456],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":18160,"sharedPasses":4540,"driverPasses":4540,"browserPasses":4540}
|
||||
{"cycle":2280,"elapsedSeconds":3036.0239925,"utc":"2026-11-10T18:35:21.1526264Z","WorkingSet64":138768384,"PrivateMemorySize64":248336384,"managedBytes":4630864,"allocatedBytes":10672261504,"gc":[686,458,458],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":18240,"sharedPasses":4560,"driverPasses":4560,"browserPasses":4560}
|
||||
{"cycle":2290,"elapsedSeconds":3049.3282305,"utc":"2026-11-10T18:35:34.4554672Z","WorkingSet64":138162176,"PrivateMemorySize64":248336384,"managedBytes":4630408,"allocatedBytes":10718984024,"gc":[689,460,460],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":18320,"sharedPasses":4580,"driverPasses":4580,"browserPasses":4580}
|
||||
{"cycle":2300,"elapsedSeconds":3062.5922737,"utc":"2026-11-10T18:35:47.7102829Z","WorkingSet64":137940992,"PrivateMemorySize64":248016896,"managedBytes":4517768,"allocatedBytes":10765702000,"gc":[692,462,462],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":18400,"sharedPasses":4600,"driverPasses":4600,"browserPasses":4600}
|
||||
{"cycle":2310,"elapsedSeconds":3075.7793947,"utc":"2026-11-10T18:36:00.9087939Z","WorkingSet64":137940992,"PrivateMemorySize64":248057856,"managedBytes":4527800,"allocatedBytes":10812416632,"gc":[695,464,464],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":18480,"sharedPasses":4620,"driverPasses":4620,"browserPasses":4620}
|
||||
{"cycle":2320,"elapsedSeconds":3088.9913025,"utc":"2026-11-10T18:36:14.1172178Z","WorkingSet64":137940992,"PrivateMemorySize64":248057856,"managedBytes":4527464,"allocatedBytes":10859101352,"gc":[698,466,466],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":18560,"sharedPasses":4640,"driverPasses":4640,"browserPasses":4640}
|
||||
{"cycle":2330,"elapsedSeconds":3102.197096,"utc":"2026-11-10T18:36:27.318052Z","WorkingSet64":137347072,"PrivateMemorySize64":248037376,"managedBytes":4585296,"allocatedBytes":10905852872,"gc":[701,468,468],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":18640,"sharedPasses":4660,"driverPasses":4660,"browserPasses":4660}
|
||||
{"cycle":2340,"elapsedSeconds":3115.528955,"utc":"2026-11-10T18:36:40.6542961Z","WorkingSet64":137347072,"PrivateMemorySize64":248037376,"managedBytes":4584520,"allocatedBytes":10952547960,"gc":[704,470,470],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":18720,"sharedPasses":4680,"driverPasses":4680,"browserPasses":4680}
|
||||
{"cycle":2350,"elapsedSeconds":3128.7993068,"utc":"2026-11-10T18:36:53.9276313Z","WorkingSet64":137347072,"PrivateMemorySize64":248037376,"managedBytes":4517728,"allocatedBytes":10999241048,"gc":[707,472,472],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":18800,"sharedPasses":4700,"driverPasses":4700,"browserPasses":4700}
|
||||
{"cycle":2360,"elapsedSeconds":3142.0147934,"utc":"2026-11-10T18:37:07.1392823Z","WorkingSet64":136847360,"PrivateMemorySize64":248102912,"managedBytes":4630792,"allocatedBytes":11046066840,"gc":[710,474,474],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":18880,"sharedPasses":4720,"driverPasses":4720,"browserPasses":4720}
|
||||
{"cycle":2370,"elapsedSeconds":3155.3268617,"utc":"2026-11-10T18:37:20.4513441Z","WorkingSet64":136847360,"PrivateMemorySize64":248082432,"managedBytes":4518040,"allocatedBytes":11092779112,"gc":[713,476,476],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":18960,"sharedPasses":4740,"driverPasses":4740,"browserPasses":4740}
|
||||
{"cycle":2380,"elapsedSeconds":3168.6958742,"utc":"2026-11-10T18:37:33.8136022Z","WorkingSet64":136847360,"PrivateMemorySize64":248082432,"managedBytes":4516928,"allocatedBytes":11139485768,"gc":[716,478,478],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":19040,"sharedPasses":4760,"driverPasses":4760,"browserPasses":4760}
|
||||
{"cycle":2390,"elapsedSeconds":3182.0088761,"utc":"2026-11-10T18:37:47.1330212Z","WorkingSet64":137252864,"PrivateMemorySize64":248123392,"managedBytes":4526840,"allocatedBytes":11186207080,"gc":[719,480,480],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":19120,"sharedPasses":4780,"driverPasses":4780,"browserPasses":4780}
|
||||
{"cycle":2400,"elapsedSeconds":3195.2878997,"utc":"2026-11-10T18:38:00.4059775Z","WorkingSet64":137252864,"PrivateMemorySize64":248102912,"managedBytes":4518696,"allocatedBytes":11232901168,"gc":[722,482,482],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":19200,"sharedPasses":4800,"driverPasses":4800,"browserPasses":4800}
|
||||
{"cycle":2410,"elapsedSeconds":3208.5911344,"utc":"2026-11-10T18:38:13.7143472Z","WorkingSet64":137252864,"PrivateMemorySize64":248102912,"managedBytes":4612400,"allocatedBytes":11279705920,"gc":[725,484,484],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":19280,"sharedPasses":4820,"driverPasses":4820,"browserPasses":4820}
|
||||
{"cycle":2420,"elapsedSeconds":3221.8951602,"utc":"2026-11-10T18:38:27.0202929Z","WorkingSet64":137252864,"PrivateMemorySize64":248102912,"managedBytes":4612560,"allocatedBytes":11326415640,"gc":[728,486,486],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":19360,"sharedPasses":4840,"driverPasses":4840,"browserPasses":4840}
|
||||
{"cycle":2430,"elapsedSeconds":3235.2068821,"utc":"2026-11-10T18:38:40.331109Z","WorkingSet64":136732672,"PrivateMemorySize64":248082432,"managedBytes":4517400,"allocatedBytes":11373122848,"gc":[731,488,488],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":19440,"sharedPasses":4860,"driverPasses":4860,"browserPasses":4860}
|
||||
{"cycle":2440,"elapsedSeconds":3248.5050924,"utc":"2026-11-10T18:38:53.6276319Z","WorkingSet64":136732672,"PrivateMemorySize64":248082432,"managedBytes":4517552,"allocatedBytes":11419821744,"gc":[734,490,490],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":19520,"sharedPasses":4880,"driverPasses":4880,"browserPasses":4880}
|
||||
{"cycle":2450,"elapsedSeconds":3261.8383554,"utc":"2026-11-10T18:39:06.9569996Z","WorkingSet64":136732672,"PrivateMemorySize64":248102912,"managedBytes":4547296,"allocatedBytes":11466564480,"gc":[737,492,492],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":19600,"sharedPasses":4900,"driverPasses":4900,"browserPasses":4900}
|
||||
{"cycle":2460,"elapsedSeconds":3275.1372908,"utc":"2026-11-10T18:39:20.2573743Z","WorkingSet64":136122368,"PrivateMemorySize64":248102912,"managedBytes":4565464,"allocatedBytes":11513291000,"gc":[740,494,494],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":19680,"sharedPasses":4920,"driverPasses":4920,"browserPasses":4920}
|
||||
{"cycle":2470,"elapsedSeconds":3288.4283588,"utc":"2026-11-10T18:39:33.5565649Z","WorkingSet64":136122368,"PrivateMemorySize64":248082432,"managedBytes":4501120,"allocatedBytes":11559988256,"gc":[743,496,496],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":19760,"sharedPasses":4940,"driverPasses":4940,"browserPasses":4940}
|
||||
{"cycle":2480,"elapsedSeconds":3301.7419796,"utc":"2026-11-10T18:39:46.8597078Z","WorkingSet64":136605696,"PrivateMemorySize64":248102912,"managedBytes":4557984,"allocatedBytes":11607734704,"gc":[746,498,498],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":19840,"sharedPasses":4960,"driverPasses":4960,"browserPasses":4960}
|
||||
{"cycle":2490,"elapsedSeconds":3314.9103339,"utc":"2026-11-10T18:40:00.0284253Z","WorkingSet64":136785920,"PrivateMemorySize64":248102912,"managedBytes":4646888,"allocatedBytes":11654534784,"gc":[749,500,500],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":19920,"sharedPasses":4980,"driverPasses":4980,"browserPasses":4980}
|
||||
{"cycle":2500,"elapsedSeconds":3328.1766942,"utc":"2026-11-10T18:40:13.3055287Z","WorkingSet64":135938048,"PrivateMemorySize64":248102912,"managedBytes":4629880,"allocatedBytes":11701253648,"gc":[752,502,502],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":20000,"sharedPasses":5000,"driverPasses":5000,"browserPasses":5000}
|
||||
{"cycle":2510,"elapsedSeconds":3341.3479978,"utc":"2026-11-10T18:40:26.4668782Z","WorkingSet64":135938048,"PrivateMemorySize64":248082432,"managedBytes":4517248,"allocatedBytes":11747954336,"gc":[755,504,504],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":20080,"sharedPasses":5020,"driverPasses":5020,"browserPasses":5020}
|
||||
{"cycle":2520,"elapsedSeconds":3354.5560829,"utc":"2026-11-10T18:40:39.6811693Z","WorkingSet64":135950336,"PrivateMemorySize64":248168448,"managedBytes":4631704,"allocatedBytes":11794779224,"gc":[758,506,506],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":20160,"sharedPasses":5040,"driverPasses":5040,"browserPasses":5040}
|
||||
{"cycle":2530,"elapsedSeconds":3367.8831981,"utc":"2026-11-10T18:40:53.0005831Z","WorkingSet64":135389184,"PrivateMemorySize64":248168448,"managedBytes":4647712,"allocatedBytes":11841513312,"gc":[761,508,508],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":20240,"sharedPasses":5060,"driverPasses":5060,"browserPasses":5060}
|
||||
{"cycle":2540,"elapsedSeconds":3381.0004846,"utc":"2026-11-10T18:41:06.1205752Z","WorkingSet64":135471104,"PrivateMemorySize64":248168448,"managedBytes":4638504,"allocatedBytes":11888204224,"gc":[764,510,510],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":20320,"sharedPasses":5080,"driverPasses":5080,"browserPasses":5080}
|
||||
{"cycle":2550,"elapsedSeconds":3394.3315319,"utc":"2026-11-10T18:41:19.4601963Z","WorkingSet64":136192000,"PrivateMemorySize64":248168448,"managedBytes":4522104,"allocatedBytes":11934927096,"gc":[767,512,512],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":20400,"sharedPasses":5100,"driverPasses":5100,"browserPasses":5100}
|
||||
{"cycle":2560,"elapsedSeconds":3407.6387038,"utc":"2026-11-10T18:41:32.7662565Z","WorkingSet64":135135232,"PrivateMemorySize64":248299520,"managedBytes":4630568,"allocatedBytes":11981734184,"gc":[770,514,514],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":20480,"sharedPasses":5120,"driverPasses":5120,"browserPasses":5120}
|
||||
{"cycle":2570,"elapsedSeconds":3420.9414793,"utc":"2026-11-10T18:41:46.0672593Z","WorkingSet64":134914048,"PrivateMemorySize64":248000512,"managedBytes":4516672,"allocatedBytes":12028474912,"gc":[773,516,516],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":20560,"sharedPasses":5140,"driverPasses":5140,"browserPasses":5140}
|
||||
{"cycle":2580,"elapsedSeconds":3434.1467509,"utc":"2026-11-10T18:41:59.2686931Z","WorkingSet64":134914048,"PrivateMemorySize64":248000512,"managedBytes":4517360,"allocatedBytes":12075178416,"gc":[776,518,518],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":20640,"sharedPasses":5160,"driverPasses":5160,"browserPasses":5160}
|
||||
{"cycle":2590,"elapsedSeconds":3447.4953282,"utc":"2026-11-10T18:42:12.6155439Z","WorkingSet64":134914048,"PrivateMemorySize64":248000512,"managedBytes":4516840,"allocatedBytes":12121868976,"gc":[779,520,520],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":20720,"sharedPasses":5180,"driverPasses":5180,"browserPasses":5180}
|
||||
{"cycle":2600,"elapsedSeconds":3460.8180704,"utc":"2026-11-10T18:42:25.9432168Z","WorkingSet64":134602752,"PrivateMemorySize64":248020992,"managedBytes":4565960,"allocatedBytes":12168611952,"gc":[782,522,522],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":20800,"sharedPasses":5200,"driverPasses":5200,"browserPasses":5200}
|
||||
{"cycle":2610,"elapsedSeconds":3474.1409163,"utc":"2026-11-10T18:42:39.2626345Z","WorkingSet64":134602752,"PrivateMemorySize64":248000512,"managedBytes":4500424,"allocatedBytes":12215376536,"gc":[785,524,524],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":20880,"sharedPasses":5220,"driverPasses":5220,"browserPasses":5220}
|
||||
{"cycle":2620,"elapsedSeconds":3487.4384872,"utc":"2026-11-10T18:42:52.5635596Z","WorkingSet64":134602752,"PrivateMemorySize64":248020992,"managedBytes":4520640,"allocatedBytes":12262106888,"gc":[788,526,526],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":20960,"sharedPasses":5240,"driverPasses":5240,"browserPasses":5240}
|
||||
{"cycle":2630,"elapsedSeconds":3500.7409119,"utc":"2026-11-10T18:43:05.8637513Z","WorkingSet64":134189056,"PrivateMemorySize64":248086528,"managedBytes":4630392,"allocatedBytes":12308912040,"gc":[791,528,528],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":21040,"sharedPasses":5260,"driverPasses":5260,"browserPasses":5260}
|
||||
{"cycle":2640,"elapsedSeconds":3514.0138351,"utc":"2026-11-10T18:43:19.1419709Z","WorkingSet64":139644928,"PrivateMemorySize64":248152064,"managedBytes":4647896,"allocatedBytes":12355632440,"gc":[794,530,530],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":21120,"sharedPasses":5280,"driverPasses":5280,"browserPasses":5280}
|
||||
{"cycle":2650,"elapsedSeconds":3526.8578798,"utc":"2026-11-10T18:43:31.9756614Z","WorkingSet64":139644928,"PrivateMemorySize64":248131584,"managedBytes":4517072,"allocatedBytes":12402336256,"gc":[797,532,532],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":21200,"sharedPasses":5300,"driverPasses":5300,"browserPasses":5300}
|
||||
{"cycle":2660,"elapsedSeconds":3540.056546,"utc":"2026-11-10T18:43:45.1735061Z","WorkingSet64":139644928,"PrivateMemorySize64":248152064,"managedBytes":4542416,"allocatedBytes":12449079888,"gc":[800,534,534],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":21280,"sharedPasses":5320,"driverPasses":5320,"browserPasses":5320}
|
||||
{"cycle":2670,"elapsedSeconds":3553.2562429,"utc":"2026-11-10T18:43:58.3748788Z","WorkingSet64":139644928,"PrivateMemorySize64":248152064,"managedBytes":4613040,"allocatedBytes":12495855928,"gc":[803,536,536],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":21360,"sharedPasses":5340,"driverPasses":5340,"browserPasses":5340}
|
||||
{"cycle":2680,"elapsedSeconds":3566.5138326,"utc":"2026-11-10T18:44:11.6413995Z","WorkingSet64":139644928,"PrivateMemorySize64":248152064,"managedBytes":4631928,"allocatedBytes":12542572184,"gc":[806,538,538],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":21440,"sharedPasses":5360,"driverPasses":5360,"browserPasses":5360}
|
||||
{"cycle":2690,"elapsedSeconds":3579.6225905,"utc":"2026-11-10T18:44:24.7434194Z","WorkingSet64":139644928,"PrivateMemorySize64":248152064,"managedBytes":4631168,"allocatedBytes":12589270856,"gc":[809,540,540],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":21520,"sharedPasses":5380,"driverPasses":5380,"browserPasses":5380}
|
||||
{"cycle":2700,"elapsedSeconds":3592.7408581,"utc":"2026-11-10T18:44:37.8674278Z","WorkingSet64":139956224,"PrivateMemorySize64":248152064,"managedBytes":4647680,"allocatedBytes":12636014544,"gc":[812,542,542],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":21600,"sharedPasses":5400,"driverPasses":5400,"browserPasses":5400}
|
||||
{"cycle":2710,"elapsedSeconds":3605.9803556,"utc":"2026-11-10T18:44:51.1101771Z","WorkingSet64":141332480,"PrivateMemorySize64":248152064,"managedBytes":4647384,"allocatedBytes":12683677808,"gc":[815,544,544],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":21680,"sharedPasses":5420,"driverPasses":5420,"browserPasses":5420}
|
||||
{"cycle":2720,"elapsedSeconds":3619.223102,"utc":"2026-11-10T18:45:04.348735Z","WorkingSet64":141332480,"PrivateMemorySize64":248131584,"managedBytes":4532640,"allocatedBytes":12730386976,"gc":[818,546,546],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":21760,"sharedPasses":5440,"driverPasses":5440,"browserPasses":5440}
|
||||
{"cycle":2730,"elapsedSeconds":3632.4494909,"utc":"2026-11-10T18:45:17.5780367Z","WorkingSet64":141062144,"PrivateMemorySize64":248152064,"managedBytes":4580712,"allocatedBytes":12777158240,"gc":[821,548,548],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":21840,"sharedPasses":5460,"driverPasses":5460,"browserPasses":5460}
|
||||
{"cycle":2740,"elapsedSeconds":3645.6272895,"utc":"2026-11-10T18:45:30.7554742Z","WorkingSet64":141156352,"PrivateMemorySize64":248152064,"managedBytes":4646272,"allocatedBytes":12823938024,"gc":[824,550,550],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":21920,"sharedPasses":5480,"driverPasses":5480,"browserPasses":5480}
|
||||
{"cycle":2750,"elapsedSeconds":3658.8946812,"utc":"2026-11-10T18:45:44.0174653Z","WorkingSet64":141156352,"PrivateMemorySize64":248152064,"managedBytes":4647376,"allocatedBytes":12870643728,"gc":[827,552,552],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":22000,"sharedPasses":5500,"driverPasses":5500,"browserPasses":5500}
|
||||
{"cycle":2760,"elapsedSeconds":3672.0557723,"utc":"2026-11-10T18:45:57.174906Z","WorkingSet64":141156352,"PrivateMemorySize64":248131584,"managedBytes":4517248,"allocatedBytes":12917347840,"gc":[830,554,554],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":22080,"sharedPasses":5520,"driverPasses":5520,"browserPasses":5520}
|
||||
{"cycle":2770,"elapsedSeconds":3685.3576271,"utc":"2026-11-10T18:46:10.4785831Z","WorkingSet64":140980224,"PrivateMemorySize64":248152064,"managedBytes":4579992,"allocatedBytes":12964116272,"gc":[833,556,556],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":22160,"sharedPasses":5540,"driverPasses":5540,"browserPasses":5540}
|
||||
{"cycle":2780,"elapsedSeconds":3698.5960382,"utc":"2026-11-10T18:46:23.7235744Z","WorkingSet64":141484032,"PrivateMemorySize64":248131584,"managedBytes":4517024,"allocatedBytes":13010832224,"gc":[836,558,558],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":22240,"sharedPasses":5560,"driverPasses":5560,"browserPasses":5560}
|
||||
{"cycle":2790,"elapsedSeconds":3711.8593586,"utc":"2026-11-10T18:46:36.9839413Z","WorkingSet64":141484032,"PrivateMemorySize64":248152064,"managedBytes":4581680,"allocatedBytes":13057584568,"gc":[839,560,560],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":22320,"sharedPasses":5580,"driverPasses":5580,"browserPasses":5580}
|
||||
{"cycle":2800,"elapsedSeconds":3725.1404624,"utc":"2026-11-10T18:46:50.2673849Z","WorkingSet64":140398592,"PrivateMemorySize64":248152064,"managedBytes":4566080,"allocatedBytes":13104297184,"gc":[842,562,562],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":22400,"sharedPasses":5600,"driverPasses":5600,"browserPasses":5600}
|
||||
{"cycle":2810,"elapsedSeconds":3738.4281808,"utc":"2026-11-10T18:47:03.5492228Z","WorkingSet64":140398592,"PrivateMemorySize64":248152064,"managedBytes":4631072,"allocatedBytes":13151092496,"gc":[845,564,564],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":22480,"sharedPasses":5620,"driverPasses":5620,"browserPasses":5620}
|
||||
{"cycle":2820,"elapsedSeconds":3751.7036567,"utc":"2026-11-10T18:47:16.8320698Z","WorkingSet64":140398592,"PrivateMemorySize64":248152064,"managedBytes":4646096,"allocatedBytes":13197811040,"gc":[848,566,566],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":22560,"sharedPasses":5640,"driverPasses":5640,"browserPasses":5640}
|
||||
{"cycle":2830,"elapsedSeconds":3764.8288594,"utc":"2026-11-10T18:47:29.9490464Z","WorkingSet64":140398592,"PrivateMemorySize64":248152064,"managedBytes":4647088,"allocatedBytes":13244521016,"gc":[851,568,568],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":22640,"sharedPasses":5660,"driverPasses":5660,"browserPasses":5660}
|
||||
{"cycle":2840,"elapsedSeconds":3778.0234287,"utc":"2026-11-10T18:47:43.1525105Z","WorkingSet64":140144640,"PrivateMemorySize64":248152064,"managedBytes":4587488,"allocatedBytes":13291310552,"gc":[854,570,570],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":22720,"sharedPasses":5680,"driverPasses":5680,"browserPasses":5680}
|
||||
{"cycle":2850,"elapsedSeconds":3791.1815004,"utc":"2026-11-10T18:47:56.310184Z","WorkingSet64":140185600,"PrivateMemorySize64":248152064,"managedBytes":4630160,"allocatedBytes":13338052256,"gc":[857,572,572],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":22800,"sharedPasses":5700,"driverPasses":5700,"browserPasses":5700}
|
||||
{"cycle":2860,"elapsedSeconds":3804.3095771,"utc":"2026-11-10T18:48:09.4272408Z","WorkingSet64":140505088,"PrivateMemorySize64":248131584,"managedBytes":4501000,"allocatedBytes":13384764736,"gc":[860,574,574],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":22880,"sharedPasses":5720,"driverPasses":5720,"browserPasses":5720}
|
||||
{"cycle":2870,"elapsedSeconds":3817.6499627,"utc":"2026-11-10T18:48:22.7774667Z","WorkingSet64":136892416,"PrivateMemorySize64":248152064,"managedBytes":4549000,"allocatedBytes":13431517264,"gc":[863,576,576],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":22960,"sharedPasses":5740,"driverPasses":5740,"browserPasses":5740}
|
||||
{"cycle":2880,"elapsedSeconds":3830.737646,"utc":"2026-11-10T18:48:35.8674593Z","WorkingSet64":136892416,"PrivateMemorySize64":248152064,"managedBytes":4630744,"allocatedBytes":13478305280,"gc":[866,578,578],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":23040,"sharedPasses":5760,"driverPasses":5760,"browserPasses":5760}
|
||||
{"cycle":2890,"elapsedSeconds":3843.9766521,"utc":"2026-11-10T18:48:49.0941582Z","WorkingSet64":136892416,"PrivateMemorySize64":248152064,"managedBytes":4622360,"allocatedBytes":13524996168,"gc":[869,580,580],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":23120,"sharedPasses":5780,"driverPasses":5780,"browserPasses":5780}
|
||||
{"cycle":2900,"elapsedSeconds":3857.2735939,"utc":"2026-11-10T18:49:02.3907588Z","WorkingSet64":136867840,"PrivateMemorySize64":248152064,"managedBytes":4630744,"allocatedBytes":13571712840,"gc":[872,582,582],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":23200,"sharedPasses":5800,"driverPasses":5800,"browserPasses":5800}
|
||||
{"cycle":2910,"elapsedSeconds":3870.5382781,"utc":"2026-11-10T18:49:15.6655812Z","WorkingSet64":136867840,"PrivateMemorySize64":248131584,"managedBytes":4581584,"allocatedBytes":13618420552,"gc":[875,584,584],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":23280,"sharedPasses":5820,"driverPasses":5820,"browserPasses":5820}
|
||||
{"cycle":2920,"elapsedSeconds":3883.7917365,"utc":"2026-11-10T18:49:28.9257285Z","WorkingSet64":136867840,"PrivateMemorySize64":248152064,"managedBytes":4627880,"allocatedBytes":13665165368,"gc":[878,586,586],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":23360,"sharedPasses":5840,"driverPasses":5840,"browserPasses":5840}
|
||||
{"cycle":2930,"elapsedSeconds":3896.8911756,"utc":"2026-11-10T18:49:42.0086581Z","WorkingSet64":136867840,"PrivateMemorySize64":248152064,"managedBytes":4647864,"allocatedBytes":13711888832,"gc":[881,588,588],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":23440,"sharedPasses":5860,"driverPasses":5860,"browserPasses":5860}
|
||||
{"cycle":2940,"elapsedSeconds":3910.2108164,"utc":"2026-11-10T18:49:55.3286969Z","WorkingSet64":139603968,"PrivateMemorySize64":248152064,"managedBytes":4646288,"allocatedBytes":13759572072,"gc":[884,590,590],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":23520,"sharedPasses":5880,"driverPasses":5880,"browserPasses":5880}
|
||||
{"cycle":2950,"elapsedSeconds":3923.5292088,"utc":"2026-11-10T18:50:08.654244Z","WorkingSet64":139603968,"PrivateMemorySize64":248152064,"managedBytes":4565440,"allocatedBytes":13806315856,"gc":[887,592,592],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":23600,"sharedPasses":5900,"driverPasses":5900,"browserPasses":5900}
|
||||
{"cycle":2960,"elapsedSeconds":3936.7270001,"utc":"2026-11-10T18:50:21.8539265Z","WorkingSet64":139603968,"PrivateMemorySize64":248152064,"managedBytes":4582136,"allocatedBytes":13853038400,"gc":[890,594,594],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":23680,"sharedPasses":5920,"driverPasses":5920,"browserPasses":5920}
|
||||
{"cycle":2970,"elapsedSeconds":3950.0404741,"utc":"2026-11-10T18:50:35.1657556Z","WorkingSet64":139108352,"PrivateMemorySize64":248131584,"managedBytes":4517256,"allocatedBytes":13899751392,"gc":[893,596,596],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":23760,"sharedPasses":5940,"driverPasses":5940,"browserPasses":5940}
|
||||
{"cycle":2980,"elapsedSeconds":3963.3270102,"utc":"2026-11-10T18:50:48.4527189Z","WorkingSet64":139214848,"PrivateMemorySize64":248131584,"managedBytes":4517168,"allocatedBytes":13946457296,"gc":[896,598,598],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":23840,"sharedPasses":5960,"driverPasses":5960,"browserPasses":5960}
|
||||
{"cycle":2990,"elapsedSeconds":3976.5313651,"utc":"2026-11-10T18:51:01.6571171Z","WorkingSet64":139214848,"PrivateMemorySize64":248131584,"managedBytes":4517592,"allocatedBytes":13993163408,"gc":[899,600,600],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":23920,"sharedPasses":5980,"driverPasses":5980,"browserPasses":5980}
|
||||
{"cycle":3000,"elapsedSeconds":3989.775694,"utc":"2026-11-10T18:51:14.8931471Z","WorkingSet64":139214848,"PrivateMemorySize64":248131584,"managedBytes":4517128,"allocatedBytes":14039860608,"gc":[902,602,602],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":24000,"sharedPasses":6000,"driverPasses":6000,"browserPasses":6000}
|
||||
{"cycle":3010,"elapsedSeconds":4003.0564251,"utc":"2026-11-10T18:51:28.1747748Z","WorkingSet64":138244096,"PrivateMemorySize64":248152064,"managedBytes":4648040,"allocatedBytes":14086691072,"gc":[905,604,604],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":24080,"sharedPasses":6020,"driverPasses":6020,"browserPasses":6020}
|
||||
{"cycle":3020,"elapsedSeconds":4016.3596689,"utc":"2026-11-10T18:51:41.4865308Z","WorkingSet64":138244096,"PrivateMemorySize64":248152064,"managedBytes":4632616,"allocatedBytes":14133515816,"gc":[908,606,606],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":24160,"sharedPasses":6040,"driverPasses":6040,"browserPasses":6040}
|
||||
{"cycle":3030,"elapsedSeconds":4029.4609105,"utc":"2026-11-10T18:51:54.5782268Z","WorkingSet64":138244096,"PrivateMemorySize64":248152064,"managedBytes":4646904,"allocatedBytes":14180220760,"gc":[911,608,608],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":24240,"sharedPasses":6060,"driverPasses":6060,"browserPasses":6060}
|
||||
{"cycle":3040,"elapsedSeconds":4042.781307,"utc":"2026-11-10T18:52:07.9075421Z","WorkingSet64":137908224,"PrivateMemorySize64":248152064,"managedBytes":4522720,"allocatedBytes":14226926368,"gc":[914,610,610],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":24320,"sharedPasses":6080,"driverPasses":6080,"browserPasses":6080}
|
||||
{"cycle":3050,"elapsedSeconds":4055.9007532,"utc":"2026-11-10T18:52:21.0227135Z","WorkingSet64":137908224,"PrivateMemorySize64":248152064,"managedBytes":4521968,"allocatedBytes":14273626640,"gc":[917,612,612],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":24400,"sharedPasses":6100,"driverPasses":6100,"browserPasses":6100}
|
||||
{"cycle":3060,"elapsedSeconds":4069.1601534,"utc":"2026-11-10T18:52:34.2860243Z","WorkingSet64":137908224,"PrivateMemorySize64":248131584,"managedBytes":4517240,"allocatedBytes":14320336456,"gc":[920,614,614],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":24480,"sharedPasses":6120,"driverPasses":6120,"browserPasses":6120}
|
||||
{"cycle":3070,"elapsedSeconds":4082.4767702,"utc":"2026-11-10T18:52:47.6078749Z","WorkingSet64":135692288,"PrivateMemorySize64":248152064,"managedBytes":4518712,"allocatedBytes":14367054848,"gc":[923,616,616],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":24560,"sharedPasses":6140,"driverPasses":6140,"browserPasses":6140}
|
||||
{"cycle":3080,"elapsedSeconds":4095.6692935,"utc":"2026-11-10T18:53:00.786607Z","WorkingSet64":135794688,"PrivateMemorySize64":248238080,"managedBytes":4671568,"allocatedBytes":14413927928,"gc":[926,618,618],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":24640,"sharedPasses":6160,"driverPasses":6160,"browserPasses":6160}
|
||||
{"cycle":3090,"elapsedSeconds":4108.7808298,"utc":"2026-11-10T18:53:13.8980819Z","WorkingSet64":138858496,"PrivateMemorySize64":248217600,"managedBytes":4623072,"allocatedBytes":14460655128,"gc":[929,620,620],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":24720,"sharedPasses":6180,"driverPasses":6180,"browserPasses":6180}
|
||||
{"cycle":3100,"elapsedSeconds":4121.8051343,"utc":"2026-11-10T18:53:26.9329517Z","WorkingSet64":138858496,"PrivateMemorySize64":248217600,"managedBytes":4521688,"allocatedBytes":14507368056,"gc":[932,622,622],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":24800,"sharedPasses":6200,"driverPasses":6200,"browserPasses":6200}
|
||||
{"cycle":3110,"elapsedSeconds":4135.0963146,"utc":"2026-11-10T18:53:40.2235589Z","WorkingSet64":138846208,"PrivateMemorySize64":248217600,"managedBytes":4522096,"allocatedBytes":14554070984,"gc":[935,624,624],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":24880,"sharedPasses":6220,"driverPasses":6220,"browserPasses":6220}
|
||||
{"cycle":3120,"elapsedSeconds":4148.3809262,"utc":"2026-11-10T18:53:53.5079928Z","WorkingSet64":138846208,"PrivateMemorySize64":248197120,"managedBytes":4517784,"allocatedBytes":14600769808,"gc":[938,626,626],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":24960,"sharedPasses":6240,"driverPasses":6240,"browserPasses":6240}
|
||||
{"cycle":3130,"elapsedSeconds":4161.6205539,"utc":"2026-11-10T18:54:06.7503381Z","WorkingSet64":138846208,"PrivateMemorySize64":248217600,"managedBytes":4581512,"allocatedBytes":14647535656,"gc":[941,628,628],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":25040,"sharedPasses":6260,"driverPasses":6260,"browserPasses":6260}
|
||||
{"cycle":3140,"elapsedSeconds":4174.8286306,"utc":"2026-11-10T18:54:19.9581221Z","WorkingSet64":138493952,"PrivateMemorySize64":248217600,"managedBytes":4647448,"allocatedBytes":14694314880,"gc":[944,630,630],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":25120,"sharedPasses":6280,"driverPasses":6280,"browserPasses":6280}
|
||||
{"cycle":3150,"elapsedSeconds":4187.9317212,"utc":"2026-11-10T18:54:33.0591689Z","WorkingSet64":138493952,"PrivateMemorySize64":248217600,"managedBytes":4647760,"allocatedBytes":14741025784,"gc":[947,632,632],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":25200,"sharedPasses":6300,"driverPasses":6300,"browserPasses":6300}
|
||||
{"cycle":3160,"elapsedSeconds":4201.3385167,"utc":"2026-11-10T18:54:46.466935Z","WorkingSet64":138625024,"PrivateMemorySize64":248217600,"managedBytes":4543056,"allocatedBytes":14787734648,"gc":[950,634,634],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":25280,"sharedPasses":6320,"driverPasses":6320,"browserPasses":6320}
|
||||
{"cycle":3170,"elapsedSeconds":4214.6318822,"utc":"2026-11-10T18:54:59.7643932Z","WorkingSet64":139108352,"PrivateMemorySize64":248238080,"managedBytes":4598704,"allocatedBytes":14835471712,"gc":[953,636,636],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":25360,"sharedPasses":6340,"driverPasses":6340,"browserPasses":6340}
|
||||
{"cycle":3180,"elapsedSeconds":4227.6011572,"utc":"2026-11-10T18:55:12.7217844Z","WorkingSet64":138567680,"PrivateMemorySize64":248258560,"managedBytes":4599088,"allocatedBytes":14882179040,"gc":[956,638,638],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":25440,"sharedPasses":6360,"driverPasses":6360,"browserPasses":6360}
|
||||
{"cycle":3190,"elapsedSeconds":4240.8800341,"utc":"2026-11-10T18:55:26.0053482Z","WorkingSet64":138567680,"PrivateMemorySize64":248197120,"managedBytes":4521984,"allocatedBytes":14928870384,"gc":[959,640,640],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":25520,"sharedPasses":6380,"driverPasses":6380,"browserPasses":6380}
|
||||
{"cycle":3200,"elapsedSeconds":4254.0874218,"utc":"2026-11-10T18:55:39.2142131Z","WorkingSet64":138567680,"PrivateMemorySize64":248197120,"managedBytes":4522008,"allocatedBytes":14975574208,"gc":[962,642,642],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":25600,"sharedPasses":6400,"driverPasses":6400,"browserPasses":6400}
|
||||
{"cycle":3210,"elapsedSeconds":4267.3777322,"utc":"2026-11-10T18:55:52.503358Z","WorkingSet64":138256384,"PrivateMemorySize64":248197120,"managedBytes":4521768,"allocatedBytes":15022280560,"gc":[965,644,644],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":25680,"sharedPasses":6420,"driverPasses":6420,"browserPasses":6420}
|
||||
{"cycle":3220,"elapsedSeconds":4280.6564608,"utc":"2026-11-10T18:56:05.7824027Z","WorkingSet64":138256384,"PrivateMemorySize64":248197120,"managedBytes":4522432,"allocatedBytes":15068975584,"gc":[968,646,646],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":25760,"sharedPasses":6440,"driverPasses":6440,"browserPasses":6440}
|
||||
{"cycle":3230,"elapsedSeconds":4293.8763882,"utc":"2026-11-10T18:56:19.0056502Z","WorkingSet64":138256384,"PrivateMemorySize64":248197120,"managedBytes":4521064,"allocatedBytes":15115690104,"gc":[971,648,648],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":25840,"sharedPasses":6460,"driverPasses":6460,"browserPasses":6460}
|
||||
{"cycle":3240,"elapsedSeconds":4307.0704815,"utc":"2026-11-10T18:56:32.2035835Z","WorkingSet64":136998912,"PrivateMemorySize64":248197120,"managedBytes":4522648,"allocatedBytes":15162383368,"gc":[974,650,650],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":25920,"sharedPasses":6480,"driverPasses":6480,"browserPasses":6480}
|
||||
{"cycle":3250,"elapsedSeconds":4320.2882786,"utc":"2026-11-10T18:56:45.4147471Z","WorkingSet64":136998912,"PrivateMemorySize64":248217600,"managedBytes":4635296,"allocatedBytes":15209206344,"gc":[977,652,652],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":26000,"sharedPasses":6500,"driverPasses":6500,"browserPasses":6500}
|
||||
{"cycle":3260,"elapsedSeconds":4333.5584568,"utc":"2026-11-10T18:56:58.6781374Z","WorkingSet64":136998912,"PrivateMemorySize64":248217600,"managedBytes":4568600,"allocatedBytes":15255964944,"gc":[980,654,654],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":26080,"sharedPasses":6520,"driverPasses":6520,"browserPasses":6520}
|
||||
{"cycle":3270,"elapsedSeconds":4347.0606786,"utc":"2026-11-10T18:57:12.1841117Z","WorkingSet64":137113600,"PrivateMemorySize64":248303616,"managedBytes":4791072,"allocatedBytes":15302893912,"gc":[983,656,656],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":26160,"sharedPasses":6540,"driverPasses":6540,"browserPasses":6540}
|
||||
{"cycle":3280,"elapsedSeconds":4360.31061,"utc":"2026-11-10T18:57:25.4340341Z","WorkingSet64":137121792,"PrivateMemorySize64":248283136,"managedBytes":4651432,"allocatedBytes":15349591520,"gc":[986,658,658],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":26240,"sharedPasses":6560,"driverPasses":6560,"browserPasses":6560}
|
||||
{"cycle":3290,"elapsedSeconds":4373.5743455,"utc":"2026-11-10T18:57:38.6952524Z","WorkingSet64":137121792,"PrivateMemorySize64":248283136,"managedBytes":4635624,"allocatedBytes":15396295048,"gc":[989,660,660],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":26320,"sharedPasses":6580,"driverPasses":6580,"browserPasses":6580}
|
||||
{"cycle":3300,"elapsedSeconds":4386.6887803,"utc":"2026-11-10T18:57:51.8081916Z","WorkingSet64":137121792,"PrivateMemorySize64":248283136,"managedBytes":4586872,"allocatedBytes":15443079392,"gc":[992,662,662],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":26400,"sharedPasses":6600,"driverPasses":6600,"browserPasses":6600}
|
||||
{"cycle":3310,"elapsedSeconds":4399.9113254,"utc":"2026-11-10T18:58:05.0420444Z","WorkingSet64":136998912,"PrivateMemorySize64":248283136,"managedBytes":4567968,"allocatedBytes":15489781728,"gc":[995,664,664],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":26480,"sharedPasses":6620,"driverPasses":6620,"browserPasses":6620}
|
||||
{"cycle":3320,"elapsedSeconds":4413.0580019,"utc":"2026-11-10T18:58:18.1870197Z","WorkingSet64":136998912,"PrivateMemorySize64":248283136,"managedBytes":4592464,"allocatedBytes":15536553896,"gc":[998,666,666],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":26560,"sharedPasses":6640,"driverPasses":6640,"browserPasses":6640}
|
||||
{"cycle":3330,"elapsedSeconds":4426.2459223,"utc":"2026-11-10T18:58:31.3722185Z","WorkingSet64":136998912,"PrivateMemorySize64":248283136,"managedBytes":4635720,"allocatedBytes":15583333064,"gc":[1001,668,668],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":26640,"sharedPasses":6660,"driverPasses":6660,"browserPasses":6660}
|
||||
{"cycle":3340,"elapsedSeconds":4439.4945903,"utc":"2026-11-10T18:58:44.612778Z","WorkingSet64":136998912,"PrivateMemorySize64":248262656,"managedBytes":4512544,"allocatedBytes":15630045576,"gc":[1004,670,670],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":26720,"sharedPasses":6680,"driverPasses":6680,"browserPasses":6680}
|
||||
{"cycle":3350,"elapsedSeconds":4452.7319327,"utc":"2026-11-10T18:58:57.8504798Z","WorkingSet64":136871936,"PrivateMemorySize64":248262656,"managedBytes":4513304,"allocatedBytes":15676735472,"gc":[1007,672,672],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":26800,"sharedPasses":6700,"driverPasses":6700,"browserPasses":6700}
|
||||
{"cycle":3360,"elapsedSeconds":4465.9665357,"utc":"2026-11-10T18:59:11.0915659Z","WorkingSet64":136871936,"PrivateMemorySize64":248262656,"managedBytes":4522208,"allocatedBytes":15723446584,"gc":[1010,674,674],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":26880,"sharedPasses":6720,"driverPasses":6720,"browserPasses":6720}
|
||||
{"cycle":3370,"elapsedSeconds":4479.2453575,"utc":"2026-11-10T18:59:24.3656984Z","WorkingSet64":136871936,"PrivateMemorySize64":248262656,"managedBytes":4521360,"allocatedBytes":15770144424,"gc":[1013,676,676],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":26960,"sharedPasses":6740,"driverPasses":6740,"browserPasses":6740}
|
||||
{"cycle":3380,"elapsedSeconds":4492.5037895,"utc":"2026-11-10T18:59:37.6220691Z","WorkingSet64":136712192,"PrivateMemorySize64":248283136,"managedBytes":4620792,"allocatedBytes":15816956776,"gc":[1016,678,678],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":27040,"sharedPasses":6760,"driverPasses":6760,"browserPasses":6760}
|
||||
{"cycle":3390,"elapsedSeconds":4505.7328701,"utc":"2026-11-10T18:59:50.8520389Z","WorkingSet64":139632640,"PrivateMemorySize64":248283136,"managedBytes":4521248,"allocatedBytes":15864653768,"gc":[1019,680,680],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":27120,"sharedPasses":6780,"driverPasses":6780,"browserPasses":6780}
|
||||
{"cycle":3400,"elapsedSeconds":4518.9633225,"utc":"2026-11-10T19:00:04.0810038Z","WorkingSet64":139632640,"PrivateMemorySize64":248262656,"managedBytes":4521944,"allocatedBytes":15911375032,"gc":[1022,682,682],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":27200,"sharedPasses":6800,"driverPasses":6800,"browserPasses":6800}
|
||||
{"cycle":3410,"elapsedSeconds":4532.2249832,"utc":"2026-11-10T19:00:17.3425888Z","WorkingSet64":138752000,"PrivateMemorySize64":248262656,"managedBytes":4521312,"allocatedBytes":15958071720,"gc":[1025,684,684],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":27280,"sharedPasses":6820,"driverPasses":6820,"browserPasses":6820}
|
||||
{"cycle":3420,"elapsedSeconds":4545.4773934,"utc":"2026-11-10T19:00:30.5988211Z","WorkingSet64":138752000,"PrivateMemorySize64":248262656,"managedBytes":4521496,"allocatedBytes":16004773048,"gc":[1028,686,686],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":27360,"sharedPasses":6840,"driverPasses":6840,"browserPasses":6840}
|
||||
{"cycle":3430,"elapsedSeconds":4558.6764164,"utc":"2026-11-10T19:00:43.7997598Z","WorkingSet64":138752000,"PrivateMemorySize64":248262656,"managedBytes":4521744,"allocatedBytes":16051486320,"gc":[1031,688,688],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":27440,"sharedPasses":6860,"driverPasses":6860,"browserPasses":6860}
|
||||
{"cycle":3440,"elapsedSeconds":4571.9136606,"utc":"2026-11-10T19:00:57.0388739Z","WorkingSet64":138752000,"PrivateMemorySize64":248283136,"managedBytes":4652224,"allocatedBytes":16098326496,"gc":[1034,690,690],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":27520,"sharedPasses":6880,"driverPasses":6880,"browserPasses":6880}
|
||||
{"cycle":3450,"elapsedSeconds":4585.1241096,"utc":"2026-11-10T19:01:10.2418696Z","WorkingSet64":137261056,"PrivateMemorySize64":248283136,"managedBytes":4550992,"allocatedBytes":16145065184,"gc":[1037,692,692],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":27600,"sharedPasses":6900,"driverPasses":6900,"browserPasses":6900}
|
||||
{"cycle":3460,"elapsedSeconds":4598.3889279,"utc":"2026-11-10T19:01:23.5146186Z","WorkingSet64":137261056,"PrivateMemorySize64":248283136,"managedBytes":4650856,"allocatedBytes":16191862272,"gc":[1040,694,694],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":27680,"sharedPasses":6920,"driverPasses":6920,"browserPasses":6920}
|
||||
{"cycle":3470,"elapsedSeconds":4611.6232963,"utc":"2026-11-10T19:01:36.7478109Z","WorkingSet64":139436032,"PrivateMemorySize64":248283136,"managedBytes":4652152,"allocatedBytes":16238573160,"gc":[1043,696,696],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":27760,"sharedPasses":6940,"driverPasses":6940,"browserPasses":6940}
|
||||
{"cycle":3480,"elapsedSeconds":4624.8695604,"utc":"2026-11-10T19:01:49.9939805Z","WorkingSet64":139436032,"PrivateMemorySize64":248283136,"managedBytes":4652288,"allocatedBytes":16285267016,"gc":[1046,698,698],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":27840,"sharedPasses":6960,"driverPasses":6960,"browserPasses":6960}
|
||||
{"cycle":3490,"elapsedSeconds":4638.140508,"utc":"2026-11-10T19:02:03.25852Z","WorkingSet64":139436032,"PrivateMemorySize64":248283136,"managedBytes":4618680,"allocatedBytes":16332074272,"gc":[1049,700,700],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":27920,"sharedPasses":6980,"driverPasses":6980,"browserPasses":6980}
|
||||
{"cycle":3500,"elapsedSeconds":4651.3904809,"utc":"2026-11-10T19:02:16.5174099Z","WorkingSet64":139436032,"PrivateMemorySize64":248303616,"managedBytes":4773480,"allocatedBytes":16378936696,"gc":[1052,702,702],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":28000,"sharedPasses":7000,"driverPasses":7000,"browserPasses":7000}
|
||||
{"cycle":3510,"elapsedSeconds":4664.5917749,"utc":"2026-11-10T19:02:29.7095169Z","WorkingSet64":139436032,"PrivateMemorySize64":248303616,"managedBytes":4765560,"allocatedBytes":16425639952,"gc":[1055,704,704],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":28080,"sharedPasses":7020,"driverPasses":7020,"browserPasses":7020}
|
||||
{"cycle":3520,"elapsedSeconds":4677.7814582,"utc":"2026-11-10T19:02:42.908182Z","WorkingSet64":139292672,"PrivateMemorySize64":248303616,"managedBytes":4724608,"allocatedBytes":16472430776,"gc":[1058,706,706],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":28160,"sharedPasses":7040,"driverPasses":7040,"browserPasses":7040}
|
||||
{"cycle":3530,"elapsedSeconds":4691.0688619,"utc":"2026-11-10T19:02:56.190364Z","WorkingSet64":139292672,"PrivateMemorySize64":248303616,"managedBytes":4691408,"allocatedBytes":16519131792,"gc":[1061,708,708],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":28240,"sharedPasses":7060,"driverPasses":7060,"browserPasses":7060}
|
||||
{"cycle":3540,"elapsedSeconds":4704.2841341,"utc":"2026-11-10T19:03:09.4022211Z","WorkingSet64":139292672,"PrivateMemorySize64":248303616,"managedBytes":4647248,"allocatedBytes":16565886696,"gc":[1064,710,710],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":28320,"sharedPasses":7080,"driverPasses":7080,"browserPasses":7080}
|
||||
{"cycle":3550,"elapsedSeconds":4717.5353571,"utc":"2026-11-10T19:03:22.6560999Z","WorkingSet64":138264576,"PrivateMemorySize64":248303616,"managedBytes":4665064,"allocatedBytes":16612615656,"gc":[1067,712,712],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":28400,"sharedPasses":7100,"driverPasses":7100,"browserPasses":7100}
|
||||
{"cycle":3560,"elapsedSeconds":4730.7733147,"utc":"2026-11-10T19:03:35.8979401Z","WorkingSet64":138264576,"PrivateMemorySize64":248283136,"managedBytes":4635016,"allocatedBytes":16659417976,"gc":[1070,714,714],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":28480,"sharedPasses":7120,"driverPasses":7120,"browserPasses":7120}
|
||||
{"cycle":3570,"elapsedSeconds":4744.0214695,"utc":"2026-11-10T19:03:49.1402769Z","WorkingSet64":138264576,"PrivateMemorySize64":248303616,"managedBytes":4773816,"allocatedBytes":16706279320,"gc":[1073,716,716],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":28560,"sharedPasses":7140,"driverPasses":7140,"browserPasses":7140}
|
||||
{"cycle":3580,"elapsedSeconds":4757.2517002,"utc":"2026-11-10T19:04:02.3755512Z","WorkingSet64":137400320,"PrivateMemorySize64":248283136,"managedBytes":4651936,"allocatedBytes":16752994160,"gc":[1076,718,718],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":28640,"sharedPasses":7160,"driverPasses":7160,"browserPasses":7160}
|
||||
{"cycle":3590,"elapsedSeconds":4770.4598148,"utc":"2026-11-10T19:04:15.5798294Z","WorkingSet64":137400320,"PrivateMemorySize64":248283136,"managedBytes":4591552,"allocatedBytes":16799682856,"gc":[1079,720,720],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":28720,"sharedPasses":7180,"driverPasses":7180,"browserPasses":7180}
|
||||
{"cycle":3600,"elapsedSeconds":4783.663336,"utc":"2026-11-10T19:04:28.7863751Z","WorkingSet64":137400320,"PrivateMemorySize64":248283136,"managedBytes":4635320,"allocatedBytes":16846434896,"gc":[1082,722,722],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":28800,"sharedPasses":7200,"driverPasses":7200,"browserPasses":7200}
|
||||
{"cycle":3610,"elapsedSeconds":4796.8948164,"utc":"2026-11-10T19:04:42.0122819Z","WorkingSet64":137400320,"PrivateMemorySize64":248283136,"managedBytes":4643400,"allocatedBytes":16893143672,"gc":[1085,724,724],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":28880,"sharedPasses":7220,"driverPasses":7220,"browserPasses":7220}
|
||||
{"cycle":3620,"elapsedSeconds":4810.139686,"utc":"2026-11-10T19:04:55.2710629Z","WorkingSet64":137535488,"PrivateMemorySize64":248283136,"managedBytes":4652376,"allocatedBytes":16940839752,"gc":[1088,726,726],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":28960,"sharedPasses":7240,"driverPasses":7240,"browserPasses":7240}
|
||||
{"cycle":3630,"elapsedSeconds":4823.3174498,"utc":"2026-11-10T19:05:08.4414838Z","WorkingSet64":137535488,"PrivateMemorySize64":248283136,"managedBytes":4652000,"allocatedBytes":16987565000,"gc":[1091,728,728],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":29040,"sharedPasses":7260,"driverPasses":7260,"browserPasses":7260}
|
||||
{"cycle":3640,"elapsedSeconds":4836.5390828,"utc":"2026-11-10T19:05:21.6663296Z","WorkingSet64":137535488,"PrivateMemorySize64":248283136,"managedBytes":4592248,"allocatedBytes":17034280824,"gc":[1094,730,730],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":29120,"sharedPasses":7280,"driverPasses":7280,"browserPasses":7280}
|
||||
{"cycle":3650,"elapsedSeconds":4849.7265939,"utc":"2026-11-10T19:05:34.8471528Z","WorkingSet64":137256960,"PrivateMemorySize64":248283136,"managedBytes":4584064,"allocatedBytes":17080982824,"gc":[1097,732,732],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":29200,"sharedPasses":7300,"driverPasses":7300,"browserPasses":7300}
|
||||
{"cycle":3660,"elapsedSeconds":4863.1091391,"utc":"2026-11-10T19:05:48.2355383Z","WorkingSet64":137256960,"PrivateMemorySize64":248283136,"managedBytes":4592568,"allocatedBytes":17127699288,"gc":[1100,734,734],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":29280,"sharedPasses":7320,"driverPasses":7320,"browserPasses":7320}
|
||||
{"cycle":3670,"elapsedSeconds":4876.4371056,"utc":"2026-11-10T19:06:01.5555912Z","WorkingSet64":137646080,"PrivateMemorySize64":248283136,"managedBytes":4654392,"allocatedBytes":17174484720,"gc":[1103,736,736],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":29360,"sharedPasses":7340,"driverPasses":7340,"browserPasses":7340}
|
||||
{"cycle":3680,"elapsedSeconds":4889.6431798,"utc":"2026-11-10T19:06:14.7616703Z","WorkingSet64":137719808,"PrivateMemorySize64":248283136,"managedBytes":4651864,"allocatedBytes":17221191664,"gc":[1106,738,738],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":29440,"sharedPasses":7360,"driverPasses":7360,"browserPasses":7360}
|
||||
{"cycle":3690,"elapsedSeconds":4902.8281565,"utc":"2026-11-10T19:06:27.957358Z","WorkingSet64":137101312,"PrivateMemorySize64":248303616,"managedBytes":4775576,"allocatedBytes":17268040424,"gc":[1109,740,740],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":29520,"sharedPasses":7380,"driverPasses":7380,"browserPasses":7380}
|
||||
{"cycle":3700,"elapsedSeconds":4916.9700826,"utc":"2026-11-10T19:06:42.0889673Z","WorkingSet64":124518400,"PrivateMemorySize64":235720704,"managedBytes":4059920,"allocatedBytes":17314749104,"gc":[1112,742,742],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":29600,"sharedPasses":7400,"driverPasses":7400,"browserPasses":7400}
|
||||
{"cycle":3710,"elapsedSeconds":4930.565824,"utc":"2026-11-10T19:06:55.6911321Z","WorkingSet64":116129792,"PrivateMemorySize64":227332096,"managedBytes":4134952,"allocatedBytes":17362152240,"gc":[1115,744,744],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":29680,"sharedPasses":7420,"driverPasses":7420,"browserPasses":7420}
|
||||
{"cycle":3720,"elapsedSeconds":4943.7746161,"utc":"2026-11-10T19:07:08.8920686Z","WorkingSet64":136949760,"PrivateMemorySize64":248373248,"managedBytes":4634312,"allocatedBytes":17409400576,"gc":[1119,746,746],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":29760,"sharedPasses":7440,"driverPasses":7440,"browserPasses":7440}
|
||||
{"cycle":3730,"elapsedSeconds":4957.0853609,"utc":"2026-11-10T19:07:22.2037568Z","WorkingSet64":136994816,"PrivateMemorySize64":248303616,"managedBytes":4137120,"allocatedBytes":17456131480,"gc":[1122,748,748],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":29840,"sharedPasses":7460,"driverPasses":7460,"browserPasses":7460}
|
||||
{"cycle":3740,"elapsedSeconds":4970.2387122,"utc":"2026-11-10T19:07:35.3656669Z","WorkingSet64":136990720,"PrivateMemorySize64":248283136,"managedBytes":4121416,"allocatedBytes":17503358312,"gc":[1125,750,750],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":29920,"sharedPasses":7480,"driverPasses":7480,"browserPasses":7480}
|
||||
{"cycle":3750,"elapsedSeconds":4983.4241664,"utc":"2026-11-10T19:07:48.5429981Z","WorkingSet64":110030848,"PrivateMemorySize64":231505920,"managedBytes":4047040,"allocatedBytes":17550491048,"gc":[1128,752,752],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":30000,"sharedPasses":7500,"driverPasses":7500,"browserPasses":7500}
|
||||
{"cycle":3760,"elapsedSeconds":4996.6297597,"utc":"2026-11-10T19:08:01.7515976Z","WorkingSet64":110759936,"PrivateMemorySize64":231370752,"managedBytes":4126712,"allocatedBytes":17597831216,"gc":[1131,754,754],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":30080,"sharedPasses":7520,"driverPasses":7520,"browserPasses":7520}
|
||||
{"cycle":3770,"elapsedSeconds":5009.807343,"utc":"2026-11-10T19:08:14.9314294Z","WorkingSet64":114229248,"PrivateMemorySize64":234778624,"managedBytes":3990440,"allocatedBytes":17645089208,"gc":[1134,756,756],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":30160,"sharedPasses":7540,"driverPasses":7540,"browserPasses":7540}
|
||||
{"cycle":3780,"elapsedSeconds":5022.9974358,"utc":"2026-11-10T19:08:28.118887Z","WorkingSet64":118804480,"PrivateMemorySize64":239341568,"managedBytes":4126312,"allocatedBytes":17692512680,"gc":[1137,758,758],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":30240,"sharedPasses":7560,"driverPasses":7560,"browserPasses":7560}
|
||||
{"cycle":3790,"elapsedSeconds":5036.1678719,"utc":"2026-11-10T19:08:41.2886046Z","WorkingSet64":113438720,"PrivateMemorySize64":234778624,"managedBytes":3983752,"allocatedBytes":17739641736,"gc":[1140,760,760],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":30320,"sharedPasses":7580,"driverPasses":7580,"browserPasses":7580}
|
||||
{"cycle":3800,"elapsedSeconds":5049.3879165,"utc":"2026-11-10T19:08:54.5088568Z","WorkingSet64":117698560,"PrivateMemorySize64":239058944,"managedBytes":4057200,"allocatedBytes":17786981520,"gc":[1143,762,762],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":30400,"sharedPasses":7600,"driverPasses":7600,"browserPasses":7600}
|
||||
{"cycle":3810,"elapsedSeconds":5062.5706221,"utc":"2026-11-10T19:09:07.6932094Z","WorkingSet64":113373184,"PrivateMemorySize64":234733568,"managedBytes":3925728,"allocatedBytes":17834219336,"gc":[1146,764,764],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":30480,"sharedPasses":7620,"driverPasses":7620,"browserPasses":7620}
|
||||
{"cycle":3820,"elapsedSeconds":5075.7628267,"utc":"2026-11-10T19:09:20.8853674Z","WorkingSet64":113299456,"PrivateMemorySize64":234979328,"managedBytes":4048560,"allocatedBytes":17881572704,"gc":[1149,766,766],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":30560,"sharedPasses":7640,"driverPasses":7640,"browserPasses":7640}
|
||||
{"cycle":3830,"elapsedSeconds":5088.9765822,"utc":"2026-11-10T19:09:34.0970322Z","WorkingSet64":113799168,"PrivateMemorySize64":231604224,"managedBytes":4116720,"allocatedBytes":17928839568,"gc":[1152,768,768],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":30640,"sharedPasses":7660,"driverPasses":7660,"browserPasses":7660}
|
||||
{"cycle":3840,"elapsedSeconds":5102.1627066,"utc":"2026-11-10T19:09:47.2830284Z","WorkingSet64":121585664,"PrivateMemorySize64":237752320,"managedBytes":4117912,"allocatedBytes":17975996528,"gc":[1155,770,770],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":30720,"sharedPasses":7680,"driverPasses":7680,"browserPasses":7680}
|
||||
{"cycle":3850,"elapsedSeconds":5115.3504075,"utc":"2026-11-10T19:10:00.4711272Z","WorkingSet64":122200064,"PrivateMemorySize64":237752320,"managedBytes":4182968,"allocatedBytes":18024210640,"gc":[1158,772,772],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":30800,"sharedPasses":7700,"driverPasses":7700,"browserPasses":7700}
|
||||
{"cycle":3860,"elapsedSeconds":5128.5365508,"utc":"2026-11-10T19:10:13.6572511Z","WorkingSet64":118472704,"PrivateMemorySize64":237469696,"managedBytes":4051336,"allocatedBytes":18071341240,"gc":[1161,774,774],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":30880,"sharedPasses":7720,"driverPasses":7720,"browserPasses":7720}
|
||||
{"cycle":3870,"elapsedSeconds":5141.7550027,"utc":"2026-11-10T19:10:26.8761396Z","WorkingSet64":118669312,"PrivateMemorySize64":237666304,"managedBytes":4060480,"allocatedBytes":18118576056,"gc":[1164,776,776],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":30960,"sharedPasses":7740,"driverPasses":7740,"browserPasses":7740}
|
||||
{"cycle":3880,"elapsedSeconds":5154.950344,"utc":"2026-11-10T19:10:40.0716106Z","WorkingSet64":122941440,"PrivateMemorySize64":241926144,"managedBytes":4052472,"allocatedBytes":18165875200,"gc":[1167,778,778],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":31040,"sharedPasses":7760,"driverPasses":7760,"browserPasses":7760}
|
||||
{"cycle":3890,"elapsedSeconds":5168.117364,"utc":"2026-11-10T19:10:53.2398938Z","WorkingSet64":117174272,"PrivateMemorySize64":237752320,"managedBytes":4141832,"allocatedBytes":18213113216,"gc":[1170,780,780],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":31120,"sharedPasses":7780,"driverPasses":7780,"browserPasses":7780}
|
||||
{"cycle":3900,"elapsedSeconds":5181.3372804,"utc":"2026-11-10T19:11:06.4597396Z","WorkingSet64":120975360,"PrivateMemorySize64":241557504,"managedBytes":3930784,"allocatedBytes":18260351928,"gc":[1173,782,782],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":31200,"sharedPasses":7800,"driverPasses":7800,"browserPasses":7800}
|
||||
{"cycle":3910,"elapsedSeconds":5194.5465198,"utc":"2026-11-10T19:11:19.6666227Z","WorkingSet64":116862976,"PrivateMemorySize64":237445120,"managedBytes":4012488,"allocatedBytes":18307701248,"gc":[1176,784,784],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":31280,"sharedPasses":7820,"driverPasses":7820,"browserPasses":7820}
|
||||
{"cycle":3920,"elapsedSeconds":5207.7307735,"utc":"2026-11-10T19:11:32.8530313Z","WorkingSet64":116346880,"PrivateMemorySize64":237887488,"managedBytes":4129120,"allocatedBytes":18355012024,"gc":[1179,786,786],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":31360,"sharedPasses":7840,"driverPasses":7840,"browserPasses":7840}
|
||||
{"cycle":3930,"elapsedSeconds":5220.9260914,"utc":"2026-11-10T19:11:46.0462522Z","WorkingSet64":112140288,"PrivateMemorySize64":233680896,"managedBytes":3987264,"allocatedBytes":18402181928,"gc":[1182,788,788],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":31440,"sharedPasses":7860,"driverPasses":7860,"browserPasses":7860}
|
||||
{"cycle":3940,"elapsedSeconds":5234.1043659,"utc":"2026-11-10T19:11:59.2333062Z","WorkingSet64":116195328,"PrivateMemorySize64":237756416,"managedBytes":4131424,"allocatedBytes":18449637448,"gc":[1185,790,790],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":31520,"sharedPasses":7880,"driverPasses":7880,"browserPasses":7880}
|
||||
{"cycle":3950,"elapsedSeconds":5247.2809462,"utc":"2026-11-10T19:12:12.4014496Z","WorkingSet64":116195328,"PrivateMemorySize64":237756416,"managedBytes":4130480,"allocatedBytes":18496919424,"gc":[1188,792,792],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":31600,"sharedPasses":7900,"driverPasses":7900,"browserPasses":7900}
|
||||
{"cycle":3960,"elapsedSeconds":5260.4711176,"utc":"2026-11-10T19:12:25.592345Z","WorkingSet64":115834880,"PrivateMemorySize64":237543424,"managedBytes":3986744,"allocatedBytes":18544086800,"gc":[1191,794,794],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":31680,"sharedPasses":7920,"driverPasses":7920,"browserPasses":7920}
|
||||
{"cycle":3970,"elapsedSeconds":5273.6560463,"utc":"2026-11-10T19:12:38.7765168Z","WorkingSet64":116748288,"PrivateMemorySize64":238514176,"managedBytes":4191968,"allocatedBytes":18591503240,"gc":[1194,796,796],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":31760,"sharedPasses":7940,"driverPasses":7940,"browserPasses":7940}
|
||||
{"cycle":3980,"elapsedSeconds":5287.0780618,"utc":"2026-11-10T19:12:52.2033699Z","WorkingSet64":120549376,"PrivateMemorySize64":238456832,"managedBytes":3933064,"allocatedBytes":18638644256,"gc":[1197,798,798],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":31840,"sharedPasses":7960,"driverPasses":7960,"browserPasses":7960}
|
||||
{"cycle":3990,"elapsedSeconds":5300.2386385,"utc":"2026-11-10T19:13:05.35981Z","WorkingSet64":116281344,"PrivateMemorySize64":238809088,"managedBytes":4059496,"allocatedBytes":18686045672,"gc":[1200,800,800],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":31920,"sharedPasses":7980,"driverPasses":7980,"browserPasses":7980}
|
||||
{"cycle":4000,"elapsedSeconds":5313.4321402,"utc":"2026-11-10T19:13:18.5522027Z","WorkingSet64":115970048,"PrivateMemorySize64":238477312,"managedBytes":3986720,"allocatedBytes":18733363784,"gc":[1203,802,802],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":32000,"sharedPasses":8000,"driverPasses":8000,"browserPasses":8000}
|
||||
{"cycle":4010,"elapsedSeconds":5326.6204532,"utc":"2026-11-10T19:13:31.7406049Z","WorkingSet64":120508416,"PrivateMemorySize64":243015680,"managedBytes":4050832,"allocatedBytes":18780637368,"gc":[1206,804,804],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":32080,"sharedPasses":8020,"driverPasses":8020,"browserPasses":8020}
|
||||
{"cycle":4020,"elapsedSeconds":5339.8009168,"utc":"2026-11-10T19:13:44.9208525Z","WorkingSet64":116535296,"PrivateMemorySize64":239058944,"managedBytes":3999648,"allocatedBytes":18828062416,"gc":[1209,806,806],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":32160,"sharedPasses":8040,"driverPasses":8040,"browserPasses":8040}
|
||||
{"cycle":4030,"elapsedSeconds":5352.99487,"utc":"2026-11-10T19:13:58.1154394Z","WorkingSet64":116318208,"PrivateMemorySize64":238993408,"managedBytes":4065696,"allocatedBytes":18875461184,"gc":[1212,808,808],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":32240,"sharedPasses":8060,"driverPasses":8060,"browserPasses":8060}
|
||||
{"cycle":4040,"elapsedSeconds":5366.1798216,"utc":"2026-11-10T19:14:11.2997745Z","WorkingSet64":120360960,"PrivateMemorySize64":243036160,"managedBytes":4065416,"allocatedBytes":18922683736,"gc":[1215,810,810],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":32320,"sharedPasses":8080,"driverPasses":8080,"browserPasses":8080}
|
||||
{"cycle":4050,"elapsedSeconds":5379.3735217,"utc":"2026-11-10T19:14:24.4955573Z","WorkingSet64":115818496,"PrivateMemorySize64":238473216,"managedBytes":3993632,"allocatedBytes":18969993968,"gc":[1218,812,812],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":32400,"sharedPasses":8100,"driverPasses":8100,"browserPasses":8100}
|
||||
{"cycle":4060,"elapsedSeconds":5392.4722436,"utc":"2026-11-10T19:14:37.5990338Z","WorkingSet64":120197120,"PrivateMemorySize64":243015680,"managedBytes":4075624,"allocatedBytes":19017373272,"gc":[1221,814,814],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":32480,"sharedPasses":8120,"driverPasses":8120,"browserPasses":8120}
|
||||
{"cycle":4070,"elapsedSeconds":5405.651646,"utc":"2026-11-10T19:14:50.7730584Z","WorkingSet64":116289536,"PrivateMemorySize64":238841856,"managedBytes":4146208,"allocatedBytes":19065570184,"gc":[1224,816,816],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":32560,"sharedPasses":8140,"driverPasses":8140,"browserPasses":8140}
|
||||
{"cycle":4080,"elapsedSeconds":5418.8294594,"utc":"2026-11-10T19:15:03.9504794Z","WorkingSet64":115945472,"PrivateMemorySize64":238477312,"managedBytes":3987144,"allocatedBytes":19112715504,"gc":[1227,818,818],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":32640,"sharedPasses":8160,"driverPasses":8160,"browserPasses":8160}
|
||||
{"cycle":4090,"elapsedSeconds":5432.0374034,"utc":"2026-11-10T19:15:17.1601803Z","WorkingSet64":119640064,"PrivateMemorySize64":243015680,"managedBytes":4117304,"allocatedBytes":19160028664,"gc":[1230,820,820],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":32720,"sharedPasses":8180,"driverPasses":8180,"browserPasses":8180}
|
||||
{"cycle":4100,"elapsedSeconds":5445.2179978,"utc":"2026-11-10T19:15:30.3375728Z","WorkingSet64":115707904,"PrivateMemorySize64":238841856,"managedBytes":4065072,"allocatedBytes":19207398240,"gc":[1233,822,822],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":32800,"sharedPasses":8200,"driverPasses":8200,"browserPasses":8200}
|
||||
{"cycle":4110,"elapsedSeconds":5458.5514867,"utc":"2026-11-10T19:15:43.6738027Z","WorkingSet64":115470336,"PrivateMemorySize64":238653440,"managedBytes":4008096,"allocatedBytes":19254725512,"gc":[1236,824,824],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":32880,"sharedPasses":8220,"driverPasses":8220,"browserPasses":8220}
|
||||
{"cycle":4120,"elapsedSeconds":5471.7379288,"utc":"2026-11-10T19:15:56.8593109Z","WorkingSet64":119562240,"PrivateMemorySize64":242696192,"managedBytes":4000688,"allocatedBytes":19302079136,"gc":[1239,826,826],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":32960,"sharedPasses":8240,"driverPasses":8240,"browserPasses":8240}
|
||||
{"cycle":4130,"elapsedSeconds":5484.9562507,"utc":"2026-11-10T19:16:10.0781878Z","WorkingSet64":112144384,"PrivateMemorySize64":238743552,"managedBytes":4117832,"allocatedBytes":19349475504,"gc":[1242,828,828],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":33040,"sharedPasses":8260,"driverPasses":8260,"browserPasses":8260}
|
||||
{"cycle":4140,"elapsedSeconds":5498.1464981,"utc":"2026-11-10T19:16:23.2672877Z","WorkingSet64":112189440,"PrivateMemorySize64":238809088,"managedBytes":4118056,"allocatedBytes":19396632144,"gc":[1245,830,830],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":33120,"sharedPasses":8280,"driverPasses":8280,"browserPasses":8280}
|
||||
{"cycle":4150,"elapsedSeconds":5511.3163326,"utc":"2026-11-10T19:16:36.4363444Z","WorkingSet64":119017472,"PrivateMemorySize64":238809088,"managedBytes":4051480,"allocatedBytes":19443932616,"gc":[1248,832,832],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":33200,"sharedPasses":8300,"driverPasses":8300,"browserPasses":8300}
|
||||
{"cycle":4160,"elapsedSeconds":5524.5309388,"utc":"2026-11-10T19:16:49.6556238Z","WorkingSet64":118751232,"PrivateMemorySize64":238542848,"managedBytes":4052120,"allocatedBytes":19491166240,"gc":[1251,834,834],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":33280,"sharedPasses":8320,"driverPasses":8320,"browserPasses":8320}
|
||||
{"cycle":4170,"elapsedSeconds":5537.7220072,"utc":"2026-11-10T19:17:02.8451387Z","WorkingSet64":118878208,"PrivateMemorySize64":238739456,"managedBytes":4117344,"allocatedBytes":19538501200,"gc":[1254,836,836],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":33360,"sharedPasses":8340,"driverPasses":8340,"browserPasses":8340}
|
||||
{"cycle":4180,"elapsedSeconds":5550.9211684,"utc":"2026-11-10T19:17:16.0417485Z","WorkingSet64":118636544,"PrivateMemorySize64":238428160,"managedBytes":3938976,"allocatedBytes":19585669400,"gc":[1257,838,838],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":33440,"sharedPasses":8360,"driverPasses":8360,"browserPasses":8360}
|
||||
{"cycle":4190,"elapsedSeconds":5564.0375684,"utc":"2026-11-10T19:17:29.1581926Z","WorkingSet64":122945536,"PrivateMemorySize64":242737152,"managedBytes":4052080,"allocatedBytes":19633028448,"gc":[1260,840,840],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":33520,"sharedPasses":8380,"driverPasses":8380,"browserPasses":8380}
|
||||
{"cycle":4200,"elapsedSeconds":5577.243114,"utc":"2026-11-10T19:17:42.3638545Z","WorkingSet64":115806208,"PrivateMemorySize64":238678016,"managedBytes":4054832,"allocatedBytes":19680330576,"gc":[1263,842,842],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":33600,"sharedPasses":8400,"driverPasses":8400,"browserPasses":8400}
|
||||
{"cycle":4210,"elapsedSeconds":5590.4732,"utc":"2026-11-10T19:17:55.5936071Z","WorkingSet64":112427008,"PrivateMemorySize64":235294720,"managedBytes":4182696,"allocatedBytes":19727670840,"gc":[1266,844,844],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":33680,"sharedPasses":8420,"driverPasses":8420,"browserPasses":8420}
|
||||
{"cycle":4220,"elapsedSeconds":5603.6574161,"utc":"2026-11-10T19:18:08.7779418Z","WorkingSet64":118849536,"PrivateMemorySize64":238821376,"managedBytes":4118560,"allocatedBytes":19774876072,"gc":[1269,846,846],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":33760,"sharedPasses":8440,"driverPasses":8440,"browserPasses":8440}
|
||||
{"cycle":4230,"elapsedSeconds":5616.9644296,"utc":"2026-11-10T19:18:22.0853634Z","WorkingSet64":116146176,"PrivateMemorySize64":238542848,"managedBytes":4052248,"allocatedBytes":19822067312,"gc":[1272,848,848],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":33840,"sharedPasses":8460,"driverPasses":8460,"browserPasses":8460}
|
||||
{"cycle":4240,"elapsedSeconds":5630.1527451,"utc":"2026-11-10T19:18:35.2735179Z","WorkingSet64":120274944,"PrivateMemorySize64":242671616,"managedBytes":3987312,"allocatedBytes":19869276368,"gc":[1275,850,850],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":33920,"sharedPasses":8480,"driverPasses":8480,"browserPasses":8480}
|
||||
{"cycle":4250,"elapsedSeconds":5643.258075,"utc":"2026-11-10T19:18:48.3788452Z","WorkingSet64":120619008,"PrivateMemorySize64":243036160,"managedBytes":4130328,"allocatedBytes":19916603080,"gc":[1278,852,852],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":34000,"sharedPasses":8500,"driverPasses":8500,"browserPasses":8500}
|
||||
{"cycle":4260,"elapsedSeconds":5656.4796106,"utc":"2026-11-10T19:19:01.6002197Z","WorkingSet64":116424704,"PrivateMemorySize64":238821376,"managedBytes":3986568,"allocatedBytes":19963759592,"gc":[1281,854,854],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":34080,"sharedPasses":8520,"driverPasses":8520,"browserPasses":8520}
|
||||
{"cycle":4270,"elapsedSeconds":5669.646909,"utc":"2026-11-10T19:19:14.768944Z","WorkingSet64":112164864,"PrivateMemorySize64":234565632,"managedBytes":4051192,"allocatedBytes":20011050336,"gc":[1284,856,856],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":34160,"sharedPasses":8540,"driverPasses":8540,"browserPasses":8540}
|
||||
{"cycle":4280,"elapsedSeconds":5682.8394648,"utc":"2026-11-10T19:19:27.9626123Z","WorkingSet64":116408320,"PrivateMemorySize64":238804992,"managedBytes":4117976,"allocatedBytes":20058264848,"gc":[1287,858,858],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":34240,"sharedPasses":8560,"driverPasses":8560,"browserPasses":8560}
|
||||
{"cycle":4290,"elapsedSeconds":5696.1424054,"utc":"2026-11-10T19:19:41.265842Z","WorkingSet64":116006912,"PrivateMemorySize64":238411776,"managedBytes":3920976,"allocatedBytes":20105484200,"gc":[1290,860,860],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":34320,"sharedPasses":8580,"driverPasses":8580,"browserPasses":8580}
|
||||
{"cycle":4300,"elapsedSeconds":5709.4636964,"utc":"2026-11-10T19:19:54.5865621Z","WorkingSet64":114135040,"PrivateMemorySize64":238739456,"managedBytes":3986864,"allocatedBytes":20153830552,"gc":[1293,862,862],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":34400,"sharedPasses":8600,"driverPasses":8600,"browserPasses":8600}
|
||||
{"cycle":4310,"elapsedSeconds":5722.6223838,"utc":"2026-11-10T19:20:07.7425961Z","WorkingSet64":113922048,"PrivateMemorySize64":238477312,"managedBytes":3986584,"allocatedBytes":20201120472,"gc":[1296,864,864],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":34480,"sharedPasses":8620,"driverPasses":8620,"browserPasses":8620}
|
||||
{"cycle":4320,"elapsedSeconds":5735.8363918,"utc":"2026-11-10T19:20:20.9579718Z","WorkingSet64":118927360,"PrivateMemorySize64":238542848,"managedBytes":4052440,"allocatedBytes":20248450960,"gc":[1299,866,866],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":34560,"sharedPasses":8640,"driverPasses":8640,"browserPasses":8640}
|
||||
{"cycle":4330,"elapsedSeconds":5749.0586465,"utc":"2026-11-10T19:20:34.1804388Z","WorkingSet64":122449920,"PrivateMemorySize64":242606080,"managedBytes":3921688,"allocatedBytes":20295685776,"gc":[1302,868,868],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":34640,"sharedPasses":8660,"driverPasses":8660,"browserPasses":8660}
|
||||
{"cycle":4340,"elapsedSeconds":5762.2892765,"utc":"2026-11-10T19:20:47.4098321Z","WorkingSet64":118632448,"PrivateMemorySize64":238804992,"managedBytes":4051800,"allocatedBytes":20343032120,"gc":[1305,870,870],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":34720,"sharedPasses":8680,"driverPasses":8680,"browserPasses":8680}
|
||||
{"cycle":4350,"elapsedSeconds":5775.4195846,"utc":"2026-11-10T19:21:00.5412352Z","WorkingSet64":122613760,"PrivateMemorySize64":242688000,"managedBytes":4010408,"allocatedBytes":20390269328,"gc":[1308,872,872],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":34800,"sharedPasses":8700,"driverPasses":8700,"browserPasses":8700}
|
||||
{"cycle":4360,"elapsedSeconds":5788.4635996,"utc":"2026-11-10T19:21:13.5854732Z","WorkingSet64":122531840,"PrivateMemorySize64":242606080,"managedBytes":3929264,"allocatedBytes":20437564128,"gc":[1311,874,874],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":34880,"sharedPasses":8720,"driverPasses":8720,"browserPasses":8720}
|
||||
{"cycle":4370,"elapsedSeconds":5801.6784919,"utc":"2026-11-10T19:21:26.8009392Z","WorkingSet64":118317056,"PrivateMemorySize64":238804992,"managedBytes":4115120,"allocatedBytes":20484954072,"gc":[1314,876,876],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":34960,"sharedPasses":8740,"driverPasses":8740,"browserPasses":8740}
|
||||
{"cycle":4380,"elapsedSeconds":5814.8853536,"utc":"2026-11-10T19:21:40.0060554Z","WorkingSet64":122527744,"PrivateMemorySize64":243015680,"managedBytes":4117160,"allocatedBytes":20532150552,"gc":[1317,878,878],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":35040,"sharedPasses":8760,"driverPasses":8760,"browserPasses":8760}
|
||||
{"cycle":4390,"elapsedSeconds":5828.0861403,"utc":"2026-11-10T19:21:53.2062004Z","WorkingSet64":118050816,"PrivateMemorySize64":238559232,"managedBytes":4059520,"allocatedBytes":20579321328,"gc":[1320,880,880],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":35120,"sharedPasses":8780,"driverPasses":8780,"browserPasses":8780}
|
||||
{"cycle":4400,"elapsedSeconds":5841.3106131,"utc":"2026-11-10T19:22:06.4312153Z","WorkingSet64":119357440,"PrivateMemorySize64":238800896,"managedBytes":4059464,"allocatedBytes":20626569768,"gc":[1323,882,882],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":35200,"sharedPasses":8800,"driverPasses":8800,"browserPasses":8800}
|
||||
{"cycle":4410,"elapsedSeconds":5854.50526,"utc":"2026-11-10T19:22:19.6298345Z","WorkingSet64":119046144,"PrivateMemorySize64":238411776,"managedBytes":3920552,"allocatedBytes":20673780112,"gc":[1326,884,884],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":35280,"sharedPasses":8820,"driverPasses":8820,"browserPasses":8820}
|
||||
{"cycle":4420,"elapsedSeconds":5867.6676296,"utc":"2026-11-10T19:22:32.7931075Z","WorkingSet64":119455744,"PrivateMemorySize64":238821376,"managedBytes":4116600,"allocatedBytes":20721190608,"gc":[1329,886,886],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":35360,"sharedPasses":8840,"driverPasses":8840,"browserPasses":8840}
|
||||
{"cycle":4430,"elapsedSeconds":5880.9144492,"utc":"2026-11-10T19:22:46.0375909Z","WorkingSet64":119111680,"PrivateMemorySize64":238477312,"managedBytes":3986256,"allocatedBytes":20768362072,"gc":[1332,888,888],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":35440,"sharedPasses":8860,"driverPasses":8860,"browserPasses":8860}
|
||||
{"cycle":4440,"elapsedSeconds":5894.200753,"utc":"2026-11-10T19:22:59.3234916Z","WorkingSet64":119373824,"PrivateMemorySize64":238804992,"managedBytes":4051808,"allocatedBytes":20815638800,"gc":[1335,890,890],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":35520,"sharedPasses":8880,"driverPasses":8880,"browserPasses":8880}
|
||||
{"cycle":4450,"elapsedSeconds":5907.41241,"utc":"2026-11-10T19:23:12.5342293Z","WorkingSet64":119373824,"PrivateMemorySize64":238804992,"managedBytes":4117192,"allocatedBytes":20862865904,"gc":[1338,892,892],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":35600,"sharedPasses":8900,"driverPasses":8900,"browserPasses":8900}
|
||||
{"cycle":4460,"elapsedSeconds":5920.5786488,"utc":"2026-11-10T19:23:25.6999325Z","WorkingSet64":119373824,"PrivateMemorySize64":238825472,"managedBytes":4204752,"allocatedBytes":20910245512,"gc":[1341,894,894],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":35680,"sharedPasses":8920,"driverPasses":8920,"browserPasses":8920}
|
||||
{"cycle":4470,"elapsedSeconds":5933.783289,"utc":"2026-11-10T19:23:38.9076636Z","WorkingSet64":117010432,"PrivateMemorySize64":238477312,"managedBytes":3986800,"allocatedBytes":20957336520,"gc":[1344,896,896],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":35760,"sharedPasses":8940,"driverPasses":8940,"browserPasses":8940}
|
||||
{"cycle":4480,"elapsedSeconds":5946.9322959,"utc":"2026-11-10T19:23:52.0538473Z","WorkingSet64":121221120,"PrivateMemorySize64":242737152,"managedBytes":4060264,"allocatedBytes":21004731368,"gc":[1347,898,898],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":35840,"sharedPasses":8960,"driverPasses":8960,"browserPasses":8960}
|
||||
{"cycle":4490,"elapsedSeconds":5960.1149296,"utc":"2026-11-10T19:24:05.2362333Z","WorkingSet64":117338112,"PrivateMemorySize64":238804992,"managedBytes":4117664,"allocatedBytes":21051919344,"gc":[1350,900,900],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":35920,"sharedPasses":8980,"driverPasses":8980,"browserPasses":8980}
|
||||
{"cycle":4500,"elapsedSeconds":5973.3097143,"utc":"2026-11-10T19:24:18.4304225Z","WorkingSet64":116944896,"PrivateMemorySize64":242671616,"managedBytes":3995088,"allocatedBytes":21099166368,"gc":[1353,902,902],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":36000,"sharedPasses":9000,"driverPasses":9000,"browserPasses":9000}
|
||||
{"cycle":4510,"elapsedSeconds":5986.4949925,"utc":"2026-11-10T19:24:31.6217934Z","WorkingSet64":113094656,"PrivateMemorySize64":238841856,"managedBytes":4136848,"allocatedBytes":21146602576,"gc":[1356,904,904],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":36080,"sharedPasses":9020,"driverPasses":9020,"browserPasses":9020}
|
||||
{"cycle":4520,"elapsedSeconds":5999.6813214,"utc":"2026-11-10T19:24:44.804008Z","WorkingSet64":112750592,"PrivateMemorySize64":238477312,"managedBytes":3993880,"allocatedBytes":21193721232,"gc":[1359,906,906],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":36160,"sharedPasses":9040,"driverPasses":9040,"browserPasses":9040}
|
||||
{"cycle":4530,"elapsedSeconds":6013.0794661,"utc":"2026-11-10T19:24:58.2004566Z","WorkingSet64":112988160,"PrivateMemorySize64":238448640,"managedBytes":3989448,"allocatedBytes":21241965352,"gc":[1362,908,908],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":36240,"sharedPasses":9060,"driverPasses":9060,"browserPasses":9060}
|
||||
{"cycle":4540,"elapsedSeconds":6026.2845092,"utc":"2026-11-10T19:25:11.4070276Z","WorkingSet64":113311744,"PrivateMemorySize64":238796800,"managedBytes":4124584,"allocatedBytes":21289271104,"gc":[1365,910,910],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":36320,"sharedPasses":9080,"driverPasses":9080,"browserPasses":9080}
|
||||
{"cycle":4550,"elapsedSeconds":6039.4915966,"utc":"2026-11-10T19:25:24.6129566Z","WorkingSet64":117800960,"PrivateMemorySize64":238841856,"managedBytes":4083408,"allocatedBytes":21336391544,"gc":[1368,912,912],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":36400,"sharedPasses":9100,"driverPasses":9100,"browserPasses":9100}
|
||||
{"cycle":4560,"elapsedSeconds":6052.6990044,"utc":"2026-11-10T19:25:37.8198535Z","WorkingSet64":117518336,"PrivateMemorySize64":238538752,"managedBytes":4058512,"allocatedBytes":21383573296,"gc":[1371,914,914],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":36480,"sharedPasses":9120,"driverPasses":9120,"browserPasses":9120}
|
||||
{"cycle":4570,"elapsedSeconds":6065.9007795,"utc":"2026-11-10T19:25:51.0221451Z","WorkingSet64":121528320,"PrivateMemorySize64":243015680,"managedBytes":4141088,"allocatedBytes":21430825480,"gc":[1374,916,916],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":36560,"sharedPasses":9140,"driverPasses":9140,"browserPasses":9140}
|
||||
{"cycle":4580,"elapsedSeconds":6079.0842576,"utc":"2026-11-10T19:26:04.2090215Z","WorkingSet64":117002240,"PrivateMemorySize64":238538752,"managedBytes":4058744,"allocatedBytes":21477997224,"gc":[1377,918,918],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":36640,"sharedPasses":9160,"driverPasses":9160,"browserPasses":9160}
|
||||
{"cycle":4590,"elapsedSeconds":6092.275871,"utc":"2026-11-10T19:26:17.3968782Z","WorkingSet64":117301248,"PrivateMemorySize64":238800896,"managedBytes":4059496,"allocatedBytes":21525255696,"gc":[1380,920,920],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":36720,"sharedPasses":9180,"driverPasses":9180,"browserPasses":9180}
|
||||
{"cycle":4600,"elapsedSeconds":6105.4664507,"utc":"2026-11-10T19:26:30.5893728Z","WorkingSet64":117301248,"PrivateMemorySize64":238800896,"managedBytes":4058936,"allocatedBytes":21572502848,"gc":[1383,922,922],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":36800,"sharedPasses":9200,"driverPasses":9200,"browserPasses":9200}
|
||||
{"cycle":4610,"elapsedSeconds":6118.7135893,"utc":"2026-11-10T19:26:43.834836Z","WorkingSet64":115064832,"PrivateMemorySize64":238473216,"managedBytes":3993272,"allocatedBytes":21619832216,"gc":[1386,924,924],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":36880,"sharedPasses":9220,"driverPasses":9220,"browserPasses":9220}
|
||||
{"cycle":4620,"elapsedSeconds":6131.9146606,"utc":"2026-11-10T19:26:57.0385977Z","WorkingSet64":115138560,"PrivateMemorySize64":238538752,"managedBytes":4059368,"allocatedBytes":21667064424,"gc":[1389,926,926],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":36960,"sharedPasses":9240,"driverPasses":9240,"browserPasses":9240}
|
||||
{"cycle":4630,"elapsedSeconds":6145.07493,"utc":"2026-11-10T19:27:10.1955085Z","WorkingSet64":115400704,"PrivateMemorySize64":238800896,"managedBytes":4124672,"allocatedBytes":21714330904,"gc":[1392,928,928],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":37040,"sharedPasses":9260,"driverPasses":9260,"browserPasses":9260}
|
||||
{"cycle":4640,"elapsedSeconds":6158.3206657,"utc":"2026-11-10T19:27:23.4544591Z","WorkingSet64":116514816,"PrivateMemorySize64":243015680,"managedBytes":4074752,"allocatedBytes":21761491936,"gc":[1395,930,930],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":37120,"sharedPasses":9280,"driverPasses":9280,"browserPasses":9280}
|
||||
{"cycle":4650,"elapsedSeconds":6171.497499,"utc":"2026-11-10T19:27:36.6216647Z","WorkingSet64":117235712,"PrivateMemorySize64":243036160,"managedBytes":4138696,"allocatedBytes":21808716864,"gc":[1398,932,932],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":37200,"sharedPasses":9300,"driverPasses":9300,"browserPasses":9300}
|
||||
{"cycle":4660,"elapsedSeconds":6184.8124801,"utc":"2026-11-10T19:27:49.938559Z","WorkingSet64":116957184,"PrivateMemorySize64":242696192,"managedBytes":3999488,"allocatedBytes":21855895072,"gc":[1401,934,934],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":37280,"sharedPasses":9320,"driverPasses":9320,"browserPasses":9320}
|
||||
{"cycle":4670,"elapsedSeconds":6198.018103,"utc":"2026-11-10T19:28:03.1413054Z","WorkingSet64":112758784,"PrivateMemorySize64":238497792,"managedBytes":3998216,"allocatedBytes":21903194216,"gc":[1404,936,936],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":37360,"sharedPasses":9340,"driverPasses":9340,"browserPasses":9340}
|
||||
{"cycle":4680,"elapsedSeconds":6211.1886598,"utc":"2026-11-10T19:28:16.3279143Z","WorkingSet64":113127424,"PrivateMemorySize64":238608384,"managedBytes":3992800,"allocatedBytes":21950465880,"gc":[1407,938,938],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":37440,"sharedPasses":9360,"driverPasses":9360,"browserPasses":9360}
|
||||
{"cycle":4690,"elapsedSeconds":6224.3907958,"utc":"2026-11-10T19:28:29.5192565Z","WorkingSet64":109191168,"PrivateMemorySize64":234684416,"managedBytes":3928200,"allocatedBytes":21997768736,"gc":[1410,940,940],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":37520,"sharedPasses":9380,"driverPasses":9380,"browserPasses":9380}
|
||||
{"cycle":4700,"elapsedSeconds":6237.6652159,"utc":"2026-11-10T19:28:42.78991Z","WorkingSet64":113373184,"PrivateMemorySize64":238821376,"managedBytes":4076504,"allocatedBytes":22045100088,"gc":[1413,942,942],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":37600,"sharedPasses":9400,"driverPasses":9400,"browserPasses":9400}
|
||||
{"cycle":4710,"elapsedSeconds":6250.8680635,"utc":"2026-11-10T19:28:55.9972871Z","WorkingSet64":112799744,"PrivateMemorySize64":238407680,"managedBytes":3928536,"allocatedBytes":22092321136,"gc":[1416,944,944],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":37680,"sharedPasses":9420,"driverPasses":9420,"browserPasses":9420}
|
||||
{"cycle":4720,"elapsedSeconds":6263.9673657,"utc":"2026-11-10T19:29:09.0921219Z","WorkingSet64":117030912,"PrivateMemorySize64":242606080,"managedBytes":3920824,"allocatedBytes":22139649760,"gc":[1419,946,946],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":37760,"sharedPasses":9440,"driverPasses":9440,"browserPasses":9440}
|
||||
{"cycle":4730,"elapsedSeconds":6277.1654307,"utc":"2026-11-10T19:29:22.2885143Z","WorkingSet64":112898048,"PrivateMemorySize64":238473216,"managedBytes":3993776,"allocatedBytes":22187080472,"gc":[1422,948,948],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":37840,"sharedPasses":9460,"driverPasses":9460,"browserPasses":9460}
|
||||
{"cycle":4740,"elapsedSeconds":6290.3636862,"utc":"2026-11-10T19:29:35.488547Z","WorkingSet64":113213440,"PrivateMemorySize64":238800896,"managedBytes":4125568,"allocatedBytes":22234324152,"gc":[1425,950,950],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":37920,"sharedPasses":9480,"driverPasses":9480,"browserPasses":9480}
|
||||
{"cycle":4750,"elapsedSeconds":6303.7261523,"utc":"2026-11-10T19:29:48.8488375Z","WorkingSet64":113274880,"PrivateMemorySize64":238411776,"managedBytes":3929424,"allocatedBytes":22282483392,"gc":[1428,952,952],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":38000,"sharedPasses":9500,"driverPasses":9500,"browserPasses":9500}
|
||||
{"cycle":4760,"elapsedSeconds":6317.0165925,"utc":"2026-11-10T19:30:02.1409803Z","WorkingSet64":113676288,"PrivateMemorySize64":238804992,"managedBytes":4124344,"allocatedBytes":22329868424,"gc":[1431,954,954],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":38080,"sharedPasses":9520,"driverPasses":9520,"browserPasses":9520}
|
||||
{"cycle":4770,"elapsedSeconds":6330.2058949,"utc":"2026-11-10T19:30:15.3267775Z","WorkingSet64":113676288,"PrivateMemorySize64":238804992,"managedBytes":4068088,"allocatedBytes":22376993912,"gc":[1434,956,956],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":38160,"sharedPasses":9540,"driverPasses":9540,"browserPasses":9540}
|
||||
{"cycle":4780,"elapsedSeconds":6343.4448178,"utc":"2026-11-10T19:30:28.5659763Z","WorkingSet64":112865280,"PrivateMemorySize64":238825472,"managedBytes":4125280,"allocatedBytes":22424311448,"gc":[1437,958,958],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":38240,"sharedPasses":9560,"driverPasses":9560,"browserPasses":9560}
|
||||
{"cycle":4790,"elapsedSeconds":6356.6536327,"utc":"2026-11-10T19:30:41.7775935Z","WorkingSet64":112865280,"PrivateMemorySize64":238432256,"managedBytes":3938040,"allocatedBytes":22471487088,"gc":[1440,960,960],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":38320,"sharedPasses":9580,"driverPasses":9580,"browserPasses":9580}
|
||||
{"cycle":4800,"elapsedSeconds":6369.8412654,"utc":"2026-11-10T19:30:54.9784635Z","WorkingSet64":113262592,"PrivateMemorySize64":238546944,"managedBytes":3931112,"allocatedBytes":22518928560,"gc":[1443,962,962],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":38400,"sharedPasses":9600,"driverPasses":9600,"browserPasses":9600}
|
||||
{"cycle":4810,"elapsedSeconds":6383.0050634,"utc":"2026-11-10T19:31:08.1344211Z","WorkingSet64":109965312,"PrivateMemorySize64":235368448,"managedBytes":4154640,"allocatedBytes":22566430632,"gc":[1446,964,964],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":38480,"sharedPasses":9620,"driverPasses":9620,"browserPasses":9620}
|
||||
{"cycle":4820,"elapsedSeconds":6396.4128499,"utc":"2026-11-10T19:31:21.5470456Z","WorkingSet64":122699776,"PrivateMemorySize64":243056640,"managedBytes":4153752,"allocatedBytes":22613653672,"gc":[1449,966,966],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":38560,"sharedPasses":9640,"driverPasses":9640,"browserPasses":9640}
|
||||
{"cycle":4830,"elapsedSeconds":6409.8899742,"utc":"2026-11-10T19:31:35.0148456Z","WorkingSet64":118222848,"PrivateMemorySize64":238538752,"managedBytes":4059152,"allocatedBytes":22660752464,"gc":[1452,968,968],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":38640,"sharedPasses":9660,"driverPasses":9660,"browserPasses":9660}
|
||||
{"cycle":4840,"elapsedSeconds":6423.0769856,"utc":"2026-11-10T19:31:48.1984714Z","WorkingSet64":117592064,"PrivateMemorySize64":238489600,"managedBytes":4009600,"allocatedBytes":22707986512,"gc":[1455,970,970],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":38720,"sharedPasses":9680,"driverPasses":9680,"browserPasses":9680}
|
||||
{"cycle":4850,"elapsedSeconds":6436.2743442,"utc":"2026-11-10T19:32:01.3946522Z","WorkingSet64":117641216,"PrivateMemorySize64":238538752,"managedBytes":4059072,"allocatedBytes":22755224496,"gc":[1458,972,972],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":38800,"sharedPasses":9700,"driverPasses":9700,"browserPasses":9700}
|
||||
{"cycle":4860,"elapsedSeconds":6449.4308089,"utc":"2026-11-10T19:32:14.5532745Z","WorkingSet64":117903360,"PrivateMemorySize64":238800896,"managedBytes":4050864,"allocatedBytes":22802404296,"gc":[1461,974,974],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":38880,"sharedPasses":9720,"driverPasses":9720,"browserPasses":9720}
|
||||
{"cycle":4870,"elapsedSeconds":6462.6500779,"utc":"2026-11-10T19:32:27.7727388Z","WorkingSet64":122109952,"PrivateMemorySize64":243015680,"managedBytes":4125408,"allocatedBytes":22849763944,"gc":[1464,976,976],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":38960,"sharedPasses":9740,"driverPasses":9740,"browserPasses":9740}
|
||||
{"cycle":4880,"elapsedSeconds":6475.8728647,"utc":"2026-11-10T19:32:41.0028163Z","WorkingSet64":119013376,"PrivateMemorySize64":243015680,"managedBytes":4076344,"allocatedBytes":22896968168,"gc":[1467,978,978],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":39040,"sharedPasses":9760,"driverPasses":9760,"browserPasses":9760}
|
||||
{"cycle":4890,"elapsedSeconds":6489.4704012,"utc":"2026-11-10T19:32:54.5983217Z","WorkingSet64":119017472,"PrivateMemorySize64":243036160,"managedBytes":4087976,"allocatedBytes":22944237912,"gc":[1470,980,980],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":39120,"sharedPasses":9780,"driverPasses":9780,"browserPasses":9780}
|
||||
{"cycle":4900,"elapsedSeconds":6502.6634906,"utc":"2026-11-10T19:33:07.7844883Z","WorkingSet64":119189504,"PrivateMemorySize64":238821376,"managedBytes":4123960,"allocatedBytes":22991398584,"gc":[1473,982,982],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":39200,"sharedPasses":9800,"driverPasses":9800,"browserPasses":9800}
|
||||
{"cycle":4910,"elapsedSeconds":6515.9048549,"utc":"2026-11-10T19:33:21.0342868Z","WorkingSet64":118906880,"PrivateMemorySize64":238542848,"managedBytes":4059704,"allocatedBytes":23038551000,"gc":[1476,984,984],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":39280,"sharedPasses":9820,"driverPasses":9820,"browserPasses":9820}
|
||||
{"cycle":4920,"elapsedSeconds":6529.1831635,"utc":"2026-11-10T19:33:34.3559789Z","WorkingSet64":118784000,"PrivateMemorySize64":238477312,"managedBytes":3994304,"allocatedBytes":23085855384,"gc":[1479,986,986],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":39360,"sharedPasses":9840,"driverPasses":9840,"browserPasses":9840}
|
||||
{"cycle":4930,"elapsedSeconds":6543.17755,"utc":"2026-11-10T19:33:48.3119325Z","WorkingSet64":118775808,"PrivateMemorySize64":238407680,"managedBytes":3928464,"allocatedBytes":23133230928,"gc":[1482,988,988],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":39440,"sharedPasses":9860,"driverPasses":9860,"browserPasses":9860}
|
||||
{"cycle":4940,"elapsedSeconds":6556.8265556,"utc":"2026-11-10T19:34:01.9503987Z","WorkingSet64":119091200,"PrivateMemorySize64":238735360,"managedBytes":4007232,"allocatedBytes":23180557088,"gc":[1485,990,990],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":39520,"sharedPasses":9880,"driverPasses":9880,"browserPasses":9880}
|
||||
{"cycle":4950,"elapsedSeconds":6570.0225311,"utc":"2026-11-10T19:34:15.1508144Z","WorkingSet64":122916864,"PrivateMemorySize64":242606080,"managedBytes":3921248,"allocatedBytes":23227888456,"gc":[1488,992,992],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":39600,"sharedPasses":9900,"driverPasses":9900,"browserPasses":9900}
|
||||
{"cycle":4960,"elapsedSeconds":6583.2502677,"utc":"2026-11-10T19:34:28.3821044Z","WorkingSet64":118792192,"PrivateMemorySize64":238477312,"managedBytes":3994432,"allocatedBytes":23275226456,"gc":[1491,994,994],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":39680,"sharedPasses":9920,"driverPasses":9920,"browserPasses":9920}
|
||||
{"cycle":4970,"elapsedSeconds":6596.4421822,"utc":"2026-11-10T19:34:41.5631278Z","WorkingSet64":123322368,"PrivateMemorySize64":243036160,"managedBytes":4130192,"allocatedBytes":23322674512,"gc":[1494,996,996],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":39760,"sharedPasses":9940,"driverPasses":9940,"browserPasses":9940}
|
||||
{"cycle":4980,"elapsedSeconds":6609.9071603,"utc":"2026-11-10T19:34:55.0294437Z","WorkingSet64":115003392,"PrivateMemorySize64":238841856,"managedBytes":4130200,"allocatedBytes":23370810824,"gc":[1497,998,998],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":39840,"sharedPasses":9960,"driverPasses":9960,"browserPasses":9960}
|
||||
{"cycle":4990,"elapsedSeconds":6623.0875258,"utc":"2026-11-10T19:35:08.246767Z","WorkingSet64":115658752,"PrivateMemorySize64":238546944,"managedBytes":3930520,"allocatedBytes":23417970072,"gc":[1500,1000,1000],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":39920,"sharedPasses":9980,"driverPasses":9980,"browserPasses":9980}
|
||||
{"cycle":5000,"elapsedSeconds":6636.2656837,"utc":"2026-11-10T19:35:21.3871775Z","WorkingSet64":112214016,"PrivateMemorySize64":235089920,"managedBytes":4051936,"allocatedBytes":23465275520,"gc":[1503,1002,1002],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":40000,"sharedPasses":10000,"driverPasses":10000,"browserPasses":10000}
|
||||
{"cycle":5010,"elapsedSeconds":6649.4581661,"utc":"2026-11-10T19:35:34.5860256Z","WorkingSet64":118226944,"PrivateMemorySize64":238522368,"managedBytes":4006736,"allocatedBytes":23512544264,"gc":[1506,1004,1004],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":40080,"sharedPasses":10020,"driverPasses":10020,"browserPasses":10020}
|
||||
{"cycle":5020,"elapsedSeconds":6662.7255967,"utc":"2026-11-10T19:35:47.8510405Z","WorkingSet64":118534144,"PrivateMemorySize64":238788608,"managedBytes":4112568,"allocatedBytes":23559813432,"gc":[1509,1006,1006],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":40160,"sharedPasses":10040,"driverPasses":10040,"browserPasses":10040}
|
||||
{"cycle":5030,"elapsedSeconds":6675.942992,"utc":"2026-11-10T19:36:01.0667856Z","WorkingSet64":118525952,"PrivateMemorySize64":238809088,"managedBytes":4091600,"allocatedBytes":23606996336,"gc":[1512,1008,1008],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":40240,"sharedPasses":10060,"driverPasses":10060,"browserPasses":10060}
|
||||
{"cycle":5040,"elapsedSeconds":6689.1504501,"utc":"2026-11-10T19:36:14.2732522Z","WorkingSet64":84996096,"PrivateMemorySize64":243003392,"managedBytes":4051304,"allocatedBytes":23654226640,"gc":[1515,1010,1010],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":40320,"sharedPasses":10080,"driverPasses":10080,"browserPasses":10080}
|
||||
{"cycle":5050,"elapsedSeconds":6702.3761049,"utc":"2026-11-10T19:36:27.4978222Z","WorkingSet64":78536704,"PrivateMemorySize64":242667520,"managedBytes":3993856,"allocatedBytes":23701567352,"gc":[1518,1012,1012],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":40400,"sharedPasses":10100,"driverPasses":10100,"browserPasses":10100}
|
||||
{"cycle":5060,"elapsedSeconds":6715.5686555,"utc":"2026-11-10T19:36:40.6899593Z","WorkingSet64":74088448,"PrivateMemorySize64":238800896,"managedBytes":4069776,"allocatedBytes":23748836984,"gc":[1521,1014,1014],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":40480,"sharedPasses":10120,"driverPasses":10120,"browserPasses":10120}
|
||||
{"cycle":5070,"elapsedSeconds":6729.2253323,"utc":"2026-11-10T19:36:54.3669364Z","WorkingSet64":73740288,"PrivateMemorySize64":238473216,"managedBytes":3992600,"allocatedBytes":23796110264,"gc":[1524,1016,1016],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":40560,"sharedPasses":10140,"driverPasses":10140,"browserPasses":10140}
|
||||
{"cycle":5080,"elapsedSeconds":6742.7711569,"utc":"2026-11-10T19:37:07.9108312Z","WorkingSet64":71524352,"PrivateMemorySize64":238473216,"managedBytes":3993256,"allocatedBytes":23843390840,"gc":[1527,1018,1018],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":40640,"sharedPasses":10160,"driverPasses":10160,"browserPasses":10160}
|
||||
{"cycle":5090,"elapsedSeconds":6755.9754822,"utc":"2026-11-10T19:37:21.1005986Z","WorkingSet64":80719872,"PrivateMemorySize64":243015680,"managedBytes":4072480,"allocatedBytes":23890872280,"gc":[1530,1020,1020],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":40720,"sharedPasses":10180,"driverPasses":10180,"browserPasses":10180}
|
||||
{"cycle":5100,"elapsedSeconds":6769.1815489,"utc":"2026-11-10T19:37:34.3029141Z","WorkingSet64":80785408,"PrivateMemorySize64":243036160,"managedBytes":4072080,"allocatedBytes":23938190040,"gc":[1533,1022,1022],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":40800,"sharedPasses":10200,"driverPasses":10200,"browserPasses":10200}
|
||||
{"cycle":5110,"elapsedSeconds":6782.6356772,"utc":"2026-11-10T19:37:47.7594223Z","WorkingSet64":76075008,"PrivateMemorySize64":238473216,"managedBytes":3993192,"allocatedBytes":23985382032,"gc":[1536,1024,1024],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":40880,"sharedPasses":10220,"driverPasses":10220,"browserPasses":10220}
|
||||
{"cycle":5120,"elapsedSeconds":6796.0574954,"utc":"2026-11-10T19:38:01.1791795Z","WorkingSet64":76414976,"PrivateMemorySize64":238800896,"managedBytes":4069792,"allocatedBytes":24032761632,"gc":[1539,1026,1026],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":40960,"sharedPasses":10240,"driverPasses":10240,"browserPasses":10240}
|
||||
{"cycle":5130,"elapsedSeconds":6810.0304017,"utc":"2026-11-10T19:38:15.1499803Z","WorkingSet64":80269312,"PrivateMemorySize64":242667520,"managedBytes":3992520,"allocatedBytes":24080041584,"gc":[1542,1028,1028],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":41040,"sharedPasses":10260,"driverPasses":10260,"browserPasses":10260}
|
||||
{"cycle":5140,"elapsedSeconds":6823.2193916,"utc":"2026-11-10T19:38:28.3394217Z","WorkingSet64":76140544,"PrivateMemorySize64":238538752,"managedBytes":4059336,"allocatedBytes":24127298840,"gc":[1545,1030,1030],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":41120,"sharedPasses":10280,"driverPasses":10280,"browserPasses":10280}
|
||||
{"cycle":5150,"elapsedSeconds":6836.4186724,"utc":"2026-11-10T19:38:41.5397392Z","WorkingSet64":71647232,"PrivateMemorySize64":238563328,"managedBytes":4059656,"allocatedBytes":24174495824,"gc":[1548,1032,1032],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":41200,"sharedPasses":10300,"driverPasses":10300,"browserPasses":10300}
|
||||
{"cycle":5160,"elapsedSeconds":6849.8761838,"utc":"2026-11-10T19:38:55.0058003Z","WorkingSet64":71593984,"PrivateMemorySize64":238501888,"managedBytes":4008752,"allocatedBytes":24221822152,"gc":[1551,1034,1034],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":41280,"sharedPasses":10320,"driverPasses":10320,"browserPasses":10320}
|
||||
{"cycle":5170,"elapsedSeconds":6863.4352003,"utc":"2026-11-10T19:39:08.5567876Z","WorkingSet64":76111872,"PrivateMemorySize64":243023872,"managedBytes":4064416,"allocatedBytes":24269196400,"gc":[1554,1036,1036],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":41360,"sharedPasses":10340,"driverPasses":10340,"browserPasses":10340}
|
||||
{"cycle":5180,"elapsedSeconds":6876.605627,"utc":"2026-11-10T19:39:21.7262022Z","WorkingSet64":72794112,"PrivateMemorySize64":238944256,"managedBytes":4051656,"allocatedBytes":24316529048,"gc":[1557,1038,1038],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":41440,"sharedPasses":10360,"driverPasses":10360,"browserPasses":10360}
|
||||
{"cycle":5190,"elapsedSeconds":6889.7919225,"utc":"2026-11-10T19:39:34.9153901Z","WorkingSet64":69259264,"PrivateMemorySize64":235216896,"managedBytes":4124776,"allocatedBytes":24363754176,"gc":[1560,1040,1040],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":41520,"sharedPasses":10380,"driverPasses":10380,"browserPasses":10380}
|
||||
{"cycle":5200,"elapsedSeconds":6902.9686972,"utc":"2026-11-10T19:39:48.0926373Z","WorkingSet64":76718080,"PrivateMemorySize64":242667520,"managedBytes":3993496,"allocatedBytes":24411014544,"gc":[1563,1042,1042],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":41600,"sharedPasses":10400,"driverPasses":10400,"browserPasses":10400}
|
||||
{"cycle":5210,"elapsedSeconds":6916.28353,"utc":"2026-11-10T19:40:01.4099085Z","WorkingSet64":72908800,"PrivateMemorySize64":238538752,"managedBytes":4059064,"allocatedBytes":24459233824,"gc":[1566,1044,1044],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":41680,"sharedPasses":10420,"driverPasses":10420,"browserPasses":10420}
|
||||
{"cycle":5220,"elapsedSeconds":6929.4862049,"utc":"2026-11-10T19:40:14.6077036Z","WorkingSet64":76505088,"PrivateMemorySize64":243015680,"managedBytes":4115936,"allocatedBytes":24506448480,"gc":[1569,1046,1046],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":41760,"sharedPasses":10440,"driverPasses":10440,"browserPasses":10440}
|
||||
{"cycle":5230,"elapsedSeconds":6942.686536,"utc":"2026-11-10T19:40:27.8068466Z","WorkingSet64":72048640,"PrivateMemorySize64":238522368,"managedBytes":4007520,"allocatedBytes":24553727008,"gc":[1572,1048,1048],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":41840,"sharedPasses":10460,"driverPasses":10460,"browserPasses":10460}
|
||||
{"cycle":5240,"elapsedSeconds":6955.8743857,"utc":"2026-11-10T19:40:40.9986371Z","WorkingSet64":76652544,"PrivateMemorySize64":243044352,"managedBytes":4073744,"allocatedBytes":24600989400,"gc":[1575,1050,1050],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":41920,"sharedPasses":10480,"driverPasses":10480,"browserPasses":10480}
|
||||
{"cycle":5250,"elapsedSeconds":6969.0821747,"utc":"2026-11-10T19:40:54.2132521Z","WorkingSet64":71778304,"PrivateMemorySize64":238518272,"managedBytes":4005784,"allocatedBytes":24648365920,"gc":[1578,1052,1052],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":42000,"sharedPasses":10500,"driverPasses":10500,"browserPasses":10500}
|
||||
{"cycle":5260,"elapsedSeconds":6982.2743101,"utc":"2026-11-10T19:41:07.4005649Z","WorkingSet64":76554240,"PrivateMemorySize64":246845440,"managedBytes":3932976,"allocatedBytes":24695635960,"gc":[1581,1054,1054],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":42080,"sharedPasses":10520,"driverPasses":10520,"browserPasses":10520}
|
||||
{"cycle":5270,"elapsedSeconds":6995.454983,"utc":"2026-11-10T19:41:20.5750363Z","WorkingSet64":77111296,"PrivateMemorySize64":247332864,"managedBytes":4142304,"allocatedBytes":24743109824,"gc":[1584,1056,1056],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":42160,"sharedPasses":10540,"driverPasses":10540,"browserPasses":10540}
|
||||
{"cycle":5280,"elapsedSeconds":7008.6546482,"utc":"2026-11-10T19:41:33.7747507Z","WorkingSet64":72658944,"PrivateMemorySize64":243404800,"managedBytes":4059128,"allocatedBytes":24790220704,"gc":[1587,1058,1058],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":42240,"sharedPasses":10560,"driverPasses":10560,"browserPasses":10560}
|
||||
{"cycle":5290,"elapsedSeconds":7022.1668582,"utc":"2026-11-10T19:41:47.3681167Z","WorkingSet64":76464128,"PrivateMemorySize64":247218176,"managedBytes":4134640,"allocatedBytes":24837586664,"gc":[1590,1060,1060],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":42320,"sharedPasses":10580,"driverPasses":10580,"browserPasses":10580}
|
||||
{"cycle":5300,"elapsedSeconds":7039.9280885,"utc":"2026-11-10T19:42:05.1661967Z","WorkingSet64":78688256,"PrivateMemorySize64":251043840,"managedBytes":3945384,"allocatedBytes":24884984856,"gc":[1593,1062,1062],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":42400,"sharedPasses":10600,"driverPasses":10600,"browserPasses":10600}
|
||||
{"cycle":5310,"elapsedSeconds":7055.1278862,"utc":"2026-11-10T19:42:20.2662731Z","WorkingSet64":80162816,"PrivateMemorySize64":255332352,"managedBytes":3951160,"allocatedBytes":24932626040,"gc":[1596,1064,1064],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":42480,"sharedPasses":10620,"driverPasses":10620,"browserPasses":10620}
|
||||
{"cycle":5320,"elapsedSeconds":7069.0184359,"utc":"2026-11-10T19:42:34.139969Z","WorkingSet64":81805312,"PrivateMemorySize64":272982016,"managedBytes":4012896,"allocatedBytes":24980315048,"gc":[1599,1066,1066],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":42560,"sharedPasses":10640,"driverPasses":10640,"browserPasses":10640}
|
||||
{"cycle":5330,"elapsedSeconds":7082.4373318,"utc":"2026-11-10T19:42:47.5640317Z","WorkingSet64":80338944,"PrivateMemorySize64":254656512,"managedBytes":4621376,"allocatedBytes":25027699008,"gc":[1602,1068,1068],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":42640,"sharedPasses":10660,"driverPasses":10660,"browserPasses":10660}
|
||||
{"cycle":5340,"elapsedSeconds":7095.5659671,"utc":"2026-11-10T19:43:00.6844669Z","WorkingSet64":87146496,"PrivateMemorySize64":261574656,"managedBytes":4762080,"allocatedBytes":25074535664,"gc":[1605,1070,1070],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":42720,"sharedPasses":10680,"driverPasses":10680,"browserPasses":10680}
|
||||
{"cycle":5350,"elapsedSeconds":7108.7394385,"utc":"2026-11-10T19:43:13.85707Z","WorkingSet64":87232512,"PrivateMemorySize64":261574656,"managedBytes":4793456,"allocatedBytes":25121278344,"gc":[1608,1072,1072],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":42800,"sharedPasses":10700,"driverPasses":10700,"browserPasses":10700}
|
||||
{"cycle":5360,"elapsedSeconds":7121.9721312,"utc":"2026-11-10T19:43:27.0909124Z","WorkingSet64":87257088,"PrivateMemorySize64":261591040,"managedBytes":4138120,"allocatedBytes":25168001184,"gc":[1611,1074,1074],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":42880,"sharedPasses":10720,"driverPasses":10720,"browserPasses":10720}
|
||||
{"cycle":5370,"elapsedSeconds":7135.1901751,"utc":"2026-11-10T19:43:40.3089895Z","WorkingSet64":87400448,"PrivateMemorySize64":253333504,"managedBytes":4119904,"allocatedBytes":25215100472,"gc":[1614,1076,1076],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":42960,"sharedPasses":10740,"driverPasses":10740,"browserPasses":10740}
|
||||
{"cycle":5380,"elapsedSeconds":7148.3734604,"utc":"2026-11-10T19:43:53.4910701Z","WorkingSet64":83238912,"PrivateMemorySize64":249159680,"managedBytes":4061016,"allocatedBytes":25262295328,"gc":[1617,1078,1078],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":43040,"sharedPasses":10760,"driverPasses":10760,"browserPasses":10760}
|
||||
{"cycle":5390,"elapsedSeconds":7161.5902538,"utc":"2026-11-10T19:44:06.7080138Z","WorkingSet64":87179264,"PrivateMemorySize64":253087744,"managedBytes":4124728,"allocatedBytes":25309616456,"gc":[1620,1080,1080],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":43120,"sharedPasses":10780,"driverPasses":10780,"browserPasses":10780}
|
||||
{"cycle":5400,"elapsedSeconds":7174.5805733,"utc":"2026-11-10T19:44:19.6989722Z","WorkingSet64":87367680,"PrivateMemorySize64":253272064,"managedBytes":4126184,"allocatedBytes":25356771080,"gc":[1623,1082,1082],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":43200,"sharedPasses":10800,"driverPasses":10800,"browserPasses":10800}
|
||||
{"cycle":5410,"elapsedSeconds":7187.6831487,"utc":"2026-11-10T19:44:32.8064007Z","WorkingSet64":87429120,"PrivateMemorySize64":253272064,"managedBytes":4059880,"allocatedBytes":25403934920,"gc":[1626,1084,1084],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":43280,"sharedPasses":10820,"driverPasses":10820,"browserPasses":10820}
|
||||
{"cycle":5420,"elapsedSeconds":7200.9350359,"utc":"2026-11-10T19:44:46.0522796Z","WorkingSet64":84873216,"PrivateMemorySize64":253251584,"managedBytes":3987480,"allocatedBytes":25451104712,"gc":[1629,1086,1086],"fdCount":148,"ownedRootFdCount":4,"ownedPkiFdCount":0,"survivingProductionObjects":0,"managerDisposalsVerified":43360,"sharedPasses":10840,"driverPasses":10840,"browserPasses":10840}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
[
|
||||
{
|
||||
"name": "Opc.Ua.Core",
|
||||
"version": "2.0.1.0",
|
||||
"sha256": "9d0857aa6b7f3c49037d4ea557b4da8edf07e146069464481783e66c5c9cd35a"
|
||||
},
|
||||
{
|
||||
"name": "Opc.Ua.Types",
|
||||
"version": "2.0.1.0",
|
||||
"sha256": "c542f69882f4e8a4ebd5ccc28a3881d6967c7ffb6dcc3c56dc6c776f39c2ede1"
|
||||
},
|
||||
{
|
||||
"name": "Opc.Ua.Security.Certificates",
|
||||
"version": "2.0.1.0",
|
||||
"sha256": "7f4969aceb2f0db5328c6882cb4b117342f3b18905c38acdba600683860c4fd0"
|
||||
},
|
||||
{
|
||||
"name": "Opc.Ua.Core.Types",
|
||||
"version": "2.0.1.0",
|
||||
"sha256": "a7dd141b8dabe8848ff28c2fb8e16a85a0508e6a49e6b1a1c1e267175b99af96"
|
||||
},
|
||||
{
|
||||
"name": "Opc.Ua.Client",
|
||||
"version": "2.0.1.0",
|
||||
"sha256": "620da406fabdbc75d11f9827ea7e39d2d3c762471bd2264b0d11f1d6cd9247cd"
|
||||
},
|
||||
{
|
||||
"name": "Opc.Ua.Configuration",
|
||||
"version": "2.0.1.0",
|
||||
"sha256": "6ed6a594c93c3ff35d1a16539a9fe87c1e2640549cc2f48ed6bae333dc8bf0e7"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"source": {
|
||||
"commit": "c25561c64d082ea2a42b03791d604f4fe9b72bd9",
|
||||
"durationSeconds": 7200,
|
||||
"mode": "full",
|
||||
"image": {
|
||||
"Id": "sha256:b04611ee9e1b7d36797012ed921962c57189cb4dbb3dff12afb8e158110f293b",
|
||||
"RepoDigests": [
|
||||
"mcr.microsoft.com/dotnet/sdk@sha256:e362a8dbcd691522456da26a5198b8f3ca1d7641c95624fadc5e3e82678bd08a"
|
||||
],
|
||||
"Architecture": "arm64",
|
||||
"Os": "linux"
|
||||
},
|
||||
"network": "none",
|
||||
"startedUtc": "2026-11-10T17:44:32Z",
|
||||
"scope": "actual unchanged production Shared/OpcUaClientDriver/AdminUI browser in a repeated same-process loop; SDK 2.0.1 only"
|
||||
},
|
||||
"containerExitCode": 0,
|
||||
"containerElapsedSeconds": 7204.404327417025,
|
||||
"summary": {
|
||||
"status": "passed",
|
||||
"diagnosticPositiveControl": "exact SDK2 manager false-before/true-after DisposeAsync",
|
||||
"requestedSeconds": 7200,
|
||||
"elapsedSeconds": 7200.984331,
|
||||
"fullTwoHourLifecycle": true,
|
||||
"cycles": 5420,
|
||||
"sharedPasses": 10840,
|
||||
"driverPasses": 10840,
|
||||
"browserPasses": 10840,
|
||||
"managerDisposalsVerified": 43360,
|
||||
"finalResources": {
|
||||
"cycle": 5420,
|
||||
"elapsedSeconds": 7200.9843313,
|
||||
"utc": "2026-11-10T19:44:46.0998573Z",
|
||||
"WorkingSet64": 86102016,
|
||||
"PrivateMemorySize64": 249212928,
|
||||
"managedBytes": 3988520,
|
||||
"allocatedBytes": 25451263856,
|
||||
"gc": [
|
||||
1631,
|
||||
1088,
|
||||
1088
|
||||
],
|
||||
"fdCount": 148,
|
||||
"ownedRootFdCount": 4,
|
||||
"ownedPkiFdCount": 0,
|
||||
"survivingProductionObjects": 0,
|
||||
"managerDisposalsVerified": 43360,
|
||||
"sharedPasses": 10840,
|
||||
"driverPasses": 10840,
|
||||
"browserPasses": 10840
|
||||
},
|
||||
"scope": "Production Shared connect/disconnect/reconnect; production driver Initialize/Reinitialize/Shutdown/dispose; production browser open/browse/dispose; scalar and Int32[3] read/write/subscribe/unsubscribe; strict mutual trusted DER/Basic256Sha256 SignAndEncrypt/API-key fixture. No network stalls, revocation, surgical edits/rebuild, CP abandonment or Windows/AVEVA/performance G4 claim."
|
||||
},
|
||||
"ownCertificatesUnchanged": true,
|
||||
"applicationOwnCertificates": 3,
|
||||
"runtime": "Microsoft.NETCore.App10.0.3 in pinned official Linux ARM64 SDK10.0.103 image; framework-dependent harness",
|
||||
"samples": 543,
|
||||
"first": {
|
||||
"cycle": 0,
|
||||
"elapsedSeconds": 0,
|
||||
"utc": "2026-11-10T17:44:45.1152841Z",
|
||||
"WorkingSet64": 50864128,
|
||||
"PrivateMemorySize64": 87015424,
|
||||
"managedBytes": 243368,
|
||||
"allocatedBytes": 307672,
|
||||
"gc": [
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
"fdCount": 66,
|
||||
"ownedRootFdCount": 4,
|
||||
"ownedPkiFdCount": 0,
|
||||
"survivingProductionObjects": 0,
|
||||
"managerDisposalsVerified": 0,
|
||||
"sharedPasses": 0,
|
||||
"driverPasses": 0,
|
||||
"browserPasses": 0
|
||||
},
|
||||
"last": {
|
||||
"cycle": 5420,
|
||||
"elapsedSeconds": 7200.9350359,
|
||||
"utc": "2026-11-10T19:44:46.0522796Z",
|
||||
"WorkingSet64": 84873216,
|
||||
"PrivateMemorySize64": 253251584,
|
||||
"managedBytes": 3987480,
|
||||
"allocatedBytes": 25451104712,
|
||||
"gc": [
|
||||
1629,
|
||||
1086,
|
||||
1086
|
||||
],
|
||||
"fdCount": 148,
|
||||
"ownedRootFdCount": 4,
|
||||
"ownedPkiFdCount": 0,
|
||||
"survivingProductionObjects": 0,
|
||||
"managerDisposalsVerified": 43360,
|
||||
"sharedPasses": 10840,
|
||||
"driverPasses": 10840,
|
||||
"browserPasses": 10840
|
||||
},
|
||||
"observedRanges": {
|
||||
"WorkingSet64": {
|
||||
"min": 50864128,
|
||||
"max": 184299520
|
||||
},
|
||||
"PrivateMemorySize64": {
|
||||
"min": 87015424,
|
||||
"max": 272982016
|
||||
},
|
||||
"managedBytes": {
|
||||
"min": 243368,
|
||||
"max": 4793456
|
||||
},
|
||||
"fdCount": {
|
||||
"min": 66,
|
||||
"max": 150
|
||||
},
|
||||
"ownedPkiFdCount": {
|
||||
"min": 0,
|
||||
"max": 0
|
||||
},
|
||||
"survivingProductionObjects": {
|
||||
"min": 0,
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"evidenceSha256": {
|
||||
"result.json": "9419f70bd292fa9298758b93f8a5e5be7b0d7392060f6b8cdc30a29a6eabe3ad",
|
||||
"source.json": "50a2f15f9059b2a9dd7d738edabbaa13a091f887d3487c4b59616cbad997def0",
|
||||
"task/summary.json": "99d435576c1feba4f1d9439e76d4a4d41d56528e80a778947252a1babcf86d5c",
|
||||
"task/resources.jsonl": "25b367bc7568da44f639f5279640820035bb5cb54611b0108dcb4094cb15ca48",
|
||||
"task/sdk-assemblies.json": "8b4d21b6730efda3f670b41bcdaf480994d4fb68cb19e4cf2705ec105e59a643",
|
||||
"artifact-manifest.json": "595712025561558e2aeaf81a369a66670ddcdbea7e6e9c47329adc7d1a984649",
|
||||
"container-dotnet-info.txt": "671a4199c1703bbae7ae9261df77c03e909b1d903dbf4d68747c9790bba5b03b"
|
||||
},
|
||||
"limits": [
|
||||
"Lifecycle completion and cleanup checks passed; RSS/FD samples are observations, not a baseline-relative performance budget.",
|
||||
"This source predates the unrelated HG1.0 consumer change; no server RSS, history CP churn, revocation, stalls, rebuild, Windows/live or full G4 claim.",
|
||||
"Private PKI, keys and credential-bearing environment remain outside versioned evidence."
|
||||
],
|
||||
"artifactManifest": {
|
||||
"scope": "recursive, all files below immutable app publication",
|
||||
"files": 428,
|
||||
"generatedAfterRun": true,
|
||||
"historicalTopLevelManifestSha256": "456a3fb35d5d5d1b3b141cfe654df3a28a66d5196d362cb6015a125491fd5691"
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build and run isolated actual production Shared/driver/browser lifecycle loops."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import secrets
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
import uuid
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
ROOT = HERE.parents[2]
|
||||
|
||||
|
||||
def write(path, value):
|
||||
path.write_text(json.dumps(value, indent=2) + '\n')
|
||||
|
||||
|
||||
def certs(root):
|
||||
return {str(p.relative_to(root)): hashlib.sha256(p.read_bytes()).hexdigest()
|
||||
for p in sorted(root.rglob('*.der')) if '/own/certs/' in str(p)}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--output', required=True, type=Path, help='Fresh nonexistent task evidence root, includes private PKI; never commit entire output')
|
||||
parser.add_argument('--duration', type=int, default=90)
|
||||
parser.add_argument('--full', action='store_true', help='Requires >=7200 seconds of real completed client runtime')
|
||||
parser.add_argument('--image', default='mcr.microsoft.com/dotnet/sdk:10.0.103-noble')
|
||||
args = parser.parse_args()
|
||||
if args.duration <= 0 or (args.full and args.duration < 7200):
|
||||
parser.error('--full requires duration >=7200; diagnostic duration must be positive')
|
||||
output = args.output.resolve()
|
||||
output.mkdir(parents=True, exist_ok=False)
|
||||
os.chmod(output, 0o700)
|
||||
build = output / 'app'
|
||||
task = output / 'task'
|
||||
task.mkdir()
|
||||
image = json.loads(subprocess.check_output(['docker', 'image', 'inspect', args.image, '--format', '{{json .}}'], text=True))
|
||||
if image['Architecture'] != 'arm64' or image['Os'] != 'linux':
|
||||
raise RuntimeError('Expected cached official Linux arm64 SDK image')
|
||||
write(output / 'source.json', {'commit': subprocess.check_output(['git', '-C', str(ROOT), 'rev-parse', 'HEAD'], text=True).strip(),
|
||||
'root': str(ROOT), 'durationSeconds': args.duration, 'mode': 'full' if args.full else 'diagnostic',
|
||||
'image': {k: image.get(k) for k in ('Id', 'RepoDigests', 'Architecture', 'Os')}, 'network': 'none',
|
||||
'startedUtc': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
|
||||
'scope': 'actual unchanged production Shared/OpcUaClientDriver/AdminUI browser in a repeated same-process loop; SDK 2.0.1 only'})
|
||||
with (output / 'build.log').open('w') as log:
|
||||
subprocess.run(['dotnet', 'publish', str(HERE / 'Lifecycle.csproj'), '-c', 'Release', '-m:1', '--nologo', '-o', str(build)], stdout=log, stderr=subprocess.STDOUT, check=True, timeout=300)
|
||||
shutil.copy2(HERE / 'container.sh', task / 'container.sh')
|
||||
name = 'otopcua-client-lifecycle-' + uuid.uuid4().hex[:12]
|
||||
env = dict(os.environ, OPCUA_MATRIX_PASSWORD='otua_lifecycle_' + secrets.token_urlsafe(32),
|
||||
LIFECYCLE_SECONDS=str(args.duration), LIFECYCLE_MODE='full' if args.full else 'diagnostic')
|
||||
command = ['docker', 'run', '--rm', '--name', name, '--network', 'none',
|
||||
'-e', 'OPCUA_MATRIX_PASSWORD', '-e', 'LIFECYCLE_SECONDS', '-e', 'LIFECYCLE_MODE',
|
||||
'-v', str(build) + ':/app:ro', '-v', str(task) + ':/task', args.image, 'bash', '/task/container.sh']
|
||||
write(output / 'command.json', {'argv': command, 'timeoutSeconds': args.duration + 300})
|
||||
started = time.monotonic()
|
||||
try:
|
||||
with (output / 'container.log').open('w') as log:
|
||||
result = subprocess.run(command, stdout=log, stderr=subprocess.STDOUT, env=env, timeout=args.duration + 300)
|
||||
except BaseException:
|
||||
subprocess.run(['docker', 'rm', '-f', name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=20)
|
||||
raise
|
||||
summary = json.loads((task / 'summary.json').read_text()) if (task / 'summary.json').exists() else None
|
||||
write(output / 'result.json', {'exitCode': result.returncode, 'containerElapsedSeconds': time.monotonic() - started,
|
||||
'summary': summary, 'applicationCertificates': certs(task)})
|
||||
if result.returncode or not summary or (args.full and not summary['fullTwoHourLifecycle']):
|
||||
raise RuntimeError('Lifecycle run failed; inspect task/failure.txt, client.log and server.log: ' + str(output))
|
||||
print(json.dumps({'output': str(output), 'summary': summary}, indent=2))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,7 @@
|
||||
__pycache__/
|
||||
bin/
|
||||
obj/
|
||||
private-fixture/
|
||||
*.pfx
|
||||
*.der
|
||||
*.nettrace
|
||||
@@ -0,0 +1,26 @@
|
||||
// Read-only managed metadata extraction; does not load or execute product assemblies.
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Text.Json;
|
||||
var root = Path.GetFullPath(args[0]);
|
||||
var modules = new List<object>();
|
||||
foreach (var directory in args.Skip(1))
|
||||
{
|
||||
foreach (var path in Directory.EnumerateFiles(directory, "*.dll", SearchOption.AllDirectories).Order())
|
||||
{
|
||||
try
|
||||
{
|
||||
using var file = File.OpenRead(path);
|
||||
using var pe = new PEReader(file);
|
||||
if (!pe.HasMetadata) continue;
|
||||
var metadata = pe.GetMetadataReader();
|
||||
if (!metadata.IsAssembly) continue;
|
||||
var assembly = metadata.GetAssemblyDefinition();
|
||||
modules.Add(new { file = Path.GetRelativePath(root, path).Replace(Path.DirectorySeparatorChar, '/'),
|
||||
assembly = metadata.GetString(assembly.Name), version = assembly.Version.ToString(),
|
||||
mvid = metadata.GetGuid(metadata.GetModuleDefinition().Mvid).ToString("D") });
|
||||
}
|
||||
catch (BadImageFormatException) { /* Native DLLs have no CLR module identity. */ }
|
||||
}
|
||||
}
|
||||
Console.WriteLine(JsonSerializer.Serialize(modules));
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text.Json;
|
||||
using Opc.Ua.Security.Certificates;
|
||||
var mode = args[0];
|
||||
var directory = args[1];
|
||||
Directory.CreateDirectory(directory);
|
||||
using var key = RSA.Create(2048);
|
||||
var request = new CertificateRequest("CN=NativeMemoryControl", key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
var original = request.CreateSelfSigned(DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddDays(1));
|
||||
var pfx = original.Export(X509ContentType.Pfx);
|
||||
// Certificate.From takes ownership of original; only wrapped disposes that native certificate.
|
||||
using var wrapped = Certificate.From(original);
|
||||
void Sample(int i, string kind)
|
||||
{
|
||||
var live = GC.GetTotalMemory(true);
|
||||
GC.WaitForPendingFinalizers();
|
||||
using var p = Process.GetCurrentProcess();
|
||||
p.Refresh();
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { kind, i, pid = Environment.ProcessId, mode, live, rss = p.WorkingSet64, methods = JitInfo.GetCompiledMethodCount(), gc = GC.GetGCMemoryInfo().TotalCommittedBytes }));
|
||||
Console.Out.Flush();
|
||||
}
|
||||
Sample(0, "ready");
|
||||
while (!File.Exists(Path.Combine(directory, "go"))) Thread.Sleep(50);
|
||||
for (int i = 1; i <= 1000; i++)
|
||||
{
|
||||
using X509Certificate2 copy = mode switch
|
||||
{
|
||||
"pfx" => System.Security.Cryptography.X509Certificates.X509CertificateLoader.LoadPkcs12(pfx, null, X509KeyStorageFlags.Exportable),
|
||||
"sdk-copy" => wrapped.AsX509Certificate2(),
|
||||
"shared-copy" => (X509Certificate2)typeof(X509Certificate2).GetConstructor(new[] { typeof(X509Certificate) })!.Invoke(new object[] { original }),
|
||||
_ => System.Security.Cryptography.X509Certificates.X509CertificateLoader.LoadCertificate(original.RawData)
|
||||
};
|
||||
if (i % 100 == 0) Sample(i, "sample");
|
||||
}
|
||||
Sample(1000, "done");
|
||||
while (!File.Exists(Path.Combine(directory, "stop"))) Thread.Sleep(50);
|
||||
@@ -0,0 +1,15 @@
|
||||
// Read-only temporary diagnostic attachment. Requires Microsoft.Diagnostics.NETCore.Client from dotnet-trace.
|
||||
using System.Diagnostics.Tracing;
|
||||
using Microsoft.Diagnostics.NETCore.Client;
|
||||
|
||||
var seconds = int.Parse(args[1]);
|
||||
if (seconds < 1 || seconds > 300) throw new ArgumentOutOfRangeException(nameof(seconds), "Use a bounded 1..300 second diagnostic attachment");
|
||||
var client = new DiagnosticsClient(int.Parse(args[0]));
|
||||
var providers = new[] { new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Verbose, 0x18) };
|
||||
// Match the recorded experiment's API default (256 MiB capacity, not measured resident allocation).
|
||||
using var session = client.StartEventPipeSession(providers, requestRundown: false, circularBufferMB: 256);
|
||||
await using var file = File.Create(args[2]);
|
||||
var copy = session.EventStream.CopyToAsync(file);
|
||||
await Task.Delay(TimeSpan.FromSeconds(seconds));
|
||||
session.Stop();
|
||||
await copy;
|
||||
@@ -0,0 +1,208 @@
|
||||
using System.Diagnostics;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Client;
|
||||
using Opc.Ua.Configuration;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
||||
using ZB.MOM.WW.OtOpcUa.OpcUaServer;
|
||||
using ZB.MOM.WW.OtOpcUa.OpcUaServer.Security;
|
||||
var root = Path.GetFullPath(args[0]);
|
||||
Directory.CreateDirectory(root);
|
||||
var seconds = int.Parse(args[1]);
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(seconds + 120));
|
||||
var ct = timeout.Token;
|
||||
var clientPki = Path.Combine(root, "client-pki");
|
||||
var telemetry = DefaultTelemetry.Create(_ => { });
|
||||
var config = new ApplicationConfiguration(telemetry);
|
||||
config.ApplicationName = "NativeLifecycleControl";
|
||||
config.ApplicationUri = "urn:NativeLifecycleControl:" + Guid.NewGuid();
|
||||
config.ApplicationType = ApplicationType.Client;
|
||||
config.SecurityConfiguration = new SecurityConfiguration
|
||||
{
|
||||
ApplicationCertificate = new CertificateIdentifier { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(clientPki, "own"), SubjectName = "CN=NativeLifecycleControl" },
|
||||
TrustedIssuerCertificates = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(clientPki, "issuer") },
|
||||
TrustedPeerCertificates = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(clientPki, "trusted") },
|
||||
RejectedCertificateStore = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(clientPki, "rejected") },
|
||||
AutoAcceptUntrustedCertificates = true
|
||||
};
|
||||
config.ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 15000 };
|
||||
var app = new ApplicationInstance(telemetry) { ApplicationConfiguration = config };
|
||||
await config.ValidateAsync(ApplicationType.Client, ct);
|
||||
await app.CheckApplicationInstanceCertificatesAsync(true, null, ct);
|
||||
int port;
|
||||
using (var listener = new TcpListener(IPAddress.Loopback, 0))
|
||||
{
|
||||
listener.Start();
|
||||
port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||
}
|
||||
var options = new OpcUaApplicationHostOptions { ApplicationName = "NativeLifecycleServer", ApplicationUri = "urn:NativeLifecycleServer:" + Guid.NewGuid(), OpcUaPort = port, PublicHostname = "127.0.0.1", PkiStoreRoot = Path.Combine(root, "server-pki"), AllowAnonymous = false, AutoAcceptUntrustedClientCertificates = true, EnabledSecurityProfiles = [OpcUaSecurityProfile.Basic256Sha256SignAndEncrypt] };
|
||||
OpcUaApplicationHost host = null!;
|
||||
OtOpcUaSdkServer server = null!;
|
||||
async Task Boot()
|
||||
{
|
||||
server = new OtOpcUaSdkServer();
|
||||
host = new OpcUaApplicationHost(options, NullLogger<OpcUaApplicationHost>.Instance, new Auth());
|
||||
await host.StartAsync(server, ct);
|
||||
var n = server.NodeManager!;
|
||||
n.NodeWriteGateway = new Gateway();
|
||||
n.EnsureFolder("Control", null, "Control", AddressSpaceRealm.Raw);
|
||||
n.EnsureFolder("Equipment", null, "Equipment", AddressSpaceRealm.Uns);
|
||||
n.EnsureVariable("Control/Scalar", "Control", "Scalar", "Int32", true, AddressSpaceRealm.Raw);
|
||||
n.EnsureVariable("Control/Array", "Control", "Array", "Int16", true, AddressSpaceRealm.Raw, isArray: true, arrayLength: 4);
|
||||
n.EnsureVariable("Equipment/Scalar", "Equipment", "Scalar", "Int32", true, AddressSpaceRealm.Uns);
|
||||
for (var i = 0; i < 16; i++) n.EnsureVariable("Control/Browse" + i, "Control", "Browse" + i, "Int32", false, AddressSpaceRealm.Raw);
|
||||
n.MaterialiseAlarmCondition("Control/Alarm", "Control", "Alarm", "OffNormalAlarm", 500, AddressSpaceRealm.Raw, isNative: true);
|
||||
n.WriteValue("Control/Scalar", 0, 0, DateTime.UtcNow, AddressSpaceRealm.Raw);
|
||||
n.WriteValue("Control/Array", new short[] { 0, 1, 2, 3 }, 0, DateTime.UtcNow, AddressSpaceRealm.Raw);
|
||||
n.WriteValue("Equipment/Scalar", 0, 0, DateTime.UtcNow, AddressSpaceRealm.Uns);
|
||||
}
|
||||
async Task<ISession> Connect()
|
||||
{
|
||||
var t = DefaultTelemetry.Create(_ => { });
|
||||
var endpoint = await CoreClientUtils.SelectEndpointAsync(config, $"opc.tcp://127.0.0.1:{port}/OtOpcUa", true, t, ct);
|
||||
var factory = new DefaultSessionFactory(t);
|
||||
var identity = new UserIdentity("control", Encoding.UTF8.GetBytes(FixtureCredential.Value));
|
||||
return await factory.CreateAsync(config, new ConfiguredEndpoint(null, endpoint ?? throw new Exception("Missing endpoint"), EndpointConfiguration.Create(config)), false, false, "NativeLifecycleControl", 15000, identity, default, ct);
|
||||
}
|
||||
static DataValue Value(object v)
|
||||
{
|
||||
#if SDK2
|
||||
if (!VariantHelper.TryCastFrom(v, out var variant)) throw new Exception("Unsupported control value");
|
||||
return new DataValue(variant);
|
||||
#else
|
||||
return new DataValue(new Variant(v));
|
||||
#endif
|
||||
}
|
||||
static object? Clr(DataValue v)
|
||||
{
|
||||
#if SDK2
|
||||
return v.WrappedValue.AsBoxedObject(Variant.BoxingBehavior.Legacy);
|
||||
#else
|
||||
return v.Value;
|
||||
#endif
|
||||
}
|
||||
object? CertificateStats()
|
||||
{
|
||||
#if SDK2
|
||||
return new { created = Opc.Ua.Security.Certificates.Certificate.InstancesCreated, disposed = Opc.Ua.Security.Certificates.Certificate.InstancesDisposed, outstanding = Opc.Ua.Security.Certificates.Certificate.InstancesLeaked };
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
void Sample(int cycle, string kind)
|
||||
{
|
||||
var live = GC.GetTotalMemory(true);
|
||||
using var p = Process.GetCurrentProcess();
|
||||
p.Refresh();
|
||||
var m = GC.GetGCMemoryInfo();
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { kind, cycle, pid = Environment.ProcessId, live, rss = p.WorkingSet64, gcCommitted = m.TotalCommittedBytes, gcHeap = m.HeapSizeBytes, fragmented = m.FragmentedBytes, jit = JitInfo.GetCompiledMethodCount(), jitIl = JitInfo.GetCompiledILBytes(), sdk = typeof(NodeId).Assembly.GetName().Version?.ToString(), scope = "matched encrypted public lifecycle subset; no history/revocation/proxy", assemblies = AppDomain.CurrentDomain.GetAssemblies().Length, dynamicAssemblies = AppDomain.CurrentDomain.GetAssemblies().Count(a => a.IsDynamic), certificate = CertificateStats(), timers = Timer.ActiveCount, threads = p.Threads.Count, sessions = server.CurrentInstance.SessionManager.GetSessions().Count, subscriptions = server.CurrentInstance.SubscriptionManager.GetSubscriptions().Count }));
|
||||
Console.Out.Flush();
|
||||
}
|
||||
int cycles = 0, restarts = 0;
|
||||
await Boot();
|
||||
var watch = Stopwatch.StartNew();
|
||||
Sample(0, "start");
|
||||
try
|
||||
{
|
||||
while (watch.Elapsed.TotalSeconds < seconds)
|
||||
{
|
||||
using (var owner = await Connect()) using (var other = await Connect())
|
||||
{
|
||||
var ns = (ushort)owner.NamespaceUris.GetIndex("https://zb.com/otopcua/raw");
|
||||
var scalar = new NodeId("Control/Scalar", ns);
|
||||
var array = new NodeId("Control/Array", ns);
|
||||
using var subscription = new Subscription(owner.DefaultSubscription) { PublishingInterval = 100 };
|
||||
owner.AddSubscription(subscription);
|
||||
await subscription.CreateAsync(ct);
|
||||
subscription.AddItem(new MonitoredItem(subscription.DefaultItem) { StartNodeId = scalar, AttributeId = Attributes.Value, SamplingInterval = 50 });
|
||||
subscription.AddItem(new MonitoredItem(subscription.DefaultItem) { StartNodeId = array, AttributeId = Attributes.Value, SamplingInterval = 50 });
|
||||
await subscription.ApplyChangesAsync(ct);
|
||||
var response = await owner.WriteAsync(null, [new WriteValue { NodeId = scalar, AttributeId = Attributes.Value, Value = Value(++cycles) }, new WriteValue { NodeId = array, AttributeId = Attributes.Value, Value = Value(new short[] { 1, 2, 3, 4 }) }], ct);
|
||||
if (response.Results.ToList().Any(s => StatusCode.IsBad(s))) throw new Exception("Control write denied");
|
||||
if (!Equals(Clr(await owner.ReadValueAsync(scalar, ct)), cycles)) throw new Exception("Scalar changed");
|
||||
if (!((short[])Clr(await owner.ReadValueAsync(array, ct))!).SequenceEqual(new short[] { 1, 2, 3, 4 })) throw new Exception("Array changed");
|
||||
var browse = await owner.BrowseAsync(null, null, 1, [new BrowseDescription { NodeId = new NodeId("Control", ns), BrowseDirection = BrowseDirection.Forward, ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences, IncludeSubtypes = true, ResultMask = (uint)BrowseResultMask.All }], ct);
|
||||
await Task.Delay(1000, ct);
|
||||
await subscription.DeleteAsync(true, ct);
|
||||
await owner.CloseAsync(ct);
|
||||
await other.CloseAsync(ct);
|
||||
}
|
||||
if (server.CurrentInstance.SessionManager.GetSessions().Count != 0 || server.CurrentInstance.SubscriptionManager.GetSubscriptions().Count != 0) throw new Exception("Control retained sessions/subscriptions");
|
||||
Sample(cycles, "sample");
|
||||
if (cycles % 4 == 0)
|
||||
{
|
||||
using var parked = await Connect();
|
||||
using var sub = new Subscription(parked.DefaultSubscription) { PublishingInterval = 100 };
|
||||
parked.AddSubscription(sub);
|
||||
await sub.CreateAsync(ct);
|
||||
sub.AddItem(new MonitoredItem(sub.DefaultItem) { StartNodeId = new NodeId("Control/Scalar", (ushort)parked.NamespaceUris.GetIndex("https://zb.com/otopcua/raw")), AttributeId = Attributes.Value, SamplingInterval = 50 });
|
||||
await sub.ApplyChangesAsync(ct);
|
||||
await host.DisposeAsync();
|
||||
await Boot();
|
||||
restarts++;
|
||||
}
|
||||
}
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { kind = "complete", cycles, restarts, seconds = watch.Elapsed.TotalSeconds, fullGate = false }));
|
||||
}
|
||||
finally
|
||||
{
|
||||
await host.DisposeAsync();
|
||||
#if SDK2
|
||||
await app.DisposeAsync();
|
||||
#endif
|
||||
}
|
||||
#if SDK2
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
var certificateType = typeof(Opc.Ua.Security.Certificates.Certificate);
|
||||
var report = new Dictionary<string, object>();
|
||||
var trackingEnabled = Environment.GetEnvironmentVariable("OPCUA_CERTIFICATE_LEAK_TRACKING") == "1";
|
||||
if (trackingEnabled)
|
||||
{
|
||||
foreach (var name in new[] { "EnumerateLiveCertificates", "EnumerateUnreachableUndisposedCertificates" })
|
||||
{
|
||||
var method = certificateType.GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("SDK2 diagnostic method missing: " + name);
|
||||
var values = method.Invoke(null, null) as IEnumerable
|
||||
?? throw new InvalidOperationException("SDK2 diagnostic method return type changed: " + name);
|
||||
var stacks = new List<string>();
|
||||
foreach (var value in values)
|
||||
{
|
||||
var field = value!.GetType().GetField(name.Contains("Unreachable") ? "Item2" : "Item4")
|
||||
?? throw new InvalidOperationException("SDK2 diagnostic tuple field missing: " + name);
|
||||
if (field.FieldType != typeof(string))
|
||||
throw new InvalidOperationException("SDK2 diagnostic stack field type changed");
|
||||
// Deliberately omit tuple Item1 (certificate identity).
|
||||
stacks.Add((string)field.GetValue(value)!);
|
||||
}
|
||||
report[name] = new
|
||||
{
|
||||
count = stacks.Count,
|
||||
allocations = stacks.GroupBy(x => x).OrderByDescending(g => g.Count()).Take(15)
|
||||
.Select(g => new { count = g.Count(), stack = g.Key }).ToArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { kind = "certificate-final-diagnostics", trackingEnabled, stats = CertificateStats(), report }));
|
||||
#endif
|
||||
class Auth : IOpcUaUserAuthenticator
|
||||
{
|
||||
public Task<OpcUaUserAuthResult> AuthenticateUserNameAsync(string user, string password, CancellationToken ct) => Task.FromResult(user == "control" && password == FixtureCredential.Value ? OpcUaUserAuthResult.Allow("control", [OpcUaDataPlaneRoles.WriteOperate]) : OpcUaUserAuthResult.Deny("Control credential refused"));
|
||||
}
|
||||
class Gateway : IOpcUaNodeWriteGateway
|
||||
{
|
||||
public Task<NodeWriteOutcome> WriteAsync(string n, object? v, AddressSpaceRealm r, CancellationToken ct) => Task.FromResult(new NodeWriteOutcome(true, null));
|
||||
}
|
||||
|
||||
static class FixtureCredential
|
||||
{
|
||||
internal static string Value { get; } = Environment.GetEnvironmentVariable("OPCUA_MEMORY_PASSWORD") ?? throw new InvalidOperationException("Set OPCUA_MEMORY_PASSWORD for the task-owned fixture.");
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
# SDK 2 lifecycle memory investigation
|
||||
|
||||
The required two-hour server lifecycle gate failed on macOS and Linux. The RSS
|
||||
limit remains **cycle-3 baseline + 128 MiB**; retained managed memory remains
|
||||
+64 MiB and descriptors +32. No threshold, production code, certificate provider,
|
||||
or session factory was changed by this investigation. These controls do not
|
||||
replace the full gate. Issue #799 remains open.
|
||||
|
||||
## Preserved full-workload failures
|
||||
|
||||
| Run | Cycles / restarts | Observation time | RSS / limit (bytes) | Outcome |
|
||||
|---|---:|---:|---:|---|
|
||||
| macOS `fa823378` | 250 / 62 | 1045.53 s | 365,215,744 / 363,331,584 | Failed |
|
||||
| macOS `e135b7c9` profile reproduction | 245 / 61 | 1024.14 s | 368,656,384 / 363,053,056 | Failed |
|
||||
| Linux full harness, recorded build `38098ce142ab8c382825aa754e0758071dd3a46b` | 248 / 61 | 903.508 s | 353,931,264 / 351,850,496 | Failed |
|
||||
| macOS `6b2d4b81` bounded profile | 432 / 108 | 1807.479 s | First rejection: 364,199,936 / 363,266,048 | Failed; first assertion rethrown |
|
||||
|
||||
The last macOS row requested 1,800 seconds. It first rejected RSS at cycle 253 /
|
||||
1055.482 seconds, continued recording explicitly rejected observations, then
|
||||
threw the original assertion before any completion record. Its final observation
|
||||
was RSS 380,059,648, live managed 41,364,096, GC committed 37,060,608, JIT
|
||||
compilations 81,535, descriptors 200, and zero sessions, subscriptions, monitored
|
||||
items, browse/history continuation points, and PKI descriptors. SDK cached stores
|
||||
remained two during operation. The test runner returned exit 1; the nonzero-execution guard passed (exit 0),
|
||||
with one executed test and zero skipped tests. There is no passing two-hour result here.
|
||||
|
||||
The first macOS run's reported PKI descriptor zero is invalid: `/var` and
|
||||
`/private/var` aliases were not canonicalized. Corrected positive-handle and timeout
|
||||
controls passed before subsequent profiles. Global descriptor counts and the RSS
|
||||
failure itself were independent of that defect. A Shouldly source-expression
|
||||
message incorrectly names `Descriptors` in the RSS failure; the actual numeric
|
||||
comparison is RSS, as retained before the assertion.
|
||||
|
||||
## What the controls demonstrate
|
||||
|
||||
`LifecycleControl.cs` uses unchanged published product DLLs and public APIs for
|
||||
both SDK generations. It opens two real encrypted **opc.tcp** sessions with
|
||||
Basic256Sha256 / SignAndEncrypt, resolves namespace URIs, reads/writes Int32 and
|
||||
Int16[] values, creates/deletes two data monitored items, abandons a browse
|
||||
continuation, closes sessions, and restarts a server with an active subscription
|
||||
every four cycles against the same task-owned PKI. Public session/subscription
|
||||
counts must return to zero. It has no historian, revocation, surgical-rebuild,
|
||||
paused-network, or private continuation-store coverage. It is a diagnostic subset,
|
||||
not TLS and not the full soak workload.
|
||||
|
||||
The original DLLs are the immutable 1.5.378.182 baseline publication associated
|
||||
with `83ed880e`; its assembly version alone is 1.5.378.0 and does not identify the
|
||||
patch. The candidate uses SDK 2.0.1 and the server implementation from `fa823378`.
|
||||
Neither baseline nor candidate product code was rebuilt for these controls.
|
||||
The controls were compiled once for each reference set, then those exact control
|
||||
artifacts were used on both operating systems. Selected DLL and input-artifact
|
||||
hashes are in `evidence.json`. Neither reference set's principal SDK/server DLLs
|
||||
has a managed native/ReadyToRun header.
|
||||
|
||||
| Ten-minute matched subset | Cycles / restarts | Final RSS | Change from cycle 3 | Final managed / change | Final JIT compilations |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| macOS original | 212 / 53 | 330,301,440 | +121,683,968 | 271,168,424 / +169,990,456 | 28,066 |
|
||||
| macOS SDK2 | 200 / 50 | 302,170,112 | +95,862,784 | 44,569,584 / +295,120 | 62,608 |
|
||||
| Linux original | 249 / 62 | 384,950,272 | +176,836,608 | 147,139,568 / +99,356,752 | 28,321 |
|
||||
| Linux SDK2 | 259 / 64 | 390,844,416 | +194,183,168 | 19,561,120 / +736 | 71,529 |
|
||||
|
||||
Both ten-minute controls exited zero because their lifecycle checks passed;
|
||||
`fullGate=false` is explicit. They do **not** enforce the full harness memory
|
||||
assertions. In particular, both Linux subset RSS deltas exceed 128 MiB and the
|
||||
original retains managed memory/timers. These observations do not waive either
|
||||
failed full gate. They are not proof of an SDK1 defect or an SDK2 improvement.
|
||||
Both control branches explicitly delete subscriptions, close normal sessions,
|
||||
and dispose sessions/subscriptions. Parked resources intentionally encounter
|
||||
server shutdown and are then disposed. Each version calls its unchanged product
|
||||
host's DisposeAsync. The baseline host only calls ApplicationInstance.StopAsync;
|
||||
that original ApplicationInstance exposes no disposal interface. The SDK2 branch
|
||||
also uses its new ApplicationInstance.DisposeAsync ownership API. Per-connection
|
||||
DefaultTelemetry factories are left to the same ownership pattern on both
|
||||
branches, separately probed above. Thus measured original growth belongs to this
|
||||
published-product/fixture lifecycle combination; its allocation owner has not
|
||||
been isolated. These are one matched run per platform, concurrent processes,
|
||||
with different runtime versions; they are not performance distributions.
|
||||
|
||||
The thirty-minute Linux comparison uses the same immutable artifacts and image,
|
||||
fresh independent fixtures, and SDK2 certificate allocation tracking enabled
|
||||
before the first certificate (the same environment is passed to both versions).
|
||||
Both finished with exit zero and `fullGate=false`:
|
||||
|
||||
| Thirty-minute Linux subset | Cycles / restarts | Final RSS | Final live managed | GC committed | JIT compilations | Active timers |
|
||||
|---|---:|---:|---:|---:|---:|---:|
|
||||
| Original | 736 / 184 | 540,057,600 | 342,626,200 | 408,399,872 | 29,177 | 926 |
|
||||
| SDK2 | 768 / 192 | 334,180,352 | 19,248,984 | 42,983,424 | 72,087 | 19 |
|
||||
|
||||
Observed runtime was 1802.542 seconds original / 1804.523 seconds SDK2. The SDK2
|
||||
RSS fell from 390,782,976 at the 632.7-second checkpoint to 334,180,352 at the last
|
||||
observation; live managed stayed near 20 MB. After disposal and final collection,
|
||||
SDK2 wrapper counters were 19,272 created = 19,272 disposed, with zero live or
|
||||
unreachable-undisposed wrappers under enabled tracking. These finite results
|
||||
support post-transition saturation of this subset, not a two-hour full-workload
|
||||
pass or a guarantee of indefinite stability.
|
||||
|
||||
### Native and runtime observations
|
||||
|
||||
- During the macOS bounded profile, `Security::AppleDatabase` counts increased
|
||||
from 278 at cycle 52 / 211 seconds to 1,953 at cycle 370 / 1546 seconds. These
|
||||
native objects continued accumulating while managed memory stayed near 40 MiB.
|
||||
- The single-owner certificate probe makes a self-signed RSA fixture in memory,
|
||||
then disposes each of 1,000 copies/imports. On macOS, public-only and
|
||||
shared-handle copies leave AppleDatabase at 2; PFX imports and SDK
|
||||
`Certificate.AsX509Certificate2` private-key copies grow it from 2 to 1,002.
|
||||
A separate PFX process retained that count through a 180-second idle hold.
|
||||
`Certificate.From(original)` owns the original; the checked-in control has no
|
||||
second owner. All four corrected single-owner modes were rerun successfully.
|
||||
- The same isolated Linux probe ended at approximately 49 MB RSS for public or
|
||||
shared copies, 61 MB for PFX imports, and 69 MB for the SDK copy. Those are
|
||||
endpoint RSS observations including different JIT/GC costs, not evidence of a
|
||||
Linux native-object leak or a complete attribution.
|
||||
- SDK2 public certificate counters after the Linux ten-minute lifecycle run were
|
||||
6,495 created = 6,495 disposed. A separate stack-tracked macOS lifecycle run
|
||||
ended 591 = 591, zero live and zero unreachable-undisposed wrappers. Allocation
|
||||
tracking is opt-in; empty internal reports without that opt-in are not proof.
|
||||
- The Linux ten-minute native snapshots show SDK2 JIT `doublemapper` resident
|
||||
mappings increasing from about 65.4 to 106.7 MiB, anonymous writable mappings
|
||||
from 84.6 to 121.3 MiB, and `[heap]` around 19.2 MiB. Original JIT mappings were
|
||||
about 33.3 to 37.7 MiB. Mapping totals are descriptive categories, not exclusive
|
||||
object allocations: anonymous memory also contains GC regions, stacks, and
|
||||
native allocator arenas. RSS includes file/shared pages and can fall with
|
||||
reclamation. JIT counters are cumulative compilations, not unique live methods.
|
||||
- 10,000 disposed versus undisposed `DefaultTelemetry` factories ended at similar
|
||||
RSS (~63.4 MB) and live managed memory (~0.43 MB), with no timer growth. This
|
||||
does not justify changing host telemetry ownership to fix this failure.
|
||||
|
||||
Exact SDK tag source shows private-key PFX copying in nonvirtual
|
||||
`Certificate.AsX509Certificate2`. A 60-second macOS sampled-thread-time trace
|
||||
instead found the dominant sampled import path through
|
||||
`CertificateIdentifierResolver.LoadPrivateKeyCoreAsync` →
|
||||
`DirectoryCertificateStore.LoadPrivateKeyAsync` → `X509CertificateLoader.LoadPkcs12`.
|
||||
Inclusive sampled thread-time was about 4.79 seconds for store load and 2.99 seconds
|
||||
for PKCS12, versus 8.5 milliseconds for AsX509. These are neither exclusive CPU
|
||||
costs nor call counts. ApplicationInstance already normalizes its validation
|
||||
input to public DER. The validation copy delegate is internal, the manager sealed,
|
||||
and no safe narrow application copy-policy seam was identified. Changing session
|
||||
factory, certificate exportability, or key storage would alter behavior and was
|
||||
not used as a workaround.
|
||||
|
||||
A major JIT/RSS step appeared near 60–64 restarts in several workloads. This is a
|
||||
hypothesis about runtime/code-cache warmup, not a finding that all RSS growth is
|
||||
benign. A separate read-only EventPipe attachment observed the thirty-minute
|
||||
Linux candidate around that transition using `Microsoft-Windows-DotNETRuntime`,
|
||||
Verbose, keywords `0x18` (Loader + JIT), no rundown, 180 seconds. The transition was UTC 18:51:02.049–18:54:02.062. Factory JIT events
|
||||
occurred UTC 18:52:48.249–18:53:21.434: 6,690 distinct method tokens from one
|
||||
module, each once. The module is Opc.Ua.Core.Types.dll; metadata declares 8,442
|
||||
static methods on OpcUaExtensions. The observed set is a subset, so this does
|
||||
not prove every possible factory or the complete canonical workload is warmed.
|
||||
Method-load events totalled 51,327,536 native code bytes, including 51,161,724
|
||||
OptimizedTier1 bytes and 49,141,484 bytes in generated factories. Only 50
|
||||
reflection InvokeStub JIT starts occurred; the transition trace reports zero lost events. MethodSize sums count loaded code
|
||||
versions, not resident/PSS allocation bytes. An additional 180-second attachment
|
||||
after the transition checks continuing factory compilation. The second attachment (UTC 19:01:44.116–19:04:44.127) recorded 85 JIT starts,
|
||||
zero lost events, zero Opc.Ua starts, zero generated-factory starts, and zero generated-factory
|
||||
loaded bytes. Only 18 ordinary Tier1 method loads (5,720 bytes) and 20
|
||||
instrumented Tier1 loads (5,800 bytes) occurred. This supports a finite generated
|
||||
factory burst in the observed subset; it does not prove complete canonical
|
||||
workload saturation. The capture API's default circular-buffer capacity was 256 MiB; that is a
|
||||
capacity setting, not measured resident overhead. Attachments can allocate event
|
||||
buffers and perturb execution. Both prior full failures and the ten-minute
|
||||
control were uninstrumented by these JIT attachments, and the thirty-minute final
|
||||
interval is unattached. No captured native-code byte total is claimed to equal an
|
||||
RSS delta. Raw traces/heaps are not committed.
|
||||
|
||||
## Reproduce the diagnostic subset
|
||||
|
||||
Requires Python 3 and .NET 10. `run.py` references existing DLL directories,
|
||||
compiles only the control, records SHA-256 provenance, and creates a fresh local
|
||||
artifact directory. First reference directory wins duplicate filenames. Use the
|
||||
actual baseline publications, rather than restoring a different package version.
|
||||
No production project or package file is changed.
|
||||
|
||||
```bash
|
||||
python3 scripts/validation/opcua-lifecycle-memory/run.py lifecycle \
|
||||
--references "$baselineHostDlls" --references "$baselineClientDlls" \
|
||||
--output /tmp/opcua-memory-original --seconds 600
|
||||
python3 scripts/validation/opcua-lifecycle-memory/run.py lifecycle --sdk2 \
|
||||
--references "$candidateDlls" --output /tmp/opcua-memory-sdk2 --seconds 600 \
|
||||
--certificate-tracking
|
||||
```
|
||||
|
||||
Fixture credentials are generated in the runner environment and not logged.
|
||||
PKI material stays under each local `private-fixture` directory. Do not publish
|
||||
those directories or unredacted process/native logs. These controls grant a
|
||||
fixture user WriteOperate and auto-accept task-owned certificates; they are
|
||||
local test fixtures, not deployment configurations.
|
||||
|
||||
For a matched Linux pair, first build both with `--build-only` into fresh
|
||||
`/tmp/opcua-memory-original` and `/tmp/opcua-memory-sdk2` output directories, then:
|
||||
|
||||
```bash
|
||||
python3 scripts/validation/opcua-lifecycle-memory/linux_pair.py \
|
||||
--baseline /tmp/opcua-memory-original/bin/Release/net10.0 \
|
||||
--candidate /tmp/opcua-memory-sdk2/bin/Release/net10.0 \
|
||||
--output /tmp/opcua-memory-linux-pair --seconds 1800 \
|
||||
--image sha256:b04611ee9e1b7d36797012ed921962c57189cb4dbb3dff12afb8e158110f293b
|
||||
```
|
||||
|
||||
The runner uses separate task-owned `--network none` containers, fresh PKI,
|
||||
read-only executable mounts, bounded shutdown, and periodic read-only `/proc`
|
||||
snapshots. It records child and container exit statuses independently. The image
|
||||
is Ubuntu 24.04 arm64, .NET SDK 10.0.103 / runtime 10.0.3. The Mac runs used
|
||||
macOS 26.5.2 arm64, SDK 10.0.403 / runtime 10.0.14. Runtime and OS differences are
|
||||
explicit; do not attribute all cross-platform differences to SDK code.
|
||||
|
||||
On macOS, run each certificate mode with a fresh output directory:
|
||||
|
||||
```bash
|
||||
python3 scripts/validation/opcua-lifecycle-memory/run.py certificate --sdk2 \
|
||||
--references "$candidateDlls" --output /tmp/opcua-memory-pfx --mode pfx
|
||||
```
|
||||
|
||||
Other modes are `sdk-copy`, `public`, and `shared-copy`. The last intentionally
|
||||
uses the old copy constructor by reflection as a diagnostic control. It is not
|
||||
proposed production code. The control exports no fixture key/certificate data.
|
||||
`TelemetryControl.cs` is available through `run.py telemetry --sdk2` with modes
|
||||
`dispose` or `undisposed`. `summarize.py` emits Linux mapping categories without
|
||||
file paths from local `*-smaps.txt` files. All controls are opt-in and outside the
|
||||
ordinary solution/test graph.
|
||||
|
||||
Optional trace reproduction uses the installed dotnet-trace 9.0.661903 managed
|
||||
libraries, without adding a product dependency:
|
||||
|
||||
```bash
|
||||
python3 scripts/validation/opcua-lifecycle-memory/build_trace_tools.py \
|
||||
--libraries "$diagnosticToolLibraries" --output /tmp/opcua-memory-trace-tools
|
||||
# Run capture in the same process namespace / user as the task-owned target.
|
||||
dotnet /tmp/opcua-memory-trace-tools/capture/bin/Release/net10.0/capture.dll \
|
||||
"$targetPid" 180 /tmp/opcua-memory-local.nettrace
|
||||
dotnet /tmp/opcua-memory-trace-tools/summary/bin/Release/net10.0/summary.dll \
|
||||
/tmp/opcua-memory-local.nettrace > /tmp/opcua-memory-method-summary.json
|
||||
```
|
||||
|
||||
Capture is read-only and opt-in; raw traces remain local. The summary exports
|
||||
static method names and aggregate counts rather than certificate identities,
|
||||
arguments, or process mappings. The original transition parser also retained
|
||||
method tokens/modules locally to cross-check name-key uniqueness.
|
||||
|
||||
## Prospective saturation experiment (not executed)
|
||||
|
||||
The observed finite factory burst supports a separately labeled readiness
|
||||
experiment; it does not authorize moving the acceptance baseline. A reviewed
|
||||
candidate proposal is exactly 384 canonical lifecycle cycles / 96 every-four-cycle
|
||||
restarts, then one fixed settling cycle, all within 1,800 seconds. No early finish,
|
||||
extension, or RSS-selected endpoint is permitted. Both fixed readiness windows
|
||||
321–352 and 353–384 must contain zero generated-factory JIT/method-load events, zero SDK assembly
|
||||
loads, and zero lost events. Other JIT/native-code totals remain review evidence,
|
||||
not a claim that every runtime path is warmed. Trace capture is bounded and identical for both versions, then
|
||||
detached/drained before the settling cycle. Ownership, FD, PKI, store, and logical
|
||||
resource checks remain active throughout; original cold budget rejections remain
|
||||
recorded. Any later full gate requires at least 7,200 additional seconds after a
|
||||
prospectively approved baseline. No such warmed full gate has been run here.
|
||||
|
||||
The count is beyond the observed 60–64-restart transition and projects to about
|
||||
1,606 seconds from the Mac canonical run, but completion within the fixed cutoff
|
||||
is not guaranteed under load. Failure to complete or satisfy readiness is a failed
|
||||
experiment, not permission to extend. The full canonical test currently depends
|
||||
on SDK2 value APIs and read-only private SDK2 continuation/store instrumentation.
|
||||
An original-SDK adapter proving identical history, revocation, surgical-edit,
|
||||
and stalled-network operation counts is a prerequisite for a matched canonical
|
||||
experiment; the public subset does not provide that proof. Continued Mac native
|
||||
inventory growth remains an independent unresolved gate, even if JIT saturates.
|
||||
|
||||
## Evidence and limits
|
||||
|
||||
`evidence.json` contains sanitized numeric endpoints, native inventory counts,
|
||||
and hashes of retained diagnostic inputs. Full artifacts remain in the originating
|
||||
worktrees or `/tmp/opcua2-rss-investigation`; no PKI, certificate identifiers,
|
||||
credentials, raw traces, unredacted heap/process dumps, or complete test logs are
|
||||
checked in. Existing failures were preserved untouched.
|
||||
|
||||
Supported progress is a reproduced RSS failure, bounded logical/managed
|
||||
inventories in SDK2, demonstrated macOS PFX native retention, and measured
|
||||
cross-platform JIT/anonymous-memory growth. A complete allocation attribution and
|
||||
safe production correction remain unresolved. The server two-hour gate remains
|
||||
failed; a separate long-running client-only process is a different scope.
|
||||
|
||||
Package verification: baseline and candidate control builds, both optional trace
|
||||
tools, and certificate/telemetry controls built with zero warnings/errors. Local
|
||||
control smoke runs and the fresh Linux pair smoke exited zero. Python compilation
|
||||
and whitespace checks passed. One reused-output smoke failed because a manual
|
||||
incremental build used a different `/tmp` path alias and its runtimeconfig was
|
||||
missing; fresh canonical outputs passed, and the Linux runner now rejects an
|
||||
incomplete output directory before launch. These smoke checks validate tooling
|
||||
only and do not substitute for either long run.
|
||||
@@ -0,0 +1,96 @@
|
||||
# Issue #799 resolution gates
|
||||
|
||||
Status: prospective procedure under review, **not approved for launch**. The
|
||||
retained cold failures remain failures. This document does not change the plan's
|
||||
resource budgets, establish a replacement baseline, or authorize release.
|
||||
|
||||
## Separate the evidence
|
||||
|
||||
| Question | Evidence required | Current boundary |
|
||||
|---|---|---|
|
||||
| Finite generated-code startup | Fixed canonical operation sequence and bounded, lossless trace windows | Candidate saturation met the selected factory criterion. Summed native MethodSize does not explain all RSS. |
|
||||
| Cross-version lifecycle ownership and memory | Equivalent operations, token invalidation, dropped harness roots, resource-owner checks and matched executable hosts | Original closed objects retain continuation lists while held by the harness; usable steady-state comparison is not established. |
|
||||
| Continuing macOS native retention | Private-key import attribution, bounded native inventory, supported ownership correction and verification | Balanced managed disposal alone has not stopped Apple Security object accumulation. Linux results cannot close this component. |
|
||||
|
||||
The coordinator owns integration and long executions. Two independent Sol agent
|
||||
lanes own `baseline-canonical/` and `native-retention/` respectively; use high
|
||||
effort for control implementation and ownership analysis. Astra at xhigh reviews
|
||||
each control design, its result, any production correction, and the complete
|
||||
acceptance procedure before execution. No worker changes the other lane's files,
|
||||
production factory architecture, immutable baseline, or acceptance denominator.
|
||||
|
||||
## Short controls before a new long run
|
||||
|
||||
The original-SDK lane must demonstrate removal from SessionManager, rejection of
|
||||
the old authentication token, and rejection of old browse/history continuation
|
||||
tokens from a fresh session. A helper boundary must drop all strong Session,
|
||||
continuation and harness references before bounded GC/weak-reference observations.
|
||||
Observe closed-session/point weak references while the same host remains live,
|
||||
then separately observe application/validator/store roots after normal host stop;
|
||||
teardown must not hide retention of closed sessions. Inventory separately
|
||||
identifies which resource owner releases each object.
|
||||
Retain the earlier held-object failure. Do not explicitly dispose abandoned
|
||||
objects or clear caches merely to make the baseline pass. Unsupported original
|
||||
ownership probes remain unsupported, rather than being reported as zero.
|
||||
|
||||
The native lane must attribute configured-store private-key loads separately
|
||||
from public certificate and shared-handle operations using fixed counts and
|
||||
identical certificates. Include a separate bounded store-update semantic control
|
||||
so registry reuse cannot silently bypass configured-store rotation. Review exact
|
||||
SDK 2.0.1 public ownership seams before any
|
||||
application correction. A supported correction needs a failing-before/passing-
|
||||
after ownership control and preserved trust, private-key availability, reconnect
|
||||
and shutdown behavior. Custom crypto, copied transport negotiation, SDK forks,
|
||||
factory substitution and certificate-policy changes require a separate design;
|
||||
they are not fixes authorized by this procedure. Keep private PKI out of Git.
|
||||
|
||||
## Prerequisites for reviewing warmed acceptance
|
||||
|
||||
1. Establish operation/status/order parity and semantically valid close/ownership
|
||||
checks across versions. Match executable host, runtime, architecture and
|
||||
instrumentation. A growing original process is not an accepted denominator.
|
||||
2. Freeze source commits, all artifact hashes, runtime/image identities, private
|
||||
PKI identity, fixtures and the serial same-host schedule before launch.
|
||||
3. Submit the actual harness change, negative controls and immutable artifact
|
||||
receipts to Astra. Approval of diagnostics is not approval of acceptance.
|
||||
4. Resolve continuing macOS native growth independently. A Linux acceptance run
|
||||
cannot waive it, even if the Linux resource guard passes.
|
||||
|
||||
Before this draft becomes executable, define version-appropriate standard-node
|
||||
loading/factory trace coverage for the original binary. The absence of SDK2's
|
||||
`Opc.Ua.OpcUaExtensions` type must not let the original pass an irrelevant zero
|
||||
counter. Also predeclare identical retained-managed/GC and RSS sampling cadence
|
||||
and statistics, including exactly which samples feed the relative 15% comparison.
|
||||
These details remain pending; observed results cannot select them retroactively.
|
||||
|
||||
## Proposed fixed warmup and measurement
|
||||
|
||||
This section is a review proposal only. If the prerequisites and Astra gate pass,
|
||||
qualification uses exactly 384 canonical cycles / 96 restarts plus one settling
|
||||
cycle within 1,800 seconds. The fixed trace windows are cycles 321–352 and
|
||||
353–384, with the continuous veto across their boundary. Require zero generated-
|
||||
factory JIT/load events, zero new SDK assembly loads and zero lost events. Reject
|
||||
missing markers, wrong identity or observer faults. Do not extend or retry the
|
||||
qualification until a favorable sample appears.
|
||||
|
||||
Prospectively select exactly the post-cycle-385 sample as the measurement
|
||||
baseline; do not choose a minimum, median or later sample from observed RSS.
|
||||
Retain all cold, peak and rejected rows. Measurement must then last at least
|
||||
7,200 actual seconds **after** warmup, retaining the +64 MiB managed, +128 MiB
|
||||
RSS and +32 descriptor caps, all canonical operations and semantic/native/
|
||||
ownership guards. Latch the first failure permanently. No resource resets,
|
||||
skipped operations or completion claim on a rejected run.
|
||||
Setup, warmup and final cleanup do not count toward measurement duration. Final
|
||||
shutdown and ownership checks are mandatory after that duration completes.
|
||||
|
||||
For uninstrumented acceptance, first perform an identical fresh paired
|
||||
qualification rehearsal, then use the fixed warmup without EventPipe or
|
||||
allocation StackTrace tracking. The earlier tracked candidate run cannot be
|
||||
relabelled as this evidence. Predefine sampling and statistics for at least three
|
||||
matched representative runs to assess the separate plan-relative throughput,
|
||||
latency, allocation and RSS budgets. A two-hour absolute-cap pass alone does not
|
||||
satisfy those comparisons.
|
||||
|
||||
Only verified results meeting these distinct requirements can support closing
|
||||
#799. Keep #782 G4 and draft PR #802 on hold until their remaining gates also
|
||||
pass; Windows/runtime and licensed interoperability evidence remain separate.
|
||||
@@ -0,0 +1,120 @@
|
||||
# Candidate saturation experiment — review before launch
|
||||
|
||||
This is a prospective diagnostic protocol, not an acceptance run or a replacement
|
||||
for the failed cold server soak. Production code, the cycle-3 resource baseline,
|
||||
and the +64 MiB managed / +128 MiB RSS / +32 descriptor caps remain unchanged.
|
||||
Only `OTOPCUA_SDK2_SOAK_MODE=saturation` selects this behavior; the existing
|
||||
`diagnostic`, `profile`, and `full` modes keep their existing stopping rules.
|
||||
|
||||
The fixed canonical phase performs exactly 384 lifecycle cycles, with the
|
||||
existing active-resource restart after every fourth cycle: 96 completed restarts.
|
||||
It then performs exactly one settling cycle, without another restart. The phase,
|
||||
including observer handshakes, must finish within 1,800 seconds. Initial fixture
|
||||
startup has a separate 120-second bound. There is no early completion, extension,
|
||||
RSS-selected endpoint, budget reset, or two-hour/full-pass flag. Cleanup remains
|
||||
bounded independently of the phase deadline.
|
||||
|
||||
All canonical operations remain in use: encrypted opc.tcp sessions, namespace URI
|
||||
binding, scalar/typed-array read/write, subscriptions, browse abandonment, history
|
||||
continuation churn/release/cross-session/close, surgical changes/rebuilds, key
|
||||
revocation, paused network reads/recovery, and same-PKI server restart. All
|
||||
ownership, continuation, PKI-descriptor, store and node inventory checks remain
|
||||
active. Cold budget rejection is latched at its original observation and rethrown,
|
||||
even if diagnostic readiness later succeeds. A rejected readiness receipt does
|
||||
not become a passing test or a new resource baseline.
|
||||
|
||||
## Observer boundaries
|
||||
|
||||
A fresh task-owned control directory and UUID bind every request/receipt to this
|
||||
run and target PID. Files are published atomically; existing, malformed, foreign,
|
||||
or incomplete receipts fail closed. The observer is a separate diagnostic process
|
||||
using public `DiagnosticsClient` / EventPipe APIs. Product dependencies are
|
||||
unchanged. A separate short positive control first invokes a named non-SDK method
|
||||
with NoInlining/NoOptimization after attaching and requires its JIT-start count in
|
||||
the parsed receipt. This proves CLR provider delivery/classification independently
|
||||
of custom marker delivery. That probe is never invoked by the canonical experiment.
|
||||
|
||||
One continuous trace covers both fixed windows, including their boundary gap:
|
||||
|
||||
| Marker | Before/after operation | Completed restarts |
|
||||
|---|---|---:|
|
||||
| window1-start | Before cycle 321 | 80 |
|
||||
| window1-end | After cycle 352 and its restart | 88 |
|
||||
| window2-start | Before cycle 353 | 88 |
|
||||
| window2-end | After cycle 384 and its restart | 96 |
|
||||
|
||||
The target waits for the observer's active receipt and enabled marker provider
|
||||
before its first window operation. The parser requires exactly these four
|
||||
markers, the correct UUID and cycle/restart values, and strictly ordered
|
||||
timestamps. A continuous veto also covers events outside the marker ranges
|
||||
within the attached interval. Both windows and the entire attached interval
|
||||
require zero `Opc.Ua.OpcUaExtensions` JIT starts and method-load events, zero new
|
||||
`Opc.Ua.*` assembly-load events, and zero lost events. Other JIT counts and loaded
|
||||
native method-byte sums are reported for review; they do not establish that all
|
||||
runtime paths are warmed. Method-size sums do not equal resident memory.
|
||||
|
||||
Runtime capture is Verbose `Microsoft-Windows-DotNETRuntime`, keywords `0x18`
|
||||
(Loader + JIT), plus Informational `OtOpcUa-Sdk2-Saturation` markers, no rundown,
|
||||
and a prospectively fixed **16 MiB** circular-buffer capacity. This is a new
|
||||
experiment and does not reinterpret the earlier 256 MiB-capacity traces.
|
||||
|
||||
After restart 96, the target requests stop and waits. The observer must stop its
|
||||
session, drain the event stream, close it, parse the entire trace, and atomically
|
||||
publish a PID/run-bound drained receipt. Its Stop/copy/dispose/parse stages have
|
||||
10/10/5/10-second bounds. Target receipt waits are bounded at 45 seconds and remain
|
||||
inside the 1,800-second phase deadline. Observer faults propagate immediately.
|
||||
The target also verifies its marker provider is disabled before the settling
|
||||
cycle. Missing markers, no events, event loss, or malformed stream cannot prove
|
||||
readiness. A parser failure produces a failed observer receipt, not empty counts.
|
||||
|
||||
The `saturation-result` separates readiness, cold budget rejection, fixed operation
|
||||
completion and verified shutdown ownership. It always records `acceptance=false`.
|
||||
It is emitted before rethrowing the original cold assertion. The launcher retains
|
||||
nonzero test/container exits and records diagnostic readiness separately.
|
||||
|
||||
## Prepare immutable Linux artifacts
|
||||
|
||||
Commit the reviewed source first. Preparation refuses a dirty checkout and
|
||||
publishes fresh, separate app/observer artifacts for `linux-arm64`, self-contained
|
||||
.NET **10.0.14**, without trimming or single-file publishing. It never starts the
|
||||
experiment. The bundle records source revision, source hashes, exact publish
|
||||
command, fixed protocol, image digest and SHA-256 of every app/observer/tool file.
|
||||
It requires the landed HistorianGateway Client/Contracts 1.0.0 dependency graph
|
||||
and records assembly versions/module MVIDs by reading PE metadata without loading
|
||||
or executing product assemblies.
|
||||
|
||||
```bash
|
||||
python3 scripts/validation/opcua-lifecycle-memory/prepare_saturation.py \
|
||||
--output /tmp/opcua-saturation-reviewed \
|
||||
--libraries "$diagnosticToolLibraries" \
|
||||
--image sha256:b04611ee9e1b7d36797012ed921962c57189cb4dbb3dff12afb8e158110f293b
|
||||
```
|
||||
|
||||
`diagnosticToolLibraries` is the installed dotnet-trace 9.0.661903
|
||||
`tools/net8.0/any` directory. No product package reference is added. Artifact
|
||||
preparation and review are prerequisites; root and Astra must review the actual
|
||||
code, control tests and receipt before launch. The coordinator owns the long
|
||||
execution.
|
||||
|
||||
```bash
|
||||
python3 /tmp/opcua-saturation-reviewed/tools/launch_saturation.py \
|
||||
--bundle /tmp/opcua-saturation-reviewed --receipt-sha256 "$reviewedReceiptHash" \
|
||||
--output /tmp/opcua-saturation-run --verify-only
|
||||
# After root/Astra review, root runs the same command without --verify-only.
|
||||
```
|
||||
|
||||
The launcher rechecks every immutable file before and after the run. It creates a
|
||||
fresh offline task-owned container, writable output/PKI directories, and read-only
|
||||
app/observer/tool mounts. Descriptor controls run first in a separate process.
|
||||
TRX guards require two descriptor tests and one canonical test actually executed,
|
||||
with no skipped results. The launcher verifies the actual target and observer
|
||||
mapped their own self-contained `libcoreclr.so`; the older SDK-image controller
|
||||
runtime is not the target runtime. A 2,040-second outer timeout includes startup,
|
||||
phase and bounded cleanup; it does not extend the 1,800-second phase.
|
||||
|
||||
No long saturation run has been launched during implementation. Existing cold
|
||||
failures remain untouched. No original-SDK full-workload adapter, warmed
|
||||
acceptance baseline, additional two-hour server run, or production correction is
|
||||
part of this change. The separately demonstrated Mac native certificate-related
|
||||
inventory growth remains unresolved even if generated-factory compilation
|
||||
saturates.
|
||||
@@ -0,0 +1,133 @@
|
||||
// Diagnostic-only observer. The target remains the unchanged canonical lifecycle test process.
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Diagnostics.NETCore.Client;
|
||||
using Microsoft.Diagnostics.Tracing;
|
||||
|
||||
const string protocol = "sdk2-saturation-v1";
|
||||
const string markerProvider = "OtOpcUa-Sdk2-Saturation";
|
||||
var control = Path.GetFullPath(args[0]);
|
||||
var runId = args[1];
|
||||
if (!Guid.TryParseExact(runId, "D", out _)) throw new InvalidDataException("Malformed run identity");
|
||||
using var lifetime = new CancellationTokenSource(TimeSpan.FromSeconds(1980));
|
||||
var ct = lifetime.Token;
|
||||
try
|
||||
{
|
||||
var request = await WaitJson("attach-request.json", ct);
|
||||
using (request)
|
||||
{
|
||||
Identity(request.RootElement);
|
||||
Require(request.RootElement.GetProperty("firstCycle").GetInt32() == 321
|
||||
&& request.RootElement.GetProperty("completedRestarts").GetInt32() == 80, "Invalid attach boundary");
|
||||
var targetPid = request.RootElement.GetProperty("targetPid").GetInt32();
|
||||
Require(targetPid > 0 && targetPid != Environment.ProcessId, "Invalid target process");
|
||||
var providers = new[]
|
||||
{
|
||||
new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Verbose, 0x18),
|
||||
new EventPipeProvider(markerProvider, EventLevel.Informational)
|
||||
};
|
||||
var client = new DiagnosticsClient(targetPid);
|
||||
var session = await Task.Run(() => client.StartEventPipeSession(providers, requestRundown: false, circularBufferMB: 16), ct)
|
||||
.WaitAsync(TimeSpan.FromSeconds(10), ct);
|
||||
var trace = Path.Combine(control, "readiness.nettrace");
|
||||
await using (var output = new FileStream(trace, FileMode.CreateNew, FileAccess.Write, FileShare.Read))
|
||||
{
|
||||
var copy = session.EventStream.CopyToAsync(output, ct);
|
||||
await Publish("active.json", new { protocol, runId, targetPid, observerPid = Environment.ProcessId, active = true });
|
||||
using var stop = await WaitJson("detach-request.json", ct);
|
||||
Identity(stop.RootElement);
|
||||
Require(stop.RootElement.GetProperty("targetPid").GetInt32() == targetPid
|
||||
&& stop.RootElement.GetProperty("lastCycle").GetInt32() == 384
|
||||
&& stop.RootElement.GetProperty("completedRestarts").GetInt32() == 96, "Invalid detach boundary");
|
||||
await Task.Run(session.Stop, ct).WaitAsync(TimeSpan.FromSeconds(10), ct);
|
||||
await copy.WaitAsync(TimeSpan.FromSeconds(10), ct);
|
||||
await output.FlushAsync(ct);
|
||||
}
|
||||
await Task.Run(session.Dispose, ct).WaitAsync(TimeSpan.FromSeconds(5), ct);
|
||||
var summary = await Task.Run(() => Parse(trace, runId), ct).WaitAsync(TimeSpan.FromSeconds(10), ct);
|
||||
// Published only after Stop, stream drain, disposal and complete parsing. Never just a file-exists ACK.
|
||||
await Publish("drained.json", new
|
||||
{
|
||||
protocol, runId, targetPid, observerPid = Environment.ProcessId, active = false, drained = true,
|
||||
summary.markersValid, summary.eventsLost, summary.sdkFactoryJit, summary.sdkFactoryLoads,
|
||||
summary.sdkAssemblyLoads, summary.windows, summary.otherJit, summary.loadedNativeBytes, summary.runtimeControlJit,
|
||||
configuration = new { runtimeProvider = "Microsoft-Windows-DotNETRuntime", level = "Verbose", keywords = "0x18",
|
||||
markerProvider, circularBufferMiB = 16, requestRundown = false },
|
||||
acceptance = false
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
await Publish("observer-failed.json", new { protocol, runId, exceptionType = error.GetType().FullName });
|
||||
// An IPC/Stop worker can still be blocked. The parent also bounds and reaps this separate process.
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
void Identity(JsonElement value) => Require(value.GetProperty("protocol").GetString() == protocol
|
||||
&& value.GetProperty("runId").GetString() == runId, "Mismatched observer identity");
|
||||
static void Require(bool condition, string message) { if (!condition) throw new InvalidDataException(message); }
|
||||
async Task<JsonDocument> WaitJson(string name, CancellationToken token)
|
||||
{
|
||||
var path = Path.Combine(control, name);
|
||||
while (!File.Exists(path)) await Task.Delay(25, token);
|
||||
return JsonDocument.Parse(await File.ReadAllTextAsync(path, token));
|
||||
}
|
||||
async Task Publish(string name, object value)
|
||||
{
|
||||
var temporary = Path.Combine(control, name + ".tmp");
|
||||
await using (var stream = new FileStream(temporary, FileMode.CreateNew, FileAccess.Write))
|
||||
await JsonSerializer.SerializeAsync(stream, value);
|
||||
File.Move(temporary, Path.Combine(control, name));
|
||||
}
|
||||
static TraceResult Parse(string path, string expectedRun)
|
||||
{
|
||||
var runtimeControlJit = 0;
|
||||
var markers = new List<Marker>();
|
||||
var events = new List<Observed>();
|
||||
using var source = new EventPipeEventSource(path);
|
||||
source.Clr.MethodJittingStarted += data =>
|
||||
{
|
||||
if (data.MethodNamespace == "ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests.Sdk2LifecycleSoakTests"
|
||||
&& data.MethodName == "SaturationProtocolRuntimeProbe") runtimeControlJit++;
|
||||
events.Add(new(data.TimeStampRelativeMSec,
|
||||
data.MethodNamespace == "Opc.Ua.OpcUaExtensions" ? "factory-jit" : "other-jit", 0));
|
||||
};
|
||||
source.Clr.MethodLoadVerbose += data => events.Add(new(data.TimeStampRelativeMSec,
|
||||
data.MethodNamespace == "Opc.Ua.OpcUaExtensions" ? "factory-load" : "other-load", data.MethodSize));
|
||||
source.Clr.LoaderAssemblyLoad += data =>
|
||||
{
|
||||
if (data.FullyQualifiedAssemblyName.StartsWith("Opc.Ua.", StringComparison.Ordinal))
|
||||
events.Add(new(data.TimeStampRelativeMSec, "sdk-assembly", 0));
|
||||
};
|
||||
source.Dynamic.All += data =>
|
||||
{
|
||||
if (data.ProviderName == "OtOpcUa-Sdk2-Saturation" && data.EventName == "Boundary")
|
||||
markers.Add(new(data.TimeStampRelativeMSec, (string)data.PayloadByName("runId"),
|
||||
(string)data.PayloadByName("name"), (int)data.PayloadByName("cycle"), (int)data.PayloadByName("restarts")));
|
||||
};
|
||||
source.Process();
|
||||
(string Name, int Cycle, int Restarts)[] expected =
|
||||
[ ("window1-start", 321, 80), ("window1-end", 352, 88),
|
||||
("window2-start", 353, 88), ("window2-end", 384, 96) ];
|
||||
var valid = markers.Count == 4 && markers.Select((marker, index) =>
|
||||
marker.Run == expectedRun && (marker.Name, marker.Cycle, marker.Restarts) == expected[index]).All(x => x)
|
||||
&& markers.Zip(markers.Skip(1)).All(pair => pair.First.Ms < pair.Second.Ms);
|
||||
var windows = Enumerable.Range(0, 2).Select(index =>
|
||||
{
|
||||
var selected = valid ? events.Where(x => x.Ms >= markers[index * 2].Ms && x.Ms <= markers[index * 2 + 1].Ms).ToArray() : [];
|
||||
return new Window($"window{index + 1}", 321 + index * 32, 352 + index * 32, 80 + index * 8, 88 + index * 8,
|
||||
selected.Count(x => x.Kind == "factory-jit"), selected.Count(x => x.Kind == "factory-load"),
|
||||
selected.Count(x => x.Kind == "sdk-assembly"));
|
||||
}).ToArray();
|
||||
// Totals include the whole attached interval, including marker gaps, startup and stopping the collector.
|
||||
return new TraceResult(valid, source.EventsLost, events.Count(x => x.Kind == "factory-jit"),
|
||||
events.Count(x => x.Kind == "factory-load"), events.Count(x => x.Kind == "sdk-assembly"), windows,
|
||||
events.Count(x => x.Kind == "other-jit"), events.Sum(x => x.Bytes), runtimeControlJit);
|
||||
}
|
||||
record Marker(double Ms, string Run, string Name, int Cycle, int Restarts);
|
||||
record Observed(double Ms, string Kind, long Bytes);
|
||||
record Window(string name, int firstCycle, int lastCycle, int startRestarts, int endRestarts,
|
||||
int sdkFactoryJit, int sdkFactoryLoads, int sdkAssemblyLoads);
|
||||
record TraceResult(bool markersValid, int eventsLost, int sdkFactoryJit, int sdkFactoryLoads,
|
||||
int sdkAssemblyLoads, Window[] windows, int otherJit, long loadedNativeBytes, int runtimeControlJit);
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Opc.Ua;
|
||||
var dispose = args[0] == "dispose";
|
||||
for (int i = 0; i <= 10000; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
var t = DefaultTelemetry.Create(_ => { });
|
||||
t.LoggerFactory.CreateLogger("TelemetryControl").LogDebug("control");
|
||||
if (dispose) ((IDisposable)t).Dispose();
|
||||
}
|
||||
if (i % 250 == 0)
|
||||
{
|
||||
var live = GC.GetTotalMemory(true);
|
||||
using var p = Process.GetCurrentProcess();
|
||||
p.Refresh();
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { i, dispose, live, rss = p.WorkingSet64, methods = JitInfo.GetCompiledMethodCount(), il = JitInfo.GetCompiledILBytes(), gc = GC.GetGCMemoryInfo().TotalCommittedBytes, threads = p.Threads.Count, timers = Timer.ActiveCount }));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Redacted aggregate summary. Keep raw traces local.
|
||||
using Microsoft.Diagnostics.Tracing;
|
||||
using System.Text.Json;
|
||||
|
||||
var starts = new Dictionary<string, int>();
|
||||
var tiers = new Dictionary<string, int>();
|
||||
var nativeBytes = new Dictionary<string, long>();
|
||||
var factoryNames = new Dictionary<string, int>();
|
||||
var factoryTokens = new HashSet<int>();
|
||||
var factoryModules = new HashSet<long>();
|
||||
var firstFactoryMs = double.PositiveInfinity;
|
||||
var lastFactoryMs = 0d;
|
||||
var factoryNativeBytes = 0L;
|
||||
var sdkAssemblyLoads = 0;
|
||||
using var source = new EventPipeEventSource(args[0]);
|
||||
source.Clr.MethodJittingStarted += data =>
|
||||
{
|
||||
var name = data.MethodNamespace + "." + data.MethodName;
|
||||
starts[name] = starts.GetValueOrDefault(name) + 1;
|
||||
if (!name.StartsWith("Opc.Ua.OpcUaExtensions.", StringComparison.Ordinal)) return;
|
||||
factoryNames[name] = factoryNames.GetValueOrDefault(name) + 1;
|
||||
factoryTokens.Add(data.MethodToken);
|
||||
factoryModules.Add(data.ModuleID);
|
||||
firstFactoryMs = Math.Min(firstFactoryMs, data.TimeStampRelativeMSec);
|
||||
lastFactoryMs = Math.Max(lastFactoryMs, data.TimeStampRelativeMSec);
|
||||
};
|
||||
source.Clr.MethodLoadVerbose += data =>
|
||||
{
|
||||
var tier = data.OptimizationTier.ToString();
|
||||
tiers[tier] = tiers.GetValueOrDefault(tier) + 1;
|
||||
nativeBytes[tier] = nativeBytes.GetValueOrDefault(tier) + data.MethodSize;
|
||||
if (data.MethodNamespace == "Opc.Ua.OpcUaExtensions") factoryNativeBytes += data.MethodSize;
|
||||
};
|
||||
source.Clr.LoaderAssemblyLoad += data =>
|
||||
{
|
||||
if (data.FullyQualifiedAssemblyName.StartsWith("Opc.Ua.", StringComparison.Ordinal)) sdkAssemblyLoads++;
|
||||
};
|
||||
source.Process();
|
||||
Console.WriteLine(JsonSerializer.Serialize(new
|
||||
{
|
||||
traceStartUtc = source.SessionStartTime.ToUniversalTime(),
|
||||
traceEndUtc = source.SessionEndTime.ToUniversalTime(),
|
||||
eventsLost = source.EventsLost,
|
||||
sdkAssemblyLoads,
|
||||
jittingStarts = starts.Values.Sum(),
|
||||
// Name keys omit generic signatures; token/module counts independently identify the factory set.
|
||||
distinctNameKeys = starts.Count,
|
||||
opcUaJittingStarts = starts.Where(x => x.Key.StartsWith("Opc.Ua.", StringComparison.Ordinal)).Sum(x => x.Value),
|
||||
invokeStubStarts = starts.Where(x => x.Key.StartsWith("dynamicClass.InvokeStub", StringComparison.Ordinal)).Sum(x => x.Value),
|
||||
tiers,
|
||||
summedLoadedNativeMethodBytes = nativeBytes,
|
||||
factory = new
|
||||
{
|
||||
starts = factoryNames.Values.Sum(), distinctNames = factoryNames.Count,
|
||||
distinctTokens = factoryTokens.Count, moduleCount = factoryModules.Count,
|
||||
everyNameOnce = factoryNames.Values.All(x => x == 1),
|
||||
firstMs = double.IsFinite(firstFactoryMs) ? (double?)firstFactoryMs : null,
|
||||
lastMs = factoryNames.Count == 0 ? (double?)null : lastFactoryMs,
|
||||
summedLoadedNativeMethodBytes = factoryNativeBytes,
|
||||
examples = factoryNames.Keys.Take(10).ToArray()
|
||||
}
|
||||
}, new JsonSerializerOptions { WriteIndented = true }));
|
||||
@@ -0,0 +1,144 @@
|
||||
using System.Globalization;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Server;
|
||||
using Shouldly;
|
||||
internal static class BaselineMetrics
|
||||
{
|
||||
private static FieldInfo Field(object owner, string name)
|
||||
{
|
||||
for (var type = owner.GetType(); type is not null; type = type.BaseType)
|
||||
if (type.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly) is { } member) return member;
|
||||
throw new InvalidOperationException("Missing exact-.182 diagnostic field: " + name);
|
||||
}
|
||||
private static T Required<T>(object owner, string name) => Field(owner, name).GetValue(owner) is T value ? value
|
||||
: throw new InvalidOperationException("Unexpected exact-.182 diagnostic field type: " + name);
|
||||
internal static (int Browse, int History) Points(ISession session)
|
||||
{
|
||||
using (Required<System.Threading.Lock>(session, "m_lock").EnterScope())
|
||||
return (Count(session, "m_browseContinuationPoints"), Count(session, "m_historyContinuationPoints"));
|
||||
}
|
||||
private static int Count(object owner, string name) => Field(owner, name).GetValue(owner) switch
|
||||
{ null => 0, ICollection values => values.Count, _ => throw new InvalidOperationException("Unexpected continuation collection type") };
|
||||
internal static int CachedStoreReferences(CertificateValidator validator)
|
||||
{
|
||||
var gate = Required<SemaphoreSlim>(validator, "m_semaphore");
|
||||
if (!gate.Wait(TimeSpan.FromSeconds(5))) throw new TimeoutException("Original validator diagnostic lock did not settle");
|
||||
try
|
||||
{
|
||||
return new[] { "m_trustedCertificateStore", "m_issuerCertificateStore", "m_rejectedCertificateStore" }
|
||||
.Select(name => Field(validator, name).GetValue(validator))
|
||||
.Count(value => value switch
|
||||
{
|
||||
null => false,
|
||||
CertificateStoreIdentifier store => Field(store, "m_store").GetValue(store) switch
|
||||
{ null => false, ICertificateStore => true, _ => throw new InvalidOperationException("Original cached store type changed") },
|
||||
_ => throw new InvalidOperationException("Original validator store identifier type changed")
|
||||
});
|
||||
}
|
||||
finally { gate.Release(); }
|
||||
}
|
||||
internal static int Counter(object manager, string name)
|
||||
{
|
||||
var property = manager.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("Missing original node-manager diagnostic property: " + name);
|
||||
return property.PropertyType == typeof(int) && property.GetValue(manager) is int value ? value
|
||||
: throw new InvalidOperationException("Unexpected original node-manager diagnostic property type: " + name);
|
||||
}
|
||||
internal static async Task<(int Total, int Pki)> DescriptorsAsync(CancellationToken ct, params string[] roots)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
var canonicalRoots = roots.Select(root => CanonicalPath(root)).Distinct(StringComparer.Ordinal).ToArray();
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
var targets = Directory.EnumerateFileSystemEntries("/proc/self/fd")
|
||||
.Select(path => new FileInfo(path).LinkTarget).ToArray();
|
||||
return (targets.Length, targets.Count(path => IsWithinRoots(path, canonicalRoots)));
|
||||
}
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
using var process = new Process { StartInfo = new ProcessStartInfo("/usr/sbin/lsof")
|
||||
{ RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false } };
|
||||
foreach (var arg in new[] { "-a", "-p", Environment.ProcessId.ToString(CultureInfo.InvariantCulture), "-Fnf" })
|
||||
process.StartInfo.ArgumentList.Add(arg);
|
||||
var text = await ReadDescriptorProbeAsync(process, TimeSpan.FromSeconds(5), ct);
|
||||
process.ExitCode.ShouldBe(0);
|
||||
var total = 0;
|
||||
var pki = 0;
|
||||
var descriptor = false;
|
||||
foreach (var line in text.Split('\n'))
|
||||
{
|
||||
if (line.StartsWith('f'))
|
||||
{
|
||||
descriptor = line.Length > 1 && char.IsDigit(line[1]);
|
||||
if (descriptor) total++;
|
||||
}
|
||||
else if (descriptor && line.StartsWith('n') && IsWithinRoots(line[1..], canonicalRoots)) pki++;
|
||||
}
|
||||
return (total, pki);
|
||||
}
|
||||
throw new PlatformNotSupportedException("This soak requires Linux procfs or macOS lsof for actual descriptor evidence.");
|
||||
}
|
||||
|
||||
private static async Task<string> ReadDescriptorProbeAsync(Process process, TimeSpan timeout, CancellationToken ct)
|
||||
{
|
||||
using var deadline = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||
deadline.CancelAfter(timeout);
|
||||
process.Start();
|
||||
var stdout = process.StandardOutput.ReadToEndAsync(deadline.Token);
|
||||
var stderr = process.StandardError.ReadToEndAsync(deadline.Token);
|
||||
var exited = process.WaitForExitAsync(deadline.Token);
|
||||
try
|
||||
{
|
||||
// Waiting for exit alone does not bound reads: both redirected pipes must also finish.
|
||||
await Task.WhenAll(stdout, stderr, exited).WaitAsync(deadline.Token).ConfigureAwait(false);
|
||||
return await stdout.ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
try { if (!process.HasExited) process.Kill(entireProcessTree: true); }
|
||||
catch (InvalidOperationException) when (process.HasExited) { /* It exited between the check and kill. */ }
|
||||
await process.WaitForExitAsync(CancellationToken.None).WaitAsync(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
|
||||
// Observe both cancelled drains; never leave a pipe read running after returning.
|
||||
try { await Task.WhenAll(stdout, stderr).WaitAsync(TimeSpan.FromSeconds(2)).ConfigureAwait(false); }
|
||||
catch (OperationCanceledException) { }
|
||||
if (ct.IsCancellationRequested) throw;
|
||||
throw new TimeoutException("Descriptor probe exceeded its process and redirected-stream deadline.");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsWithinRoots(string? path, IReadOnlyList<string> roots)
|
||||
{
|
||||
if (path is null || !Path.IsPathFullyQualified(path)) return false;
|
||||
// procfs marks an unlinked but still open file; its containing physical directory still counts.
|
||||
const string deleted = " (deleted)";
|
||||
if (path.EndsWith(deleted, StringComparison.Ordinal)) path = path[..^deleted.Length];
|
||||
var canonical = CanonicalPath(path);
|
||||
return roots.Any(root => canonical.Equals(root, StringComparison.Ordinal)
|
||||
|| canonical.StartsWith(Path.EndsInDirectorySeparator(root) ? root : root + Path.DirectorySeparatorChar,
|
||||
StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
private static string CanonicalPath(string path, int depth = 0)
|
||||
{
|
||||
if (depth > 32) throw new IOException("Descriptor path link resolution exceeded its bound.");
|
||||
var full = Path.GetFullPath(path);
|
||||
var current = Path.GetPathRoot(full)!;
|
||||
foreach (var component in full[current.Length..].Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
current = Path.Combine(current, component);
|
||||
FileSystemInfo entry = Directory.Exists(current) ? new DirectoryInfo(current) : new FileInfo(current);
|
||||
if (entry.LinkTarget is not null)
|
||||
{
|
||||
var target = entry.ResolveLinkTarget(returnFinalTarget: true)
|
||||
?? throw new IOException("Descriptor path link resolution returned no target.");
|
||||
// Resolve parent links too: an alias target can itself begin with /var or /tmp.
|
||||
current = CanonicalPath(target.FullName, depth + 1);
|
||||
}
|
||||
}
|
||||
return Path.TrimEndingDirectorySeparator(current);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Opc.Ua;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Authorization;
|
||||
using ZB.MOM.WW.OtOpcUa.OpcUaServer;
|
||||
using ZB.MOM.WW.OtOpcUa.OpcUaServer.Security;
|
||||
|
||||
internal sealed class BaselineRig : IAsyncDisposable
|
||||
{
|
||||
internal const string Cluster = "SITE-A", RawDeviceFolder = "Plant/Modbus/dev1", RawAlarmPath = "Plant/Modbus/dev1/temp_hi";
|
||||
internal const string EqA = "EqA", EqB = "EqB", KeyPrincipal = "event-probe", KeyId = "k-server-events";
|
||||
internal static string KeyPassword => Environment.GetEnvironmentVariable("OTOPCUA_BASELINE_CANONICAL_KEY")
|
||||
?? throw new InvalidOperationException("An ephemeral fixture credential is required");
|
||||
private readonly OpcUaApplicationHostOptions _options;
|
||||
private OpcUaApplicationHost _host = null!;
|
||||
private bool _disposed;
|
||||
internal OtOpcUaSdkServer Server { get; private set; } = null!;
|
||||
internal OtOpcUaNodeManager Nm => Server.NodeManager!;
|
||||
internal SdkAddressSpaceSink Sink { get; private set; } = null!;
|
||||
internal string EndpointUrl => $"opc.tcp://127.0.0.1:{_options.OpcUaPort}/OtOpcUa";
|
||||
internal string PkiRoot { get; }
|
||||
internal CertificateValidator Validator
|
||||
{
|
||||
get
|
||||
{
|
||||
var applicationField = typeof(OpcUaApplicationHost).GetField("_application", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
?? throw new InvalidOperationException("Original host application field is missing");
|
||||
return applicationField.GetValue(_host) is Opc.Ua.Configuration.ApplicationInstance application
|
||||
&& application.ApplicationConfiguration.CertificateValidator is CertificateValidator validator ? validator
|
||||
: throw new InvalidOperationException("Original host application field type changed");
|
||||
}
|
||||
}
|
||||
internal BaselineRig(string directory)
|
||||
{
|
||||
PkiRoot = Path.Combine(directory, "server-pki");
|
||||
using var listener = new TcpListener(IPAddress.Loopback, 0);
|
||||
listener.Start();
|
||||
_options = new OpcUaApplicationHostOptions
|
||||
{
|
||||
ApplicationName = "OtOpcUa.SDK2LifecycleSoak", ApplicationUri = $"urn:OtOpcUa.SDK2LifecycleSoak.{Guid.NewGuid():N}",
|
||||
OpcUaPort = ((IPEndPoint)listener.LocalEndpoint).Port, PublicHostname = "127.0.0.1", PkiStoreRoot = PkiRoot,
|
||||
EnabledSecurityProfiles = [OpcUaSecurityProfile.None, OpcUaSecurityProfile.Basic256Sha256SignAndEncrypt],
|
||||
AutoAcceptUntrustedClientCertificates = true,
|
||||
};
|
||||
}
|
||||
internal async Task BootAsync(CancellationToken ct)
|
||||
{
|
||||
Server = new OtOpcUaSdkServer();
|
||||
_host = new OpcUaApplicationHost(_options, NullLogger<OpcUaApplicationHost>.Instance, new Auth());
|
||||
await _host.StartAsync(Server, ct);
|
||||
Canonical.Ledger.Record("host-start");
|
||||
Sink = new SdkAddressSpaceSink(Nm);
|
||||
}
|
||||
internal async Task RestartAsync(CancellationToken ct)
|
||||
{
|
||||
await _host.DisposeAsync();
|
||||
Canonical.Ledger.Record("host-stop");
|
||||
await BootAsync(ct);
|
||||
}
|
||||
internal DataPlaneAuthorityState Enforce(bool enabled, ApiKey[] keys, params NodeAcl[] grants)
|
||||
{
|
||||
var state = new DataPlaneAuthorityState();
|
||||
state.Apply(Cluster, "rev-1", keys, grants, new Dictionary<AclScopeRef, IReadOnlyList<NodeAclPath>>
|
||||
{
|
||||
[new AclScopeRef(NodeAclScopeKind.Equipment, "eq-" + EqA)] = [new NodeAclPath([NodeAclRealms.Uns, EqA])],
|
||||
[new AclScopeRef(NodeAclScopeKind.Equipment, "eq-" + EqB)] = [new NodeAclPath([NodeAclRealms.Uns, EqB])]
|
||||
});
|
||||
Nm.DataPlaneAuthority = state;
|
||||
Nm.Authorizer = new DataPlaneAuthorizer(state, new TriePermissionEvaluator(state.Tries), enabled, NullLogger.Instance, TimeProvider.System);
|
||||
Canonical.Ledger.Record("authority-restore");
|
||||
return state;
|
||||
}
|
||||
internal static ApiKey Key() => new() { ApiKeyId = KeyId, Name = KeyId, SecretHash = "hash", Principal = KeyPrincipal,
|
||||
Enabled = true, CreatedUtc = DateTime.UtcNow.AddDays(-1) };
|
||||
internal static NodeAcl ClusterGrant(string principal, string id, NodePermissions permissions) => new()
|
||||
{ NodeAclId = id, ClusterId = Cluster, Principal = principal, ScopeKind = NodeAclScopeKind.Cluster, PermissionFlags = permissions };
|
||||
internal void MaterialiseNative()
|
||||
{
|
||||
Sink.EnsureFolder(RawDeviceFolder, null, "dev1", AddressSpaceRealm.Raw);
|
||||
Sink.MaterialiseAlarmCondition(RawAlarmPath, RawDeviceFolder, "temp_hi", "OffNormalAlarm", 700, AddressSpaceRealm.Raw, isNative: true);
|
||||
Sink.EnsureFolder(EqA, null, "A", AddressSpaceRealm.Uns);
|
||||
Sink.EnsureFolder(EqB, null, "B", AddressSpaceRealm.Uns);
|
||||
Sink.WireAlarmNotifiers(RawAlarmPath, AddressSpaceRealm.Raw, [EqA, EqB], AddressSpaceRealm.Uns);
|
||||
}
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
try
|
||||
{
|
||||
if (_host is not null) { await _host.DisposeAsync(); Canonical.Ledger.Record("host-stop"); }
|
||||
}
|
||||
finally { if (Directory.Exists(PkiRoot)) Directory.Delete(PkiRoot, true); }
|
||||
}
|
||||
private sealed class Auth : IOpcUaUserAuthenticator
|
||||
{
|
||||
public Task<OpcUaUserAuthResult> AuthenticateUserNameAsync(string username, string password, CancellationToken ct) =>
|
||||
Task.FromResult(username == "apikey" && password.StartsWith($"otua_{KeyId}_", StringComparison.Ordinal)
|
||||
? OpcUaUserAuthResult.AllowKey(KeyPrincipal, [OpcUaDataPlaneRoles.AlarmAck, OpcUaDataPlaneRoles.WriteOperate], KeyId)
|
||||
: OpcUaUserAuthResult.Deny("Invalid fixture key"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Client;
|
||||
using Opc.Ua.Configuration;
|
||||
using Shouldly;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Authorization;
|
||||
using ClientSession = Opc.Ua.Client.ISession;
|
||||
using Subscription = Opc.Ua.Client.Subscription;
|
||||
using MonitoredItem = Opc.Ua.Client.MonitoredItem;
|
||||
internal static partial class Canonical
|
||||
{
|
||||
private static void Configure(BaselineRig rig)
|
||||
{
|
||||
Ledger.Record("configure");
|
||||
rig.MaterialiseNative();
|
||||
rig.Nm.HistorianDataSource = new SqlLikeEventSource(Enumerable.Range(0, 40)
|
||||
.Select(i => new HistoricalEvent($"soak-{i}", "source", Epoch.AddSeconds(i), Epoch, "soak", 500)).ToList());
|
||||
rig.Nm.NodeWriteGateway = new AcceptWrites();
|
||||
MaterialiseValues(rig);
|
||||
rig.Enforce(true, [BaselineRig.Key()], Grant());
|
||||
}
|
||||
|
||||
private static ZB.MOM.WW.OtOpcUa.Configuration.Entities.NodeAcl Grant() => BaselineRig.ClusterGrant(
|
||||
BaselineRig.KeyPrincipal, "soak-grant", NodePermissions.Browse | NodePermissions.Read | NodePermissions.Subscribe
|
||||
| NodePermissions.WriteOperate | NodePermissions.HistoryRead);
|
||||
|
||||
private static void MaterialiseValues(BaselineRig rig)
|
||||
{
|
||||
rig.Sink.EnsureFolder(BaselineRig.RawDeviceFolder, null, "Device", AddressSpaceRealm.Raw);
|
||||
rig.Sink.EnsureVariable(Scalar, BaselineRig.RawDeviceFolder, "Scalar", "Int32", true, AddressSpaceRealm.Raw);
|
||||
rig.Sink.EnsureVariable(ArrayTag, BaselineRig.RawDeviceFolder, "Array", "Int16", true, AddressSpaceRealm.Raw,
|
||||
isArray: true, arrayLength: 4);
|
||||
rig.Sink.EnsureFolder("SoakEquipment", null, "Equipment", AddressSpaceRealm.Uns);
|
||||
rig.Sink.EnsureVariable(UnsScalar, "SoakEquipment", "Scalar", "Int32", true, AddressSpaceRealm.Uns);
|
||||
for (var i = 0; i < 16; i++) rig.Sink.EnsureVariable($"{BaselineRig.RawDeviceFolder}/Browse{i}",
|
||||
BaselineRig.RawDeviceFolder, $"Browse{i}", "Int32", false, AddressSpaceRealm.Raw);
|
||||
rig.Sink.WriteValue(Scalar, 0, OpcUaStatus.Good, DateTime.UtcNow, AddressSpaceRealm.Raw);
|
||||
rig.Sink.WriteValue(ArrayTag, new short[] { 0, 1, 2, 3 }, OpcUaStatus.Good, DateTime.UtcNow, AddressSpaceRealm.Raw);
|
||||
rig.Sink.WriteValue(UnsScalar, 0, OpcUaStatus.Good, DateTime.UtcNow, AddressSpaceRealm.Uns);
|
||||
}
|
||||
|
||||
private static async Task ExerciseAsync(BaselineRig rig, ApplicationConfiguration config, int cycle,
|
||||
StreamWriter evidence, CancellationToken ct)
|
||||
{
|
||||
await using var proxy = new PauseProxy(new Uri(rig.EndpointUrl).Port);
|
||||
using var owner = await ConnectAsync(rig, config, proxy.Port, ct);
|
||||
using var other = await ConnectAsync(rig, config, null, ct);
|
||||
var ownedSessions = rig.Server.CurrentInstance.SessionManager.GetSessions().ToArray();
|
||||
ownedSessions.Length.ShouldBe(2);
|
||||
var scalar = Raw(owner, Scalar);
|
||||
var array = Raw(owner, ArrayTag);
|
||||
var unsNs = owner.NamespaceUris.GetIndex(V3NodeIds.UnsNamespaceUri);
|
||||
unsNs.ShouldBeGreaterThan(0);
|
||||
var uns = new NodeId(UnsScalar, (ushort)unsNs);
|
||||
owner.NamespaceUris.GetString(scalar.NamespaceIndex).ShouldBe(V3NodeIds.RawNamespaceUri);
|
||||
owner.NamespaceUris.GetString(uns.NamespaceIndex).ShouldBe(V3NodeIds.UnsNamespaceUri);
|
||||
using var subscription = new Subscription(owner.DefaultSubscription) { PublishingInterval = 100, KeepAliveCount = 10 };
|
||||
owner.AddSubscription(subscription);
|
||||
try
|
||||
{
|
||||
await Ledger.Call("subscription-create", () => subscription.CreateAsync(ct));
|
||||
var seen = new ConcurrentDictionary<NodeId, Variant>();
|
||||
foreach (var id in new[] { scalar, array, uns })
|
||||
{
|
||||
var item = new MonitoredItem(subscription.DefaultItem) { StartNodeId = id, AttributeId = Attributes.Value,
|
||||
SamplingInterval = 50, QueueSize = 10 };
|
||||
item.Notification += (sender, _) =>
|
||||
{
|
||||
foreach (var value in sender.DequeueValues()) if (StatusCode.IsGood(value.StatusCode)) seen[id] = value.WrappedValue;
|
||||
};
|
||||
subscription.AddItem(item);
|
||||
}
|
||||
await Ledger.Call("subscription-apply", () => subscription.ApplyChangesAsync(ct));
|
||||
subscription.MonitoredItems.ShouldAllBe(item => ServiceResult.IsGood(item.Status.Error));
|
||||
PublicCounts(rig).ShouldBe((2, 1, 3));
|
||||
var values = new short[] { (short)(cycle % 30000), 2, 3, 4 };
|
||||
var writes = await Ledger.Call("write", () => owner.WriteAsync(null, new[]
|
||||
{
|
||||
new WriteValue { NodeId = scalar, AttributeId = Attributes.Value, Value = new DataValue(new Variant(cycle)) },
|
||||
new WriteValue { NodeId = array, AttributeId = Attributes.Value, Value = new DataValue(new Variant(values)) },
|
||||
new WriteValue { NodeId = uns, AttributeId = Attributes.Value, Value = new DataValue(new Variant(cycle)) },
|
||||
}, ct));
|
||||
writes.Results.ToList().ShouldAllBe(status => status == StatusCodes.Good);
|
||||
(await Ledger.Call("read-value", () => owner.ReadValueAsync(scalar, ct))).Value.ShouldBeOfType<int>().ShouldBe(cycle);
|
||||
(await Ledger.Call("read-value", () => owner.ReadValueAsync(array, ct))).Value.ShouldBeOfType<short[]>().ShouldBe(values);
|
||||
(await Ledger.Call("read-value", () => owner.ReadValueAsync(uns, ct))).Value.ShouldBeOfType<int>().ShouldBe(cycle);
|
||||
await EventuallyAsync(() => seen.TryGetValue(scalar, out var value) && ScalarValue(value) == cycle
|
||||
&& seen.ContainsKey(array) && seen.TryGetValue(uns, out var mirror) && ScalarValue(mirror) == cycle, ct);
|
||||
|
||||
// One explicitly released and one deliberately abandoned browse point.
|
||||
var browse = await BrowseAsync(owner, ct);
|
||||
RequirePoint(browse);
|
||||
(await Ledger.Call("browse-next", () => owner.BrowseNextAsync(null, true, [browse], ct))).Results[0].StatusCode.ShouldBe(StatusCodes.Good);
|
||||
RequirePoint(await BrowseAsync(owner, ct));
|
||||
// Production history points: cross-session denial, single-use resume, explicit release and close cleanup.
|
||||
var first = (await PageAsync(owner, default, false, ct)).ContinuationPoint;
|
||||
RequirePoint(first);
|
||||
(await PageAsync(other, first, false, ct)).StatusCode.ShouldBe(StatusCodes.BadContinuationPointInvalid);
|
||||
var next = (await PageAsync(owner, first, false, ct)).ContinuationPoint;
|
||||
RequirePoint(next);
|
||||
(await PageAsync(owner, next, true, ct)).StatusCode.ShouldBe(StatusCodes.Good);
|
||||
for (var i = 0; i < 6; i++) RequirePoint((await PageAsync(owner, default, false, ct)).ContinuationPoint);
|
||||
var held = ownedSessions.Select(ContinuationCounts).ToArray();
|
||||
held.Sum(count => count.Browse).ShouldBeGreaterThan(0);
|
||||
held.Sum(count => count.History).ShouldBeGreaterThan(0);
|
||||
var active = PublicCounts(rig);
|
||||
await LogAsync(evidence, new { kind = "active", cycle, sessions = active.Sessions,
|
||||
subscriptions = active.Subscriptions, items = active.Items, browsePoints = held.Sum(x => x.Browse), historyPoints = held.Sum(x => x.History) });
|
||||
|
||||
rig.Nm.UpdateTagAttributes(Scalar, false, null, "Int32", false, null, AddressSpaceRealm.Raw).ShouldBeTrue();
|
||||
Ledger.Record("writable-off");
|
||||
rig.Nm.UpdateTagAttributes(Scalar, true, null, "Int32", false, null, AddressSpaceRealm.Raw).ShouldBeTrue();
|
||||
Ledger.Record("writable-on");
|
||||
rig.Nm.RemoveVariableNode(ArrayTag, AddressSpaceRealm.Raw).ShouldBeTrue();
|
||||
Ledger.Record("variable-remove");
|
||||
rig.Sink.EnsureVariable(ArrayTag, BaselineRig.RawDeviceFolder, "Array", "Int16", true, AddressSpaceRealm.Raw,
|
||||
isArray: true, arrayLength: 4);
|
||||
rig.Sink.WriteValue(ArrayTag, values, OpcUaStatus.Good, DateTime.UtcNow, AddressSpaceRealm.Raw);
|
||||
var authority = rig.Nm.DataPlaneAuthority.ShouldBeOfType<DataPlaneAuthorityState>();
|
||||
Ledger.Record("key-revoke");
|
||||
authority.Apply(BaselineRig.Cluster, $"revoked-{cycle}", [], [Grant()],
|
||||
new Dictionary<AclScopeRef, IReadOnlyList<NodeAclPath>>());
|
||||
var denied = await Ledger.Call("read-service", () => owner.ReadAsync(null, 0, TimestampsToReturn.Neither,
|
||||
[new ReadValueId { NodeId = scalar, AttributeId = Attributes.Value }], ct));
|
||||
denied.Results[0].StatusCode.ShouldBe(StatusCodes.BadUserAccessDenied);
|
||||
rig.Enforce(true, [BaselineRig.Key()], Grant());
|
||||
|
||||
proxy.Pause();
|
||||
Ledger.Record("network-pause");
|
||||
var stalled = Stopwatch.StartNew();
|
||||
string stalledStatus;
|
||||
try
|
||||
{
|
||||
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||
timeout.CancelAfter(TimeSpan.FromSeconds(1));
|
||||
await Ledger.Call("read-value", () => owner.ReadValueAsync(scalar, timeout.Token)).WaitAsync(TimeSpan.FromSeconds(5), ct);
|
||||
throw new InvalidOperationException("The deliberately paused network unexpectedly completed the read.");
|
||||
}
|
||||
catch (OperationCanceledException) when (!ct.IsCancellationRequested) { stalledStatus = "OperationCanceled"; }
|
||||
// Exact-tag ChannelAsyncOperation maps a canceled completion waiter to BadRequestInterrupted.
|
||||
catch (ServiceResultException error) when (error.StatusCode == StatusCodes.BadTimeout
|
||||
|| error.StatusCode == StatusCodes.BadRequestCancelledByClient || error.StatusCode == StatusCodes.BadRequestTimeout
|
||||
|| error.StatusCode == StatusCodes.BadRequestInterrupted) { stalledStatus = error.StatusCode.ToString(); }
|
||||
finally { proxy.Resume(); }
|
||||
(await Ledger.Call("read-value", () => owner.ReadValueAsync(scalar, ct))).StatusCode.ShouldBe(StatusCodes.Good);
|
||||
await LogAsync(evidence, new { kind = "network-stall-recovered", cycle, status = stalledStatus, seconds = stalled.Elapsed.TotalSeconds });
|
||||
await Ledger.Call("subscription-delete", () => subscription.DeleteAsync(true, ct));
|
||||
await Ledger.Call("subscription-remove", () => owner.RemoveSubscriptionAsync(subscription, ct));
|
||||
rig.Sink.RebuildAddressSpace();
|
||||
Ledger.Record("address-space-rebuild");
|
||||
Configure(rig);
|
||||
Ledger.Record("cycle-wire-body-completed");
|
||||
}
|
||||
finally
|
||||
{
|
||||
proxy.Resume();
|
||||
await Ledger.Call("session-close", () => owner.CloseAsync(ct)).WaitAsync(TimeSpan.FromSeconds(10), ct);
|
||||
await Ledger.Call("session-close", () => other.CloseAsync(ct)).WaitAsync(TimeSpan.FromSeconds(10), ct);
|
||||
foreach (var closed in ownedSessions)
|
||||
{
|
||||
var before = ContinuationCounts(closed);
|
||||
if (before != (0, 0))
|
||||
{
|
||||
await LogAsync(evidence, new { kind = "closed-session-observation", cycle,
|
||||
activeSessions = rig.Server.CurrentInstance.SessionManager.GetSessions().Count,
|
||||
browsePoints = before.Browse, historyPoints = before.History, diagnosticReferenceHeld = true });
|
||||
await Task.Delay(250, ct); // Read-only failure investigation; never turns the failed guard into a pass.
|
||||
var after = ContinuationCounts(closed);
|
||||
await LogAsync(evidence, new { kind = "closed-session-after-fixed-observation", cycle,
|
||||
observationMilliseconds = 250, browsePoints = after.Browse, historyPoints = after.History });
|
||||
}
|
||||
before.ShouldBe((0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task RestartWithActiveResourcesAsync(BaselineRig rig, ApplicationConfiguration config,
|
||||
CancellationToken ct)
|
||||
{
|
||||
using var client = await ConnectAsync(rig, config, null, ct);
|
||||
using var subscription = new Subscription(client.DefaultSubscription) { PublishingInterval = 100 };
|
||||
client.AddSubscription(subscription);
|
||||
await Ledger.Call("subscription-create", () => subscription.CreateAsync(ct));
|
||||
subscription.AddItem(new MonitoredItem(subscription.DefaultItem)
|
||||
{
|
||||
StartNodeId = Raw(client, Scalar), AttributeId = Attributes.Value, SamplingInterval = 50,
|
||||
});
|
||||
await Ledger.Call("subscription-apply", () => subscription.ApplyChangesAsync(ct));
|
||||
PublicCounts(rig).ShouldBe((1, 1, 1));
|
||||
RequirePoint(await BrowseAsync(client, ct));
|
||||
RequirePoint((await PageAsync(client, default, false, ct)).ContinuationPoint);
|
||||
var oldInternal = rig.Server.CurrentInstance;
|
||||
var oldSessionManager = oldInternal.SessionManager;
|
||||
var oldSubscriptionManager = oldInternal.SubscriptionManager;
|
||||
var oldSessions = oldSessionManager.GetSessions().ToArray();
|
||||
var oldSubscriptions = oldSubscriptionManager.GetSubscriptions().ToArray();
|
||||
var oldValidator = rig.Validator;
|
||||
var oldStores = BaselineMetrics.CachedStoreReferences(oldValidator);
|
||||
await rig.RestartAsync(ct).WaitAsync(TimeSpan.FromSeconds(20), ct);
|
||||
oldSessionManager.GetSessions().ShouldBeEmpty();
|
||||
oldSubscriptionManager.GetSubscriptions().ShouldBeEmpty();
|
||||
oldSubscriptions.ShouldAllBe(item => item.MonitoredItemCount == 0);
|
||||
foreach (var session in oldSessions) ContinuationCounts(session).ShouldBe((0, 0));
|
||||
await LogAsync(CurrentEvidence, new { kind = "original-validator-after-stop", cachedStoreReferencesBefore = oldStores,
|
||||
cachedStoreReferencesAfter = BaselineMetrics.CachedStoreReferences(oldValidator),
|
||||
disposalFlagObservable = false, reason = "SDK1.5 has no CertificateManager disposal API or disposed flag" });
|
||||
PublicCounts(rig).ShouldBe((0, 0, 0));
|
||||
}
|
||||
|
||||
private static async Task<ClientSession> ConnectAsync(BaselineRig rig, ApplicationConfiguration config,
|
||||
int? proxyPort, CancellationToken ct)
|
||||
{
|
||||
var telemetry = DefaultTelemetry.Create(_ => { });
|
||||
var endpoint = await CoreClientUtils.SelectEndpointAsync(config, rig.EndpointUrl, true, telemetry, ct)
|
||||
?? throw new InvalidOperationException("No encrypted endpoint.");
|
||||
endpoint.SecurityMode.ShouldBe(MessageSecurityMode.SignAndEncrypt);
|
||||
if (proxyPort is { } port) endpoint.EndpointUrl = $"opc.tcp://127.0.0.1:{port}/OtOpcUa";
|
||||
return await Ledger.Call("session-create", () => new DefaultSessionFactory(telemetry).CreateAsync(config,
|
||||
new ConfiguredEndpoint(null, endpoint, EndpointConfiguration.Create(config)), false, false,
|
||||
"SDK2LifecycleSoak", 15_000, new UserIdentity("apikey", Encoding.UTF8.GetBytes(BaselineRig.KeyPassword)), default, ct));
|
||||
}
|
||||
|
||||
private static NodeId Raw(ClientSession session, string id)
|
||||
{
|
||||
var ns = session.NamespaceUris.GetIndex(V3NodeIds.RawNamespaceUri);
|
||||
ns.ShouldBeGreaterThan(0);
|
||||
return new NodeId(id, (ushort)ns);
|
||||
}
|
||||
|
||||
private static async Task<byte[]?> BrowseAsync(ClientSession session, CancellationToken ct) =>
|
||||
(await Ledger.Call("browse", () => session.BrowseAsync(null, null, 1, [new BrowseDescription
|
||||
{
|
||||
NodeId = Raw(session, BaselineRig.RawDeviceFolder), BrowseDirection = BrowseDirection.Forward,
|
||||
ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences, IncludeSubtypes = true,
|
||||
ResultMask = (uint)BrowseResultMask.All,
|
||||
}], ct))).Results[0].ContinuationPoint;
|
||||
|
||||
private static async Task<Opc.Ua.HistoryReadResult> PageAsync(ClientSession session, byte[]? point,
|
||||
bool release, CancellationToken ct) => (await Ledger.Call("history-read", () => session.HistoryReadAsync(null, new ExtensionObject(new ReadEventDetails
|
||||
{
|
||||
StartTime = Epoch.AddDays(-1), EndTime = Epoch.AddDays(1), NumValuesPerNode = 4,
|
||||
Filter = new EventFilter { SelectClauses = [new SimpleAttributeOperand(ObjectTypeIds.BaseEventType,
|
||||
new QualifiedName(BrowseNames.EventId))] },
|
||||
}), TimestampsToReturn.Both, release,
|
||||
[new HistoryReadValueId { NodeId = Raw(session, BaselineRig.RawDeviceFolder), ContinuationPoint = point }], ct))).Results[0];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Server;
|
||||
using ZB.MOM.WW.OtOpcUa.OpcUaServer;
|
||||
var types = new[] { typeof(Session), typeof(CertificateValidator), typeof(CertificateStoreIdentifier),
|
||||
typeof(Opc.Ua.Configuration.ApplicationInstance), typeof(OtOpcUaSdkServer), typeof(OpcUaApplicationHost), typeof(OtOpcUaNodeManager) };
|
||||
Console.WriteLine(JsonSerializer.Serialize(new
|
||||
{
|
||||
sdk = typeof(NodeId).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion,
|
||||
sdkFile = FileVersionInfo.GetVersionInfo(typeof(NodeId).Assembly.Location).FileVersion,
|
||||
modules = types.Select(t => new { type = t.FullName, assembly = t.Assembly.GetName().Name, mvid = t.Module.ModuleVersionId,
|
||||
interfaces = t.GetInterfaces().Select(x => x.FullName),
|
||||
fields = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
|
||||
.Select(f => new { name = f.Name, type = f.FieldType.FullName }),
|
||||
lifecycle = t.GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(m => m.Name.Contains("Dispose") || m.Name.Contains("Close") || m.Name.Contains("Stop"))
|
||||
.Select(m => m.ToString()) })
|
||||
}, new JsonSerializerOptions { WriteIndented = true }));
|
||||
@@ -0,0 +1,167 @@
|
||||
# Original close semantics and bounded retention controls
|
||||
|
||||
These are separately reviewed short controls against the unchanged 83ed880e
|
||||
publication, SDK 1.5.378.182. They do not replace the historical `(1 browse,
|
||||
6 history)` held-list port comparison, change an oracle, approve a memory
|
||||
baseline, or claim an unbounded leak. The operation deadline is 120 seconds;
|
||||
normal shutdown/private-directory cleanup has independent 20/20/5/5-second
|
||||
bounds, and the semantic runner has a final 180-second process bound.
|
||||
|
||||
## What was tested
|
||||
|
||||
A fresh fixture creates the original production host, encrypted opc.tcp client,
|
||||
one abandoned browse point and six event-history points. After normal client
|
||||
CloseAsync, the control verifies removal from the same running SessionManager.
|
||||
The old closed object still exposes `(1,6)` while explicitly held, as previously
|
||||
recorded; that observation is not treated as live protocol ownership.
|
||||
|
||||
A fresh valid session supplies the same open raw transport for all following
|
||||
requests. `ITransportChannel.SendRequestAsync` receives explicit request headers;
|
||||
no closed Session client guard or ClientBase header substitution can satisfy the
|
||||
negative checks. Each response must carry the exact sent RequestHandle.
|
||||
|
||||
| Request | Verified outcome |
|
||||
|---|---|
|
||||
| Fresh raw read before old-token request | Good service/item status |
|
||||
| Read with old authentication token | Correlated ServiceFault, BadSessionIdInvalid (2149908480) |
|
||||
| Fresh raw read after the fault, same transport | Good service/item status |
|
||||
| Fresh browse point creation | Good, nonempty point |
|
||||
| Old browse point from the fresh session | BadContinuationPointInvalid (2152333312) |
|
||||
| Fresh own browse point resume and release | Good; resume returns references and another point |
|
||||
| Fresh history page | Good; nonempty point; backend dispatched |
|
||||
| Old history point from the fresh session | BadContinuationPointInvalid; backend counter remains 7 to 7 |
|
||||
| Fresh own history point resume and release | Good; resume dispatches backend and returns another point |
|
||||
|
||||
All 11 raw responses were correlated. Generic transport failures/timeouts are not
|
||||
accepted as token denial. The exact .182 ClientBase independently preserves a
|
||||
nonnull AuthenticationToken in UpdateRequestHeader; the raw path bypasses that
|
||||
method entirely. The protected token getter is read through an exact-type-checked
|
||||
diagnostic accessor before close; tokens and payloads are never logged.
|
||||
|
||||
## Two distinct weak-reference boundaries
|
||||
|
||||
A NoInlining synchronous helper completes the async closed-session scope from a
|
||||
thread-pool context, returning only weak references and primitive/type metadata.
|
||||
No Session, point, response, token array or backend is returned as a strong
|
||||
reference. The first GC phase runs while the SAME original host/configuration
|
||||
remain alive. Their weak targets are explicitly expected alive. A second
|
||||
NoInlining fixture boundary completes normal host shutdown, private-directory
|
||||
cleanup and the complete async fixture scope before the outer GC phase.
|
||||
|
||||
Each phase records round 0 followed by exactly five forced blocking/compacting
|
||||
collections with fixed 50/100/250/500/1000 ms delays. Each GC/finalizer worker is
|
||||
bounded at five seconds. Observers use WeakReference.IsAlive rather than keeping
|
||||
TryGetTarget values across collections. A deliberately retained positive object
|
||||
must stay alive and an isolated collectible negative must disappear. No delay is
|
||||
extended to obtain collection.
|
||||
|
||||
In the ordinary run all **16 old client/server Session, browse point/Data,
|
||||
history-slot wrapper and history-value targets** disappeared during the first
|
||||
live-host forced collection. This separates the held-list implementation detail
|
||||
from those objects' actual reclamation in this fixture. After normal stop and
|
||||
all fixture boundaries, five server-graph targets remained across all five
|
||||
observations: server, node manager, backend, server validator and server cached
|
||||
store. Rig/client configuration/client application/client validator/client cached
|
||||
store targets disappeared. Positive and negative controls behaved correctly.
|
||||
PKI descriptors were zero in live-host and stopped-but-harness-held observations.
|
||||
The ordinary run preserves **exit 1**, with unsupported SDK2 manager-disposal
|
||||
observables still explicit.
|
||||
|
||||
For this single fixture, live-host final managed bytes were 94,550,728 and timers
|
||||
10; after-stop final managed bytes were 7,774,200 and timers 7. This drop does not
|
||||
establish that the earlier multi-restart managed/timer growth disappears, nor is
|
||||
it a usable performance denominator. Full process/framework parity remains open.
|
||||
|
||||
## Exact point-resource ownership
|
||||
|
||||
Runtime inspection identifies browse `ContinuationPoint.Data` as **Opc.Ua.NodeBrowser**
|
||||
from the original Opc.Ua.Types binary. Its exact Dispose(bool) implementation is
|
||||
empty. Its declared storage is managed IReference/List/HashSet/index state plus
|
||||
managed context/view properties, with no owned IDisposable field. The containing
|
||||
ContinuationPoint.Dispose delegates to Data, so omission of that call on this
|
||||
exact browser does not skip a direct cleanup action. This is not a statement
|
||||
about every INodeBrowser implementation or arbitrary driver browser resources.
|
||||
|
||||
The six history values are the original event HistoryContinuationState records;
|
||||
the original store saves each record directly in the SDK slot. They have scalar,
|
||||
string/date and managed paging/set state, no IDisposable contract or backend/store
|
||||
handle ownership. This event fixture has no pending raw end-bound resource. Both
|
||||
the SDK slot wrapper and its Value are weak-tracked; all collected while the
|
||||
original host remained running.
|
||||
|
||||
## Stop is distinct from terminal Dispose
|
||||
|
||||
Read-only inspection of the pinned original product/SDK establishes:
|
||||
|
||||
1. Archived OtOpcUa OpcUaApplicationHost.DisposeAsync awaits only
|
||||
ApplicationInstance.StopAsync.
|
||||
2. Original ApplicationInstance.StopAsync delegates to Server.StopAsync.
|
||||
3. ServerBase.StopAsync stops listeners/service hosts and invokes the standard
|
||||
stopping hook. StandardServer's stopping hook DOES shut down subscriptions,
|
||||
sessions and node managers and dispose internal server data.
|
||||
4. Terminal ServerBase.Dispose separately disposes its RequestQueue.
|
||||
RequestQueue owns a ServerBase reference and starts minimum worker tasks;
|
||||
those workers await SemaphoreSlim with a 15,000 ms timeout and cancellation
|
||||
token. At the minimum worker count they continue waiting. Queue.Dispose
|
||||
cancels that token, releases/disposes its signal and terminates its ownership.
|
||||
5. StandardServer.Dispose also unregisters its CertificateUpdate callback and
|
||||
disposes remaining terminal resources. The SDK2 ApplicationInstance.DisposeAsync
|
||||
explicitly performs StopAsync THEN Server.Dispose; the archived original host
|
||||
never calls that terminal API.
|
||||
|
||||
This is a source-backed original application lifecycle gap and a plausible
|
||||
retention path consistent with the observed server graph. It is not a measured
|
||||
heap dominator/root chain or attribution of any particular timer. No original
|
||||
production code was patched, and no extra disposal was added to ordinary mode.
|
||||
|
||||
A separate, gated `semantic-terminal-dispose` attribution mode first records the
|
||||
ordinary result. Only afterward does a NoInlining helper briefly acquire the
|
||||
known weak original server target, invoke its PUBLIC terminal Dispose once,
|
||||
drop that strong reference, then repeat the same fixed GC schedule. No private
|
||||
timer disposal, cache clearing, oracle adjustment or delay extension is used.
|
||||
The original ordinary exit is returned even if terminal disposal clears all
|
||||
observed targets. Its results are recorded separately in `semantic-evidence.json`.
|
||||
It cannot turn the archived original behavior or failed RSS gates into a pass.
|
||||
|
||||
That separately approved run reproduced the same normal-stop five survivors and
|
||||
returned **exit 1**. After the single public Dispose, all five disappeared at the
|
||||
first next forced collection and remained absent through round 5. At the final
|
||||
observations, timers changed from 7 to 2 and managed bytes from 7,692,144 to
|
||||
4,513,760. Positive/negative GC controls remained valid. This controlled intervention
|
||||
supports missing terminal server disposal as the ownership distinction; it does
|
||||
not identify which queue task, timer or event subscription was the actual GC root.
|
||||
It does not establish an unbounded leak from one fixture, eliminate unrelated
|
||||
native retention, or make the original process a performance denominator.
|
||||
|
||||
Both immutable adapter builds completed with zero warnings/errors, using the
|
||||
same 208 reference hashes. The ordinary run uses build 3 and the separate
|
||||
counterfactual uses build 4; their exact source, binary, build-log and raw-result
|
||||
hashes are retained in `semantic-evidence.json`. Raw evidence remains in private
|
||||
external directories. No additional control was run to adjust these findings.
|
||||
|
||||
The exact original test seams are public virtual
|
||||
`ServerBase.StopAsync(CancellationToken)`, protected virtual `Dispose(bool)`,
|
||||
`OnServerStarting(ApplicationConfiguration)`,
|
||||
`StartApplicationAsync(ApplicationConfiguration, CancellationToken)`, and
|
||||
`OnServerStoppingAsync(CancellationToken)`. Public `Dispose()` and `StartAsync`
|
||||
are nonvirtual. StandardServer overrides those protected lifecycle hooks and
|
||||
also exposes protected virtual `OnServerStarted(IServerInternal)`. A separate
|
||||
reviewed application repair can test stop failure/partial startup using these
|
||||
supported seams; this diagnostic does not repair or rebuild the archived product.
|
||||
|
||||
## Smallest future matched-host path (proposal only)
|
||||
|
||||
A standalone SDK2 driver mirroring this original standalone driver is the smallest
|
||||
path: same .NET runtime, assertions, telemetry/logging configuration, PKI/security,
|
||||
operation recipe/counts, raw request/correlation probes, NoInlining boundaries,
|
||||
weak controls, sampling schedule and module provenance. Keep SDK-specific value
|
||||
representations and read-only ownership observers explicit. Both xUnit hosts
|
||||
would require creating/porting a larger original test-host graph and introduce
|
||||
more loader/framework differences. Neither path has been implemented or approved
|
||||
as an acceptance protocol. The current candidate xUnit control is semantic
|
||||
corroboration, not process-memory comparability.
|
||||
|
||||
Any correction of archived app ownership must be a separate reviewed change and
|
||||
new provenance; these controls do not substitute a repaired baseline or use its
|
||||
post-counterfactual memory as a denominator. Long original runs, reset baselines,
|
||||
RSS caps and acceptance remain outside this work.
|
||||
@@ -0,0 +1,214 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Configuration;
|
||||
using Opc.Ua.Server;
|
||||
using Shouldly;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using CoreHistoryReadResult = ZB.MOM.WW.OtOpcUa.Core.Abstractions.HistoryReadResult;
|
||||
return args.Length == 3 && (args[2] == "--semantic-retention-control" || args[2] == "--semantic-terminal-dispose-control")
|
||||
? await Canonical.SemanticRetentionAsync(args) : await Canonical.RunAsync(args);
|
||||
internal static partial class Canonical
|
||||
{
|
||||
private const string Scalar = BaselineRig.RawDeviceFolder + "/SoakScalar";
|
||||
private const string ArrayTag = BaselineRig.RawDeviceFolder + "/SoakArray";
|
||||
private const string UnsScalar = "SoakEquipment/Scalar";
|
||||
private static readonly DateTime Epoch = new(2026,10,1,0,0,0,DateTimeKind.Utc);
|
||||
internal static readonly OperationLedger Ledger = new();
|
||||
private static StreamWriter CurrentEvidence = null!;
|
||||
internal static async Task<int> RunAsync(string[] args)
|
||||
{
|
||||
var root=Path.GetFullPath(args[0]); var cycles=int.Parse(args[1]);
|
||||
var isolatedRestart = args.Length == 3 && args[2] == "--active-restart-control";
|
||||
if (args.Length > 3 || (args.Length == 3 && !isolatedRestart)) throw new InvalidOperationException("Unknown short-control mode");
|
||||
if(cycles<1 || cycles>4) throw new InvalidOperationException("Only a 1..4-cycle short diagnostic smoke is authorized");
|
||||
if(Directory.Exists(root)) throw new InvalidOperationException("Use a fresh private artifact directory");
|
||||
Directory.CreateDirectory(root);
|
||||
if(!OperatingSystem.IsWindows()) File.SetUnixFileMode(root,UnixFileMode.UserRead|UnixFileMode.UserWrite|UnixFileMode.UserExecute);
|
||||
await using var output=new StreamWriter(new FileStream(Path.Combine(root,"resources.jsonl"),FileMode.CreateNew,FileAccess.Write,FileShare.Read));
|
||||
CurrentEvidence=output;
|
||||
using var deadline=new CancellationTokenSource(TimeSpan.FromSeconds(120)); var ct=deadline.Token;
|
||||
var sdk=typeof(NodeId).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
||||
if(sdk is null || !sdk.StartsWith("1.5.378.182+",StringComparison.Ordinal)) throw new InvalidOperationException("The immutable .182 SDK is required");
|
||||
var clientRoot=Path.Combine(root,"private-fixture","client-pki");
|
||||
var telemetry=DefaultTelemetry.Create(_=>{});
|
||||
var config=new ApplicationConfiguration(telemetry)
|
||||
{
|
||||
ApplicationName="SDK2LifecycleSoak",ApplicationUri=$"urn:SDK2LifecycleSoak:{Guid.NewGuid():N}",ApplicationType=ApplicationType.Client,
|
||||
SecurityConfiguration=new SecurityConfiguration
|
||||
{
|
||||
ApplicationCertificate=new CertificateIdentifier{StoreType=CertificateStoreType.Directory,StorePath=Path.Combine(clientRoot,"own"),SubjectName="CN=SDK2LifecycleSoak"},
|
||||
TrustedIssuerCertificates=new CertificateTrustList{StoreType=CertificateStoreType.Directory,StorePath=Path.Combine(clientRoot,"issuer")},
|
||||
TrustedPeerCertificates=new CertificateTrustList{StoreType=CertificateStoreType.Directory,StorePath=Path.Combine(clientRoot,"trusted")},
|
||||
RejectedCertificateStore=new CertificateTrustList{StoreType=CertificateStoreType.Directory,StorePath=Path.Combine(clientRoot,"rejected")},AutoAcceptUntrustedCertificates=true
|
||||
},ClientConfiguration=new ClientConfiguration{DefaultSessionTimeout=15000},TransportQuotas=new TransportQuotas{OperationTimeout=5000}
|
||||
};
|
||||
var application=new ApplicationInstance(telemetry){ApplicationConfiguration=config};
|
||||
await config.ValidateAsync(ApplicationType.Client,ct);await application.CheckApplicationInstanceCertificatesAsync(true,null,ct);
|
||||
await using var rig=new BaselineRig(Path.Combine(root,"private-fixture"));
|
||||
await rig.BootAsync(ct);Configure(rig);
|
||||
var watch=Stopwatch.StartNew();var completed=0;var restarts=0;
|
||||
await LogAsync(output,new {kind="start",sdk,productRebuilt=false,sourcePublication="83ed880e",requestedCycles=cycles,
|
||||
processId=Environment.ProcessId,acceptance=false,scope="canonical operations short smoke; ownership differences remain explicit",
|
||||
unsupportedObservables=new[]{"SDK2 CertificateManager disposed flag","SDK2 manager-owned cache-clear proof","ApplicationInstance.DisposeAsync"}});
|
||||
try
|
||||
{
|
||||
if (isolatedRestart)
|
||||
{
|
||||
if (cycles != 1) throw new InvalidOperationException("The isolated restart control performs exactly one restart");
|
||||
await RestartWithActiveResourcesAsync(rig,config,ct);Configure(rig);restarts++;
|
||||
await LogAsync(output,new {kind="isolated-active-restart-completed",normalCycles=0,restarts,
|
||||
acceptance=false,ownershipParity=false,ledger=Ledger.Snapshot()});
|
||||
return 2;
|
||||
}
|
||||
while(completed<cycles)
|
||||
{
|
||||
await ExerciseAsync(rig,config,completed+1,output,ct);completed++;
|
||||
await EventuallyAsync(()=>PublicCounts(rig)==(0,0,0),ct);
|
||||
var counts=PublicCounts(rig);var fd=await BaselineMetrics.DescriptorsAsync(ct,clientRoot,rig.PkiRoot);
|
||||
fd.Pki.ShouldBe(0);
|
||||
foreach(var name in new[]{"LiveItemCount","LiveEventItemCount","MonitoredNodeCount"}) BaselineMetrics.Counter(rig.Nm,name).ShouldBe(0);
|
||||
using var process=Process.GetCurrentProcess();process.Refresh();
|
||||
await LogAsync(output,new {kind="quiescent",cycle=completed,seconds=watch.Elapsed.TotalSeconds,
|
||||
sessions=counts.Sessions,subscriptions=counts.Subscriptions,items=counts.Items,
|
||||
pkiDescriptors=fd.Pki,descriptors=fd.Total,managedBytes=GC.GetTotalMemory(true),rssBytes=process.WorkingSet64,
|
||||
serverCachedStoreReferences=BaselineMetrics.CachedStoreReferences(rig.Validator),
|
||||
clientCachedStoreReferences=BaselineMetrics.CachedStoreReferences(config.CertificateValidator),jit=System.Runtime.JitInfo.GetCompiledMethodCount(),timers=Timer.ActiveCount});
|
||||
if(completed%4==0){await RestartWithActiveResourcesAsync(rig,config,ct);Configure(rig);restarts++;}
|
||||
}
|
||||
await LogAsync(output,new {kind="operations-completed",cycles=completed,restarts,seconds=watch.Elapsed.TotalSeconds,
|
||||
acceptance=false,ownershipParity=false,reason="Original SDK has no equivalent manager-disposal observable; review required",ledger=Ledger.Snapshot()});
|
||||
return 2;
|
||||
}
|
||||
catch(Exception error)
|
||||
{
|
||||
await LogAsync(output,new {kind="failed",cycles=completed,restarts,exceptionType=error.GetType().FullName,
|
||||
uaStatus=error is ServiceResultException ua ? ua.StatusCode.ToString() : null,ledger=Ledger.Snapshot(),acceptance=false});
|
||||
Console.Error.WriteLine(error);return 1;
|
||||
}
|
||||
}
|
||||
private static int ScalarValue(Variant value)=>value.Value is int result?result:throw new InvalidOperationException("Expected scalar Int32 CLR shape");
|
||||
private static void RequirePoint(byte[]? point)=>(point is {Length:>0}).ShouldBeTrue();
|
||||
private static (int Browse,int History) ContinuationCounts(ISession session)=>BaselineMetrics.Points(session);
|
||||
private static (int Sessions,int Subscriptions,int Items) PublicCounts(BaselineRig rig)
|
||||
{var subscriptions=rig.Server.CurrentInstance.SubscriptionManager.GetSubscriptions();return(rig.Server.CurrentInstance.SessionManager.GetSessions().Count,subscriptions.Count,subscriptions.Sum(s=>s.MonitoredItemCount));}
|
||||
private static async Task EventuallyAsync(Func<bool> condition,CancellationToken ct)
|
||||
{var elapsed=Stopwatch.StartNew();while(!condition()&&elapsed.Elapsed<TimeSpan.FromSeconds(10))await Task.Delay(25,ct);condition().ShouldBeTrue();}
|
||||
private static async Task LogAsync(StreamWriter output,object value){await output.WriteLineAsync(JsonSerializer.Serialize(value));await output.FlushAsync();}
|
||||
private sealed class AcceptWrites:IOpcUaNodeWriteGateway
|
||||
{public Task<NodeWriteOutcome> WriteAsync(string id,object? value,AddressSpaceRealm realm,CancellationToken ct)=>Task.FromResult(new NodeWriteOutcome(true,null));}
|
||||
internal sealed class SqlLikeEventSource(List<HistoricalEvent> events) : IHistorianDataSource
|
||||
{
|
||||
private int _eventReads;
|
||||
internal int EventReads => Volatile.Read(ref _eventReads);
|
||||
private readonly List<HistoricalEvent> _all =
|
||||
events.OrderBy(e => e.EventTimeUtc).ThenBy(e => e.EventId, StringComparer.Ordinal).ToList();
|
||||
|
||||
public async Task<HistoricalEventsResult> ReadEventsAsync(
|
||||
string? sourceName, DateTime startUtc, DateTime endUtc, int maxEvents, CancellationToken ct)
|
||||
{
|
||||
Interlocked.Increment(ref _eventReads);
|
||||
await Task.Delay(1, ct).ConfigureAwait(false); // complete on a pool thread, like a real backend
|
||||
var window = _all.Where(e => e.EventTimeUtc >= startUtc && e.EventTimeUtc < endUtc).ToList();
|
||||
var cap = maxEvents <= 0 ? 1000 : maxEvents;
|
||||
return new HistoricalEventsResult(window.Take(cap).ToList(), window.Count > cap ? new byte[] { 1 } : null);
|
||||
}
|
||||
|
||||
public bool SupportsReverseEvents => true;
|
||||
|
||||
/// <summary>The #675 overload: reverse is <c>(lo, hi]</c> by (time, id) descending.</summary>
|
||||
public async Task<HistoricalEventsResult> ReadEventsAsync(
|
||||
string? sourceName, DateTime startUtc, DateTime endUtc, int maxEvents, bool reverse, CancellationToken ct)
|
||||
{
|
||||
if (!reverse) return await ReadEventsAsync(sourceName, startUtc, endUtc, maxEvents, ct).ConfigureAwait(false);
|
||||
await Task.Delay(1, ct).ConfigureAwait(false);
|
||||
var (lo, hi) = startUtc <= endUtc ? (startUtc, endUtc) : (endUtc, startUtc);
|
||||
var window = _all.Where(e => e.EventTimeUtc > lo && e.EventTimeUtc <= hi).Reverse().ToList();
|
||||
var cap = maxEvents <= 0 ? 1000 : maxEvents;
|
||||
return new HistoricalEventsResult(window.Take(cap).ToList(), window.Count > cap ? new byte[] { 1 } : null);
|
||||
}
|
||||
|
||||
public Task<CoreHistoryReadResult> ReadRawAsync(string r, DateTime s, DateTime e, uint m, CancellationToken ct) =>
|
||||
NullHistorianDataSource.Instance.ReadRawAsync(r, s, e, m, ct);
|
||||
public Task<CoreHistoryReadResult> ReadProcessedAsync(
|
||||
string r, DateTime s, DateTime e, TimeSpan i, HistoryAggregateType a, CancellationToken ct) =>
|
||||
NullHistorianDataSource.Instance.ReadProcessedAsync(r, s, e, i, a, ct);
|
||||
public Task<CoreHistoryReadResult> ReadAtTimeAsync(string r, IReadOnlyList<DateTime> t, CancellationToken ct) =>
|
||||
NullHistorianDataSource.Instance.ReadAtTimeAsync(r, t, ct);
|
||||
public HistorianHealthSnapshot GetHealthSnapshot() => NullHistorianDataSource.Instance.GetHealthSnapshot();
|
||||
public void Dispose() { }
|
||||
}
|
||||
/// <summary>Task-owned TCP relay. Pause blocks forwarding in both directions without closing either socket.</summary>
|
||||
private sealed class PauseProxy : IAsyncDisposable
|
||||
{
|
||||
private readonly TcpListener _listener = new(IPAddress.Loopback, 0);
|
||||
private readonly CancellationTokenSource _stop = new();
|
||||
private readonly ConcurrentBag<TcpClient> _sockets = [];
|
||||
private readonly ConcurrentBag<Task> _pumps = [];
|
||||
private readonly Task _accept;
|
||||
private TaskCompletionSource? _pause;
|
||||
internal int Port => ((IPEndPoint)_listener.LocalEndpoint).Port;
|
||||
internal PauseProxy(int targetPort)
|
||||
{
|
||||
_listener.Start();
|
||||
_accept = AcceptAsync(targetPort);
|
||||
}
|
||||
internal void Pause() => Volatile.Write(ref _pause, new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously));
|
||||
internal void Resume() => Interlocked.Exchange(ref _pause, null)?.TrySetResult();
|
||||
private async Task AcceptAsync(int port)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!_stop.IsCancellationRequested)
|
||||
{
|
||||
var incoming = await _listener.AcceptTcpClientAsync(_stop.Token);
|
||||
_sockets.Add(incoming);
|
||||
var outgoing = new TcpClient(); _sockets.Add(outgoing);
|
||||
await outgoing.ConnectAsync(IPAddress.Loopback, port, _stop.Token);
|
||||
_pumps.Add(PumpAsync(incoming.GetStream(), outgoing.GetStream()));
|
||||
_pumps.Add(PumpAsync(outgoing.GetStream(), incoming.GetStream()));
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (_stop.IsCancellationRequested) { }
|
||||
}
|
||||
private async Task PumpAsync(NetworkStream source, NetworkStream destination)
|
||||
{
|
||||
var buffer = new byte[16 * 1024];
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var length = await source.ReadAsync(buffer, _stop.Token);
|
||||
if (length == 0) return;
|
||||
if (Volatile.Read(ref _pause) is { } paused) await paused.Task.WaitAsync(_stop.Token);
|
||||
await destination.WriteAsync(buffer.AsMemory(0, length), _stop.Token);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (_stop.IsCancellationRequested) { }
|
||||
catch (IOException) when (_stop.IsCancellationRequested) { }
|
||||
}
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
Resume(); _stop.Cancel(); _listener.Stop();
|
||||
foreach (var socket in _sockets) socket.Dispose();
|
||||
try { await _accept; await Task.WhenAll(_pumps); }
|
||||
finally { _stop.Dispose(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class OperationLedger
|
||||
{
|
||||
private readonly ConcurrentDictionary<string,long> _counts=new();
|
||||
internal void Record(string name)=>_counts.AddOrUpdate(name,1,(_,count)=>count+1);
|
||||
internal async Task<T> Call<T>(string name,Func<Task<T>> operation)
|
||||
{Record(name+".attempted");try{var value=await operation();Record(name+".returned");return value;}catch{Record(name+".faulted");throw;}}
|
||||
internal async Task Call(string name,Func<Task> operation)
|
||||
{Record(name+".attempted");try{await operation();Record(name+".returned");}catch{Record(name+".faulted");throw;}}
|
||||
internal IReadOnlyDictionary<string,long> Snapshot()=>new SortedDictionary<string,long>(_counts);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
# Original 1.5.378.182 canonical adapter — partial diagnostic proof
|
||||
|
||||
This folder compiles a standalone diagnostic against the **unchanged original
|
||||
83ed880e published host/client DLLs**. It does not rebuild or patch product code,
|
||||
restore a substitute SDK, edit a baseline oracle, install a replacement session
|
||||
factory, or approve a performance denominator. Only short 1–4-cycle smoke runs are
|
||||
implemented. No original warmed/full/two-hour run has been performed.
|
||||
|
||||
The immutable SDK reports `1.5.378.182+6cca32dd6d79c8b57d2e00e4d2071b991d2cf71b`.
|
||||
The source workload was ported from `Sdk2LifecycleSoakTests` at `75057d8c`.
|
||||
`evidence.json` records source/input hashes, exact SDK layout and short-run results.
|
||||
Raw logs and task-owned PKI stay outside the repository; credentials are not logged.
|
||||
|
||||
## Supported operation coverage
|
||||
|
||||
`CanonicalOperations.cs` preserves the candidate's operation ordering, status
|
||||
assertions, timeouts, model, grants, event data, page size and typed-array shape.
|
||||
The representation changes are explicit: SDK2 Variant/ArrayOf helpers become the
|
||||
original Variant constructor and CLR values; ByteString becomes byte[]; and
|
||||
QualifiedName.From becomes its original constructor. There are no collection
|
||||
shims or private-state mutations.
|
||||
|
||||
| Canonical operation per normal cycle | Original short control observation |
|
||||
|---|---|
|
||||
| Two encrypted opc.tcp / SignAndEncrypt sessions, Raw/UNS URI binding | Completed |
|
||||
| One subscription, three data items, notifications | Completed |
|
||||
| One Write service with Int32/Int16[]/UNS Int32 values, three readbacks | Completed with original Good/type assertions |
|
||||
| Two browse pages, one explicit release and one abandonment | Completed; active browse count 1 |
|
||||
| History first page, foreign-session denial, resume, explicit release, six abandoned points | All 10 HistoryRead services returned; active history count 6 |
|
||||
| Writable off/on, array removal/recreation/publication | Completed |
|
||||
| Key revocation, denied read, authority restoration | Completed |
|
||||
| Paused TCP read, one-second cancellation, resume and successful recovery read | Completed; original UA status 2156134400 on the canceled read |
|
||||
| Subscription deletion/removal and address-space rebuild/reconfiguration | Completed |
|
||||
| Both normal clients CloseAsync | Both returned; version-specific post-close list comparison differs |
|
||||
| Every-fourth-cycle restart with an active session/subscription/item, browse/history points | Separate isolated one-restart control completed |
|
||||
|
||||
The failed normal control attempted one whole wire body. Its ledger records
|
||||
2 connects, 1 subscription create/apply/delete/remove, 1 Write service (3 values),
|
||||
5 ReadValue attempts (4 returned, 1 expected canceled fault), 1 revoked Read
|
||||
service, 2 Browse calls, 1 BrowseNext release, 10 HistoryRead calls, and 2 CloseAsync
|
||||
returns. Surgery, revocation, stalled-network recovery and rebuild all occurred
|
||||
before the post-close comparison. No normal cycle is reported as ownership-complete.
|
||||
|
||||
The independent active-restart control does **not** stand in for a successful
|
||||
four-cycle sequence: it starts one client/subscription/item, abandons browse and
|
||||
history points, stops/reboots the same URI/port/PKI server, and checks the captured
|
||||
old managers/sessions/subscription items and new manager counts. Those checks
|
||||
completed. It returned **exit 2**, explicitly unsupported ownership parity,
|
||||
not a green acceptance result.
|
||||
|
||||
## Close and ownership differences — unresolved
|
||||
|
||||
The first normal smoke returned **exit 1** at the copied zero-closed-list
|
||||
comparison: a retained server Session still had **1 browse / 6 history** points.
|
||||
The server SessionManager already had **zero active sessions**. A separate fixed
|
||||
250 ms read-only observation saw the same lists; the original failed comparison
|
||||
was still enforced. The first failure artifacts were left untouched.
|
||||
|
||||
Exact immutable-binary inspection explains the internal difference:
|
||||
|
||||
- SessionManager.CloseSession removes the session and calls Session.Close.
|
||||
- Session.Close only deletes its diagnostics; it does not clear continuation lists.
|
||||
- Session.Dispose clears both lists and disposes their retained values.
|
||||
- SessionManager.Shutdown/Dispose disposes sessions still in its active dictionary.
|
||||
|
||||
These are **version-specific diagnostics**, not proof that the original violates
|
||||
the wire close contract, has an unbounded leak, or retains the same objects once
|
||||
all strong diagnostic references are dropped. The adapter deliberately holds the
|
||||
old Session references while checking these fields. The SDK2 zero-list invariant
|
||||
cannot silently become a shared cross-version acceptance contract.
|
||||
|
||||
The restart control follows the different active-session shutdown path: captured
|
||||
old session/point lists and subscription items became empty. The original
|
||||
validator's cached store **references** remained 1 before and after stop. A cached
|
||||
store reference is not a count of open handles or proof of a leak.
|
||||
|
||||
Original ApplicationInstance and CertificateValidator expose no Dispose API, and
|
||||
there is no SDK2 CertificateManager/m_disposed/core-store-clear equivalent. The
|
||||
adapter reports those observables as **unsupported**, never as zero. Actual PKI
|
||||
file descriptors can be measured with the same bounded/canonical-path observer
|
||||
as the candidate; those observations cannot invent the missing manager ownership
|
||||
proof. No manual Session.Dispose, store replacement or cache clearing was added
|
||||
to make an assertion pass.
|
||||
|
||||
The historical partial proof above did not test token/continuation rejection or
|
||||
collection after dropping its diagnostic references. Later, separately reviewed
|
||||
short controls established those semantics and observed the remaining server
|
||||
graph retention after normal shutdown. Their direct point-resource inspection,
|
||||
two weak-reference phases and separate terminal-disposal attribution are recorded
|
||||
in [OWNERSHIP.md](OWNERSHIP.md) and `semantic-evidence.json`. Both ordinary failure
|
||||
results remain intact. Certificate-store/native ownership gaps remain explicit;
|
||||
these findings do not approve a common lifecycle/acceptance protocol.
|
||||
|
||||
The standalone adapter also lacks the candidate's xUnit/VSTest host; equal process
|
||||
memory overhead is not proven. Earlier original-subset managed/timer growth is an
|
||||
observation, not an approved RSS denominator or a proven SDK1 defect. Resource and
|
||||
operation parity require Astra/root review before any long run; no cap, baseline
|
||||
or acceptance protocol changes are included here.
|
||||
|
||||
## Build and short controls
|
||||
|
||||
`build.py` selects original DLLs from the provided immutable directories (host
|
||||
wins duplicate names), plus Shouldly 4.3.0/assertion dependencies only. It emits
|
||||
SHA-256 provenance and a new external project. No production project/package file
|
||||
is touched. The inventory lists exact types, fields, lifecycle APIs and MVIDs
|
||||
without creating product instances.
|
||||
|
||||
```bash
|
||||
python3 scripts/validation/opcua-lifecycle-memory/baseline-canonical/build.py \
|
||||
--host "$originalHost" --client "$originalClient" --assertions "$assertionDlls" \
|
||||
--output /tmp/original-canonical-build
|
||||
python3 scripts/validation/opcua-lifecycle-memory/baseline-canonical/run_smoke.py \
|
||||
--adapter /tmp/original-canonical-build/bin/Release/net10.0/adapter.dll \
|
||||
--output /tmp/original-canonical-close --mode normal
|
||||
python3 scripts/validation/opcua-lifecycle-memory/baseline-canonical/run_smoke.py \
|
||||
--adapter /tmp/original-canonical-build/bin/Release/net10.0/adapter.dll \
|
||||
--output /tmp/original-canonical-active-restart --mode active-restart
|
||||
```
|
||||
|
||||
Add `--inventory` to build the read-only metadata inventory instead. The runner
|
||||
uses a fresh private directory and a temporary environment credential with the
|
||||
same prefix/byte length as the canonical fixture; it preserves the original child
|
||||
exit. Exit 1 is an observed control failure; exit 2 is operations completed with
|
||||
unsupported ownership parity. Neither is a passing baseline. Normal/restart modes
|
||||
have a hard 120-second bound and no long-run option.
|
||||
|
||||
The later semantic modes are `--mode semantic-retention` and the separately gated
|
||||
`--mode semantic-terminal-dispose` attribution control. They use the 120-second
|
||||
operation deadline plus bounded independent cleanup and a 180-second outer runner
|
||||
limit. Neither changes normal/active-restart results or authorizes a long run.
|
||||
@@ -0,0 +1,385 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Text.Json;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Client;
|
||||
using Opc.Ua.Configuration;
|
||||
using Shouldly;
|
||||
using ServerSession = Opc.Ua.Server.ISession;
|
||||
using ClientSession = Opc.Ua.Client.ISession;
|
||||
|
||||
internal static partial class Canonical
|
||||
{
|
||||
internal static async Task<int> SemanticRetentionAsync(string[] args)
|
||||
{
|
||||
if (args.Length != 3 || args[1] != "1") throw new InvalidOperationException("One bounded semantic control only");
|
||||
var root = Path.GetFullPath(args[0]);
|
||||
var counterfactual = args[2] == "--semantic-terminal-dispose-control";
|
||||
if (Directory.Exists(root)) throw new InvalidOperationException("Use a fresh private directory");
|
||||
Directory.CreateDirectory(root);
|
||||
if (!OperatingSystem.IsWindows()) File.SetUnixFileMode(root, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
|
||||
await using var output = new StreamWriter(new FileStream(Path.Combine(root, "resources.jsonl"), FileMode.CreateNew, FileAccess.Write, FileShare.Read));
|
||||
CurrentEvidence = output;
|
||||
using var deadline = new CancellationTokenSource(TimeSpan.FromSeconds(120));
|
||||
var retainedPositive = new object();
|
||||
var positive = Witness("positive-retained", retainedPositive);
|
||||
var negative = EphemeralNegative();
|
||||
try
|
||||
{
|
||||
await LogAsync(output, new { kind = "semantic-process", phase = "before-fixture", process = ProcessCounters() });
|
||||
// Task.Run has no captured synchronization context. Its closure contains only path/writer/token.
|
||||
var result = await Task.Run(() => FixtureBoundary(root, output, positive, negative, deadline.Token), deadline.Token);
|
||||
var phase2 = await ObserveAsync("after-stop", result.All, positive, negative, output, deadline.Token);
|
||||
var liveOld = result.LiveHostSurvivors;
|
||||
var stopped = phase2.Where(x => x.alive && x.label != "positive-retained").Select(x => x.label).ToArray();
|
||||
await LogAsync(output, new { kind = "semantic-retention-result", semanticsComplete = true,
|
||||
liveHostOldSurvivors = liveOld, afterStopSurvivors = stopped, acceptance = false, ownershipParity = false,
|
||||
unsupportedObservables = new[] { "SDK2 CertificateManager disposed flag", "SDK2 manager-owned cache-clear proof", "ApplicationInstance.DisposeAsync" } });
|
||||
var ordinaryExitCode = liveOld.Length == 0 && stopped.Length == 0 ? 2 : 1;
|
||||
if (counterfactual)
|
||||
{
|
||||
// Separate attribution only, AFTER preserving the ordinary stop result above.
|
||||
var server = result.All.Single(x => x.Label == "fixture-server");
|
||||
var invoked = await Task.Run(() => TerminalDisposeBoundary(server), deadline.Token).WaitAsync(TimeSpan.FromSeconds(20), deadline.Token);
|
||||
await LogAsync(output, new { kind = "counterfactual-terminal-dispose", invoked, ordinaryExitCode,
|
||||
publicMethod = "ServerBase.Dispose", acceptance = false });
|
||||
var terminal = await ObserveAsync("after-terminal-dispose", result.All, positive, negative, output, deadline.Token);
|
||||
await LogAsync(output, new { kind = "counterfactual-attribution-result", ordinaryExitCode,
|
||||
survivors = terminal.Where(x => x.alive && x.label != "positive-retained").Select(x => x.label).ToArray(), acceptance = false });
|
||||
}
|
||||
GC.KeepAlive(retainedPositive);
|
||||
// Even collection of every observed target does not invent the unsupported ownership proof.
|
||||
return ordinaryExitCode;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
await LogAsync(output, new { kind = "semantic-control-failed", exceptionType = error.GetType().FullName, acceptance = false });
|
||||
Console.Error.WriteLine(error);
|
||||
GC.KeepAlive(retainedPositive);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static FixtureWitness FixtureBoundary(string root, StreamWriter output, WeakWitness positive, WeakWitness negative, CancellationToken ct) =>
|
||||
FixtureScopeAsync(root, output, positive, negative, ct).GetAwaiter().GetResult();
|
||||
|
||||
private static async Task<FixtureWitness> FixtureScopeAsync(string root, StreamWriter output, WeakWitness positive, WeakWitness negative, CancellationToken ct)
|
||||
{
|
||||
var sdk = typeof(NodeId).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
||||
if (sdk is null || !sdk.StartsWith("1.5.378.182+", StringComparison.Ordinal)) throw new InvalidOperationException("Immutable .182 SDK required");
|
||||
var clientRoot = Path.Combine(root, "private-fixture", "client-pki");
|
||||
BaselineRig? rig = null;
|
||||
ApplicationConfiguration? config = null;
|
||||
ApplicationInstance? application = null;
|
||||
ExceptionDispatchInfo? failure = null;
|
||||
FixtureWitness? result = null;
|
||||
var booted = false;
|
||||
try
|
||||
{
|
||||
var telemetry = DefaultTelemetry.Create(_ => { });
|
||||
config = new ApplicationConfiguration(telemetry)
|
||||
{
|
||||
ApplicationName = "SDK2LifecycleSoak", ApplicationUri = $"urn:SDK2LifecycleSoak:{Guid.NewGuid():N}", ApplicationType = ApplicationType.Client,
|
||||
SecurityConfiguration = new SecurityConfiguration
|
||||
{
|
||||
ApplicationCertificate = new CertificateIdentifier { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(clientRoot, "own"), SubjectName = "CN=SDK2LifecycleSoak" },
|
||||
TrustedIssuerCertificates = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(clientRoot, "issuer") },
|
||||
TrustedPeerCertificates = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(clientRoot, "trusted") },
|
||||
RejectedCertificateStore = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(clientRoot, "rejected") }, AutoAcceptUntrustedCertificates = true
|
||||
},
|
||||
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 15000 }, TransportQuotas = new TransportQuotas { OperationTimeout = 5000 }
|
||||
};
|
||||
application = new ApplicationInstance(telemetry) { ApplicationConfiguration = config };
|
||||
await config.ValidateAsync(ApplicationType.Client, ct);
|
||||
await application.CheckApplicationInstanceCertificatesAsync(true, null, ct);
|
||||
rig = new BaselineRig(Path.Combine(root, "private-fixture"));
|
||||
await rig.BootAsync(ct);
|
||||
Configure(rig);
|
||||
booted = true;
|
||||
await LogAsync(output, new { kind = "semantic-start", sdk, acceptance = false, productRebuilt = false,
|
||||
maximumSeconds = 120, gcDelaysMilliseconds = new[] { 50, 100, 250, 500, 1000 }, gcRoundTimeoutSeconds = 5 });
|
||||
var fixture = new List<WeakWitness>
|
||||
{
|
||||
Witness("fixture-rig", rig), Witness("fixture-server", rig.Server), Witness("fixture-node-manager", rig.Nm),
|
||||
Witness("fixture-backend", rig.Nm.HistorianDataSource), Witness("fixture-client-config", config),
|
||||
Witness("fixture-client-application", application), Witness("fixture-server-validator", rig.Validator),
|
||||
Witness("fixture-client-validator", config.CertificateValidator)
|
||||
};
|
||||
// This synchronous NoInlining boundary drops the completed async state machine before phase1 GC.
|
||||
var closed = ClosedBoundary(rig, config, output, ct);
|
||||
PublicCounts(rig).ShouldBe((0, 0, 0));
|
||||
// Validation may populate stores on first connection; inspect after those operations.
|
||||
CaptureStores(rig.Validator, "fixture-server-store", fixture);
|
||||
CaptureStores(config.CertificateValidator, "fixture-client-store", fixture);
|
||||
var all = closed.Concat(fixture).ToArray();
|
||||
await WriteShapes(all, output);
|
||||
var phase1 = await ObserveAsync("live-host", all, positive, negative, output, ct);
|
||||
var survivors = phase1.Where(x => x.alive && x.label.StartsWith("old-", StringComparison.Ordinal)).Select(x => x.label).ToArray();
|
||||
foreach (var live in phase1.Where(x => x.label.StartsWith("fixture-", StringComparison.Ordinal)))
|
||||
live.alive.ShouldBeTrue("the host/configuration are intentionally still owned in phase1");
|
||||
var descriptors = await BaselineMetrics.DescriptorsAsync(ct, clientRoot, rig.PkiRoot);
|
||||
descriptors.Pki.ShouldBe(0);
|
||||
await LogAsync(output, new { kind = "semantic-owned-resources", phase = "live-host", pkiDescriptors = descriptors.Pki,
|
||||
serverCachedStoreReferences = BaselineMetrics.CachedStoreReferences(rig.Validator),
|
||||
clientCachedStoreReferences = BaselineMetrics.CachedStoreReferences(config.CertificateValidator),
|
||||
cachedReferenceIsOpenHandle = false });
|
||||
result = new FixtureWitness(all, survivors);
|
||||
}
|
||||
catch (Exception error) { failure = ExceptionDispatchInfo.Capture(error); }
|
||||
finally
|
||||
{
|
||||
// These bounds do not reuse the canceled 120-second operation token.
|
||||
async Task Cleanup(string step, Func<Task> operation, int seconds)
|
||||
{
|
||||
try { await operation().WaitAsync(TimeSpan.FromSeconds(seconds), CancellationToken.None); }
|
||||
catch (Exception error)
|
||||
{
|
||||
failure ??= ExceptionDispatchInfo.Capture(error);
|
||||
await LogAsync(output, new { kind = "semantic-cleanup-failed", step, exceptionType = error.GetType().FullName });
|
||||
}
|
||||
}
|
||||
if (rig is not null)
|
||||
await Cleanup("normal-host-stop", () => rig.DisposeAsync().AsTask(), 20);
|
||||
if (booted && rig is not null && config is not null)
|
||||
await Cleanup("post-stop-observations", async () =>
|
||||
{
|
||||
using var probe = new CancellationTokenSource(TimeSpan.FromSeconds(15));
|
||||
var after = await BaselineMetrics.DescriptorsAsync(probe.Token, clientRoot, rig.PkiRoot);
|
||||
after.Pki.ShouldBe(0);
|
||||
await LogAsync(output, new { kind = "semantic-owned-resources", phase = "stopped-but-harness-held", pkiDescriptors = after.Pki,
|
||||
serverCachedStoreReferences = BaselineMetrics.CachedStoreReferences(rig.Validator),
|
||||
clientCachedStoreReferences = BaselineMetrics.CachedStoreReferences(config.CertificateValidator), disposalFlagObservable = false });
|
||||
}, 20);
|
||||
await Cleanup("client-private-directory", () => Task.Run(() => { if (Directory.Exists(clientRoot)) Directory.Delete(clientRoot, true); }), 5);
|
||||
if (rig is not null)
|
||||
await Cleanup("server-private-directory", () => Task.Run(() => { if (Directory.Exists(rig.PkiRoot)) Directory.Delete(rig.PkiRoot, true); }), 5);
|
||||
GC.KeepAlive(application); GC.KeepAlive(config); GC.KeepAlive(rig);
|
||||
}
|
||||
failure?.Throw();
|
||||
return result ?? throw new InvalidOperationException("The fixture did not produce diagnostic witnesses");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static WeakWitness[] ClosedBoundary(BaselineRig rig, ApplicationConfiguration config, StreamWriter output, CancellationToken ct) =>
|
||||
ClosedScopeAsync(rig, config, output, ct).GetAwaiter().GetResult();
|
||||
|
||||
private static async Task<WeakWitness[]> ClosedScopeAsync(BaselineRig rig, ApplicationConfiguration config, StreamWriter output, CancellationToken ct)
|
||||
{
|
||||
var weak = new List<WeakWitness>();
|
||||
using var owner = await ConnectAsync(rig, config, null, ct);
|
||||
weak.Add(Witness("old-client-session", owner));
|
||||
var oldServerSession = rig.Server.CurrentInstance.SessionManager.GetSessions().Single(x => x.Id == owner.SessionId);
|
||||
weak.Add(Witness("old-server-session", oldServerSession));
|
||||
var oldAuthenticationToken = Token(owner);
|
||||
var oldBrowse = (await BrowseAsync(owner, ct))!;
|
||||
RequirePoint(oldBrowse);
|
||||
byte[]? oldHistory = null;
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
var page = await PageAsync(owner, null, false, ct);
|
||||
RequirePoint(page.ContinuationPoint);
|
||||
oldHistory ??= page.ContinuationPoint;
|
||||
}
|
||||
CapturePoints(oldServerSession, weak);
|
||||
ContinuationCounts(oldServerSession).ShouldBe((1, 6));
|
||||
await owner.CloseAsync(ct);
|
||||
rig.Server.CurrentInstance.SessionManager.GetSessions().Any(x => ReferenceEquals(x, oldServerSession)).ShouldBeFalse();
|
||||
rig.Server.CurrentInstance.SessionManager.GetSessions().Count.ShouldBe(0);
|
||||
await LogAsync(output, new { kind = "semantic-closed-state", activeSessions = 0, rawClosedBrowse = ContinuationCounts(oldServerSession).Browse,
|
||||
rawClosedHistory = ContinuationCounts(oldServerSession).History, diagnosticReferenceHeld = true });
|
||||
using var fresh = await ConnectAsync(rig, config, null, ct);
|
||||
var channel = fresh.TransportChannel;
|
||||
var freshToken = Token(fresh);
|
||||
freshToken.ShouldNotBe(oldAuthenticationToken);
|
||||
uint sequence = 10000;
|
||||
RequestHeader Header(NodeId token) => new() { AuthenticationToken = token, RequestHandle = ++sequence, Timestamp = DateTime.UtcNow, TimeoutHint = 5000 };
|
||||
ReadRequest Read(NodeId token) => new() { RequestHeader = Header(token), TimestampsToReturn = TimestampsToReturn.Neither,
|
||||
NodesToRead = [new ReadValueId { NodeId = Raw(fresh, Scalar), AttributeId = Attributes.Value }] };
|
||||
await RequireReadGood(channel, Read(freshToken), "fresh-read-before", output, ct);
|
||||
var oldRequest = Read(oldAuthenticationToken);
|
||||
var denied = await Send(channel, oldRequest, "old-authentication-token", output, ct);
|
||||
var deniedCode = denied.ResponseHeader.ServiceResult;
|
||||
(deniedCode == StatusCodes.BadSessionIdInvalid || deniedCode == StatusCodes.BadSessionClosed).ShouldBeTrue();
|
||||
await RequireReadGood(channel, Read(freshToken), "fresh-read-after", output, ct);
|
||||
|
||||
var ownBrowseRequest = new BrowseRequest { RequestHeader = Header(freshToken), RequestedMaxReferencesPerNode = 1,
|
||||
NodesToBrowse = [new BrowseDescription { NodeId = Raw(fresh, BaselineRig.RawDeviceFolder), BrowseDirection = BrowseDirection.Forward,
|
||||
ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences, IncludeSubtypes = true, ResultMask = (uint)BrowseResultMask.All }] };
|
||||
var ownBrowse = (BrowseResponse)await Send(channel, ownBrowseRequest, "fresh-own-browse", output, ct);
|
||||
ownBrowse.Results[0].StatusCode.ShouldBe(StatusCodes.Good); RequirePoint(ownBrowse.Results[0].ContinuationPoint);
|
||||
var oldBrowseResponse = (BrowseNextResponse)await Send(channel, new BrowseNextRequest { RequestHeader = Header(freshToken),
|
||||
ContinuationPoints = [oldBrowse], ReleaseContinuationPoints = false }, "old-browse-point", output, ct);
|
||||
oldBrowseResponse.Results[0].StatusCode.ShouldBe(StatusCodes.BadContinuationPointInvalid);
|
||||
await LogAsync(output, new { kind = "semantic-item-status", name = "old-browse-point", statusCode = (uint)oldBrowseResponse.Results[0].StatusCode });
|
||||
var ownBrowseNext = (BrowseNextResponse)await Send(channel, new BrowseNextRequest { RequestHeader = Header(freshToken),
|
||||
ContinuationPoints = [ownBrowse.Results[0].ContinuationPoint], ReleaseContinuationPoints = false }, "fresh-own-browse-resume", output, ct);
|
||||
ownBrowseNext.Results[0].StatusCode.ShouldBe(StatusCodes.Good);
|
||||
ownBrowseNext.Results[0].References.Count.ShouldBeGreaterThan(0); RequirePoint(ownBrowseNext.Results[0].ContinuationPoint);
|
||||
var ownBrowseRelease = (BrowseNextResponse)await Send(channel, new BrowseNextRequest { RequestHeader = Header(freshToken),
|
||||
ContinuationPoints = [ownBrowseNext.Results[0].ContinuationPoint], ReleaseContinuationPoints = true }, "fresh-own-browse-release", output, ct);
|
||||
ownBrowseRelease.Results[0].StatusCode.ShouldBe(StatusCodes.Good);
|
||||
|
||||
HistoryReadRequest History(byte[]? point, bool release) => new()
|
||||
{
|
||||
RequestHeader = Header(freshToken), TimestampsToReturn = TimestampsToReturn.Both, ReleaseContinuationPoints = release,
|
||||
HistoryReadDetails = new ExtensionObject(new ReadEventDetails { StartTime = Epoch.AddDays(-1), EndTime = Epoch.AddDays(1), NumValuesPerNode = 4,
|
||||
Filter = new EventFilter { SelectClauses = [new SimpleAttributeOperand(ObjectTypeIds.BaseEventType, new QualifiedName(BrowseNames.EventId))] } }),
|
||||
NodesToRead = [new HistoryReadValueId { NodeId = Raw(fresh, BaselineRig.RawDeviceFolder), ContinuationPoint = point }]
|
||||
};
|
||||
var backend = (SqlLikeEventSource)rig.Nm.HistorianDataSource;
|
||||
var beforeOwn = backend.EventReads;
|
||||
var ownHistory = (HistoryReadResponse)await Send(channel, History(null, false), "fresh-own-history", output, ct);
|
||||
ownHistory.Results[0].StatusCode.ShouldBe(StatusCodes.Good); RequirePoint(ownHistory.Results[0].ContinuationPoint);
|
||||
backend.EventReads.ShouldBeGreaterThan(beforeOwn);
|
||||
var beforeOld = backend.EventReads;
|
||||
var oldHistoryResponse = (HistoryReadResponse)await Send(channel, History(oldHistory, false), "old-history-point", output, ct);
|
||||
oldHistoryResponse.Results[0].StatusCode.ShouldBe(StatusCodes.BadContinuationPointInvalid);
|
||||
backend.EventReads.ShouldBe(beforeOld);
|
||||
await LogAsync(output, new { kind = "semantic-backend", operation = "old-history-point", before = beforeOld, after = backend.EventReads,
|
||||
statusCode = (uint)oldHistoryResponse.Results[0].StatusCode });
|
||||
var ownHistoryNext = (HistoryReadResponse)await Send(channel, History(ownHistory.Results[0].ContinuationPoint, false), "fresh-own-history-resume", output, ct);
|
||||
ownHistoryNext.Results[0].StatusCode.ShouldBe(StatusCodes.Good); RequirePoint(ownHistoryNext.Results[0].ContinuationPoint);
|
||||
backend.EventReads.ShouldBeGreaterThan(beforeOld);
|
||||
var releaseHistory = (HistoryReadResponse)await Send(channel, History(ownHistoryNext.Results[0].ContinuationPoint, true), "fresh-own-history-release", output, ct);
|
||||
releaseHistory.Results[0].StatusCode.ShouldBe(StatusCodes.Good);
|
||||
await fresh.CloseAsync(ct);
|
||||
rig.Server.CurrentInstance.SessionManager.GetSessions().Count.ShouldBe(0);
|
||||
return weak.ToArray();
|
||||
}
|
||||
|
||||
private static NodeId Token(ClientSession session)
|
||||
{
|
||||
var property = typeof(ClientBase).GetProperty("AuthenticationToken", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("Exact .182 token getter is missing");
|
||||
return property.PropertyType == typeof(NodeId) && property.GetValue(session) is NodeId value && !NodeId.IsNull(value)
|
||||
? NodeId.Parse(value.ToString()) : throw new InvalidOperationException("Exact .182 token getter changed");
|
||||
}
|
||||
private static async Task<IServiceResponse> Send(ITransportChannel channel, IServiceRequest request, string name, StreamWriter output, CancellationToken ct)
|
||||
{
|
||||
// No Session closed guard or ClientBase header update runs on this public raw transport path.
|
||||
var response = await channel.SendRequestAsync(request, ct);
|
||||
response.ResponseHeader.RequestHandle.ShouldBe(request.RequestHeader.RequestHandle);
|
||||
await LogAsync(output, new { kind = "semantic-wire", name, requestHandle = request.RequestHeader.RequestHandle,
|
||||
responseHandle = response.ResponseHeader.RequestHandle, responseType = response.GetType().Name,
|
||||
serviceStatusCode = (uint)response.ResponseHeader.ServiceResult });
|
||||
return response;
|
||||
}
|
||||
private static async Task RequireReadGood(ITransportChannel channel, ReadRequest request, string name, StreamWriter output, CancellationToken ct)
|
||||
{
|
||||
var response = (ReadResponse)await Send(channel, request, name, output, ct);
|
||||
response.ResponseHeader.ServiceResult.ShouldBe(StatusCodes.Good);
|
||||
response.Results[0].StatusCode.ShouldBe(StatusCodes.Good);
|
||||
}
|
||||
|
||||
private static FieldInfo ExactField(object owner, string name)
|
||||
{
|
||||
for (var type = owner.GetType(); type is not null; type = type.BaseType)
|
||||
if (type.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly) is { } field) return field;
|
||||
throw new InvalidOperationException("Missing exact .182 diagnostic field: " + name);
|
||||
}
|
||||
private static void CapturePoints(ServerSession session, List<WeakWitness> target)
|
||||
{
|
||||
var gate = ExactField(session, "m_lock").GetValue(session) as System.Threading.Lock ?? throw new InvalidOperationException("Unexpected session lock");
|
||||
using (gate.EnterScope())
|
||||
{
|
||||
var browse = ExactField(session, "m_browseContinuationPoints").GetValue(session) as IList ?? throw new InvalidOperationException("Missing populated browse list");
|
||||
foreach (var value in browse)
|
||||
{
|
||||
var point = value as Opc.Ua.Server.ContinuationPoint ?? throw new InvalidOperationException("Unexpected browse point type");
|
||||
target.Add(Witness("old-browse-point", point));
|
||||
if (point.Data is { } data) target.Add(Witness("old-browse-data", data));
|
||||
}
|
||||
var history = ExactField(session, "m_historyContinuationPoints").GetValue(session) as IList ?? throw new InvalidOperationException("Missing populated history list");
|
||||
var index = 0;
|
||||
foreach (var value in history)
|
||||
{
|
||||
var slot = value ?? throw new InvalidOperationException("Null history slot");
|
||||
target.Add(Witness("old-history-slot-" + index, slot));
|
||||
var state = ExactField(slot, "Value").GetValue(slot) ?? throw new InvalidOperationException("Null history state");
|
||||
target.Add(Witness("old-history-value-" + index++, state));
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void CaptureStores(CertificateValidator validator, string label, List<WeakWitness> target)
|
||||
{
|
||||
var gate = ExactField(validator, "m_semaphore").GetValue(validator) as SemaphoreSlim ?? throw new InvalidOperationException("Unexpected validator lock");
|
||||
if (!gate.Wait(TimeSpan.FromSeconds(5))) throw new TimeoutException("Validator witness lock timed out");
|
||||
try
|
||||
{
|
||||
var index = 0;
|
||||
foreach (var name in new[] { "m_trustedCertificateStore", "m_issuerCertificateStore", "m_rejectedCertificateStore" })
|
||||
if (ExactField(validator, name).GetValue(validator) is CertificateStoreIdentifier identifier
|
||||
&& ExactField(identifier, "m_store").GetValue(identifier) is ICertificateStore store)
|
||||
target.Add(Witness(label + "-" + index++, store));
|
||||
}
|
||||
finally { gate.Release(); }
|
||||
}
|
||||
private static WeakWitness Witness(string label, object target) => new(label, target);
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static WeakWitness EphemeralNegative() => Witness("negative-collectible", new object());
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static bool TerminalDisposeBoundary(WeakWitness server) => server.DisposeOriginalServerForAttribution();
|
||||
private static async Task WriteShapes(IEnumerable<WeakWitness> witnesses, StreamWriter output)
|
||||
{ foreach (var witness in witnesses) await LogAsync(output, witness.Shape); }
|
||||
private static async Task<WeakObservation[]> ObserveAsync(string phase, WeakWitness[] witnesses, WeakWitness positive, WeakWitness negative, StreamWriter output, CancellationToken ct)
|
||||
{
|
||||
var delays = new[] { 0, 50, 100, 250, 500, 1000 };
|
||||
WeakObservation[] last = [];
|
||||
for (var round = 0; round < delays.Length; round++)
|
||||
{
|
||||
if (round > 0)
|
||||
{
|
||||
await Task.Delay(delays[round], ct);
|
||||
await Task.Run(ForcedCollection, ct).WaitAsync(TimeSpan.FromSeconds(5), ct);
|
||||
}
|
||||
last = witnesses.Concat([positive, negative]).Select(x => x.Observe(phase == "live-host" && x.Label.StartsWith("fixture-", StringComparison.Ordinal)
|
||||
|| x.Label == "positive-retained")).ToArray();
|
||||
await LogAsync(output, new { kind = "weak-observation", phase, round, groups = last, process = ProcessCounters() });
|
||||
positive.IsAlive().ShouldBeTrue("the retained positive must remain observable");
|
||||
if (round > 0) negative.IsAlive().ShouldBeFalse("the isolated collectible negative proves GC observation is effective");
|
||||
}
|
||||
return last;
|
||||
}
|
||||
private static void ForcedCollection() { GC.Collect(2, GCCollectionMode.Forced, true, true); GC.WaitForPendingFinalizers(); GC.Collect(2, GCCollectionMode.Forced, true, true); }
|
||||
private static object ProcessCounters()
|
||||
{
|
||||
using var process = Process.GetCurrentProcess(); process.Refresh();
|
||||
return new { managedBytes = GC.GetTotalMemory(false), rssBytes = process.WorkingSet64,
|
||||
activeTimers = Timer.ActiveCount, gcCommittedBytes = GC.GetGCMemoryInfo().TotalCommittedBytes,
|
||||
jitMethods = System.Runtime.JitInfo.GetCompiledMethodCount() };
|
||||
}
|
||||
private sealed class WeakWitness
|
||||
{
|
||||
private readonly WeakReference _reference;
|
||||
internal string Label { get; }
|
||||
internal object Shape { get; }
|
||||
private readonly string _type;
|
||||
internal WeakWitness(string label, object target)
|
||||
{
|
||||
Label = label; _type = target.GetType().FullName ?? target.GetType().Name; _reference = new WeakReference(target);
|
||||
var fields = new List<object>();
|
||||
for (var type = target.GetType(); type is not null; type = type.BaseType)
|
||||
foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
|
||||
if (typeof(IDisposable).IsAssignableFrom(field.FieldType)) fields.Add(new { name = field.Name, type = field.FieldType.FullName });
|
||||
Shape = new { kind = "weak-resource-shape", label, type = _type, isDisposable = target is IDisposable, resourceFields = fields };
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.NoInlining)] internal bool IsAlive() => _reference.IsAlive;
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal bool DisposeOriginalServerForAttribution()
|
||||
{
|
||||
var value = _reference.Target;
|
||||
if (value is null) return false;
|
||||
if (Label != "fixture-server" || value is not ZB.MOM.WW.OtOpcUa.OpcUaServer.OtOpcUaSdkServer server)
|
||||
throw new InvalidOperationException("Only the known original server may receive terminal attribution disposal");
|
||||
server.Dispose(); // Public terminal lifecycle API, once; no private timer/cache manipulation.
|
||||
return true;
|
||||
}
|
||||
internal WeakObservation Observe(bool expectedAlive) => new(Label, _type, IsAlive(), expectedAlive);
|
||||
}
|
||||
private sealed record WeakObservation(string label, string type, bool alive, bool expectedAlive);
|
||||
private sealed record FixtureWitness(WeakWitness[] All, string[] LiveHostSurvivors);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compile diagnostic adapters against the immutable original publication; never build product code."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--host', type=Path, required=True)
|
||||
parser.add_argument('--client', type=Path, required=True)
|
||||
parser.add_argument('--assertions', type=Path, required=True, help='Directory containing Shouldly.dll; only assertion dependencies are selected')
|
||||
parser.add_argument('--output', type=Path, required=True)
|
||||
parser.add_argument('--inventory', action='store_true')
|
||||
args = parser.parse_args()
|
||||
root = args.output.resolve(); root.mkdir(parents=True, exist_ok=False)
|
||||
source = Path(__file__).resolve().parent
|
||||
references = {}
|
||||
for directory in [args.host, args.client]:
|
||||
for dll in sorted(directory.resolve(strict=True).glob('*.dll')):
|
||||
references.setdefault(dll.name, dll)
|
||||
for name in ['Shouldly.dll', 'DiffEngine.dll', 'EmptyFiles.Xml.dll']:
|
||||
path = args.assertions.resolve(strict=True) / name
|
||||
if path.exists(): references.setdefault(name, path)
|
||||
project = ET.Element('Project', Sdk='Microsoft.NET.Sdk')
|
||||
properties = ET.SubElement(project, 'PropertyGroup')
|
||||
for key,value in {'OutputType':'Exe','TargetFramework':'net10.0','Nullable':'enable','ImplicitUsings':'enable',
|
||||
'TreatWarningsAsErrors':'true','EnableDefaultCompileItems':'false'}.items():
|
||||
ET.SubElement(properties,key).text=value
|
||||
items = ET.SubElement(project,'ItemGroup')
|
||||
ET.SubElement(items,'FrameworkReference',Include='Microsoft.AspNetCore.App')
|
||||
for name,path in references.items():
|
||||
reference=ET.SubElement(items,'Reference',Include=path.stem); ET.SubElement(reference,'HintPath').text=str(path)
|
||||
files = ['Inventory.cs'] if args.inventory else ['Program.cs','BaselineRig.cs','BaselineMetrics.cs','CanonicalOperations.cs','SemanticRetention.cs']
|
||||
for name in files: ET.SubElement(items,'Compile',Include=str(source/name))
|
||||
path=root/'adapter.csproj'; ET.indent(project); ET.ElementTree(project).write(path,encoding='unicode')
|
||||
manifest={'diagnosticOnly':True,'productRebuilt':False,'references':{name:hashlib.sha256(p.read_bytes()).hexdigest() for name,p in references.items()},
|
||||
'sources':{name:hashlib.sha256((source/name).read_bytes()).hexdigest() for name in files}}
|
||||
(root/'provenance.json').write_text(json.dumps(manifest,indent=2)+'\n')
|
||||
with (root/'build.log').open('w') as log:
|
||||
result=subprocess.run(['dotnet','build',str(path),'-c','Release','-m:2'],stdout=log,stderr=subprocess.STDOUT)
|
||||
raise SystemExit(result.returncode)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run one bounded original-only diagnostic control; preserve failures and unsupported exit 2."""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import secrets
|
||||
import subprocess
|
||||
|
||||
parser=argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--adapter',type=Path,required=True)
|
||||
parser.add_argument('--output',type=Path,required=True)
|
||||
parser.add_argument('--mode',choices=['normal','active-restart','semantic-retention','semantic-terminal-dispose'],required=True)
|
||||
args=parser.parse_args()
|
||||
output=args.output.resolve()
|
||||
if output.exists():raise RuntimeError('Use a fresh private output directory')
|
||||
log=output.with_suffix('.log')
|
||||
environment=os.environ.copy()
|
||||
environment['OTOPCUA_BASELINE_CANONICAL_KEY']='otua_k-server-events_'+secrets.token_hex(3)
|
||||
command=['dotnet',str(args.adapter.resolve(strict=True)),str(output),'1']
|
||||
if args.mode=='active-restart':command+=['--active-restart-control']
|
||||
if args.mode=='semantic-retention':command+=['--semantic-retention-control']
|
||||
if args.mode=='semantic-terminal-dispose':command+=['--semantic-terminal-dispose-control']
|
||||
outer_timeout=180 if args.mode.startswith('semantic-') else 120
|
||||
with os.fdopen(os.open(log,os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o600),'w') as stream:
|
||||
try:result=subprocess.run(command,env=environment,stdout=stream,stderr=subprocess.STDOUT,timeout=outer_timeout);exit_code=result.returncode
|
||||
except subprocess.TimeoutExpired:exit_code=124
|
||||
if output.exists():
|
||||
(output/'runner-receipt.json').write_text(json.dumps({'mode':args.mode,'exitCode':exit_code,'acceptance':False,
|
||||
'productRebuilt':False,'timeoutSeconds':outer_timeout,'credentialBytes':len(environment['OTOPCUA_BASELINE_CANONICAL_KEY'].encode())},indent=2)+'\n')
|
||||
print(json.dumps({'mode':args.mode,'exitCode':exit_code,'acceptance':False}))
|
||||
raise SystemExit(exit_code)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build the opt-in saturation observer without changing product dependencies."""
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
def build(output, libraries, linux):
|
||||
output = output.resolve()
|
||||
output.mkdir(parents=True, exist_ok=False)
|
||||
project = ET.Element("Project", Sdk="Microsoft.NET.Sdk")
|
||||
properties = ET.SubElement(project, "PropertyGroup")
|
||||
for key, value in {"OutputType": "Exe", "TargetFramework": "net10.0", "ImplicitUsings": "enable",
|
||||
"TreatWarningsAsErrors": "true", "EnableDefaultCompileItems": "false"}.items():
|
||||
ET.SubElement(properties, key).text = value
|
||||
items = ET.SubElement(project, "ItemGroup")
|
||||
ET.SubElement(items, "FrameworkReference", Include="Microsoft.AspNetCore.App")
|
||||
ET.SubElement(items, "Compile", Include=str(Path(__file__).resolve().parent / "SaturationObserver.cs"))
|
||||
for name in ["Microsoft.Diagnostics.NETCore.Client", "Microsoft.Diagnostics.Tracing.TraceEvent", "Microsoft.Diagnostics.FastSerialization"]:
|
||||
path = (libraries / (name + ".dll")).resolve(strict=True)
|
||||
reference = ET.SubElement(items, "Reference", Include=name)
|
||||
ET.SubElement(reference, "HintPath").text = str(path)
|
||||
project_path = output / "observer.csproj"
|
||||
ET.indent(project)
|
||||
ET.ElementTree(project).write(project_path, encoding="unicode")
|
||||
command = ["dotnet", "publish" if linux else "build", str(project_path), "-c", "Release", "-m:2", "--nologo"]
|
||||
if linux:
|
||||
command += ["-r", "linux-arm64", "--self-contained", "true", "-p:RuntimeFrameworkVersion=10.0.14",
|
||||
"-p:PublishSingleFile=false", "-p:PublishTrimmed=false", "-p:UseAppHost=true", "-o", str(output / "published")]
|
||||
with (output / "build.log").open("w") as log:
|
||||
result = subprocess.run(command, stdout=log, stderr=subprocess.STDOUT)
|
||||
(output / "build-command.json").write_text(json.dumps(command, indent=2) + "\n")
|
||||
if result.returncode:
|
||||
raise subprocess.CalledProcessError(result.returncode, command)
|
||||
return output / ("published" if linux else "bin/Release/net10.0")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--output", required=True, type=Path)
|
||||
parser.add_argument("--libraries", required=True, type=Path)
|
||||
parser.add_argument("--linux-self-contained", action="store_true")
|
||||
args = parser.parse_args()
|
||||
print(build(args.output, args.libraries, args.linux_self_contained))
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build optional local capture/summary tools using the user's installed dotnet-trace libraries."""
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--libraries", required=True, type=Path, help="dotnet-trace tools/net8.0/any directory")
|
||||
parser.add_argument("--output", required=True, type=Path, help="Fresh local output directory")
|
||||
args = parser.parse_args()
|
||||
args.output = args.output.resolve()
|
||||
args.output.mkdir(parents=True, exist_ok=False)
|
||||
for name, source, references in [
|
||||
("capture", "EventPipeCapture.cs", ["Microsoft.Diagnostics.NETCore.Client"]),
|
||||
("summary", "TraceSummary.cs", ["Microsoft.Diagnostics.Tracing.TraceEvent", "Microsoft.Diagnostics.FastSerialization", "Microsoft.Diagnostics.NETCore.Client"])
|
||||
]:
|
||||
output = args.output / name
|
||||
output.mkdir()
|
||||
project = ET.Element("Project", Sdk="Microsoft.NET.Sdk")
|
||||
properties = ET.SubElement(project, "PropertyGroup")
|
||||
for key, value in {"OutputType": "Exe", "TargetFramework": "net10.0", "ImplicitUsings": "enable",
|
||||
"EnableDefaultCompileItems": "false"}.items():
|
||||
ET.SubElement(properties, key).text = value
|
||||
items = ET.SubElement(project, "ItemGroup")
|
||||
ET.SubElement(items, "FrameworkReference", Include="Microsoft.AspNetCore.App")
|
||||
ET.SubElement(items, "Compile", Include=str(Path(__file__).resolve().parent / source))
|
||||
for reference in references:
|
||||
dll = (args.libraries / (reference + ".dll")).resolve(strict=True)
|
||||
item = ET.SubElement(items, "Reference", Include=reference)
|
||||
ET.SubElement(item, "HintPath").text = str(dll)
|
||||
path = output / (name + ".csproj")
|
||||
ET.indent(project)
|
||||
ET.ElementTree(project).write(path, encoding="unicode")
|
||||
with (output / "build.log").open("w") as log:
|
||||
result = subprocess.run(["dotnet", "build", str(path), "-c", "Release", "-m:2"], stdout=log, stderr=subprocess.STDOUT)
|
||||
if result.returncode:
|
||||
raise SystemExit(result.returncode)
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
# Diagnostic only. The published xUnit apphost and observer are self-contained .NET 10.0.14.
|
||||
set -euo pipefail
|
||||
mkdir -p /task/tmp /task/data /task/results /task/control
|
||||
export TMPDIR=/task/tmp XDG_DATA_HOME=/task/data
|
||||
export OTOPCUA_SDK2_SOAK=1 OTOPCUA_SDK2_SOAK_MODE=saturation OTOPCUA_SDK2_SOAK_SECONDS=1800
|
||||
export OTOPCUA_SDK2_SOAK_OUTPUT=/task/resources.jsonl
|
||||
export OTOPCUA_SDK2_SATURATION_CONTROL=/task/control
|
||||
export OPCUA_CERTIFICATE_LEAK_TRACKING=1
|
||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_NOLOGO=1
|
||||
[[ $(uname -s) == Linux && $(uname -m) == aarch64 && -d /proc/self/fd ]]
|
||||
dotnet --info > /task/controller-runtime.txt
|
||||
vstest_pid= observer_pid=
|
||||
cleanup() {
|
||||
for child in "$vstest_pid" "$observer_pid"; do
|
||||
if [[ -n "$child" ]] && kill -0 "$child" 2>/dev/null; then kill "$child" 2>/dev/null || true; fi
|
||||
done
|
||||
}
|
||||
trap cleanup EXIT
|
||||
dotnet vstest /app/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests.dll \
|
||||
--TestCaseFilter:'FullyQualifiedName~Sdk2LifecycleSoakTests.Descriptor_probe_' \
|
||||
--ResultsDirectory:/task/results '--Logger:trx;LogFileName=descriptor-controls.trx' > /task/descriptor-controls.log 2>&1
|
||||
/observer/observer /task/control "$OTOPCUA_SDK2_SATURATION_RUN_ID" > /task/observer.log 2>&1 &
|
||||
observer_pid=$!
|
||||
dotnet vstest /app/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests.dll \
|
||||
--TestCaseFilter:'FullyQualifiedName=ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests.Sdk2LifecycleSoakTests.Repeated_lifecycles_return_resources_to_a_steady_baseline' \
|
||||
--ResultsDirectory:/task/results '--Logger:trx;LogFileName=saturation.trx' > /task/test.log 2>&1 &
|
||||
vstest_pid=$!
|
||||
observed=0
|
||||
for attempt in $(seq 1 1200); do
|
||||
if [[ -s /task/resources.jsonl ]]; then
|
||||
target_pid=$(head -n 1 /task/resources.jsonl | sed -n 's/.*"processId":\([0-9]*\),.*/\1/p')
|
||||
[[ -n "$target_pid" ]]
|
||||
target_exe=$(readlink "/proc/$target_pid/exe")
|
||||
[[ "$target_exe" == /app/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests ]]
|
||||
printf 'targetPid=%s\nactualExecutable=%s\n' "$target_pid" "$target_exe" > /task/target-runtime.txt
|
||||
awk '$6 ~ /\/(libcoreclr.so|libhostfxr.so|libhostpolicy.so|System.Private.CoreLib.dll)$/ {print $6}' "/proc/$target_pid/maps" | sort -u >> /task/target-runtime.txt
|
||||
grep -Fx /app/libcoreclr.so /task/target-runtime.txt > /dev/null
|
||||
[[ $(readlink "/proc/$observer_pid/exe") == /observer/observer ]]
|
||||
awk '$6 ~ /\/libcoreclr.so$/ {print $6}' "/proc/$observer_pid/maps" | sort -u > /task/observer-runtime.txt
|
||||
grep -Fx /observer/libcoreclr.so /task/observer-runtime.txt > /dev/null
|
||||
observed=1
|
||||
break
|
||||
fi
|
||||
kill -0 "$vstest_pid" 2>/dev/null || break
|
||||
sleep 0.1
|
||||
done
|
||||
[[ "$observed" == 1 ]]
|
||||
set +e
|
||||
wait "$vstest_pid"
|
||||
test_status=$?
|
||||
printf '%s\n' "$test_status" > /task/test-exit-status.txt
|
||||
for attempt in $(seq 1 400); do
|
||||
kill -0 "$observer_pid" 2>/dev/null || break
|
||||
sleep 0.1
|
||||
done
|
||||
if kill -0 "$observer_pid" 2>/dev/null; then kill "$observer_pid"; fi
|
||||
wait "$observer_pid"
|
||||
observer_status=$?
|
||||
printf '%s\n' "$observer_status" > /task/observer-exit-status.txt
|
||||
# Preserve a cold-cap failure even when diagnostic readiness succeeds.
|
||||
if [[ "$test_status" != 0 ]]; then exit "$test_status"; fi
|
||||
exit "$observer_status"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Launch only a previously reviewed immutable candidate saturation bundle. Never an acceptance run."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import time
|
||||
import uuid
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
def digest(path):
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def require(condition, message):
|
||||
if not condition:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def verify(root, expected):
|
||||
receipt_path = root / "receipt.json"
|
||||
if digest(receipt_path) != expected:
|
||||
raise ValueError("Receipt hash differs from the reviewed artifact")
|
||||
receipt = json.loads(receipt_path.read_text())
|
||||
for name, value in receipt["files"].items():
|
||||
if digest(root / name) != value:
|
||||
raise ValueError("Immutable bundle file changed: " + name)
|
||||
require(receipt["protocol"] == "sdk2-saturation-v1" and receipt["acceptance"] is False, "Invalid diagnostic protocol")
|
||||
require((receipt["canonicalCycles"], receipt["completedRestarts"], receipt["settlingCycles"], receipt["maximumPhaseSeconds"]) == (384, 96, 1, 1800), "Changed prospective limits")
|
||||
return receipt
|
||||
|
||||
|
||||
def trx(path, minimum):
|
||||
document = ET.parse(path)
|
||||
counters = document.find(".//{*}Counters")
|
||||
if counters is None:
|
||||
raise ValueError("Missing TRX counters")
|
||||
executed = int(counters.attrib.get("executed", "0"))
|
||||
skipped = max(int(counters.attrib.get("notExecuted", "0")),
|
||||
sum(result.get("outcome") == "NotExecuted" for result in document.findall("./{*}Results/{*}UnitTestResult")))
|
||||
if executed < minimum or skipped:
|
||||
raise ValueError("Required diagnostic tests were skipped or not executed")
|
||||
return {"executed": executed, "skipped": skipped}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--bundle", type=Path, required=True)
|
||||
parser.add_argument("--receipt-sha256", required=True, help="Hash reviewed by root and Astra before launch")
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--verify-only", action="store_true")
|
||||
options = parser.parse_args()
|
||||
root = options.bundle.resolve(strict=True)
|
||||
receipt = verify(root, options.receipt_sha256)
|
||||
if options.verify_only:
|
||||
print("Immutable bundle verified; no experiment launched")
|
||||
return 0
|
||||
output = options.output.resolve()
|
||||
output.mkdir(parents=True, exist_ok=False)
|
||||
run_id = str(uuid.uuid4())
|
||||
container = "opcua-saturation-" + uuid.uuid4().hex[:12]
|
||||
command = ["docker", "run", "--rm", "--name", container, "--network", "none",
|
||||
"-e", "OTOPCUA_SOAK_BUILD_SHA=" + receipt["sourceRevision"],
|
||||
"-e", "OTOPCUA_SDK2_SATURATION_RUN_ID=" + run_id,
|
||||
"-v", str(root / "app") + ":/app:ro", "-v", str(root / "observer") + ":/observer:ro",
|
||||
"-v", str(root / "tools") + ":/tools:ro", "-v", str(output) + ":/task",
|
||||
receipt["image"], "bash", "/tools/container_saturation.sh"]
|
||||
(output / "launch.json").write_text(json.dumps({"command": command, "receiptSha256": options.receipt_sha256,
|
||||
"sourceRevision": receipt["sourceRevision"], "acceptance": False}, indent=2) + "\n")
|
||||
start = time.monotonic()
|
||||
try:
|
||||
with (output / "container.log").open("w") as log:
|
||||
process = subprocess.run(command, stdout=log, stderr=subprocess.STDOUT, timeout=2040)
|
||||
status = process.returncode
|
||||
except subprocess.TimeoutExpired:
|
||||
status = 124
|
||||
finally:
|
||||
subprocess.run(["docker", "stop", "--time", "10", container], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=20)
|
||||
(output / "container-exit-status.txt").write_text(str(status) + "\n")
|
||||
results = {"acceptance": False, "containerExitStatus": status, "wallSeconds": time.monotonic() - start,
|
||||
"sourceRevision": receipt["sourceRevision"], "receiptSha256": options.receipt_sha256}
|
||||
try:
|
||||
results["descriptorTests"] = trx(output / "results/descriptor-controls.trx", 2)
|
||||
results["experimentTests"] = trx(output / "results/saturation.trx", 1)
|
||||
rows = [json.loads(line) for line in (output / "resources.jsonl").read_text().splitlines()]
|
||||
header = rows[0]
|
||||
require(header["mode"] == "saturation" and header["requestedSeconds"] == 1800 and header["build"] == receipt["sourceRevision"], "Wrong executed source or mode")
|
||||
require(not any(row["kind"] == "complete" for row in rows), "Saturation must never emit an ordinary completion result")
|
||||
outcome = [row for row in rows if row["kind"] == "saturation-result"]
|
||||
require(len(outcome) == 1, "Missing unique diagnostic result")
|
||||
outcome = outcome[0]
|
||||
require((outcome["canonicalCycles"], outcome["settlingCycles"], outcome["restarts"]) == (384, 1, 96), "Incomplete fixed work")
|
||||
require(outcome["phaseSeconds"] <= 1800 and outcome["shutdownOwnershipVerified"], "Cutoff or cleanup proof failed")
|
||||
require(outcome["acceptance"] is False, "This is not an acceptance run")
|
||||
results["diagnosticOutcome"] = outcome
|
||||
observer_status = int((output / "observer-exit-status.txt").read_text())
|
||||
test_status = int((output / "test-exit-status.txt").read_text())
|
||||
require(observer_status == 0, "Observer failed")
|
||||
require((test_status != 0) == (outcome["coldBudgetRejected"] or not outcome["readiness"]), "Cold-cap exit was not preserved")
|
||||
results["protocolCompleted"] = True
|
||||
except Exception as error:
|
||||
results["protocolCompleted"] = False
|
||||
results["validationErrorType"] = type(error).__name__
|
||||
status = status or 1
|
||||
verify(root, options.receipt_sha256)
|
||||
(output / "run-receipt.json").write_text(json.dumps(results, indent=2) + "\n")
|
||||
print(json.dumps(results, indent=2))
|
||||
return status
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run two already-built public lifecycle controls in isolated, offline Linux containers."""
|
||||
import argparse
|
||||
from datetime import datetime, timezone
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import secrets
|
||||
import subprocess
|
||||
import time
|
||||
import uuid
|
||||
|
||||
|
||||
def latest_sample(path):
|
||||
# A concurrently-written final line may be incomplete. All complete rows remain in the source log.
|
||||
for row in reversed(path.read_text().splitlines()):
|
||||
try:
|
||||
value = json.loads(row)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if value.get("kind") == "sample":
|
||||
return value
|
||||
return None
|
||||
|
||||
|
||||
def run(options):
|
||||
options.output = options.output.resolve()
|
||||
options.output.mkdir(parents=True, exist_ok=False)
|
||||
processes = []
|
||||
environment = os.environ.copy()
|
||||
environment["OPCUA_MEMORY_PASSWORD"] = secrets.token_urlsafe(32)
|
||||
# Both receive the same environment. SDK1 ignores the SDK2 diagnostic opt-in.
|
||||
environment["OPCUA_CERTIFICATE_LEAK_TRACKING"] = "1"
|
||||
started = time.monotonic()
|
||||
try:
|
||||
for label, directory in [("baseline", options.baseline), ("candidate", options.candidate)]:
|
||||
directory = directory.resolve(strict=True)
|
||||
for required in ["control.dll", "control.deps.json", "control.runtimeconfig.json"]:
|
||||
if not (directory / required).is_file():
|
||||
raise FileNotFoundError("Use a complete, canonically-built control output: missing " + required)
|
||||
output = options.output / label
|
||||
output.mkdir()
|
||||
container = "opcua-memory-" + label + "-" + uuid.uuid4().hex[:10]
|
||||
shell = ('dotnet /app/control.dll /task/private-fixture ' + str(options.seconds) +
|
||||
' > /task/samples.jsonl 2>&1 & child=$!; echo "$child" > /task/dotnet.pid; '
|
||||
'wait "$child"; status=$?; echo "$status" > /task/exit-status.txt; exit "$status"')
|
||||
command = ["docker", "run", "--rm", "--name", container, "--network", "none",
|
||||
"--env", "OPCUA_MEMORY_PASSWORD", "--env", "OPCUA_CERTIFICATE_LEAK_TRACKING",
|
||||
"-v", str(directory) + ":/app:ro", "-v", str(output) + ":/task",
|
||||
options.image, "bash", "-c", shell]
|
||||
manifest = {"argv": command, "fullGate": False, "seconds": options.seconds,
|
||||
"environmentNamesOnly": ["OPCUA_MEMORY_PASSWORD", "OPCUA_CERTIFICATE_LEAK_TRACKING"],
|
||||
"startedUtc": datetime.now(timezone.utc).isoformat(),
|
||||
"runtimeFiles": {p.name: hashlib.sha256(p.read_bytes()).hexdigest()
|
||||
for p in sorted(directory.iterdir()) if p.is_file()}}
|
||||
(output / "command.json").write_text(json.dumps(manifest, indent=2) + "\n")
|
||||
stream = (output / "container.log").open("w")
|
||||
process = subprocess.Popen(command, env=environment, stdout=stream, stderr=subprocess.STDOUT)
|
||||
processes.append((label, container, output, stream, process))
|
||||
next_sample = started + 60
|
||||
while any(process.poll() is None for _, _, _, _, process in processes):
|
||||
if time.monotonic() - started > options.seconds + 240:
|
||||
raise TimeoutError("Diagnostic controls exceeded bounded shutdown allowance")
|
||||
if time.monotonic() >= next_sample:
|
||||
for label, container, output, _, process in processes:
|
||||
pid_file = output / "dotnet.pid"
|
||||
if process.poll() is not None or not pid_file.exists():
|
||||
continue
|
||||
pid = pid_file.read_text().strip()
|
||||
if not pid.isdecimal():
|
||||
raise RuntimeError("Unexpected task-owned process id")
|
||||
stamp = str(int(time.time()))
|
||||
latest = latest_sample(output / "samples.jsonl")
|
||||
with (output / "observations.jsonl").open("a") as observed:
|
||||
observed.write(json.dumps({"utc": datetime.now(timezone.utc).isoformat(),
|
||||
"seconds": time.monotonic() - started,
|
||||
"sample": latest}) + "\n")
|
||||
for name in ["smaps", "smaps_rollup", "status"]:
|
||||
with (output / (stamp + "-" + name + ".txt")).open("w") as destination:
|
||||
result = subprocess.run(["docker", "exec", container, "cat", "/proc/" + pid + "/" + name],
|
||||
stdout=destination, stderr=subprocess.STDOUT, timeout=15)
|
||||
if result.returncode and process.poll() is None:
|
||||
raise RuntimeError(label + " live process diagnostic read failed")
|
||||
next_sample += 60
|
||||
time.sleep(.5)
|
||||
statuses = []
|
||||
for label, _, output, _, process in processes:
|
||||
status = process.wait()
|
||||
(output / "docker-exit-status.txt").write_text(str(status) + "\n")
|
||||
print(label + " exit " + str(status), flush=True)
|
||||
statuses.append(status)
|
||||
return 0 if all(status == 0 for status in statuses) else 1
|
||||
finally:
|
||||
for _, container, _, stream, process in processes:
|
||||
if process.poll() is None:
|
||||
subprocess.run(["docker", "stop", "--time", "10", container], stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL, timeout=20)
|
||||
process.wait(timeout=20)
|
||||
stream.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--baseline", type=Path, required=True, help="Already-built control output directory")
|
||||
parser.add_argument("--candidate", type=Path, required=True, help="Already-built control output directory")
|
||||
parser.add_argument("--output", type=Path, required=True, help="New local diagnostic directory")
|
||||
parser.add_argument("--image", required=True, help="Immutable Linux .NET10 image digest")
|
||||
parser.add_argument("--seconds", type=int, default=1800)
|
||||
options = parser.parse_args()
|
||||
if not 1 <= options.seconds <= 1800 or not options.image.startswith("sha256:"):
|
||||
parser.error("Use an immutable image digest and duration 1..1800; never a full-soak gate")
|
||||
raise SystemExit(run(options))
|
||||
@@ -0,0 +1 @@
|
||||
__pycache__/
|
||||
@@ -0,0 +1,95 @@
|
||||
# Native empty-keychain control — negative retention result
|
||||
|
||||
The plain C control completed **200 private keychain create/delete/CFRelease
|
||||
operations successfully**, with `Security::AppleDatabase` heap inventory staying
|
||||
at **1 object / 768 shallow bytes** before, after the first operation, after 200,
|
||||
and after a five-second idle. This **does not reproduce** the retention seen in
|
||||
private-PFX and SDK configured-store loading. Empty native create/delete/release
|
||||
alone is therefore insufficient to explain that result on this machine.
|
||||
|
||||
This does not rule out deletion/resource interactions for populated imported
|
||||
keychains, identify the retaining owner, or assign an SDK, CLR or Apple defect.
|
||||
Private-key/certificate materialization and its associated native interactions
|
||||
remain distinct. No production mitigation, resource-budget decision, G4 pass or
|
||||
#799 closure follows from this control.
|
||||
|
||||
## Reviewed execution and isolation
|
||||
|
||||
Astra reviewed the actual C/Python source before execution and afterward
|
||||
independently verified all 207 JSONL rows, 200 ordered operations/status triples,
|
||||
reference balance, default/search-list fingerprints, all four native heap
|
||||
counts/bytes, FD/file counts, source/runner/binary hashes, cleanup and warnings.
|
||||
Only **one actual native execution** ran, serialized after the coordinator's other
|
||||
ownership control. Source is `PureAppleControl.c`; collector is `pure_apple.py`.
|
||||
|
||||
The child is a plain native C program linked only to Security.framework,
|
||||
CoreFoundation.framework and libSystem. It does not load certificates/PFX, the
|
||||
OPC UA SDK or CLR. `SecKeychainCreate` used a fresh random filename beneath its
|
||||
private mode-0700 task root, a generated password and `promptUser=false`. A returned
|
||||
keychain's path had to exactly match that generated private filename/backing file
|
||||
before deletion was allowed. Each returned create reference received exactly
|
||||
one `CFRelease`, including failure paths. The program made no default/search-list
|
||||
setter or restore call. Read-only `SecKeychainCopyDefault`,
|
||||
`SecKeychainCopySearchList` and `SecKeychainGetPath` produced hashed before/after
|
||||
fingerprints; owned Copy results were released, borrowed array elements were not
|
||||
released separately. Existing keychain paths/passwords were never logged.
|
||||
|
||||
Native checkpoints were before, first, 200 and five-second idle. Each parent
|
||||
snapshot used `heap --noContent -s` and numeric lsof FDs, with Mach task resident
|
||||
bytes reported by the child. It did not invoke GC or allocator-pressure APIs.
|
||||
The original fixed 180-second checkpoint/child limit, 20-second heap and
|
||||
five-second lsof limits, bounded final wait and terminate/kill reap remain in the
|
||||
collector; those probe/reap bounds can extend total wall time beyond 180 seconds.
|
||||
Actual child monotonic duration was **8.995 seconds**; parent wall time including
|
||||
probes was **9.288 seconds**. The child exited zero and was reaped.
|
||||
|
||||
## Actual results
|
||||
|
||||
| Checkpoint | AppleDatabase count / shallow bytes | Numeric FDs | Owned keychain files / FDs | RSS bytes |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Before | 1 / 768 | 3 | 0 / 0 | 7,372,800 |
|
||||
| First | 1 / 768 | 3 | 0 / 0 | 8,028,160 |
|
||||
| 200 | 1 / 768 | 3 | 0 / 0 | 8,699,904 |
|
||||
| Five-second idle | 1 / 768 | 3 | 0 / 0 | 8,699,904 |
|
||||
|
||||
All **200 create/path/delete OSStatus triples were `(0, 0, 0)`**. Each create
|
||||
returned an owned reference, its private path was verified, and all **200/200**
|
||||
references were released exactly once. All read-only default/path/search-list
|
||||
query statuses were zero. Before/after fingerprints were identical, with search
|
||||
list count **2** at both observations. Four query-owned Copy references were
|
||||
released across the two fingerprint collections. No list setter was invoked.
|
||||
|
||||
Default fingerprint:
|
||||
`08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63`.
|
||||
Search-list fingerprint:
|
||||
`c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c`.
|
||||
|
||||
Owned keychain files were already zero before harness cleanup. The fresh owned
|
||||
fixture root was then removed. Native class count is a heap classification, not
|
||||
an open-handle count or an exclusive attribution of the modest RSS change.
|
||||
|
||||
The first attempt stopped at **compilation**, before a native child or API call:
|
||||
strict C11 headers did not declare `explicit_bzero`. Replacing only that
|
||||
caller-owned buffer wipe with a volatile-byte wipe made the build succeed; empty
|
||||
fixture creation was moved after successful compilation. The failed build is
|
||||
retained separately. Both compile attempts emitted **five unsuppressed
|
||||
deprecated-public-API warnings** for the intentionally exercised legacy keychain
|
||||
APIs. This is not a zero-warning build and no warning suppression was added.
|
||||
|
||||
## Evidence and reproduction
|
||||
|
||||
Selected, home-path-redacted receipts are under
|
||||
`evidence/2026-11-10-pure-apple/`; `verification.json` retains the unchanged local
|
||||
raw receipt hashes. Actual raw output is
|
||||
`/tmp/opcua2-pure-apple-20261110b`; the first compile-only failure is
|
||||
`/tmp/opcua2-pure-apple-20261110a`. Raw native snapshots stay private/local.
|
||||
|
||||
```sh
|
||||
python3 scripts/validation/opcua-lifecycle-memory/native-retention/pure_apple.py \
|
||||
--output /tmp/opcua2-pure-apple-FRESH
|
||||
```
|
||||
|
||||
The output must be fresh. Fixed counts are not configurable; all generated paths
|
||||
are task-owned and no user/fleet PKI is used. This negative control refines the
|
||||
[A/B directory-load findings](README.md), and does not approve another experiment,
|
||||
a native workaround, a public upstream post or a long run.
|
||||
@@ -0,0 +1,123 @@
|
||||
// Diagnostic only: private SecKeychainCreate/Delete/CFRelease, with no certificate, PFX, SDK or CLR.
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Security/Security.h>
|
||||
#include <CommonCrypto/CommonDigest.h>
|
||||
#include <mach/mach.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define REPETITIONS 200
|
||||
static char root[PATH_MAX];
|
||||
static struct timespec started;
|
||||
static unsigned created_refs, released_refs, query_releases;
|
||||
static double elapsed(void) {
|
||||
struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
return (double)(now.tv_sec-started.tv_sec)+(double)(now.tv_nsec-started.tv_nsec)/1e9;
|
||||
}
|
||||
static void die(const char *message) {
|
||||
fprintf(stdout,"{\"kind\":\"failed\",\"message\":\"%s\",\"createdRefs\":%u,\"releasedRefs\":%u,\"elapsedSeconds\":%.6f}\n",message,created_refs,released_refs,elapsed());
|
||||
fflush(stdout); exit(1);
|
||||
}
|
||||
static void hex(const unsigned char *bytes, size_t length, char *out) {
|
||||
const char *alphabet="0123456789abcdef";
|
||||
for(size_t i=0;i<length;i++){out[i*2]=alphabet[bytes[i]>>4];out[i*2+1]=alphabet[bytes[i]&15];}
|
||||
out[length*2]=0;
|
||||
}
|
||||
static void wipe(void *memory, size_t length) {
|
||||
volatile unsigned char *bytes=(volatile unsigned char *)memory;
|
||||
while(length--)*bytes++=0;
|
||||
}
|
||||
static OSStatus keychain_path(SecKeychainRef keychain, char *path) {
|
||||
UInt32 length=PATH_MAX-1;
|
||||
OSStatus status=SecKeychainGetPath(keychain,&length,path);
|
||||
if(status==errSecSuccess && length<PATH_MAX)path[length]=0;
|
||||
return status;
|
||||
}
|
||||
struct fingerprint { char default_sha[65], search_sha[65]; CFIndex search_count; };
|
||||
static struct fingerprint fingerprint(const char *stage) {
|
||||
SecKeychainRef default_keychain=NULL;
|
||||
CFArrayRef search=NULL;
|
||||
struct fingerprint out; memset(&out,0,sizeof(out));
|
||||
char path[PATH_MAX]; memset(path,0,sizeof(path));
|
||||
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
|
||||
OSStatus default_status=SecKeychainCopyDefault(&default_keychain);
|
||||
OSStatus default_path_status=default_keychain?keychain_path(default_keychain,path):errSecInvalidKeychain;
|
||||
if(default_status==errSecSuccess && default_path_status==errSecSuccess){CC_SHA256(path,(CC_LONG)strlen(path),digest);hex(digest,sizeof(digest),out.default_sha);}
|
||||
if(default_keychain){CFRelease(default_keychain);query_releases++;default_keychain=NULL;}
|
||||
OSStatus search_status=SecKeychainCopySearchList(&search);
|
||||
OSStatus search_path_status=errSecSuccess;
|
||||
CC_SHA256_CTX hash; CC_SHA256_Init(&hash);
|
||||
if(search_status==errSecSuccess && search){
|
||||
out.search_count=CFArrayGetCount(search);
|
||||
uint64_t count=(uint64_t)out.search_count; CC_SHA256_Update(&hash,&count,sizeof(count));
|
||||
for(CFIndex i=0;i<out.search_count;i++){
|
||||
// CFArray values are borrowed. Releasing the owned array balances its element references.
|
||||
SecKeychainRef borrowed=(SecKeychainRef)CFArrayGetValueAtIndex(search,i);
|
||||
memset(path,0,sizeof(path)); OSStatus status=keychain_path(borrowed,path);
|
||||
if(status!=errSecSuccess){search_path_status=status;break;}
|
||||
uint32_t length=(uint32_t)strlen(path);CC_SHA256_Update(&hash,&length,sizeof(length));CC_SHA256_Update(&hash,path,length);
|
||||
}
|
||||
}
|
||||
CC_SHA256_Final(digest,&hash);hex(digest,sizeof(digest),out.search_sha);
|
||||
if(search){CFRelease(search);query_releases++;search=NULL;}
|
||||
fprintf(stdout,"{\"kind\":\"fingerprint\",\"stage\":\"%s\",\"defaultStatus\":%d,\"defaultPathStatus\":%d,\"searchStatus\":%d,\"searchPathStatus\":%d,\"defaultSha256\":\"%s\",\"searchSha256\":\"%s\",\"searchCount\":%ld,\"ownedQueryReferencesReleased\":%u}\n",stage,(int)default_status,(int)default_path_status,(int)search_status,(int)search_path_status,out.default_sha,out.search_sha,(long)out.search_count,query_releases);fflush(stdout);
|
||||
if(default_status || default_path_status || search_status || search_path_status)die("Read-only keychain fingerprint failed");
|
||||
return out;
|
||||
}
|
||||
static void checkpoint(const char *stage, unsigned operations) {
|
||||
struct mach_task_basic_info info; mach_msg_type_number_t count=MACH_TASK_BASIC_INFO_COUNT;
|
||||
kern_return_t status=task_info(mach_task_self(),MACH_TASK_BASIC_INFO,(task_info_t)&info,&count);
|
||||
if(status!=KERN_SUCCESS)die("task_info failed");
|
||||
struct timespec wall;clock_gettime(CLOCK_REALTIME,&wall);struct tm utc;gmtime_r(&wall.tv_sec,&utc);
|
||||
char stamp[40];strftime(stamp,sizeof(stamp),"%Y-%m-%dT%H:%M:%S",&utc);
|
||||
fprintf(stdout,"{\"kind\":\"checkpoint\",\"mode\":\"pure-apple\",\"stage\":\"%s\",\"operations\":%u,\"processId\":%d,\"utc\":\"%s.%03ldZ\",\"elapsedSeconds\":%.6f,\"rssBytes\":%llu,\"createdRefs\":%u,\"releasedRefs\":%u,\"managedRuntimePresent\":false}\n",stage,operations,getpid(),stamp,wall.tv_nsec/1000000,elapsed(),(unsigned long long)info.resident_size,created_refs,released_refs);fflush(stdout);
|
||||
char next[PATH_MAX];int n=snprintf(next,sizeof(next),"%s/continue-%s",root,stage);if(n<0 || (size_t)n>=sizeof(next))die("Checkpoint path overflow");
|
||||
while(access(next,F_OK)!=0){if(elapsed()>180)die("Checkpoint deadline exceeded");struct timespec pause={0,25000000};nanosleep(&pause,NULL);}
|
||||
}
|
||||
static int absent(const char *path){struct stat state;if(lstat(path,&state)==0)return 0;return errno==ENOENT;}
|
||||
static void operation(unsigned iteration) {
|
||||
unsigned char random[48];if(SecRandomCopyBytes(kSecRandomDefault,sizeof(random),random)!=errSecSuccess)die("Secure random failed");
|
||||
char suffix[33],password[65];hex(random,16,suffix);hex(random+16,32,password);wipe(random,sizeof(random));
|
||||
char path[PATH_MAX],backing[PATH_MAX];int n=snprintf(path,sizeof(path),"%s/tmp/native-retention-%s.keychain",root,suffix);
|
||||
if(n<0 || (size_t)n>=sizeof(path))die("Fixture path overflow");
|
||||
n=snprintf(backing,sizeof(backing),"%s-db",path);if(n<0 || (size_t)n>=sizeof(backing))die("Fixture backing path overflow");
|
||||
if(!absent(path)||!absent(backing))die("Generated private fixture path already exists");
|
||||
SecKeychainRef keychain=NULL;
|
||||
OSStatus create_status=SecKeychainCreate(path,(UInt32)strlen(password),password,false,NULL,&keychain);
|
||||
wipe(password,sizeof(password));
|
||||
OSStatus path_status=errSecInvalidKeychain,delete_status=INT32_MIN;
|
||||
int verified=0,released=0;
|
||||
if(keychain){
|
||||
created_refs++;
|
||||
char actual[PATH_MAX];memset(actual,0,sizeof(actual));path_status=keychain_path(keychain,actual);
|
||||
// Only a returned reference whose path is exactly this generated private fixture can be deleted.
|
||||
verified=path_status==errSecSuccess && (!strcmp(actual,path)||!strcmp(actual,backing));
|
||||
if(verified)delete_status=SecKeychainDelete(keychain);
|
||||
// Exactly one CFRelease for this returned owned reference, irrespective of delete status.
|
||||
CFRelease(keychain);keychain=NULL;released_refs++;released=1;
|
||||
}
|
||||
fprintf(stdout,"{\"kind\":\"operation\",\"iteration\":%u,\"createStatus\":%d,\"pathStatus\":%d,\"deleteStatus\":%d,\"returnedRef\":%s,\"privatePathVerified\":%s,\"releasedExactlyOnce\":%s}\n",iteration,(int)create_status,(int)path_status,(int)delete_status,released?"true":"false",verified?"true":"false",released?"true":"false");fflush(stdout);
|
||||
if(create_status || path_status || delete_status || !verified || !released)die("Private keychain lifecycle operation failed");
|
||||
}
|
||||
int main(int argc,char **argv) {
|
||||
clock_gettime(CLOCK_MONOTONIC,&started);
|
||||
if(argc!=2 || !realpath(argv[1],root))die("Expected existing private fixture root");
|
||||
struct stat state;if(stat(root,&state) || !S_ISDIR(state.st_mode) || (state.st_mode&077))die("Fixture root must be mode0700");
|
||||
struct fingerprint before=fingerprint("before");checkpoint("before",0);
|
||||
for(unsigned i=1;i<=REPETITIONS;i++){operation(i);if(i==1)checkpoint("first",i);}
|
||||
checkpoint("repeated",REPETITIONS);
|
||||
struct timespec idle={5,0};while(nanosleep(&idle,&idle)!=0){if(errno!=EINTR)die("Idle delay failed");}
|
||||
struct fingerprint after=fingerprint("after");
|
||||
checkpoint("idle",REPETITIONS);
|
||||
int unchanged=!strcmp(before.default_sha,after.default_sha)&&!strcmp(before.search_sha,after.search_sha)&&before.search_count==after.search_count;
|
||||
if(!unchanged || created_refs!=REPETITIONS || released_refs!=REPETITIONS)die("Fingerprint or native ownership guard failed");
|
||||
fprintf(stdout,"{\"kind\":\"complete\",\"operations\":200,\"createdRefs\":%u,\"releasedRefs\":%u,\"readOnlyDefaultAndSearchListUnchanged\":true,\"elapsedSeconds\":%.6f,\"fullSoakGate\":false}\n",created_refs,released_refs,elapsed());fflush(stdout);return 0;
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
# macOS private-key reload native retention — #799
|
||||
|
||||
The bounded control identifies **configured-directory private-key loading** as a
|
||||
trigger for continuing `Security::AppleDatabase` heap inventory on this machine,
|
||||
while observed SDK certificate-core ownership and tracked .NET temporary-keychain
|
||||
ownership return to zero. No narrow built-in application ownership correction was
|
||||
found that preserves the current raw-session factory, Directory PKI, certificate
|
||||
selection/update behavior and certificate properties. **#799 remains open.** This
|
||||
is private reproduction/support material, not an approved production mitigation.
|
||||
|
||||
Only this new subfolder was changed. No product source, trust policy, SDK version,
|
||||
certificate factory/crypto provider, exportability, key storage format, existing
|
||||
PKI or resource budget was changed. No public upstream issue/message was posted.
|
||||
|
||||
## Reviewed, executed control
|
||||
|
||||
Astra reviewed the actual source and installed-runtime decompile before execution,
|
||||
approved one fixed **200-acquisition** run per arm as attribution-only evidence,
|
||||
and afterward independently matched all ten raw heap counts/bytes, checkpoint and
|
||||
completion counts, source hashes, installed crypto hash and eight SDK DLL hashes.
|
||||
The configured-store versus registry distinction below is supported by inspected
|
||||
upstream regression source; that upstream regression test was **not executed**.
|
||||
|
||||
Both arms ran in separate fresh processes on macOS **26.5 arm64**, .NET SDK
|
||||
**10.0.403**, runtime **10.0.14**. A normal SDK ApplicationInstance created one RSA
|
||||
application PKI; its persisted tree was copied into separate private arm roots.
|
||||
Both used the same application URI, public DER identity and private key. Normal
|
||||
SDK configuration validation/certificate checks and teardown were identical. The
|
||||
loop called these public APIs, disposing each returned owner before checkpoints:
|
||||
|
||||
| Arm | Public acquisition, repeated exactly 200 times |
|
||||
|---|---|
|
||||
| Store | `SecurityConfiguration.FindApplicationCertificateAsync(Basic256Sha256, privateKey: true, ...)` |
|
||||
| Registry | `ICertificateRegistry.AcquireApplicationCertificateBySecurityPolicy(Basic256Sha256)` |
|
||||
|
||||
Each arm sampled ready, first acquisition, acquisition 200, application disposed,
|
||||
and after a fixed five-second idle. Every checkpoint used the same full-GC /
|
||||
finalizer / full-GC / 250 ms schedule, then a parent-controlled heap/lsof barrier.
|
||||
Matched initial/first/last RSA sign-and-verify checks confirmed private-key use.
|
||||
No iteration called `AsX509Certificate2`; no network, server or session was opened.
|
||||
`AutoAcceptUntrustedCertificates=false` stayed configured; this key-acquisition
|
||||
control makes no additional peer-validation wire claim.
|
||||
|
||||
Allocation-stack tracking was enabled in **both** arms, so its allocation/GC cost
|
||||
is part of these observations. The 180-second child cancellation/checkpoint
|
||||
limit is not a strict total wall deadline: a running heap probe (20 seconds),
|
||||
lsof probe (five seconds), final wait and bounded terminate/kill reap can add
|
||||
bounded overhead. Stdout is a file, with no unbounded `communicate` or pipe drain.
|
||||
The recorded artifact-creation-to-result-write wall interval was **34.699 seconds**
|
||||
(filesystem timestamps, not a monotonic acceptance gate). Ready-to-idle checkpoint
|
||||
intervals were **21.504 seconds store** and **9.123 seconds registry**, including
|
||||
probe barriers. Both process exits were zero and all children were reaped.
|
||||
|
||||
## Native inventory and ownership results
|
||||
|
||||
| Checkpoint | Store AppleDatabase | Registry AppleDatabase | Tracked runtime keychains, each arm | Outstanding SDK cores, each arm | Temporary keychain files, each arm |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| Ready | 3 | 3 | 1 | 1 | 1 |
|
||||
| First acquisition | 4 | 3 | 1 | 1 | 1 |
|
||||
| Acquisition 200 | 203 | 3 | 1 | 1 | 1 |
|
||||
| Application disposed | 203 | 3 | 0 | 0 | 0 |
|
||||
| Five-second idle | 203 | 3 | 0 | 0 | 0 |
|
||||
|
||||
Configured PKI FDs and temporary-keychain FDs were **zero at every checkpoint**.
|
||||
One other task-TMPDIR descriptor remained in each process; it is reported
|
||||
separately and is not labelled a keychain descriptor. Total numeric FDs were
|
||||
110 ready / 112 final in both arms. Manager disposal was observed through an
|
||||
exact-SDK, read-only `m_disposed` boolean probe. Store certificate-core counters
|
||||
finished **407 created / 407 disposed**; registry finished **7 / 7**. These public
|
||||
counters concern underlying certificate cores, not every AddRef wrapper.
|
||||
|
||||
The exact installed runtime's internal temporary-keychain dictionary was read
|
||||
under the same dictionary monitor, with a one-second bounded acquisition and
|
||||
exact informational-version/type/field guards. Only its count was read; no native
|
||||
pointer, handle, key or certificate material was enumerated or modified. Its
|
||||
count returning zero establishes no remaining entries in that managed lookup.
|
||||
The installed cleanup code removes the entry **before** calling native
|
||||
`SecKeychainDelete`, suppresses delete errors, and then releases the CF handle.
|
||||
The actual installed base `SafeKeychainHandle.ReleaseHandle` was separately
|
||||
decompiled and does call `Interop.CoreFoundation.CFRelease(handle)`, then zeros
|
||||
its handle. Apple's [SecKeychainDelete documentation](https://developer.apple.com/documentation/security/seckeychaindelete(_:).md)
|
||||
distinguishes file/keychain deletion from releasing keychain-object memory; this
|
||||
inspection finds the separate CFRelease in the installed runtime, not an obvious
|
||||
missing base-release implementation. These are inspected calls, not native
|
||||
return-status instrumentation. Therefore lookup zero does **not** prove that
|
||||
native deletion succeeded.
|
||||
|
||||
| Memory observation | Store ready → final idle | Registry ready → final idle |
|
||||
|---|---:|---:|
|
||||
| Managed live bytes | 831,080 → 1,222,192 | 831,128 → 1,130,616 |
|
||||
| RSS bytes | 87,621,632 → 115,130,368 | 87,654,400 → 97,927,168 |
|
||||
| Compiled methods | 1,192 → 4,638 | 1,192 → 2,251 |
|
||||
|
||||
RSS includes different JIT, tracking, GC and allocator effects; this comparison
|
||||
is not an exclusive native-byte attribution or a performance budget. The native
|
||||
class's shallow heap bytes were 2,304 → 155,904 in store and 2,304 → 2,304 in
|
||||
registry (768 bytes per classified object). Shallow class bytes exclude owned
|
||||
allocations and do not explain the entire RSS difference. `PrivateMemorySize64`
|
||||
returned zero on this runtime and is not used as a private-memory result.
|
||||
|
||||
`heap --noContent -s` reports allocations classified as the C++
|
||||
`Security::AppleDatabase` class. Apple's published file-database source declares
|
||||
that class as a `Database` subclass with database open/close/delete methods and
|
||||
owned file/modifier state; it is not a file-descriptor counter. The published
|
||||
source is conceptual evidence, **not** a claim that it is the exact source build
|
||||
of this installed Security.framework. Retaining classified heap objects after
|
||||
observed closure is different from proving an open handle, failed native delete,
|
||||
or the identity of the retaining native owner. See the pinned
|
||||
[Apple class definition](https://github.com/apple-oss-distributions/Security/blob/db15acbe6a7f257a859ad9a3bb86097bfe0679d9/OSX/libsecurity_filedb/lib/AppleDatabase.h#L485).
|
||||
|
||||
The leaf DER SHA-256 was identical before/after and across arms:
|
||||
`fadcc11ec694732b82fe4dc47bb41bce6023d113339e53aca9fee5b079f200b2`.
|
||||
After samples and process exits, every seed/arm PKI, TMPDIR and data fixture
|
||||
was removed; cleanup receipt is true. Raw native snapshots stay local and private.
|
||||
|
||||
## Exact SDK path and public API limits
|
||||
|
||||
The inspected SDK checkout is the clean **2.0.1** tag at commit
|
||||
`07e62f72500cc0923a2bab940653c8c1116fa286`; the control references immutable actual
|
||||
published SDK 2.0.1 DLLs, with all eight hashes in `evidence/2026-11-10/provenance.json`.
|
||||
|
||||
| Source, pinned to the exact SDK commit | Relevant behavior |
|
||||
|---|---|
|
||||
| [DefaultSessionFactory.CreateChannelAsync](https://github.com/OPCFoundation/UA-.NETStandard/blob/07e62f72500cc0923a2bab940653c8c1116fa286/src/Opc.Ua.Client/Session/DefaultSessionFactory.cs#L406) | Loads a private certificate through the internal overload that defaults registry use to false. |
|
||||
| [DefaultSessionFactory private CreateAsync](https://github.com/OPCFoundation/UA-.NETStandard/blob/07e62f72500cc0923a2bab940653c8c1116fa286/src/Opc.Ua.Client/Session/DefaultSessionFactory.cs#L579) | Constructs the raw Session with null certificate/chain. |
|
||||
| [Session.LoadInstanceCertificateAsync](https://github.com/OPCFoundation/UA-.NETStandard/blob/07e62f72500cc0923a2bab940653c8c1116fa286/src/Opc.Ua.Client/Session/Session.cs#L6335) | Opening that Session independently loads its private certificate; registry preference depends on a channel manager, absent in the current raw factory. |
|
||||
| [Session.LoadInstanceCertificateEntryAsync](https://github.com/OPCFoundation/UA-.NETStandard/blob/07e62f72500cc0923a2bab940653c8c1116fa286/src/Opc.Ua.Client/Session/Session.cs#L6432) | Documents configured-store selection for unmanaged callers and registry preference for managed callers. |
|
||||
| [SecurityConfiguration.FindApplicationCertificateAsync](https://github.com/OPCFoundation/UA-.NETStandard/blob/07e62f72500cc0923a2bab940653c8c1116fa286/src/Opc.Ua.Core/Security/Certificates/SecurityConfiguration.cs#L182) | Private-key branch calls registry-less CertificateIdentifierResolver.LoadPrivateKeyAsync. |
|
||||
| [CertificateIdentifierResolver.LoadPrivateKeyCoreAsync](https://github.com/OPCFoundation/UA-.NETStandard/blob/07e62f72500cc0923a2bab940653c8c1116fa286/src/Opc.Ua.Core/Security/Certificates/CertificateIdentifierResolver.cs#L249) | Opens the configured store and asks it to load the private key; returned reference is caller-owned. |
|
||||
| [DirectoryCertificateStore.LoadPrivateKeyAsync](https://github.com/OPCFoundation/UA-.NETStandard/blob/07e62f72500cc0923a2bab940653c8c1116fa286/src/Opc.Ua.Core/Security/Certificates/DirectoryCertificateStore.cs#L758) | For PFX, macOS storage flags omit EphemeralKeySet and include Exportable, then import through X509CertificateLoader.LoadPkcs12. No public per-store private-key cache/flag setting is offered. |
|
||||
| [ApplicationConfiguration.ValidateAsync](https://github.com/OPCFoundation/UA-.NETStandard/blob/07e62f72500cc0923a2bab940653c8c1116fa286/src/Opc.Ua.Core/Stack/Configuration/ApplicationConfiguration.cs#L735), [ApplicationInstance.CheckOrCreateCertificateAsync](https://github.com/OPCFoundation/UA-.NETStandard/blob/07e62f72500cc0923a2bab940653c8c1116fa286/src/Opc.Ua.Configuration/ApplicationInstance.cs#L425), [ServerBase.OnServerStartingAsync](https://github.com/OPCFoundation/UA-.NETStandard/blob/07e62f72500cc0923a2bab940653c8c1116fa286/src/Opc.Ua.Core/Stack/Server/ServerBase.cs#L1887) | Application validation, normal own-certificate checking and server bring-up also load configured private certificates. Checking disposes its temporary certificate; registry replacement disposes old entries. |
|
||||
| [Certificate.AsX509Certificate2](https://github.com/OPCFoundation/UA-.NETStandard/blob/07e62f72500cc0923a2bab940653c8c1116fa286/src/Opc.Ua.Security.Certificates/X509Certificate/Certificate.cs#L258) | Nonvirtual export/reimport copy for exportable private keys. Existing isolated controls demonstrate its PFX retention trigger, but prior sampled real-workload stacks identified directory loading as the dominant sampled import path. |
|
||||
|
||||
`CertificateIdentifier` has **no Certificate preload/cache property** and only a
|
||||
public default constructor. Actual-binary public API inventory is retained in
|
||||
`seed-api-inventory.jsonl`. `RawData` carries public encoded certificate material;
|
||||
its setter parses `Certificate.FromRawData` and derives metadata. It cannot carry
|
||||
the reusable private Certificate owner needed by the raw private-key load path.
|
||||
The [exact-tag manager documentation](https://github.com/OPCFoundation/UA-.NETStandard/blob/07e62f72500cc0923a2bab940653c8c1116fa286/docs/CertificateManager.md#materializing-a-certificate-from-a-certificateidentifier)
|
||||
explicitly places materialized ownership in the manager registry.
|
||||
|
||||
The public registry acquisition API is reference-counted and supported. However,
|
||||
substituting it in the raw session path changes certificate selection semantics:
|
||||
[UnmanagedCertificateLoadHonorsAnExplicitConfiguredStoreReplacementAsync](https://github.com/OPCFoundation/UA-.NETStandard/blob/07e62f72500cc0923a2bab940653c8c1116fa286/tests/Opc.Ua.Client.Tests/Session/SessionCertificateRotationRegressionTests.cs#L198)
|
||||
requires a configured replacement to win over an older registry certificate and
|
||||
verifies the registry API is never called. This is **inspected upstream test
|
||||
source, not a test run here**. A cache/factory rewrite must additionally preserve
|
||||
update/removal visibility, cancellation/fault cleanup, concurrent owners,
|
||||
reconnect and certificate/issuer-chain selection; the B arm proves none of those
|
||||
replacement semantics. ChannelManagerSessionFactory changes architecture and was
|
||||
not used. Overriding/reimplementing channel and session construction is not a
|
||||
narrow application disposal fix.
|
||||
|
||||
Production source at `ed60970fc294ab171a80cda1b57942a7130b4c39` was inspected:
|
||||
[OpcUaApplicationHost](../../../../src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OpcUaApplicationHost.cs)
|
||||
assigns configuration ownership before async validation and disposes its
|
||||
ApplicationInstance; Shared transfers manager lifetime to the session adapter
|
||||
and disposes failed configurations; driver/browser release their owned
|
||||
ApplicationInstance after session cleanup. No missing owner was identified in
|
||||
those paths. The store arm already keeps its one application/configuration and
|
||||
registry alive across all 200 acquisitions, so simply lengthening that
|
||||
application owner cannot remove these private-key imports. Existing full-client
|
||||
lifecycle disposal checks are complementary
|
||||
coordinator evidence, not additional executions in this control.
|
||||
|
||||
## Runtime dependency and upstream-ready boundary
|
||||
|
||||
Actual installed `System.Security.Cryptography.dll` informational version:
|
||||
`10.0.14+0427b9abbead3fa64c6655eb4290c537397131fa`.
|
||||
SHA-256: `f4957d7218a004a27bf8964e45147df6a88092c6e7344582082840d62c6c579a`.
|
||||
|
||||
The official .NET `v10.0.14` tag resolves to
|
||||
`6aa72daa24f66c01c3d7b6a32519e9d21396deec`, different from the installed binary's
|
||||
informational commit. It is not presented as exact installed source. Read-only
|
||||
ILSpy inspection of the **actual binary** verified the temporary-keychain
|
||||
creation, dictionary/monitor and release behavior used by diagnostics. Paths and
|
||||
hashes of those retained local decompilations are in `verification.json`.
|
||||
|
||||
The public runtime source provides matching context for
|
||||
[temporary keychain creation during non-persisted import and unsupported EphemeralKeySet](https://github.com/dotnet/runtime/blob/6aa72daa24f66c01c3d7b6a32519e9d21396deec/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.macOS.cs)
|
||||
and [tracked-keychain release ordering](https://github.com/dotnet/runtime/blob/6aa72daa24f66c01c3d7b6a32519e9d21396deec/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.macOS.cs).
|
||||
Microsoft documents that macOS PFX private-key loading requires a keychain and
|
||||
cannot use EphemeralKeySet in its [cross-platform cryptography guide](https://learn.microsoft.com/en-us/dotnet/standard/security/cross-platform-cryptography#read-a-pkcs12pfx-file).
|
||||
Changing those flags, exportability, storage format or native crypto was not an
|
||||
application mitigation attempted here.
|
||||
|
||||
The remaining dependency is an upstream/platform investigation of why the
|
||||
classified native file-database objects remain after observed SDK owner release,
|
||||
tracked runtime owner release and backing-file cleanup, and whether there is a
|
||||
supported correction at that boundary. Native delete status and the retaining
|
||||
C++ owner are still unknown. This control does not identify a specific .NET or
|
||||
Apple defect/fix. An upstream SDK improvement would also need to avoid redundant
|
||||
imports **without losing the existing raw configured-store semantics**. No
|
||||
supported application-only correction meeting the stated constraints was proven.
|
||||
|
||||
## Private reproduction and evidence
|
||||
|
||||
```sh
|
||||
python3 scripts/validation/opcua-lifecycle-memory/native-retention/run.py \
|
||||
--references /tmp/opcua2-publish-hg100-20261110/linux-arm64/host \
|
||||
--output /tmp/opcua2-native-reload-FRESH
|
||||
```
|
||||
|
||||
This builds only the standalone control against existing actual SDK DLLs, never
|
||||
rebuilds product code, requires the exact installed runtime diagnostic layout,
|
||||
and refuses an existing output directory. The fixed repetition count is not
|
||||
configurable. Use a private fresh root and serialize with other ownership/native
|
||||
controls. The checked-in selected receipts are in `evidence/2026-11-10/`;
|
||||
raw artifacts are `/tmp/opcua2-native-reload-20261110a`. Raw heap/lsof output and
|
||||
any failed-run private fixture material must not be redistributed wholesale.
|
||||
|
||||
No long run, deployment, trust weakening, SDK fork/version change, budget change,
|
||||
public upstream post or #799 closure is authorized or claimed by these results.
|
||||
|
||||
A separately reviewed [plain native Apple empty-keychain control](PURE_APPLE.md)
|
||||
then executed 200 successful create/delete/CFRelease operations and kept native
|
||||
AppleDatabase at 1 throughout. That negative result rules out empty native
|
||||
create/delete/release alone as sufficient here; it does not identify the
|
||||
private-key/certificate import retaining owner.
|
||||
@@ -0,0 +1,176 @@
|
||||
using System.Diagnostics;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Runtime;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Configuration;
|
||||
using Opc.Ua.Security.Certificates;
|
||||
|
||||
internal static class ReloadControl
|
||||
{
|
||||
const string Uri = "urn:localhost:NativeRetention.Control";
|
||||
const int Repetitions = 200;
|
||||
static readonly byte[] Challenge = Encoding.UTF8.GetBytes("native-retention-diagnostic-only");
|
||||
static string Root = "";
|
||||
static string Hash(Certificate cert) => Convert.ToHexString(SHA256.HashData(cert.RawData)).ToLowerInvariant();
|
||||
static void Require(bool yes, string message) { if (!yes) throw new InvalidOperationException(message); }
|
||||
static ApplicationConfiguration Configuration(string pki, ITelemetryContext telemetry) => new(telemetry)
|
||||
{
|
||||
ApplicationName = "NativeRetention.Control", ApplicationUri = Uri, ApplicationType = ApplicationType.Client,
|
||||
SecurityConfiguration = new SecurityConfiguration
|
||||
{
|
||||
ApplicationCertificate = new CertificateIdentifier { StoreType = CertificateStoreType.Directory,
|
||||
StorePath = Path.Combine(pki, "own"), SubjectName = "CN=NativeRetention.Control" },
|
||||
TrustedPeerCertificates = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(pki, "trusted") },
|
||||
TrustedIssuerCertificates = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(pki, "issuers") },
|
||||
RejectedCertificateStore = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(pki, "rejected") },
|
||||
AutoAcceptUntrustedCertificates = false,
|
||||
},
|
||||
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 15000 },
|
||||
};
|
||||
static async Task<ApplicationInstance> Open(string pki, ITelemetryContext telemetry, CancellationToken ct)
|
||||
{
|
||||
var app = new ApplicationInstance(telemetry) { ApplicationType = ApplicationType.Client,
|
||||
ApplicationName = "NativeRetention.Control", ApplicationConfiguration = Configuration(pki, telemetry) };
|
||||
try
|
||||
{
|
||||
await app.ApplicationConfiguration!.ValidateAsync(ApplicationType.Client, ct);
|
||||
Require(await app.CheckApplicationInstanceCertificatesAsync(true, null, ct), "Normal application certificate check failed");
|
||||
return app;
|
||||
}
|
||||
catch { await app.DisposeAsync(); throw; }
|
||||
}
|
||||
static bool ManagerDisposed(ICertificateManager manager)
|
||||
{
|
||||
Require(manager.GetType().FullName == "Opc.Ua.CertificateManager" && manager.GetType().Assembly.GetName().Version?.ToString() == "2.0.1.0", "Exact SDK diagnostic layout changed");
|
||||
var field = manager.GetType().GetField("m_disposed", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
|
||||
Require(field?.FieldType == typeof(bool), "Exact SDK disposal bool missing");
|
||||
return (bool)field!.GetValue(manager)!;
|
||||
}
|
||||
static int RuntimeTemporaryKeychainCount()
|
||||
{
|
||||
var assembly = typeof(X509Certificate2).Assembly;
|
||||
Require(assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion == "10.0.14+0427b9abbead3fa64c6655eb4290c537397131fa", "Exact runtime diagnostic build changed");
|
||||
var type = assembly.GetType("System.Security.Cryptography.Apple.SafeTemporaryKeychainHandle", throwOnError: true)!;
|
||||
var field = type.GetField("s_lookup", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
|
||||
Require(field is not null && field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(Dictionary<,>)
|
||||
&& field.FieldType.GetGenericArguments()[0] == typeof(IntPtr) && field.FieldType.GetGenericArguments()[1] == type,
|
||||
"Exact runtime keychain dictionary layout changed");
|
||||
var lookup = (IDictionary)(field!.GetValue(null) ?? throw new InvalidOperationException("Runtime keychain dictionary absent"));
|
||||
Require(Monitor.TryEnter(lookup, TimeSpan.FromSeconds(1)), "Runtime keychain monitor did not become available");
|
||||
try { return lookup.Count; }
|
||||
finally { Monitor.Exit(lookup); }
|
||||
}
|
||||
static void ValidateKey(Certificate certificate, string expected, bool sign)
|
||||
{
|
||||
Require(certificate.HasPrivateKey && Hash(certificate) == expected, "Wrong/missing private certificate");
|
||||
if (!sign) return;
|
||||
using var privateKey = certificate.GetRSAPrivateKey();
|
||||
using var publicKey = certificate.GetRSAPublicKey();
|
||||
Require(privateKey is not null && publicKey is not null, "Expected normal SDK RSA application certificate");
|
||||
var signature = privateKey!.SignData(Challenge, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
Require(publicKey!.VerifyData(Challenge, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1), "Private key mismatch");
|
||||
}
|
||||
static async Task Stage(string mode, string stage, int operations, bool disposed, CancellationToken ct)
|
||||
{
|
||||
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
|
||||
await Task.Delay(250, ct);
|
||||
using var process = Process.GetCurrentProcess(); process.Refresh();
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { kind = "checkpoint", mode, stage, operations, processId = Environment.ProcessId,
|
||||
utc = DateTimeOffset.UtcNow, managerDisposed = disposed, managedBytes = GC.GetTotalMemory(false), rssBytes = process.WorkingSet64,
|
||||
privateBytes = process.PrivateMemorySize64, allocatedBytes = GC.GetTotalAllocatedBytes(), compiledMethods = JitInfo.GetCompiledMethodCount(),
|
||||
gc = new[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) },
|
||||
certificateCoresCreated = Certificate.InstancesCreated, certificateCoresDisposed = Certificate.InstancesDisposed,
|
||||
certificateCoresOutstanding = Certificate.InstancesLeaked,
|
||||
runtimeTrackedTemporaryKeychains = RuntimeTemporaryKeychainCount() }));
|
||||
Console.Out.Flush();
|
||||
while (!File.Exists(Path.Combine(Root, "continue-" + stage))) await Task.Delay(25, ct);
|
||||
}
|
||||
static async Task Arm(string mode, CancellationToken ct)
|
||||
{
|
||||
var telemetry = DefaultTelemetry.Create(_ => { });
|
||||
var pki = Path.Combine(Root, "pki");
|
||||
var before = Directory.GetFiles(pki, "*.der", SearchOption.AllDirectories).Where(p => p.Contains(Path.DirectorySeparatorChar + "own" + Path.DirectorySeparatorChar + "certs" + Path.DirectorySeparatorChar, StringComparison.Ordinal))
|
||||
.Select(p => Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(p)))).Order().ToArray();
|
||||
Require(before.Length == 1, "Expected one copied own DER certificate");
|
||||
ApplicationInstance? app = null;
|
||||
try
|
||||
{
|
||||
app = await Open(pki, telemetry, ct);
|
||||
var config = app.ApplicationConfiguration!;
|
||||
var manager = config.CertificateManager;
|
||||
string expected;
|
||||
using (var entry = manager.AcquireApplicationCertificateBySecurityPolicy(SecurityPolicies.Basic256Sha256))
|
||||
{
|
||||
Require(entry is not null, "Normal initialization did not populate registry");
|
||||
expected = Hash(entry!.Certificate); ValidateKey(entry.Certificate, expected, true);
|
||||
}
|
||||
await Stage(mode, "ready", 0, ManagerDisposed(manager), ct);
|
||||
for (var i = 1; i <= Repetitions; i++)
|
||||
{
|
||||
if (mode == "store")
|
||||
{
|
||||
using var certificate = await config.SecurityConfiguration.FindApplicationCertificateAsync(
|
||||
SecurityPolicies.Basic256Sha256, true, telemetry, ct).WaitAsync(ct);
|
||||
Require(certificate is not null, "Configured-store acquisition failed");
|
||||
ValidateKey(certificate!, expected, i is 1 or Repetitions);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var entry = manager.AcquireApplicationCertificateBySecurityPolicy(SecurityPolicies.Basic256Sha256);
|
||||
Require(entry is not null, "Registry acquisition failed");
|
||||
ValidateKey(entry!.Certificate, expected, i is 1 or Repetitions);
|
||||
}
|
||||
if (i is 1 or Repetitions) await Stage(mode, i == 1 ? "first" : "repeated", i, ManagerDisposed(manager), ct);
|
||||
}
|
||||
await app.DisposeAsync().AsTask().WaitAsync(ct); app = null;
|
||||
await Stage(mode, "disposed", Repetitions, ManagerDisposed(manager), ct);
|
||||
await Task.Delay(TimeSpan.FromSeconds(5), ct);
|
||||
await Stage(mode, "idle", Repetitions, ManagerDisposed(manager), ct);
|
||||
// Preserve managed and native post-disposal observations before any ownership assertion rejects the arm.
|
||||
Require(ManagerDisposed(manager), "Application manager not disposed");
|
||||
Require(Certificate.InstancesLeaked == 0, "SDK certificate cores remain after owner disposal");
|
||||
Require(RuntimeTemporaryKeychainCount() == 0, "Runtime tracked temporary keychain owners remain after application disposal");
|
||||
var after = Directory.GetFiles(pki, "*.der", SearchOption.AllDirectories).Where(p => p.Contains(Path.DirectorySeparatorChar + "own" + Path.DirectorySeparatorChar + "certs" + Path.DirectorySeparatorChar, StringComparison.Ordinal))
|
||||
.Select(p => Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(p)))).Order().ToArray();
|
||||
Require(after.SequenceEqual(before), "Own application certificate changed");
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { kind = "complete", mode, operations = Repetitions, sameLeafSha256 = expected,
|
||||
certificateUnchanged = true, managerDisposed = true, certificateCoresOutstanding = Certificate.InstancesLeaked, fullSoakGate = false }));
|
||||
}
|
||||
finally { if (app is not null) await app.DisposeAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); }
|
||||
}
|
||||
public static async Task<int> Main(string[] args)
|
||||
{
|
||||
Root = Path.GetFullPath(args[1]); Directory.CreateDirectory(Root);
|
||||
using var deadline = new CancellationTokenSource(TimeSpan.FromSeconds(180));
|
||||
try
|
||||
{
|
||||
Require(OperatingSystem.IsMacOS(), "This control requires macOS native inventory");
|
||||
Require(Environment.Version.ToString() == "10.0.14", "Matched runtime must be .NET 10.0.14");
|
||||
if (args[0] == "seed")
|
||||
{
|
||||
await using var app = await Open(Path.Combine(Root, "pki"), DefaultTelemetry.Create(_ => { }), deadline.Token);
|
||||
using var entry = app.ApplicationConfiguration!.CertificateManager.AcquireApplicationCertificateBySecurityPolicy(SecurityPolicies.Basic256Sha256);
|
||||
Require(entry is not null, "Seed registry not populated");
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { kind = "seed", leafSha256 = Hash(entry!.Certificate), runtime = Environment.Version.ToString(),
|
||||
identifierPublicProperties = typeof(CertificateIdentifier).GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(p => p.Name).Order().ToArray(),
|
||||
identifierPublicConstructors = typeof(CertificateIdentifier).GetConstructors().Select(c => c.ToString()).ToArray(),
|
||||
cryptographyRuntime = new { file = typeof(X509Certificate2).Assembly.Location,
|
||||
informationalVersion = typeof(X509Certificate2).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion,
|
||||
sha256 = Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(typeof(X509Certificate2).Assembly.Location))).ToLowerInvariant() } }));
|
||||
return 0;
|
||||
}
|
||||
Require(args[0] is "store" or "registry", "Unknown control mode");
|
||||
await Arm(args[0], deadline.Token); return 0;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { kind = "failed", errorType = error.GetType().FullName, message = error.Message }));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/PureAppleControl.c:40:21: warning: 'SecKeychainGetPath' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
|
||||
40 | OSStatus status=SecKeychainGetPath(keychain,&length,path);
|
||||
| ^
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:474:10: note: 'SecKeychainGetPath' has been explicitly marked deprecated here
|
||||
474 | OSStatus SecKeychainGetPath(SecKeychainRef __nullable keychain, UInt32 *ioPathLength, char *pathName)
|
||||
| ^
|
||||
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/PureAppleControl.c:51:29: warning: 'SecKeychainCopyDefault' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
|
||||
51 | OSStatus default_status=SecKeychainCopyDefault(&default_keychain);
|
||||
| ^
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:384:10: note: 'SecKeychainCopyDefault' has been explicitly marked deprecated here
|
||||
384 | OSStatus SecKeychainCopyDefault(SecKeychainRef * __nonnull CF_RETURNS_RETAINED keychain)
|
||||
| ^
|
||||
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/PureAppleControl.c:55:28: warning: 'SecKeychainCopySearchList' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
|
||||
55 | OSStatus search_status=SecKeychainCopySearchList(&search);
|
||||
| ^
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:404:10: note: 'SecKeychainCopySearchList' has been explicitly marked deprecated here
|
||||
404 | OSStatus SecKeychainCopySearchList(CFArrayRef * __nonnull CF_RETURNS_RETAINED searchList)
|
||||
| ^
|
||||
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/PureAppleControl.c:94:28: warning: 'SecKeychainCreate' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
|
||||
94 | OSStatus create_status=SecKeychainCreate(path,(UInt32)strlen(password),password,false,NULL,&keychain);
|
||||
| ^
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:309:10: note: 'SecKeychainCreate' has been explicitly marked deprecated here
|
||||
309 | OSStatus SecKeychainCreate(const char *pathName, UInt32 passwordLength, const void * __nullable password, Boolean promptUser, SecAccessRef __nullable initialAccess, SecKeychainRef * __nonnull CF_RETURNS_RETAINED keychain)
|
||||
| ^
|
||||
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/PureAppleControl.c:103:35: warning: 'SecKeychainDelete' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
|
||||
103 | if(verified)delete_status=SecKeychainDelete(keychain);
|
||||
| ^
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:319:10: note: 'SecKeychainDelete' has been explicitly marked deprecated here
|
||||
319 | OSStatus SecKeychainDelete(SecKeychainRef __nullable keychainOrArray)
|
||||
| ^
|
||||
5 warnings generated.
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"remainingOwnedKeychainFiles": 0
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ownedFixtureRootRemoved": true
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"sourceSha256": "78354b154778fa61a7fe67fdef3fab6ea04ac5bdf2ae8190a2e7414781e1397d",
|
||||
"runnerSourceSha256": "1b2148f1c20578529355379ab94b439931d7241608a5d5effce8fc86f4af7bc6",
|
||||
"compileCommand": [
|
||||
"clang",
|
||||
"-std=c11",
|
||||
"-O0",
|
||||
"-g",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/PureAppleControl.c",
|
||||
"-framework",
|
||||
"Security",
|
||||
"-framework",
|
||||
"CoreFoundation",
|
||||
"-o",
|
||||
"/private/tmp/opcua2-pure-apple-20261110b/PureAppleControl"
|
||||
],
|
||||
"compiler": "Apple clang version 21.0.0 (clang-2100.0.123.102)\nTarget: arm64-apple-darwin25.5.0\nThread model: posix\nInstalledDir: /Library/Developer/CommandLineTools/usr/bin\n",
|
||||
"os": "ProductName:\t\tmacOS\nProductVersion:\t\t26.5.2\nBuildVersion:\t\t25F84\n",
|
||||
"architecture": "arm64",
|
||||
"repetitions": 200,
|
||||
"idleSeconds": 5,
|
||||
"stages": [
|
||||
"before",
|
||||
"first",
|
||||
"repeated",
|
||||
"idle"
|
||||
],
|
||||
"fullSoakGate": false,
|
||||
"readOnlyKeychainQueries": [
|
||||
"SecKeychainCopyDefault",
|
||||
"SecKeychainCopySearchList",
|
||||
"SecKeychainGetPath"
|
||||
],
|
||||
"doesNotCallAnyDefaultOrSearchListSetter": true,
|
||||
"binarySha256": "6c87f4394c0e51bcf26f27187628d0a867feacf9b8c7a20fb94ab42baeb1d087",
|
||||
"linkedLibraries": "/private/tmp/opcua2-pure-apple-20261110b/PureAppleControl:\n\t/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 61901.101.4)\n\t/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 4424.1.255)\n\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)\n"
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"status": "passed",
|
||||
"exitCode": 0,
|
||||
"wallSecondsIncludingProbes": 9.287588333012536,
|
||||
"samples": [
|
||||
{
|
||||
"kind": "checkpoint",
|
||||
"mode": "pure-apple",
|
||||
"stage": "before",
|
||||
"operations": 0,
|
||||
"processId": 93020,
|
||||
"utc": "2026-11-10T21:47:09.322Z",
|
||||
"elapsedSeconds": 0.004359,
|
||||
"rssBytes": 7372800,
|
||||
"createdRefs": 0,
|
||||
"releasedRefs": 0,
|
||||
"managedRuntimePresent": false,
|
||||
"AppleDatabaseCount": 1,
|
||||
"AppleDatabaseShallowBytes": 768,
|
||||
"AppleDatabaseClassAbsent": false,
|
||||
"databaseManagerClassPresent": true,
|
||||
"fdCount": 3,
|
||||
"ownedTemporaryFdCount": 0,
|
||||
"ownedKeychainFdCount": 0,
|
||||
"ownedKeychainFileCount": 0
|
||||
},
|
||||
{
|
||||
"kind": "checkpoint",
|
||||
"mode": "pure-apple",
|
||||
"stage": "first",
|
||||
"operations": 1,
|
||||
"processId": 93020,
|
||||
"utc": "2026-11-10T21:47:10.311Z",
|
||||
"elapsedSeconds": 0.993379,
|
||||
"rssBytes": 8028160,
|
||||
"createdRefs": 1,
|
||||
"releasedRefs": 1,
|
||||
"managedRuntimePresent": false,
|
||||
"AppleDatabaseCount": 1,
|
||||
"AppleDatabaseShallowBytes": 768,
|
||||
"AppleDatabaseClassAbsent": false,
|
||||
"databaseManagerClassPresent": true,
|
||||
"fdCount": 3,
|
||||
"ownedTemporaryFdCount": 0,
|
||||
"ownedKeychainFdCount": 0,
|
||||
"ownedKeychainFileCount": 0
|
||||
},
|
||||
{
|
||||
"kind": "checkpoint",
|
||||
"mode": "pure-apple",
|
||||
"stage": "repeated",
|
||||
"operations": 200,
|
||||
"processId": 93020,
|
||||
"utc": "2026-11-10T21:47:11.859Z",
|
||||
"elapsedSeconds": 2.541567,
|
||||
"rssBytes": 8699904,
|
||||
"createdRefs": 200,
|
||||
"releasedRefs": 200,
|
||||
"managedRuntimePresent": false,
|
||||
"AppleDatabaseCount": 1,
|
||||
"AppleDatabaseShallowBytes": 768,
|
||||
"AppleDatabaseClassAbsent": false,
|
||||
"databaseManagerClassPresent": true,
|
||||
"fdCount": 3,
|
||||
"ownedTemporaryFdCount": 0,
|
||||
"ownedKeychainFdCount": 0,
|
||||
"ownedKeychainFileCount": 0
|
||||
},
|
||||
{
|
||||
"kind": "checkpoint",
|
||||
"mode": "pure-apple",
|
||||
"stage": "idle",
|
||||
"operations": 200,
|
||||
"processId": 93020,
|
||||
"utc": "2026-11-10T21:47:17.582Z",
|
||||
"elapsedSeconds": 8.264326,
|
||||
"rssBytes": 8699904,
|
||||
"createdRefs": 200,
|
||||
"releasedRefs": 200,
|
||||
"managedRuntimePresent": false,
|
||||
"AppleDatabaseCount": 1,
|
||||
"AppleDatabaseShallowBytes": 768,
|
||||
"AppleDatabaseClassAbsent": false,
|
||||
"databaseManagerClassPresent": true,
|
||||
"fdCount": 3,
|
||||
"ownedTemporaryFdCount": 0,
|
||||
"ownedKeychainFdCount": 0,
|
||||
"ownedKeychainFileCount": 0
|
||||
}
|
||||
],
|
||||
"fingerprints": [
|
||||
{
|
||||
"kind": "fingerprint",
|
||||
"stage": "before",
|
||||
"defaultStatus": 0,
|
||||
"defaultPathStatus": 0,
|
||||
"searchStatus": 0,
|
||||
"searchPathStatus": 0,
|
||||
"defaultSha256": "08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63",
|
||||
"searchSha256": "c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c",
|
||||
"searchCount": 2,
|
||||
"ownedQueryReferencesReleased": 2
|
||||
},
|
||||
{
|
||||
"kind": "fingerprint",
|
||||
"stage": "after",
|
||||
"defaultStatus": 0,
|
||||
"defaultPathStatus": 0,
|
||||
"searchStatus": 0,
|
||||
"searchPathStatus": 0,
|
||||
"defaultSha256": "08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63",
|
||||
"searchSha256": "c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c",
|
||||
"searchCount": 2,
|
||||
"ownedQueryReferencesReleased": 4
|
||||
}
|
||||
],
|
||||
"complete": {
|
||||
"kind": "complete",
|
||||
"operations": 200,
|
||||
"createdRefs": 200,
|
||||
"releasedRefs": 200,
|
||||
"readOnlyDefaultAndSearchListUnchanged": true,
|
||||
"elapsedSeconds": 8.994713,
|
||||
"fullSoakGate": false
|
||||
},
|
||||
"fullSoakGate": false,
|
||||
"nativeInventoryIsNotOpenHandlesOrExclusiveRss": true,
|
||||
"nativeDeleteOsStatusesAllZero": true
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
{"kind":"fingerprint","stage":"before","defaultStatus":0,"defaultPathStatus":0,"searchStatus":0,"searchPathStatus":0,"defaultSha256":"08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63","searchSha256":"c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c","searchCount":2,"ownedQueryReferencesReleased":2}
|
||||
{"kind":"checkpoint","mode":"pure-apple","stage":"before","operations":0,"processId":93020,"utc":"2026-11-10T21:47:09.322Z","elapsedSeconds":0.004359,"rssBytes":7372800,"createdRefs":0,"releasedRefs":0,"managedRuntimePresent":false}
|
||||
{"kind":"operation","iteration":1,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"checkpoint","mode":"pure-apple","stage":"first","operations":1,"processId":93020,"utc":"2026-11-10T21:47:10.311Z","elapsedSeconds":0.993379,"rssBytes":8028160,"createdRefs":1,"releasedRefs":1,"managedRuntimePresent":false}
|
||||
{"kind":"operation","iteration":2,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":3,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":4,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":5,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":6,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":7,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":8,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":9,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":10,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":11,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":12,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":13,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":14,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":15,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":16,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":17,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":18,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":19,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":20,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":21,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":22,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":23,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":24,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":25,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":26,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":27,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":28,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":29,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":30,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":31,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":32,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":33,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":34,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":35,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":36,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":37,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":38,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":39,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":40,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":41,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":42,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":43,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":44,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":45,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":46,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":47,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":48,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":49,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":50,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":51,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":52,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":53,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":54,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":55,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":56,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":57,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":58,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":59,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":60,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":61,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":62,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":63,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":64,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":65,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":66,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":67,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":68,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":69,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":70,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":71,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":72,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":73,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":74,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":75,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":76,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":77,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":78,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":79,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":80,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":81,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":82,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":83,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":84,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":85,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":86,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":87,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":88,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":89,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":90,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":91,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":92,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":93,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":94,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":95,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":96,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":97,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":98,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":99,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":100,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":101,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":102,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":103,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":104,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":105,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":106,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":107,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":108,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":109,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":110,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":111,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":112,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":113,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":114,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":115,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":116,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":117,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":118,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":119,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":120,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":121,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":122,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":123,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":124,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":125,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":126,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":127,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":128,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":129,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":130,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":131,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":132,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":133,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":134,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":135,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":136,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":137,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":138,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":139,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":140,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":141,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":142,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":143,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":144,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":145,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":146,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":147,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":148,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":149,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":150,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":151,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":152,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":153,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":154,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":155,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":156,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":157,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":158,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":159,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":160,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":161,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":162,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":163,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":164,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":165,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":166,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":167,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":168,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":169,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":170,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":171,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":172,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":173,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":174,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":175,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":176,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":177,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":178,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":179,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":180,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":181,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":182,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":183,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":184,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":185,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":186,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":187,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":188,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":189,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":190,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":191,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":192,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":193,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":194,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":195,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":196,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":197,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":198,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":199,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"operation","iteration":200,"createStatus":0,"pathStatus":0,"deleteStatus":0,"returnedRef":true,"privatePathVerified":true,"releasedExactlyOnce":true}
|
||||
{"kind":"checkpoint","mode":"pure-apple","stage":"repeated","operations":200,"processId":93020,"utc":"2026-11-10T21:47:11.859Z","elapsedSeconds":2.541567,"rssBytes":8699904,"createdRefs":200,"releasedRefs":200,"managedRuntimePresent":false}
|
||||
{"kind":"fingerprint","stage":"after","defaultStatus":0,"defaultPathStatus":0,"searchStatus":0,"searchPathStatus":0,"defaultSha256":"08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63","searchSha256":"c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c","searchCount":2,"ownedQueryReferencesReleased":4}
|
||||
{"kind":"checkpoint","mode":"pure-apple","stage":"idle","operations":200,"processId":93020,"utc":"2026-11-10T21:47:17.582Z","elapsedSeconds":8.264326,"rssBytes":8699904,"createdRefs":200,"releasedRefs":200,"managedRuntimePresent":false}
|
||||
{"kind":"complete","operations":200,"createdRefs":200,"releasedRefs":200,"readOnlyDefaultAndSearchListUnchanged":true,"elapsedSeconds":8.994713,"fullSoakGate":false}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/PureAppleControl.c:36:21: warning: 'SecKeychainGetPath' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
|
||||
36 | OSStatus status=SecKeychainGetPath(keychain,&length,path);
|
||||
| ^
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:474:10: note: 'SecKeychainGetPath' has been explicitly marked deprecated here
|
||||
474 | OSStatus SecKeychainGetPath(SecKeychainRef __nullable keychain, UInt32 *ioPathLength, char *pathName)
|
||||
| ^
|
||||
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/PureAppleControl.c:47:29: warning: 'SecKeychainCopyDefault' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
|
||||
47 | OSStatus default_status=SecKeychainCopyDefault(&default_keychain);
|
||||
| ^
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:384:10: note: 'SecKeychainCopyDefault' has been explicitly marked deprecated here
|
||||
384 | OSStatus SecKeychainCopyDefault(SecKeychainRef * __nonnull CF_RETURNS_RETAINED keychain)
|
||||
| ^
|
||||
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/PureAppleControl.c:51:28: warning: 'SecKeychainCopySearchList' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
|
||||
51 | OSStatus search_status=SecKeychainCopySearchList(&search);
|
||||
| ^
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:404:10: note: 'SecKeychainCopySearchList' has been explicitly marked deprecated here
|
||||
404 | OSStatus SecKeychainCopySearchList(CFArrayRef * __nonnull CF_RETURNS_RETAINED searchList)
|
||||
| ^
|
||||
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/PureAppleControl.c:84:83: error: call to undeclared function 'explicit_bzero'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
|
||||
84 | char suffix[33],password[65];hex(random,16,suffix);hex(random+16,32,password);explicit_bzero(random,sizeof(random));
|
||||
| ^
|
||||
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/PureAppleControl.c:90:28: warning: 'SecKeychainCreate' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
|
||||
90 | OSStatus create_status=SecKeychainCreate(path,(UInt32)strlen(password),password,false,NULL,&keychain);
|
||||
| ^
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:309:10: note: 'SecKeychainCreate' has been explicitly marked deprecated here
|
||||
309 | OSStatus SecKeychainCreate(const char *pathName, UInt32 passwordLength, const void * __nullable password, Boolean promptUser, SecAccessRef __nullable initialAccess, SecKeychainRef * __nonnull CF_RETURNS_RETAINED keychain)
|
||||
| ^
|
||||
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/PureAppleControl.c:99:35: warning: 'SecKeychainDelete' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
|
||||
99 | if(verified)delete_status=SecKeychainDelete(keychain);
|
||||
| ^
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:319:10: note: 'SecKeychainDelete' has been explicitly marked deprecated here
|
||||
319 | OSStatus SecKeychainDelete(SecKeychainRef __nullable keychainOrArray)
|
||||
| ^
|
||||
5 warnings and 1 error generated.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"compileExitCode": 1,
|
||||
"nativeProcessNeverStarted": true,
|
||||
"emptyOwnedFixtureRootRemoved": true
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"sourceSha256": "f3d5b393f4bfcb350b37d610a8b6602b480e573e632f697b311813df276be347",
|
||||
"runnerSourceSha256": "22f0ee93b2998f1cb337aa5f763dcf578b2efe4616502ad095a309cc774c3c76",
|
||||
"compileCommand": [
|
||||
"clang",
|
||||
"-std=c11",
|
||||
"-O0",
|
||||
"-g",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/PureAppleControl.c",
|
||||
"-framework",
|
||||
"Security",
|
||||
"-framework",
|
||||
"CoreFoundation",
|
||||
"-o",
|
||||
"/private/tmp/opcua2-pure-apple-20261110a/PureAppleControl"
|
||||
],
|
||||
"compiler": "Apple clang version 21.0.0 (clang-2100.0.123.102)\nTarget: arm64-apple-darwin25.5.0\nThread model: posix\nInstalledDir: /Library/Developer/CommandLineTools/usr/bin\n",
|
||||
"os": "ProductName:\t\tmacOS\nProductVersion:\t\t26.5.2\nBuildVersion:\t\t25F84\n",
|
||||
"architecture": "arm64",
|
||||
"repetitions": 200,
|
||||
"idleSeconds": 5,
|
||||
"stages": [
|
||||
"before",
|
||||
"first",
|
||||
"repeated",
|
||||
"idle"
|
||||
],
|
||||
"fullSoakGate": false,
|
||||
"readOnlyKeychainQueries": [
|
||||
"SecKeychainCopyDefault",
|
||||
"SecKeychainCopySearchList",
|
||||
"SecKeychainGetPath"
|
||||
],
|
||||
"doesNotCallAnyDefaultOrSearchListSetter": true
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"actualNativeExecutions": 1,
|
||||
"earlierAttemptWasCompileOnly": true,
|
||||
"rawActualOutput": "/tmp/opcua2-pure-apple-20261110b",
|
||||
"rawCompileOnlyOutput": "/tmp/opcua2-pure-apple-20261110a",
|
||||
"rawSha256": {
|
||||
"actual-run-result.json": "70b7f3c57a5d14baf9cc593c16670da85adcb525e5f60bb419c8f0f01e206347",
|
||||
"actual-run-provenance.json": "aa181c0213588aede6d20888ede49ef2a74bb409c075d90a6bad214cfbdac29f",
|
||||
"actual-run-samples.jsonl": "4ebb9870e0e66d1a217ce489058a1658b0be71a5928115bbf347478bc1e6066a",
|
||||
"actual-run-build.log": "786ee9f813146b634d814e1cdb8f35fe1d9ef88a9ea96265bc3ffdafe4c61b6e",
|
||||
"actual-run-fixture-before-harness-cleanup.json": "eaaf36a5e7f7d1d3635e987d8f9a2600ce55f6482011ec9a7abf3e4dd759d9c4",
|
||||
"actual-run-fixture-cleanup.json": "1785d6ee67b2f7e77037a469ca2bdb9b07c8892781ea830a985f6ef505f84d69",
|
||||
"first-build-only-build.log": "ebaa2a34424167053a50f410ee04c6ed0988566c5c68e7c2473a55579baf1818",
|
||||
"first-build-only-provenance.json": "7236bb169732e2fe03d78af2691be3d41632bf1e9a79902ae74aa0ed4c6a539b",
|
||||
"first-build-only-fixture-cleanup.json": "e5551f93e5a2d898cf35b59f775ef8b6fecc06c673680cf8486019e73b44866f"
|
||||
},
|
||||
"redaction": "Selected text replaces only the local home prefix; raw receipts were not modified.",
|
||||
"review": "Astra independently verified all207 rows,200 ordered operations/status triples, reference balance, fingerprints, all4 native counts/bytes, FD/file counts, hashes, cleanup and5 unsuppressed warnings.",
|
||||
"retentionReproducedByEmptyNativeLifecycle": false,
|
||||
"fullSoakGate": false
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
Determining projects to restore...
|
||||
Restored /private/tmp/opcua2-native-reload-20261110a/Control.csproj (in 34 ms).
|
||||
Control -> /private/tmp/opcua2-native-reload-20261110a/bin/Release/net10.0/Control.dll
|
||||
|
||||
Build succeeded.
|
||||
0 Warning(s)
|
||||
0 Error(s)
|
||||
|
||||
Time Elapsed 00:00:00.63
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"allOwnedFixtureDirectoriesRemoved": true
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"controlSourceSha256": "e8c959a076bf1eecaad7e1ae71d8df3c2c86d2978a4558c98792471e880efc27",
|
||||
"runnerSourceSha256": "159ba76b3048d1a594e3a28032d350e36bc8ead4c93c525c5a60b3013699aeef",
|
||||
"repetitionsPerArm": 200,
|
||||
"stages": [
|
||||
"ready",
|
||||
"first",
|
||||
"repeated",
|
||||
"disposed",
|
||||
"idle"
|
||||
],
|
||||
"idleSeconds": 5,
|
||||
"fullSoakGate": false,
|
||||
"leakTracking": true,
|
||||
"trackingOverhead": "Both A/B processes capture allocation stacks; additional allocation and GC overhead is included in observed memory.",
|
||||
"nativeReferences": {
|
||||
"Opc.Ua.Client.dll": "620da406fabdbc75d11f9827ea7e39d2d3c762471bd2264b0d11f1d6cd9247cd",
|
||||
"Opc.Ua.Configuration.dll": "6ed6a594c93c3ff35d1a16539a9fe87c1e2640549cc2f48ed6bae333dc8bf0e7",
|
||||
"Opc.Ua.Core.Schema.dll": "51cfad48657d95d38074bb88be29aea6f5b361a07397247d58838d066b99772e",
|
||||
"Opc.Ua.Core.Types.dll": "a7dd141b8dabe8848ff28c2fb8e16a85a0508e6a49e6b1a1c1e267175b99af96",
|
||||
"Opc.Ua.Core.dll": "9d0857aa6b7f3c49037d4ea557b4da8edf07e146069464481783e66c5c9cd35a",
|
||||
"Opc.Ua.Security.Certificates.dll": "7f4969aceb2f0db5328c6882cb4b117342f3b18905c38acdba600683860c4fd0",
|
||||
"Opc.Ua.Server.dll": "930e714a2f6e15d393840bcdc95e1afa8012bc740c3738a655f958142721864e",
|
||||
"Opc.Ua.Types.dll": "c542f69882f4e8a4ebd5ccc28a3881d6967c7ffb6dcc3c56dc6c776f39c2ede1"
|
||||
},
|
||||
"hostDotnet": ".NET SDK:\n Version: 10.0.403\n Commit: 5a2162bea6\n Workload version: 10.0.400-manifests.a00bebb6\n MSBuild version: 18.9.15+5a2162bea\n\nRuntime Environment:\n OS Name: Mac OS X\n OS Version: 26.5\n OS Platform: Darwin\n RID: osx-arm64\n Base Path: /opt/homebrew/Cellar/dotnet/10.0.403/libexec/sdk/10.0.403/\n\n.NET workloads installed:\nThere are no installed workloads to display.\nConfigured to use workload sets when installing new manifests.\nNo workload sets are installed. Run \"dotnet workload restore\" to install a workload set.\n\nHost:\n Version: 10.0.14\n Architecture: arm64\n Commit: 0427b9abbe\n\n.NET SDKs installed:\n 10.0.403 [/opt/homebrew/Cellar/dotnet/10.0.403/libexec/sdk]\n\n.NET runtimes installed:\n Microsoft.AspNetCore.App 10.0.14 [/opt/homebrew/Cellar/dotnet/10.0.403/libexec/shared/Microsoft.AspNetCore.App]\n Microsoft.NETCore.App 10.0.14 [/opt/homebrew/Cellar/dotnet/10.0.403/libexec/shared/Microsoft.NETCore.App]\n\nOther architectures found:\n None\n\nEnvironment variables:\n DOTNET_ROOT [/opt/homebrew/Cellar/dotnet/10.0.403/libexec]\n\nglobal.json file:\n <user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/global.json\n\nLearn more:\n https://aka.ms/dotnet/info\n\nDownload .NET:\n https://aka.ms/dotnet/download\n",
|
||||
"os": "ProductName:\t\tmacOS\nProductVersion:\t\t26.5.2\nBuildVersion:\t\t25F84\n",
|
||||
"architecture": "arm64",
|
||||
"cryptographyRuntime": {
|
||||
"file": "/opt/homebrew/Cellar/dotnet/10.0.403/libexec/shared/Microsoft.NETCore.App/10.0.14/System.Security.Cryptography.dll",
|
||||
"informationalVersion": "10.0.14+0427b9abbead3fa64c6655eb4290c537397131fa",
|
||||
"sha256": "f4957d7218a004a27bf8964e45147df6a88092c6e7344582082840d62c6c579a"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{"kind":"checkpoint","mode":"registry","stage":"ready","operations":0,"processId":55851,"utc":"2026-11-10T21:30:30.043224+00:00","managerDisposed":false,"managedBytes":831128,"rssBytes":87654400,"privateBytes":0,"allocatedBytes":1761744,"compiledMethods":1192,"gc":[2,2,2],"certificateCoresCreated":7,"certificateCoresDisposed":6,"certificateCoresOutstanding":1,"runtimeTrackedTemporaryKeychains":1}
|
||||
{"kind":"checkpoint","mode":"registry","stage":"first","operations":1,"processId":55851,"utc":"2026-11-10T21:30:31.093095+00:00","managerDisposed":false,"managedBytes":1055944,"rssBytes":91095040,"privateBytes":0,"allocatedBytes":2051888,"compiledMethods":1777,"gc":[4,4,4],"certificateCoresCreated":7,"certificateCoresDisposed":6,"certificateCoresOutstanding":1,"runtimeTrackedTemporaryKeychains":1}
|
||||
{"kind":"checkpoint","mode":"registry","stage":"repeated","operations":200,"processId":55851,"utc":"2026-11-10T21:30:32.143169+00:00","managerDisposed":false,"managedBytes":1098664,"rssBytes":97779712,"privateBytes":0,"allocatedBytes":7937936,"compiledMethods":2093,"gc":[6,6,6],"certificateCoresCreated":7,"certificateCoresDisposed":6,"certificateCoresOutstanding":1,"runtimeTrackedTemporaryKeychains":1}
|
||||
{"kind":"checkpoint","mode":"registry","stage":"disposed","operations":200,"processId":55851,"utc":"2026-11-10T21:30:33.170061+00:00","managerDisposed":true,"managedBytes":1104720,"rssBytes":97927168,"privateBytes":0,"allocatedBytes":7949840,"compiledMethods":2207,"gc":[8,8,8],"certificateCoresCreated":7,"certificateCoresDisposed":7,"certificateCoresOutstanding":0,"runtimeTrackedTemporaryKeychains":0}
|
||||
{"kind":"checkpoint","mode":"registry","stage":"idle","operations":200,"processId":55851,"utc":"2026-11-10T21:30:39.166632+00:00","managerDisposed":true,"managedBytes":1130616,"rssBytes":97927168,"privateBytes":0,"allocatedBytes":7966976,"compiledMethods":2251,"gc":[10,10,10],"certificateCoresCreated":7,"certificateCoresDisposed":7,"certificateCoresOutstanding":0,"runtimeTrackedTemporaryKeychains":0}
|
||||
{"kind":"complete","mode":"registry","operations":200,"sameLeafSha256":"fadcc11ec694732b82fe4dc47bb41bce6023d113339e53aca9fee5b079f200b2","certificateUnchanged":true,"managerDisposed":true,"certificateCoresOutstanding":0,"fullSoakGate":false}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user