Enable debug stuffs

 Bug: 14596762
 * dumpsys gfxinfo implemented
 * profile GPU visual_bars implemented

Change-Id: Icb948a9d5af5989b5615504d0d76ade64b93ef5b
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
index 6dd7c00..64a4c41 100644
--- a/core/java/android/view/GLRenderer.java
+++ b/core/java/android/view/GLRenderer.java
@@ -61,6 +61,7 @@
 
 import com.google.android.gles_jni.EGLImpl;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -106,7 +107,6 @@
 
     private static final String[] VISUALIZERS = {
             PROFILE_PROPERTY_VISUALIZE_BARS,
-            PROFILE_PROPERTY_VISUALIZE_LINES
     };
 
     private static final String[] OVERDRAW = {
@@ -674,7 +674,7 @@
             mProfilePaint = null;
 
             if (value) {
-                mDebugDataProvider = new DrawPerformanceDataProvider(graphType);
+                mDebugDataProvider = new GraphDataProvider(graphType);
             } else {
                 mDebugDataProvider = null;
             }
@@ -742,7 +742,7 @@
     }
 
     @Override
-    void dumpGfxInfo(PrintWriter pw) {
+    void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) {
         if (mProfileEnabled) {
             pw.printf("\n\tDraw\tProcess\tExecute\n");
 
@@ -763,11 +763,6 @@
         }
     }
 
-    @Override
-    long getFrameCount() {
-        return mFrameCount;
-    }
-
     /**
      * Indicates whether this renderer instance can track and update dirty regions.
      */
@@ -1446,7 +1441,18 @@
 
     private static native void nPrepareTree(long displayListPtr);
 
