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