Developer Perspective: UE4 Logging and Console Commands for Mobile VR
Oculus Developer Blog
|
Posted by Brock Heinz
|
July 2, 2019
|
Share

From time to time we collaborate with partner developers around the world, so that we can share even more VR development learnings with our community. Today, we hear from Brock Heinz of Turtle Rock Studios, as he provides a number of recommendations for using UE4 to develop mobile VR applications. Be sure to check out the latest UE4 Integration Release, we hope you find this latest post insightful!


If you’re new to working on the Quest, Go or GearVR, then you may not know how to use some of the standard development tools that you would rely on when developing a UE4 game. In this blog post, we’ll show you how to get those tools back in to your toolbox when developing for these Android based mobile VR devices. We used these same tools to help supercharge our development team while shipping Journey of the Gods and Face Your Fears 2 for the Oculus Quest!

Using ADB to control the device

You’ll be using the Android Debug Bridge (adb) command line tool to work with your mobile VR device. To use it, open a Command Prompt and type "adb" followed by the command you want to use:

C:\Temp>adb devices
List of devices attached
e6555e96  device

Retrieving Logs

You can use the adb "pull" command to retrieve files from the device. This can be used to pull logs and other files as long as you know the full path:

adb pull /sdcard/UE4Game/ProjectName/ProjectName/Saved/Logs

Screenshot

You can take a screenshot from the command line, and then pull it from the device to your PC.

adb shell screencap -p /sdcard/screencap.png
adb pull /sdcard/screencap.png

Installing an application

UE4’s package building process will make .bat files that you can use to install / uninstall an application, but you can do it manually via adb if you need to. You can install them with the filename, and supply the "-g" flag to grant all permissions to the application. You install by specifying the APK filename:

adb install -g ProjectName-armv7-es2.apk

Uninstalling an application

You uninstall by specifying the package name:

adb uninstall com.yourcompany.yourproject

Launching an application

You can launch an application by issuing a command to the application manager and providing your application's package name:

adb shell am start -n 
com.yourcompany.yourproject/com.epicgames.ue4.GameActivity

Stopping an application

adb shell am force-stop com.yourcompany.yourproject

Real Time Logging

You can get a lot of useful information by using android’s real time logging feature, commonly called “logcat”. You can see this by opening a command prompt and running:

adb logcat

You’ll see something like the following:

This will often include information that is not relevant to your current focus. You can use filters to only show the information you need. For example, these logcat filters will show you the VrApi performance stats:

adb logcat VrApi:I --regex="FPS="


We find it easier to use the Android Device Monitor utility to get real time logging data from the Quest. In addition to having an easy to use UI, it contains a number of other useful development tools.

To access Android Device Monitor make a shortcut that points at this batch file:

C:\NVPACK\android-sdk-windows\tools\monitor.bat

When you launch it, you’ll see a window like this one. To see logcat messages from your Quest, you’ll need to select it in the Devices list.


The first thing that you’ll want to do is go into the preferences and increase the size of the message buffer:

Now make a few filters. When you add the filter below, you’ll only see log messages from UE4 applications running on your Quest!

Console Commands

UE4 provides a way to send console commands to games on Android devices via broadcast intents:

adb shell "am broadcast -a android.intent.action.RUN -e cmd 'CONSOLE COMMAND GOES HERE'"

For example, this command will turn on an in-game frametime display:

adb shell "am broadcast -a android.intent.action.RUN -e cmd 'stat unit'"

However, this can get cumbersome to use. Luckily, you can use the “doskey” command to create macros that make this easier:

doskey com=adb shell "am broadcast -a android.intent.action.RUN -e cmd '$*'"
com stat unit

Diagnosing Crashes

