1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #ifdef TEST_TRACE
16 #include <fstream>
17 #include <iostream>
18
19 #if defined(OHOS_JSVMTEST_XTS)
20 #define USE_HEADER_WASM
21 #endif
22
23 #include "jsvmtest.h"
24
25 #if defined(USE_HEADER_WASM)
26 #include <unordered_map>
27 namespace {
28 #include "add.wasm.def"
29
30 static const std::string wasmPath = "";
31
32 using WasmMap = std::unordered_map<std::string, std::pair<unsigned char *, unsigned int>>;
InitWasmMap()33 const WasmMap &InitWasmMap()
34 {
35 static WasmMap wasmMap{};
36 if (wasmMap.size() == 0) {
37 wasmMap["add.wasm"] = std::pair<unsigned char *, unsigned int>(add_wasm, add_wasm_len);
38 }
39 return wasmMap;
40 }
41 }
42 #elif defined(OHOS_JSVM_HAP)
43 static const std::string wasmPath = "/data/storage/el1/base/wasm/";
44 #else
45 static const std::string wasmPath = "./unittests/wasm/";
46 #endif
47
IsWasmModuleObject(JSVM_Value value)48 static bool IsWasmModuleObject(JSVM_Value value)
49 {
50 bool result;
51 JSVM_CALL(OH_JSVM_IsWasmModuleObject(jsvm_env, value, &result));
52 return result;
53 }
54
IsWebAssemblyInstance(JSVM_Value value)55 static bool IsWebAssemblyInstance(JSVM_Value value)
56 {
57 auto Instance = jsvm::GetProperty(jsvm::Global("WebAssembly"), "Instance");
58 return jsvm::InstanceOf(value, Instance);
59 }
60
ReadBinaryFile(const char * path,std::vector<uint8_t> & buffer)61 static void ReadBinaryFile(const char *path, std::vector<uint8_t> &buffer)
62 {
63 #if !defined(USE_HEADER_WASM)
64 std::ifstream infile(path, std::ifstream::binary);
65 CHECK_FATAL(infile.good(), "can not access file: %s\n", path);
66 infile.seekg(0, std::ifstream::end);
67 size_t size = infile.tellg();
68 infile.seekg(0);
69 buffer.resize(size);
70 infile.read(reinterpret_cast<char *>(&buffer[0]), size);
71 infile.close();
72 #else
73 const auto &wasmMap = InitWasmMap();
74 auto it = wasmMap.find(path);
75 if (it == wasmMap.end()) {
76 CHECK_FATAL(false, "can not find file: %s", path);
77 }
78 auto p = it->second;
79 std::vector<uint8_t> newBuffer((uint8_t *)p.first, p.first + p.second);
80 buffer.swap(newBuffer);
81 #endif
82 }
83
ReadBinaryFile(const std::string & path,std::vector<uint8_t> & buffer)84 static void ReadBinaryFile(const std::string &path, std::vector<uint8_t> &buffer)
85 {
86 ReadBinaryFile(path.c_str(), buffer);
87 }
88
InstantiateWasmModule(JSVM_Value wasmModule,JSVM_Value importedObject)89 static JSVM_Value InstantiateWasmModule(JSVM_Value wasmModule, JSVM_Value importedObject)
90 {
91 auto globalThis = jsvm::Global();
92 auto WebAssembly = jsvm::GetProperty(globalThis, "WebAssembly");
93 CHECK(jsvm::IsObject(WebAssembly));
94 auto WebAssemblyInstance = jsvm::GetProperty(WebAssembly, "Instance");
95 CHECK(jsvm::IsFunction(WebAssemblyInstance));
96 JSVM_Value wasmInstance;
97 JSVM_Value argv[] = {wasmModule, importedObject};
98 size_t args = 2;
99 JSVM_CALL(OH_JSVM_NewInstance(jsvm_env, WebAssemblyInstance, args, argv, &wasmInstance));
100 CHECK(IsWebAssemblyInstance(wasmInstance));
101 return wasmInstance;
102 }
103
OutputStream(const char * data,int size,void * streamData)104 bool OutputStream(const char *data, int size, void *streamData)
105 {
106 std::string value(data, size);
107 *(std::string *)streamData = std::string(data, size);
108 return true;
109 }
110
TestCategory(JSVM_TraceCategory category,void (* func)())111 void TestCategory(JSVM_TraceCategory category, void (*func)())
112 {
113 JSVM_Status status;
114 std::string data;
115 {
116 status = OH_JSVM_TraceStart(1, &category, "trace", 0);
117 CHECK(status == JSVM_OK);
118
119 func();
120 status = OH_JSVM_TraceStop(OutputStream, (void *)&data);
121 CHECK(status == JSVM_OK);
122
123 JSVM_Value obj;
124 JSVM_CALL(OH_JSVM_JsonParse(jsvm_env, jsvm::Str(data), &obj));
125 CHECK(jsvm::IsObject(obj));
126 JSVM_Value trace = jsvm::GetProperty(obj, "trace");
127 CHECK(jsvm::IsArray(trace));
128 CHECK(jsvm::ArrayLength(trace) != 0);
129 }
130 }
131
132 #define TEST_TRACE_CATEGROY(category, func) \
133 TEST(Trace_##category) \
134 { \
135 TestCategory(category, func); \
136 }
137
RunCycle()138 void RunCycle()
139 {
140 const char *cycle = R"JS(
141 function map(x, y) {
142 return {"a": x, "b": y};
143 }
144 for (var i = 0; i < 80000; ++i) {
145 var x = map(i, i);
146 }
147 )JS";
148 jsvm::Run(cycle);
149 }
150
Empty(JSVM_Env env,JSVM_CallbackInfo info)151 static JSVM_Value Empty(JSVM_Env env, JSVM_CallbackInfo info)
152 {
153 return jsvm::Undefined();
154 }
155
RunFunction()156 void RunFunction()
157 {
158 JSVM_Value func;
159 JSVM_Status status;
160 JSVM_CallbackStruct callback = {.callback = Empty, .data = nullptr};
161 status = OH_JSVM_CreateFunction(jsvm_env, "func", JSVM_AUTO_LENGTH, &callback, &func);
162 CHECK(status == JSVM_OK);
163 JSVM_Value result;
164 status = OH_JSVM_CallFunction(jsvm_env, jsvm::Undefined(), func, 0, nullptr, &result);
165 CHECK(status == JSVM_OK);
166 }
167
RunStackTrace()168 void RunStackTrace()
169 {
170 // run unimplemented func to do stack trace.
171 JSVM_Value result;
172 JSVM_Status status = OH_JSVM_RunScript(jsvm_env, jsvm::Compile("func();"), &result);
173 CHECK(status == JSVM_GENERIC_FAILURE);
174 JSVM_Value exception;
175 status = OH_JSVM_GetAndClearLastException(jsvm_env, &exception);
176 CHECK(status == JSVM_OK);
177 }
178
RunWasm()179 void RunWasm()
180 {
181 std::vector<uint8_t> buffer;
182 ReadBinaryFile(wasmPath + "add.wasm", buffer);
183 CHECK(buffer.size() > 0);
184 JSVM_Value wasmModule;
185 JSVM_CALL(OH_JSVM_CompileWasmModule(jsvm_env, buffer.data(), buffer.size(), NULL, 0, NULL, &wasmModule));
186 CHECK(IsWasmModuleObject(wasmModule));
187 JSVM_Value wasmInstance = InstantiateWasmModule(wasmModule, jsvm::Run(""));
188 JSVM_Value exports = jsvm::GetProperty(wasmInstance, jsvm::Str("exports"));
189 CHECK(jsvm::IsObject(exports));
190 JSVM_Value add = jsvm::GetProperty(exports, jsvm::Str("add"));
191 CHECK(jsvm::IsFunction(add));
192 const uint32_t len = 1000;
193 for (uint32_t i = 0; i < len; ++i) {
194 jsvm::Call(add, jsvm::Undefined(), {jsvm::Run("1"), jsvm::Run("2")});
195 }
196 }
197
198 TEST_TRACE_CATEGROY(JSVM_TRACE_VM, RunCycle);
199 TEST_TRACE_CATEGROY(JSVM_TRACE_COMPILE, RunCycle);
200 TEST_TRACE_CATEGROY(JSVM_TRACE_EXECUTE, RunCycle);
201 TEST_TRACE_CATEGROY(JSVM_TRACE_RUNTIME, RunFunction);
202 TEST_TRACE_CATEGROY(JSVM_TRACE_STACK_TRACE, RunStackTrace);
203 #if !defined(JSVM_JITLESS)
204 TEST_TRACE_CATEGROY(JSVM_TRACE_WASM, RunWasm);
205 TEST_TRACE_CATEGROY(JSVM_TRACE_WASM_DETAILED, RunWasm);
206 #endif
207
TEST(Trace_MultiCategory)208 TEST(Trace_MultiCategory)
209 {
210 std::vector<JSVM_TraceCategory> categories = {
211 JSVM_TRACE_VM, JSVM_TRACE_COMPILE, JSVM_TRACE_EXECUTE, JSVM_TRACE_RUNTIME,
212 JSVM_TRACE_STACK_TRACE, JSVM_TRACE_WASM, JSVM_TRACE_WASM_DETAILED,
213 };
214 JSVM_Status status;
215 std::string data;
216 {
217 status = OH_JSVM_TraceStart(categories.size(), categories.data(), "all", 0);
218 CHECK(status == JSVM_OK);
219
220 RunCycle();
221 #if !defined(JSVM_JITLESS)
222 RunWasm();
223 #endif
224 RunStackTrace();
225 RunFunction();
226
227 status = OH_JSVM_TraceStop(OutputStream, (void *)&data);
228 CHECK(status == JSVM_OK);
229
230 JSVM_Value obj;
231 JSVM_CALL(OH_JSVM_JsonParse(env, jsvm::Str(data), &obj));
232 CHECK(jsvm::IsObject(obj));
233 JSVM_Value trace = jsvm::GetProperty(obj, "all");
234 CHECK(jsvm::IsArray(trace));
235 CHECK(jsvm::ArrayLength(trace) != 0);
236 }
237 }
238
TEST(Trace_DefaultCategory)239 TEST(Trace_DefaultCategory)
240 {
241 JSVM_Status status;
242 std::string data;
243 {
244 status = OH_JSVM_TraceStart(0, nullptr, "default", 0);
245 CHECK(status == JSVM_OK);
246
247 RunCycle();
248
249 status = OH_JSVM_TraceStop(OutputStream, (void *)&data);
250 CHECK(status == JSVM_OK);
251
252 JSVM_Value obj;
253 JSVM_CALL(OH_JSVM_JsonParse(env, jsvm::Str(data), &obj));
254 CHECK(jsvm::IsObject(obj));
255 JSVM_Value trace = jsvm::GetProperty(obj, "default");
256 CHECK(jsvm::IsArray(trace));
257 CHECK(jsvm::ArrayLength(trace) != 0);
258 }
259 }
260
TEST(Trace_MultiStart)261 TEST(Trace_MultiStart)
262 {
263 JSVM_Status status;
264 std::string data;
265 {
266 {
267 std::vector<JSVM_TraceCategory> category = {JSVM_TRACE_VM};
268 status = OH_JSVM_TraceStart(category.size(), category.data(), "start first", 0);
269 CHECK(status == JSVM_OK);
270 }
271 {
272 std::vector<JSVM_TraceCategory> category = {JSVM_TRACE_WASM};
273 status = OH_JSVM_TraceStart(category.size(), category.data(), "start second", 0);
274 CHECK(status == JSVM_OK);
275 }
276 RunCycle();
277
278 status = OH_JSVM_TraceStop(OutputStream, (void *)&data);
279 CHECK(status == JSVM_OK);
280
281 JSVM_Value obj;
282 JSVM_CALL(OH_JSVM_JsonParse(env, jsvm::Str(data), &obj));
283 CHECK(jsvm::IsObject(obj));
284 JSVM_Value trace = jsvm::GetProperty(obj, "start second");
285 CHECK(jsvm::IsArray(trace));
286 CHECK(jsvm::ArrayLength(trace) == 0);
287 }
288 {
289 {
290 std::vector<JSVM_TraceCategory> category = {JSVM_TRACE_WASM};
291 status = OH_JSVM_TraceStart(category.size(), category.data(), "start first", 0);
292 CHECK(status == JSVM_OK);
293 }
294 {
295 std::vector<JSVM_TraceCategory> category = {JSVM_TRACE_VM};
296 status = OH_JSVM_TraceStart(category.size(), category.data(), "start second", 0);
297 CHECK(status == JSVM_OK);
298 }
299 RunCycle();
300
301 status = OH_JSVM_TraceStop(OutputStream, (void *)&data);
302 CHECK(status == JSVM_OK);
303
304 JSVM_Value obj;
305 JSVM_CALL(OH_JSVM_JsonParse(env, jsvm::Str(data), &obj));
306 CHECK(jsvm::IsObject(obj));
307 JSVM_Value trace = jsvm::GetProperty(obj, "start second");
308 CHECK(jsvm::IsArray(trace));
309 CHECK(jsvm::ArrayLength(trace) != 0);
310 }
311 }
312
TEST(Trace_MaxTrunks)313 TEST(Trace_MaxTrunks)
314 {
315 std::vector<JSVM_TraceCategory> category = {JSVM_TRACE_STACK_TRACE};
316 JSVM_Status status;
317 std::string data;
318 {
319 status = OH_JSVM_TraceStart(category.size(), category.data(), "default trunk", 0);
320 CHECK(status == JSVM_OK);
321
322 for (size_t i = 0; i < 100; ++i) {
323 RunStackTrace();
324 }
325 status = OH_JSVM_TraceStop(OutputStream, (void *)&data);
326 CHECK(status == JSVM_OK);
327
328 JSVM_Value obj;
329 JSVM_CALL(OH_JSVM_JsonParse(env, jsvm::Str(data), &obj));
330 CHECK(jsvm::IsObject(obj));
331 JSVM_Value trace = jsvm::GetProperty(obj, "default trunk");
332 CHECK(jsvm::IsArray(trace));
333 CHECK(jsvm::ArrayLength(trace) != 0);
334 CHECK(jsvm::ArrayLength(trace) > 64);
335 }
336
337 { // test maxChunks
338 status = OH_JSVM_TraceStart(category.size(), category.data(), "one trunk", 1);
339 CHECK(status == JSVM_OK);
340
341 // one chunk has 64 trace events object, so we should generate over 64 trace events here.
342
343 for (size_t i = 0; i < 100; ++i) {
344 RunStackTrace();
345 }
346 status = OH_JSVM_TraceStop(OutputStream, (void *)&data);
347 CHECK(status == JSVM_OK);
348
349 JSVM_Value obj;
350 JSVM_CALL(OH_JSVM_JsonParse(env, jsvm::Str(data), &obj));
351 CHECK(jsvm::IsObject(obj));
352 JSVM_Value trace = jsvm::GetProperty(obj, "one trunk");
353 CHECK(jsvm::IsArray(trace));
354 CHECK(jsvm::ArrayLength(trace) != 0);
355
356 // one chunk has 64 trace events object, so length is not more than 64.
357 CHECK(jsvm::ArrayLength(trace) <= 64);
358 }
359 }
360
TEST(Trace_InvalidArg)361 TEST(Trace_InvalidArg)
362 {
363 JSVM_Status status;
364 std::string data;
365
366 { // test trace without trace start
367 status = OH_JSVM_TraceStop(OutputStream, (void *)&data);
368 CHECK(status == JSVM_INVALID_ARG);
369 }
370
371 { // test trace with invalid category
372 std::vector<JSVM_TraceCategory> category(1);
373 *(int *)&category[0] = 100;
374 status = OH_JSVM_TraceStart(category.size(), category.data(), "invalid", 0);
375 CHECK(status == JSVM_INVALID_ARG);
376 }
377
378 { // test trace with count or category is invalid
379 std::vector<JSVM_TraceCategory> category = {JSVM_TRACE_WASM};
380 status = OH_JSVM_TraceStart(0, category.data(), "invalid", 0);
381 CHECK(status == JSVM_INVALID_ARG);
382
383 status = OH_JSVM_TraceStart(1, nullptr, "invalid", 0);
384 CHECK(status == JSVM_INVALID_ARG);
385 }
386
387 { // test trace with nullptr
388 std::vector<JSVM_TraceCategory> category = {JSVM_TRACE_WASM};
389 status = OH_JSVM_TraceStart(category.size(), category.data(), nullptr, 0);
390 CHECK(status == JSVM_OK);
391
392 status = OH_JSVM_TraceStop(nullptr, (void *)&data);
393 CHECK(status == JSVM_INVALID_ARG);
394
395 status = OH_JSVM_TraceStop(OutputStream, nullptr);
396 CHECK(status == JSVM_INVALID_ARG);
397
398 status = OH_JSVM_TraceStop(OutputStream, (void *)&data);
399 CHECK(status == JSVM_OK);
400 }
401 }
402
403 #endif // TEST_TRACE