Skip to content

Commit

Permalink
Add order file sample.
Browse files Browse the repository at this point in the history
I took the PGO and Camera example and added order file functionalities
to them. I added steps so people can figure out how to add order file to
their own Android application.
  • Loading branch information
Sharjeel-Khan authored Aug 4, 2023
1 parent a95778a commit 87bf557
Show file tree
Hide file tree
Showing 35 changed files with 1,024 additions and 0 deletions.
100 changes: 100 additions & 0 deletions orderfile/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Order file demo

Order files are text files containing symbols representing functions names.
Linkers (lld) uses order files to layout functions in a specific order. These
binaries with ordered symbols will reduce page faults and improve a program's
launch time due to the efficient loading of symbols during a program’s
cold-start.

## Files

- app/src/main/cpp/orderfile.cpp: The source code for the orderfile library that
is used by the Kotlin app.
- app/src/main/cpp/CMakeLists.txt: The CMakeLists either sets the orderfile
library as generating profiles or loading the orderfile.
- app/src/main/java/MainActivity.kt: The Kotlin app source code.

## Profile Steps

1. For simplicity, we have setup the `CMakeLists.txt` and you just need make
sure `set(GENERATE_PROFILES ON)` is not commented. You need to pass any
optimization flag except `-O0`. The mapping file is not generated and the
profile instrumentation does not work without an optimization flag.
1. Run the app on Android Studio. You can either run it on a physical or virtual
device. You will see "Hello World" on the screen.
1. To pull the data from the device, you'll need to move it from an app-writable
directory to a shell readable directory for adb pull. We also need to
transfer the output into hexadecimal format.

```
adb shell "run-as com.example.orderfiledemo sh -c 'cat /data/user/0/com.example.orderfiledemo/cache/demo.output.order' | cat > /data/local/tmp/demo.output.order"
adb pull /data/local/tmp/demo.output.order .
# Convert to hexdeciaml format on Linux, Mac, or ChromeOS
hexdump -C demo.output.order > demo.prof
# Convert to hexdecimal format on Windows
certutil -f -encodeHex demo.output.order demo.prof
```

4. Once you get both mapping file and profile file, you can use
[this script](https://android.googlesource.com/toolchain/pgo-profiles/+/refs/heads/main/scripts/create_orderfile.py)
to create the order file:

```
python3 create_orderfile.py --profile-file demo.prof --mapping-file mapping.txt --output app/src/main/cpp/demo.orderfile
```

## Load Steps

1. For load, you need to uncomment
`set(USE_PROFILE "${CMAKE_SOURCE_DIR}/demo.orderfile")` and make sure
`set(GENERATE_PROFILES ON)` is commented.

1. If you want to validate the shared library's layout is different, you need to
find `liborderfiledemo.so` and run `nm`

```
nm -n liborderfiledemo.so
```

## Difference between Java and Kotlin App

The main difference between a Java app and a Kotlin app is the syntax. You can
easily change this Kotlin example into a Java example.

- Load Library

```
# Kotlin
companion object {
init {
System.loadLibrary("orderfiledemo")
}
}
# Java
static {
System.loadLibrary("orderfiledemo");
}
```

- Recognize an external method

```
# Kotlin
external fun runWorkload(tempDir: String)
# Java
private native void runWorkload(String tempDir);
```

- Get the cache directory

```agsl
# Kotlin
runWorkload(applicationContext.cacheDir.toString())
# Java
runWorkload(getcacheDir().toString())
```
1 change: 1 addition & 0 deletions orderfile/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
60 changes: 60 additions & 0 deletions orderfile/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
}

android {
compileSdk 31

defaultConfig {
applicationId "com.example.orderfiledemo"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
ndkVersion '25.2.9519653'

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ''
}
}
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.22.1'
}
}
buildFeatures {
viewBinding true
}
namespace 'com.example.orderfiledemo'
ndkVersion '25.2.9519653'
}

dependencies {

implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
21 changes: 21 additions & 0 deletions orderfile/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.orderfiledemo

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.orderfiledemo", appContext.packageName)
}
}
22 changes: 22 additions & 0 deletions orderfile/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.OrderfileDemo">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
25 changes: 25 additions & 0 deletions orderfile/app/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.22.1)
project(OrderfileDemo CXX)

