• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# JSVM-API Debugging
2
3JavaScript virtual machine (JSVM) is a standard JavaScript (JS) code execution engine that strictly complies with the ECMAScript specification. For details, see [JSVM](../reference/common/_j_s_v_m.md).
4The JSVM-based code debugging and tuning capabilities include Debugger, CPU Profiler, Heap Snapshot, and Heap Statistics. The following APIs are involved:
5| API |  Description|
6|---|---|
7| OH_JSVM_GetVM  |  Obtains a VM instance.|
8| OH_JSVM_GetHeapStatistics  |  Obtains heap statistics of a VM.|
9| OH_JSVM_StartCpuProfiler  |  Creates and starts a CPU profiler instance.|
10| OH_JSVM_StopCpuProfiler  |  Stops the CPU profiler and outputs the result to a stream.|
11| OH_JSVM_TakeHeapSnapshot  |  Obtains a snapshot of the current heap and outputs it to a stream.|
12| OH_JSVM_OpenInspector  |  Opens an inspector instance on the specified host and port for debugging JS code.|
13| OH_JSVM_OpenInspectorWithName | Opens an inspector instance based on the PID and name.|
14| OH_JSVM_CloseInspector  |  Closes all remaining inspector connections.|
15| OH_JSVM_WaitForDebugger  |  Waits for the host to set up a socket connection with an inspector. After the connection is set up, the application continues to run. You can use **Runtime.runIfWaitingForDebugger** to run paused targets.|
16
17
18This topic describes how to use Debugger, CPU Profiler, and Heap Snapshot.
19
20## Using Debugger
21
22### Using OH_JSVM_OpenInspector
23
241. Configure the permission for accessing the Internet in the **module.json** file of the application project.
25
26```
27"requestPermissions": [{
28  "name": "ohos.permission.INTERNET",
29  "reason": "$string:app_name",
30  "usedScene": {
31    "abilities": [
32      "FromAbility"
33    ],
34    "when": "inuse"
35  }
36}]
37```
38
392. To prevent the pause during the debugging process from being falsely reported as no response, [enable the DevEco Studio debug mode](https://developer.huawei.com/consumer/en/doc/harmonyos-guides-V5/ide-debug-arkts-debug-V5) without setting breakpoints or run JSVM-API in threads except the main thread.
403. Call **OH_JSVM_OpenInspector** to open an inspector instance on the specified host and port. For example, call **OH_JSVM_OpenInspector(env, "localhost", 9225)** to create a socket on local port 9225 of the device.
414. Call **OH_JSVM_WaitForDebugger** to wait for the setup of a socket connection.
425. Check whether the port on the device is enabled successfully. For example, run **hdc shell "netstat -anp | grep 9225"**. If the status of port 9225 is **LISTEN**, the port is enabled.
436. Forward port. For example, run **hdc fport tcp:9229 tcp:9225** to forward port 9229 on a PC to port 9225 on a device. If **Forwardport result:OK** is returned, the port is forwarded successfully.
447. Enter **localhost:9229/json** in the address box of Google Chrome and press **Enter**. Obtain port connection information. Copy the URL in the **devtoolsFrontendUrl** field to the address box and press **Enter**. <br>On the DevTools source code page displayed, the JS source code executed by **OH_JSVM_RunScript** in the application is displayed. The Debugger pauses at the first line of the JS source code.<br> > **NOTE**<br>The URL specified by the **devtoolsFrontendUrl** field can be opened only on Google Chrome or Microsoft Edge. Mozilla Firefox and Apple Safari are not supported.
458. You can set breakpoints on the source code page, send debugging commands using the buttons to control JS code execution, and view variables.
469. Call **OH_JSVM_CloseInspector** to close the inspector instance and release the socket connection.
47
48#### Example
49If you are just starting out with JSVM-API, see [JSVM-API Development Process](use-jsvm-process.md). The following demonstrates only the C++ code involved.
50```cpp
51#include "ark_runtime/jsvm.h"
52
53#include <string>
54
55using namespace std;
56
57// JS source code to be debugged.
58static string srcDebugger = R"JS(
59const concat = (...args) => args.reduce((a, b) => a + b);
60var dialogue = concat('"What ', 'is ', 'your ', 'name ', '?"');
61dialogue = concat(dialogue, ' --', '"My ', 'name ', 'is ', 'Bob ', '."');
62)JS";
63
64// Enable Debugger.
65static void EnableInspector(JSVM_Env env) {
66    // Open an inspector instance on the specified host and port to create a socket.
67    OH_JSVM_OpenInspector(env, "localhost", 9225);
68    // Wait for the host to set up a socket connection with the inspector.
69    OH_JSVM_WaitForDebugger(env, true);
70}
71
72// Close Debugger.
73static void CloseInspector(JSVM_Env env) {
74    // Close the inspector to release the socket connection.
75    OH_JSVM_CloseInspector(env);
76}
77
78static void RunScript(JSVM_Env env) {
79    JSVM_HandleScope handleScope;
80    OH_JSVM_OpenHandleScope(env, &handleScope);
81
82    JSVM_Value jsSrc;
83    OH_JSVM_CreateStringUtf8(env, srcDebugger.c_str(), srcDebugger.size(), &jsSrc);
84
85    JSVM_Script script;
86    OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script);
87
88    JSVM_Value result;
89    OH_JSVM_RunScript(env, script, &result);
90
91    OH_JSVM_CloseHandleScope(env, handleScope);
92}
93
94void TestJSVM() {
95    JSVM_InitOptions initOptions{};
96    OH_JSVM_Init(&initOptions);
97
98    JSVM_VM vm;
99    OH_JSVM_CreateVM(nullptr, &vm);
100    JSVM_VMScope vmScope;
101    OH_JSVM_OpenVMScope(vm, &vmScope);
102
103    JSVM_Env env;
104    OH_JSVM_CreateEnv(vm, 0, nullptr, &env);
105    // Enable Debugger before executing the JS code.
106    EnableInspector(env);
107    JSVM_EnvScope envScope;
108    OH_JSVM_OpenEnvScope(env, &envScope);
109
110    // Execute the JS code.
111    RunScript(env);
112
113    OH_JSVM_CloseEnvScope(env, envScope);
114    // Close Debugger after the JS code is executed.
115    CloseInspector(env);
116    OH_JSVM_DestroyEnv(env);
117    OH_JSVM_CloseVMScope(vm, vmScope);
118    OH_JSVM_DestroyVM(vm);
119}
120
121```
122
123### Using OH_JSVM_OpenInspectorWithName
124
1251. Configure the permission for accessing the Internet in the **module.json** file of the application project.
126
127```
128"requestPermissions": [{
129  "name": "ohos.permission.INTERNET",
130  "reason": "$string:app_name",
131  "usedScene": {
132    "abilities": [
133      "FromAbility"
134    ],
135    "when": "inuse"
136  }
137}]
138```
139
1402. To prevent the pause during the debugging process from being falsely reported as no response, [enable the DevEco Studio debug mode](https://developer.huawei.com/consumer/en/doc/harmonyos-guides-V5/ide-debug-arkts-debug-V5) without setting breakpoints or run JSVM-API in threads except the main thread.
1413. Enable the inspector port and connect to devtools for debugging.<br>Before executing the JS code, call **OH_JSVM_OpenInspector** to open an inspector instance on the specified host and port and create a socket. For example, call **OH_JSVM_OpenInspectorWithName (env, 123, "test")** to create a TCP socket and the corresponding unixdomain port.
1424. Call **OH_JSVM_WaitForDebugger** to wait for the setup of a socket connection.
1435. Check whether the port on the device is enabled successfully. <br>Run **hdc shell "cat /proc/net/unix | grep jsvm"**. An available Unix port is displayed, for example, **jsvm_devtools_remote_9229_123**, where **9229** is the TCP port number and **123** is the PID.
1446. Forward port. <br>For example, run **hdc fport tcp:9229 tcp:9229** to forward port 9229 on a PC to port 9229 on a device. If **Forwardport result:OK** is returned, the port is forwarded successfully.
1457. Enter **localhost:9229/json** in the address box of Google Chrome and press **Enter**. Obtain port connection information. Open Google Chrome DevTools, copy the URL in the **devtoolsFrontendUrl** field to the address box, and press **Enter**. <br>On the DevTools source code page displayed, the JS source code executed by **OH_JSVM_RunScript** is displayed. The Debugger pauses at the first line of the JS code.<br> > **NOTE**<br>The URL specified by the **devtoolsFrontendUrl** field can be opened only on Google Chrome or Microsoft Edge. Mozilla Firefox and Apple Safari are not supported.
1468. You can set breakpoints on the source code page, send debugging commands using the buttons to control JS code execution, and view variables.
1479. Call **OH_JSVM_CloseInspector** to close the inspector instance and release the socket connection.
148
149#### Example
150
151Replace the "//Enable Debugger" section with the following:
152```cpp
153// Enable Debugger.
154static void EnableInspector(JSVM_Env env) {
155    // Open an inspector instance on the specified host and port to create a socket.
156    OH_JSVM_OpenInspectorWithName(env, 123, "test");
157    // Wait for the host to set up a socket connection with the inspector.
158    OH_JSVM_WaitForDebugger(env, true);
159}
160```
161
162### Using Chrome Inspect
163In addition to opening the URL specified by the **devtoolsFrontendUrl** field for debugging, you can also open the **chrome://inspect/#devices** page on Google Chrome to perform debugging. The procedure is as follows:
1641. On Google Chrome, enter **chrome://inspect/#devices** in the address box and press **Enter**. On the page displayed, select the following options:
165   <div align=left><img src="figures/jsvm-debugger-cpuprofiler-heapsnapshot_1.png"/></div>
1662. Run **hdc fport** [*Port number on the developer's PC*] [*Port number on a device*] to forward a port.
167    Example: **hdc fport tcp:9227 tcp:9226**
1683. Click **Port forwarding**, enter the port number on the PC in the left text box and the port number on the device in the right text box, and click **Done**, as shown in the figure below.
169   <div align=left><img src="figures/jsvm-debugger-cpuprofiler-heapsnapshot_2.png"/></div>
1704. Click **Configure**, enter the port number on the PC, for example, **localhost:9227**, and click **Done**, as shown in the figure below.
171   <div align=left><img src="figures/jsvm-debugger-cpuprofiler-heapsnapshot_3.png"/></div>
1725. Wait until the debugging content is displayed under **Target**, and click **inspect** to start debugging, as shown in the figure below.
173   <div align=left><img src="figures/jsvm-debugger-cpuprofiler-heapsnapshot_4.png"/></div>
174
175### Using WebSocket Port
176In addition to opening the URL specified by the **devtoolsFrontendUrl** field through Chrome DevTools, if you know how to use Chrome DevTools Protocol (CDP), you can also connect to the WebSocket port provided by Inspector for debugging.
177
178The procedure is as follows: After port mapping (for example, mapping to port 9229) is complete, enter **localhost:9229/json** in the address box of Chrome and press **Enter** to obtain the URL of the **webSocketDebuggerUrl** field. Then, use the standard WebSocket client to connect to the URL to send CDP for debugging. Note that the WebSocket port provided by Inspector of the current version can receive only text frames, ping frames, and connection close frames. All other types of frames are considered as error frames, which will interrupt the WebSocket connection.
179
180For details about CDP, see [the official document](https://chromedevtools.github.io/devtools-protocol/).
181
182## Using CPU Profiler and Heap Snapshot
183
184### Using CPU Profiler APIs
185
1861. Before executing the JS code, call **OH_JSVM_StartCpuProfiler** to start sampling and return a **JSVM_CpuProfiler** instance.
1872. Run the JS code and call **OH_JSVM_StopCpuProfiler**, in which you need to pass in the **JSVM_CpuProfiler** instance (obtained in step 1), callback for the output stream, and pointer to the output stream. Then, the data will be written to the specified output stream.
1883. Obtain the output data in JSON strings. You can also save it to the **.cpuprofile** file, which can be parsed into profiling views with the Chrome DevTools-JavaScript Profiler.
189
190### Using Heap Snapshot APIs
191
1921. To analyze the heap object creation of a piece of JS code, call **OH_JSVM_TakeHeapSnapshot** before and after the JS code is executed. You need to pass in the callback used to return the output stream and the pointer to the output stream. Then, the data will be written to the specified output stream.
1932. Save the output data to the **.heapsnapshot** file, which can be parsed into memory analysis views with the Chrome DevTools-Memory.
194
195### Example
196If you are just starting out with JSVM-API, see [JSVM-API Development Process](use-jsvm-process.md). The following demonstrates only the C++ code involved.
197
198```cpp
199#include "ark_runtime/jsvm.h"
200
201#include <fstream>
202#include <iostream>
203
204using namespace std;
205
206// JS code to be optimized.
207static string srcProf = R"JS(
208function sleep(delay) {
209    var start = (new Date()).getTime();
210    while ((new Date()).getTime() - start < delay) {
211        continue;
212    }
213}
214
215function work3() {
216    sleep(300);
217}
218
219function work2() {
220    work3();
221    sleep(200);
222}
223
224function work1() {
225    work2();
226    sleep(100);
227}
228
229work1();
230)JS";
231
232// Callback for the data output stream, which is customized to process the returned data. In this example, the output data is written to a file.
233static bool OutputStream(const char *data, int size, void *streamData) {
234    auto &os = *reinterpret_cast<ofstream *>(streamData);
235    if (data) {
236        os.write(data, size);
237    } else {
238        os.close();
239    }
240    return true;
241}
242
243static JSVM_CpuProfiler ProfilingBegin(JSVM_VM vm) {
244    // Specify the path of the file saving the output profiling data. In this example, the sandbox path is /data/storage/el2/base/files, and the bundle name is com.example.helloworld.
245    // The output data will be saved to /data/app/el2/100/base/com.example.helloworld/files/heap-snapshot-begin.heapsnapshot.
246    ofstream heapSnapshot("/data/storage/el2/base/files/heap-snapshot-begin.heapsnapshot",
247                          ios::out | ios:: binary | ios::trunc);
248    // Task a heap snapshot before the JS code is executed.
249    OH_JSVM_TakeHeapSnapshot(vm, OutputStream, &heapSnapshot);
250    JSVM_CpuProfiler cpuProfiler;
251    // Start the CPU Profiler.
252    OH_JSVM_StartCpuProfiler(vm, &cpuProfiler);
253    return cpuProfiler;
254}
255
256// Stop the profiling data collection tool.
257static void ProfilingEnd(JSVM_VM vm, JSVM_CpuProfiler cpuProfiler) {
258    // Specify the path of the file saving the output profiling data. In this example, the sandbox path is /data/storage/el2/base/files, and the bundle name is com.example.helloworld.
259    // The output data will be saved to /data/app/el2/100/base/com.example.helloworld/files/cpu-profile.cpuprofile.
260    ofstream cpuProfile("/data/storage/el2/base/files/cpu-profile.cpuprofile",
261                        ios::out | ios:: binary | ios::trunc);
262    // Stop the CPU Profiler to obtain data.
263    OH_JSVM_StopCpuProfiler(vm, cpuProfiler, OutputStream, &cpuProfile);
264    ofstream heapSnapshot("/data/storage/el2/base/files/heap-snapshot-end.heapsnapshot",
265                              ios::out | ios:: binary | ios::trunc);
266    // After the JS is executed, take a heap snapshot again and compare the two snapshots for further analysis.
267    OH_JSVM_TakeHeapSnapshot(vm, OutputStream, &heapSnapshot);
268}
269
270static JSVM_Value RunScriptWithStatistics(JSVM_Env env, JSVM_CallbackInfo info) {
271    JSVM_VM vm;
272    OH_JSVM_GetVM(env, &vm);
273
274    // Start profiling.
275    auto cpuProfiler = ProfilingBegin(vm);
276
277    JSVM_HandleScope handleScope;
278    OH_JSVM_OpenHandleScope(env, &handleScope);
279
280    JSVM_Value jsSrc;
281    OH_JSVM_CreateStringUtf8(env, srcProf.c_str(), srcProf.size(), &jsSrc);
282
283    JSVM_Script script;
284    OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script);
285
286    JSVM_Value result;
287    // Execute the JS code.
288    OH_JSVM_RunScript(env, script, &result);
289
290    OH_JSVM_CloseHandleScope(env, handleScope);
291
292    // End profiling.
293    ProfilingEnd(vm, cpuProfiler);
294    return nullptr;
295}
296static JSVM_CallbackStruct param[] = {
297    {.data = nullptr, .callback = RunScriptWithStatistics},
298};
299static JSVM_CallbackStruct *method = param;
300// Alias for the runScriptWithStatistics method to be called from JS.
301static JSVM_PropertyDescriptor descriptor[] = {
302    {"runScriptWithStatistics", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
303};
304```
305// Call C++ code from JS.
306```cpp
307const char *srcCallNative = R"JS(runScriptWithStatistics();)JS";
308```
309Expected result:
310```
311Two files are generated in the Harmony device for subsequent optimization.
312heap-snapshot-end.heapsnapshot,
313cpu-profile.cpuprofile
314For file features, see API usage.
315```
316