Skip to content

Commit 2cd806d

Browse files
author
Github Executorch
committed
Add runtime control to Android ETDump profiling
Enhances existing EXECUTORCH_ANDROID_PROFILING with runtime control API. Replaces hardcoded profiling with flexible, production-ready implementation. New Capabilities: - Runtime profiling control via ETDump.enableProfiling()/disableProfiling() - Configurable output paths (no root access required) - Programmatic data access with getETDumpData() - Zero performance overhead when profiling is disabled - Single APK deployment (enable/disable at runtime) Implementation: - Add ETDump.java: Public Java API for profiling control - Add executorch_jni_etdump.cpp: JNI implementation with global ETDumpManager - Modify jni_layer.cpp: Integrate ETDump with Module loading - Modify CMakeLists.txt: Enhance EXECUTORCH_ANDROID_PROFILING flag Backward Compatible: - Same build flag (EXECUTORCH_ANDROID_PROFILING) - Default behavior: profiling disabled (zero overhead) - Users who already enable profiling get new features automatically Usage: // Build with: -DEXECUTORCH_ANDROID_PROFILING=ON ETDump.enableProfiling("/sdcard/profile.etdump"); Module module = Module.load("model.pte"); module.forward(inputs); ETDump.writeETDump(); ETDump.disableProfiling();
1 parent aea2784 commit 2cd806d

File tree

6 files changed

+743
-8
lines changed

6 files changed

+743
-8
lines changed

extension/android/CMakeLists.txt

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,36 @@ list(
9191
fbjni
9292
)
9393

94+
# =============================================================================
95+
# Android ETDump Profiling with Runtime Control
96+
# =============================================================================
97+
9498
if(EXECUTORCH_ANDROID_PROFILING)
95-
list(APPEND link_libraries etdump flatccrt)
99+
# Add ETDump JNI implementation
100+
target_sources(executorch_jni PRIVATE jni/jni_etdump.cpp)
101+
102+
# Add SDK include directories for ETDump headers
103+
target_include_directories(
104+
executorch_jni
105+
PRIVATE
106+
${EXECUTORCH_ROOT}/sdk
107+
${EXECUTORCH_ROOT}/third-party/flatcc/include
108+
)
109+
110+
# Add compile definitions
96111
target_compile_definitions(
97-
executorch_jni PUBLIC EXECUTORCH_ANDROID_PROFILING=1
112+
executorch_jni
113+
PUBLIC
114+
ET_EVENT_TRACER_ENABLED
115+
EXECUTORCH_ANDROID_PROFILING=1
98116
)
117+
118+
# Link ETDump libraries
119+
list(APPEND link_libraries etdump flatccrt)
120+
121+
message(STATUS "ETDump profiling support with runtime control: ENABLED")
122+
else()
123+
message(STATUS "ETDump profiling support: DISABLED")
99124
endif()
100125

