Skip to content

Commit df36e27

Browse files
committed
implement debug-brk functionality support
1 parent e683d82 commit df36e27

File tree

6 files changed

+130
-43
lines changed

6 files changed

+130
-43
lines changed

build-artifacts/project-template-gradle/src/main/java/com/tns/RuntimeHelper.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import android.content.pm.PackageManager.NameNotFoundException;
77
import android.os.Build;
88
import android.util.Log;
9+
910
import java.io.IOException;
1011

1112
public final class RuntimeHelper {
@@ -28,7 +29,7 @@ private static boolean hasErrorIntent(Application app) {
2829
try {
2930
java.lang.Class ErrReport = java.lang.Class.forName("com.tns.ErrorReport");
3031
java.lang.reflect.Field field = ErrReport.getDeclaredField("ERROR_FILE_NAME");
31-
fileName = (String)field.get(null);
32+
fileName = (String) field.get(null);
3233
} catch (Exception e) {
3334
return false;
3435
}
@@ -97,7 +98,7 @@ public static Runtime initRuntime(Application app) {
9798
logger.write("Extracting snapshot blob");
9899
}
99100

100-
aE.extractAssets(app, "snapshots/" + Build.CPU_ABI, outputDir, extractPolicy);
101+
aE.extractAssets(app, "snapshots/" + Build.CPU_ABI, outputDir, extractPolicy);
101102
}
102103

103104
extractPolicy.setAssetsThumb(app);
@@ -131,17 +132,32 @@ public static Runtime initRuntime(Application app) {
131132
runtime = Runtime.initializeRuntimeWithConfiguration(config);
132133
if (isDebuggable) {
133134
try {
134-
v8Inspector = new AndroidJsV8Inspector(app, logger);
135+
v8Inspector = new AndroidJsV8Inspector(app.getFilesDir().getAbsolutePath(), app.getPackageName());
135136
v8Inspector.start();
136137

137138
// the following snippet is used as means to notify the VSCode extension
138139
// debugger that the debugger agent has started
139-
File debugBreakFile = new File("/data/local/tmp", app.getPackageName() + "-debugger-started");
140+
File debuggerStartedFile = new File("/data/local/tmp", app.getPackageName() + "-debugger-started");
141+
if (debuggerStartedFile.exists() && !debuggerStartedFile.isDirectory() && debuggerStartedFile.length() == 0) {
142+
java.io.FileWriter fileWriter = new java.io.FileWriter(debuggerStartedFile);
143+
fileWriter.write("started");
144+
fileWriter.close();
145+
}
146+
147+
// check if --debug-brk flag has been set. If positive:
148+
// write to the file to invalidate the flag
149+
// inform the v8Inspector to pause the main thread
150+
File debugBreakFile = new File("/data/local/tmp", app.getPackageName() + "-debugbreak");
151+
boolean shouldBreak = false;
140152
if (debugBreakFile.exists() && !debugBreakFile.isDirectory() && debugBreakFile.length() == 0) {
141153
java.io.FileWriter fileWriter = new java.io.FileWriter(debugBreakFile);
142154
fileWriter.write("started");
143155
fileWriter.close();
156+
157+
shouldBreak = true;
144158
}
159+
160+
v8Inspector.waitForDebugger(shouldBreak);
145161
} catch (IOException e) {
146162
e.printStackTrace();
147163
}

runtime/src/main/java/com/tns/AndroidJsV8Inspector.java

Lines changed: 81 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.List;
1919
import java.util.Queue;
2020
import java.util.concurrent.LinkedBlockingQueue;
21+
import java.util.concurrent.atomic.AtomicBoolean;
2122

2223
import fi.iki.elonen.NanoHTTPD;
2324
import fi.iki.elonen.NanoWSD;
@@ -26,23 +27,32 @@ class AndroidJsV8Inspector {
2627
private static boolean DEBUG_LOG_ENABLED = false;
2728

2829
private JsV8InspectorServer server;
29-
private Context context;
30-
private static String applicationDir;
30+
private static String ApplicationDir;
31+
private String packageName;
3132

3233
protected native final void init();
3334

3435
protected native final void connect(Object connection);
3536

37+
private native void scheduleBreak();
38+
3639
protected static native void disconnect();
3740

3841
protected native final void dispatchMessage(String message);
3942

4043
private Handler mainHandler;
44+
45+
private final Object debugBrkLock;
46+
47+
private static AtomicBoolean DebugInitialized = new AtomicBoolean(false);
48+
4149
private LinkedBlockingQueue<String> inspectorMessages = new LinkedBlockingQueue<String>();
50+
private LinkedBlockingQueue<String> pendingInspectorMessages = new LinkedBlockingQueue<String>();
4251

43-
AndroidJsV8Inspector(Context context, Logger logger) {
44-
this.context = context;
45-
applicationDir = context.getFilesDir().getAbsolutePath();
52+
AndroidJsV8Inspector(String filesDir, String packageName) {
53+
ApplicationDir = filesDir;
54+
this.packageName = packageName;
55+
this.debugBrkLock = new Object();
4656
}
4757

4858
public void start() throws IOException {
@@ -51,7 +61,7 @@ public void start() throws IOException {
5161

5262
mainHandler = currentRuntime.getHandler();
5363

54-
this.server = new JsV8InspectorServer(this.context.getPackageName() + "-inspectorServer");
64+
this.server = new JsV8InspectorServer(this.packageName + "-inspectorServer");
5565
this.server.start(-1);
5666

5767
if (DEBUG_LOG_ENABLED) {
@@ -82,9 +92,7 @@ private static void sendToDevToolsConsole(Object connection, String message, Str
8292
String sendingText = consoleMessage.toString();
8393
AndroidJsV8Inspector.send(connection, sendingText);
8494

85-
} catch (JSONException e) {
86-
e.printStackTrace();
87-
} catch (IOException e) {
95+
} catch (JSONException | IOException e) {
8896
e.printStackTrace();
8997
}
9098
}
@@ -102,11 +110,11 @@ private static String getInspectorMessage(Object connection) {
102110
@RuntimeCallable
103111
public static Pair<String, String>[] getPageResources() {
104112
// necessary to align the data dir returned by context (emulator) and that used by the v8 inspector
105-
if (applicationDir.startsWith("/data/user/0/")) {
106-
applicationDir = applicationDir.replaceFirst("/data/user/0/", "/data/data/");
113+
if (ApplicationDir.startsWith("/data/user/0/")) {
114+
ApplicationDir = ApplicationDir.replaceFirst("/data/user/0/", "/data/data/");
107115
}
108116

109-
String dataDir = applicationDir;
117+
String dataDir = ApplicationDir;
110118
File rootFilesDir = new File(dataDir, "app");
111119

112120

@@ -168,8 +176,41 @@ private static String getMimeType(String url) {
168176
return type;
169177
}
170178

171-
class JsV8InspectorServer extends NanoWSD {
172-
public JsV8InspectorServer(String name) {
179+
// pause the main thread for 30 seconds (30 * 1000 ms)
180+
// allowing the devtools frontend to establish connection with the inspector
181+
protected void waitForDebugger(boolean shouldBreak) {
182+
if (shouldBreak) {
183+
synchronized (this.debugBrkLock) {
184+
try {
185+
this.debugBrkLock.wait(1000 * 30);
186+
} catch (InterruptedException e) {
187+
e.printStackTrace();
188+
} finally {
189+
AndroidJsV8Inspector.DebugInitialized.getAndSet(true);
190+
this.processDebugBreak();
191+
}
192+
}
193+
} else {
194+
AndroidJsV8Inspector.DebugInitialized.getAndSet(true);
195+
}
196+
}
197+
198+
// process all messages coming front the frontend necessary to initialize the inspector backend
199+
// schedule a debug line break at first convenience
200+
private void processDebugBreak() {
201+
processDebugBreakMessages();
202+
scheduleBreak();
203+
}
204+
205+
private void processDebugBreakMessages() {
206+
while (!pendingInspectorMessages.isEmpty()) {
207+
String inspectorMessage = pendingInspectorMessages.poll();
208+
dispatchMessage(inspectorMessage);
209+
}
210+
}
211+
212+
private class JsV8InspectorServer extends NanoWSD {
213+
JsV8InspectorServer(String name) {
173214
super(name);
174215
}
175216

@@ -187,9 +228,8 @@ protected WebSocket openWebSocket(IHTTPSession handshake) {
187228
}
188229
}
189230

190-
class JsV8InspectorWebSocket extends NanoWSD.WebSocket {
191-
192-
public JsV8InspectorWebSocket(NanoHTTPD.IHTTPSession handshakeRequest) {
231+
private class JsV8InspectorWebSocket extends NanoWSD.WebSocket {
232+
JsV8InspectorWebSocket(NanoHTTPD.IHTTPSession handshakeRequest) {
193233
super(handshakeRequest);
194234
}
195235

@@ -199,16 +239,7 @@ protected void onOpen() {
199239
Log.d("V8Inspector", "onOpen: ThreadID: " + Thread.currentThread().getId());
200240
}
201241

202-
mainHandler.post(new Runnable() {
203-
@Override
204-
public void run() {
205-
if (DEBUG_LOG_ENABLED) {
206-
Log.d("V8Inspector", "Connecting. threadID : " + Thread.currentThread().getId());
207-
}
208-
209-
connect(JsV8InspectorWebSocket.this);
210-
}
211-
});
242+
connect(JsV8InspectorWebSocket.this);
212243
}
213244

214245
@Override
@@ -236,16 +267,30 @@ protected void onMessage(final NanoWSD.WebSocketFrame message) {
236267

237268
inspectorMessages.offer(message.getTextPayload());
238269

239-
mainHandler.post(new Runnable() {
240-
@Override
241-
public void run() {
242-
String nextMessage = inspectorMessages.poll();
243-
while (nextMessage != null) {
244-
dispatchMessage(nextMessage);
245-
nextMessage = inspectorMessages.poll();
270+
if (!AndroidJsV8Inspector.DebugInitialized.get()) {
271+
String nextMessage = inspectorMessages.poll();
272+
while (nextMessage != null) {
273+
pendingInspectorMessages.offer(nextMessage);
274+
nextMessage = inspectorMessages.poll();
275+
}
276+
277+
if (message.getTextPayload().contains("Debugger.enable")) {
278+
synchronized (debugBrkLock) {
279+
debugBrkLock.notify();
246280
}
247281
}
248-
});
282+
} else {
283+
mainHandler.postAtFrontOfQueue(new Runnable() {
284+
@Override
285+
public void run() {
286+
String nextMessage = inspectorMessages.poll();
287+
while (nextMessage != null) {
288+
dispatchMessage(nextMessage);
289+
nextMessage = inspectorMessages.poll();
290+
}
291+
}
292+
});
293+
}
249294
}
250295

251296
@Override
@@ -259,8 +304,7 @@ public void send(String payload) throws IOException {
259304

260305
public String getInspectorMessage() {
261306
try {
262-
String message = inspectorMessages.take();
263-
return message;
307+
return inspectorMessages.take();
264308
} catch (InterruptedException e) {
265309
e.printStackTrace();
266310
}

runtime/src/main/jni/JsV8InspectorClient.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ void JsV8InspectorClient::connect(jobject connection) {
4343
this->isConnected = true;
4444
}
4545

46+
void JsV8InspectorClient::scheduleBreak() {
47+
Isolate::Scope isolate_scope(isolate_);
48+
v8::HandleScope handleScope(isolate_);
49+
50+
this->session_->schedulePauseOnNextStatement(v8_inspector::StringView(), v8_inspector::StringView());
51+
}
52+
4653
void JsV8InspectorClient::createInspectorSession(v8::Isolate* isolate, const v8::Local<v8::Context>& context) {
4754
session_ = inspector_->connect(0, this, v8_inspector::StringView());
4855
}

runtime/src/main/jni/JsV8InspectorClient.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class JsV8InspectorClient : V8InspectorClient, v8_inspector::V8Inspector::Channe
2323

2424
void init();
2525
void connect(jobject connection);
26+
void scheduleBreak();
2627
void createInspectorSession(v8::Isolate* isolate, const v8::Local<v8::Context>& context);
2728
void disconnect();
2829
void dispatchMessage(const std::string& message);

runtime/src/main/jni/com_tns_AndroidJsV8Inspector.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ JNIEXPORT extern "C" void Java_com_tns_AndroidJsV8Inspector_connect(JNIEnv* env,
1515
JsV8InspectorClient::GetInstance()->connect(connection);
1616
}
1717

18+
JNIEXPORT extern "C" void Java_com_tns_AndroidJsV8Inspector_scheduleBreak(JNIEnv* env, jobject instance) {
19+
JsV8InspectorClient::GetInstance()->scheduleBreak();
20+
}
21+
1822
JNIEXPORT extern "C" void Java_com_tns_AndroidJsV8Inspector_disconnect(JNIEnv* env, jobject instance) {
1923
JsV8InspectorClient::GetInstance()->disconnect();
2024
}

test-app/app/src/main/java/com/tns/RuntimeHelper.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,32 @@ public static Runtime initRuntime(Application app) {
132132
runtime = Runtime.initializeRuntimeWithConfiguration(config);
133133
if (isDebuggable) {
134134
try {
135-
v8Inspector = new AndroidJsV8Inspector(app, logger);
135+
v8Inspector = new AndroidJsV8Inspector(app.getFilesDir().getAbsolutePath(), app.getPackageName());
136136
v8Inspector.start();
137137

138138
// the following snippet is used as means to notify the VSCode extension
139139
// debugger that the debugger agent has started
140-
File debugBreakFile = new File("/data/local/tmp", app.getPackageName() + "-debugger-started");
140+
File debuggerStartedFile = new File("/data/local/tmp", app.getPackageName() + "-debugger-started");
141+
if (debuggerStartedFile.exists() && !debuggerStartedFile.isDirectory() && debuggerStartedFile.length() == 0) {
142+
java.io.FileWriter fileWriter = new java.io.FileWriter(debuggerStartedFile);
143+
fileWriter.write("started");
144+
fileWriter.close();
145+
}
146+
147+
// check if --debug-brk flag has been set. If positive:
148+
// write to the file to invalidate the flag
149+
// inform the v8Inspector to pause the main thread
150+
File debugBreakFile = new File("/data/local/tmp", app.getPackageName() + "-debugbreak");
151+
boolean shouldBreak = false;
141152
if (debugBreakFile.exists() && !debugBreakFile.isDirectory() && debugBreakFile.length() == 0) {
142153
java.io.FileWriter fileWriter = new java.io.FileWriter(debugBreakFile);
143154
fileWriter.write("started");
144155
fileWriter.close();
156+
157+
shouldBreak = true;
145158
}
159+
160+
v8Inspector.waitForDebugger(shouldBreak);
146161
} catch (IOException e) {
147162
e.printStackTrace();
148163
}

0 commit comments

Comments
 (0)