I was given some code, which I tried to port as an OF addon. This code works well with a desktop OF build; however, when I try to use it in an Android build, the rendering is not as expected. While I do not at present understand fully this code (as I am not an OpenGL expert), I suspect it has something to do with textures and transparencies, and/or differences between standard OpenGL and GLES. So I tried to do some OpenGL (ES) debugging on Android, but I cannot get it to work. The Android device that I work with is unrooted, which is likely the reason for my failure - unfortunately, it is borrowed, and I would rather not root it. So my question, in general, is: can I get OpenGL debugging to work with OF on an unrooted Android device, and if so - how?
This is what I’ve tried so far:
1. gltracer
I’ve tried using gltracer
, Android’s own Tracer for OpenGL ES. In my case, it can be accessed via [Android SDK dir]/sdk/tools/monitor
, then choosing “Window > Open Perspective… > Tracer for OpenGL ES”; then click the icon for “Connects to the device and collects OpenGL trace information”, and in the window enter this:
My app has debuggable: true
set, and when I click on Trace, the trace does get collected in the destination file (on the PC), however the app runs extremely slow (for about 1-2 mins of runtime, almost 500 MB are collected, but apparently only for some 12 frames). The strange thing is that when I open the saved trace in the tracer, if I select glDrawElements
commands, in Details window I do get a rendering as on the device:
… however, if I select any of the textures, I always get a blank/empty texture (or, rarely, some random pixels):
So, my subquestion here would be: why cannot I get the textures properly; could that be related to the device not being rooted? How could I otherwise get the correct textures with this approach?
Actually, it turns out these textures - or at least the “Texture Mipmap State” - are unpacked as images in /tmp
when the tracer reads the trace file; there are some .dat
files which I don’t know how to open, but also some .png
files, which I can see are “all zeroes” - for the ones I see as blank:
$ hexdump -C /tmp/1452590078932-0/tex8208098562390494194.png
00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR|
00000010 00 00 02 00 00 00 02 00 08 06 00 00 00 f4 78 d4 |..............x.|
00000020 fa 00 00 04 0f 49 44 41 54 78 da ed c1 31 01 00 |.....IDATx...1..|
00000030 00 00 c2 a0 f5 4f 6d 07 6f a0 00 00 00 00 00 00 |.....Om.o.......|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000430 00 00 e0 37 02 f0 00 01 2f da c0 4b 00 00 00 00 |...7..../..K....|
00000440 49 45 4e 44 ae 42 60 82 |IEND.B`.|
EDIT: Actually, gltracer
did reveal something; namely, when the application comes to the problematic rendering, I get this in adb logcat
:
...
W/Adreno-ES20(16293): <core_glTexSubImage2D:562>: GL_INVALID_OPERATION
...
… and, when I step through the gl calls in the list, I get the message:
Error applying transformations for glTexSubImage2D(target = GL_TEXTURE_2D, level = 0, xoffset = 0, yoffset = 0, width = 256, height = 256, format = GL_BGRA_EXT, type = GL_UNSIGNED_BYTE, pixels = 0x??)
java.lang.RuntimeException
… but this error is not reported when I step through, say,
glTexSubImage2D(target = GL_TEXTURE_2D, level = 0, xoffset = 0, yoffset = 0, width = 256, height = 256, format = GL_RGBA, type = GL_UNSIGNED_BYTE, pixels = 0x??)
… which probably points to a problem in using format = GL_BGRA_EXT
with glTexSubImage2D
? But it’s difficult to confirm this otherwise…
2. apitrace
Because of the above problems (and others, such as not being able to copy/paste some of the data shown in the Android gltracer window; being unable to obtain C++ code backtrace for a particular OpenGL call in Android gltracer), I also tried apitrace. I have tested it with the desktop version of my app, and apitrace
there works great: I get OpenGL calls, as well as all textures; however, I don’t have a problem with the desktop version.
So, I followed apitrace/INSTALL.markdown, and managed to build an Android version of apitrace
. Here, I get an Android .apk
built, however, it packs only these files:
$ unzip -l /path/to/apitrace_git/build/android-build/build/outputs/apk/android-build-debug.apk
Archive: /path/to/apitrace_git/build/android-build/build/outputs/apk/android-build-debug.apk
Length Date Time Name
--------- ---------- ----- ----
2308 2015-12-15 01:23 AndroidManifest.xml
9193 2015-12-15 01:23 res/drawable-hdpi-v4/ic_launcher.png
5057 2015-12-15 01:23 res/drawable-mdpi-v4/ic_launcher.png
14068 2015-12-15 01:23 res/drawable-xhdpi-v4/ic_launcher.png
18365 2015-12-15 01:23 res/drawable-xxhdpi-v4/ic_launcher.png
1148 2015-12-15 01:23 resources.arsc
15928 2015-12-15 01:23 classes.dex
7294732 2015-12-15 01:23 lib/armeabi-v7a/libretrace.so
5630724 2015-12-15 01:23 lib/armeabi-v7a/libgnustl_shared.so
808 2015-12-15 01:23 META-INF/MANIFEST.MF
837 2015-12-15 01:23 META-INF/CERT.SF
776 2015-12-15 01:23 META-INF/CERT.RSA
--------- -------
12993944 12 files
… and I really cannot tell what is supposed to run here; I can install the .apk
on the Android device, I can also open it, but it seems to be doing nothing?! I really cannot tell what this .apk is supposed to be used for…
Anyways, if one consults apitrace/Android.markdown (used to be called Dalvik.markdown
), one can see this android-build-debug.apk
is not even mentioned - what one is supposed to use, is /path/to/apitrace_git/build/wrappers/egltrace.so
. However, there is a problem because I have an unrooted device:
- Since the device is unrooted, I cannot run
adb root
- Likewise, I cannot copy anything to the
/data
folder on the Android device - Finally, I cannot run
adb shell setprop
to set arbitrary properties
As a check for setprop
, I ran this in adb shell
:
shell@flo:/ $ setprop persist.this.is.my.property testing
shell@flo:/ $ getprop persist.this.is.my.property
shell@flo:/ $
shell@flo:/ $ getprop camera.disable_zsl_mode
1
… and while I can read some properties, I definitely cannot set my arbitrary ones (even if there is no error raised during setprop
; so it is the following getprop
that confirms that the property hasn’t been set).
In terms of apitrace/Android.markdown, that means I cannot set the wrap.$PROCNAME
property, which would load egltrace.so
via LD_PRELOAD
environment variable. (Note also the 31 character property limitation, which otherwise leads to the error “could not set property”, see http://stackoverflow.com/questions/19011887/how-do-i-run-valgrind-with-an-android-app)
So, first, I copied libgnustl_shared.so
and egltrace.so
(renamed to libegltrace.so
) to a new folder, libs/armeabi-v7a/
in my OF project, which packs these shared object files in the .apk
of my OF Android app, thereby making them present on the device (see ofxQCAR-Android : OF overwrites custom Android.mk when trying to add external library). Then I tried to manually “force” LD_PRELOAD
in adb shell
:
shell@flo:/ LD_PRELOAD=/data/app/cc.openframeworks.MY_APP-2/lib/arm/libegltrace.so am start -S cc.openframeworks.MY_APP/cc.openframeworks.MY_APP.OFActivity
CANNOT LINK EXECUTABLE DEPENDENCIES: library "libgnustl_shared.so" not found
shell@flo:/ LD_PRELOAD=/data/app/cc.openframeworks.MY_APP-2/lib/arm/libgnustl_shared.so:/data/app/cc.openframeworks.MY_APP-2/lib/arm/libegltrace.so \
TRACE_FILE=MY_APP.trace \
am start -S \
cc.openframeworks.MY_APP/cc.openframeworks.MY_APP.OFActivity
Segmentation fault
Well, clearly that didn’t work either - and the segfault produces this log in adb logcat
:
D/apitrace(25758): apitrace: loaded into /system/bin/sh
D/apitrace(25758): apitrace: loaded into app_process
D/AndroidRuntime(25758): >>>>>> START com.android.internal.os.RuntimeInit uid 2000 <<<<<<
D/AndroidRuntime(25758): CheckJNI is OFF
D/apitrace(25758): apitrace: warning: caught signal 11
--------- beginning of crash
F/libc (25758): Fatal signal 11 (SIGSEGV), code 1, fault addr 0x2b in tid 25758 (main)
I/DEBUG ( 184): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG ( 184): Build fingerprint: 'google/razor/flo:5.1.1/LMY48M/2167285:user/release-keys'
I/DEBUG ( 184): Revision: '0'
I/DEBUG ( 184): ABI: 'arm'
I/DEBUG ( 184): pid: 25758, tid: 25758, name: main >>> app_process <<<
I/DEBUG ( 184): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x2b
W/NativeCrashListener( 544): Couldn't find ProcessRecord for pid 25758
I/DEBUG ( 184): r0 b4427c00 r1 beb5552c r2 00000000 r3 0000002f
E/DEBUG ( 184): AM write failure (32 / Broken pipe)
I/DEBUG ( 184): r4 b4427c00 r5 00000001 r6 b4427c00 r7 b4427c18
I/DEBUG ( 184): r8 b4427c00 r9 00000000 sl beb5552c fp 0aaaaaaa
I/DEBUG ( 184): ip b00f97a4 sp beb55488 lr b00d76f3 pc b6f52848 cpsr 60070010
I/DEBUG ( 184):
I/DEBUG ( 184): backtrace:
I/DEBUG ( 184): #00 pc 000bc848 /data/app/cc.openframeworks.MY_APP-2/lib/arm/libgnustl_shared.so (std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&)+12)
I/DEBUG ( 184): #01 pc 00000014 <unknown>
I/DEBUG ( 184):
Finally, I found http://www.hackermusings.com/2012/03/debugging-opengl-on-android-without-losing-your-sanity/, which instead of using this approach, uses dlopen
; so I do something like this in my OF main.cpp
:
string tracepath = ofToDataPath("MY_APP.trace", true);
setenv("TRACE_FILE", tracepath.c_str(), true);
void* handle = dlopen("libegltrace.so", RTLD_NOW); // RTLD_LAZY
if (!handle) {
ofLogNotice() << "Cannot open library: " << dlerror() << '\n';
}
… and this, to an extent, works - in the sense, that when I run the app, I get these entries in adb logcat
upon starting and exiting the application:
D/apitrace(16995): apitrace: loaded into cc.openframeworks.MY_APP
....
D/apitrace(16995): apitrace: unloaded from cc.openframeworks.MY_APP
… however, there is no other mention of apitrace
in the log, and there is no log file created anywhere on the Android device. Apparently, after the dlopen
, I should also somehow “overload” symbols, but that seems to be not trivial for C++ (see:
- http://stackoverflow.com/questions/1067346/alternatives-to-dlsym-and-dlopen-in-c
- http://stackoverflow.com/questions/4713343/how-to-manipulate-return-data-with-dynamically-loaded-functions-dlopen
- Dynamic Class Loading for C++ on Linux | Linux Journal
); and besides, the “loaded into…” message is produced by LocalWriter::LocalWriter()
constructor method in /path/to/apitrace_git/common/trace_writer_local.cpp
; but the opening of the trace file is in void LocalWriter::open(void)
- and I’m really not clear on how to trigger this function from my OF application (apparently, some sort of extern "C"
would be required, but where, and for which functions/symbols?)
So my question here is: given that the LD_PRELOAD
approach seemingly doesn’t work for me (due to not being root) - is there anything else I could do, to use apitrace
's egltrace.so
on Android? The dlopen
approach seems promising - but what else should I do, in order to trigger actual tracing into a trace file on Android, from my OF application?