Implement source debugging for interpreter and AOT (#769)
Implement source debugging feature for classic interpreter and AOT: - use `cmake -DWAMR_BUILD_DEBUG_INTERP=1` to enable interpreter debugging - use `cmake -DWAMR_BUILD_DEBUG_AOT=1` to enable AOT debugging See doc/source_debugging.md for more details.
This commit is contained in:
152
core/iwasm/aot/debug/elf_parser.c
Normal file
152
core/iwasm/aot/debug/elf_parser.c
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Ant Group. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
#include "aot_runtime.h"
|
||||
#include "bh_log.h"
|
||||
#include "elf_parser.h"
|
||||
|
||||
bool
|
||||
is_ELF(void *buf)
|
||||
{
|
||||
Elf32_Ehdr *eh = (Elf32_Ehdr *)buf;
|
||||
if (!strncmp((char *)eh->e_ident, "\177ELF", 4)) {
|
||||
LOG_VERBOSE("the buffer is ELF entry!");
|
||||
return true;
|
||||
}
|
||||
LOG_VERBOSE("the buffer is not ELF entry!");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
is64Bit(Elf32_Ehdr *eh)
|
||||
{
|
||||
if (eh->e_ident[EI_CLASS] == ELFCLASS64)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
is32Bit(Elf32_Ehdr *eh)
|
||||
{
|
||||
if (eh->e_ident[EI_CLASS] == ELFCLASS32)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
is_ELF64(void *buf)
|
||||
{
|
||||
Elf64_Ehdr *eh = (Elf64_Ehdr *)buf;
|
||||
if (!strncmp((char *)eh->e_ident, "\177ELF", 4)) {
|
||||
LOG_VERBOSE("the buffer is ELF entry!");
|
||||
return true;
|
||||
}
|
||||
LOG_VERBOSE("the buffer is not ELF entry!");
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
read_section_header_table(Elf32_Ehdr *eh, Elf32_Shdr *sh_table[])
|
||||
{
|
||||
uint32_t i;
|
||||
char *buf = (char *)eh;
|
||||
buf += eh->e_shoff;
|
||||
LOG_VERBOSE("str index = %d count=%d", eh->e_shstrndx, eh->e_shnum);
|
||||
for (i = 0; i < eh->e_shnum; i++) {
|
||||
sh_table[i] = (Elf32_Shdr *)buf;
|
||||
buf += eh->e_shentsize;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
read_section_header_table64(Elf64_Ehdr *eh, Elf64_Shdr *sh_table[])
|
||||
{
|
||||
uint32_t i;
|
||||
char *buf = (char *)eh;
|
||||
buf += eh->e_shoff;
|
||||
|
||||
for (i = 0; i < eh->e_shnum; i++) {
|
||||
sh_table[i] = (Elf64_Shdr *)buf;
|
||||
buf += eh->e_shentsize;
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
get_section(Elf32_Ehdr *eh, Elf32_Shdr *section_header)
|
||||
{
|
||||
char *buf = (char *)eh;
|
||||
return buf + section_header->sh_offset;
|
||||
}
|
||||
|
||||
static char *
|
||||
get_section64(Elf64_Ehdr *eh, Elf64_Shdr *section_header)
|
||||
{
|
||||
char *buf = (char *)eh;
|
||||
return buf + section_header->sh_offset;
|
||||
}
|
||||
|
||||
bool
|
||||
get_text_section(void *buf, uint64_t *offset, uint64_t *size)
|
||||
{
|
||||
bool ret = false;
|
||||
uint32 i;
|
||||
char *sh_str;
|
||||
|
||||
if (is64Bit(buf)) {
|
||||
Elf64_Ehdr *eh = (Elf64_Ehdr *)buf;
|
||||
Elf64_Shdr **sh_table =
|
||||
wasm_runtime_malloc(eh->e_shnum * sizeof(Elf64_Shdr *));
|
||||
if (sh_table) {
|
||||
read_section_header_table64(eh, sh_table);
|
||||
sh_str = get_section64(eh, sh_table[eh->e_shstrndx]);
|
||||
for (i= 0; i < eh->e_shnum; i++) {
|
||||
if (!strcmp(sh_str + sh_table[i]->sh_name, ".text")) {
|
||||
*offset = sh_table[i]->sh_offset;
|
||||
*size = sh_table[i]->sh_size;
|
||||
sh_table[i]->sh_addr = (Elf64_Addr)(uintptr_t)
|
||||
((char *)buf + sh_table[i]->sh_offset);
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
wasm_runtime_free(sh_table);
|
||||
}
|
||||
}
|
||||
else if (is32Bit(buf)) {
|
||||
Elf32_Ehdr *eh = (Elf32_Ehdr *)buf;
|
||||
Elf32_Shdr **sh_table =
|
||||
wasm_runtime_malloc(eh->e_shnum * sizeof(Elf32_Shdr *));
|
||||
if (sh_table) {
|
||||
read_section_header_table(eh, sh_table);
|
||||
sh_str = get_section(eh, sh_table[eh->e_shstrndx]);
|
||||
for (i= 0; i < eh->e_shnum; i++) {
|
||||
if (!strcmp(sh_str + sh_table[i]->sh_name, ".text")) {
|
||||
*offset = sh_table[i]->sh_offset;
|
||||
*size = sh_table[i]->sh_size;
|
||||
sh_table[i]->sh_addr = (Elf32_Addr)(uintptr_t)
|
||||
((char *)buf + sh_table[i]->sh_offset);
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
wasm_runtime_free(sh_table);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
27
core/iwasm/aot/debug/elf_parser.h
Normal file
27
core/iwasm/aot/debug/elf_parser.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Ant Group. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#ifndef _ELF_PARSERE_H_
|
||||
#define _ELF_PARSER_H_
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool
|
||||
is_ELF(void *buf);
|
||||
|
||||
bool
|
||||
is_ELF64(void *buf);
|
||||
|
||||
bool
|
||||
get_text_section(void *buf, uint64_t *offset, uint64_t *size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
253
core/iwasm/aot/debug/jit_debug.c
Normal file
253
core/iwasm/aot/debug/jit_debug.c
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (C) 2021 Ant Group. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#include "bh_log.h"
|
||||
#include "bh_platform.h"
|
||||
#include "wasm_runtime.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* This must be kept in sync with gdb/gdb/jit.h */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
JIT_NOACTION = 0,
|
||||
JIT_REGISTER_FN,
|
||||
JIT_UNREGISTER_FN
|
||||
} JITAction;
|
||||
|
||||
typedef struct JITCodeEntry {
|
||||
struct JITCodeEntry *next_;
|
||||
struct JITCodeEntry *prev_;
|
||||
const uint8 *symfile_addr_;
|
||||
uint64 symfile_size_;
|
||||
} JITCodeEntry;
|
||||
|
||||
typedef struct JITDescriptor {
|
||||
uint32 version_;
|
||||
uint32 action_flag_;
|
||||
JITCodeEntry *relevant_entry_;
|
||||
JITCodeEntry *first_entry_;
|
||||
} JITDescriptor;
|
||||
|
||||
/* LLVM has already define this */
|
||||
#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0)
|
||||
/**
|
||||
* GDB will place breakpoint into this function.
|
||||
* To prevent GCC from inlining or removing it we place noinline attribute
|
||||
* and inline assembler statement inside.
|
||||
*/
|
||||
void __attribute__((noinline)) __jit_debug_register_code();
|
||||
|
||||
void __attribute__((noinline)) __jit_debug_register_code()
|
||||
{
|
||||
int x;
|
||||
*(char *)&x = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* GDB will inspect contents of this descriptor.
|
||||
* Static initialization is necessary to prevent GDB from seeing
|
||||
* uninitialized descriptor.
|
||||
*/
|
||||
|
||||
JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, NULL, NULL };
|
||||
#else
|
||||
extern void __jit_debug_register_code();
|
||||
extern JITDescriptor __jit_debug_descriptor;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Call __jit_debug_register_code indirectly via global variable.
|
||||
* This gives the debugger an easy way to inject custom code to
|
||||
* handle the events.
|
||||
*/
|
||||
void (*__jit_debug_register_code_ptr)() = __jit_debug_register_code;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef struct WASMJITDebugEngine {
|
||||
korp_mutex jit_entry_lock;
|
||||
bh_list jit_entry_list;
|
||||
} WASMJITDebugEngine;
|
||||
|
||||
typedef struct WASMJITEntryNode {
|
||||
struct WASMJITEntryNode *next;
|
||||
JITCodeEntry *entry;
|
||||
} WASMJITEntryNode;
|
||||
|
||||
static WASMJITDebugEngine *jit_debug_engine;
|
||||
|
||||
static JITCodeEntry *
|
||||
CreateJITCodeEntryInternal(const uint8 *symfile_addr, uint64 symfile_size)
|
||||
{
|
||||
JITCodeEntry *entry;
|
||||
|
||||
os_mutex_lock(&jit_debug_engine->jit_entry_lock);
|
||||
|
||||
if (!(entry = wasm_runtime_malloc(sizeof(JITCodeEntry)))) {
|
||||
LOG_ERROR("WASM JIT Debug Engine error: failed to allocate memory");
|
||||
os_mutex_unlock(&jit_debug_engine->jit_entry_lock);
|
||||
return NULL;
|
||||
}
|
||||
entry->symfile_addr_ = symfile_addr;
|
||||
entry->symfile_size_ = symfile_size;
|
||||
entry->prev_ = NULL;
|
||||
|
||||
entry->next_ = __jit_debug_descriptor.first_entry_;
|
||||
if (entry->next_ != NULL) {
|
||||
entry->next_->prev_ = entry;
|
||||
}
|
||||
__jit_debug_descriptor.first_entry_ = entry;
|
||||
__jit_debug_descriptor.relevant_entry_ = entry;
|
||||
|
||||
__jit_debug_descriptor.action_flag_ = JIT_REGISTER_FN;
|
||||
|
||||
(*__jit_debug_register_code_ptr)();
|
||||
|
||||
os_mutex_unlock(&jit_debug_engine->jit_entry_lock);
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void
|
||||
DestroyJITCodeEntryInternal(JITCodeEntry *entry)
|
||||
{
|
||||
os_mutex_lock(&jit_debug_engine->jit_entry_lock);
|
||||
|
||||
if (entry->prev_ != NULL) {
|
||||
entry->prev_->next_ = entry->next_;
|
||||
}
|
||||
else {
|
||||
__jit_debug_descriptor.first_entry_ = entry->next_;
|
||||
}
|
||||
|
||||
if (entry->next_ != NULL) {
|
||||
entry->next_->prev_ = entry->prev_;
|
||||
}
|
||||
|
||||
__jit_debug_descriptor.relevant_entry_ = entry;
|
||||
__jit_debug_descriptor.action_flag_ = JIT_UNREGISTER_FN;
|
||||
(*__jit_debug_register_code_ptr)();
|
||||
|
||||
wasm_runtime_free(entry);
|
||||
|
||||
os_mutex_unlock(&jit_debug_engine->jit_entry_lock);
|
||||
}
|
||||
|
||||
bool
|
||||
jit_debug_engine_init()
|
||||
{
|
||||
if (jit_debug_engine) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(jit_debug_engine =
|
||||
wasm_runtime_malloc(sizeof(WASMJITDebugEngine)))) {
|
||||
LOG_ERROR("WASM JIT Debug Engine error: failed to allocate memory");
|
||||
return false;
|
||||
}
|
||||
memset(jit_debug_engine, 0, sizeof(WASMJITDebugEngine));
|
||||
|
||||
if (os_mutex_init(&jit_debug_engine->jit_entry_lock) != 0) {
|
||||
wasm_runtime_free(jit_debug_engine);
|
||||
jit_debug_engine = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
bh_list_init(&jit_debug_engine->jit_entry_list);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
jit_debug_engine_destroy()
|
||||
{
|
||||
if (jit_debug_engine) {
|
||||
WASMJITEntryNode *node, *node_next;
|
||||
|
||||
/* Destroy all nodes */
|
||||
node = bh_list_first_elem(&jit_debug_engine->jit_entry_list);
|
||||
while (node) {
|
||||
node_next = bh_list_elem_next(node);
|
||||
DestroyJITCodeEntryInternal(node->entry);
|
||||
bh_list_remove(&jit_debug_engine->jit_entry_list, node);
|
||||
wasm_runtime_free(node);
|
||||
node = node_next;
|
||||
}
|
||||
|
||||
/* Destroy JIT Debug Engine */
|
||||
os_mutex_destroy(&jit_debug_engine->jit_entry_lock);
|
||||
wasm_runtime_free(jit_debug_engine);
|
||||
jit_debug_engine = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
jit_code_entry_create(const uint8 *symfile_addr, uint64 symfile_size)
|
||||
{
|
||||
JITCodeEntry *entry;
|
||||
WASMJITEntryNode *node;
|
||||
|
||||
if (!(node = wasm_runtime_malloc(sizeof(WASMJITEntryNode)))) {
|
||||
LOG_ERROR("WASM JIT Debug Engine error: failed to allocate memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
entry = CreateJITCodeEntryInternal(symfile_addr, symfile_size);
|
||||
|
||||
if (!entry) {
|
||||
wasm_runtime_free(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
node->entry = entry;
|
||||
os_mutex_lock(&jit_debug_engine->jit_entry_lock);
|
||||
bh_list_insert(&jit_debug_engine->jit_entry_list, node);
|
||||
os_mutex_unlock(&jit_debug_engine->jit_entry_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
jit_code_entry_destroy(const uint8 *symfile_addr)
|
||||
{
|
||||
WASMJITEntryNode *node;
|
||||
|
||||
node = bh_list_first_elem(&jit_debug_engine->jit_entry_list);
|
||||
while (node) {
|
||||
WASMJITEntryNode *next_node = bh_list_elem_next(node);
|
||||
if (node->entry->symfile_addr_ == symfile_addr) {
|
||||
DestroyJITCodeEntryInternal(node->entry);
|
||||
os_mutex_lock(&jit_debug_engine->jit_entry_lock);
|
||||
bh_list_remove(&jit_debug_engine->jit_entry_list, node);
|
||||
os_mutex_unlock(&jit_debug_engine->jit_entry_lock);
|
||||
wasm_runtime_free(node);
|
||||
}
|
||||
node = next_node;
|
||||
}
|
||||
}
|
||||
29
core/iwasm/aot/debug/jit_debug.h
Normal file
29
core/iwasm/aot/debug/jit_debug.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Ant Group. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#ifndef _JIT_DEBUG_H_
|
||||
#define _JIT_DEBUG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool
|
||||
jit_debug_engine_init();
|
||||
|
||||
void
|
||||
jit_debug_engine_destroy();
|
||||
|
||||
bool
|
||||
jit_code_entry_create(const uint8 *symfile_addr, uint64 symfile_size);
|
||||
|
||||
void
|
||||
jit_code_entry_destroy(const uint8 *symfile_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user