# We have setup build variables that you can just comment or uncomment to use.
# Make sure to have only one build variable uncommented at a time.
# If you want to generate profiles and mapping file, make sure GENERATE_PROFILES is uncommented.
# If you want to use your generated order file to layout symbols, uncomment USE_PROFILE.


set(GENERATE_PROFILES ON)
#set(USE_PROFILE "${CMAKE_SOURCE_DIR}/demo.orderfile")

add_library(orderfiledemo SHARED orderfile.cpp)
target_link_libraries(orderfiledemo log)

if(GENERATE_PROFILES)
# Generating profiles requires any optimization flag aside from -O0.
# The mapping file will not generate and the profile instrumentation does not work without an optimization flag.
target_compile_options(orderfiledemo PRIVATE -forder-file-instrumentation -O1 -mllvm -orderfile-write-mapping=mapping.txt )
target_link_options(orderfiledemo PRIVATE -forder-file-instrumentation )
target_compile_definitions(orderfiledemo PRIVATE GENERATE_PROFILES)
elseif(USE_PROFILE)
target_compile_options(orderfiledemo PRIVATE -Wl,--symbol-ordering-file=${USE_PROFILE} -Wl,--no-warn-symbol-ordering )
target_link_options(orderfiledemo PRIVATE -Wl,--symbol-ordering-file=${USE_PROFILE} -Wl,--no-warn-symbol-ordering )
endif()
54 changes: 54 additions & 0 deletions orderfile/app/src/main/cpp/orderfile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include <android/log.h>
#include <errno.h>
#include <jni.h>
#include <linux/limits.h>
#include <stdlib.h>
#include <string.h>

const char kLogTag[] = "orderfiledemo";

#ifdef GENERATE_PROFILES
extern "C" int __llvm_profile_set_filename(const char *);
extern "C" int __llvm_profile_initialize_file(void);
extern "C" int __llvm_orderfile_dump(void);
#endif

void DumpProfileDataIfNeeded(const char *temp_dir) {
#ifdef GENERATE_PROFILES
char profile_location[PATH_MAX] = {};
snprintf(profile_location, sizeof(profile_location), "%s/demo.output",
temp_dir);
if (__llvm_profile_set_filename(profile_location) == -1) {
__android_log_print(ANDROID_LOG_ERROR, kLogTag,
"__llvm_profile_set_filename(\"%s\") failed: %s",
profile_location, strerror(errno));
return;
}

if (__llvm_profile_initialize_file() == -1) {
__android_log_print(ANDROID_LOG_ERROR, kLogTag,
"__llvm_profile_initialize_file failed: %s",
strerror(errno));
return;
}

if (__llvm_orderfile_dump() == -1) {
__android_log_print(ANDROID_LOG_ERROR, kLogTag,
"__llvm_orderfile_dump() failed: %s", strerror(errno));
return;
}
__android_log_print(ANDROID_LOG_DEBUG, kLogTag, "Wrote profile data to %s",
profile_location);
#else
__android_log_print(ANDROID_LOG_DEBUG, kLogTag,
"Did not write profile data because the app was not "
"built for profile generation");
#endif
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_orderfiledemo_MainActivity_runWorkload(JNIEnv *env,
jobject /* this */,
jstring temp_dir) {
DumpProfileDataIfNeeded(env->GetStringUTFChars(temp_dir, 0));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.example.orderfiledemo

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.orderfiledemo .databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
runWorkload(applicationContext.cacheDir.toString())
binding.sampleText.text = "Hello, world!"
}

/**
* A native method that is implemented by the 'orderfiledemo' native library,
* which is packaged with this application.
*/
external fun runWorkload(tempDir: String)

companion object {
// Used to load the 'orderfiledemo' library on application startup.
init {
System.loadLibrary("orderfiledemo")
}
}
}
30 changes: 30 additions & 0 deletions orderfile/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
Loading

0 comments on commit 87bf557

Please sign in to comment.