Commit f9dd030c authored by Min Qin's avatar Min Qin Committed by Commit Bot

Dump crash stack when renderer hangs on Android

Android currently doesn't report crash stack when renderer hangs.
This CL will ask the renderer process to dump the stack to crash report page, without crashing it.
Maybe i can do this for OOP network process alone for the crbug, but I think in general, we should
have the stack for non-responding renderers.

BUG=934317

Change-Id: I5d039c3c6d026e4b608a4f39da4b71933b19c566
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1498415Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Commit-Queue: Min Qin <qinmin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#638667}
parent 57bab706
......@@ -5,6 +5,7 @@
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/library_loader/library_loader_hooks.h"
#include "base/debug/dump_without_crashing.h"
#include "base/file_descriptor_store.h"
#include "base/logging.h"
#include "base/macros.h"
......@@ -72,5 +73,9 @@ void JNI_ChildProcessService_ExitChildProcess(JNIEnv* env) {
_exit(0);
}
void JNI_ChildProcessService_DumpProcessStack(JNIEnv* env) {
base::debug::DumpWithoutCrashing();
}
} // namespace android
} // namespace base
......@@ -437,6 +437,19 @@ public class ChildProcessConnection {
notifyChildProcessDied();
}
/**
* Dumps the stack of the child process without crashing it.
*/
public void dumpProcessStack() {
assert isRunningOnLauncherThread();
IChildProcessService service = mService;
try {
if (service != null) service.dumpProcessStack();
} catch (RemoteException e) {
Log.e(TAG, "Failed to dump process stack.", e);
}
}
@VisibleForTesting
protected void onServiceConnectedOnLauncherThread(IBinder service) {
assert isRunningOnLauncherThread();
......
......@@ -187,6 +187,19 @@ public abstract class ChildProcessService extends Service {
}
});
}
@Override
public void dumpProcessStack() {
assert mServiceBound;
synchronized (mLibraryInitializedLock) {
if (!mLibraryInitialized) {
Log.e(TAG, "Cannot dump process stack before native is loaded");
return;
}
}
nativeDumpProcessStack();
}
};
/**
......@@ -361,4 +374,9 @@ public abstract class ChildProcessService extends Service {
* Force the child process to exit.
*/
private static native void nativeExitChildProcess();
/**
* Dumps the child process stack without crashing it.
*/
private static native void nativeDumpProcessStack();
}
......@@ -23,4 +23,7 @@ interface IChildProcessService {
// Notifies about memory pressure. The argument is MemoryPressureLevel enum.
oneway void onMemoryPressure(int pressure);
// Dumps the stack for the child process without crashing it.
oneway void dumpProcessStack();
}
......@@ -14,6 +14,7 @@
#include "base/command_line.h"
#include "base/metrics/histogram_macros.h"
#include "base/optional.h"
#include "base/rand_util.h"
#include "chrome/browser/android/chrome_feature_list.h"
#include "chrome/browser/android/feature_utilities.h"
#include "chrome/browser/android/hung_renderer_infobar_delegate.h"
......@@ -495,13 +496,18 @@ TabWebContentsDelegateAndroid::SwapWebContents(
void JNI_TabWebContentsDelegateAndroid_OnRendererUnresponsive(
JNIEnv* env,
const JavaParamRef<jobject>& java_web_contents) {
// Rate limit the number of stack dumps so we don't overwhelm our crash
// reports.
content::WebContents* web_contents =
content::WebContents::FromJavaWebContents(java_web_contents);
if (base::RandDouble() < 0.01)
web_contents->GetMainFrame()->GetProcess()->DumpProcessStack();
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableHungRendererInfoBar)) {
return;
}
content::WebContents* web_contents =
content::WebContents::FromJavaWebContents(java_web_contents);
InfoBarService* infobar_service =
InfoBarService::FromWebContents(web_contents);
DCHECK(!FindHungRendererInfoBar(infobar_service));
......
......@@ -161,6 +161,15 @@ void ChildProcessLauncher::ResetRegisteredFilesForTesting() {
ChildProcessLauncherHelper::ResetRegisteredFilesForTesting();
}
#if defined(OS_ANDROID)
void ChildProcessLauncher::DumpProcessStack() {
base::Process to_pass = process_.process.Duplicate();
GetProcessLauncherTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&ChildProcessLauncherHelper::DumpProcessStack,
helper_, std::move(to_pass)));
}
#endif
ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest(
Client* client) {
Client* ret = client_;
......
......@@ -210,6 +210,10 @@ class CONTENT_EXPORT ChildProcessLauncher {
// support multiple shell context creation in unit_tests.
static void ResetRegisteredFilesForTesting();
#if defined(OS_ANDROID)
// Dumps the stack of the child process without crashing it.
void DumpProcessStack();
#endif
private:
friend class internal::ChildProcessLauncherHelper;
......
......@@ -190,6 +190,9 @@ class ChildProcessLauncherHelper :
void OnChildProcessStarted(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jint handle);
// Dumps the stack of the child process without crashing it.
void DumpProcessStack(const base::Process& process);
#endif // OS_ANDROID
private:
......
......@@ -250,6 +250,14 @@ base::File OpenFileToShare(const base::FilePath& path,
return base::File(base::android::OpenApkAsset(path.value(), region));
}
void ChildProcessLauncherHelper::DumpProcessStack(
const base::Process& process) {
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
return Java_ChildProcessLauncherHelperImpl_dumpProcessStack(env, java_peer_,
process.Handle());
}
// Called from ChildProcessLauncher.java when the ChildProcess was started.
// |handle| is the processID of the child process as originated in Java, 0 if
// the ChildProcess could not be created.
......
......@@ -2647,6 +2647,11 @@ bool RenderProcessHostImpl::GetIntersectsViewport() {
ChildProcessImportance RenderProcessHostImpl::GetEffectiveImportance() {
return effective_importance_;
}
void RenderProcessHostImpl::DumpProcessStack() {
if (child_process_launcher_)
child_process_launcher_->DumpProcessStack();
}
#endif
RendererAudioOutputStreamFactoryContext*
......
......@@ -185,6 +185,7 @@ class CONTENT_EXPORT RenderProcessHostImpl
void RemovePriorityClient(PriorityClient* priority_client) override;
#if defined(OS_ANDROID)
ChildProcessImportance GetEffectiveImportance() override;
void DumpProcessStack() override;
#endif
void SetSuddenTerminationAllowed(bool enabled) override;
bool SuddenTerminationAllowed() override;
......
......@@ -520,6 +520,20 @@ public final class ChildProcessLauncherHelperImpl {
}
}
/**
* Dumps the stack of the child process with |pid| without crashing it.
* @param pid Process id of the child process.
*/
@CalledByNative
private void dumpProcessStack(int pid) {
assert LauncherThread.runningOnLauncherThread();
ChildProcessLauncherHelperImpl launcher = getByPid(pid);
if (launcher != null) {
ChildProcessConnection connection = launcher.mLauncher.getConnection();
connection.dumpProcessStack();
}
}
// Can be called on a number of threads, including launcher, and binder.
private static native void nativeOnChildProcessStarted(
long nativeChildProcessLauncherHelper, int pid);
......
......@@ -276,6 +276,9 @@ class CONTENT_EXPORT RenderProcessHost : public IPC::Sender,
#if defined(OS_ANDROID)
// Return the highest importance of all widgets in this process.
virtual ChildProcessImportance GetEffectiveImportance() = 0;
// Dumps the stack of this render process without crashing it.
virtual void DumpProcessStack() = 0;
#endif
// Sets a flag indicating that the process can be abnormally terminated.
......
......@@ -306,6 +306,8 @@ ChildProcessImportance MockRenderProcessHost::GetEffectiveImportance() {
NOTIMPLEMENTED();
return ChildProcessImportance::NORMAL;
}
void MockRenderProcessHost::DumpProcessStack() {}
#endif
void MockRenderProcessHost::SetSuddenTerminationAllowed(bool allowed) {
......
......@@ -110,6 +110,7 @@ class MockRenderProcessHost : public RenderProcessHost {
void RemovePriorityClient(PriorityClient* priority_client) override;
#if defined(OS_ANDROID)
ChildProcessImportance GetEffectiveImportance() override;
void DumpProcessStack() override;
#endif
void SetSuddenTerminationAllowed(bool allowed) override;
bool SuddenTerminationAllowed() override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment