Add logging of graphics acceleration info to bugreports

Change-Id: I9fa4cda6ccf92df9d1c644ccdc0e7274a30106e0
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cb07135..751726a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -394,6 +394,8 @@
         ParcelFileDescriptor fd;
     }
 
+    native private void dumpGraphicsInfo(FileDescriptor fd);
+
     private final class ApplicationThread extends ApplicationThreadNative {
         private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
         private static final String ONE_COUNT_COLUMN = "%17s %8d";
@@ -711,9 +713,14 @@
                 }
             }
         }
-        
+
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (args != null && args.length == 1 && args[0].equals("graphics")) {
+                pw.flush();
+                dumpGraphicsInfo(fd);
+                return;
+            }
             long nativeMax = Debug.getNativeHeapSize() / 1024;
             long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
             long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index e4eb692..25c0a13 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -165,6 +165,7 @@
 extern int register_android_backup_BackupDataOutput(JNIEnv *env);
 extern int register_android_backup_FileBackupHelperBase(JNIEnv *env);
 extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env);
+extern int register_android_app_ActivityThread(JNIEnv *env);
 extern int register_android_app_NativeActivity(JNIEnv *env);
 extern int register_android_view_InputChannel(JNIEnv* env);
 extern int register_android_view_InputQueue(JNIEnv* env);
@@ -1298,6 +1299,7 @@
     REG_JNI(register_android_backup_FileBackupHelperBase),
     REG_JNI(register_android_backup_BackupHelperDispatcher),
     
+    REG_JNI(register_android_app_ActivityThread),
     REG_JNI(register_android_app_NativeActivity),
     REG_JNI(register_android_view_InputChannel),
     REG_JNI(register_android_view_InputQueue),
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index a4931ac..d471f7f 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -592,6 +592,19 @@
 }
 
 // ----------------------------------------------------------------------------
+// Logging
+// ----------------------------------------------------------------------------
+
+jfieldID gFileDescriptorField;
+
+static void
+android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor)
+{
+    int fd = env->GetIntField(javaFileDescriptor, gFileDescriptorField);
+    uirenderer::DisplayList::outputLogBuffer(fd);
+}
+
+// ----------------------------------------------------------------------------
 // JNI Glue
 // ----------------------------------------------------------------------------
 
@@ -690,6 +703,12 @@
 #endif
 };
 
+static JNINativeMethod gActivityThreadMethods[] = {
+    { "dumpGraphicsInfo",        "(Ljava/io/FileDescriptor;)V",
+                                               (void*) android_app_ActivityThread_dumpGraphics }
+};
+
+
 #ifdef USE_OPENGL_RENDERER
     #define FIND_CLASS(var, className) \
             var = env->FindClass(className); \
@@ -711,4 +730,19 @@
     return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
+const char* const kActivityThreadPathName = "android/app/ActivityThread";
+
+int register_android_app_ActivityThread(JNIEnv* env)
+{
+    jclass gFileDescriptorClass = env->FindClass("java/io/FileDescriptor");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
+    gFileDescriptorField = env->GetFieldID(gFileDescriptorClass, "descriptor", "I");
+    LOG_FATAL_IF(gFileDescriptorField == NULL,
+                 "Unable to find descriptor field in java.io.FileDescriptor");
+
+    return AndroidRuntime::registerNativeMethods(
+            env, kActivityThreadPathName,
+            gActivityThreadMethods, NELEM(gActivityThreadMethods));
+}
+
 };
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index b465fee..a98e4cd 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -9,6 +9,7 @@
 		FontRenderer.cpp \
 		GammaFontRenderer.cpp \
 		Caches.cpp \
+		DisplayListLogBuffer.cpp \
 		DisplayListRenderer.cpp \
 		FboCache.cpp \
 		GradientCache.cpp \
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 4f5edd5..cd48429 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "OpenGLRenderer"
 
 #include <utils/Log.h>
+#include <utils/String8.h>
 
 #include "Caches.h"
 #include "Properties.h"
@@ -69,30 +70,43 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void Caches::dumpMemoryUsage() {
-    LOGD("Current memory usage / total memory usage (bytes):");
-    LOGD("  TextureCache         %8d / %8d", textureCache.getSize(), textureCache.getMaxSize());
-    LOGD("  LayerCache           %8d / %8d", layerCache.getSize(), layerCache.getMaxSize());
-    LOGD("  GradientCache        %8d / %8d", gradientCache.getSize(), gradientCache.getMaxSize());
-    LOGD("  PathCache            %8d / %8d", pathCache.getSize(), pathCache.getMaxSize());
-    LOGD("  CircleShapeCache     %8d / %8d",
+    String8 stringLog;
+    dumpMemoryUsage(stringLog);
+    LOGD("%s", stringLog.string());
+    delete stringLog;
+}
+
+void Caches::dumpMemoryUsage(String8 &log) {
+    log.appendFormat("Current memory usage / total memory usage (bytes):\n");
+    log.appendFormat("  TextureCache         %8d / %8d\n",
+            textureCache.getSize(), textureCache.getMaxSize());
+    log.appendFormat("  LayerCache           %8d / %8d\n",
+            layerCache.getSize(), layerCache.getMaxSize());
+    log.appendFormat("  GradientCache        %8d / %8d\n",
+            gradientCache.getSize(), gradientCache.getMaxSize());
+    log.appendFormat("  PathCache            %8d / %8d\n",
+            pathCache.getSize(), pathCache.getMaxSize());
+    log.appendFormat("  CircleShapeCache     %8d / %8d\n",
             circleShapeCache.getSize(), circleShapeCache.getMaxSize());
-    LOGD("  OvalShapeCache       %8d / %8d",
+    log.appendFormat("  OvalShapeCache       %8d / %8d\n",
             ovalShapeCache.getSize(), ovalShapeCache.getMaxSize());
-    LOGD("  RoundRectShapeCache  %8d / %8d",
+    log.appendFormat("  RoundRectShapeCache  %8d / %8d\n",
             roundRectShapeCache.getSize(), roundRectShapeCache.getMaxSize());
-    LOGD("  RectShapeCache       %8d / %8d",
+    log.appendFormat("  RectShapeCache       %8d / %8d\n",
             rectShapeCache.getSize(), rectShapeCache.getMaxSize());
-    LOGD("  ArcShapeCache        %8d / %8d",
+    log.appendFormat("  ArcShapeCache        %8d / %8d\n",
             arcShapeCache.getSize(), arcShapeCache.getMaxSize());
-    LOGD("  TextDropShadowCache  %8d / %8d", dropShadowCache.getSize(),
+    log.appendFormat("  TextDropShadowCache  %8d / %8d\n", dropShadowCache.getSize(),
             dropShadowCache.getMaxSize());
     for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) {
         const uint32_t size = fontRenderer.getFontRendererSize(i);
-        LOGD("  FontRenderer %d       %8d / %8d", i, size, size);
+        log.appendFormat("  FontRenderer %d       %8d / %8d\n", i, size, size);
     }
-    LOGD("Other:");
-    LOGD("  FboCache             %8d / %8d", fboCache.getSize(), fboCache.getMaxSize());
-    LOGD("  PatchCache           %8d / %8d", patchCache.getSize(), patchCache.getMaxSize());
+    log.appendFormat("Other:");
+    log.appendFormat("  FboCache             %8d / %8d\n",
+            fboCache.getSize(), fboCache.getMaxSize());
+    log.appendFormat("  PatchCache           %8d / %8d\n",
+            patchCache.getSize(), patchCache.getMaxSize());
 
     uint32_t total = 0;
     total += textureCache.getSize();
@@ -109,9 +123,8 @@
         total += fontRenderer.getFontRendererSize(i);
     }
 
-    LOGD("Total memory usage:");
-    LOGD("  %d bytes, %.2f MB", total, total / 1024.0f / 1024.0f);
-    LOGD("\n");
+    log.appendFormat("Total memory usage:\n");
+    log.appendFormat("  %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 0a9335f..7d02cf8 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -140,6 +140,7 @@
      * Displays the memory usage of each cache and the total sum.
      */
     void dumpMemoryUsage();
+    void dumpMemoryUsage(String8& log);
 
     bool blend;
     GLenum lastSrcMode;
diff --git a/libs/hwui/DisplayListLogBuffer.cpp b/libs/hwui/DisplayListLogBuffer.cpp
new file mode 100644
index 0000000..f204644
--- /dev/null
+++ b/libs/hwui/DisplayListLogBuffer.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DisplayListLogBuffer.h"
+
+// BUFFER_SIZE size must be one more than a multiple of COMMAND_SIZE to ensure
+// that mStart always points at the next command, not just the next item
+#define COMMAND_SIZE 2
+#define NUM_COMMANDS 50
+#define BUFFER_SIZE ((NUM_COMMANDS * COMMAND_SIZE) + 1)
+
+/**
+ * DisplayListLogBuffer is a utility class which logs the most recent display
+ * list operations in a circular buffer. The log is process-wide, because we
+ * only care about the most recent operations, not the operations on a per-window
+ * basis for a given activity. The purpose of the log is to provide more debugging
+ * information in a bug report, by telling us not just where a process hung (which
+ * generally is just reported as a stack trace at the Java level) or crashed, but
+ * also what happened immediately before that hang or crash. This may help track down
+ * problems in the native rendering code or driver interaction related to the display
+ * list operations that led up to the hang or crash.
+ *
+ * The log is implemented as a circular buffer for both space and performance
+ * reasons - we only care about the last several operations to give us context
+ * leading up to the problem, and we don't want to constantly copy data around or do
+ * additional mallocs to keep the most recent operations logged. Only numbers are
+ * logged to make the operation fast. If and when the log is output, we process this
+ * data into meaningful strings.
+ *
+ * There is an assumption about the format of the command (currently 2 ints: the
+ * opcode and the nesting level). If the type of information logged changes (for example,
+ * we may want to save a timestamp), then the size of the buffer and the way the
+ * information is recorded in writeCommand() should change to suit.
+ */
+
+namespace android {
+
+#ifdef USE_OPENGL_RENDERER
+using namespace uirenderer;
+ANDROID_SINGLETON_STATIC_INSTANCE(DisplayListLogBuffer);
+#endif
+
+namespace uirenderer {
+
+
+DisplayListLogBuffer::DisplayListLogBuffer() {
+    mBufferFirst = (int*) malloc(BUFFER_SIZE * sizeof(int));
+    mStart = mBufferFirst;
+    mBufferLast = mBufferFirst + BUFFER_SIZE - 1;
+    mEnd = mStart;
+}
+
+DisplayListLogBuffer::~DisplayListLogBuffer() {
+    free(mBufferFirst);
+}
+
+/**
+ * Called from DisplayListRenderer to output the current buffer into the
+ * specified FILE. This only happens in a dumpsys/bugreport operation.
+ */
+void DisplayListLogBuffer::outputCommands(FILE *file, const char* opNames[])
+{
+    int *tmpBufferPtr = mStart;
+    while (true) {
+        if (tmpBufferPtr == mEnd) {
+            break;
+        }
+        int level = *tmpBufferPtr++;
+        if (tmpBufferPtr > mBufferLast) {
+            tmpBufferPtr = mBufferFirst;
+        }
+        int op = *tmpBufferPtr++;
+        if (tmpBufferPtr > mBufferLast) {
+            tmpBufferPtr = mBufferFirst;
+        }
+        uint32_t count = (level + 1) * 2;
+        char indent[count + 1];
+        for (uint32_t i = 0; i < count; i++) {
+            indent[i] = ' ';
+        }
+        indent[count] = '\0';
+        fprintf(file, "%s%s\n", indent, opNames[op]);
+    }
+}
+
+void DisplayListLogBuffer::writeCommand(int level, int op) {
+    writeInt(level);
+    writeInt(op);
+}
+
+/**
+ * Store the given value in the buffer and increment/wrap the mEnd
+ * and mStart values as appropriate.
+ */
+void DisplayListLogBuffer::writeInt(int value) {
+    *((int*)mEnd) = value;
+    if (mEnd == mBufferLast) {
+        mEnd = mBufferFirst;
+    } else {
+        mEnd++;
+    }
+    if (mEnd == mStart) {
+        mStart++;
+        if (mStart > mBufferLast) {
+            mStart = mBufferFirst;
+        }
+    }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/DisplayListLogBuffer.h b/libs/hwui/DisplayListLogBuffer.h
new file mode 100644
index 0000000..bf16f29
--- /dev/null
+++ b/libs/hwui/DisplayListLogBuffer.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
+#define ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
+
+#include <utils/Singleton.h>
+#include <stdio.h>
+
+namespace android {
+namespace uirenderer {
+
+class DisplayListLogBuffer: public Singleton<DisplayListLogBuffer> {
+    DisplayListLogBuffer();
+    ~DisplayListLogBuffer();
+
+    friend class Singleton<DisplayListLogBuffer>;
+
+public:
+    void writeCommand(int level, int op);
+    void writeInt(int value);
+    void outputCommands(FILE *file, const char* opNames[]);
+
+    bool isEmpty() {
+        return (mStart == mEnd);
+    }
+
+private:
+    int *mBufferFirst; // where the memory starts
+    int* mStart;       // where the current command stream starts
+    int* mEnd;         // where the current commands end
+    int* mBufferLast;  // where the buffer memory ends
+
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index c7459d1..34dda9a 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -16,11 +16,16 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
+
+#include "DisplayListLogBuffer.h"
 #include "DisplayListRenderer.h"
+#include <utils/String8.h>
+#include "Caches.h"
 
 namespace android {
 namespace uirenderer {
 
+
 ///////////////////////////////////////////////////////////////////////////////
 // Display list
 ///////////////////////////////////////////////////////////////////////////////
@@ -64,6 +69,20 @@
     "DrawGLFunction"
 };
 
+void DisplayList::outputLogBuffer(int fd) {
+    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+    if (logBuffer.isEmpty()) {
+        return;
+    }
+    String8 cachesLog;
+    Caches::getInstance().dumpMemoryUsage(cachesLog);
+    FILE *file = fdopen(fd, "a");
+    fprintf(file, "\nCaches:\n%s", cachesLog.string());
+    fprintf(file, "\nRecent DisplayList operations\n");
+    logBuffer.outputCommands(file, OP_NAMES);
+    fflush(file);
+}
+
 DisplayList::DisplayList(const DisplayListRenderer& recorder) {
     initFromDisplayListRenderer(recorder);
 }
@@ -173,9 +192,11 @@
     DISPLAY_LIST_LOGD("%sStart display list (%p)", (char*) indent + 2, this);
 #endif
 
+    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
     int saveCount = renderer.getSaveCount() - 1;
     while (!mReader.eof()) {
         int op = mReader.readInt();
+        logBuffer.writeCommand(level, op);
 
         switch (op) {
             case DrawGLFunction: {
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index da57e4a..b782103 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -26,6 +26,7 @@
 #include <SkTDArray.h>
 #include <SkTSearch.h>
 
+#include "DisplayListLogBuffer.h"
 #include "OpenGLRenderer.h"
 #include "utils/Functor.h"
 
@@ -106,6 +107,8 @@
 
     bool replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level = 0);
 
+    static void outputLogBuffer(int fd);
+
 private:
     void init();
 
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 50fffd0..5f471fe 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1276,6 +1276,7 @@
             
             ServiceManager.addService("activity", m);
             ServiceManager.addService("meminfo", new MemBinder(m));
+            ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
             if (MONITOR_CPU_USAGE) {
                 ServiceManager.addService("cpuinfo", new CpuBinder(m));
             }
@@ -1429,6 +1430,46 @@
         }
     }
 
+    static class GraphicsBinder extends Binder {
+        ActivityManagerService mActivityManagerService;
+        GraphicsBinder(ActivityManagerService activityManagerService) {
+            mActivityManagerService = activityManagerService;
+        }
+
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            ActivityManagerService service = mActivityManagerService;
+            ArrayList<ProcessRecord> procs;
+            synchronized (mActivityManagerService) {
+                if (args != null && args.length > 0
+                        && args[0].charAt(0) != '-') {
+                    procs = new ArrayList<ProcessRecord>();
+                    int pid = -1;
+                    try {
+                        pid = Integer.parseInt(args[0]);
+                    } catch (NumberFormatException e) {
+
+                    }
+                    for (int i=service.mLruProcesses.size()-1; i>=0; i--) {
+                        ProcessRecord proc = service.mLruProcesses.get(i);
+                        if (proc.pid == pid) {
+                            procs.add(proc);
+                        } else if (proc.processName.equals(args[0])) {
+                            procs.add(proc);
+                        }
+                    }
+                    if (procs.size() <= 0) {
+                        pw.println("No process found for: " + args[0]);
+                        return;
+                    }
+                } else {
+                    procs = new ArrayList<ProcessRecord>(service.mLruProcesses);
+                }
+            }
+            dumpGraphicsHardwareUsage(fd, pw, procs);
+        }
+    }
+
     static class CpuBinder extends Binder {
         ActivityManagerService mActivityManagerService;
         CpuBinder(ActivityManagerService activityManagerService) {
@@ -8471,6 +8512,28 @@
         }
     }
 
+    static final void dumpGraphicsHardwareUsage(FileDescriptor fd,
+            PrintWriter pw, List list) {
+        String args[] = {"graphics"};
+        pw.println("-------------------------------------------------------------------------------");
+        pw.println("DUMP OF GRAPHICS ACCELERATION INFO:");
+        for (int i = list.size() - 1 ; i >= 0 ; i--) {
+            ProcessRecord r = (ProcessRecord)list.get(i);
+            if (r.thread != null) {
+                pw.println("\n** Graphics info for pid " + r.pid + " [" + r.processName + "] **");
+                pw.flush();
+                try {
+                    r.thread.asBinder().dump(fd, args);
+                } catch (RemoteException e) {
+                    pw.println("Got RemoteException!");
+                    pw.flush();
+                }
+            }
+        }
+        pw.println("\n");
+        pw.flush();
+    }
+
     static final void dumpApplicationMemoryUsage(FileDescriptor fd,
             PrintWriter pw, List list, String prefix, String[] args) {
         final boolean isCheckinRequest = scanArgs(args, "--checkin");