If you need to investigate a crash, you have a few options. If it is easy to reproduce, you can use Android Device Monitor to get real time logging, then trigger the crash. As long as you have installed a development or debug build with symbols, you’ll see a callstack leading to the function that crashed and some other relevant info:

  
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'oculus/vr_monterey/monterey:7.1.1/NGI77B/256550.6170.5:user/release-keys'
Revision: '0'
ABI: 'arm'
pid: 2571, tid: 2586, name: GameThread >>> com.yourcompany.yourproject <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x3
r0 00000003 r1 0000007b r2 00000000 r3 00000a1a
r4 cf926570 r5 cf926570 r6 ffffffff r7 000f4240
r8 fc000000 r9 d11c5aa0 sl 000f4240 fp d1091868
ip fcf45380 sp d10915f0 lr ccef6c6c pc ccef6c74 cpsr 80070010
backtrace:
#00 pc 068dec74 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN7UEngine12PerformErrorEPKDsR13FOutputDevice+1260)
#01 pc 068c31a0 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN7UEngine4ExecEP6UWorldPKDsR13FOutputDevice+4388)
#02 pc 0602b480 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN11UGameEngine4ExecEP6UWorldPKDsR13FOutputDevice+1068)
#03 pc 06086628 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN19UGameViewportClient4ExecEP6UWorldPKDsR13FOutputDevice+1576)
#04 pc 061f4b0c /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN12ULocalPlayer4ExecEP6UWorldPKDsR13FOutputDevice+2264)
#05 pc 068b80d4 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN7UEngine20TickDeferredCommandsEv+932)
#06 pc 03359d50 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN11FEngineLoop4TickEv+42496)
#07 pc 0334d51c /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_Z11AndroidMainP11android_app+4348)
#08 pc 0335c400 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (android_main+144)
#09 pc 0337df28 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so
#10 pc 000478d3 /system/lib/libc.so (_ZL15__pthread_startPv+22)
#11 pc 00019fe5 /system/lib/libc.so (__start_thread+6)

If you don’t have Android Device Monitor running when the game crashes, or if you need crash data from a headset that your QA team is using, you can use adb to retrieve bug reporting info from the device:

adb bugreport
/data/user_de/0/com.android.shell/files/bugreports/bugrepo...39.zip: 1 file pulled. 37.2 MB/s (1393690 bytes in 0.036s)

If you open that zip file, you’ll find a ton logs and other data about the state of the device. The information we want is in the “data/tombstones” folder. Look for a tombstone file with time and date stamp that corresponds when the crash occurred, and open it in a text editor. As long as you had symbols in the build, you should see a backtrace with the function calls leading to the crash, similar to the output above from Android Device Monitor.

Passing Command Line Switches

Out of the box, UE4 doesn’t have a way to pass a command line switch to the application, however, you can add this feature with a few code changes. These changes were made in UE 4.21, but should be easy to adapt to other engine versions.

GameActivity.java.template

// TRS CHANGE BEGIN 
public String AndroidThunkJava_GetExtraCommandLineArg()
{ 
	String extraArg = "";
	try 
	{ 
		if (_extrasBundle != null)
		{
			extraArg = _extrasBundle.getString("extraArg");
		} 
	} 
	catch (Exception e) 
	{
		Log.debug("[JAVA] - AndroidThunkJava_GetExtraCommandLineArg exception " + e.toString());
	} 

				//Log.debug("AndroidThunkJava_GetExtraCommandLineArg returning " + extraArg);
			return extraArg;
}
// TRS CHANGE END 

AndroidJNI.h

class FJavaWrapper
{
public:


<SNIP> - around line 140
// TRS CHANGE BEGIN 
static jmethodID AndroidThunkJava_GetExtraCommandLineArg;
// TRS CHANGE END 


};

AndroidJNI.cpp

void FJavaWrapper::FindClassesAndMethods(JNIEnv* Env)
{
<SNIP> - around line 150
// TRS CHANGE BEGIN 
AndroidThunkJava_GetExtraCommandLineArg = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_GetExtraCommandLineArg", "()Ljava/lang/String;", bIsOptional);
// TRS CHANGE END 
}


<SNIP> - around line 450
// TRS CHANGE BEGIN - Brock H. - 01/24/2019
jmethodID FJavaWrapper::AndroidThunkJava_GetExtraCommandLineArg;
// TRS CHANGE END - Brock H. - 01/24/2019


<SNIP> - around line 1600
// TRS CHANGE BEGIN 
FString AndroidThunkCpp_GetExtraCommandLineArg()
{
	FString ExtraArg;
	if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
	{
		jstring extraArg = (jstring)FJavaWrapper::CallObjectMethod(Env, FJavaWrapper::GameActivityThis, FJavaWrapper::AndroidThunkJava_GetExtraCommandLineArg);
		if (!Env->IsSameObject(extraArg, NULL))
		{
			const char *nativeandroidextraArgString = Env->GetStringUTFChars(extraArg, 0);
			ExtraArg = FString(nativeandroidextraArgString);
			Env->ReleaseStringUTFChars(extraArg, nativeandroidextraArgString);
			Env->DeleteLocalRef(extraArg);
		}
	}
	return ExtraArg;
}
// TRS CHANGE END
  