-    class DrawPerformanceDataProvider extends GraphDataProvider {
+    class GraphDataProvider {
+        /**
+         * Draws the graph as bars. Frame elements are stacked on top of
+         * each other.
+         */
+        public static final int GRAPH_TYPE_BARS = 0;
+        /**
+         * Draws the graph as lines. The number of series drawn corresponds
+         * to the number of elements.
+         */
+        public static final int GRAPH_TYPE_LINES = 1;
+
         private final int mGraphType;
 
         private int mVerticalUnit;
@@ -1454,11 +1460,10 @@
         private int mHorizontalMargin;
         private int mThresholdStroke;
 
-        DrawPerformanceDataProvider(int graphType) {
+        public GraphDataProvider(int graphType) {
             mGraphType = graphType;
         }
 
-        @Override
         void prepare(DisplayMetrics metrics) {
             final float density = metrics.density;
 
@@ -1468,64 +1473,52 @@
             mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
         }
 
-        @Override
         int getGraphType() {
             return mGraphType;
         }
 
-        @Override
         int getVerticalUnitSize() {
             return mVerticalUnit;
         }
 
-        @Override
         int getHorizontalUnitSize() {
             return mHorizontalUnit;
         }
 
-        @Override
         int getHorizontaUnitMargin() {
             return mHorizontalMargin;
         }
 
-        @Override
         float[] getData() {
             return mProfileData;
         }
 
-        @Override
         float getThreshold() {
             return 16;
         }
 
-        @Override
         int getFrameCount() {
             return mProfileData.length / PROFILE_FRAME_DATA_COUNT;
         }
 
-        @Override
         int getElementCount() {
             return PROFILE_FRAME_DATA_COUNT;
         }
 
-        @Override
         int getCurrentFrame() {
             return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT;
         }
 
-        @Override
         void setupGraphPaint(Paint paint, int elementIndex) {
             paint.setColor(PROFILE_DRAW_COLORS[elementIndex]);
             if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
         }
 
-        @Override
         void setupThresholdPaint(Paint paint) {
             paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
             paint.setStrokeWidth(mThresholdStroke);
         }
 
-        @Override
         void setupCurrentFramePaint(Paint paint) {
             paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
             if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 3c4d83f..60f8ee3 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -17,13 +17,13 @@
 package android.view;
 
 import android.graphics.Bitmap;
-import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.util.DisplayMetrics;
 import android.view.Surface.OutOfResourcesException;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
 /**
@@ -61,11 +61,9 @@
      * Possible values:
      * "true", to enable profiling
      * "visual_bars", to enable profiling and visualize the results on screen
-     * "visual_lines", to enable profiling and visualize the results on screen
      * "false", to disable profiling
      *
      * @see #PROFILE_PROPERTY_VISUALIZE_BARS
-     * @see #PROFILE_PROPERTY_VISUALIZE_LINES
      *
      * @hide
      */
@@ -80,14 +78,6 @@
     public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars";
 
     /**
-     * Value for {@link #PROFILE_PROPERTY}. When the property is set to this
-     * value, profiling data will be visualized on screen as a line chart.
-     *
-     * @hide
-     */
-    public static final String PROFILE_PROPERTY_VISUALIZE_LINES = "visual_lines";
-
-    /**
      * System property used to specify the number of frames to be used
      * when doing hardware rendering profiling.
      * The default value of this property is #PROFILE_MAX_FRAMES.
@@ -298,16 +288,8 @@
 
     /**
      * Outputs extra debugging information in the specified file descriptor.
-     * @param pw
      */
-    abstract void dumpGfxInfo(PrintWriter pw);
-
-    /**
-     * Outputs the total number of frames rendered (used for fps calculations)
-     *
-     * @return the number of frames rendered
-     */
-    abstract long getFrameCount();
+    abstract void dumpGfxInfo(PrintWriter pw, FileDescriptor fd);
 
     /**
      * Loads system properties used by the renderer. This method is invoked
@@ -583,98 +565,4 @@
      */
     public void notifyFramePending() {
     }
-
-    /**
-     * Describes a series of frames that should be drawn on screen as a graph.
-     * Each frame is composed of 1 or more elements.
-     */
-    abstract class GraphDataProvider {
-        /**
-         * Draws the graph as bars. Frame elements are stacked on top of
-         * each other.
-         */
-        public static final int GRAPH_TYPE_BARS = 0;
-        /**
-         * Draws the graph as lines. The number of series drawn corresponds
-         * to the number of elements.
-         */
-        public static final int GRAPH_TYPE_LINES = 1;
-
-        /**
-         * Returns the type of graph to render.
-         *
-         * @return {@link #GRAPH_TYPE_BARS} or {@link #GRAPH_TYPE_LINES}
-         */
-        abstract int getGraphType();
-
-        /**
-         * This method is invoked before the graph is drawn. This method
-         * can be used to compute sizes, etc.
-         *
-         * @param metrics The display metrics
-         */
-        abstract void prepare(DisplayMetrics metrics);
-
-        /**
-         * @return The size in pixels of a vertical unit.
-         */
-        abstract int getVerticalUnitSize();
-
-        /**
-         * @return The size in pixels of a horizontal unit.
-         */
-        abstract int getHorizontalUnitSize();
-
-        /**
-         * @return The size in pixels of the margin between horizontal units.
-         */
-        abstract int getHorizontaUnitMargin();
-
-        /**
-         * An optional threshold value.
-         *
-         * @return A value >= 0 to draw the threshold, a negative value
-         *         to ignore it.
-         */
-        abstract float getThreshold();
-
-        /**
-         * The data to draw in the graph. The number of elements in the
-         * array must be at least {@link #getFrameCount()} * {@link #getElementCount()}.
-         * If a value is negative the following values will be ignored.
-         */
-        abstract float[] getData();
-
-        /**
-         * Returns the number of frames to render in the graph.
-         */
-        abstract int getFrameCount();
-
-        /**
-         * Returns the number of elements in each frame. This directly affects
-         * the number of series drawn in the graph.
-         */
-        abstract int getElementCount();
-
-        /**
-         * Returns the current frame, if any. If the returned value is negative
-         * the current frame is ignored.
-         */
-        abstract int getCurrentFrame();
-
-        /**
-         * Prepares the paint to draw the specified element (or series.)
-         */
-        abstract void setupGraphPaint(Paint paint, int elementIndex);
-
-        /**
-         * Prepares the paint to draw the threshold.
-         */
-        abstract void setupThresholdPaint(Paint paint);
-
-        /**
-         * Prepares the paint to draw the current frame indicator.
-         */
-        abstract void setupCurrentFramePaint(Paint paint);
-    }
 }
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index cf125bc..e63829e 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -850,6 +850,13 @@
         nOutput(mNativeRenderNode);
     }
 
+    /**
+     * Gets the size of the DisplayList for debug purposes.
+     */
+    public int getDebugSize() {
+        return nGetDebugSize(mNativeRenderNode);
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Animations
     ///////////////////////////////////////////////////////////////////////////
@@ -941,6 +948,7 @@
     private static native float nGetPivotX(long renderNode);
     private static native float nGetPivotY(long renderNode);
     private static native void nOutput(long renderNode);
+    private static native int nGetDebugSize(long renderNode);
 
     ///////////////////////////////////////////////////////////////////////////
     // Animations
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 9c9a939..cac23a8 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -22,12 +22,14 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.Log;
 import android.util.TimeUtils;
 import android.view.Surface.OutOfResourcesException;
 import android.view.View.AttachInfo;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
 /**
@@ -60,11 +62,16 @@
     // Needs a ViewRoot invalidate
     private static final int SYNC_INVALIDATE_REQUIRED = 0x1;
 
+    private static final String[] VISUALIZERS = {
+        PROFILE_PROPERTY_VISUALIZE_BARS,
+    };
+
     private int mWidth, mHeight;
     private long mNativeProxy;
     private boolean mInitialized = false;
     private RenderNode mRootNode;
     private Choreographer mChoreographer;
+    private boolean mProfilingEnabled;
 
     ThreadedRenderer(boolean translucent) {
         AtlasInitializer.sInstance.init();
@@ -77,6 +84,8 @@
         // Setup timing
         mChoreographer = Choreographer.getInstance();
         nSetFrameInterval(mNativeProxy, mChoreographer.getFrameIntervalNanos());
+
+        loadSystemProperties();
     }
 
     @Override
@@ -166,19 +175,33 @@
     }
 
     @Override
-    void dumpGfxInfo(PrintWriter pw) {
-        // TODO Auto-generated method stub
+    void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) {
+        pw.flush();
+        nDumpProfileInfo(mNativeProxy, fd);
     }
 
-    @Override
-    long getFrameCount() {
-        // TODO Auto-generated method stub
-        return 0;
+    private static int search(String[] values, String value) {
+        for (int i = 0; i < values.length; i++) {
+            if (values[i].equals(value)) return i;
+        }
+        return -1;
+    }
+
+    private static boolean checkIfProfilingRequested() {
+        String profiling = SystemProperties.get(HardwareRenderer.PROFILE_PROPERTY);
+        int graphType = search(VISUALIZERS, profiling);
+        return (graphType >= 0) || Boolean.parseBoolean(profiling);
     }
 
     @Override
     boolean loadSystemProperties() {
-        return nLoadSystemProperties(mNativeProxy);
+        boolean changed = nLoadSystemProperties(mNativeProxy);
+        boolean wantProfiling = checkIfProfilingRequested();
+        if (wantProfiling != mProfilingEnabled) {
+            mProfilingEnabled = wantProfiling;
+            changed = true;
+        }
+        return changed;
     }
 
     private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
@@ -210,14 +233,24 @@
         long frameTimeNanos = mChoreographer.getFrameTimeNanos();
         attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS;
 
+        long recordDuration = 0;
+        if (mProfilingEnabled) {
+            recordDuration = System.nanoTime();
+        }
+
         updateRootDisplayList(view, callbacks);
 
+        if (mProfilingEnabled) {
+            recordDuration = System.nanoTime() - recordDuration;
+        }
+
         attachInfo.mIgnoreDirtyState = false;
 
         if (dirty == null) {
             dirty = NULL_RECT;
         }
         int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
+                recordDuration, view.getResources().getDisplayMetrics().density,
                 dirty.left, dirty.top, dirty.right, dirty.bottom);
         if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
             attachInfo.mViewRootImpl.invalidate();
@@ -354,7 +387,8 @@
     private static native void nSetup(long nativeProxy, int width, int height,
             float lightX, float lightY, float lightZ, float lightRadius);
     private static native void nSetOpaque(long nativeProxy, boolean opaque);
-    private static native int nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos,
+    private static native int nSyncAndDrawFrame(long nativeProxy,
+            long frameTimeNanos, long recordDuration, float density,
             int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     private static native void nRunWithGlContext(long nativeProxy, Runnable runnable);
     private static native void nDestroyCanvasAndSurface(long nativeProxy);
@@ -370,4 +404,6 @@
 
     private static native void nFence(long nativeProxy);
     private static native void nNotifyFramePending(long nativeProxy);
+
+    private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd);
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fc7bf0e..aa06d15 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5329,7 +5329,7 @@
         RenderNode renderNode = view.mRenderNode;
         info[0]++;
         if (renderNode != null) {
-            info[1] += 0; /* TODO: Memory used by RenderNodes (properties + DisplayLists) */
+            info[1] += renderNode.getDebugSize();
         }
 
         if (view instanceof ViewGroup) {
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 96c0ed2..b4779f4 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -430,7 +430,7 @@
                     HardwareRenderer renderer =
                             root.getView().mAttachInfo.mHardwareRenderer;
                     if (renderer != null) {
-                        renderer.dumpGfxInfo(pw);
+                        renderer.dumpGfxInfo(pw, fd);
                     }
                 }
 
@@ -447,11 +447,6 @@
                     String name = getWindowName(root);
                     pw.printf("  %s\n  %d views, %.2f kB of display lists",
                             name, info[0], info[1] / 1024.0f);
-                    HardwareRenderer renderer =
-                            root.getView().mAttachInfo.mHardwareRenderer;
-                    if (renderer != null) {
-                        pw.printf(", %d frames rendered", renderer.getFrameCount());
-                    }
                     pw.printf("\n\n");
 
                     viewsCount += info[0];
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 867c1b1..26022e0 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -48,6 +48,12 @@
     renderNode->output();
 }
 
+static jint android_view_RenderNode_getDebugSize(JNIEnv* env,
+        jobject clazz, jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->getDebugSize();
+}
+
 static jlong android_view_RenderNode_create(JNIEnv* env, jobject clazz, jstring name) {
     RenderNode* renderNode = new RenderNode();
     renderNode->incStrong(0);
@@ -505,6 +511,7 @@
     { "nDestroyRenderNode",   "(J)V",   (void*) android_view_RenderNode_destroyRenderNode },
     { "nSetDisplayListData",   "(JJ)V", (void*) android_view_RenderNode_setDisplayListData },
     { "nOutput",               "(J)V",  (void*) android_view_RenderNode_output },
+    { "nGetDebugSize",         "(J)I",  (void*) android_view_RenderNode_getDebugSize },
 
     { "nSetCaching",           "(JZ)V",  (void*) android_view_RenderNode_setCaching },
     { "nSetStaticMatrix",      "(JJ)V",  (void*) android_view_RenderNode_setStaticMatrix },
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 6f256f0..bd016fd 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -238,10 +238,11 @@
 }
 
 static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jlong frameTimeNanos, jint dirtyLeft, jint dirtyTop,
-        jint dirtyRight, jint dirtyBottom) {
+        jlong proxyPtr, jlong frameTimeNanos, jlong recordDuration, jfloat density,
+        jint dirtyLeft, jint dirtyTop, jint dirtyRight, jint dirtyBottom) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    return proxy->syncAndDrawFrame(frameTimeNanos, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+    return proxy->syncAndDrawFrame(frameTimeNanos, recordDuration, density,
+            dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
 }
 
 static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, jobject clazz,
@@ -311,6 +312,13 @@
     proxy->notifyFramePending();
 }
 
