mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Tool] (extmem_probe, P11 B2): route mode - the probe serves in the render server's place (-Pmobilegl.extmemProbeAsServer) and --route-client sends a held-lock AHB over B1's fd: pair, directly on aux and through a socketpair hop
This commit is contained in:
@@ -30,6 +30,7 @@ option(MOBILEGL_BUILD_DISAGGREGATED "Build the MG_Remote transport layer (two-pr
|
||||
option(MOBILEGL_BUILD_DISAGGREGATED_INPROC "Compile the in-process (one-process, two-role) split shims; implies MOBILEGL_BUILD_DISAGGREGATED" OFF)
|
||||
option(MOBILEGL_BUILD_SERVER_SPIKE "Build the P0 spike-A MobileGLServer delivery-chain executable (Android only)" OFF)
|
||||
option(MOBILEGL_BUILD_EXTMEM_PROBE "Package tools/spikes/extmem_probe as libMobileGLExtmemProbe.so for the trace APK's exec hook (P11 A3, Android only)" OFF)
|
||||
option(MOBILEGL_EXTMEM_PROBE_AS_SERVER "Spike only (P11 B2 step 1): also package the probe as libMobileGLServer.so, the real server as libMobileGLServerReal.so" OFF)
|
||||
# The PipeInputs strangler (ARCHITECTURE.md 9.2). OFF is the pull build and must stay
|
||||
# byte-identical to a tree without either option: MGB_CTX is the live GLContext, no
|
||||
# MGPipe/PipeInputs source is compiled, every MGP_FILL is ((void)0).
|
||||
@@ -1127,4 +1128,27 @@ if (ANDROID AND MOBILEGL_BUILD_EXTMEM_PROBE)
|
||||
set_target_properties(MobileGLExtmemProbe PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
|
||||
endif()
|
||||
if (MOBILEGL_EXTMEM_PROBE_AS_SERVER)
|
||||
# P11 B2 step 1, spike APKs only: MobileGLServerService execs `libMobileGLServer.so
|
||||
# <endpoint> --serve` and B1's broker connects to whatever that listens on, so the probe
|
||||
# takes the server's file name and answers "does a client's AHardwareBuffer cross this
|
||||
# route into the server app" on the real route (tools/spikes/extmem_probe, route mode).
|
||||
if (TARGET MobileGLServer)
|
||||
set_target_properties(MobileGLServer PROPERTIES OUTPUT_NAME "MobileGLServerReal")
|
||||
endif()
|
||||
add_executable(MobileGLExtmemProbeAsServer
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tools/spikes/extmem_probe/extmem_probe.cpp)
|
||||
set_target_properties(MobileGLExtmemProbeAsServer PROPERTIES
|
||||
PREFIX "lib"
|
||||
SUFFIX ".so"
|
||||
OUTPUT_NAME "MobileGLServer"
|
||||
POSITION_INDEPENDENT_CODE ON)
|
||||
target_compile_options(MobileGLExtmemProbeAsServer PRIVATE -fPIE)
|
||||
target_link_options(MobileGLExtmemProbeAsServer PRIVATE -pie)
|
||||
target_link_libraries(MobileGLExtmemProbeAsServer PRIVATE vulkan EGL GLESv3 android log)
|
||||
if (CMAKE_LIBRARY_OUTPUT_DIRECTORY)
|
||||
set_target_properties(MobileGLExtmemProbeAsServer PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -27,6 +27,12 @@ def mobileGlBuildServerSpike = {
|
||||
def mobileGlBuildExtmemProbe = {
|
||||
(rootProject.findProperty('mobilegl.buildExtmemProbe') ?: System.getenv('MOBILEGL_BUILD_EXTMEM_PROBE') ?: 'OFF') as String
|
||||
}
|
||||
// P11 B2 step 1, spike only: -Pmobilegl.extmemProbeAsServer=ON also packages the probe AS
|
||||
// libMobileGLServer.so (the real server becomes libMobileGLServerReal.so), so the render server's
|
||||
// service and B1's broker run the AHB route probe in the server's place. Never for a real APK.
|
||||
def mobileGlExtmemProbeAsServer = {
|
||||
(rootProject.findProperty('mobilegl.extmemProbeAsServer') ?: 'OFF') as String
|
||||
}
|
||||
|
||||
// Shared native shape for BOTH plugin and trace APKs. The render server (libMobileGLServer.so and
|
||||
// the display server's JNI) is part of the split shape, and both flavors of a standalone plugin
|
||||
@@ -117,6 +123,7 @@ android {
|
||||
// above for the two ways to enable it.
|
||||
arguments "-DMOBILEGL_BUILD_SERVER_SPIKE=${mobileGlBuildServerSpike()}"
|
||||
arguments "-DMOBILEGL_BUILD_EXTMEM_PROBE=${mobileGlBuildExtmemProbe()}"
|
||||
arguments "-DMOBILEGL_EXTMEM_PROBE_AS_SERVER=${mobileGlExtmemProbeAsServer()}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,3 +180,22 @@ copy; GL compute on the `glBufferStorageExternalEXT` buffer) and fills REG_E / R
|
||||
and the client reads both fills through the still-held pointer; after the last
|
||||
round it unlocks and relocks once more (`after unlock+relock`), which separates
|
||||
"GPU writes never landed" from "the held mapping kept stale lines".
|
||||
|
||||
## Route mode (P11 B2 step 1)
|
||||
|
||||
Does a client's AHB cross the *real* B1 routes into the server app? A spike APK built with
|
||||
`-Pmobilegl.buildExtmemProbe=ON -Pmobilegl.extmemProbeAsServer=ON` packages the probe as
|
||||
`libMobileGLServer.so` (the real server becomes `libMobileGLServerReal.so`), so
|
||||
`MobileGLServerService` execs `libMobileGLServer.so <endpoint> --serve` = the probe's route server
|
||||
in the server app's own domain, and B1's broker connects to it and writes its PairBind pair.
|
||||
|
||||
* `--serve` (argv[2]): listen on `<endpoint>`; pair each two connections by their PairBind frame
|
||||
length (control 64 bytes, aux 68); per client variant receive the handle, import it into
|
||||
Vulkan and GL, and run the T0S rounds on the control connection. Rows `ROUTE-<variant>-<size>`.
|
||||
* `--route-client[=@endpoint]`: from `MOBILEGL_IPC_CONTROL=fd:<c>,<a>` (the B1 helper; presents
|
||||
its own PairBind pair unless `MOBILEGL_IPC_FD_PAIRED=1`) or by connecting itself (same app only).
|
||||
Two variants per run: `direct` (`AHardwareBuffer_sendHandleToUnixSocket` on the aux connection)
|
||||
and `hop` (sent on a fresh socketpair whose other end travels over aux by `SCM_RIGHTS`).
|
||||
`--size`, `--sustained-lock=N` (default 4), `--regions-at-end` as above.
|
||||
|
||||
Never ship an APK built with `extmemProbeAsServer`: it has no render server.
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
#include <signal.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -2786,6 +2787,9 @@ static void runT0Parent(VkCtx& c, GlCtx& g, bool glOk, uint64_t size) {
|
||||
// mapping kept stale lines".
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static int t0sClientRounds(int sock, AHardwareBuffer* ahb, void* p, int lockRc, uint32_t rounds,
|
||||
uint32_t seedBase);
|
||||
|
||||
static int childT0Sustained(int sock) {
|
||||
setRecvTimeout(sock, 60);
|
||||
T0SRequest rq{};
|
||||
@@ -2832,12 +2836,23 @@ static int childT0Sustained(int sock) {
|
||||
int sendRc = AHardwareBuffer_sendHandleToUnixSocket(ahb, sock);
|
||||
pr("child: T0S held lock ptr=%p, sendHandle rc=%d", p, sendRc);
|
||||
if (sendRc != 0) return 6;
|
||||
const int rc2 = t0sClientRounds(sock, ahb, p, lockRc, rq.rounds, rq.seedBase);
|
||||
AHardwareBuffer_release(ahb);
|
||||
return rc2;
|
||||
}
|
||||
|
||||
// The client half of the T0S rounds, after the handle has been sent: per round the CPU writes
|
||||
// REG_A through the held lock, the server's GPU reads it and fills REG_E / REG_F, and the client
|
||||
// checks both fills through the still-held pointer; then unlock + relock. The caller releases.
|
||||
static int t0sClientRounds(int sock, AHardwareBuffer* ahb, void* p, int lockRc, uint32_t rounds,
|
||||
uint32_t seedBase) {
|
||||
uint32_t tag = 0;
|
||||
size_t got = 0;
|
||||
uint32_t lastVk = 0, lastGl = 0, lastMask = 0;
|
||||
for (uint32_t i = 0; i < rq.rounds; ++i) {
|
||||
for (uint32_t i = 0; i < rounds; ++i) {
|
||||
T0SRound r{};
|
||||
r.round = i;
|
||||
r.seedA = rq.seedBase + i * 0x01010101u;
|
||||
r.seedA = seedBase + i * 0x01010101u;
|
||||
writeRegion(p, REG_A, r.seedA); // through the held lock, no unlock
|
||||
if (!sendMsg(sock, MSG_T0S_ROUND, &r, sizeof(r), -1)) return 7;
|
||||
T0SGpuDone gd{};
|
||||
@@ -2868,7 +2883,6 @@ static int childT0Sustained(int sock) {
|
||||
snprintf(dn.note, sizeof(dn.note), "held ptr=%p relock ptr=%p", p, q);
|
||||
sendMsg(sock, MSG_T0S_DONE, &dn, sizeof(dn), -1);
|
||||
recvMsg(sock, &tag, nullptr, 0, &got, nullptr); // BYE
|
||||
AHardwareBuffer_release(ahb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2916,6 +2930,10 @@ static GLuint buildT0SCompute(std::string* fail) {
|
||||
return prog;
|
||||
}
|
||||
|
||||
static void t0sServe(VkCtx& c, GlCtx& g, bool glOk, int sock, AHardwareBuffer* ahb, uint64_t size,
|
||||
uint32_t rounds, const char* rowPrefix, pid_t pid);
|
||||
static void printSummary();
|
||||
|
||||
static void runT0SustainedParent(VkCtx& c, GlCtx& g, bool glOk, uint64_t size, uint32_t rounds) {
|
||||
const uint32_t seedBase = 0x5A000001u;
|
||||
int sock = -1;
|
||||
@@ -2949,7 +2967,19 @@ static void runT0SustainedParent(VkCtx& c, GlCtx& g, bool glOk, uint64_t size, u
|
||||
reapChild(pid);
|
||||
return;
|
||||
}
|
||||
t0sServe(c, g, glOk, sock, ahb, size, rounds, "T0S-sustained-lock", pid);
|
||||
AHardwareBuffer_release(ahb);
|
||||
close(sock);
|
||||
}
|
||||
|
||||
// The server half of the T0S rounds on a received (still client-locked) AHB: import it into
|
||||
// Vulkan and GL, run `rounds` rounds against the client on `sock`, take its DONE, send BYE, and
|
||||
// record <rowPrefix>-vulkan / -gles / <rowPrefix>. `pid` >= 0 is a spawned child to reap. The
|
||||
// caller releases the AHB and closes `sock`.
|
||||
static void t0sServe(VkCtx& c, GlCtx& g, bool glOk, int sock, AHardwareBuffer* ahb, uint64_t size,
|
||||
uint32_t rounds, const char* rowPrefix, pid_t pid) {
|
||||
uint32_t tag = 0;
|
||||
size_t got = 0;
|
||||
// --- Vulkan import of the (still client-locked) AHB ---
|
||||
VkBuffer vbuf = VK_NULL_HANDLE;
|
||||
VkDeviceMemory vmem = VK_NULL_HANDLE;
|
||||
@@ -3115,7 +3145,9 @@ static void runT0SustainedParent(VkCtx& c, GlCtx& g, bool glOk, uint64_t size, u
|
||||
dn.relockMismatchE = dn.relockMismatchF = -3;
|
||||
bool gotDone = recvMsg(sock, &tag, &dn, sizeof(dn), &got, nullptr) && tag == MSG_T0S_DONE;
|
||||
sendMsg(sock, MSG_BYE, nullptr, 0, -1);
|
||||
std::string reap = reapChild(pid);
|
||||
std::string reap = pid >= 0 ? reapChild(pid) : std::string("no child");
|
||||
const std::string rowVk = std::string(rowPrefix) + "-vulkan";
|
||||
const std::string rowGl = std::string(rowPrefix) + "-gles";
|
||||
|
||||
auto rowStatus = [&](bool ready, uint32_t rd, uint32_t wr) -> const char* {
|
||||
if (!ready) return "FAIL";
|
||||
@@ -3123,13 +3155,13 @@ static void runT0SustainedParent(VkCtx& c, GlCtx& g, bool glOk, uint64_t size, u
|
||||
if (rd > 0 || wr > 0) return "PARTIAL";
|
||||
return "FAIL";
|
||||
};
|
||||
record("T0S-sustained-lock-vulkan", rowStatus(vkReady, vkRead, vkWrite),
|
||||
record(rowVk.c_str(), rowStatus(vkReady, vkRead, vkWrite),
|
||||
fmt("rounds=%u/%u gpuReadOfHeldCpuWrite=%u gpuFillSeenByHeldLock=%u serverHostMapRead=%u%s | after "
|
||||
"unlock+relock: fill=%lld | %s%s",
|
||||
roundsRun, rounds, vkRead, vkWrite, vkHostRead, vhost ? "" : "(no host map)",
|
||||
(long long)dn.relockMismatchE, vkReady ? vkFirst.c_str() : vkFail.c_str(),
|
||||
hostFirst.empty() ? "" : (" | " + hostFirst).c_str()));
|
||||
record("T0S-sustained-lock-gles", rowStatus(glReady, glRead, glWrite),
|
||||
record(rowGl.c_str(), rowStatus(glReady, glRead, glWrite),
|
||||
fmt("rounds=%u/%u computeReadOfHeldCpuWrite=%u computeFillSeenByHeldLock=%u | after unlock+relock: "
|
||||
"fill=%lld | %s",
|
||||
roundsRun, rounds, glRead, glWrite, (long long)dn.relockMismatchF,
|
||||
@@ -3157,7 +3189,7 @@ static void runT0SustainedParent(VkCtx& c, GlCtx& g, bool glOk, uint64_t size, u
|
||||
}
|
||||
std::string why;
|
||||
const char* status = legVerdict(legs, &why);
|
||||
record("T0S-sustained-lock", status,
|
||||
record(rowPrefix, status,
|
||||
fmt("%s | size=%llu regionBase=%llu rounds=%u client lock rc=%d, unlock rc=%d, relock rc=%d (%s) %s | %s",
|
||||
legTrace(legs).c_str(), (unsigned long long)size, (unsigned long long)gRegionBase, rounds,
|
||||
gotDone ? dn.lockRc : -99, gotDone ? dn.unlockRc : -99, gotDone ? dn.relockRc : -99,
|
||||
@@ -3169,12 +3201,354 @@ static void runT0SustainedParent(VkCtx& c, GlCtx& g, bool glOk, uint64_t size, u
|
||||
if (vhost) vkUnmapMemory(c.device, vmem);
|
||||
if (vmem) vkFreeMemory(c.device, vmem, nullptr);
|
||||
if (vbuf) vkDestroyBuffer(c.device, vbuf, nullptr);
|
||||
AHardwareBuffer_release(ahb);
|
||||
close(sock);
|
||||
(void)lastVk;
|
||||
(void)lastGl;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// P11 B2 step 1: does a client's AHardwareBuffer cross the REAL routes into the server app?
|
||||
//
|
||||
// server `libMobileGLServer.so <endpoint> --serve`: the probe packaged in the render server's
|
||||
// place (-Pmobilegl.extmemProbeAsServer=ON), so MobileGLServerService execs it where the
|
||||
// supervisor would run - in the server app's own untrusted_app domain - and B1's broker
|
||||
// connects to it, writes its PairBind pair and hands the connected pair to the helper.
|
||||
// Each accepted pair: skip the PairBind frame on both connections (told apart by the
|
||||
// frame length: control 64 bytes, aux 68 - PairBindFrames.java), then serve the client.
|
||||
// client `extmem_probe --route-client[=<endpoint>]`: exec'd by the B1 helper with
|
||||
// MOBILEGL_IPC_CONTROL=fd:<control>,<aux> (routes a/b), or connecting to <endpoint>
|
||||
// itself (same app only - another domain is refused `connectto`, B0).
|
||||
//
|
||||
// Per variant the client allocates a BLOB AHB, locks it ONCE for the whole run, and hands it over
|
||||
// direct AHardwareBuffer_sendHandleToUnixSocket on the aux connection itself;
|
||||
// hop on a fresh socketpair whose other end travels over aux by SCM_RIGHTS (the server
|
||||
// then receives the handle from that socket).
|
||||
// Then the T0S rounds run on the control connection: the server imports the handle into Vulkan
|
||||
// and GL, its GPU reads what the client's CPU wrote through the held lock and writes fills the
|
||||
// client checks through the same held pointer. Rows: ROUTE-<variant>-<size>{,-vulkan,-gles}.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
enum : uint32_t { MSG_RT_HELLO = 40, MSG_RT_HOP = 41, MSG_RT_SERVER = 42 };
|
||||
|
||||
struct RtHello {
|
||||
uint32_t variant; // 0 direct, 1 hop
|
||||
uint32_t rounds;
|
||||
uint64_t size;
|
||||
uint64_t regionBase;
|
||||
uint32_t seedBase;
|
||||
int32_t pad;
|
||||
char client[192];
|
||||
char note[160];
|
||||
};
|
||||
|
||||
struct RtServer {
|
||||
int32_t received; // 1 = the handle arrived and describes as a BLOB of `size`
|
||||
int32_t pad;
|
||||
char server[192];
|
||||
char note[192];
|
||||
};
|
||||
|
||||
static std::string selfContext() {
|
||||
std::string sec = readSmallFile("/proc/self/attr/current");
|
||||
while (!sec.empty() && (sec.back() == '\n' || sec.back() == '\0')) sec.pop_back();
|
||||
return fmt("%s uid=%d pid=%d", sec.c_str(), (int)getuid(), (int)getpid());
|
||||
}
|
||||
|
||||
static const char* kRtControlPrefixHex =
|
||||
"100000004d474c4308000e0007000800080000000000000f0c0000000000060008000400060000000400000010000000";
|
||||
static const char* kRtAuxPrefixHex =
|
||||
"100000004d474c4308000c0007000800080000000000000f0c00000008000c000800070008000000000000010400000010000000";
|
||||
|
||||
static std::vector<uint8_t> rtPairBindFrame(bool aux, const uint8_t nonce[16]) {
|
||||
const char* hex = aux ? kRtAuxPrefixHex : kRtControlPrefixHex;
|
||||
std::vector<uint8_t> payload;
|
||||
for (size_t i = 0; hex[i] && hex[i + 1]; i += 2) {
|
||||
char b[3] = {hex[i], hex[i + 1], 0};
|
||||
payload.push_back((uint8_t)strtoul(b, nullptr, 16));
|
||||
}
|
||||
payload.insert(payload.end(), nonce, nonce + 16);
|
||||
std::vector<uint8_t> out(8);
|
||||
const uint32_t magic = 0x464C474Du, len = (uint32_t)payload.size();
|
||||
memcpy(out.data(), &magic, 4);
|
||||
memcpy(out.data() + 4, &len, 4);
|
||||
out.insert(out.end(), payload.begin(), payload.end());
|
||||
return out;
|
||||
}
|
||||
|
||||
// Reads one framed control message's header and payload; returns the payload length or -1.
|
||||
static int rtSkipFrame(int fd) {
|
||||
uint32_t hdr[2] = {0, 0};
|
||||
if (!readAll(fd, hdr, sizeof(hdr))) return -1;
|
||||
if (hdr[0] != 0x464C474Du || hdr[1] > 4096) return -1;
|
||||
std::vector<uint8_t> skip(hdr[1]);
|
||||
if (hdr[1] && !readAll(fd, skip.data(), hdr[1])) return -1;
|
||||
return (int)hdr[1];
|
||||
}
|
||||
|
||||
static int rtListen(const char* endpoint) {
|
||||
int s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (s < 0) return -1;
|
||||
struct sockaddr_un a;
|
||||
memset(&a, 0, sizeof(a));
|
||||
a.sun_family = AF_UNIX;
|
||||
socklen_t len;
|
||||
if (endpoint[0] == '@') {
|
||||
strncpy(a.sun_path + 1, endpoint + 1, sizeof(a.sun_path) - 2);
|
||||
len = (socklen_t)(offsetof(struct sockaddr_un, sun_path) + 1 + strlen(endpoint + 1));
|
||||
} else {
|
||||
unlink(endpoint);
|
||||
strncpy(a.sun_path, endpoint, sizeof(a.sun_path) - 1);
|
||||
len = (socklen_t)sizeof(a);
|
||||
}
|
||||
if (bind(s, (struct sockaddr*)&a, len) != 0 || listen(s, 16) != 0) {
|
||||
pr("route server: bind/listen %s failed errno=%d", endpoint, errno);
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static int rtConnect(const char* endpoint) {
|
||||
int s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (s < 0) return -1;
|
||||
struct sockaddr_un a;
|
||||
memset(&a, 0, sizeof(a));
|
||||
a.sun_family = AF_UNIX;
|
||||
socklen_t len;
|
||||
if (endpoint[0] == '@') {
|
||||
strncpy(a.sun_path + 1, endpoint + 1, sizeof(a.sun_path) - 2);
|
||||
len = (socklen_t)(offsetof(struct sockaddr_un, sun_path) + 1 + strlen(endpoint + 1));
|
||||
} else {
|
||||
strncpy(a.sun_path, endpoint, sizeof(a.sun_path) - 1);
|
||||
len = (socklen_t)sizeof(a);
|
||||
}
|
||||
if (connect(s, (struct sockaddr*)&a, len) != 0) {
|
||||
pr("route client: connect %s failed errno=%d", endpoint, errno);
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static void rtServePair(VkCtx& c, GlCtx& g, bool glOk, int ctl, int aux) {
|
||||
setRecvTimeout(ctl, 60);
|
||||
setRecvTimeout(aux, 60);
|
||||
for (;;) {
|
||||
RtHello h{};
|
||||
uint32_t tag = 0;
|
||||
size_t got = 0;
|
||||
if (!recvMsg(ctl, &tag, &h, sizeof(h), &got, nullptr) || tag == MSG_BYE) break;
|
||||
if (tag != MSG_RT_HELLO) {
|
||||
pr("route server: unexpected tag %u", tag);
|
||||
break;
|
||||
}
|
||||
h.client[sizeof(h.client) - 1] = 0;
|
||||
h.note[sizeof(h.note) - 1] = 0;
|
||||
gRegionBase = h.regionBase;
|
||||
const char* vname = h.variant ? "hop" : "direct";
|
||||
pr("route server: client [%s] variant=%s size=%llu rounds=%u (%s)", h.client, vname,
|
||||
(unsigned long long)h.size, h.rounds, h.note);
|
||||
AHardwareBuffer* ahb = nullptr;
|
||||
int rc = -1;
|
||||
std::string how;
|
||||
if (h.variant == 0) {
|
||||
rc = AHardwareBuffer_recvHandleFromUnixSocket(aux, &ahb);
|
||||
how = fmt("recvHandleFromUnixSocket(aux) rc=%d errno=%d", rc, errno);
|
||||
} else {
|
||||
int hopFd = -1;
|
||||
uint32_t t2 = 0;
|
||||
if (!recvMsg(aux, &t2, nullptr, 0, &got, &hopFd) || t2 != MSG_RT_HOP || hopFd < 0) {
|
||||
how = fmt("hop socket did not arrive over aux (tag=%u fd=%d errno=%d)", t2, hopFd, errno);
|
||||
} else {
|
||||
rc = AHardwareBuffer_recvHandleFromUnixSocket(hopFd, &ahb);
|
||||
how = fmt("hop %s; recvHandleFromUnixSocket(hop) rc=%d errno=%d", describeFd(hopFd).c_str(), rc,
|
||||
errno);
|
||||
close(hopFd);
|
||||
}
|
||||
}
|
||||
RtServer sv{};
|
||||
snprintf(sv.server, sizeof(sv.server), "%s", selfContext().c_str());
|
||||
if (rc == 0 && ahb) {
|
||||
AHardwareBuffer_Desc d{};
|
||||
AHardwareBuffer_describe(ahb, &d);
|
||||
sv.received = (d.format == AHARDWAREBUFFER_FORMAT_BLOB && d.width == h.size) ? 1 : 0;
|
||||
snprintf(sv.note, sizeof(sv.note), "%s | desc w=%u fmt=0x%x usage=0x%llx", how.c_str(), d.width,
|
||||
d.format, (unsigned long long)d.usage);
|
||||
} else {
|
||||
snprintf(sv.note, sizeof(sv.note), "%s", how.c_str());
|
||||
}
|
||||
pr("route server: %s", sv.note);
|
||||
sendMsg(ctl, MSG_RT_SERVER, &sv, sizeof(sv), -1);
|
||||
const std::string row = fmt("ROUTE-%s-%llu", vname, (unsigned long long)h.size);
|
||||
if (!sv.received) {
|
||||
record(row.c_str(), "FAIL", fmt("client [%s] -> server [%s]: %s", h.client, sv.server, sv.note));
|
||||
if (ahb) AHardwareBuffer_release(ahb);
|
||||
break;
|
||||
}
|
||||
pr("route server: client [%s] -> server [%s]", h.client, sv.server);
|
||||
t0sServe(c, g, glOk, ctl, ahb, h.size, h.rounds, row.c_str(), -1);
|
||||
AHardwareBuffer_release(ahb);
|
||||
}
|
||||
}
|
||||
|
||||
static int routeServe(const char* endpoint) {
|
||||
setvbuf(stdout, nullptr, _IOLBF, 0);
|
||||
gRole = "route-server";
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
pr("extmem route server: %s", selfContext().c_str());
|
||||
VkCtx c;
|
||||
const bool vkOk = vkCtxInit(c, false);
|
||||
GlCtx g;
|
||||
const bool glOk = glCtxInit(g);
|
||||
pr("extmem route server: vulkan=%d gl=%d", (int)vkOk, (int)glOk);
|
||||
if (!vkOk) return 1;
|
||||
int ls = rtListen(endpoint);
|
||||
if (ls < 0) return 2;
|
||||
// The service's readiness line (srv.sh waits for it) - the real supervisor says the same.
|
||||
pr("extmem route server: listening on %s", endpoint);
|
||||
int pendingCtl = -1, pendingAux = -1;
|
||||
for (;;) {
|
||||
int s = accept4(ls, nullptr, nullptr, SOCK_CLOEXEC);
|
||||
if (s < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
pr("route server: accept errno=%d", errno);
|
||||
return 3;
|
||||
}
|
||||
setRecvTimeout(s, 10);
|
||||
const int n = rtSkipFrame(s);
|
||||
if (n == 64) {
|
||||
if (pendingCtl >= 0) close(pendingCtl);
|
||||
pendingCtl = s;
|
||||
} else if (n == 68) {
|
||||
if (pendingAux >= 0) close(pendingAux);
|
||||
pendingAux = s;
|
||||
} else {
|
||||
pr("route server: connection without a PairBind (frame=%d) closed", n);
|
||||
close(s);
|
||||
continue;
|
||||
}
|
||||
if (pendingCtl >= 0 && pendingAux >= 0) {
|
||||
pr("route server: paired control=%d aux=%d", pendingCtl, pendingAux);
|
||||
rtServePair(c, g, glOk, pendingCtl, pendingAux);
|
||||
close(pendingCtl);
|
||||
close(pendingAux);
|
||||
pendingCtl = pendingAux = -1;
|
||||
gResults.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int rtClientVariant(int ctl, int aux, uint32_t variant, uint64_t size, uint32_t rounds, bool atEnd) {
|
||||
AHardwareBuffer_Desc desc{};
|
||||
desc.width = (uint32_t)size;
|
||||
desc.height = 1;
|
||||
desc.layers = 1;
|
||||
desc.format = AHARDWAREBUFFER_FORMAT_BLOB;
|
||||
desc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
|
||||
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER;
|
||||
AHardwareBuffer* ahb = nullptr;
|
||||
const int arc = AHardwareBuffer_allocate(&desc, &ahb);
|
||||
const char* vname = variant ? "hop" : "direct";
|
||||
const std::string row = fmt("ROUTE-%s-%llu-client", vname, (unsigned long long)size);
|
||||
if (arc != 0 || !ahb) {
|
||||
record(row.c_str(), "FAIL", fmt("AHardwareBuffer_allocate rc=%d", arc));
|
||||
return 3;
|
||||
}
|
||||
void* p = nullptr;
|
||||
const int lockRc = AHardwareBuffer_lock(
|
||||
ahb, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, nullptr, &p);
|
||||
if (lockRc != 0 || !p) {
|
||||
record(row.c_str(), "FAIL", fmt("held lock rc=%d", lockRc));
|
||||
AHardwareBuffer_release(ahb);
|
||||
return 4;
|
||||
}
|
||||
memset(p, 0, (size_t)size);
|
||||
gRegionBase = atEnd ? size - kRegionCount * kRegion : 0;
|
||||
RtHello h{};
|
||||
h.variant = variant;
|
||||
h.rounds = rounds;
|
||||
h.size = size;
|
||||
h.regionBase = gRegionBase;
|
||||
h.seedBase = 0x3B000001u + variant * 0x100u;
|
||||
snprintf(h.client, sizeof(h.client), "%s", selfContext().c_str());
|
||||
snprintf(h.note, sizeof(h.note), "held lock ptr=%p", p);
|
||||
int sendRc = -1;
|
||||
if (!sendMsg(ctl, MSG_RT_HELLO, &h, sizeof(h), -1)) {
|
||||
record(row.c_str(), "FAIL", fmt("hello send errno=%d", errno));
|
||||
} else if (variant == 0) {
|
||||
sendRc = AHardwareBuffer_sendHandleToUnixSocket(ahb, aux);
|
||||
} else {
|
||||
int sp[2];
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sp) == 0) {
|
||||
sendRc = AHardwareBuffer_sendHandleToUnixSocket(ahb, sp[0]);
|
||||
close(sp[0]);
|
||||
if (sendRc == 0 && !sendMsg(aux, MSG_RT_HOP, nullptr, 0, sp[1])) sendRc = -errno;
|
||||
close(sp[1]);
|
||||
}
|
||||
}
|
||||
RtServer sv{};
|
||||
uint32_t tag = 0;
|
||||
size_t got = 0;
|
||||
const bool haveServer = recvMsg(ctl, &tag, &sv, sizeof(sv), &got, nullptr) && tag == MSG_RT_SERVER;
|
||||
sv.server[sizeof(sv.server) - 1] = 0;
|
||||
sv.note[sizeof(sv.note) - 1] = 0;
|
||||
int rrc = 10;
|
||||
if (sendRc != 0 || !haveServer || !sv.received) {
|
||||
record(row.c_str(), "FAIL",
|
||||
fmt("sendRc=%d serverAnswered=%d received=%d server [%s]: %s", sendRc, (int)haveServer,
|
||||
(int)sv.received, sv.server, sv.note));
|
||||
} else {
|
||||
rrc = t0sClientRounds(ctl, ahb, p, lockRc, rounds, h.seedBase);
|
||||
record(row.c_str(), rrc == 0 ? "OK" : "FAIL",
|
||||
fmt("client [%s] -> server [%s] | %s | rounds rc=%d (the server's rows carry the GPU verdict)",
|
||||
h.client, sv.server, sv.note, rrc));
|
||||
}
|
||||
AHardwareBuffer_release(ahb);
|
||||
return rrc;
|
||||
}
|
||||
|
||||
static int routeClient(const char* endpoint, uint64_t size, uint32_t rounds, bool atEnd) {
|
||||
gRole = "route-client";
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
int ctl = -1, aux = -1;
|
||||
const char* env = getenv("MOBILEGL_IPC_CONTROL");
|
||||
bool paired = false;
|
||||
if (endpoint && *endpoint) {
|
||||
ctl = rtConnect(endpoint);
|
||||
aux = rtConnect(endpoint);
|
||||
} else if (env && !strncmp(env, "fd:", 3) && sscanf(env + 3, "%d,%d", &ctl, &aux) == 2) {
|
||||
const char* pe = getenv("MOBILEGL_IPC_FD_PAIRED");
|
||||
paired = pe && !strcmp(pe, "1");
|
||||
} else {
|
||||
pr("route client: no endpoint and MOBILEGL_IPC_CONTROL is not fd:<c>,<a> (%s)", env ? env : "unset");
|
||||
return 2;
|
||||
}
|
||||
if (ctl < 0 || aux < 0) return 2;
|
||||
pr("route client: %s control=%d aux=%d %s aux=%s", selfContext().c_str(), ctl, aux,
|
||||
paired ? "paired at hand-off" : "presenting its own PairBind pair", describeFd(aux).c_str());
|
||||
if (!paired) {
|
||||
uint8_t nonce[16];
|
||||
FILE* r = fopen("/dev/urandom", "rb");
|
||||
if (!r || fread(nonce, 1, 16, r) != 16) memset(nonce, 0x5A, 16);
|
||||
if (r) fclose(r);
|
||||
const std::vector<uint8_t> fc = rtPairBindFrame(false, nonce), fa = rtPairBindFrame(true, nonce);
|
||||
if (!writeAll(ctl, fc.data(), fc.size()) || !writeAll(aux, fa.data(), fa.size())) {
|
||||
pr("route client: PairBind write failed errno=%d", errno);
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
setRecvTimeout(ctl, 60);
|
||||
int worst = 0;
|
||||
for (uint32_t v = 0; v < 2; ++v) {
|
||||
const int rc = rtClientVariant(ctl, aux, v, size, rounds, atEnd);
|
||||
if (rc != 0) worst = rc;
|
||||
}
|
||||
sendMsg(ctl, MSG_BYE, nullptr, 0, -1);
|
||||
printSummary();
|
||||
close(ctl);
|
||||
close(aux);
|
||||
return worst;
|
||||
}
|
||||
|
||||
#else // !PROBE_HAVE_AHB
|
||||
|
||||
static int childT0(int) {
|
||||
@@ -3191,6 +3565,14 @@ static int childT0Sustained(int) {
|
||||
static void runT0SustainedParent(VkCtx&, GlCtx&, bool, uint64_t, uint32_t) {
|
||||
record("T0S-sustained-lock", "SKIP", "AHardwareBuffer is Android-only; host build cannot run T0S");
|
||||
}
|
||||
static int routeServe(const char*) {
|
||||
pr("the AHB route server is Android-only");
|
||||
return 1;
|
||||
}
|
||||
static int routeClient(const char*, uint64_t, uint32_t, bool) {
|
||||
pr("the AHB route client is Android-only");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif // PROBE_HAVE_AHB
|
||||
|
||||
@@ -3690,7 +4072,11 @@ static std::vector<std::string> readArgsFile(const std::string& path) {
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// P11 B2 step 1: exec'd by MobileGLServerService as `libMobileGLServer.so <endpoint> --serve`.
|
||||
if (argc >= 3 && !strcmp(argv[2], "--serve")) return routeServe(argv[1]);
|
||||
uint64_t size = kDefaultSize;
|
||||
bool routeClientMode = false;
|
||||
std::string routeEndpoint;
|
||||
const char* childRoute = nullptr;
|
||||
bool doT1 = true, doT0 = true, doT3 = true, doGles = true;
|
||||
bool regionsAtEnd = false;
|
||||
@@ -3715,6 +4101,11 @@ int main(int argc, char** argv) {
|
||||
static std::string route; // outlives `args`
|
||||
route = a + 8;
|
||||
childRoute = route.c_str();
|
||||
} else if (!strcmp(a, "--route-client")) {
|
||||
routeClientMode = true;
|
||||
} else if (!strncmp(a, "--route-client=", 15)) {
|
||||
routeClientMode = true;
|
||||
routeEndpoint = a + 15;
|
||||
} else if (!strncmp(a, "--size=", 7)) {
|
||||
size = strtoull(a + 7, nullptr, 0);
|
||||
} else if (!strncmp(a, "--region-base=", 14)) {
|
||||
@@ -3740,6 +4131,8 @@ int main(int argc, char** argv) {
|
||||
doGles = false;
|
||||
} else if (!strcmp(a, "--help")) {
|
||||
printf("usage: extmem_probe [markerPath] [--size=BYTES] [--regions-at-end] [--sustained-lock[=ROUNDS]]\n"
|
||||
" extmem_probe --route-client[=@endpoint] [--size=BYTES] [--sustained-lock=ROUNDS] [--regions-at-end]\n"
|
||||
" extmem_probe <endpoint> --serve (packaged as libMobileGLServer.so)\n"
|
||||
" [--only-t0|--only-t1|--only-t3|--only-gles|--only-sustained] [--no-gles]\n"
|
||||
" markerPath: exec-hook mode; options are also read from <markerPath>.args\n");
|
||||
return 0;
|
||||
@@ -3748,6 +4141,8 @@ int main(int argc, char** argv) {
|
||||
if (size < kRegionCount * kRegion) size = kRegionCount * kRegion;
|
||||
size = (size + kRegion - 1) / kRegion * kRegion;
|
||||
if (regionsAtEnd && !childRoute) gRegionBase = size - kRegionCount * kRegion;
|
||||
if (routeClientMode)
|
||||
return routeClient(routeEndpoint.c_str(), size, sustainedRounds ? sustainedRounds : 4, regionsAtEnd);
|
||||
|
||||
// A peer that has already exited must not take this process down with it.
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
Reference in New Issue
Block a user