Android: Add option to print native stack traces in PeerConnectionFactory API

This CL hooks up the recently added native stack trace functionality to
the existing PeerConnectionFactory API.

Bug: webrtc:10168
Change-Id: I16189d2988b1359fc53f9a4d0b3d06f34e2a8fd5
Reviewed-on: https://webrtc-review.googlesource.com/c/118600
Reviewed-by: Tommi <tommi@webrtc.org>
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26344}
diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn
index 534865e..c6ffe73 100644
--- a/sdk/android/BUILD.gn
+++ b/sdk/android/BUILD.gn
@@ -784,6 +784,7 @@
       ":generated_peerconnection_jni",
       ":logging_jni",
       ":native_api_jni",
+      ":native_api_stacktrace",
       "../..:webrtc_common",
       "../../api:libjingle_peerconnection_api",
       "../../api/video_codecs:video_codecs_api",
diff --git a/sdk/android/api/org/webrtc/PeerConnectionFactory.java b/sdk/android/api/org/webrtc/PeerConnectionFactory.java
index b68c816..6d00baa 100644
--- a/sdk/android/api/org/webrtc/PeerConnectionFactory.java
+++ b/sdk/android/api/org/webrtc/PeerConnectionFactory.java
@@ -11,6 +11,7 @@
 package org.webrtc;
 
 import android.content.Context;
+import android.os.Process;
 import android.support.annotation.Nullable;
 import java.util.List;
 import org.webrtc.Logging.Severity;
@@ -29,17 +30,32 @@
   private static final String TAG = "PeerConnectionFactory";
   private static final String VIDEO_CAPTURER_THREAD_NAME = "VideoCapturerThread";
 
+  /** Helper class holding both Java and C++ thread info. */
+  private static class ThreadInfo {
+    final Thread thread;
+    final int tid;
+
+    public static ThreadInfo getCurrent() {
+      return new ThreadInfo(Thread.currentThread(), Process.myTid());
+    }
+
+    private ThreadInfo(Thread thread, int tid) {
+      this.thread = thread;
+      this.tid = tid;
+    }
+  }
+
   private static volatile boolean internalTracerInitialized;
 
   // Remove these once deprecated static printStackTrace() is gone.
-  @Nullable private static Thread staticNetworkThread;
-  @Nullable private static Thread staticWorkerThread;
-  @Nullable private static Thread staticSignalingThread;
+  @Nullable private static ThreadInfo staticNetworkThread;
+  @Nullable private static ThreadInfo staticWorkerThread;
+  @Nullable private static ThreadInfo staticSignalingThread;
 
   private long nativeFactory;
-  @Nullable private volatile Thread networkThread;
-  @Nullable private volatile Thread workerThread;
-  @Nullable private volatile Thread signalingThread;
+  @Nullable private volatile ThreadInfo networkThread;
+  @Nullable private volatile ThreadInfo workerThread;
+  @Nullable private volatile ThreadInfo signalingThread;
 
   public static class InitializationOptions {
     final Context applicationContext;
@@ -471,24 +487,37 @@
     }
   }
 
