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. For example, call **OH_JSVM_OpenInspector(env, "localhost", 9225)** to create a socket on local port 9225 of the device. 42 434. Call **OH_JSVM_WaitForDebugger** to wait for the setup of a socket connection. 44 455. Check whether the port on the device is enabled successfully. 46 47 For example, run **hdc shell "netstat -anp | grep 9225"**. If the status of port 9225 is **LISTEN**, the port is enabled. 48 496. Forward port. 50 51 For example, run **hdc fport tcp:9229 tcp:9225** to forward PC port 9229 to device port 9225. If the command output is **Forwardport result:OK**, the port is forwarded successfully. 52 537. Enter **localhost:9229/json** in the address box of the Chrome browser 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. 54 558. You can set breakpoints on the source code page, send debugging commands using the buttons to control JS code execution, and view variables. 56 579. Call **OH_JSVM_CloseInspector** to close the inspector instance and release the socket connection. 58 59#### Example 60If 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. 61```cpp 62#include "ark_runtime/jsvm.h" 63 64#include <string> 65 66using namespace std; 67 68// JS source code to be debugged. 69static string srcDebugger = R"JS( 70const concat = (...args) => args.reduce((a, b) => a + b); 71var dialogue = concat('"What ', 'is ', 'your ', 'name ', '?"'); 72dialogue = concat(dialogue, ' --', '"My ', 'name ', 'is ', 'Bob ', '."'); 73)JS"; 74 75// Enable the debugger. 76static void EnableInspector(JSVM_Env env) { 77 // Open an inspector instance on the specified host and port to create a socket. 78 OH_JSVM_OpenInspector(env, "localhost", 9225); 79 // Wait for the host to set up a socket connection with the inspector. 80 OH_JSVM_WaitForDebugger(env, true); 81} 82 83// Close the Debugger. 84static void CloseInspector(JSVM_Env env) { 85 // Close the inspector to release the socket connection. 86 OH_JSVM_CloseInspector(env); 87} 88 89static void RunScript(JSVM_Env env) { 90 JSVM_HandleScope handleScope; 91 OH_JSVM_OpenHandleScope(env, &handleScope); 92 93 JSVM_Value jsSrc; 94 OH_JSVM_CreateStringUtf8(env, srcDebugger.c_str(), srcDebugger.size(), &jsSrc); 95 96 JSVM_Script script; 97 OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script); 98 99 JSVM_Value result; 100 OH_JSVM_RunScript(env, script, &result); 101 102 OH_JSVM_CloseHandleScope(env, handleScope); 103} 104 105void TestJSVM() { 106 JSVM_InitOptions initOptions{}; 107 OH_JSVM_Init(&initOptions); 108 109 JSVM_VM vm; 110 OH_JSVM_CreateVM(nullptr, &vm); 111 JSVM_VMScope vmScope; 112 OH_JSVM_OpenVMScope(vm, &vmScope); 113 114 JSVM_Env env; 115 OH_JSVM_CreateEnv(vm, 0, nullptr, &env); 116 // Enable the Debugger before executing the JS code. 117 EnableInspector(env); 118 JSVM_EnvScope envScope; 119 OH_JSVM_OpenEnvScope(env, &envScope); 120 121 // Execute the JS code. 122 RunScript(env); 123 124 OH_JSVM_CloseEnvScope(env, envScope); 125 // Close the Debugger after the JS code is executed. 126 CloseInspector(env); 127 OH_JSVM_DestroyEnv(env); 128 OH_JSVM_CloseVMScope(vm, vmScope); 129 OH_JSVM_DestroyVM(vm); 130} 131 132``` 133 134### Using OH_JSVM_OpenInspectorWithName 135 1361. Configure the permission for accessing the Internet in the **module.json** file of the application project. 137 138``` 139"requestPermissions": [{ 140 "name": "ohos.permission.INTERNET", 141 "reason": "$string:app_name", 142 "usedScene": { 143 "abilities": [ 144 "FromAbility" 145 ], 146 "when": "inuse" 147 } 148}] 149``` 150 1512. 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. 152 1533. 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. 154 1554. Call **OH_JSVM_WaitForDebugger** to wait for the setup of a socket connection. 156 1575. Check whether the port on the device is enabled successfully. <br>Run **hdc shell "cat /proc/net/unix | grep jsvm"**. 158 159 The Unix port is displayed, for example, **jsvm_devtools_remote_9229_123**, where **9229** is the TCP port number and **123** is the PID. 160 1616. Forward port. <br>Run **hdc fport tcp:9229 tcp:9229**. In this example, PC port 9229 is forwarded to device port 9229. <br>If the command output is **Forwardport result:OK**, the port is forwarded successfully. 162 1637. Enter **localhost:9229/json** in the address box of the Google Chrome browser and press **Enter**. Obtain port connection information. Open the Chrome developer tool, 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 source code. 164 1658. You can set breakpoints on the source code page, send debugging commands using the buttons to control JS code execution, and view variables. 166 1679. Call **OH_JSVM_CloseInspector** to close the inspector instance and release the socket connection. 168 169#### Example 170 171Replace the "//Enable the debugger" section with the following: 172```cpp 173// Enable the debugger. 174static void EnableInspector(JSVM_Env env) { 175 // Open an inspector instance on the specified host and port to create a socket. 176 OH_JSVM_OpenInspectorWithName(env, 123, "test"); 177 // Wait for the host to set up a socket connection with the inspector. 178 OH_JSVM_WaitForDebugger(env, true); 179} 180``` 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 profiling 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// Set a property descriptor named runScriptWithStatistics and associate it with a callback. This allows the runScriptWithStatistics callback to be called from JS. 301static JSVM_PropertyDescriptor descriptor[] = { 302 {"runScriptWithStatistics", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 303}; 304``` 305 306 307// Call C++ code from JS. 308```cpp 309const char *srcCallNative = R"JS(runScriptWithStatistics();)JS"; 310``` 311