wasm-micro-runtime/core/iwasm/libraries/debug-engine/debug_engine.c
Zhenwei Jin 419f5cbf9e
Merge and solve conflict for extended const (#4435)
* build(deps): Bump github/codeql-action from 3.28.18 to 3.28.19 (#4346)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.18 to 3.28.19.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Commits](https://github.com/github/codeql-action/compare/v3.28.18...v3.28.19)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.28.19
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* wasi_socket_ext.c: avoid tls to make this library-friendly (#4338)

* Enable aot memory64 sw bounds checks by default (#4350)

- enable aot memory64 sw bounds checks by default

* build(deps): Bump requests from 2.32.3 to 2.32.4 in /build-scripts (#4349)

Bumps [requests](https://github.com/psf/requests) from 2.32.3 to 2.32.4.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.32.3...v2.32.4)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.32.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* wasi_nn_types.h: remove a seemingly stale comment (#4348)

* add heap-type check for GC when ref.null (#4300)

- According to [Link 1](https://webassembly.github.io/gc/core/valid/instructions.html#xref-syntax-instructions-syntax-instr-ref-mathsf-ref-null-mathit-ht), we must ensure that the heap type is valid when ref.null.
- According to [Link 2](https://webassembly.github.io/gc/core/valid/types.html#heap-types), a heap type is considered valid if it is either a concrete heap type or an abstract heap type.

However, in this function, the check for abstract heap types (absheaptype) was clearly missing, so this condition needs to be added explicitly in the if statement.

- When GC is disabled, no change is needed.
- When GC is enabled, heap types in WAMR are LEB-encoded values ([Link 3](https://webassembly.github.io/gc/core/appendix/index-types.html)). Therefore, we must use read_leb_int32 to parse the heap type correctly. And we can compute the original type1 using type1 = (uint8)((int32)0x80 + heap_type);.

* wamr-wasi-extensions: add a cmake package to provide our wasi extension (#4344)

* wasi_ephemeral_nn.h: add a convenience wrapper header
* wamr-wasi-extensions: add a cmake package to provide our wasi extension

the sample app was tested with:
* wasmtime
* iwasm with https://github.com/bytecodealliance/wasm-micro-runtime/pull/4308

currently only contains wasi-nn.
maybe it makes sense to add lib-socket things as well.

cf. https://github.com/bytecodealliance/wasm-micro-runtime/issues/4288

* wasi_nn_openvino.c: remove the tensor layout adjustment logic (#4308)

the logic in question seems like an attempt to work around
some application bugs.
my wild guess is that it was for classification-example.
cf. https://github.com/bytecodealliance/wasmtime/issues/10867

* Update type validation in load_table_import() and load_table() (#4296)

Prevent from value type.

https://webassembly.github.io/spec/core/valid/types.html#table-types
https://webassembly.github.io/gc/core/syntax/types.html#reference-types

* Follow #4268 to deprecate wamr_ide-related components (#4341)

refer to: Bypass wamr_ide-related components from the release process. (#4268)

* clean up incompatible running mode checks in test script and ci (#4342)

Rearrange the content of do_execute_in_running_mode() in alphabetical
order. 

Add an incompatible check for x86_32. Now, all belows will be bypassed:
- jit, fast-jit, multi-tier-jit
- memory64
- multi-memory
- simd

* Update WABT downloads URL (#4357)

Plus, skip unsupported running mode instead quit during wamr compiler
test

* Modify AOT static PGO to conform to llvm-18 and add a CI job to test static PGO on the coremark benchmark (#4345)

* static PGO compatible with llvm18 and add CI job to test static PGO on coremark benchmark
* update comments and warning info, bitmaps section in llvm profdata shouldn't be used in PGO

* Collective fix for typos and minor bugs (#4369)

* wasi-nn: fix backend leak on multiple loads (#4366)

cf. https://github.com/bytecodealliance/wasm-micro-runtime/issues/4340

* build(deps): Bump github/codeql-action from 3.28.19 to 3.29.0 (#4371)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.19 to 3.29.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Commits](https://github.com/github/codeql-action/compare/v3.28.19...v3.29.0)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.29.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* add validation for array type in load_init_expr(GC only) (#4370)

* wasi_nn_openvino.c: remove broken xml check (#4365)

`xml.buf[xml.size]` check is broken because it accesses past
the end of the buffer.

anyway, openvino doesn't seem to care the NUL termination.

* wamr-wasi-extensions: add lib-socket things (#4360)

* improve installation steps for wasi-sdk and wabt on Windows (#4359)

* wasi_ephemeral_nn.h: prefix identfiers to avoid too generic names (#4358)

* wasi_nn_openvino.c: add a missing buffer overflow check in get_output (#4353)

cf. https://github.com/bytecodealliance/wasm-micro-runtime/issues/4351

* send an empty/error reply from server (#4362)

Signed-off-by: Su Yihan <yihan.su@intel.com>

* wasi_nn_openvino.c: remove pre/postprocessing and layout assumptions (#4361)

as wasi-nn doesn't have these concepts, the best we can do without
risking breaking certain applications here is to pass through tensors
as they are.

this matches wasmtime's behavior.

tested with:

* wasmtime classification-example
  (with this change, this example fails on tensor size mismatch
  instead of implicitly resizing it.)

* license-plate-recognition-barrier-0007, a converted version
  with non-fp32 output. [1]
  (with this change, this model outputs integers as expected.)

[1] cd7ebe313b/models/public/license-plate-recognition-barrier-0007

* add nn-cli example (#4373)

an example application with flexible cli options which
aims to allow us to perform any wasi-nn operations.

eg.
```
--load-graph=file=fixture/model.xml,file=fixture/model.bin,id=graph
--init-execution-context=graph-id=graph,id=ctx
--set-input=file=fixture/tensor.bgr,context-id=ctx,dim=1,dim=3,dim=224,dim=224
--compute=context-id=ctx
--get-output=context-id=ctx,file=output.bin
```

* wasi-nn: apply the shared library hack to darwin as well (#4374)

copied from the linux version.

i'm a bit skeptical with this workaround though.
it might be simpler to prohibit the use of wamr api in these
shared libraries. after all, what these libraries do is nothing
specific to wasm.

* wasi-nn: don't try to deinit uninitialized backend (#4375)

cf. https://github.com/bytecodealliance/wasm-micro-runtime/issues/4339

* core/iwasm/libraries/wasi-nn/test/build.sh: add a tip for intel mac (#4389)

i keep forgetting this and had to re-investigate it at least twice.
hopefully this can be helpful for others too.

* wasi_nn_tensorflowlite.cpp: reject non-fp32 input earlier (#4388)

this backend assumes fp32 here and there.
it's safer to reject unexpected inputs explicitly.

* Fix several issues related to night-run CI and test scripts. (#4385)

- remove duplicated options
- fix test script
- change ci to use binary

* core/iwasm/libraries/wasi-nn/test: use the correct version of keras (#4383)

* wasi-nn: fix tensor_data abi for wasi_ephemeral_nn (#4379)

it's "(list u8)" in the witx definition.

the new definition matches both of our own host definition
(struct tensor_wasm) and wasmtime.

cf. https://github.com/bytecodealliance/wasm-micro-runtime/issues/4352

* enable WAMR_BUILD_WASI_EPHEMERAL_NN by default (#4381)

cf. https://github.com/bytecodealliance/wasm-micro-runtime/issues/4326

* deprecate legacy WAMR-specific "wasi_nn" module (#4382)

wasi_nn.h: deprecate legacy "wasi_nn"

cf. https://github.com/bytecodealliance/wasm-micro-runtime/issues/4326

* wasi-nn: add minimum serialization on WASINNContext (#4387)

currently this is not necessary because context (WASINNContext) is
local to instance. (wasm_module_instance_t)

i plan to make a context shared among instances in a cluster when
fixing https://github.com/bytecodealliance/wasm-micro-runtime/issues/4313.
this is a preparation for that direction.

an obvious alternative is to tweak the module instance context APIs
to allow declaring some kind of contexts instance-local. but i feel,
in this particular case, it's more natural to make "wasi-nn handles"
shared among threads within a "process".

note that, spec-wise, how wasi-nn behaves wrt threads is not defined
at all because wasi officially doesn't have threads yet. i suppose, at
this point, that how wasi-nn interacts with wasi-threads is something
we need to define by ourselves, especially when we are using an outdated
wasi-nn version.

with this change, if a thread attempts to access a context while
another thread is using it, we simply make the operation fail with
the "busy" error. this is intended for the mimimum serialization to
avoid problems like crashes/leaks/etc. this is not intended to allow
parallelism or such.

no functional changes are intended at this point yet.

cf.
https://github.com/bytecodealliance/wasm-micro-runtime/issues/4313
https://github.com/bytecodealliance/wasm-micro-runtime/issues/2430

* Improve spec test execution by adding retry logic for transient errors (#4393)

* wasi_nn_openvino.c: implement multiple models per instance (#4380)

tested with two models:
```
--load-graph=id=graph1,file=public/license-plate-recognition-barrier-0007/FP32/license-plate-recognition-barrier-0007.xml,file=public/license-plate-recognition-barrier-0007/FP32/license-plate-recognition-barrier-0007.bin \
--load-graph=id=graph2,file=classify/model.xml,file=classify/model.bin \
--init-execution-context=id=exec1,graph-id=graph1 \
--init-execution-context=id=exec2,graph-id=graph2 \
--set-input=context-id=exec1,dim=1,dim=24,dim=94,dim=3,file=out.bin \
--set-input=context-id=exec2,file=classify/banana-3x224x224-bgr.bin,dim=1,dim=3,dim=224,dim=224 \
--compute=context-id=exec1 \
--compute=context-id=exec2 \
--get-output=context-id=exec1,file=exec1-result.bin \
--get-output=context-id=exec2,file=exec2-result.bin
```

a detailed HOWTO: https://github.com/bytecodealliance/wasm-micro-runtime/pull/4380#issuecomment-2986882718

* wamr-wasi-extensions/socket: disable reference-types (#4392)

and add a comment to explain why.

* CI: fix the description of upload_url (#4407)

* wasi-nn: fix context lifetime issues (#4396)

* wasi-nn: fix context lifetime issues

use the module instance context api instead of trying to roll
our own with a hashmap. this fixes context lifetime problems mentioned in
https://github.com/bytecodealliance/wasm-micro-runtime/issues/4313.

namely,

* wasi-nn resources will be freed earlier now. before this change,
  they used to be kept until the runtime shutdown. (wasm_runtime_destroy)
  after this change, they will be freed together with the associated
  instances.

* wasm_module_inst_t pointer uniqueness assumption (which is wrong
  after wasm_runtime_deinstantiate) was lifted.

as a side effect, this change also makes a context shared among threads
within a cluster. note that this is a user-visible api/abi breaking change.
before this change, wasi-nn "handles" like wasi_ephemeral_nn_graph were
thread-local. after this change, they are shared among threads within
a cluster, similarly to wasi file descriptors. spec-wise, either behavior
should be ok simply because wasi officially doesn't have threads yet.
althogh i feel the latter semantics is more intuitive, if your application
depends on the thread-local behavior, this change breaks your application.

tested with wamr-wasi-extensions/samples/nn-cli, modified to
call each wasi-nn operations on different threads. (if you are
interested, you can find the modification at
https://github.com/yamt/wasm-micro-runtime/tree/yamt-nn-wip-20250619.)

cf.
https://github.com/bytecodealliance/wasm-micro-runtime/issues/4313
https://github.com/bytecodealliance/wasm-micro-runtime/issues/2430

* runtime_lib.cmake: enable WAMR_BUILD_MODULE_INST_CONTEXT for wasi-nn

as we do for wasi (WAMR_BUILD_LIBC_WASI)

* wasi_nn_tensorflowlite.cpp: fix get_output return size (#4390)

it should be byte size, not the number of (fp32) values.

i'm ambivalent about how to deal with the compatibility for
the legacy wamr-specific "wasi_nn". for now, i avoided changing it.
(so that existing tests using the legacy abi, namely test_tensorflow.c
and test_tensorflow_quantized.c, passes as they are.)
if we have any users who still want to use the legacy abi,
i suppose they consider the compatibility is more important
than the consistency with other backends.

cf. https://github.com/bytecodealliance/wasm-micro-runtime/issues/4376

* Refactor copy callstack feature (#4401)

- Change `WAMR_ENABLE_COPY_CALLSTACK` to `WAMR_BUILD_COPY_CALL_STACK`, as
  `WAMR_BUILD` is the prefix for a command line option.
- Change `WAMR_ENABLE_COPY_CALLSTACK` to `WASM_ENABLE_COPY_CALL_STACK`, as
  `WASM_ENABLE` is the prefix for a macro in the source code.
- Change `CALLSTACK` to `CALL_STACK` to align with the existing
  `DUMP_CALL_STACK` feature.
- Continue using `WASMCApiFrame` instead of `wasm_frame_t` outside of
  *wasm_c_api.xxx* to avoid a typedef redefinition warning, which is
  identified by Clang.

* loader: add type index checking (#4402)

* Fix handling of non-nullable global_type during global import (#4408)

* wasi_nn_llamacpp.c: make this compilable (#4403)

* fix bug in bh_vector when extending (#4414)

* Collective fix (#4413)

* Fix vector growth check and typos in core (#9)
* Fix resource cleanup in memory and running modes tests (#10)
* Add end of file empty line in wasm_running_modes_test.cc

* wasi-nn: make the host use the wasi_ephemeral_nn version of tensor_data (#4411)

the motivations:

* make the actual input size available to the backends.
  (currently the backends have to make a guess from shape/type.)

* make the host logic look a bit similar to wasi_ephemeral_nn.

this is a backend api/abi change.

* wasi_nn_llamacpp.c: fix buffer overruns in set_input (#4420)

note: for some reasons, wasmedge seems to ignore type/dimensions
for the input of ggml. some user code relies on it.
cf. https://github.com/second-state/WasmEdge-WASINN-examples/issues/196

note: despite the comment in our code, the input doesn't seem
nul-terminated.

* wasi_nn_llamacpp.c: remove an unused variable (#4415)

* Fix few shadow warnings (#4409)

- declaration of ‘memidx’ shadows a previous local
- declaration of ‘count’ shadows a previous local

* CI: build wamr-wasi-extensions (#4394)

* wamr-wasi-extensions: separate test scripts
also, allow to specify the prefix directory.
for the convenience of the CI.

* CI: build wamr-wasi-extensions
fragments are copied from compilation_on_macos.yml.
(thus intel copyright notice)

* wasi_nn_openvino.c: fix a debug build (#4416)

after "wasi_nn_openvino.c: implement multiple models per instance" change.
(https://github.com/bytecodealliance/wasm-micro-runtime/pull/4380)

* loader: fix a potential overflow issue (#4427)

* CI: revert SGX retry attempts (#4421)

* Revert "Improve spec test execution by adding retry logic for transient errors (#4393)"

This reverts commit 64cafaff1e.

* Revert "Add error handling for sgx ci (#4222)"

This reverts commit 8ad47897d1.

* implement extended const expr (#4318)

* add a toggle to enable extended const on wamrc (#4412)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Su Yihan <yihan.su@intel.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: YAMAMOTO Takashi <yamamoto@midokura.com>
Co-authored-by: TianlongLiang <111852609+TianlongLiang@users.noreply.github.com>
Co-authored-by: Liu Jia <jia3.liu@intel.com>
Co-authored-by: liang.he <liang.he@intel.com>
Co-authored-by: Su Yihan <yihan.su@intel.com>
2025-07-01 10:39:44 +08:00

1434 lines
42 KiB
C

/*
* Copyright (C) 2021 Ant Group. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include "debug_engine.h"
#include "gdbserver.h"
#include "handler.h"
#include "bh_platform.h"
#include "wasm_interp.h"
#include "wasm_opcode.h"
#include "wasm_runtime.h"
static const uint8 break_instr[] = { DEBUG_OP_BREAK };
typedef struct WASMDebugEngine {
struct WASMDebugEngine *next;
WASMDebugControlThread *control_thread;
char ip_addr[128];
int32 process_base_port;
bh_list debug_instance_list;
korp_mutex instance_list_lock;
} WASMDebugEngine;
void
on_thread_stop_event(WASMDebugInstance *debug_inst, WASMExecEnv *exec_env)
{
os_mutex_lock(&debug_inst->wait_lock);
debug_inst->stopped_thread = exec_env;
if (debug_inst->current_state == DBG_LAUNCHING) {
/* In launching phase, send a signal so that handle_threadstop_request
* can be woken up */
os_cond_signal(&debug_inst->wait_cond);
}
os_mutex_unlock(&debug_inst->wait_lock);
}
void
on_thread_exit_event(WASMDebugInstance *debug_inst, WASMExecEnv *exec_env)
{
os_mutex_lock(&debug_inst->wait_lock);
/* DBG_LAUNCHING: exit when debugger detached,
* DBG_ERROR: exit when debugger error */
if (debug_inst->current_state != DBG_LAUNCHING
&& debug_inst->current_state != DBG_ERROR) {
/* only when exit normally the debugger thread will participate in
* teardown phase */
debug_inst->stopped_thread = exec_env;
}
os_mutex_unlock(&debug_inst->wait_lock);
}
static WASMDebugEngine *g_debug_engine;
static uint32 current_instance_id = 1;
static uint32
allocate_instance_id(void)
{
uint32 id;
bh_assert(g_debug_engine);
os_mutex_lock(&g_debug_engine->instance_list_lock);
id = current_instance_id++;
os_mutex_unlock(&g_debug_engine->instance_list_lock);
return id;
}
static bool
is_thread_running(WASMDebugControlThread *control_thread)
{
return control_thread->status == RUNNING;
}
static bool
is_thread_stopped(WASMDebugControlThread *control_thread)
{
return control_thread->status == STOPPED;
}
static bool
is_thread_detached(WASMDebugControlThread *control_thread)
{
return control_thread->status == DETACHED;
}
static void *
control_thread_routine(void *arg)
{
WASMDebugInstance *debug_inst = (WASMDebugInstance *)arg;
WASMDebugControlThread *control_thread = NULL;
control_thread = debug_inst->control_thread;
bh_assert(control_thread);
os_mutex_lock(&debug_inst->wait_lock);
control_thread->status = RUNNING;
debug_inst->id = allocate_instance_id();
control_thread->debug_engine = g_debug_engine;
control_thread->debug_instance = debug_inst;
bh_strcpy_s(control_thread->ip_addr, sizeof(control_thread->ip_addr),
g_debug_engine->ip_addr);
if (control_thread->port == -1) {
control_thread->port =
(g_debug_engine->process_base_port == 0)
? 0
: g_debug_engine->process_base_port + debug_inst->id - 1;
}
LOG_WARNING("control thread of debug object %p start\n", debug_inst);
control_thread->server =
wasm_create_gdbserver(control_thread->ip_addr, &control_thread->port);
if (!control_thread->server) {
LOG_ERROR("Failed to create debug server\n");
control_thread->port = 0;
os_cond_signal(&debug_inst->wait_cond);
os_mutex_unlock(&debug_inst->wait_lock);
return NULL;
}
control_thread->server->thread = control_thread;
/*
* wasm gdbserver created, the execution thread
* doesn't need to wait for the debugger connection,
* so we wake up the execution thread before listen
*/
os_cond_signal(&debug_inst->wait_cond);
os_mutex_unlock(&debug_inst->wait_lock);
if (!wasm_gdbserver_listen(control_thread->server)) {
LOG_ERROR("Failed while listening for debugger\n");
goto fail;
}
/* outer infinite loop: try to connect with the debugger */
while (true) {
/* wait lldb client to connect */
if (!wasm_gdbserver_accept(control_thread->server)) {
LOG_ERROR("Failed while accepting debugger connection\n");
goto fail;
}
control_thread->status = RUNNING;
/* when reattached, send signal */
wasm_cluster_send_signal_all(debug_inst->cluster, WAMR_SIG_SINGSTEP);
/* inner infinite loop: keep serving until detach */
while (true) {
os_mutex_lock(&control_thread->wait_lock);
if (is_thread_running(control_thread)) {
/* send thread stop reply */
if (debug_inst->stopped_thread
&& debug_inst->current_state == APP_RUNNING) {
uint32 status;
korp_tid tid;
status = (uint32)debug_inst->stopped_thread->current_status
->signal_flag;
tid = debug_inst->stopped_thread->handle;
if (debug_inst->stopped_thread->current_status
->running_status
== STATUS_EXIT) {
/* If the thread exits, report "W00" if it's the last
* thread in the cluster, otherwise ignore this event */
status = 0;
/* By design, all the other threads should have been
* stopped at this moment, so it is safe to access the
* exec_env_list.len without lock */
if (debug_inst->cluster->exec_env_list.len != 1) {
debug_inst->stopped_thread = NULL;
/* The exiting thread may wait for the signal */
os_cond_signal(&debug_inst->wait_cond);
os_mutex_unlock(&control_thread->wait_lock);
continue;
}
}
wasm_debug_instance_set_cur_thread(
debug_inst, debug_inst->stopped_thread->handle);
send_thread_stop_status(control_thread->server, status,
tid);
debug_inst->current_state = APP_STOPPED;
debug_inst->stopped_thread = NULL;
if (status == 0) {
/* The exiting thread may wait for the signal */
os_cond_signal(&debug_inst->wait_cond);
}
}
/* Processing incoming requests */
if (!wasm_gdbserver_handle_packet(control_thread->server)) {
control_thread->status = STOPPED;
LOG_ERROR("An error occurs when handling a packet\n");
os_mutex_unlock(&control_thread->wait_lock);
goto fail;
}
}
else if (is_thread_detached(control_thread)) {
os_mutex_unlock(&control_thread->wait_lock);
break;
}
else if (is_thread_stopped(control_thread)) {
os_mutex_unlock(&control_thread->wait_lock);
return NULL;
}
os_mutex_unlock(&control_thread->wait_lock);
}
}
fail:
wasm_debug_instance_on_failure(debug_inst);
LOG_VERBOSE("control thread of debug object [%p] stopped with failure\n",
debug_inst);
return NULL;
}
static WASMDebugControlThread *
wasm_debug_control_thread_create(WASMDebugInstance *debug_instance, int32 port)
{
WASMDebugControlThread *control_thread;
if (!(control_thread =
wasm_runtime_malloc(sizeof(WASMDebugControlThread)))) {
LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
return NULL;
}
memset(control_thread, 0, sizeof(WASMDebugControlThread));
control_thread->port = port;
if (os_mutex_init(&control_thread->wait_lock) != 0)
goto fail;
debug_instance->control_thread = control_thread;
os_mutex_lock(&debug_instance->wait_lock);
if (0
!= os_thread_create(&control_thread->tid, control_thread_routine,
debug_instance, APP_THREAD_STACK_SIZE_DEFAULT)) {
os_mutex_unlock(&debug_instance->wait_lock);
goto fail1;
}
/* wait until the debug control thread ready */
os_cond_wait(&debug_instance->wait_cond, &debug_instance->wait_lock);
os_mutex_unlock(&debug_instance->wait_lock);
if (!control_thread->server) {
os_thread_join(control_thread->tid, NULL);
goto fail1;
}
os_mutex_lock(&g_debug_engine->instance_list_lock);
/* create control thread success, append debug instance to debug engine */
bh_list_insert(&g_debug_engine->debug_instance_list, debug_instance);
os_mutex_unlock(&g_debug_engine->instance_list_lock);
/* If we set WAMR_SIG_STOP here, the VSCode debugger adaptor will raise an
* exception in the UI. We use WAMR_SIG_SINGSTEP to avoid this exception for
* better user experience */
wasm_cluster_send_signal_all(debug_instance->cluster, WAMR_SIG_SINGSTEP);
return control_thread;
fail1:
os_mutex_destroy(&control_thread->wait_lock);
fail:
wasm_runtime_free(control_thread);
return NULL;
}
static void
wasm_debug_control_thread_destroy(WASMDebugInstance *debug_instance)
{
WASMDebugControlThread *control_thread = debug_instance->control_thread;
LOG_VERBOSE("stopping control thread of debug object [%p]\n",
debug_instance);
control_thread->status = STOPPED;
os_mutex_lock(&control_thread->wait_lock);
wasm_close_gdbserver(control_thread->server);
os_mutex_unlock(&control_thread->wait_lock);
os_thread_join(control_thread->tid, NULL);
wasm_runtime_free(control_thread->server);
os_mutex_destroy(&control_thread->wait_lock);
wasm_runtime_free(control_thread);
}
static WASMDebugEngine *
wasm_debug_engine_create(void)
{
WASMDebugEngine *engine;
if (!(engine = wasm_runtime_malloc(sizeof(WASMDebugEngine)))) {
LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
return NULL;
}
memset(engine, 0, sizeof(WASMDebugEngine));
if (os_mutex_init(&engine->instance_list_lock) != 0) {
wasm_runtime_free(engine);
LOG_ERROR("WASM Debug Engine error: failed to init mutex");
return NULL;
}
/* reset current instance id */
current_instance_id = 1;
bh_list_init(&engine->debug_instance_list);
return engine;
}
void
wasm_debug_engine_destroy(void)
{
if (g_debug_engine) {
wasm_debug_handler_deinit();
os_mutex_destroy(&g_debug_engine->instance_list_lock);
wasm_runtime_free(g_debug_engine);
g_debug_engine = NULL;
}
}
bool
wasm_debug_engine_init(char *ip_addr, int32 process_port)
{
if (wasm_debug_handler_init() != 0) {
return false;
}
if (g_debug_engine == NULL) {
g_debug_engine = wasm_debug_engine_create();
}
if (g_debug_engine) {
g_debug_engine->process_base_port =
(process_port > 0) ? process_port : 0;
if (ip_addr)
snprintf(g_debug_engine->ip_addr, sizeof(g_debug_engine->ip_addr),
"%s", ip_addr);
else
snprintf(g_debug_engine->ip_addr, sizeof(g_debug_engine->ip_addr),
"%s", "127.0.0.1");
}
else {
wasm_debug_handler_deinit();
}
return g_debug_engine != NULL ? true : false;
}
/* A debug Instance is a debug "process" in gdb remote protocol
and bound to a runtime cluster */
WASMDebugInstance *
wasm_debug_instance_create(WASMCluster *cluster, int32 port)
{
WASMDebugInstance *instance;
WASMExecEnv *exec_env = NULL;
wasm_module_inst_t module_inst = NULL;
if (!g_debug_engine) {
return NULL;
}
if (!(instance = wasm_runtime_malloc(sizeof(WASMDebugInstance)))) {
LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
return NULL;
}
memset(instance, 0, sizeof(WASMDebugInstance));
if (os_mutex_init(&instance->wait_lock) != 0) {
goto fail1;
}
if (os_cond_init(&instance->wait_cond) != 0) {
goto fail2;
}
bh_list_init(&instance->break_point_list);
bh_list_init(&instance->watch_point_list_read);
bh_list_init(&instance->watch_point_list_write);
instance->cluster = cluster;
exec_env = bh_list_first_elem(&cluster->exec_env_list);
bh_assert(exec_env);
instance->current_tid = exec_env->handle;
module_inst = wasm_runtime_get_module_inst(exec_env);
bh_assert(module_inst);
/* Allocate linear memory for evaluating expressions during debugging. If
* the allocation failed, the debugger will not be able to evaluate
* expressions */
instance->exec_mem_info.size = DEBUG_EXECUTION_MEMORY_SIZE;
instance->exec_mem_info.start_offset = wasm_runtime_module_malloc(
module_inst, (uint64)instance->exec_mem_info.size, NULL);
if (instance->exec_mem_info.start_offset == 0) {
LOG_WARNING(
"WASM Debug Engine warning: failed to allocate linear memory for "
"execution. \n"
"Will not be able to evaluate expressions during "
"debugging");
}
instance->exec_mem_info.current_pos = instance->exec_mem_info.start_offset;
if (!wasm_debug_control_thread_create(instance, port)) {
LOG_ERROR("WASM Debug Engine error: failed to create control thread");
goto fail3;
}
wasm_cluster_set_debug_inst(cluster, instance);
return instance;
fail3:
os_cond_destroy(&instance->wait_cond);
fail2:
os_mutex_destroy(&instance->wait_lock);
fail1:
wasm_runtime_free(instance);
return NULL;
}
static void
wasm_debug_instance_destroy_breakpoints(WASMDebugInstance *instance)
{
WASMDebugBreakPoint *breakpoint, *next_bp;
breakpoint = bh_list_first_elem(&instance->break_point_list);
while (breakpoint) {
next_bp = bh_list_elem_next(breakpoint);
bh_list_remove(&instance->break_point_list, breakpoint);
wasm_runtime_free(breakpoint);
breakpoint = next_bp;
}
}
static void
wasm_debug_instance_destroy_watchpoints(WASMDebugInstance *instance,
bh_list *watchpoints)
{
WASMDebugWatchPoint *watchpoint, *next;
watchpoint = bh_list_first_elem(watchpoints);
while (watchpoint) {
next = bh_list_elem_next(watchpoint);
bh_list_remove(watchpoints, watchpoint);
wasm_runtime_free(watchpoint);
watchpoint = next;
}
}
void
wasm_debug_instance_destroy(WASMCluster *cluster)
{
WASMDebugInstance *instance = NULL;
if (!g_debug_engine) {
return;
}
instance = cluster->debug_inst;
if (instance) {
/* destroy control thread */
wasm_debug_control_thread_destroy(instance);
os_mutex_lock(&g_debug_engine->instance_list_lock);
bh_list_remove(&g_debug_engine->debug_instance_list, instance);
os_mutex_unlock(&g_debug_engine->instance_list_lock);
/* destroy all breakpoints */
wasm_debug_instance_destroy_breakpoints(instance);
wasm_debug_instance_destroy_watchpoints(
instance, &instance->watch_point_list_read);
wasm_debug_instance_destroy_watchpoints(
instance, &instance->watch_point_list_write);
os_mutex_destroy(&instance->wait_lock);
os_cond_destroy(&instance->wait_cond);
wasm_runtime_free(instance);
cluster->debug_inst = NULL;
}
}
WASMExecEnv *
wasm_debug_instance_get_current_env(WASMDebugInstance *instance)
{
WASMExecEnv *exec_env = NULL;
if (instance) {
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
while (exec_env) {
if (exec_env->handle == instance->current_tid)
break;
exec_env = bh_list_elem_next(exec_env);
}
}
return exec_env;
}
#if WASM_ENABLE_LIBC_WASI != 0
bool
wasm_debug_instance_get_current_object_name(WASMDebugInstance *instance,
char name_buffer[], uint32 len)
{
WASMExecEnv *exec_env;
WASIArguments *wasi_args;
WASMModuleInstance *module_inst;
if (!instance)
return false;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
wasi_args = &module_inst->module->wasi_args;
if (wasi_args && wasi_args->argc > 0) {
char *argv_name = wasi_args->argv[0];
uint32 name_len = (uint32)strlen(argv_name);
printf("the module name is %s\n", argv_name);
if (len - 1 >= name_len)
bh_strcpy_s(name_buffer, len, argv_name);
else
bh_strcpy_s(name_buffer, len, argv_name + (name_len + 1 - len));
return true;
}
return false;
}
#endif
uint64
wasm_debug_instance_get_pid(WASMDebugInstance *instance)
{
if (instance != NULL) {
return (uint64)instance->id;
}
return (uint64)0;
}
korp_tid
wasm_debug_instance_get_tid(WASMDebugInstance *instance)
{
if (instance != NULL) {
return instance->current_tid;
}
return (korp_tid)(uintptr_t)0;
}
uint32
wasm_debug_instance_get_tids(WASMDebugInstance *instance, korp_tid tids[],
uint32 len)
{
WASMExecEnv *exec_env;
uint32 i = 0, threads_num = 0;
if (!instance)
return 0;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
while (exec_env && i < len) {
/* Some threads may not be ready */
if (exec_env->handle != 0) {
tids[i++] = exec_env->handle;
threads_num++;
}
exec_env = bh_list_elem_next(exec_env);
}
LOG_VERBOSE("find %d tids\n", threads_num);
return threads_num;
}
uint32
wasm_debug_instance_get_thread_status(WASMDebugInstance *instance, korp_tid tid)
{
WASMExecEnv *exec_env = NULL;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
while (exec_env) {
if (exec_env->handle == tid) {
return (uint32)exec_env->current_status->signal_flag;
}
exec_env = bh_list_elem_next(exec_env);
}
return 0;
}
void
wasm_debug_instance_set_cur_thread(WASMDebugInstance *instance, korp_tid tid)
{
instance->current_tid = tid;
}
uint64
wasm_debug_instance_get_pc(WASMDebugInstance *instance)
{
WASMExecEnv *exec_env;
if (!instance)
return 0;
exec_env = wasm_debug_instance_get_current_env(instance);
if ((exec_env != NULL) && (exec_env->cur_frame != NULL)
&& (exec_env->cur_frame->ip != NULL)) {
WASMModuleInstance *module_inst =
(WASMModuleInstance *)exec_env->module_inst;
return WASM_ADDR(
WasmObj, instance->id,
(exec_env->cur_frame->ip - module_inst->module->load_addr));
}
return 0;
}
uint64
wasm_debug_instance_get_load_addr(WASMDebugInstance *instance)
{
WASMExecEnv *exec_env;
if (!instance)
return WASM_ADDR(WasmInvalid, 0, 0);
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (exec_env) {
return WASM_ADDR(WasmObj, instance->id, 0);
}
return WASM_ADDR(WasmInvalid, 0, 0);
}
WASMDebugMemoryInfo *
wasm_debug_instance_get_memregion(WASMDebugInstance *instance, uint64 addr)
{
WASMDebugMemoryInfo *mem_info;
WASMExecEnv *exec_env;
WASMModuleInstance *module_inst;
WASMMemoryInstance *memory;
uint32 num_bytes_per_page;
uint32 linear_mem_size = 0;
if (!instance)
return NULL;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return NULL;
if (!(mem_info = wasm_runtime_malloc(sizeof(WASMDebugMemoryInfo)))) {
LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
return NULL;
}
memset(mem_info, 0, sizeof(WASMDebugMemoryInfo));
mem_info->start = WASM_ADDR(WasmInvalid, 0, 0);
mem_info->size = 0;
mem_info->name[0] = '\0';
mem_info->permisson[0] = '\0';
module_inst = (WASMModuleInstance *)exec_env->module_inst;
switch (WASM_ADDR_TYPE(addr)) {
case WasmObj:
if (WASM_ADDR_OFFSET(addr) < module_inst->module->load_size) {
mem_info->start = WASM_ADDR(WasmObj, instance->id, 0);
mem_info->size = module_inst->module->load_size;
snprintf(mem_info->name, sizeof(mem_info->name), "%s",
"module");
snprintf(mem_info->permisson, sizeof(mem_info->permisson), "%s",
"rx");
}
break;
case WasmMemory:
{
memory = wasm_get_default_memory(module_inst);
if (memory) {
num_bytes_per_page = memory->num_bytes_per_page;
linear_mem_size = num_bytes_per_page * memory->cur_page_count;
}
if (WASM_ADDR_OFFSET(addr) < linear_mem_size) {
mem_info->start = WASM_ADDR(WasmMemory, instance->id, 0);
mem_info->size = linear_mem_size;
snprintf(mem_info->name, sizeof(mem_info->name), "%s",
"memory");
snprintf(mem_info->permisson, sizeof(mem_info->permisson), "%s",
"rw");
}
break;
}
default:
mem_info->start = WASM_ADDR(WasmInvalid, 0, 0);
mem_info->size = 0;
}
return mem_info;
}
void
wasm_debug_instance_destroy_memregion(WASMDebugInstance *instance,
WASMDebugMemoryInfo *mem_info)
{
wasm_runtime_free(mem_info);
}
bool
wasm_debug_instance_get_obj_mem(WASMDebugInstance *instance, uint64 offset,
char *buf, uint64 *size)
{
WASMExecEnv *exec_env;
WASMModuleInstance *module_inst;
WASMDebugBreakPoint *breakpoint;
WASMFastOPCodeNode *fast_opcode;
if (!instance)
return false;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
if (offset + *size > module_inst->module->load_size) {
LOG_VERBOSE("wasm_debug_instance_get_data_mem size overflow!\n");
*size = module_inst->module->load_size >= offset
? module_inst->module->load_size - offset
: 0;
}
bh_memcpy_s(buf, (uint32)*size, module_inst->module->load_addr + offset,
(uint32)*size);
breakpoint = bh_list_first_elem(&instance->break_point_list);
while (breakpoint) {
if (offset <= breakpoint->addr && breakpoint->addr < offset + *size) {
bh_memcpy_s(buf + (breakpoint->addr - offset), sizeof(break_instr),
&breakpoint->orignal_data, sizeof(break_instr));
}
breakpoint = bh_list_elem_next(breakpoint);
}
fast_opcode = bh_list_first_elem(&module_inst->module->fast_opcode_list);
while (fast_opcode) {
if (offset <= fast_opcode->offset
&& fast_opcode->offset < offset + *size) {
*(uint8 *)(buf + (fast_opcode->offset - offset)) =
fast_opcode->orig_op;
}
fast_opcode = bh_list_elem_next(fast_opcode);
}
return true;
}
bool
wasm_debug_instance_get_linear_mem(WASMDebugInstance *instance, uint64 offset,
char *buf, uint64 *size)
{
WASMExecEnv *exec_env;
WASMModuleInstance *module_inst;
WASMMemoryInstance *memory;
uint32 num_bytes_per_page;
uint32 linear_mem_size;
if (!instance)
return false;
exec_env = wasm_debug_instance_get_current_env(instance);
if (!exec_env)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
memory = wasm_get_default_memory(module_inst);
if (memory) {
num_bytes_per_page = memory->num_bytes_per_page;
linear_mem_size = num_bytes_per_page * memory->cur_page_count;
if (offset + *size > linear_mem_size) {
LOG_VERBOSE("wasm_debug_instance_get_linear_mem size overflow!\n");
*size = linear_mem_size >= offset ? linear_mem_size - offset : 0;
}
bh_memcpy_s(buf, (uint32)*size, memory->memory_data + offset,
(uint32)*size);
return true;
}
return false;
}
bool
wasm_debug_instance_set_linear_mem(WASMDebugInstance *instance, uint64 offset,
char *buf, uint64 *size)
{
WASMExecEnv *exec_env;
WASMModuleInstance *module_inst;
WASMMemoryInstance *memory;
uint32 num_bytes_per_page;
uint32 linear_mem_size;
if (!instance)
return false;
exec_env = wasm_debug_instance_get_current_env(instance);
if (!exec_env)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
memory = wasm_get_default_memory(module_inst);
if (memory) {
num_bytes_per_page = memory->num_bytes_per_page;
linear_mem_size = num_bytes_per_page * memory->cur_page_count;
if (offset + *size > linear_mem_size) {
LOG_VERBOSE("wasm_debug_instance_get_linear_mem size overflow!\n");
*size = linear_mem_size >= offset ? linear_mem_size - offset : 0;
}
bh_memcpy_s(memory->memory_data + offset, (uint32)*size, buf,
(uint32)*size);
return true;
}
return false;
}
bool
wasm_debug_instance_get_mem(WASMDebugInstance *instance, uint64 addr, char *buf,
uint64 *size)
{
switch (WASM_ADDR_TYPE(addr)) {
case WasmMemory:
return wasm_debug_instance_get_linear_mem(
instance, WASM_ADDR_OFFSET(addr), buf, size);
break;
case WasmObj:
return wasm_debug_instance_get_obj_mem(
instance, WASM_ADDR_OFFSET(addr), buf, size);
break;
default:
return false;
}
}
bool
wasm_debug_instance_set_mem(WASMDebugInstance *instance, uint64 addr, char *buf,
uint64 *size)
{
switch (WASM_ADDR_TYPE(addr)) {
case WasmMemory:
return wasm_debug_instance_set_linear_mem(
instance, WASM_ADDR_OFFSET(addr), buf, size);
break;
case WasmObj:
default:
return false;
}
}
WASMDebugInstance *
wasm_exec_env_get_instance(WASMExecEnv *exec_env)
{
WASMDebugInstance *instance = NULL;
if (!g_debug_engine) {
return NULL;
}
os_mutex_lock(&g_debug_engine->instance_list_lock);
instance = bh_list_first_elem(&g_debug_engine->debug_instance_list);
while (instance) {
if (instance->cluster == exec_env->cluster)
break;
instance = bh_list_elem_next(instance);
}
os_mutex_unlock(&g_debug_engine->instance_list_lock);
return instance;
}
uint32
wasm_debug_instance_get_call_stack_pcs(WASMDebugInstance *instance,
korp_tid tid, uint64 buf[], uint64 size)
{
WASMExecEnv *exec_env;
struct WASMInterpFrame *frame;
uint32 i = 0;
if (!instance)
return 0;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
while (exec_env) {
if (exec_env->handle == tid) {
WASMModuleInstance *module_inst =
(WASMModuleInstance *)exec_env->module_inst;
frame = exec_env->cur_frame;
while (frame && i < size) {
if (frame->ip != NULL) {
buf[i++] =
WASM_ADDR(WasmObj, instance->id,
(frame->ip - module_inst->module->load_addr));
}
frame = frame->prev_frame;
}
return i;
}
exec_env = bh_list_elem_next(exec_env);
}
return 0;
}
bool
wasm_debug_instance_add_breakpoint(WASMDebugInstance *instance, uint64 addr,
uint64 length)
{
WASMExecEnv *exec_env;
WASMModuleInstance *module_inst;
uint64 offset;
if (!instance)
return false;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
if (WASM_ADDR_TYPE(addr) != WasmObj)
return false;
offset = WASM_ADDR_OFFSET(addr);
if (length >= sizeof(break_instr)) {
if (offset + sizeof(break_instr) <= module_inst->module->load_size) {
WASMDebugBreakPoint *breakpoint;
if (!(breakpoint =
wasm_runtime_malloc(sizeof(WASMDebugBreakPoint)))) {
LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
return false;
}
memset(breakpoint, 0, sizeof(WASMDebugBreakPoint));
breakpoint->addr = offset;
/* TODO: how to if more than one breakpoints are set
at the same addr? */
bh_memcpy_s(&breakpoint->orignal_data, (uint32)sizeof(break_instr),
module_inst->module->load_addr + offset,
(uint32)sizeof(break_instr));
bh_memcpy_s(module_inst->module->load_addr + offset,
(uint32)sizeof(break_instr), break_instr,
(uint32)sizeof(break_instr));
bh_list_insert(&instance->break_point_list, breakpoint);
return true;
}
}
return false;
}
bool
wasm_debug_instance_remove_breakpoint(WASMDebugInstance *instance, uint64 addr,
uint64 length)
{
WASMExecEnv *exec_env;
WASMModuleInstance *module_inst;
uint64 offset;
if (!instance)
return false;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
if (WASM_ADDR_TYPE(addr) != WasmObj)
return false;
offset = WASM_ADDR_OFFSET(addr);
if (length >= sizeof(break_instr)) {
if (offset + sizeof(break_instr) <= module_inst->module->load_size) {
WASMDebugBreakPoint *breakpoint =
bh_list_first_elem(&instance->break_point_list);
while (breakpoint) {
WASMDebugBreakPoint *next_break = bh_list_elem_next(breakpoint);
if (breakpoint->addr == offset) {
/* TODO: how to if more than one breakpoints are set
at the same addr? */
bh_memcpy_s(module_inst->module->load_addr + offset,
(uint32)sizeof(break_instr),
&breakpoint->orignal_data,
(uint32)sizeof(break_instr));
bh_list_remove(&instance->break_point_list, breakpoint);
wasm_runtime_free(breakpoint);
}
breakpoint = next_break;
}
}
}
return true;
}
static bool
add_watchpoint(bh_list *list, uint64 addr, uint64 length)
{
WASMDebugWatchPoint *watchpoint;
if (!(watchpoint = wasm_runtime_malloc(sizeof(WASMDebugWatchPoint)))) {
LOG_ERROR("WASM Debug Engine error: failed to allocate memory for "
"watchpoint");
return false;
}
memset(watchpoint, 0, sizeof(WASMDebugWatchPoint));
watchpoint->addr = addr;
watchpoint->length = length;
bh_list_insert(list, watchpoint);
return true;
}
static bool
remove_watchpoint(bh_list *list, uint64 addr, uint64 length)
{
WASMDebugWatchPoint *watchpoint = bh_list_first_elem(list);
while (watchpoint) {
WASMDebugWatchPoint *next = bh_list_elem_next(watchpoint);
if (watchpoint->addr == addr && watchpoint->length == length) {
bh_list_remove(list, watchpoint);
wasm_runtime_free(watchpoint);
}
watchpoint = next;
}
return true;
}
bool
wasm_debug_instance_watchpoint_write_add(WASMDebugInstance *instance,
uint64 addr, uint64 length)
{
return add_watchpoint(&instance->watch_point_list_write, addr, length);
}
bool
wasm_debug_instance_watchpoint_write_remove(WASMDebugInstance *instance,
uint64 addr, uint64 length)
{
return remove_watchpoint(&instance->watch_point_list_write, addr, length);
}
bool
wasm_debug_instance_watchpoint_read_add(WASMDebugInstance *instance,
uint64 addr, uint64 length)
{
return add_watchpoint(&instance->watch_point_list_read, addr, length);
}
bool
wasm_debug_instance_watchpoint_read_remove(WASMDebugInstance *instance,
uint64 addr, uint64 length)
{
return remove_watchpoint(&instance->watch_point_list_read, addr, length);
}
bool
wasm_debug_instance_on_failure(WASMDebugInstance *instance)
{
WASMExecEnv *exec_env;
if (!instance)
return false;
os_mutex_lock(&instance->wait_lock);
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env) {
os_mutex_unlock(&instance->wait_lock);
return false;
}
if (instance->stopped_thread == NULL
&& instance->current_state == DBG_LAUNCHING) {
/* if fail in start stage: may need wait for main thread to notify it */
os_cond_wait(&instance->wait_cond, &instance->wait_lock);
}
instance->current_state = DBG_ERROR;
instance->stopped_thread = NULL;
/* terminate the wasm execution thread */
while (exec_env) {
/* Resume all threads so they can receive the TERM signal */
os_mutex_lock(&exec_env->wait_lock);
wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TERM);
exec_env->current_status->running_status = STATUS_RUNNING;
os_cond_signal(&exec_env->wait_cond);
os_mutex_unlock(&exec_env->wait_lock);
exec_env = bh_list_elem_next(exec_env);
}
os_mutex_unlock(&instance->wait_lock);
return true;
}
bool
wasm_debug_instance_continue(WASMDebugInstance *instance)
{
WASMExecEnv *exec_env;
if (!instance)
return false;
if (instance->current_state == APP_RUNNING) {
LOG_VERBOSE("Already in running state, ignore continue request");
return false;
}
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
while (exec_env) {
wasm_cluster_thread_continue(exec_env);
exec_env = bh_list_elem_next(exec_env);
}
instance->current_state = APP_RUNNING;
return true;
}
bool
wasm_debug_instance_interrupt_all_threads(WASMDebugInstance *instance)
{
WASMExecEnv *exec_env;
if (!instance)
return false;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
while (exec_env) {
wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TRAP);
exec_env = bh_list_elem_next(exec_env);
}
return true;
}
bool
wasm_debug_instance_detach(WASMDebugInstance *instance)
{
WASMExecEnv *exec_env;
if (!instance)
return false;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
wasm_gdbserver_detach(instance->control_thread->server);
while (exec_env) {
if (instance->current_state == APP_STOPPED) {
/* Resume all threads since remote debugger detached*/
wasm_cluster_thread_continue(exec_env);
}
exec_env = bh_list_elem_next(exec_env);
}
/* relaunch, accept new debug connection */
instance->current_state = DBG_LAUNCHING;
instance->control_thread->status = DETACHED;
instance->stopped_thread = NULL;
return true;
}
bool
wasm_debug_instance_kill(WASMDebugInstance *instance)
{
WASMExecEnv *exec_env;
if (!instance)
return false;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
while (exec_env) {
wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TERM);
if (instance->current_state == APP_STOPPED) {
/* Resume all threads so they can receive the TERM signal */
os_mutex_lock(&exec_env->wait_lock);
exec_env->current_status->running_status = STATUS_RUNNING;
os_cond_signal(&exec_env->wait_cond);
os_mutex_unlock(&exec_env->wait_lock);
}
exec_env = bh_list_elem_next(exec_env);
}
instance->current_state = APP_RUNNING;
return true;
}
bool
wasm_debug_instance_singlestep(WASMDebugInstance *instance, korp_tid tid)
{
WASMExecEnv *exec_env;
if (!instance)
return false;
if (instance->current_state == APP_RUNNING) {
LOG_VERBOSE("Already in running state, ignore step request");
return false;
}
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
while (exec_env) {
if (exec_env->handle == tid || tid == (korp_tid)(uintptr_t)~0LL) {
wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_SINGSTEP);
wasm_cluster_thread_step(exec_env);
}
exec_env = bh_list_elem_next(exec_env);
}
instance->current_state = APP_RUNNING;
return true;
}
bool
wasm_debug_instance_get_local(WASMDebugInstance *instance, int32 frame_index,
int32 local_index, char buf[], int32 *size)
{
WASMExecEnv *exec_env;
struct WASMInterpFrame *frame;
WASMFunctionInstance *cur_func;
uint8 local_type = 0xFF;
uint32 local_offset;
int32 param_count;
int32 fi = 0;
if (!instance)
return false;
exec_env = wasm_debug_instance_get_current_env(instance);
if (!exec_env)
return false;
frame = exec_env->cur_frame;
while (frame && fi++ != frame_index) {
frame = frame->prev_frame;
}
if (!frame)
return false;
cur_func = frame->function;
if (!cur_func)
return false;
param_count = cur_func->param_count;
if (local_index >= param_count + cur_func->local_count)
return false;
local_offset = cur_func->local_offsets[local_index];
if (local_index < param_count)
local_type = cur_func->param_types[local_index];
else if (local_index < cur_func->local_count + param_count)
local_type = cur_func->local_types[local_index - param_count];
switch (local_type) {
case VALUE_TYPE_I32:
case VALUE_TYPE_F32:
*size = 4;
bh_memcpy_s(buf, 4, (char *)(frame->lp + local_offset), 4);
break;
case VALUE_TYPE_I64:
case VALUE_TYPE_F64:
*size = 8;
bh_memcpy_s(buf, 8, (char *)(frame->lp + local_offset), 8);
break;
default:
*size = 0;
break;
}
return true;
}
bool
wasm_debug_instance_get_global(WASMDebugInstance *instance, int32 frame_index,
int32 global_index, char buf[], int32 *size)
{
WASMExecEnv *exec_env;
struct WASMInterpFrame *frame;
WASMModuleInstance *module_inst;
WASMGlobalInstance *globals, *global;
uint8 *global_addr;
uint8 global_type = 0xFF;
uint8 *global_data;
int32 fi = 0;
if (!instance)
return false;
exec_env = wasm_debug_instance_get_current_env(instance);
if (!exec_env)
return false;
frame = exec_env->cur_frame;
while (frame && fi++ != frame_index) {
frame = frame->prev_frame;
}
if (!frame)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
global_data = module_inst->global_data;
globals = module_inst->e->globals;
if ((global_index < 0)
|| ((uint32)global_index >= module_inst->e->global_count)) {
return false;
}
global = globals + global_index;
#if WASM_ENABLE_MULTI_MODULE == 0
global_addr = global_data + global->data_offset;
#else
global_addr = global->import_global_inst
? global->import_module_inst->global_data
+ global->import_global_inst->data_offset
: global_data + global->data_offset;
#endif
global_type = global->type;
switch (global_type) {
case VALUE_TYPE_I32:
case VALUE_TYPE_F32:
*size = 4;
bh_memcpy_s(buf, 4, (char *)(global_addr), 4);
break;
case VALUE_TYPE_I64:
case VALUE_TYPE_F64:
*size = 8;
bh_memcpy_s(buf, 8, (char *)(global_addr), 8);
break;
default:
*size = 0;
break;
}
return true;
}
uint64
wasm_debug_instance_mmap(WASMDebugInstance *instance, uint32 size,
int32 map_prot)
{
WASMExecEnv *exec_env;
uint32 offset = 0;
(void)map_prot;
if (!instance)
return 0;
exec_env = wasm_debug_instance_get_current_env(instance);
if (!exec_env)
return 0;
if (instance->exec_mem_info.start_offset == 0) {
return 0;
}
if (instance->exec_mem_info.current_pos
- instance->exec_mem_info.start_offset + size
<= (uint64)instance->exec_mem_info.size) {
offset = instance->exec_mem_info.current_pos;
instance->exec_mem_info.current_pos += size;
}
if (offset == 0) {
LOG_WARNING("the memory may be not enough for debug, try use larger "
"--heap-size");
return 0;
}
return WASM_ADDR(WasmMemory, 0, offset);
}
bool
wasm_debug_instance_ummap(WASMDebugInstance *instance, uint64 addr)
{
WASMExecEnv *exec_env;
if (!instance)
return false;
exec_env = wasm_debug_instance_get_current_env(instance);
if (!exec_env)
return false;
if (instance->exec_mem_info.start_offset == 0) {
return false;
}
(void)addr;
/* Currently we don't support to free the execution memory, simply return
* true here */
return true;
}