• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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