libhwcomposer: Log and dump app layers of screen frames
Log a frame-by-frame succession of HWComposer layers' data and write
their buffers, if any, into raw or png files based on system property
values.
Change-Id: Icceccf5fff5ab4d78dbc6c28c11210b8837ddfd9
diff --git a/libhwcomposer/Android.mk b/libhwcomposer/Android.mk
index 3b08fc5..4b9d04b 100644
--- a/libhwcomposer/Android.mk
+++ b/libhwcomposer/Android.mk
@@ -5,11 +5,13 @@
LOCAL_MODULE := hwcomposer.$(TARGET_BOARD_PLATFORM)
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_MODULE_TAGS := optional
-LOCAL_C_INCLUDES := $(common_includes) $(kernel_includes)
+LOCAL_C_INCLUDES := $(common_includes) $(kernel_includes) \
+ $(TOP)/external/skia/include/core \
+ $(TOP)/external/skia/include/images
LOCAL_SHARED_LIBRARIES := $(common_libs) libEGL liboverlay \
libexternal libqdutils libhardware_legacy \
libdl libmemalloc libqservice libsync \
- libbinder libmedia
+ libbinder libmedia libskia
LOCAL_CFLAGS := $(common_flags) -DLOG_TAG=\"qdhwcomposer\"
LOCAL_ADDITIONAL_DEPENDENCIES := $(common_deps)
LOCAL_SRC_FILES := hwc.cpp \
@@ -19,6 +21,7 @@
hwc_fbupdate.cpp \
hwc_mdpcomp.cpp \
hwc_copybit.cpp \
- hwc_qclient.cpp
+ hwc_qclient.cpp \
+ hwc_dump_layers.cpp
include $(BUILD_SHARED_LIBRARY)
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index 42f5c85..8e7afa2 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -32,6 +32,7 @@
#include "hwc_utils.h"
#include "hwc_fbupdate.h"
#include "hwc_mdpcomp.h"
+#include "hwc_dump_layers.h"
#include "external.h"
#include "hwc_copybit.h"
#include "profiler.h"
@@ -357,6 +358,10 @@
if(list->numHwLayers > 1)
hwc_sync(ctx, list, dpy, fd);
+ // Dump the layers for primary
+ if(ctx->mHwcDebug[dpy])
+ ctx->mHwcDebug[dpy]->dumpLayers(list);
+
if (!ctx->mMDPComp[dpy]->draw(ctx, list)) {
ALOGE("%s: MDPComp draw failed", __FUNCTION__);
ret = -1;
@@ -406,6 +411,10 @@
if(list->numHwLayers > 1)
hwc_sync(ctx, list, dpy, fd);
+ // Dump the layers for external
+ if(ctx->mHwcDebug[dpy])
+ ctx->mHwcDebug[dpy]->dumpLayers(list);
+
if (!ctx->mMDPComp[dpy]->draw(ctx, list)) {
ALOGE("%s: MDPComp draw failed", __FUNCTION__);
ret = -1;
diff --git a/libhwcomposer/hwc_dump_layers.cpp b/libhwcomposer/hwc_dump_layers.cpp
new file mode 100644
index 0000000..cf23b65
--- /dev/null
+++ b/libhwcomposer/hwc_dump_layers.cpp
@@ -0,0 +1,437 @@
+/*
+ * Copyright (c) 2012-2013, Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LOG_TAG
+#define LOG_TAG "qsfdump"
+#endif
+#define LOG_NDEBUG 0
+#include <hwc_utils.h>
+#include <hwc_dump_layers.h>
+#include <cutils/log.h>
+#include <sys/stat.h>
+#include <comptype.h>
+#include <SkBitmap.h>
+#include <SkImageEncoder.h>
+
+namespace qhwc {
+
+// MAX_ALLOWED_FRAMEDUMPS must be capped to (LONG_MAX - 1)
+// 60fps => 216000 frames per hour
+// Below setting of 216000 * 24 * 7 => 1 week or 168 hours of capture.
+ enum {
+ MAX_ALLOWED_FRAMEDUMPS = (216000 * 24 * 7)
+ };
+
+bool HwcDebug::sDumpEnable = false;
+
+HwcDebug::HwcDebug(uint32_t dpy):
+ mDumpCntLimRaw(0),
+ mDumpCntrRaw(1),
+ mDumpCntLimPng(0),
+ mDumpCntrPng(1),
+ mDpy(dpy) {
+ char dumpPropStr[PROPERTY_VALUE_MAX];
+ if(mDpy) {
+ strncpy(mDisplayName, "external", strlen("external"));
+ } else {
+ strncpy(mDisplayName, "primary", strlen("primary"));
+ }
+ sprintf(mDumpPropKeyDisplayType, "debug.sf.dump.%s", (char *)mDisplayName);
+
+ if ((property_get("debug.sf.dump.enable", dumpPropStr, NULL) > 0)) {
+ if(!strncmp(dumpPropStr, "true", strlen("true"))) {
+ sDumpEnable = true;
+ }
+ }
+}
+
+void HwcDebug::dumpLayers(hwc_display_contents_1_t* list)
+{
+ // Check need for dumping layers for debugging.
+ if (UNLIKELY(sDumpEnable) && UNLIKELY(needToDumpLayers()) && LIKELY(list)) {
+ logHwcProps(list->flags);
+ for (size_t i = 0; i < list->numHwLayers; i++) {
+ logLayer(i, list->hwLayers);
+ dumpLayer(i, list->hwLayers);
+ }
+ }
+}
+
+bool HwcDebug::needToDumpLayers()
+{
+ bool bDumpLayer = false;
+ char dumpPropStr[PROPERTY_VALUE_MAX];
+ // Enable primary dump and disable external dump by default.
+ bool bDumpEnable = !mDpy;
+ time_t timeNow;
+ tm dumpTime;
+
+ // Override the bDumpEnable based on the property value, if the property
+ // is present in the build.prop file.
+ if ((property_get(mDumpPropKeyDisplayType, dumpPropStr, NULL) > 0)) {
+ if(!strncmp(dumpPropStr, "true", strlen("true")))
+ bDumpEnable = true;
+ else
+ bDumpEnable = false;
+ }
+
+ if (false == bDumpEnable)
+ return false;
+
+ time(&timeNow);
+ localtime_r(&timeNow, &dumpTime);
+
+ if ((property_get("debug.sf.dump.png", dumpPropStr, NULL) > 0) &&
+ (strncmp(dumpPropStr, mDumpPropStrPng, PROPERTY_VALUE_MAX - 1))) {
+ // Strings exist & not equal implies it has changed, so trigger a dump
+ strncpy(mDumpPropStrPng, dumpPropStr, PROPERTY_VALUE_MAX - 1);
+ mDumpCntLimPng = atoi(dumpPropStr);
+ if (mDumpCntLimPng > MAX_ALLOWED_FRAMEDUMPS) {
+ ALOGW("Warning: Using debug.sf.dump.png %d (= max)",
+ MAX_ALLOWED_FRAMEDUMPS);
+ mDumpCntLimPng = MAX_ALLOWED_FRAMEDUMPS;
+ }
+ mDumpCntLimPng = (mDumpCntLimPng < 0) ? 0: mDumpCntLimPng;
+ if (mDumpCntLimPng) {
+ sprintf(mDumpDirPng,
+ "/data/sfdump.png.%04d.%02d.%02d.%02d.%02d.%02d",
+ dumpTime.tm_year + 1900, dumpTime.tm_mon + 1,
+ dumpTime.tm_mday, dumpTime.tm_hour,
+ dumpTime.tm_min, dumpTime.tm_sec);
+ if (0 == mkdir(mDumpDirPng, 0777))
+ mDumpCntrPng = 0;
+ else {
+ ALOGE("Error: %s. Failed to create sfdump directory: %s",
+ strerror(errno), mDumpDirPng);
+ mDumpCntrPng = mDumpCntLimPng + 1;
+ }
+ }
+ }
+
+ if (mDumpCntrPng <= mDumpCntLimPng)
+ mDumpCntrPng++;
+
+ if ((property_get("debug.sf.dump", dumpPropStr, NULL) > 0) &&
+ (strncmp(dumpPropStr, mDumpPropStrRaw, PROPERTY_VALUE_MAX - 1))) {
+ // Strings exist & not equal implies it has changed, so trigger a dump
+ strncpy(mDumpPropStrRaw, dumpPropStr, PROPERTY_VALUE_MAX - 1);
+ mDumpCntLimRaw = atoi(dumpPropStr);
+ if (mDumpCntLimRaw > MAX_ALLOWED_FRAMEDUMPS) {
+ ALOGW("Warning: Using debug.sf.dump %d (= max)",
+ MAX_ALLOWED_FRAMEDUMPS);
+ mDumpCntLimRaw = MAX_ALLOWED_FRAMEDUMPS;
+ }
+ mDumpCntLimRaw = (mDumpCntLimRaw < 0) ? 0: mDumpCntLimRaw;
+ if (mDumpCntLimRaw) {
+ sprintf(mDumpDirRaw,
+ "/data/sfdump.raw.%04d.%02d.%02d.%02d.%02d.%02d",
+ dumpTime.tm_year + 1900, dumpTime.tm_mon + 1,
+ dumpTime.tm_mday, dumpTime.tm_hour,
+ dumpTime.tm_min, dumpTime.tm_sec);
+ if (0 == mkdir(mDumpDirRaw, 0777))
+ mDumpCntrRaw = 0;
+ else {
+ ALOGE("Error: %s. Failed to create sfdump directory: %s",
+ strerror(errno), mDumpDirRaw);
+ mDumpCntrRaw = mDumpCntLimRaw + 1;
+ }
+ }
+ }
+
+ if (mDumpCntrRaw <= mDumpCntLimRaw)
+ mDumpCntrRaw++;
+
+ bDumpLayer = (mDumpCntLimPng || mDumpCntLimRaw)? true : false;
+ return bDumpLayer;
+}
+
+void HwcDebug::logHwcProps(uint32_t listFlags)
+{
+ static int hwcModuleCompType = -1;
+ static int sMdpCompMaxLayers = 0;
+ static String8 hwcModuleCompTypeLog("");
+ if (-1 == hwcModuleCompType) {
+ // One time stuff
+ char mdpCompPropStr[PROPERTY_VALUE_MAX];
+ if (property_get("debug.mdpcomp.maxlayer", mdpCompPropStr, NULL) > 0) {
+ sMdpCompMaxLayers = atoi(mdpCompPropStr);
+ }
+ hwcModuleCompType =
+ qdutils::QCCompositionType::getInstance().getCompositionType();
+ hwcModuleCompTypeLog.appendFormat("%s%s%s%s%s%s",
+ // Is hwc module composition type now a bit-field?!
+ (hwcModuleCompType == qdutils::COMPOSITION_TYPE_GPU)?
+ "[GPU]": "",
+ (hwcModuleCompType & qdutils::COMPOSITION_TYPE_MDP)?
+ "[MDP]": "",
+ (hwcModuleCompType & qdutils::COMPOSITION_TYPE_C2D)?
+ "[C2D]": "",
+ (hwcModuleCompType & qdutils::COMPOSITION_TYPE_CPU)?
+ "[CPU]": "",
+ (hwcModuleCompType & qdutils::COMPOSITION_TYPE_DYN)?
+ "[DYN]": "",
+ (hwcModuleCompType >= (qdutils::COMPOSITION_TYPE_DYN << 1))?
+ "[???]": "");
+ }
+ ALOGI("Display[%s] Layer[*] %s-HwcModuleCompType, %d-layer MdpComp %s",
+ mDisplayName, hwcModuleCompTypeLog.string(), sMdpCompMaxLayers,
+ (listFlags & HWC_GEOMETRY_CHANGED)? "[HwcList Geometry Changed]": "");
+}
+
+void HwcDebug::logLayer(size_t layerIndex, hwc_layer_1_t hwLayers[])
+{
+ if (NULL == hwLayers) {
+ ALOGE("Display[%s] Layer[%d] Error. No hwc layers to log.",
+ mDisplayName, layerIndex);
+ return;
+ }
+
+ hwc_layer_1_t *layer = &hwLayers[layerIndex];
+ hwc_rect_t sourceCrop = layer->sourceCrop;
+ hwc_rect_t displayFrame = layer->displayFrame;
+ size_t numHwcRects = layer->visibleRegionScreen.numRects;
+ hwc_rect_t const *hwcRects = layer->visibleRegionScreen.rects;
+ private_handle_t *hnd = (private_handle_t *)layer->handle;
+
+ char pixFormatStr[32] = "None";
+ String8 hwcVisRegsScrLog("[None]");
+
+ for (size_t i = 0 ; (hwcRects && (i < numHwcRects)); i++) {
+ if (0 == i)
+ hwcVisRegsScrLog.clear();
+ hwcVisRegsScrLog.appendFormat("[%dl, %dt, %dr, %db]",
+ hwcRects[i].left, hwcRects[i].top,
+ hwcRects[i].right, hwcRects[i].bottom);
+ }
+
+ if (hnd)
+ getHalPixelFormatStr(hnd->format, pixFormatStr);
+
+ // Log Line 1
+ ALOGI("Display[%s] Layer[%d] SrcBuff[%dx%d] SrcCrop[%dl, %dt, %dr, %db] "
+ "DispFrame[%dl, %dt, %dr, %db] VisRegsScr%s", mDisplayName, layerIndex,
+ (hnd)? hnd->width : -1, (hnd)? hnd->height : -1,
+ sourceCrop.left, sourceCrop.top,
+ sourceCrop.right, sourceCrop.bottom,
+ displayFrame.left, displayFrame.top,
+ displayFrame.right, displayFrame.bottom,
+ hwcVisRegsScrLog.string());
+ // Log Line 2
+ ALOGI("Display[%s] Layer[%d] LayerCompType = %s, Format = %s, "
+ "Orientation = %s, Flags = %s%s%s, Hints = %s%s%s, "
+ "Blending = %s%s%s", mDisplayName, layerIndex,
+ (layer->compositionType == HWC_FRAMEBUFFER)? "Framebuffer(GPU)":
+ (layer->compositionType == HWC_OVERLAY)? "Overlay":
+ (layer->compositionType == HWC_BACKGROUND)? "Background":"???",
+ pixFormatStr,
+ (layer->transform == 0)? "ROT_0":
+ (layer->transform == HWC_TRANSFORM_FLIP_H)? "FLIP_H":
+ (layer->transform == HWC_TRANSFORM_FLIP_V)? "FLIP_V":
+ (layer->transform == HWC_TRANSFORM_ROT_90)? "ROT_90":
+ "ROT_INVALID",
+ (layer->flags)? "": "[None]",
+ (layer->flags & HWC_SKIP_LAYER)? "[Skip layer]":"",
+ (layer->flags & qhwc::HWC_MDPCOMP)? "[MDP Comp]":"",
+ (layer->hints)? "":"[None]",
+ (layer->hints & HWC_HINT_TRIPLE_BUFFER)? "[Triple Buffer]":"",
+ (layer->hints & HWC_HINT_CLEAR_FB)? "[Clear FB]":"",
+ (layer->blending == HWC_BLENDING_NONE)? "[None]":"",
+ (layer->blending == HWC_BLENDING_PREMULT)? "[PreMult]":"",
+ (layer->blending == HWC_BLENDING_COVERAGE)? "[Coverage]":"");
+}
+
+void HwcDebug::dumpLayer(size_t layerIndex, hwc_layer_1_t hwLayers[])
+{
+ char dumpLogStrPng[128] = "";
+ char dumpLogStrRaw[128] = "";
+ bool needDumpPng = (mDumpCntrPng <= mDumpCntLimPng)? true:false;
+ bool needDumpRaw = (mDumpCntrRaw <= mDumpCntLimRaw)? true:false;
+
+ if (needDumpPng) {
+ sprintf(dumpLogStrPng, "[png-dump-frame: %03d of %03d]", mDumpCntrPng,
+ mDumpCntLimPng);
+ }
+ if (needDumpRaw) {
+ sprintf(dumpLogStrRaw, "[raw-dump-frame: %03d of %03d]", mDumpCntrRaw,
+ mDumpCntLimRaw);
+ }
+
+ if (!(needDumpPng || needDumpRaw))
+ return;
+
+ if (NULL == hwLayers) {
+ ALOGE("Display[%s] Layer[%d] %s%s Error: No hwc layers to dump.",
+ mDisplayName, layerIndex, dumpLogStrRaw, dumpLogStrPng);
+ return;
+ }
+
+ hwc_layer_1_t *layer = &hwLayers[layerIndex];
+ private_handle_t *hnd = (private_handle_t *)layer->handle;
+ char pixFormatStr[32] = "None";
+
+ if (NULL == hnd) {
+ ALOGI("Display[%s] Layer[%d] %s%s Skipping dump: Bufferless layer.",
+ mDisplayName, layerIndex, dumpLogStrRaw, dumpLogStrPng);
+ return;
+ }
+
+ getHalPixelFormatStr(hnd->format, pixFormatStr);
+
+ if (needDumpPng && hnd->base) {
+ bool bResult = false;
+ char dumpFilename[PATH_MAX];
+ SkBitmap *tempSkBmp = new SkBitmap();
+ SkBitmap::Config tempSkBmpConfig = SkBitmap::kNo_Config;
+ sprintf(dumpFilename, "%s/sfdump%03d.layer%d.%s.png", mDumpDirPng,
+ mDumpCntrPng, layerIndex, mDisplayName);
+
+ switch (hnd->format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ tempSkBmpConfig = SkBitmap::kARGB_8888_Config;
+ break;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ case HAL_PIXEL_FORMAT_RGBA_5551:
+ case HAL_PIXEL_FORMAT_RGBA_4444:
+ tempSkBmpConfig = SkBitmap::kRGB_565_Config;
+ break;
+ case HAL_PIXEL_FORMAT_RGB_888:
+ default:
+ tempSkBmpConfig = SkBitmap::kNo_Config;
+ break;
+ }
+ if (SkBitmap::kNo_Config != tempSkBmpConfig) {
+ tempSkBmp->setConfig(tempSkBmpConfig, hnd->width, hnd->height);
+ tempSkBmp->setPixels((void*)hnd->base);
+ bResult = SkImageEncoder::EncodeFile(dumpFilename,
+ *tempSkBmp, SkImageEncoder::kPNG_Type, 100);
+ ALOGI("Display[%s] Layer[%d] %s Dump to %s: %s",
+ mDisplayName, layerIndex, dumpLogStrPng,
+ dumpFilename, bResult ? "Success" : "Fail");
+ } else {
+ ALOGI("Display[%s] Layer[%d] %s Skipping dump: Unsupported layer"
+ " format %s for png encoder",
+ mDisplayName, layerIndex, dumpLogStrPng, pixFormatStr);
+ }
+ delete tempSkBmp; // Calls SkBitmap::freePixels() internally.
+ }
+
+ if (needDumpRaw && hnd->base) {
+ char dumpFilename[PATH_MAX];
+ bool bResult = false;
+ sprintf(dumpFilename, "%s/sfdump%03d.layer%d.%dx%d.%s.%s.raw",
+ mDumpDirRaw, mDumpCntrRaw,
+ layerIndex, hnd->width, hnd->height,
+ pixFormatStr, mDisplayName);
+ FILE* fp = fopen(dumpFilename, "w+");
+ if (NULL != fp) {
+ bResult = (bool) fwrite((void*)hnd->base, hnd->size, 1, fp);
+ fclose(fp);
+ }
+ ALOGI("Display[%s] Layer[%d] %s Dump to %s: %s",
+ mDisplayName, layerIndex, dumpLogStrRaw,
+ dumpFilename, bResult ? "Success" : "Fail");
+ }
+}
+
+void HwcDebug::getHalPixelFormatStr(int format, char pixFormatStr[])
+{
+ if (!pixFormatStr)
+ return;
+
+ switch(format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ strcpy(pixFormatStr, "RGBA_8888");
+ break;
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ strcpy(pixFormatStr, "RGBX_8888");
+ break;
+ case HAL_PIXEL_FORMAT_RGB_888:
+ strcpy(pixFormatStr, "RGB_888");
+ break;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ strcpy(pixFormatStr, "RGB_565");
+ break;
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ strcpy(pixFormatStr, "BGRA_8888");
+ break;
+ case HAL_PIXEL_FORMAT_RGBA_5551:
+ strcpy(pixFormatStr, "RGBA_5551");
+ break;
+ case HAL_PIXEL_FORMAT_RGBA_4444:
+ strcpy(pixFormatStr, "RGBA_4444");
+ break;
+ case HAL_PIXEL_FORMAT_YV12:
+ strcpy(pixFormatStr, "YV12");
+ break;
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ strcpy(pixFormatStr, "YCbCr_422_SP_NV16");
+ break;
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ strcpy(pixFormatStr, "YCrCb_420_SP_NV21");
+ break;
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ strcpy(pixFormatStr, "YCbCr_422_I_YUY2");
+ break;
+ case HAL_PIXEL_FORMAT_NV12_ENCODEABLE:
+ strcpy(pixFormatStr, "NV12_ENCODEABLE");
+ break;
+ case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
+ strcpy(pixFormatStr, "YCbCr_420_SP_TILED_TILE_4x2");
+ break;
+ case HAL_PIXEL_FORMAT_YCbCr_420_SP:
+ strcpy(pixFormatStr, "YCbCr_420_SP");
+ break;
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO:
+ strcpy(pixFormatStr, "YCrCb_420_SP_ADRENO");
+ break;
+ case HAL_PIXEL_FORMAT_YCrCb_422_SP:
+ strcpy(pixFormatStr, "YCrCb_422_SP");
+ break;
+ case HAL_PIXEL_FORMAT_R_8:
+ strcpy(pixFormatStr, "R_8");
+ break;
+ case HAL_PIXEL_FORMAT_RG_88:
+ strcpy(pixFormatStr, "RG_88");
+ break;
+ case HAL_PIXEL_FORMAT_INTERLACE:
+ strcpy(pixFormatStr, "INTERLACE");
+ break;
+ case HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
+ strcpy(pixFormatStr, "YCbCr_420_SP_VENUS");
+ break;
+ default:
+ sprintf(pixFormatStr, "Unknown0x%X", format);
+ break;
+ }
+}
+
+} // namespace qhwc
+
diff --git a/libhwcomposer/hwc_dump_layers.h b/libhwcomposer/hwc_dump_layers.h
new file mode 100644
index 0000000..f0d654f
--- /dev/null
+++ b/libhwcomposer/hwc_dump_layers.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2012-2013, Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HWC_DUMP_LAYERS_H
+#define HWC_DUMP_LAYERS_H
+
+#include <gralloc_priv.h>
+#include <comptype.h>
+#include <ui/Region.h>
+#include <hardware/hwcomposer.h>
+#include <utils/String8.h>
+
+namespace qhwc {
+
+class HwcDebug {
+private:
+
+// Using static variables for layer dumping since "property_set("debug.sf.dump",
+// property)" does not work.
+ int mDumpCntLimRaw;
+ int mDumpCntrRaw;
+ char mDumpPropStrRaw[PROPERTY_VALUE_MAX];
+ char mDumpDirRaw[PATH_MAX];
+ int mDumpCntLimPng;
+ int mDumpCntrPng;
+ char mDumpPropStrPng[PROPERTY_VALUE_MAX];
+ char mDumpDirPng[PATH_MAX];
+ uint32_t mDpy;
+ char mDisplayName[PROPERTY_VALUE_MAX];
+ char mDumpPropKeyDisplayType[PROPERTY_KEY_MAX];
+ static bool sDumpEnable;
+
+public:
+ HwcDebug(uint32_t dpy);
+ ~HwcDebug() {};
+
+ /*
+ * Dump layers for debugging based on "debug.sf.dump*" system property.
+ * See needToDumpLayers() for usage.
+ *
+ * @param: list - The HWC layer-list to dump.
+ *
+ */
+ void dumpLayers(hwc_display_contents_1_t* list);
+
+/*
+ * Checks if layers need to be dumped based on system property "debug.sf.dump"
+ * for raw dumps and "debug.sf.dump.png" for png dumps.
+ *
+ * Note: Set "debug.sf.dump.primary" or "debug.sf.dump.external" as true
+ * in the device's /system/build.prop file to enable layer logging/capturing
+ * feature for primary or external respectively. The feature is disabled by
+ * default to avoid per-frame property_get() calls.
+ *
+ * To turn on layer dump, set "debug.sf.dump.enable" to true in build.prop.
+ * By default debug.sf.dump.primary will be set to true for user convenience.
+ *
+ * To turn on layer dump for primary, do,
+ * adb shell setprop debug.sf.dump.primary true
+ *
+ * To turn on layer dump for external, do,
+ * adb shell setprop debug.sf.dump.external true
+ *
+ * For example, to dump 25 frames in raw format, do,
+ * adb shell setprop debug.sf.dump 25
+ * Layers are dumped in a time-stamped location: /data/sfdump*.
+ *
+ * To dump 10 frames in png format, do,
+ * adb shell setprop debug.sf.dump.png 10
+ * To dump another 25 or so frames in raw format, do,
+ * adb shell setprop debug.sf.dump 26
+ *
+ * To turn off logcat logging of layer-info, set both properties to 0,
+ * adb shell setprop debug.sf.dump.png 0
+ * adb shell setprop debug.sf.dump 0
+ *
+ * @return: true if layers need to be dumped (or logcat-ed).
+ */
+bool needToDumpLayers();
+
+/*
+ * Log a few per-frame hwc properties into logcat.
+ *
+ * @param: listFlags - Flags used in hwcomposer's list.
+ *
+ */
+void logHwcProps(uint32_t listFlags);
+
+/*
+ * Log a layer's info into logcat.
+ *
+ * @param: layerIndex - Index of layer being dumped.
+ * @param: hwLayers - Address of hwc_layer_1_t to log and dump.
+ *
+ */
+void logLayer(size_t layerIndex, hwc_layer_1_t hwLayers[]);
+
+/*
+ * Dumps a layer buffer into raw/png files.
+ *
+ * @param: layerIndex - Index of layer being dumped.
+ * @param: hwLayers - Address of hwc_layer_1_t to log and dump.
+ *
+ */
+void dumpLayer(size_t layerIndex, hwc_layer_1_t hwLayers[]);
+
+void getHalPixelFormatStr(int format, char pixelformatstr[]);
+};
+
+} // namespace qhwc
+
+#endif /* HWC_DUMP_LAYERS_H */
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index 57f94fe..8299200 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -31,6 +31,7 @@
#include "hwc_fbupdate.h"
#include "mdp_version.h"
#include "hwc_copybit.h"
+#include "hwc_dump_layers.h"
#include "external.h"
#include "hwc_qclient.h"
#include "QService.h"
@@ -134,6 +135,9 @@
MDPComp::getObject(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres,
HWC_DISPLAY_PRIMARY);
+ for (uint32_t i = 0; i < MAX_DISPLAYS; i++) {
+ ctx->mHwcDebug[i] = new HwcDebug(i);
+ }
MDPComp::init(ctx);
pthread_mutex_init(&(ctx->vstate.lock), NULL);
@@ -192,6 +196,10 @@
delete ctx->mMDPComp[i];
ctx->mMDPComp[i] = NULL;
}
+ if(ctx->mHwcDebug[i]) {
+ delete ctx->mHwcDebug[i];
+ ctx->mHwcDebug[i] = NULL;
+ }
}
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index 9da10f4..8c51b38 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -59,6 +59,7 @@
class IVideoOverlay;
class MDPComp;
class CopyBit;
+class HwcDebug;
struct MDPInfo {
@@ -259,6 +260,7 @@
qhwc::ListStats listStats[MAX_DISPLAYS];
qhwc::LayerProp *layerProp[MAX_DISPLAYS];
qhwc::MDPComp *mMDPComp[MAX_DISPLAYS];
+ qhwc::HwcDebug *mHwcDebug[MAX_DISPLAYS];
//Securing in progress indicator
bool mSecuring;