-  private static void printStackTrace(@Nullable Thread thread, String threadName) {
-    if (thread != null) {
-      StackTraceElement[] stackTraces = thread.getStackTrace();
-      if (stackTraces.length > 0) {
-        Logging.w(TAG, threadName + " stacks trace:");
-        for (StackTraceElement stackTrace : stackTraces) {
-          Logging.w(TAG, stackTrace.toString());
-        }
+  private static void printStackTrace(
+      @Nullable ThreadInfo threadInfo, boolean printNativeStackTrace) {
+    if (threadInfo == null) {
+      // Thread callbacks have not been completed yet, ignore call.
+      return;
+    }
+    final String threadName = threadInfo.thread.getName();
+    StackTraceElement[] stackTraces = threadInfo.thread.getStackTrace();
+    if (stackTraces.length > 0) {
+      Logging.w(TAG, threadName + " stacktrace:");
+      for (StackTraceElement stackTrace : stackTraces) {
+        Logging.w(TAG, stackTrace.toString());
       }
     }
+    if (printNativeStackTrace) {
+      // Imitate output from debuggerd/tombstone so that stack trace can easily be symbolized with
+      // ndk-stack.
+      Logging.w(TAG, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***");
+      Logging.w(TAG,
+          "pid: " + Process.myPid() + ", tid: " + threadInfo.tid + ", name: " + threadName
+              + "  >>> WebRTC <<<");
+      nativePrintStackTrace(threadInfo.tid);
+    }
   }
 
   /** Deprecated, use non-static version instead. */
   @Deprecated
   public static void printStackTraces() {
-    printStackTrace(staticNetworkThread, "Network thread");
-    printStackTrace(staticWorkerThread, "Worker thread");
-    printStackTrace(staticSignalingThread, "Signaling thread");
+    printStackTrace(staticNetworkThread, /* printNativeStackTrace= */ false);
+    printStackTrace(staticWorkerThread, /* printNativeStackTrace= */ false);
+    printStackTrace(staticSignalingThread, /* printNativeStackTrace= */ false);
   }
 
   /**
@@ -497,28 +526,28 @@
    * attempt to print the C++ stack traces for these threads.
    */
   public void printInternalStackTraces(boolean printNativeStackTraces) {
-    printStackTrace(signalingThread, "Signaling thread");
-    printStackTrace(workerThread, "Worker thread");
-    printStackTrace(networkThread, "Network thread");
+    printStackTrace(signalingThread, printNativeStackTraces);
+    printStackTrace(workerThread, printNativeStackTraces);
+    printStackTrace(networkThread, printNativeStackTraces);
   }
 
   @CalledByNative
   private void onNetworkThreadReady() {
-    networkThread = Thread.currentThread();
+    networkThread = ThreadInfo.getCurrent();
     staticNetworkThread = networkThread;
     Logging.d(TAG, "onNetworkThreadReady");
   }
 
   @CalledByNative
   private void onWorkerThreadReady() {
-    workerThread = Thread.currentThread();
+    workerThread = ThreadInfo.getCurrent();
     staticWorkerThread = workerThread;
     Logging.d(TAG, "onWorkerThreadReady");
   }
 
   @CalledByNative
   private void onSignalingThreadReady() {
-    signalingThread = Thread.currentThread();
+    signalingThread = ThreadInfo.getCurrent();
     staticSignalingThread = signalingThread;
     Logging.d(TAG, "onSignalingThreadReady");
   }
@@ -558,4 +587,5 @@
   private static native long nativeGetNativePeerConnectionFactory(long factory);
   private static native void nativeInjectLoggable(JNILogging jniLogging, int severity);
   private static native void nativeDeleteLoggable();
+  private static native void nativePrintStackTrace(int tid);
 }
diff --git a/sdk/android/src/jni/pc/peer_connection_factory.cc b/sdk/android/src/jni/pc/peer_connection_factory.cc
index 040753d..79ac732 100644
--- a/sdk/android/src/jni/pc/peer_connection_factory.cc
+++ b/sdk/android/src/jni/pc/peer_connection_factory.cc
@@ -26,6 +26,7 @@
 #include "rtc_base/thread.h"
 #include "sdk/android/generated_peerconnection_jni/jni/PeerConnectionFactory_jni.h"
 #include "sdk/android/native_api/jni/java_types.h"
+#include "sdk/android/native_api/stacktrace/stacktrace.h"
 #include "sdk/android/src/jni/jni_helpers.h"
 #include "sdk/android/src/jni/logging/log_sink.h"
 #include "sdk/android/src/jni/pc/android_network_monitor.h"
@@ -515,5 +516,9 @@
   }
 }
 
+static void JNI_PeerConnectionFactory_PrintStackTrace(JNIEnv* env, jint tid) {
+  RTC_LOG(LS_WARNING) << StackTraceToString(GetStackTrace(tid));
+}
+
 }  // namespace jni
 }  // namespace webrtc