LaunchAndroid.cpp

static void InitCommandLine()
{
<SNIP> - around line 300
	// TRS CHANGE BEGIN 
#if !UE_BUILD_SHIPPING
	{
		extern FString AndroidThunkCpp_GetExtraCommandLineArg();


		FString ExtraArg = AndroidThunkCpp_GetExtraCommandLineArg();
		if (!ExtraArg.IsEmpty())
		{
			FCommandLine::Append(TEXT(" "));
			FCommandLine::Append(*ExtraArg);
			FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Extra Command Line Arg: %s"), *ExtraArg);
			UE_LOG(LogAndroid, Log, TEXT("Extra Command Line Arg: %"), *ExtraArg);
		} 
}
#endif
	// TRS CHANGE END 
}

Once you have this code in place, you can pass a command line argument to the game when you launch it via adb. For example, the code below will launch the game with garbage collection verification disabled, which helps reduce hitches in development builds:

adb shell am start -e extraArg -NoVerifyGC -n gg.trs.porter/com.epicgames.ue4.GameActivity

We have simplified this a bit by having the UE4 packaging process generate a “Launch.bat” file when it generates the install and uninstall batch files. It is an easy change in AndroidPlatform.Automation.cs if you want to make the same change for your project.

Basic Profiling

We can start to use the following tools to get some basic profiling information from the game while it’s running on a Quest or Go.

Real time performance stats

You can use the ‘stat unit’ command to get an onscreen display of real time performance statistics:

com stat unit

  • Frame: Total frame time, in milliseconds. You want this at 16 for 60fps, 13 for 72 fps.
  • Game: Main gameplay thread CPU frame time
  • Draw: Render thread CPU time
  • GPU: GPU frame time.
  • RHIT: If you’ve enabled the OpenGL RHI Thread, this will be the time spent in that thread each frame.
  • Memory: Memory used by the game. This isn’t 100% accurate, since some memory allocated by the OS, video driver, etc. won’t be reflected.
  • Draws: Number of objects drawn per frame.
  • Prims: Number of triangles drawn per frame.

Memory Usage

You can generate a memory report at any time. We’ll discuss this further in a future post:

com memreport -full
adb pull /sdcard/UE4Game/YourProject/YourProject/Saved/

CPU Profiling

The Profiler Tool enables you to capture profiling stats on your device, and bring them in to the editor to be analyzed. You can turn on stats recording, play a bit of the game, then turn stats recording off and pull it with the following:

com stat startfile
< play the game for a bit >
com stat stopfile
adb pull /sdcard/UE4Game/YourProject/YourProject/Saved/
  

Enabling Logging in Shipping Builds

UE4 disables logs for shipping builds. Generally, this is a good policy, but there are times when you need logs from a build configured for distribution. Luckily, you can enable logging in shipping builds with just a few changes.

First, you’ll need to open the YourProject.Target.cs file, and add this line:

bUseLoggingInShipping = true;

Second, you’ll need to open AndroidMisc.cpp, and make this change:

void FAndroidMisc::LocalPrint(const TCHAR *Message)
{
// TRS CHANGE BEGIN 
#if !UE_BUILD_SHIPPING || (USE_LOGGING_IN_SHIPPING==1)
// TRS CHANGE END

This will re-enable logging functionality for your Windows and Android builds. Note that if you want to find the logs from a shipping build on Windows, they will be in a different location:

C:\Users\UserName\AppData\Local\YourProject\Saved\Logs

Useful Links

Go Make Awesome Games!

Hopefully this post will help you get off to a good start during the early phases of developing mobile VR games with UE4. In my next post, I’ll discuss a few things we learned to help improve loading times and memory usage in Journey of the Gods and Face Your Fears 2.

If you’d like to learn more about what we’re doing in VR, jump in to our forums, join us on Discord, or hit us up on Twitter @TurtleRock!

Brock Heinz
Lead Programmer
Turtle Rock Studios