• 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.
40
413. Call **OH_JSVM_OpenInspector** to open an inspector instance on the specified host and port.
42
43   For example, call **OH_JSVM_OpenInspector(env, "localhost", 9225)** to create a socket on local port 9225 of the device.
44
454. Call **OH_JSVM_WaitForDebugger** to wait for the setup of a socket connection.
46
475. Check whether the port on the device is enabled successfully.
48
49   For example, run **hdc shell "netstat -anp | grep 9225"**. If the status of port 9225 is **LISTEN**, the port is enabled.
50
516. 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.
52
537. 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**.
54
55   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.
56
57   > **NOTE**
58   >
59   > 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.
60
618. You can set breakpoints on the source code page, send debugging commands using the buttons to control JS code execution, and view variables.
62
639. Call **OH_JSVM_CloseInspector** to close the inspector instance and release the socket connection.
64
65#### Example
66If 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.
67```cpp
68#include "ark_runtime/jsvm.h"
69
70#include <string>
71
72using namespace std;
73
74// JS source code to be debugged.
75static string srcDebugger = R"JS(
76const concat = (...args) => args.reduce((a, b) => a + b);
77var dialogue = concat('"What ', 'is ', 'your ', 'name ', '?"');
78dialogue = concat(dialogue, ' --', '"My ', 'name ', 'is ', 'Bob ', '."');
79)JS";
80
81// Enable Debugger.
82static void EnableInspector(JSVM_Env env) {
83    // Open an inspector instance on the specified host and port to create a socket.
84    OH_JSVM_OpenInspector(env, "localhost", 9225);
85    // Wait for the host to set up a socket connection with the inspector.
86    OH_JSVM_WaitForDebugger(env, true);
87}
88
89// Close Debugger.
90static void CloseInspector(JSVM_Env env) {
91    // Close the inspector to release the socket connection.
92    OH_JSVM_CloseInspector(env);
93}
94
95static void RunScript(JSVM_Env env) {
96    JSVM_HandleScope handleScope;
97    OH_JSVM_OpenHandleScope(env, &handleScope);
98
99    JSVM_Value jsSrc;
100    OH_JSVM_CreateStringUtf8(env, srcDebugger.c_str(), srcDebugger.size(), &jsSrc);
101
102    JSVM_Script script;
103    OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script);
104
105    JSVM_Value result;
106    OH_JSVM_RunScript(env, script, &result);
107
108    OH_JSVM_CloseHandleScope(env, handleScope);
109}
110
111void TestJSVM() {
112    JSVM_InitOptions initOptions{};
113    OH_JSVM_Init(&initOptions);
114
115    JSVM_VM vm;
116    OH_JSVM_CreateVM(nullptr, &vm);
117    JSVM_VMScope vmScope;
118    OH_JSVM_OpenVMScope(vm, &vmScope);
119
120    JSVM_Env env;
121    OH_JSVM_CreateEnv(vm, 0, nullptr, &env);
122    // Enable Debugger before executing the JS code.
123    EnableInspector(env);
124    JSVM_EnvScope envScope;
125    OH_JSVM_OpenEnvScope(env, &envScope);
126
127    // Execute the JS code.
128    RunScript(env);
129
130    OH_JSVM_CloseEnvScope(env, envScope);
131    // Close Debugger after the JS code is executed.
132    CloseInspector(env);
133    OH_JSVM_DestroyEnv(env);
134    OH_JSVM_CloseVMScope(vm, vmScope);
135    OH_JSVM_DestroyVM(vm);
136}
137
138```
139
140### Using OH_JSVM_OpenInspectorWithName
141
1421. Configure the permission for accessing the Internet in the **module.json** file of the application project.
143
144   ```
145   "requestPermissions": [{
146     "name": "ohos.permission.INTERNET",
147     "reason": "$string:app_name",
148     "usedScene": {
149       "abilities": [
150         "FromAbility"
151       ],
152       "when": "inuse"
153     }
154   }]
155   ```
156
1572. 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.
158
1593. Enable the inspector port and connect to devtools for debugging.
160
161   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.
162
1634. Call **OH_JSVM_WaitForDebugger** to wait for the setup of a socket connection.
164
1655. 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.
166
1676. Forward port. 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.
168
1697. 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.
170
171   > **NOTE**
172   >
173   > 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.
174
1758. You can set breakpoints on the source code page, send debugging commands using the buttons to control JS code execution, and view variables.
176
1779. Call **OH_JSVM_CloseInspector** to close the inspector instance and release the socket connection.
178
179#### Example
180
181Replace the "//Enable Debugger" section in the previous sample code with the following:
182```cpp
183// Enable Debugger.
184static void EnableInspector(JSVM_Env env) {
185    // Open an inspector instance on the specified host and port to create a socket.
186    OH_JSVM_OpenInspectorWithName(env, 123, "test");
187    // Wait for the host to set up a socket connection with the inspector.
188    OH_JSVM_WaitForDebugger(env, true);
189}
190```
191
192### Using Chrome inspect
193In 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:
1941. On Google Chrome, enter **chrome://inspect/#devices** in the address box and press **Enter**. On the page displayed, select the following options:
195
196   <div align=left><img src="figures/jsvm-debugger-cpuprofiler-heapsnapshot_1.png"/></div>
197
1982. Run **hdc fport** [*Port number on the developer's PC*] [*Port number on a device*] to forward a port.
199
200    Example: **hdc fport tcp:9227 tcp:9226**
201
2023. 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.
203
204   <div align=left><img src="figures/jsvm-debugger-cpuprofiler-heapsnapshot_2.png"/></div>
205
2064. Click **Configure**, enter the port number on the PC, for example, **localhost:9227**, and click **Done**, as shown in the figure below.
207
208   <div align=left><img src="figures/jsvm-debugger-cpuprofiler-heapsnapshot_3.png"/></div>
209
2105. Wait until the debugging content is displayed under **Target**, and click **inspect** to start debugging, as shown in the figure below.
211
212   <div align=left><img src="figures/jsvm-debugger-cpuprofiler-heapsnapshot_4.png"/></div>
213
214## Using CPU Profiler and Heap Snapshot
215
216### Using CPU Profiler APIs
217
2181. Before executing the JS code, call **OH_JSVM_StartCpuProfiler** to start sampling and return a **JSVM_CpuProfiler** instance.
2192. 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.
2203. 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.
221
222### Using Heap Snapshot APIs
223
2241. 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.
2252. Save the output data to the **.heapsnapshot** file, which can be parsed into memory analysis views with the Chrome DevTools-Memory.
226
227### Example
228If 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.
229
230```cpp
231#include "ark_runtime/jsvm.h"
232
233#include <fstream>
234#include <iostream>
235
236using namespace std;
237
238// JS code to be optimized.
239static string srcProf = R"JS(
240function sleep(delay) {
241    var start = (new Date()).getTime();
242    while ((new Date()).getTime() - start < delay) {
243        continue;
244    }
245}
246
247function work3() {
248    sleep(300);
249}
250
251function work2() {
252    work3();
253    sleep(200);
254}
255
256function work1() {
257    work2();
258    sleep(100);
259}
260
261work1();
262)JS";
263
264// 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.
265static bool OutputStream(const char *data, int size, void *streamData) {
266    auto &os = *reinterpret_cast<ofstream *>(streamData);
267    if (data) {
268        os.write(data, size);
269    } else {
270        os.close();
271    }
272    return true;
273}
274
275static JSVM_CpuProfiler ProfilingBegin(JSVM_VM vm) {
276    // 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.
277    // The output data will be saved to /data/app/el2/100/base/com.example.helloworld/files/heap-snapshot-begin.heapsnapshot.
278    ofstream heapSnapshot("/data/storage/el2/base/files/heap-snapshot-begin.heapsnapshot",
279                          ios::out | ios:: binary | ios::trunc);
280    // Task a heap snapshot before the JS code is executed.
281    OH_JSVM_TakeHeapSnapshot(vm, OutputStream, &heapSnapshot);
282    JSVM_CpuProfiler cpuProfiler;
283    // Start the CPU Profiler.
284    OH_JSVM_StartCpuProfiler(vm, &cpuProfiler);
285    return cpuProfiler;
286}
287
288// Stop the profiling data collection tool.
289static void ProfilingEnd(JSVM_VM vm, JSVM_CpuProfiler cpuProfiler) {
290    // 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.
291    // The output data will be saved to /data/app/el2/100/base/com.example.helloworld/files/cpu-profile.cpuprofile.
292    ofstream cpuProfile("/data/storage/el2/base/files/cpu-profile.cpuprofile",
293                        ios::out | ios:: binary | ios::trunc);
294    // Stop the CPU Profiler to obtain data.
295    OH_JSVM_StopCpuProfiler(vm, cpuProfiler, OutputStream, &cpuProfile);
296    ofstream heapSnapshot("/data/storage/el2/base/files/heap-snapshot-end.heapsnapshot",
297                              ios::out | ios:: binary | ios::trunc);
298    // After the JS is executed, take a heap snapshot again and compare the two snapshots for further analysis.
299    OH_JSVM_TakeHeapSnapshot(vm, OutputStream, &heapSnapshot);
300}
301
302static JSVM_Value RunScriptWithStatistics(JSVM_Env env, JSVM_CallbackInfo info) {
303    JSVM_VM vm;
304    OH_JSVM_GetVM(env, &vm);
305
306    // Start profiling.
307    auto cpuProfiler = ProfilingBegin(vm);
308
309    JSVM_HandleScope handleScope;
310    OH_JSVM_OpenHandleScope(env, &handleScope);
311
312    JSVM_Value jsSrc;
313    OH_JSVM_CreateStringUtf8(env, srcProf.c_str(), srcProf.size(), &jsSrc);
314
315    JSVM_Script script;
316    OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script);
317
318    JSVM_Value result;
319    // Execute the JS code.
320    OH_JSVM_RunScript(env, script, &result);
321
322    OH_JSVM_CloseHandleScope(env, handleScope);
323
324    // End profiling.
325    ProfilingEnd(vm, cpuProfiler);
326    return nullptr;
327}
328static JSVM_CallbackStruct param[] = {
329    {.data = nullptr, .callback = RunScriptWithStatistics},
330};
331static JSVM_CallbackStruct *method = param;
332// Alias for the runScriptWithStatistics method to be called from JS.
333static JSVM_PropertyDescriptor descriptor[] = {
334    {"runScriptWithStatistics", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
335};
336// Call C++ code from JS.
337const char *srcCallNative = R"JS(runScriptWithStatistics();)JS";
338```
339
340