libQcomUI/libhwcomposer: Log and dump 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. CRs-fixed: 339748 Change-Id: I171a9891a0867548b34d45c0561f25f66cf0580c (cherry picked from commit 8166b062df4765af0b0d9f95d55fa14dd4b75b8d)
This commit is contained in:
parent
43afc2743e
commit
0f6ca9f6c9
@ -1495,7 +1495,10 @@ static int hwc_set(hwc_composer_device_t *dev,
|
||||
|
||||
int ret = 0;
|
||||
if (list) {
|
||||
bool bDumpLayers = needToDumpLayers(); // Check need for debugging dumps
|
||||
for (size_t i=0; i<list->numHwLayers; i++) {
|
||||
if (bDumpLayers)
|
||||
dumpLayer(hwcModule->compositionType, list->flags, i, list->hwLayers);
|
||||
if (list->hwLayers[i].flags & HWC_SKIP_LAYER) {
|
||||
continue;
|
||||
#ifdef COMPOSITION_BYPASS
|
||||
|
@ -14,9 +14,14 @@ LOCAL_SHARED_LIBRARIES := \
|
||||
libcutils \
|
||||
libmemalloc \
|
||||
libui \
|
||||
libEGL
|
||||
libEGL \
|
||||
libskia
|
||||
|
||||
LOCAL_C_INCLUDES := $(TOP)/hardware/qcom/display/libgralloc \
|
||||
$(TOP)/frameworks/base/services/surfaceflinger \
|
||||
$(TOP)/external/skia/include/core \
|
||||
$(TOP)/external/skia/include/images
|
||||
|
||||
LOCAL_CFLAGS := -DLOG_TAG=\"libQcomUI\"
|
||||
LOCAL_CFLAGS += -DQCOM_HARDWARE
|
||||
LOCAL_CFLAGS += -DDEBUG_CALC_FPS
|
||||
|
@ -34,8 +34,11 @@
|
||||
#include <alloc_controller.h>
|
||||
#include <memalloc.h>
|
||||
#include <errno.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <sys/stat.h>
|
||||
#include <SkBitmap.h>
|
||||
#include <SkImageEncoder.h>
|
||||
#include <Transform.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <GLES2/gl2.h>
|
||||
@ -536,6 +539,281 @@ external_display handleEventHDMI(external_display newState, external_display
|
||||
}
|
||||
return retState;
|
||||
}
|
||||
|
||||
// Using global variables for layer dumping since "property_set("debug.sf.dump",
|
||||
// property)" does not work.
|
||||
int sfdump_countlimit_raw = 0;
|
||||
int sfdump_counter_raw = 1;
|
||||
char sfdump_propstr_persist_raw[PROPERTY_VALUE_MAX] = "";
|
||||
char sfdumpdir_raw[256] = "";
|
||||
int sfdump_countlimit_png = 0;
|
||||
int sfdump_counter_png = 1;
|
||||
char sfdump_propstr_persist_png[PROPERTY_VALUE_MAX] = "";
|
||||
char sfdumpdir_png[256] = "";
|
||||
|
||||
bool needToDumpLayers()
|
||||
{
|
||||
bool bDumpLayer = false;
|
||||
char sfdump_propstr[PROPERTY_VALUE_MAX];
|
||||
time_t timenow;
|
||||
tm sfdump_time;
|
||||
|
||||
time(&timenow);
|
||||
localtime_r(&timenow, &sfdump_time);
|
||||
|
||||
if ((property_get("debug.sf.dump.png", sfdump_propstr, NULL) > 0) &&
|
||||
(strncmp(sfdump_propstr, sfdump_propstr_persist_png,
|
||||
PROPERTY_VALUE_MAX - 1))) {
|
||||
// Strings exist & not equal implies it has changed, so trigger a dump
|
||||
strncpy(sfdump_propstr_persist_png, sfdump_propstr,
|
||||
PROPERTY_VALUE_MAX - 1);
|
||||
sfdump_countlimit_png = atoi(sfdump_propstr);
|
||||
sfdump_countlimit_png = (sfdump_countlimit_png < 0) ? 0:
|
||||
(sfdump_countlimit_png >= LONG_MAX) ? (LONG_MAX - 1):
|
||||
sfdump_countlimit_png;
|
||||
if (sfdump_countlimit_png) {
|
||||
sprintf(sfdumpdir_png,"/data/sfdump.png%04d%02d%02d.%02d%02d%02d",
|
||||
sfdump_time.tm_year + 1900, sfdump_time.tm_mon + 1,
|
||||
sfdump_time.tm_mday, sfdump_time.tm_hour,
|
||||
sfdump_time.tm_min, sfdump_time.tm_sec);
|
||||
if (0 == mkdir(sfdumpdir_png, 0777))
|
||||
sfdump_counter_png = 0;
|
||||
else
|
||||
LOGE("sfdump: Error: %s. Failed to create sfdump directory"
|
||||
": %s", strerror(errno), sfdumpdir_png);
|
||||
}
|
||||
}
|
||||
|
||||
if (sfdump_counter_png <= sfdump_countlimit_png)
|
||||
sfdump_counter_png++;
|
||||
|
||||
if ((property_get("debug.sf.dump", sfdump_propstr, NULL) > 0) &&
|
||||
(strncmp(sfdump_propstr, sfdump_propstr_persist_raw,
|
||||
PROPERTY_VALUE_MAX - 1))) {
|
||||
// Strings exist & not equal implies it has changed, so trigger a dump
|
||||
strncpy(sfdump_propstr_persist_raw, sfdump_propstr,
|
||||
PROPERTY_VALUE_MAX - 1);
|
||||
sfdump_countlimit_raw = atoi(sfdump_propstr);
|
||||
sfdump_countlimit_raw = (sfdump_countlimit_raw < 0) ? 0:
|
||||
(sfdump_countlimit_raw >= LONG_MAX) ? (LONG_MAX - 1):
|
||||
sfdump_countlimit_raw;
|
||||
if (sfdump_countlimit_raw) {
|
||||
sprintf(sfdumpdir_raw,"/data/sfdump.raw%04d%02d%02d.%02d%02d%02d",
|
||||
sfdump_time.tm_year + 1900, sfdump_time.tm_mon + 1,
|
||||
sfdump_time.tm_mday, sfdump_time.tm_hour,
|
||||
sfdump_time.tm_min, sfdump_time.tm_sec);
|
||||
if (0 == mkdir(sfdumpdir_raw, 0777))
|
||||
sfdump_counter_raw = 0;
|
||||
else
|
||||
LOGE("sfdump: Error: %s. Failed to create sfdump directory"
|
||||
": %s", strerror(errno), sfdumpdir_raw);
|
||||
}
|
||||
}
|
||||
|
||||
if (sfdump_counter_raw <= sfdump_countlimit_raw)
|
||||
sfdump_counter_raw++;
|
||||
|
||||
bDumpLayer = (sfdump_countlimit_png || sfdump_countlimit_raw)? true : false;
|
||||
return bDumpLayer;
|
||||
}
|
||||
|
||||
inline void getHalPixelFormatStr(int format, char pixelformatstr[])
|
||||
{
|
||||
if (!pixelformatstr)
|
||||
return;
|
||||
|
||||
switch(format) {
|
||||
case HAL_PIXEL_FORMAT_RGBA_8888:
|
||||
strcpy(pixelformatstr, "RGBA_8888");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_RGBX_8888:
|
||||
strcpy(pixelformatstr, "RGBX_8888");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_RGB_888:
|
||||
strcpy(pixelformatstr, "RGB_888");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_RGB_565:
|
||||
strcpy(pixelformatstr, "RGB_565");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_BGRA_8888:
|
||||
strcpy(pixelformatstr, "BGRA_8888");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_RGBA_5551:
|
||||
strcpy(pixelformatstr, "RGBA_5551");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_RGBA_4444:
|
||||
strcpy(pixelformatstr, "RGBA_4444");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_YV12:
|
||||
strcpy(pixelformatstr, "YV12");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
|
||||
strcpy(pixelformatstr, "YCbCr_422_SP_NV16");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
||||
strcpy(pixelformatstr, "YCrCb_420_SP_NV21");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_YCbCr_422_I:
|
||||
strcpy(pixelformatstr, "YCbCr_422_I_YUY2");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_NV12_ENCODEABLE:
|
||||
strcpy(pixelformatstr, "NV12_ENCODEABLE");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
|
||||
strcpy(pixelformatstr, "YCbCr_420_SP_TILED_TILE_4x2");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_YCbCr_420_SP:
|
||||
strcpy(pixelformatstr, "YCbCr_420_SP");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO:
|
||||
strcpy(pixelformatstr, "YCrCb_420_SP_ADRENO");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_YCrCb_422_SP:
|
||||
strcpy(pixelformatstr, "YCrCb_422_SP");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_R_8:
|
||||
strcpy(pixelformatstr, "R_8");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_RG_88:
|
||||
strcpy(pixelformatstr, "RG_88");
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_INTERLACE:
|
||||
strcpy(pixelformatstr, "INTERLACE");
|
||||
break;
|
||||
default:
|
||||
sprintf(pixelformatstr, "Unknown0x%X", format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dumpLayer(int moduleCompositionType, int listFlags, size_t layerIndex,
|
||||
hwc_layer_t hwLayers[])
|
||||
{
|
||||
char dumplogstr_png[128] = "";
|
||||
char dumplogstr_raw[128] = "";
|
||||
if (sfdump_counter_png <= sfdump_countlimit_png) {
|
||||
sprintf(dumplogstr_png, "[png-dump-frame: %03d of %03d] ",
|
||||
sfdump_counter_png, sfdump_countlimit_png);
|
||||
}
|
||||
if (sfdump_counter_raw <= sfdump_countlimit_raw) {
|
||||
sprintf(dumplogstr_raw, "[raw-dump-frame: %03d of %03d]",
|
||||
sfdump_counter_raw, sfdump_countlimit_raw);
|
||||
}
|
||||
if (NULL == hwLayers) {
|
||||
LOGE("sfdump: Error.%s%sLayer[%d] No hwLayers to dump.",
|
||||
dumplogstr_raw, dumplogstr_png, layerIndex);
|
||||
return;
|
||||
}
|
||||
hwc_layer *layer = &hwLayers[layerIndex];
|
||||
hwc_rect_t sourceCrop = layer->sourceCrop;
|
||||
hwc_rect_t displayFrame = layer->displayFrame;
|
||||
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
||||
char pixelformatstr[32] = "None";
|
||||
|
||||
if (hnd)
|
||||
getHalPixelFormatStr(hnd->format, pixelformatstr);
|
||||
|
||||
LOGE("sfdump: %s%s[%s]-Composition, Layer[%d] SrcBuff[%dx%d] "
|
||||
"SrcCrop[%dl, %dt, %dr, %db] "
|
||||
"DispFrame[%dl, %dt, %dr, %db] Composition-type = %s, Format = %s, "
|
||||
"Orientation = %s, Flags = %s%s%s%s%s%s%s%s%s%s",
|
||||
dumplogstr_raw, dumplogstr_png,
|
||||
(moduleCompositionType == COMPOSITION_TYPE_GPU)? "GPU":
|
||||
(moduleCompositionType == COMPOSITION_TYPE_MDP)? "MDP":
|
||||
(moduleCompositionType == COMPOSITION_TYPE_C2D)? "C2D":
|
||||
(moduleCompositionType == COMPOSITION_TYPE_CPU)? "CPU":
|
||||
(moduleCompositionType == COMPOSITION_TYPE_DYN)? "DYN": "???",
|
||||
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,
|
||||
(layer->compositionType == HWC_FRAMEBUFFER)? "Framebuffer (OpenGL ES)":
|
||||
(layer->compositionType == HWC_OVERLAY)? "Overlay":
|
||||
(layer->compositionType == HWC_USE_COPYBIT)? "Copybit": "???",
|
||||
pixelformatstr,
|
||||
(layer->transform == Transform::ROT_0)? "ROT_0":
|
||||
(layer->transform == Transform::FLIP_H)? "FLIP_H":
|
||||
(layer->transform == Transform::FLIP_V)? "FLIP_V":
|
||||
(layer->transform == Transform::ROT_90)? "ROT_90":
|
||||
(layer->transform == Transform::ROT_180)? "ROT_180":
|
||||
(layer->transform == Transform::ROT_270)? "ROT_270":
|
||||
(layer->transform == Transform::ROT_INVALID)? "ROT_INVALID":"???",
|
||||
(layer->flags == 0)? "[None]":"",
|
||||
(layer->flags & HWC_SKIP_LAYER)? "[Skip layer]":"",
|
||||
(layer->flags & HWC_LAYER_NOT_UPDATING)? "[Layer not updating]":"",
|
||||
(layer->flags & HWC_USE_ORIGINAL_RESOLUTION)? "[Original Resolution]":"",
|
||||
(layer->flags & HWC_DO_NOT_USE_OVERLAY)? "[Do not use Overlay]":"",
|
||||
(layer->flags & HWC_COMP_BYPASS)? "[Bypass]":"",
|
||||
(layer->flags & HWC_BYPASS_RESERVE_0)? "[Bypass Reserve 0]":"",
|
||||
(layer->flags & HWC_BYPASS_RESERVE_1)? "[Bypass Reserve 1]":"",
|
||||
(listFlags & HWC_GEOMETRY_CHANGED)? "[List: Geometry Changed]":"",
|
||||
(listFlags & HWC_SKIP_COMPOSITION)? "[List: Skip Composition]":"");
|
||||
|
||||
if (NULL == hnd) {
|
||||
LOGE("sfdump: %s%sLayer[%d] private-handle is invalid.",
|
||||
dumplogstr_raw, dumplogstr_png, layerIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((sfdump_counter_png <= sfdump_countlimit_png) && hnd->base) {
|
||||
bool bResult = false;
|
||||
char sfdumpfile_name[256];
|
||||
SkBitmap *tempSkBmp = new SkBitmap();
|
||||
SkBitmap::Config tempSkBmpConfig = SkBitmap::kNo_Config;
|
||||
sprintf(sfdumpfile_name, "%s/sfdump%03d_layer%d.png", sfdumpdir_png,
|
||||
sfdump_counter_png, layerIndex);
|
||||
|
||||
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(sfdumpfile_name,
|
||||
*tempSkBmp, SkImageEncoder::kPNG_Type, 100);
|
||||
LOGE("sfdump: %sDumped Layer[%d] to %s: %s", dumplogstr_png,
|
||||
layerIndex, sfdumpfile_name, bResult ? "Success" : "Fail");
|
||||
}
|
||||
else {
|
||||
LOGE("sfdump: %sSkipping Layer[%d] dump: Unsupported layer "
|
||||
"format %s for png encoder.", dumplogstr_png, layerIndex,
|
||||
pixelformatstr);
|
||||
}
|
||||
delete tempSkBmp; // Calls SkBitmap::freePixels() internally.
|
||||
}
|
||||
|
||||
if ((sfdump_counter_raw <= sfdump_countlimit_raw) && hnd->base) {
|
||||
char sfdumpfile_name[256];
|
||||
bool bResult = false;
|
||||
sprintf(sfdumpfile_name, "%s/sfdump%03d_layer%d_%dx%d_%s.raw",
|
||||
sfdumpdir_raw,
|
||||
sfdump_counter_raw, layerIndex, hnd->width, hnd->height,
|
||||
pixelformatstr);
|
||||
FILE* fp = fopen(sfdumpfile_name, "w+");
|
||||
if (fp != NULL) {
|
||||
bResult = (bool) fwrite((void*)hnd->base, hnd->size, 1, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
LOGE("sfdump: %s Dumped Layer[%d] to %s: %s", dumplogstr_raw,
|
||||
layerIndex, sfdumpfile_name, bResult ? "Success" : "Fail");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CALC_FPS
|
||||
ANDROID_SINGLETON_STATIC_INSTANCE(CalcFps) ;
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <ui/Region.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <utils/Singleton.h>
|
||||
#include <cutils/properties.h>
|
||||
#include "../libgralloc/gralloc_priv.h"
|
||||
|
||||
using namespace android;
|
||||
@ -364,4 +365,38 @@ int qcomuiClearRegion(Region region, EGLDisplay dpy, EGLSurface sur);
|
||||
*/
|
||||
external_display handleEventHDMI(external_display newEvent, external_display
|
||||
currEvent);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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();
|
||||
|
||||
/*
|
||||
* Dumps a layer's info into logcat and its buffer into raw/png files.
|
||||
*
|
||||
* @param: moduleCompositionType - Composition type set in hwcomposer module.
|
||||
* @param: listFlags - Flags used in hwcomposer's list.
|
||||
* @param: layerIndex - Index of layer being dumped.
|
||||
* @param: hwLayers - Address of hwc_layer_t to log and dump.
|
||||
*
|
||||
*/
|
||||
void dumpLayer(int moduleCompositionType, int listFlags, size_t layerIndex,
|
||||
hwc_layer_t hwLayers[]);
|
||||
|
||||
#endif // INCLUDE_LIBQCOM_UI
|
||||
|
Loading…
x
Reference in New Issue
Block a user