зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1743454 - Add junit test to ensure GPU process crash triggers GeckoView crash reporter. r=agi,aosmond
Add a function to GPUProcessManager to force the GPU process to crash, and expose it through gfxInfo. Expose this to geckoview tests via the test-support webextension. Add a junit test GpuCrashTest, which triggers a GPU process crash and ensures the crash reporter was notified. Additionally, ensure the TestCrashHandler service is stopped in between tests, as otherwise only the first crash test to run will be notified of the crash. Differential Revision: https://phabricator.services.mozilla.com/D132812
This commit is contained in:
Родитель
3fcda9ab4f
Коммит
3e2edea9fd
|
@ -630,6 +630,11 @@ mozilla::ipc::IPCResult GPUParent::RecvTestTriggerMetrics() {
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult GPUParent::RecvCrashProcess() {
|
||||
MOZ_CRASH("Deliberate GPU process crash");
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void GPUParent::ActorDestroy(ActorDestroyReason aWhy) {
|
||||
if (AbnormalShutdown == aWhy) {
|
||||
NS_WARNING("Shutting down GPU process early due to a crash!");
|
||||
|
|
|
@ -101,6 +101,8 @@ class GPUParent final : public PGPUParent {
|
|||
|
||||
mozilla::ipc::IPCResult RecvTestTriggerMetrics();
|
||||
|
||||
mozilla::ipc::IPCResult RecvCrashProcess();
|
||||
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -239,6 +239,8 @@ uint64_t GPUProcessHost::GetProcessToken() const { return mProcessToken; }
|
|||
|
||||
void GPUProcessHost::KillProcess() { KillHard("DiagnosticKill"); }
|
||||
|
||||
void GPUProcessHost::CrashProcess() { mGPUChild->SendCrashProcess(); }
|
||||
|
||||
void GPUProcessHost::DestroyProcess() {
|
||||
// Cancel all tasks. We don't want anything triggering after our caller
|
||||
// expects this to go away.
|
||||
|
|
|
@ -105,9 +105,12 @@ class GPUProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
|
|||
|
||||
void SetListener(Listener* aListener);
|
||||
|
||||
// Used for tests and diagnostics
|
||||
// Kills the GPU process. Used for tests and diagnostics
|
||||
void KillProcess();
|
||||
|
||||
// Causes the GPU process to crash. Used for tests and diagnostics
|
||||
void CrashProcess();
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
java::CompositorSurfaceManager::Param GetCompositorSurfaceManager();
|
||||
#endif
|
||||
|
|
|
@ -812,6 +812,14 @@ void GPUProcessManager::KillProcess() {
|
|||
mProcess->KillProcess();
|
||||
}
|
||||
|
||||
void GPUProcessManager::CrashProcess() {
|
||||
if (!mProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
mProcess->CrashProcess();
|
||||
}
|
||||
|
||||
void GPUProcessManager::DestroyProcess(bool aUnexpectedShutdown) {
|
||||
if (!mProcess) {
|
||||
return;
|
||||
|
|
|
@ -173,9 +173,12 @@ class GPUProcessManager final : public GPUProcessHost::Listener {
|
|||
// true if the message was sent, false if not.
|
||||
bool NotifyGpuObservers(const char* aTopic);
|
||||
|
||||
// Used for tests and diagnostics
|
||||
// Kills the GPU process. Used for tests and diagnostics
|
||||
void KillProcess();
|
||||
|
||||
// Causes the GPU process to crash. Used for tests and diagnostics
|
||||
void CrashProcess();
|
||||
|
||||
// Returns -1 if there is no GPU process, or the platform pid for it.
|
||||
base::ProcessId GPUProcessPid();
|
||||
|
||||
|
|
|
@ -122,6 +122,9 @@ parent:
|
|||
// Asks the gpu process to trigger test-only instrumentation.
|
||||
async TestTriggerMetrics();
|
||||
|
||||
// Causes the GPU process to crash. Used for tests and diagnostics.
|
||||
async CrashProcess();
|
||||
|
||||
child:
|
||||
// Sent when the GPU process has initialized devices. This occurs once, after
|
||||
// Init().
|
||||
|
|
|
@ -240,6 +240,10 @@ class MockGfxInfo final : public nsIGfxInfo {
|
|||
bool* _retval) override {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
NS_IMETHOD CrashGPUProcessForTests() override {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHOD_(void) GetData() override {}
|
||||
|
||||
private:
|
||||
|
|
|
@ -59,6 +59,12 @@ const APIS = {
|
|||
PromiseAllPaintsDone({ tab }) {
|
||||
return browser.test.promiseAllPaintsDone(tab.id);
|
||||
},
|
||||
UsingGpuProcess() {
|
||||
return browser.test.usingGpuProcess();
|
||||
},
|
||||
CrashGpuProcess() {
|
||||
return browser.test.crashGpuProcess();
|
||||
},
|
||||
};
|
||||
|
||||
port.onMessage.addListener(async message => {
|
||||
|
|
|
@ -243,6 +243,20 @@ this.test = class extends ExtensionAPI {
|
|||
.getActor("TestSupport")
|
||||
.sendQuery("PromiseAllPaintsDone");
|
||||
},
|
||||
|
||||
async usingGpuProcess() {
|
||||
const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(
|
||||
Ci.nsIGfxInfo
|
||||
);
|
||||
return gfxInfo.usingGPUProcess;
|
||||
},
|
||||
|
||||
async crashGpuProcess() {
|
||||
const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(
|
||||
Ci.nsIGfxInfo
|
||||
);
|
||||
return gfxInfo.crashGPUProcessForTests();
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -197,6 +197,21 @@
|
|||
"name": "tabId"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "usingGpuProcess",
|
||||
"type": "function",
|
||||
"async": true,
|
||||
"description": "Returns true if Gecko is using a GPU process.",
|
||||
"parameters": []
|
||||
},
|
||||
|
||||
{
|
||||
"name": "crashGpuProcess",
|
||||
"type": "function",
|
||||
"async": true,
|
||||
"description": "Causes the GPU process to crash.",
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package org.mozilla.geckoview.test
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.filters.MediumTest
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assume.assumeTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.geckoview.BuildConfig
|
||||
import org.mozilla.geckoview.GeckoRuntime
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.IgnoreCrash
|
||||
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
class GpuCrashTest : BaseSessionTest() {
|
||||
val client = TestCrashHandler.Client(InstrumentationRegistry.getInstrumentation().targetContext)
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
assertTrue(client.connect(sessionRule.env.defaultTimeoutMillis))
|
||||
client.setEvalNextCrashDump(/* expectFatal */ false, GeckoRuntime.CRASHED_PROCESS_TYPE_BACKGROUND_CHILD)
|
||||
}
|
||||
|
||||
@IgnoreCrash
|
||||
@Test
|
||||
fun crashGpu() {
|
||||
// We need the crash reporter for this test
|
||||
assumeTrue(BuildConfig.MOZ_CRASHREPORTER)
|
||||
|
||||
// We need the GPU process for this test
|
||||
assumeTrue(sessionRule.usingGpuProcess())
|
||||
|
||||
// Cause the GPU process to crash.
|
||||
sessionRule.crashGpuProcess()
|
||||
|
||||
val evalResult = client.getEvalResult(sessionRule.env.defaultTimeoutMillis)
|
||||
assertTrue(evalResult.mMsg, evalResult.mResult)
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
client.disconnect()
|
||||
}
|
||||
}
|
|
@ -264,6 +264,10 @@ public class TestCrashHandler extends Service {
|
|||
public synchronized int onStartCommand(final Intent intent, final int flags, final int startId) {
|
||||
if (mMsgHandler != null) {
|
||||
mMsgHandler.reportResult(evalCrashInfo(intent));
|
||||
// We must manually call stopSelf() here to ensure the Service gets killed once the client
|
||||
// unbinds. If we don't, then when the next client attempts to bind for a different test,
|
||||
// onBind() will not be called, and mMsgHandler will not get set.
|
||||
stopSelf();
|
||||
return Service.START_NOT_STICKY;
|
||||
}
|
||||
|
||||
|
|
|
@ -2417,6 +2417,16 @@ public class GeckoSessionTestRule implements TestRule {
|
|||
webExtensionApiCall(session, "PromiseAllPaintsDone", null);
|
||||
}
|
||||
|
||||
/** Returns true if Gecko is using a GPU process. */
|
||||
public boolean usingGpuProcess() {
|
||||
return (Boolean) webExtensionApiCall("UsingGpuProcess", null);
|
||||
}
|
||||
|
||||
/** Causes the GPU process to crash. */
|
||||
public void crashGpuProcess() {
|
||||
webExtensionApiCall("CrashGpuProcess", null);
|
||||
}
|
||||
|
||||
private Object webExtensionApiCall(
|
||||
final @NonNull String apiName, final @NonNull SetArgs argsSetter) {
|
||||
return webExtensionApiCall(null, apiName, argsSetter);
|
||||
|
|
|
@ -1881,6 +1881,17 @@ GfxInfoBase::ControlGPUProcessForXPCShell(bool aEnable, bool* _retval) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP GfxInfoBase::CrashGPUProcessForTests() {
|
||||
GPUProcessManager* gpm = GPUProcessManager::Get();
|
||||
if (!gpm) {
|
||||
// gfxPlatform has not been initialized.
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
gpm->CrashProcess();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
GfxInfoCollectorBase::GfxInfoCollectorBase() {
|
||||
GfxInfoBase::AddCollector(this);
|
||||
}
|
||||
|
|
|
@ -151,6 +151,8 @@ class GfxInfoBase : public nsIGfxInfo,
|
|||
|
||||
NS_IMETHOD ControlGPUProcessForXPCShell(bool aEnable, bool* _retval) override;
|
||||
|
||||
NS_IMETHOD CrashGPUProcessForTests() override;
|
||||
|
||||
// Total number of pixels for all detected screens at startup.
|
||||
int64_t mScreenPixels;
|
||||
|
||||
|
|
|
@ -314,6 +314,9 @@ interface nsIGfxInfo : nsISupports
|
|||
// Forces the GPU process to start or shutdown. This is intended only for
|
||||
// xpcshell-tests.
|
||||
boolean controlGPUProcessForXPCShell(in boolean aEnable);
|
||||
|
||||
// Causes the GPU process to crash. This is intended only for use by tests.
|
||||
void crashGPUProcessForTests();
|
||||
};
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче