From 9c1e23baf5bfbebd1aebbd6d9a18c225325567ce Mon Sep 17 00:00:00 2001 From: Chet Haase Date: Thu, 24 Mar 2011 10:51:31 -0700 Subject: [PATCH] Add logging of graphics acceleration info to bugreports Change-Id: I9fa4cda6ccf92df9d1c644ccdc0e7274a30106e0 --- core/java/android/app/ActivityThread.java | 9 +- core/jni/AndroidRuntime.cpp | 2 + core/jni/android_view_GLES20Canvas.cpp | 34 +++++ libs/hwui/Android.mk | 1 + libs/hwui/Caches.cpp | 49 ++++--- libs/hwui/Caches.h | 1 + libs/hwui/DisplayListLogBuffer.cpp | 124 ++++++++++++++++++ libs/hwui/DisplayListLogBuffer.h | 52 ++++++++ libs/hwui/DisplayListRenderer.cpp | 21 +++ libs/hwui/DisplayListRenderer.h | 3 + .../server/am/ActivityManagerService.java | 63 +++++++++ 11 files changed, 340 insertions(+), 19 deletions(-) create mode 100644 libs/hwui/DisplayListLogBuffer.cpp create mode 100644 libs/hwui/DisplayListLogBuffer.h diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index cb0713596bc1f..751726a6b8c07 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -394,6 +394,8 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { } } } - + @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 e4eb6925a0d87..25c0a13b0cf31 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -165,6 +165,7 @@ extern int register_android_backup_BackupDataInput(JNIEnv *env); 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 @@ static const RegJNIRec gRegJNI[] = { 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 a4931aca99359..d471f7fab5b2a 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -591,6 +591,19 @@ static jboolean android_view_GLES20Canvas_isAvailable(JNIEnv* env, jobject clazz #endif } +// ---------------------------------------------------------------------------- +// 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 @@ static JNINativeMethod gMethods[] = { #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 @@ int register_android_view_GLES20Canvas(JNIEnv* env) { 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 b465fee71a0a0..a98e4cd302353 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -9,6 +9,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) 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 4f5edd5759264..cd484299f3a84 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "OpenGLRenderer" #include +#include #include "Caches.h" #include "Properties.h" @@ -69,30 +70,43 @@ Caches::~Caches() { /////////////////////////////////////////////////////////////////////////////// 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 @@ void Caches::dumpMemoryUsage() { 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 0a9335f232886..7d02cf8e12d8c 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -140,6 +140,7 @@ public: * 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 0000000000000..f204644d5ec03 --- /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 0000000000000..bf16f297fb9bd --- /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 +#include + +namespace android { +namespace uirenderer { + +class DisplayListLogBuffer: public Singleton { + DisplayListLogBuffer(); + ~DisplayListLogBuffer(); + + friend class Singleton; + +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 c7459d1518161..34dda9a71bc67 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 +#include "Caches.h" namespace android { namespace uirenderer { + /////////////////////////////////////////////////////////////////////////////// // Display list /////////////////////////////////////////////////////////////////////////////// @@ -64,6 +69,20 @@ const char* DisplayList::OP_NAMES[] = { "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 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) 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 da57e4a59ed2d..b78210305be60 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -26,6 +26,7 @@ #include #include +#include "DisplayListLogBuffer.h" #include "OpenGLRenderer.h" #include "utils/Functor.h" @@ -106,6 +107,8 @@ public: 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 50fffd004723d..5f471fea594cc 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1276,6 +1276,7 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative } } + 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 procs; + synchronized (mActivityManagerService) { + if (args != null && args.length > 0 + && args[0].charAt(0) != '-') { + procs = new ArrayList(); + 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(service.mLruProcesses); + } + } + dumpGraphicsHardwareUsage(fd, pw, procs); + } + } + static class CpuBinder extends Binder { ActivityManagerService mActivityManagerService; CpuBinder(ActivityManagerService activityManagerService) { @@ -8471,6 +8512,28 @@ public final class ActivityManagerService extends ActivityManagerNative } } + 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");