+static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jobject javaFileDescriptor) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
+    proxy->dumpProfileInfo(fd);
+}
+
 #endif
 
 // ----------------------------------------------------------------------------
@@ -332,7 +340,7 @@
     { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
     { "nSetup", "(JIIFFFF)V", (void*) android_view_ThreadedRenderer_setup },
     { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
-    { "nSyncAndDrawFrame", "(JJIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
+    { "nSyncAndDrawFrame", "(JJJFIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
     { "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
     { "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
     { "nRunWithGlContext", "(JLjava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_runWithGlContext },
@@ -343,6 +351,7 @@
     { "nFlushCaches", "(JI)V", (void*) android_view_ThreadedRenderer_flushCaches },
     { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence },
     { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
+    { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
 #endif
 };
 
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 2cadf09..442f327 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -23,6 +23,7 @@
 		DisplayListLogBuffer.cpp \
 		DisplayListRenderer.cpp \
 		Dither.cpp \
+		DrawProfiler.cpp \
 		Extensions.cpp \
 		FboCache.cpp \
 		GradientCache.cpp \
diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp
new file mode 100644
index 0000000..971a66e
--- /dev/null
+++ b/libs/hwui/DrawProfiler.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2014 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 "DrawProfiler.h"
+
+#include <cutils/compiler.h>
+
+#include "OpenGLRenderer.h"
+#include "Properties.h"
+
+#define DEFAULT_MAX_FRAMES 128
+
+#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == kNone)) return
+
+#define NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f)
+
+#define PROFILE_DRAW_WIDTH 3
+#define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2
+#define PROFILE_DRAW_DP_PER_MS 7
+
+// Number of floats we want to display from FrameTimingData
+// If this is changed make sure to update the indexes below
+#define NUM_ELEMENTS 4
+
+#define RECORD_INDEX 0
+#define PREPARE_INDEX 1
+#define PLAYBACK_INDEX 2
+#define SWAPBUFFERS_INDEX 3
+
+// Must be NUM_ELEMENTS in size
+static const SkColor ELEMENT_COLORS[] = { 0xcf3e66cc, 0xcf8f00ff, 0xcfdc3912, 0xcfe69800 };
+static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d;
+static const SkColor THRESHOLD_COLOR = 0xff5faa4d;
+
+// We could get this from TimeLord and use the actual frame interval, but
+// this is good enough
+#define FRAME_THRESHOLD 16
+
+namespace android {
+namespace uirenderer {
+
+static int dpToPx(int dp, float density) {
+    return (int) (dp * density + 0.5f);
+}
+
+DrawProfiler::DrawProfiler()
+        : mType(kNone)
+        , mDensity(0)
+        , mData(NULL)
+        , mDataSize(0)
+        , mCurrentFrame(-1)
+        , mPreviousTime(0)
+        , mVerticalUnit(0)
+        , mHorizontalUnit(0)
+        , mThresholdStroke(0) {
+    setDensity(1);
+}
+
+DrawProfiler::~DrawProfiler() {
+    destroyData();
+}
+
+void DrawProfiler::setDensity(float density) {
+    if (CC_UNLIKELY(mDensity != density)) {
+        mDensity = density;
+        mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
+        mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
+        mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
+    }
+}
+
+void DrawProfiler::startFrame(nsecs_t recordDurationNanos) {
+    RETURN_IF_DISABLED();
+    mData[mCurrentFrame].record = NANOS_TO_MILLIS_FLOAT(recordDurationNanos);
+    mPreviousTime = systemTime(CLOCK_MONOTONIC);
+}
+
+void DrawProfiler::markPlaybackStart() {
+    RETURN_IF_DISABLED();
+    nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    mData[mCurrentFrame].prepare = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
+    mPreviousTime = now;
+}
+
+void DrawProfiler::markPlaybackEnd() {
+    RETURN_IF_DISABLED();
+    nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    mData[mCurrentFrame].playback = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
+    mPreviousTime = now;
+}
+
+void DrawProfiler::finishFrame() {
+    RETURN_IF_DISABLED();
+    nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    mData[mCurrentFrame].swapBuffers = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
+    mPreviousTime = now;
+    mCurrentFrame = (mCurrentFrame + 1) % mDataSize;
+}
+
+void DrawProfiler::unionDirty(Rect* dirty) {
+    RETURN_IF_DISABLED();
+    // Not worth worrying about minimizing the dirty region for debugging, so just
+    // dirty the entire viewport.
+    if (dirty) {
+        dirty->setEmpty();
+    }
+}
+
+void DrawProfiler::draw(OpenGLRenderer* canvas) {
+    if (CC_LIKELY(mType != kBars)) {
+        return;
+    }
+
+    prepareShapes(canvas->getViewportHeight());
+    drawGraph(canvas);
+    drawCurrentFrame(canvas);
+    drawThreshold(canvas);
+}
+
+void DrawProfiler::createData() {
+    if (mData) return;
+
+    mDataSize = property_get_int32(PROPERTY_PROFILE_MAXFRAMES, DEFAULT_MAX_FRAMES);
+    if (mDataSize <= 0) mDataSize = 1;
+    if (mDataSize > 4096) mDataSize = 4096; // Reasonable maximum
+    mData = (FrameTimingData*) calloc(mDataSize, sizeof(FrameTimingData));
+    mRects = new float*[NUM_ELEMENTS];
+    for (int i = 0; i < NUM_ELEMENTS; i++) {
+        // 4 floats per rect
+        mRects[i] = (float*) calloc(mDataSize, 4 * sizeof(float));
+    }
+    mCurrentFrame = 0;
+}
+
+void DrawProfiler::destroyData() {
+    delete mData;
+    mData = NULL;
+}
+
+void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) {
+    r.top = r.bottom - (data * mVerticalUnit);
+    shapeOutput[0] = r.left;
+    shapeOutput[1] = r.top;
+    shapeOutput[2] = r.right;
+    shapeOutput[3] = r.bottom;
+    r.bottom = r.top;
+}
+
+void DrawProfiler::prepareShapes(const int baseline) {
+    Rect r;
+    r.right = mHorizontalUnit;
+    for (int i = 0; i < mDataSize; i++) {
+        const int shapeIndex = i * 4;
+        r.bottom = baseline;
+        addRect(r, mData[i].record, mRects[RECORD_INDEX] + shapeIndex);
+        addRect(r, mData[i].prepare, mRects[PREPARE_INDEX] + shapeIndex);
+        addRect(r, mData[i].playback, mRects[PLAYBACK_INDEX] + shapeIndex);
+        addRect(r, mData[i].swapBuffers, mRects[SWAPBUFFERS_INDEX] + shapeIndex);
+        r.translate(mHorizontalUnit, 0);
+    }
+}
+
+void DrawProfiler::drawGraph(OpenGLRenderer* canvas) {
+    SkPaint paint;
+    for (int i = 0; i < NUM_ELEMENTS; i++) {
+        paint.setColor(ELEMENT_COLORS[i]);
+        canvas->drawRects(mRects[i], mDataSize * 4, &paint);
+    }
+}
+
+void DrawProfiler::drawCurrentFrame(OpenGLRenderer* canvas) {
+    // This draws a solid rect over the entirety of the current frame's shape
+    // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1]
+    // which will therefore fully overlap the previously drawn rects
+    SkPaint paint;
+    paint.setColor(CURRENT_FRAME_COLOR);
+    const int i = mCurrentFrame * 4;
+    canvas->drawRect(mRects[0][i], mRects[NUM_ELEMENTS-1][i+1], mRects[0][i+2],
+            mRects[0][i+3], &paint);
+}
+
+void DrawProfiler::drawThreshold(OpenGLRenderer* canvas) {
+    SkPaint paint;
+    paint.setColor(THRESHOLD_COLOR);
+    paint.setStrokeWidth(mThresholdStroke);
+
+    float pts[4];
+    pts[0] = 0.0f;
+    pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
+    pts[2] = canvas->getViewportWidth();
+    canvas->drawLines(pts, 4, &paint);
+}
+
+DrawProfiler::ProfileType DrawProfiler::loadRequestedProfileType() {
+    ProfileType type = kNone;
+    char buf[PROPERTY_VALUE_MAX] = {'\0',};
+    if (property_get(PROPERTY_PROFILE, buf, "") > 0) {
+        if (!strcmp(buf, PROPERTY_PROFILE_VISUALIZE_BARS)) {
+            type = kBars;
+        } else if (!strcmp(buf, "true")) {
+            type = kConsole;
+        }
+    }
+    return type;
+}
+
+bool DrawProfiler::loadSystemProperties() {
+    ProfileType newType = loadRequestedProfileType();
+    if (newType != mType) {
+        mType = newType;
+        if (mType == kNone) {
+            destroyData();
+        } else {
+            createData();
+        }
+        return true;
+    }
+    return false;
+}
+
+void DrawProfiler::dumpData(int fd) {
+    RETURN_IF_DISABLED();
+
+    // This method logs the last N frames (where N is <= mDataSize) since the
+    // last call to dumpData(). In other words if there's a dumpData(), draw frame,
+    // dumpData(), the last dumpData() should only log 1 frame.
+
+    const FrameTimingData emptyData = {0, 0, 0, 0};
+
+    FILE *file = fdopen(fd, "a");
+    fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
+
+    for (int frameOffset = 1; frameOffset <= mDataSize; frameOffset++) {
+        int i = (mCurrentFrame + frameOffset) % mDataSize;
+        if (!memcmp(mData + i, &emptyData, sizeof(FrameTimingData))) {
+            continue;
+        }
+        fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
+                mData[i].record, mData[i].prepare, mData[i].playback, mData[i].swapBuffers);
+    }
+    // reset the buffer
+    memset(mData, 0, sizeof(FrameTimingData) * mDataSize);
+    mCurrentFrame = 0;
+
+    fflush(file);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h
new file mode 100644
index 0000000..c1aa1c6
--- /dev/null
+++ b/libs/hwui/DrawProfiler.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 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 DRAWPROFILER_H
+#define DRAWPROFILER_H
+
+#include <utils/Timers.h>
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+class OpenGLRenderer;
+
+class DrawProfiler {
+public:
+    DrawProfiler();
+    ~DrawProfiler();
+
+    bool loadSystemProperties();
+    void setDensity(float density);
+
+    void startFrame(nsecs_t recordDurationNanos = 0);
+    void markPlaybackStart();
+    void markPlaybackEnd();
+    void finishFrame();
+
+    void unionDirty(Rect* dirty);
+    void draw(OpenGLRenderer* canvas);
+
+    void dumpData(int fd);
+
+private:
+    enum ProfileType {
+        kNone,
+        kConsole,
+        kBars,
+    };
+
+    typedef struct {
+        float record;
+        float prepare;
+        float playback;
+        float swapBuffers;
+    } FrameTimingData;
+
+    void createData();
+    void destroyData();
+
+    void addRect(Rect& r, float data, float* shapeOutput);
+    void prepareShapes(const int baseline);
+    void drawGraph(OpenGLRenderer* canvas);
+    void drawCurrentFrame(OpenGLRenderer* canvas);
+    void drawThreshold(OpenGLRenderer* canvas);
+
+    ProfileType loadRequestedProfileType();
+
+    ProfileType mType;
+    float mDensity;
+
+    FrameTimingData* mData;
+    int mDataSize;
+
+    int mCurrentFrame;
+    nsecs_t mPreviousTime;
+
+    int mVerticalUnit;
+    int mHorizontalUnit;
+    int mThresholdStroke;
+
+    /*
+     * mRects represents an array of rect shapes, divided into NUM_ELEMENTS
+     * groups such that each group is drawn with the same paint.
+     * For example mRects[0] is the array of rect floats suitable for
+     * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record
+     * information.
+     */
+    float** mRects;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* DRAWPROFILER_H */
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 20b8f2f..12241b8 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -89,6 +89,36 @@
 #define PROPERTY_DEBUG_NV_PROFILING "debug.hwui.nv_profiling"
 
 /**
+ *  System property used to enable or disable hardware rendering profiling.
+ * The default value of this property is assumed to be false.
+ *
+ * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+ * output extra information about the time taken to execute by the last
+ * frames.
+ *
+ * Possible values:
+ * "true", to enable profiling
+ * "visual_bars", to enable profiling and visualize the results on screen
+ * "false", to disable profiling
+ */
+#define PROPERTY_PROFILE "debug.hwui.profile"
+#define PROPERTY_PROFILE_VISUALIZE_BARS "visual_bars"
+
+/**
+ * System property used to specify the number of frames to be used
+ * when doing hardware rendering profiling.
+ * The default value of this property is #PROFILE_MAX_FRAMES.
+ *
+ * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+ * output extra information about the time taken to execute by the last
+ * frames.
+ *
+ * Possible values:
+ * "60", to set the limit of frames to 60
+ */
+#define PROPERTY_PROFILE_MAXFRAMES "debug.hwui.profile.maxframes"
+
+/**
  * Used to enable/disable non-rectangular clipping debugging.
  *
  * The accepted values are:
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index df74f31..d964efc 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -93,6 +93,17 @@
     ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName());
 }
 
+int RenderNode::getDebugSize() {
+    int size = sizeof(RenderNode);
+    if (mStagingDisplayListData) {
+        size += mStagingDisplayListData->allocator.usedSize();
+    }
+    if (mDisplayListData && mDisplayListData != mStagingDisplayListData) {
+        size += mDisplayListData->allocator.usedSize();
+    }
+    return size;
+}
+
 void RenderNode::prepareTree(TreeInfo& info) {
     ATRACE_CALL();
 
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 1811a7b..1a5377b 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -119,6 +119,7 @@
     void replayNodeInParent(ReplayStateStruct& replayStruct, const int level);
 
     ANDROID_API void output(uint32_t level = 1);
+    ANDROID_API int getDebugSize();
 
     bool isRenderable() const {
         return mDisplayListData && mDisplayListData->hasDrawOps;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 160fbea..f849273 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -486,6 +486,8 @@
     LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
             "drawDisplayList called on a context with no canvas or surface!");
 
+    profiler().markPlaybackStart();
+
     EGLint width, height;
     mGlobalContext->beginFrame(mEglSurface, &width, &height);
     if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
@@ -493,6 +495,8 @@
         dirty = NULL;
     } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
         dirty = NULL;
+    } else {
+        profiler().unionDirty(dirty);
     }
 
     status_t status;
@@ -506,14 +510,17 @@
     Rect outBounds;
     status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds);
 
-    // TODO: Draw debug info
-    // TODO: Performance tracking
+    profiler().draw(mCanvas);
 
     mCanvas->finish();
 
+    profiler().markPlaybackEnd();
+
     if (status & DrawGlInfo::kStatusDrew) {
         swapBuffers();
     }
+
+    profiler().finishFrame();
 }
 
 // Called by choreographer to do an RT-driven animation
@@ -524,6 +531,8 @@
 
     ATRACE_CALL();
 
+    profiler().startFrame();
+
     TreeInfo info;
     info.evaluateAnimations = true;
     info.performStagingPush = false;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index da85d44..a04269b 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -23,6 +23,7 @@
 #include <utils/Functor.h>
 #include <utils/Vector.h>
 
+#include "../DrawProfiler.h"
 #include "../RenderNode.h"
 #include "RenderTask.h"
 #include "RenderThread.h"
@@ -77,6 +78,8 @@
 
     void notifyFramePending();
 
+    DrawProfiler& profiler() { return mProfiler; }
+
 private:
     friend class RegisterFrameCallbackTask;
 
@@ -100,6 +103,8 @@
     bool mHaveNewSurface;
 
     const sp<RenderNode> mRootRenderNode;
+
+    DrawProfiler mProfiler;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index ee3e059..7ea358f 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -34,6 +34,8 @@
         : mRenderThread(NULL)
         , mContext(NULL)
         , mFrameTimeNanos(0)
+        , mRecordDurationNanos(0)
+        , mDensity(1.0f) // safe enough default
         , mSyncResult(kSync_OK) {
 }
 
@@ -64,15 +66,17 @@
     mDirty.set(left, top, right, bottom);
 }
 
-int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos) {
+int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) {
     LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
 
     mSyncResult = kSync_OK;
     mFrameTimeNanos = frameTimeNanos;
+    mRecordDurationNanos = recordDurationNanos;
     postAndWait();
 
     // Reset the single-frame data
     mFrameTimeNanos = 0;
+    mRecordDurationNanos = 0;
     mDirty.setEmpty();
 
     return mSyncResult;
@@ -87,6 +91,9 @@
 void DrawFrameTask::run() {
     ATRACE_NAME("DrawFrame");
 
+    mContext->profiler().setDensity(mDensity);
+    mContext->profiler().startFrame(mRecordDurationNanos);
+
     bool canUnblockUiThread;
     bool canDrawThisFrame;
     {
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index acbc02a..30c8880 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -60,7 +60,8 @@
     void removeLayer(DeferredLayerUpdater* layer);
 
     void setDirty(int left, int top, int right, int bottom);
-    int drawFrame(nsecs_t frameTimeNanos);
+    void setDensity(float density) { mDensity = density; }
+    int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos);
 
     virtual void run();
 
@@ -80,6 +81,8 @@
      *********************************************/
     Rect mDirty;
     nsecs_t mFrameTimeNanos;
+    nsecs_t mRecordDurationNanos;
+    float mDensity;
 
     int mSyncResult;
 
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 8e772f2..77c0aa7 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -99,16 +99,20 @@
     post(task);
 }
 
-CREATE_BRIDGE0(loadSystemProperties) {
+CREATE_BRIDGE1(loadSystemProperties, CanvasContext* context) {
     bool needsRedraw = false;
     if (Caches::hasInstance()) {
         needsRedraw = Caches::getInstance().initProperties();
     }
+    if (args->context->profiler().loadSystemProperties()) {
+        needsRedraw = true;
+    }
     return (void*) needsRedraw;
 }
 
 bool RenderProxy::loadSystemProperties() {
     SETUP_TASK(loadSystemProperties);
+    args->context = mContext;
     return (bool) postAndWait(task);
 }
 
@@ -175,10 +179,11 @@
     post(task);
 }
 
-int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos,
-        int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
+int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
+        float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
     mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
-    return mDrawFrameTask.drawFrame(frameTimeNanos);
+    mDrawFrameTask.setDensity(density);
+    return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos);
 }
 
 CREATE_BRIDGE1(destroyCanvasAndSurface, CanvasContext* context) {
@@ -315,6 +320,18 @@
     mRenderThread.queueAtFront(task);
 }
 
+CREATE_BRIDGE2(dumpProfileInfo, CanvasContext* context, int fd) {
+    args->context->profiler().dumpData(args->fd);
+    return NULL;
+}
+
+void RenderProxy::dumpProfileInfo(int fd) {
+    SETUP_TASK(dumpProfileInfo);
+    args->context = mContext;
+    args->fd = fd;
+    postAndWait(task);
+}
+
 void RenderProxy::post(RenderTask* task) {
     mRenderThread.queue(task);
 }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 22d4e22..c8d42ec 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -69,8 +69,8 @@
     ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
     ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
     ANDROID_API void setOpaque(bool opaque);
-    ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos,
-            int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
+    ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
+            float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     ANDROID_API void destroyCanvasAndSurface();
 
     ANDROID_API void invokeFunctor(Functor* functor, bool waitForCompletion);
@@ -87,6 +87,8 @@
     ANDROID_API void fence();
     ANDROID_API void notifyFramePending();
 
+    ANDROID_API void dumpProfileInfo(int fd);
+
 private:
     RenderThread& mRenderThread;
     CanvasContext* mContext;