101126
if(TARGET optimized_native_cpu_ops_lib)
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
package org.pytorch.executorch;
10+
11+
import android.util.Log;
12+
import androidx.annotation.Nullable;
13+
import java.io.File;
14+
15+
/**
16+
* ETDump provides profiling and performance analysis capabilities for ExecuTorch models.
17+
*
18+
* <p>This class allows you to:
19+
* <ul>
20+
* <li>Enable/disable profiling at runtime</li>
21+
* <li>Capture detailed execution traces including operator timings</li>
22+
* <li>Write profiling data to .etdump files for analysis</li>
23+
* </ul>
24+
*
25+
* <p>Example usage:
26+
* <pre>{@code
27+
* // Enable profiling before loading the model
28+
* ETDump.enableProfiling("/sdcard/model_profile.etdump");
29+
*
30+
* // Load and run your model
31+
* Module module = Module.load(modelPath);
32+
* EValue[] outputs = module.forward(inputs);
33+
*
34+
* // Write profiling data
35+
* ETDump.writeETDump();
36+
*
37+
* // Or write to a different path
38+
* ETDump.writeETDumpToPath("/sdcard/another_profile.etdump");
39+
*
40+
* // Disable profiling when done
41+
* ETDump.disableProfiling();
42+
* }</pre>
43+
*
44+
* <p>Note: To use ETDump, ExecuTorch must be built with profiling support enabled.
45+
* See the build documentation for details.
46+
*/
47+
public class ETDump {
48+
private static final String TAG = "ExecuTorch-ETDump";
49+
50+
static {
51+
try {
52+
System.loadLibrary("executorch");
53+
nativeInit();
54+
} catch (UnsatisfiedLinkError e) {
55+
Log.e(TAG, "Failed to load executorch library", e);
56+
throw e;
57+
}
58+
}
59+
60+
/**
61+
* Initialize the ETDump subsystem.
62+
* Called automatically when the class is loaded.
63+
*/
64+
private static native void nativeInit();
65+
66+
/**
67+
* Enable profiling and set the default output path for ETDump data.
68+
*
69+
* <p>This must be called before loading any models that you want to profile.
70+
* Once enabled, all subsequent model executions will be profiled.
71+
*
72+
* @param outputPath The file path where ETDump data will be written.
73+
* Must be a writable location (e.g., app's cache or external storage).
74+
* @return true if profiling was successfully enabled, false otherwise
75+
*/
76+
public static boolean enableProfiling(String outputPath) {
77+
if (outputPath == null || outputPath.isEmpty()) {
78+
Log.e(TAG, "Output path cannot be null or empty");
79+
return false;
80+
}
81+
82+
// Validate that parent directory exists
83+
File file = new File(outputPath);
84+
File parentDir = file.getParentFile();
85+
if (parentDir != null && !parentDir.exists()) {
86+
Log.w(TAG, "Parent directory does not exist, attempting to create: " + parentDir);
87+
if (!parentDir.mkdirs()) {
88+
Log.e(TAG, "Failed to create parent directory: " + parentDir);
89+
return false;
90+
}
91+
}
92+
93+
boolean result = nativeEnableProfiling(outputPath);
94+
if (result) {
95+
Log.i(TAG, "Profiling enabled. Output path: " + outputPath);
96+
} else {
97+
Log.e(TAG, "Failed to enable profiling");
98+
}
99+
return result;
100+
}
101+
102+
/**
103+
* Disable profiling.
104+
*
105+
* <p>After calling this, subsequent model executions will not be profiled.
106+
* Any existing profiling data will be retained and can still be written.
107+
*/
108+
public static void disableProfiling() {
109+
nativeDisableProfiling();
110+
Log.i(TAG, "Profiling disabled");
111+
}
112+
113+
/**
114+
* Check if profiling is currently enabled.
115+
*
116+
* @return true if profiling is enabled, false otherwise
117+
*/
118+
public static boolean isProfilingEnabled() {
119+
return nativeIsProfilingEnabled();
120+
}
121+
122+
/**
123+
* Write accumulated ETDump data to the default output path.
124+
*
125+
* <p>The default output path is set when calling {@link #enableProfiling(String)}.
126+
* This method writes all profiling data collected since profiling was enabled.
127+
*
128+
* @return true if the data was successfully written, false otherwise
129+
*/
130+
public static boolean writeETDump() {
131+
boolean result = nativeWriteETDump();
132+
if (result) {
133+
Log.i(TAG, "ETDump data written successfully");
134+
} else {
135+
Log.e(TAG, "Failed to write ETDump data");
136+
}
137+
return result;
138+
}
139+
140+
/**
141+
* Write accumulated ETDump data to a specific path.
142+
*
143+
* <p>This allows writing profiling data to a different location than the default
144+
* path set in {@link #enableProfiling(String)}.
145+
*
146+
* @param outputPath The file path where ETDump data will be written
147+
* @return true if the data was successfully written, false otherwise
148+
*/
149+
public static boolean writeETDumpToPath(String outputPath) {
150+
if (outputPath == null || outputPath.isEmpty()) {
151+
Log.e(TAG, "Output path cannot be null or empty");
152+
return false;
153+
}
154+
155+
// Validate that parent directory exists
156+
File file = new File(outputPath);
157+
File parentDir = file.getParentFile();
158+
if (parentDir != null && !parentDir.exists()) {
159+
Log.w(TAG, "Parent directory does not exist, attempting to create: " + parentDir);
160+
if (!parentDir.mkdirs()) {
161+
Log.e(TAG, "Failed to create parent directory: " + parentDir);
162+
return false;
163+
}
164+
}
165+
166+
boolean result = nativeWriteETDumpToPath(outputPath);
167+
if (result) {
168+
Log.i(TAG, "ETDump data written to: " + outputPath);
169+
} else {
170+
Log.e(TAG, "Failed to write ETDump data to: " + outputPath);
171+
}
172+
return result;
173+
}
174+
175+
/**
176+
* Get the raw ETDump data as a byte array.
177+
*
178+
* <p>This is useful for custom handling of the profiling data, such as
179+
* uploading to a server or processing in-memory.
180+
*
181+
* @return byte array containing the ETDump data, or null if no data is available
182+
*/
183+
@Nullable
184+
public static byte[] getETDumpData() {
185+
byte[] data = nativeGetETDumpData();
186+
if (data == null) {
187+
Log.w(TAG, "No ETDump data available");
188+
} else {
189+
Log.i(TAG, "Retrieved " + data.length + " bytes of ETDump data");
190+
}
191+
return data;
192+
}
193+
194+
// Native methods
195+
private static native boolean nativeEnableProfiling(String outputPath);
196+
private static native void nativeDisableProfiling();
197+
private static native boolean nativeIsProfilingEnabled();
198+
private static native boolean nativeWriteETDump();
199+
private static native boolean nativeWriteETDumpToPath(String outputPath);
200+
private static native byte[] nativeGetETDumpData();
201+
}

0 commit comments

Comments
 (0)