[Feat] (Plugin, P11 B1): the broker presents the PairBind pair at hand-off (PairBindFrames, the encoder's bytes) and answers v2 with that flag and the apply core its supervisor announced; the helper passes MOBILEGL_IPC_FD_PAIRED=1 on, and Java no longer re-derives the reserved core

This commit is contained in:
2026-09-29 15:03:52 -04:00
parent 582adb0dd6
commit 7ccb920ad2
8 changed files with 214 additions and 91 deletions
@@ -25,8 +25,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
* receiver checks the token against the running server's own ({@code MOBILEGL_IPC_TOKEN}), connects
* TWICE to the server's unix endpoint - or, when the server listens on TCP only, to the private
* abstract listener {@link MobileGLServerService} runs beside it for this - and hands both client
* ends back as {@link ParcelFileDescriptor}s. It writes nothing on them: the client presents its own
* PairBind pair ({@code MOBILEGL_IPC_CONTROL=fd:<control>,<aux>}).
* ends back as {@link ParcelFileDescriptor}s. Ruling ID-P11-12: whoever opens the connections presents
* the pair's identity, so it writes one {@code PairBind} pair on them at once ({@link PairBindFrames}:
* a fresh nonce, control then aux) and says so in its answer; the helper tells the program
* ({@code MOBILEGL_IPC_FD_PAIRED=1}) not to present a second one. The server pairs the connections
* now, not when the program - which may spend many seconds loading first - reaches its first EGL call.
*
* <p>EVERY request is answered on the callback - the two descriptors, or a refusal with its name -
* so a helper never has to time out to learn it was refused. The refusals: {@code bad-request},
@@ -47,13 +50,17 @@ public final class ExternalClientBroker extends BroadcastReceiver {
public static final String KEY_CALLBACK = "callback";
public static final String KEY_TOKEN = "token";
public static final String KEY_VERSION = "version";
public static final int PROTOCOL_VERSION = 1;
/** 2: the answer carries the paired-at-hand-off flag (a v1 helper would add a second PairBind pair). */
public static final int PROTOCOL_VERSION = 2;
/**
* The broker's answer: int version, the control and aux descriptors, then long applyCore - the cpu
* mask the server's `auto` policy reserves for this session's apply thread (0 = none).
* The broker's answer: int version, the control and aux descriptors, long applyCore - the cpu mask
* the running supervisor announced it reserves for this session's apply thread (0 = none) - and
* int flags ({@link #FLAG_PAIRED_AT_HANDOFF}).
*/
public static final int REPLY_FDS = IBinder.FIRST_CALL_TRANSACTION;
/** The broker already wrote the PairBind pair on the two connections. */
public static final int FLAG_PAIRED_AT_HANDOFF = 1;
/** The broker's refusal: String code, String message. */
public static final int REPLY_REFUSED = IBinder.FIRST_CALL_TRANSACTION + 1;
@@ -160,21 +167,42 @@ public final class ExternalClientBroker extends BroadcastReceiver {
+ ": " + failure[0]);
return;
}
// ID-P11-12: the pair's identity, presented by whoever opened the connections - here, now. The
// server's PairAcceptor pairs them within its 2000 ms budget however long the program the helper
// execs takes to reach its Hello (the paired session then waits for that Hello until EOF).
try {
byte[] nonce = new byte[PairBindFrames.NONCE_BYTES];
new java.security.SecureRandom().nextBytes(nonce);
java.io.OutputStream controlOut = sockets[0].getOutputStream();
controlOut.write(PairBindFrames.frame(nonce, false));
controlOut.flush();
java.io.OutputStream auxOut = sockets[1].getOutputStream();
auxOut.write(PairBindFrames.frame(nonce, true));
auxOut.flush();
} catch (Exception error) {
closeQuietly(sockets[0]);
closeQuietly(sockets[1]);
refuse(callback, REFUSE_CONNECT_FAILED, "could not present the PairBind pair on " + target.endpoint
+ ": " + error);
return;
}
ParcelFileDescriptor control = null;
ParcelFileDescriptor aux = null;
Parcel data = Parcel.obtain();
long applyCore = Math.max(0, target.applyCore.get()); // -1: not announced yet -> no mask
try {
control = ParcelFileDescriptor.dup(sockets[0].getFileDescriptor());
aux = ParcelFileDescriptor.dup(sockets[1].getFileDescriptor());
data.writeInt(PROTOCOL_VERSION);
control.writeToParcel(data, 0);
aux.writeToParcel(data, 0);
// The core the server reserves for this session's apply thread (0 = none): the helper keeps
// the client off it. Appended after the descriptors; a reader that stops before it is fine.
data.writeLong(target.applyCore);
// The core the running supervisor announced it reserves for this session's apply thread
// (0 = none): the helper keeps the client off it.
data.writeLong(applyCore);
data.writeInt(FLAG_PAIRED_AT_HANDOFF);
boolean delivered = callback.transact(REPLY_FDS, data, null, IBinder.FLAG_ONEWAY);
Log.i(TAG, "brokered a connection pair to " + target.endpoint + " for a client (delivered="
+ delivered + ", apply core 0x" + Long.toHexString(target.applyCore) + ")");
Log.i(TAG, "brokered a connection pair to " + target.endpoint + " for a client, paired at hand-off "
+ "(delivered=" + delivered + ", apply core 0x" + Long.toHexString(applyCore) + ")");
} catch (Exception error) {
Log.e(TAG, "could not hand the connection pair to the client", error);
} finally {
@@ -43,7 +43,9 @@ import java.util.Map;
* that enforces it there breaks this route; CONTRACT-P11 B1), and waits for the broker's answer.
* Two connected sockets: it clears their close-on-exec flag and {@code execve}s the program with
* {@code MOBILEGL_TRANSPORT=spawn}, {@code MOBILEGL_IPC_CONTROL=fd:<control>,<aux>},
* {@code MOBILEGL_IPC_DATA=shm} and {@code MOBILEGL_IPC_TOKEN} (the session's Hello carries it too).
* {@code MOBILEGL_IPC_DATA=shm} and {@code MOBILEGL_IPC_TOKEN} (the session's Hello carries it too), plus
* {@code MOBILEGL_IPC_FD_PAIRED=1} when the broker already presented the PairBind pair at hand-off
* (ID-P11-12) - the program may then take as long as it likes to reach its first EGL call.
* When the broker names a cpu core the server reserves for the session's apply thread, the program is
* exec'd through {@code taskset} on every other core (see {@link #clientMask}; {@code --keep-affinity}
* opts out). A refusal: it prints the broker's name for it and exits with that refusal's code. No
@@ -147,15 +149,18 @@ public final class ExternalClientHelper {
ParcelFileDescriptor control = ParcelFileDescriptor.CREATOR.createFromParcel(data);
ParcelFileDescriptor aux = ParcelFileDescriptor.CREATOR.createFromParcel(data);
long applyCore = data.dataAvail() >= 8 ? data.readLong() : 0;
int replyFlags = data.dataAvail() >= 4 ? data.readInt() : 0;
boolean paired = (replyFlags & ExternalClientBroker.FLAG_PAIRED_AT_HANDOFF) != 0;
int controlFd = inheritable(control);
int auxFd = inheritable(aux);
long mask = keepAffinity ? 0 : clientMask(applyCore);
say("broker v" + version + " handed over fd:" + controlFd + "," + auxFd
+ (paired ? " paired at hand-off" : " unpaired")
+ (mask != 0 ? "; the server's apply core 0x" + Long.toHexString(applyCore)
+ " reserved, the program runs on cpu mask 0x" + Long.toHexString(mask)
: "; no cpu mask applied")
+ "; exec " + programPath);
exec(programPath, argv, controlFd, auxFd, presented, mask);
exec(programPath, argv, controlFd, auxFd, presented, mask, paired);
} catch (Throwable error) {
say("could not exec " + programPath + ": " + error);
finish(EXIT_EXEC_FAILED);
@@ -267,8 +272,8 @@ public final class ExternalClientHelper {
return pfd.detachFd();
}
private static void exec(String path, List<String> argv, int control, int aux, String token, long cpuMask)
throws Exception {
private static void exec(String path, List<String> argv, int control, int aux, String token, long cpuMask,
boolean pairedAtHandoff) throws Exception {
Map<String, String> env = new LinkedHashMap<>();
String[] inherited = Os.environ();
if (inherited != null) {
@@ -283,6 +288,9 @@ public final class ExternalClientHelper {
env.put("MOBILEGL_IPC_CONTROL", "fd:" + control + "," + aux);
env.put("MOBILEGL_IPC_DATA", "shm");
env.put("MOBILEGL_IPC_TOKEN", token);
// ID-P11-12: the broker presented the PairBind pair; the program's fd: endpoint must not add one.
if (pairedAtHandoff) env.put("MOBILEGL_IPC_FD_PAIRED", "1");
else env.remove("MOBILEGL_IPC_FD_PAIRED");
List<String> envp = new ArrayList<>();
for (Map.Entry<String, String> entry : env.entrySet()) envp.add(entry.getKey() + "=" + entry.getValue());
if (cpuMask != 0 && new File(TASKSET).canExecute()) {
@@ -17,6 +17,7 @@ import java.io.File;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
/** Entry point for the offscreen TCP supervisor; EGL belongs to its session children. */
public final class MobileGLServerService extends Service {
@@ -40,12 +41,12 @@ public final class MobileGLServerService extends Service {
final String endpoint;
final String token;
final Process process;
// The core the server's `auto` policy gives a dialled-in shared-segment client's apply thread
// (ServerEnvironment.reservedApplyCore); the broker hands it to the helper, which keeps the
// client off it. 0 = none reserved.
final long applyCore;
// The core THIS supervisor reserves for a dialled-in shared-segment client's apply thread, as it
// announced on stdout at start-up (ServerEnvironment.parseReservedApplyCore); the broker hands
// it to the helper, which keeps the client off it. -1 until the line is read, 0 = none.
final AtomicLong applyCore;
BrokerTarget(String endpoint, String token, Process process, long applyCore) {
BrokerTarget(String endpoint, String token, Process process, AtomicLong applyCore) {
this.endpoint = endpoint;
this.token = token;
this.process = process;
@@ -53,21 +54,6 @@ public final class MobileGLServerService extends Service {
}
}
/** cpuinfo_max_freq of every cpu this process can count (0 where unreadable). */
private static long[] cpuMaxFrequenciesKHz() {
int count = Math.min(64, Math.max(1, Runtime.getRuntime().availableProcessors()));
long[] frequencies = new long[count];
for (int cpu = 0; cpu < count; cpu++) {
File file = new File("/sys/devices/system/cpu/cpu" + cpu + "/cpufreq/cpuinfo_max_freq");
try (BufferedReader reader = new BufferedReader(new java.io.FileReader(file))) {
frequencies[cpu] = Long.parseLong(reader.readLine().trim());
} catch (Exception unreadable) {
frequencies[cpu] = 0;
}
}
return frequencies;
}
// Set when a supervisor the broker can reach is running, cleared when it stops. The broker runs
// in this same process (:mglsrv), so a broadcast that finds null here finds no server.
private static volatile BrokerTarget brokerTarget;
@@ -155,30 +141,26 @@ public final class MobileGLServerService extends Service {
if (endpoint == null) endpoint = "tcp://127.0.0.1:40613";
final String envExtra = intent == null ? null : intent.getStringExtra("env");
final String token = intent == null ? null : intent.getStringExtra("token");
java.util.Map<String, String> requested = new java.util.HashMap<>();
ServerEnvironment.apply(requested, envExtra);
final long applyCore = ServerEnvironment.reservedApplyCore(cpuMaxFrequenciesKHz(),
requested.get("MOBILEGL_IPC_SERVER_AFFINITY"));
final AtomicLong primaryCore = new AtomicLong(-1);
try {
supervisor = startSupervisor(endpoint, envExtra, token, "mgl-supervisor-log", startId, true);
supervisor = startSupervisor(endpoint, envExtra, token, "mgl-supervisor-log", startId, true, primaryCore);
// P11 B1: the broker's target. A unix endpoint is reachable by this app as it is (a
// relative path resolves against filesDir, the supervisor's working directory); a TCP
// one gets a private abstract listener of its own (BrokerTarget).
if (endpoint.startsWith("tcp://")) {
String privateName = "@" + getPackageName() + ".broker." + Long.toHexString(
new java.security.SecureRandom().nextLong() & 0xffffffffL);
final AtomicLong brokerCore = new AtomicLong(-1);
brokerSupervisor = startSupervisor(privateName, envExtra, token, "mgl-broker-supervisor-log",
startId, false);
brokerTarget = new BrokerTarget(privateName, token, brokerSupervisor, applyCore);
startId, false, brokerCore);
brokerTarget = new BrokerTarget(privateName, token, brokerSupervisor, brokerCore);
Log.i(TAG, "same-device external clients: broker listener " + privateName
+ " beside the TCP-only supervisor on " + endpoint + "; apply core reserved for them 0x"
+ Long.toHexString(applyCore));
+ " beside the TCP-only supervisor on " + endpoint);
} else {
String unix = endpoint.startsWith("@") || endpoint.startsWith("/") ? endpoint
: new File(getFilesDir(), endpoint).getAbsolutePath();
brokerTarget = new BrokerTarget(unix, token, supervisor, applyCore);
Log.i(TAG, "same-device external clients: the broker connects to " + unix
+ "; apply core reserved for them 0x" + Long.toHexString(applyCore));
brokerTarget = new BrokerTarget(unix, token, supervisor, primaryCore);
Log.i(TAG, "same-device external clients: the broker connects to " + unix);
}
} catch (Exception error) {
Log.e(TAG, "cannot start TCP supervisor", error);
@@ -190,10 +172,11 @@ public final class MobileGLServerService extends Service {
/**
* Execs one {@code libMobileGLServer.so <endpoint> --serve} with the server role's environment and
* logs its output. When {@code primary}, its exit stops the service.
* logs its output. When {@code primary}, its exit stops the service. {@code reservedCore} receives the
* apply core the supervisor announces at start-up (P11 B1).
*/
private Process startSupervisor(String endpoint, String envExtra, String token, String logThreadName,
int startId, boolean primary) throws java.io.IOException {
int startId, boolean primary, AtomicLong reservedCore) throws java.io.IOException {
File executable = new File(getApplicationInfo().nativeLibraryDir, "libMobileGLServer.so");
ProcessBuilder builder = new ProcessBuilder(executable.getAbsolutePath(), endpoint, "--serve");
builder.directory(getFilesDir()).redirectErrorStream(true);
@@ -207,7 +190,11 @@ public final class MobileGLServerService extends Service {
new Thread(() -> {
try (BufferedReader output = new BufferedReader(new InputStreamReader(child.getInputStream()))) {
String line;
while ((line = output.readLine()) != null) Log.i(TAG, line);
while ((line = output.readLine()) != null) {
Log.i(TAG, line);
long announced = ServerEnvironment.parseReservedApplyCore(line);
if (announced != -1) reservedCore.set(announced); // one core, so never all 64 bits
}
Log.i(TAG, (primary ? "supervisor" : "broker supervisor") + " exited " + child.waitFor());
} catch (java.io.InterruptedIOException stopped) {
// onDestroy's Process.destroy() closes this stream under the read: the service
@@ -0,0 +1,58 @@
package top.mobilegl.plugin;
/**
* P11 B1 (CONTRACT-P11 B1, ruling ID-P11-12): the two framed {@code PairBind} control messages the
* broker writes at hand-off - {@code PairBind{nonce, aux=false}} first on the control connection and
* {@code PairBind{nonce, aux=true}} on the aux one - so the server's PairAcceptor pairs the two
* connections the moment the broker opens them, not when the program it opened them for reaches its
* first EGL call.
*
* <p>The bytes are the native encoder's own ({@code MG_Remote/Transport/PairBind.h EncodePairBind}
* framed by {@code Framing.h AppendFrame}): a flatbuffers {@code CtrlEnvelope} whose layout for a
* 16-byte nonce is fixed, with the nonce as its last 16 bytes. The two prefixes below are that
* encoder's output up to the nonce, and {@code ServerSpawnTest.TheBrokersPairBindTemplatesAreTheEncoders}
* reads them out of this file and compares against the encoder for random nonces - a schema change
* that moves a byte turns that test red, not a device.
*
* <p>No android.* import: tools/trace_replay/test_android_lifecycle.py compiles and runs it with javac
* (PairBindFramesTest).
*/
public final class PairBindFrames {
private PairBindFrames() {}
public static final int NONCE_BYTES = 16;
/** Framing.h kFrameMagic, little-endian on the wire. */
public static final int FRAME_MAGIC = 0x464C474D;
// EncodePairBind(nonce, aux=false) up to the nonce (the nonce is the payload's last 16 bytes).
static final String CONTROL_PREFIX_HEX =
"100000004d474c4308000e0007000800080000000000000f0c0000000000060008000400060000000400000010000000";
// EncodePairBind(nonce, aux=true) up to the nonce.
static final String AUX_PREFIX_HEX =
"100000004d474c4308000c0007000800080000000000000f0c00000008000c000800070008000000000000010400000010000000";
/** One framed PairBind: 8-byte header (magic, payload length; little-endian) + the payload. */
public static byte[] frame(byte[] nonce, boolean aux) {
if (nonce == null || nonce.length != NONCE_BYTES) {
throw new IllegalArgumentException("a PairBind nonce is " + NONCE_BYTES + " bytes");
}
byte[] prefix = hex(aux ? AUX_PREFIX_HEX : CONTROL_PREFIX_HEX);
int payload = prefix.length + NONCE_BYTES;
byte[] out = new byte[8 + payload];
putLittleEndian(out, 0, FRAME_MAGIC);
putLittleEndian(out, 4, payload);
System.arraycopy(prefix, 0, out, 8, prefix.length);
System.arraycopy(nonce, 0, out, 8 + prefix.length, NONCE_BYTES);
return out;
}
private static void putLittleEndian(byte[] out, int offset, int value) {
for (int i = 0; i < 4; i++) out[offset + i] = (byte) (value >>> (8 * i));
}
static byte[] hex(String text) {
byte[] out = new byte[text.length() / 2];
for (int i = 0; i < out.length; i++) out[i] = (byte) Integer.parseInt(text.substring(2 * i, 2 * i + 2), 16);
return out;
}
}
@@ -81,32 +81,25 @@ public final class ServerEnvironment {
return edits;
}
/** The supervisor's start-up announcement (MG_Remote/Server/ServerMain.cpp), up to the mask. */
static final String RESERVED_CORE_ANNOUNCEMENT =
"MG_Remote server: apply core reserved for dialled-in shared-segment clients: 0x";
/**
* P11 B1 (CONTRACT-P11 B1): the core the server's `auto` policy reserves for the apply thread of a
* dialled-in shared-segment client - MG_Remote/Server/ApplyThreadPolicy.h ReservedApplyCore over
* ServerLoop.cpp's DetectBigCoreMask, mirrored here so the broker can tell the helper which core to
* keep the client off. {@code maxFreqKHz[i]} is cpu i's cpuinfo_max_freq (0 = unreadable, which
* like the native probe gives no answer). A cpu is big when its ceiling is within 15% of the peak;
* the reserved core is the LOWEST big one, and only when the big set is a strict subset of at least
* two. 0 = none. {@code affinityOverride} is the server's MOBILEGL_IPC_SERVER_AFFINITY: anything
* but unset / empty / {@code auto} is an operator's own placement, and then nothing is reserved.
* P11 B1 (CONTRACT-P11 B1): the core the running supervisor reserves for a dialled-in shared-segment
* client's apply thread, read off the line it prints on stdout at start-up (the native policy,
* ApplyThreadPolicy.h ReservedApplyCoreForConfig, is the one source of it; nothing here re-derives
* it). The mask, or -1 when {@code line} is not that announcement.
*/
public static long reservedApplyCore(long[] maxFreqKHz, String affinityOverride) {
if (affinityOverride != null && !affinityOverride.isEmpty() && !affinityOverride.equals("auto")) return 0;
int count = Math.min(64, maxFreqKHz.length);
long peak = 0;
for (int cpu = 0; cpu < count; cpu++) {
if (maxFreqKHz[cpu] <= 0) return 0;
peak = Math.max(peak, maxFreqKHz[cpu]);
}
if (count == 0 || peak == 0) return 0;
long big = 0;
for (int cpu = 0; cpu < count; cpu++) {
if (maxFreqKHz[cpu] >= peak * 85 / 100) big |= 1L << cpu;
}
long online = count >= 64 ? ~0L : (1L << count) - 1;
if ((big & online) == online || Long.bitCount(big) < 2) return 0;
return Long.lowestOneBit(big);
public static long parseReservedApplyCore(String line) {
if (line == null) return -1;
int at = line.indexOf(RESERVED_CORE_ANNOUNCEMENT);
if (at < 0) return -1;
int start = at + RESERVED_CORE_ANNOUNCEMENT.length();
int end = start;
while (end < line.length() && Character.digit(line.charAt(end), 16) >= 0) end++;
if (end == start || end - start > 16) return -1;
return Long.parseUnsignedLong(line.substring(start, end), 16);
}
/**
@@ -0,0 +1,47 @@
package top.mobilegl.plugin;
/**
* Host-side checks for PairBindFrames, the broker's hand-off PairBinds (P11 B1): the frame header and
* the nonce's place. That the prefixes ARE the native encoder's bytes is ServerSpawnTest's
* TheBrokersPairBindTemplatesAreTheEncoders (it reads them out of PairBindFrames.java). Run by
* tools/trace_replay/test_android_lifecycle.py with plain javac; assert-free like its siblings.
*/
public final class PairBindFramesTest {
private static int failures = 0;
private static void expect(String what, Object actual, Object expected) {
if (expected == null ? actual == null : expected.equals(actual)) return;
++failures;
System.err.println(what + "\n got: [" + actual + "]\n expected: [" + expected + "]");
}
private static int littleEndian(byte[] b, int offset) {
return (b[offset] & 0xff) | (b[offset + 1] & 0xff) << 8 | (b[offset + 2] & 0xff) << 16 | (b[offset + 3] & 0xff) << 24;
}
public static void main(String[] args) {
byte[] nonce = new byte[PairBindFrames.NONCE_BYTES];
for (int i = 0; i < nonce.length; i++) nonce[i] = (byte) (0xA0 + i);
for (boolean aux : new boolean[]{false, true}) {
byte[] frame = PairBindFrames.frame(nonce, aux);
String role = aux ? "aux" : "control";
expect(role + ": the frame magic is Framing.h's, little-endian", littleEndian(frame, 0), 0x464C474D);
expect(role + ": the length field is the payload's", littleEndian(frame, 4), frame.length - 8);
expect(role + ": the payload is the encoder's size (64 control, 68 aux)", frame.length - 8, aux ? 68 : 64);
expect(role + ": the envelope carries the MGLC identifier", new String(frame, 12, 4, java.nio.charset.StandardCharsets.US_ASCII), "MGLC");
byte[] tail = java.util.Arrays.copyOfRange(frame, frame.length - 16, frame.length);
expect(role + ": the nonce is the last 16 bytes", java.util.Arrays.toString(tail), java.util.Arrays.toString(nonce));
}
try {
PairBindFrames.frame(new byte[8], false);
++failures;
System.err.println("a short nonce was framed");
} catch (IllegalArgumentException expected) {
}
if (failures != 0) {
System.err.println("PairBindFramesTest: " + failures + " failure(s)");
System.exit(1);
}
System.out.println("PairBindFramesTest: all checks passed");
}
}
+17 -18
View File
@@ -94,24 +94,23 @@ public final class ServerEnvironmentTest {
expect("an unmeasured view fills the view",
java.util.Arrays.toString(ServerEnvironment.aspectFit(0, 0, 640, 480)), "[-1, -1]");
// ---- P11 B1: the apply core the broker tells the helper to keep the client off ------------------
// The Java mirror of MG_Remote/Server/ApplyThreadPolicy.h ReservedApplyCore over DetectBigCoreMask.
long[] redmi = {3532800, 3532800, 3532800, 3532800, 3532800, 3532800, 4320000, 4320000};
expect("the Redmi (cpu6-7 prime): the LOWEST prime core, cpu6",
ServerEnvironment.reservedApplyCore(redmi, null), 0x40L);
expect("`auto` and an empty override are the policy's own", ServerEnvironment.reservedApplyCore(redmi, "auto"),
0x40L);
expect("an operator's affinity (a mask or off) reserves nothing",
ServerEnvironment.reservedApplyCore(redmi, "0x80") + ServerEnvironment.reservedApplyCore(redmi, "off"),
0L);
expect("a symmetric machine has no prime pair to split",
ServerEnvironment.reservedApplyCore(new long[]{2000000, 2000000, 2000000, 2000000}, ""), 0L);
expect("one prime core is not a pair",
ServerEnvironment.reservedApplyCore(new long[]{1800000, 1800000, 1800000, 3000000}, null), 0L);
expect("within 15% of the peak is big: cpu2 and cpu3 of {1.0, 1.0, 2.6, 3.0} GHz, the lowest is cpu2",
ServerEnvironment.reservedApplyCore(new long[]{1000000, 1000000, 2600000, 3000000}, null), 0x4L);
expect("an unreadable ceiling gives no answer, like the native probe",
ServerEnvironment.reservedApplyCore(new long[]{3532800, 0, 4320000, 4320000}, null), 0L);
// ---- P11 B1: the apply core the supervisor announces and the broker relays ---------------------
// The native policy is the one source; the service only reads the supervisor's start-up line.
expect("the announcement as the supervisor prints it",
ServerEnvironment.parseReservedApplyCore("MG_Remote server: apply core reserved for dialled-in "
+ "shared-segment clients: 0x40 (auto: the lowest of the prime cores, the rest left to the "
+ "dialled-in shared-segment client)"), 0x40L);
expect("none reserved is 0, not absent",
ServerEnvironment.parseReservedApplyCore("MG_Remote server: apply core reserved for dialled-in "
+ "shared-segment clients: 0x0 (auto: unpinned - no asymmetric big/prime topology to split)"), 0L);
expect("a 64-bit mask", ServerEnvironment.parseReservedApplyCore(
"MG_Remote server: apply core reserved for dialled-in shared-segment clients: 0x8000000000000000 (x)"),
Long.MIN_VALUE);
expect("any other supervisor line is not the announcement",
ServerEnvironment.parseReservedApplyCore("MG_Remote server: pid=12 listening on @x"), -1L);
expect("a truncated announcement is not one",
ServerEnvironment.parseReservedApplyCore("MG_Remote server: apply core reserved for dialled-in "
+ "shared-segment clients: 0x"), -1L);
if (failures != 0) {
System.err.println("ServerEnvironmentTest: " + failures + " failure(s)");
@@ -71,6 +71,9 @@ def main():
(trace, "top.mobilegl.plugin.trace", "TraceReplaySession", "TraceReplaySessionTest"),
(trace, "top.mobilegl.plugin.trace", "SpawnServerPath", "SpawnServerPathTest"),
(main_src, "top.mobilegl.plugin", "ServerEnvironment", "ServerEnvironmentTest"),
# P11 B1: the broker's hand-off PairBinds (their bytes are pinned to the native encoder by
# ServerSpawnTest.TheBrokersPairBindTemplatesAreTheEncoders).
(main_src, "top.mobilegl.plugin", "PairBindFrames", "PairBindFramesTest"),
]
with tempfile.TemporaryDirectory(prefix="mobilegl-replay-lifecycle-") as output:
sources = []