diff --git a/.github/workflows/build_wamr_lldb.yml b/.github/workflows/build_wamr_lldb.yml index ba491ad3a..c376506ed 100644 --- a/.github/workflows/build_wamr_lldb.yml +++ b/.github/workflows/build_wamr_lldb.yml @@ -36,6 +36,11 @@ jobs: needs: try_reuse if: needs.try_reuse.outputs.result != 'hit' runs-on: ${{ inputs.runner }} + + env: + PYTHON_VERSION: '3.10' + PYTHON_UBUNTU_STANDALONE_BUILD: https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.10.11+20230507-x86_64-unknown-linux-gnu-install_only.tar.gz + PYTHON_MACOS_STANDALONE_BUILD: https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.10.11+20230507-x86_64-apple-darwin-install_only.tar.gz steps: - uses: actions/checkout@v3 @@ -63,10 +68,12 @@ jobs: - name: install utils macos if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'macos') run: | - brew install swig cmake ninja libedit + brew remove swig + brew install swig@3 cmake ninja libedit + brew link --overwrite swig@3 sudo rm -rf /Library/Developer/CommandLineTools - - name: intsall utils ubuntu + - name: install utils ubuntu if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'ubuntu') run: sudo apt update && sudo apt-get install -y lld ninja-build @@ -88,6 +95,20 @@ jobs: git apply ../../../build-scripts/lldb-wasm.patch working-directory: core/deps/llvm-project + - name: get stand-alone python ubuntu + if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'ubuntu') + run: | + wget ${{ env.PYTHON_UBUNTU_STANDALONE_BUILD }} -O python.tar.gz + tar -xvf python.tar.gz + working-directory: core/deps + + - name: get stand-alone python macos + if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'macos') + run: | + wget ${{ env.PYTHON_MACOS_STANDALONE_BUILD }} -O python.tar.gz + tar -xvf python.tar.gz + working-directory: core/deps + - name: build lldb ubuntu if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'ubuntu') run: | @@ -102,17 +123,21 @@ jobs: -DLLVM_TARGETS_TO_BUILD:STRING="X86;WebAssembly" \ -DLLVM_BUILD_BENCHMARKS:BOOL=OFF \ -DLLVM_BUILD_DOCS:BOOL=OFF \ - -DLLVM_BUILD_EXAMPLES:BOOL=OFF \ + -DLLVM_BUILD_EXAMPLES:BOOL=OFF \ -DLLVM_BUILD_LLVM_DYLIB:BOOL=OFF \ - -DLLVM_BUILD_TESTS:BOOL=OFF \ - -DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF \ + -DLLVM_BUILD_TESTS:BOOL=OFF \ + -DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF \ -DLLVM_INCLUDE_DOCS:BOOL=OFF \ -DLLVM_INCLUDE_EXAMPLES:BOOL=OFF \ -DLLVM_INCLUDE_TESTS:BOOL=OFF \ -DLLVM_ENABLE_BINDINGS:BOOL=OFF \ -DLLVM_ENABLE_LIBXML2:BOOL=ON \ - -DLLDB_ENABLE_PYTHON:BOOL=OFF \ - -DLLVM_ENABLE_LLD:BOOL=ON + -DLLVM_ENABLE_LLD:BOOL=ON \ + -DLLDB_ENABLE_PYTHON:BOOL=ON \ + -DLLDB_EMBED_PYTHON_HOME=ON \ + -DLLDB_PYTHON_HOME=.. \ + -DLLDB_PYTHON_RELATIVE_PATH=lib/lldb-python \ + -DPython3_EXECUTABLE="$(pwd)/../python/bin/python${{ env.PYTHON_VERSION }}" cmake --build build --target lldb install --parallel $(nproc) working-directory: core/deps/llvm-project @@ -130,20 +155,21 @@ jobs: -DLLVM_TARGETS_TO_BUILD:STRING="X86;WebAssembly" \ -DLLVM_BUILD_BENCHMARKS:BOOL=OFF \ -DLLVM_BUILD_DOCS:BOOL=OFF \ - -DLLVM_BUILD_EXAMPLES:BOOL=OFF \ + -DLLVM_BUILD_EXAMPLES:BOOL=OFF \ -DLLVM_BUILD_LLVM_DYLIB:BOOL=OFF \ - -DLLVM_BUILD_TESTS:BOOL=OFF \ - -DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF \ + -DLLVM_BUILD_TESTS:BOOL=OFF \ + -DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF \ -DLLVM_INCLUDE_DOCS:BOOL=OFF \ -DLLVM_INCLUDE_EXAMPLES:BOOL=OFF \ -DLLVM_INCLUDE_TESTS:BOOL=OFF \ - -DLLVM_BUILD_BENCHMARKS:BOOL=OFF \ - -DLLVM_BUILD_DOCS:BOOL=OFF \ - -DLLVM_BUILD_LLVM_DYLIB:BOOL=OFF \ -DLLVM_ENABLE_BINDINGS:BOOL=OFF \ -DLLVM_ENABLE_LIBXML2:BOOL=ON \ - -DLLDB_ENABLE_PYTHON:BOOL=OFF \ - -DLLDB_BUILD_FRAMEWORK:BOOL=OFF + -DLLDB_BUILD_FRAMEWORK:BOOL=OFF \ + -DLLDB_ENABLE_PYTHON:BOOL=ON \ + -DLLDB_EMBED_PYTHON_HOME=ON \ + -DLLDB_PYTHON_HOME=.. \ + -DLLDB_PYTHON_RELATIVE_PATH=lib/lldb-python \ + -DPython3_EXECUTABLE="$(pwd)/../python/bin/python${{ env.PYTHON_VERSION }}" cmake --build build --target lldb install --parallel $(nproc) working-directory: core/deps/llvm-project @@ -162,12 +188,20 @@ jobs: run: | cp build/lib/liblldb*.so wamr-lldb/lib cp build/lib/liblldb*.so.* wamr-lldb/lib + cp -R build/lib/lldb-python wamr-lldb/lib + cp -R ../python/lib/python* wamr-lldb/lib + cp ../python/lib/libpython${{ env.PYTHON_VERSION }}.so.1.0 wamr-lldb/lib working-directory: core/deps/llvm-project - name: pack macos specific libraries if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'macos') run: | cp build/lib/liblldb*.dylib wamr-lldb/lib + cp -R build/lib/lldb-python wamr-lldb/lib + cp -R ../python/lib/python* wamr-lldb/lib + cp ../python/lib/libpython*.dylib wamr-lldb/lib + install_name_tool -change /install/lib/libpython${{ env.PYTHON_VERSION }}.dylib @rpath/libpython${{ env.PYTHON_VERSION }}.dylib wamr-lldb/lib/liblldb.*.dylib + # Patch path of python library -> https://github.com/indygreg/python-build-standalone/blob/85923ca3911784e6978b85d56e06e9ae75cb2dc4/docs/quirks.rst?plain=1#L412-L446 working-directory: core/deps/llvm-project - name: compress the binary diff --git a/test-tools/wamr-ide/VSCode-Extension/formatters/rust.py b/test-tools/wamr-ide/VSCode-Extension/formatters/rust.py new file mode 100644 index 000000000..6fd5f1682 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/formatters/rust.py @@ -0,0 +1,747 @@ +''' +Copyright (c) 2016 Vadim Chugunov +SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +''' + +from __future__ import print_function, division +import sys +import logging +import lldb +import weakref + +if sys.version_info[0] == 2: + # python2-based LLDB accepts utf8-encoded ascii strings only. + def to_lldb_str(s): return s.encode('utf8', 'backslashreplace') if isinstance(s, unicode) else s + range = xrange +else: + to_lldb_str = str + +log = logging.getLogger(__name__) + +module = sys.modules[__name__] +rust_category = None + + +def initialize_category(debugger, internal_dict): + global module, rust_category + + rust_category = debugger.CreateCategory('Rust') + # rust_category.AddLanguage(lldb.eLanguageTypeRust) + rust_category.SetEnabled(True) + + attach_summary_to_type(tuple_summary_provider, r'^\(.*\)$', True) + attach_synthetic_to_type(MsvcTupleSynthProvider, r'^tuple\$?<.+>$', + True) # *-windows-msvc uses this name since 1.47 + + attach_synthetic_to_type(StrSliceSynthProvider, '&str') + attach_synthetic_to_type(StrSliceSynthProvider, 'str*') + attach_synthetic_to_type(StrSliceSynthProvider, 'str') # *-windows-msvc uses this name since 1.5? + + attach_synthetic_to_type(StdStringSynthProvider, '^(collections|alloc)::string::String$', True) + attach_synthetic_to_type(StdVectorSynthProvider, r'^(collections|alloc)::vec::Vec<.+>$', True) + attach_synthetic_to_type(StdVecDequeSynthProvider, + r'^(collections|alloc::collections)::vec_deque::VecDeque<.+>$', True) + + attach_synthetic_to_type(MsvcEnumSynthProvider, r'^enum\$<.+>$', True) + attach_synthetic_to_type(MsvcEnum2SynthProvider, r'^enum2\$<.+>$', True) + + attach_synthetic_to_type(SliceSynthProvider, r'^&(mut *)?\[.*\]$', True) + attach_synthetic_to_type(MsvcSliceSynthProvider, r'^(mut *)?slice\$?<.+>.*$', True) + + attach_synthetic_to_type(StdCStringSynthProvider, '^(std|alloc)::ffi::c_str::CString$', True) + attach_synthetic_to_type(StdCStrSynthProvider, '^&?(std|core)::ffi::c_str::CStr$', True) + + attach_synthetic_to_type(StdOsStringSynthProvider, 'std::ffi::os_str::OsString') + attach_synthetic_to_type(StdOsStrSynthProvider, '^&?std::ffi::os_str::OsStr', True) + + attach_synthetic_to_type(StdPathBufSynthProvider, 'std::path::PathBuf') + attach_synthetic_to_type(StdPathSynthProvider, '^&?std::path::Path', True) + + attach_synthetic_to_type(StdRcSynthProvider, r'^alloc::rc::Rc<.+>$', True) + attach_synthetic_to_type(StdRcSynthProvider, r'^alloc::rc::Weak<.+>$', True) + attach_synthetic_to_type(StdArcSynthProvider, r'^alloc::(sync|arc)::Arc<.+>$', True) + attach_synthetic_to_type(StdArcSynthProvider, r'^alloc::(sync|arc)::Weak<.+>$', True) + attach_synthetic_to_type(StdMutexSynthProvider, r'^std::sync::mutex::Mutex<.+>$', True) + + attach_synthetic_to_type(StdCellSynthProvider, r'^core::cell::Cell<.+>$', True) + attach_synthetic_to_type(StdRefCellSynthProvider, r'^core::cell::RefCell<.+>$', True) + attach_synthetic_to_type(StdRefCellBorrowSynthProvider, r'^core::cell::Ref<.+>$', True) + attach_synthetic_to_type(StdRefCellBorrowSynthProvider, r'^core::cell::RefMut<.+>$', True) + + attach_synthetic_to_type(StdHashMapSynthProvider, r'^std::collections::hash::map::HashMap<.+>$', True) + attach_synthetic_to_type(StdHashSetSynthProvider, r'^std::collections::hash::set::HashSet<.+>$', True) + + attach_synthetic_to_type(GenericEnumSynthProvider, r'^core::option::Option<.+>$', True) + attach_synthetic_to_type(GenericEnumSynthProvider, r'^core::result::Result<.+>$', True) + attach_synthetic_to_type(GenericEnumSynthProvider, r'^alloc::borrow::Cow<.+>$', True) + + if 'rust' in internal_dict.get('source_languages', []): + lldb.SBDebugger.SetInternalVariable('target.process.thread.step-avoid-regexp', + '^ +def read_unique_ptr(valobj): + pointer = valobj.GetChildMemberWithName('pointer') + if pointer.TypeIsPointerType(): # Between 1.33 and 1.63 pointer was just *const T + return pointer + return pointer.GetChildAtIndex(0) + + +def string_from_ptr(pointer, length): + if length <= 0: + return u'' + error = lldb.SBError() + process = pointer.GetProcess() + data = process.ReadMemory(pointer.GetValueAsUnsigned(), length, error) + if error.Success(): + return data.decode('utf8', 'replace') + else: + log.error('ReadMemory error: %s', error.GetCString()) + + +def get_template_params(type_name): + params = [] + level = 0 + start = 0 + for i, c in enumerate(type_name): + if c == '<': + level += 1 + if level == 1: + start = i + 1 + elif c == '>': + level -= 1 + if level == 0: + params.append(type_name[start:i].strip()) + elif c == ',' and level == 1: + params.append(type_name[start:i].strip()) + start = i + 1 + return params + + +def obj_summary(valobj, unavailable='{...}'): + summary = valobj.GetSummary() + if summary is not None: + return summary + summary = valobj.GetValue() + if summary is not None: + return summary + return unavailable + + +def sequence_summary(childern, maxsize=32): + s = '' + for child in childern: + if len(s) > 0: + s += ', ' + s += obj_summary(child) + if len(s) > maxsize: + s += ', ...' + break + return s + + +def tuple_summary(obj, skip_first=0): + fields = [obj_summary(obj.GetChildAtIndex(i)) for i in range(skip_first, obj.GetNumChildren())] + return '(%s)' % ', '.join(fields) + + +# ----- Summaries ----- + +def tuple_summary_provider(valobj, dict={}): + return tuple_summary(valobj) + + +# ----- Synth providers ------ + + +class RustSynthProvider(object): + synth_by_id = weakref.WeakValueDictionary() + next_id = 0 + + def __init__(self, valobj, dict={}): + self.valobj = valobj + self.obj_id = RustSynthProvider.next_id + RustSynthProvider.synth_by_id[self.obj_id] = self + RustSynthProvider.next_id += 1 + + def update(self): + return True + + def has_children(self): + return False + + def num_children(self): + return 0 + + def get_child_at_index(self, index): + return None + + def get_child_index(self, name): + if name == '$$object-id$$': + return self.obj_id + + try: + return self.get_index_of_child(name) + except Exception as e: + log.exception('%s', e) + raise + + def get_summary(self): + return None + + +class ArrayLikeSynthProvider(RustSynthProvider): + '''Base class for providers that represent array-like objects''' + + def update(self): + self.ptr, self.len = self.ptr_and_len(self.valobj) # type: ignore + self.item_type = self.ptr.GetType().GetPointeeType() + self.item_size = self.item_type.GetByteSize() + + def ptr_and_len(self, obj): + pass # abstract + + def num_children(self): + return self.len + + def has_children(self): + return True + + def get_child_at_index(self, index): + try: + if not 0 <= index < self.len: + return None + offset = index * self.item_size + return self.ptr.CreateChildAtOffset('[%s]' % index, offset, self.item_type) + except Exception as e: + log.exception('%s', e) + raise + + def get_index_of_child(self, name): + return int(name.lstrip('[').rstrip(']')) + + def get_summary(self): + return '(%d)' % (self.len,) + + +class StdVectorSynthProvider(ArrayLikeSynthProvider): + def ptr_and_len(self, vec): + return ( + read_unique_ptr(gcm(vec, 'buf', 'ptr')), + gcm(vec, 'len').GetValueAsUnsigned() + ) + + def get_summary(self): + return '(%d) vec![%s]' % (self.len, sequence_summary((self.get_child_at_index(i) for i in range(self.len)))) + + +class StdVecDequeSynthProvider(RustSynthProvider): + def update(self): + self.ptr = read_unique_ptr(gcm(self.valobj, 'buf', 'ptr')) + self.cap = gcm(self.valobj, 'buf', 'cap').GetValueAsUnsigned() + + head = gcm(self.valobj, 'head').GetValueAsUnsigned() + + # rust 1.67 changed from a head, tail implementation to a head, length impl + # https://github.com/rust-lang/rust/pull/102991 + vd_len = gcm(self.valobj, 'len') + if vd_len.IsValid(): + self.len = vd_len.GetValueAsUnsigned() + self.startptr = head + else: + tail = gcm(self.valobj, 'tail').GetValueAsUnsigned() + self.len = head - tail + self.startptr = tail + + self.item_type = self.ptr.GetType().GetPointeeType() + self.item_size = self.item_type.GetByteSize() + + def num_children(self): + return self.len + + def has_children(self): + return True + + def get_child_at_index(self, index): + try: + if not 0 <= index < self.num_children(): + return None + offset = ((self.startptr + index) % self.cap) * self.item_size + return self.ptr.CreateChildAtOffset('[%s]' % index, offset, self.item_type) + except Exception as e: + log.exception('%s', e) + raise + + def get_index_of_child(self, name): + return int(name.lstrip('[').rstrip(']')) + + def get_summary(self): + return '(%d) VecDeque[%s]' % (self.num_children(), sequence_summary((self.get_child_at_index(i) for i in range(self.num_children())))) + +################################################################################################################## + + +class SliceSynthProvider(ArrayLikeSynthProvider): + def ptr_and_len(self, vec): + return ( + gcm(vec, 'data_ptr'), + gcm(vec, 'length').GetValueAsUnsigned() + ) + + def get_summary(self): + return '(%d) &[%s]' % (self.len, sequence_summary((self.get_child_at_index(i) for i in range(self.len)))) + + +class MsvcSliceSynthProvider(SliceSynthProvider): + def get_type_name(self): + tparams = get_template_params(self.valobj.GetTypeName()) + return '&[' + tparams[0] + ']' + + +# Base class for *String providers +class StringLikeSynthProvider(ArrayLikeSynthProvider): + def get_child_at_index(self, index): + ch = ArrayLikeSynthProvider.get_child_at_index(self, index) + ch.SetFormat(lldb.eFormatChar) + return ch + + def get_summary(self): + # Limit string length to 1000 characters to cope with uninitialized values whose + # length field contains garbage. + strval = string_from_ptr(self.ptr, min(self.len, 1000)) + if strval == None: + return None + if self.len > 1000: + strval += u'...' + return u'"%s"' % strval + + +class StrSliceSynthProvider(StringLikeSynthProvider): + def ptr_and_len(self, valobj): + return ( + gcm(valobj, 'data_ptr'), + gcm(valobj, 'length').GetValueAsUnsigned() + ) + + +class StdStringSynthProvider(StringLikeSynthProvider): + def ptr_and_len(self, valobj): + vec = gcm(valobj, 'vec') + return ( + read_unique_ptr(gcm(vec, 'buf', 'ptr')), + gcm(vec, 'len').GetValueAsUnsigned() + ) + + +class StdCStringSynthProvider(StringLikeSynthProvider): + def ptr_and_len(self, valobj): + vec = gcm(valobj, 'inner') + return ( + gcm(vec, 'data_ptr'), + gcm(vec, 'length').GetValueAsUnsigned() - 1 + ) + + +class StdOsStringSynthProvider(StringLikeSynthProvider): + def ptr_and_len(self, valobj): + vec = gcm(valobj, 'inner', 'inner') + tmp = gcm(vec, 'bytes') # Windows OSString has an extra layer + if tmp.IsValid(): + vec = tmp + return ( + read_unique_ptr(gcm(vec, 'buf', 'ptr')), + gcm(vec, 'len').GetValueAsUnsigned() + ) + + +class FFISliceSynthProvider(StringLikeSynthProvider): + def ptr_and_len(self, valobj): + process = valobj.GetProcess() + slice_ptr = valobj.GetLoadAddress() + data_ptr_type = valobj.GetTarget().GetBasicType(lldb.eBasicTypeChar).GetPointerType() + # Unsized slice objects have incomplete debug info, so here we just assume standard slice + # reference layout: [, ] + error = lldb.SBError() + pointer = valobj.CreateValueFromAddress('data', slice_ptr, data_ptr_type) + length = process.ReadPointerFromMemory(slice_ptr + process.GetAddressByteSize(), error) + return pointer, length + + +class StdCStrSynthProvider(FFISliceSynthProvider): + def ptr_and_len(self, valobj): + ptr, len = FFISliceSynthProvider.ptr_and_len(self, valobj) + return (ptr, len-1) # drop terminaing '\0' + + +class StdOsStrSynthProvider(FFISliceSynthProvider): + pass + + +class StdPathBufSynthProvider(StdOsStringSynthProvider): + def ptr_and_len(self, valobj): + return StdOsStringSynthProvider.ptr_and_len(self, gcm(valobj, 'inner')) + + +class StdPathSynthProvider(FFISliceSynthProvider): + pass + +################################################################################################################## + + +class DerefSynthProvider(RustSynthProvider): + deref = lldb.SBValue() + + def has_children(self): + return self.deref.MightHaveChildren() + + def num_children(self): + return self.deref.GetNumChildren() + + def get_child_at_index(self, index): + return self.deref.GetChildAtIndex(index) + + def get_index_of_child(self, name): + return self.deref.GetIndexOfChildWithName(name) + + def get_summary(self): + return obj_summary(self.deref) + +# Base for Rc and Arc + + +class StdRefCountedSynthProvider(DerefSynthProvider): + weak = 0 + strong = 0 + + def get_summary(self): + if self.weak != 0: + s = '(refs:%d,weak:%d) ' % (self.strong, self.weak) + else: + s = '(refs:%d) ' % self.strong + if self.strong > 0: + s += obj_summary(self.deref) + else: + s += '' + return s + + +class StdRcSynthProvider(StdRefCountedSynthProvider): + def update(self): + inner = read_unique_ptr(gcm(self.valobj, 'ptr')) + self.strong = gcm(inner, 'strong', 'value', 'value').GetValueAsUnsigned() + self.weak = gcm(inner, 'weak', 'value', 'value').GetValueAsUnsigned() + if self.strong > 0: + self.deref = gcm(inner, 'value') + self.weak -= 1 # There's an implicit weak reference communally owned by all the strong pointers + else: + self.deref = lldb.SBValue() + self.deref.SetPreferSyntheticValue(True) + + +class StdArcSynthProvider(StdRefCountedSynthProvider): + def update(self): + inner = read_unique_ptr(gcm(self.valobj, 'ptr')) + self.strong = gcm(inner, 'strong', 'v', 'value').GetValueAsUnsigned() + self.weak = gcm(inner, 'weak', 'v', 'value').GetValueAsUnsigned() + if self.strong > 0: + self.deref = gcm(inner, 'data') + self.weak -= 1 # There's an implicit weak reference communally owned by all the strong pointers + else: + self.deref = lldb.SBValue() + self.deref.SetPreferSyntheticValue(True) + + +class StdMutexSynthProvider(DerefSynthProvider): + def update(self): + self.deref = gcm(self.valobj, 'data', 'value') + self.deref.SetPreferSyntheticValue(True) + + +class StdCellSynthProvider(DerefSynthProvider): + def update(self): + self.deref = gcm(self.valobj, 'value', 'value') + self.deref.SetPreferSyntheticValue(True) + + +class StdRefCellSynthProvider(DerefSynthProvider): + def update(self): + self.deref = gcm(self.valobj, 'value', 'value') + self.deref.SetPreferSyntheticValue(True) + + def get_summary(self): + borrow = gcm(self.valobj, 'borrow', 'value', 'value').GetValueAsSigned() + s = '' + if borrow < 0: + s = '(borrowed:mut) ' + elif borrow > 0: + s = '(borrowed:%d) ' % borrow + return s + obj_summary(self.deref) + + +class StdRefCellBorrowSynthProvider(DerefSynthProvider): + def update(self): + self.deref = gcm(self.valobj, 'value', 'pointer').Dereference() + self.deref.SetPreferSyntheticValue(True) + +################################################################################################################## + + +class EnumSynthProvider(RustSynthProvider): + variant = lldb.SBValue() + summary = '' + skip_first = 0 + + def has_children(self): + return self.variant.MightHaveChildren() + + def num_children(self): + return self.variant.GetNumChildren() - self.skip_first + + def get_child_at_index(self, index): + return self.variant.GetChildAtIndex(index + self.skip_first) + + def get_index_of_child(self, name): + return self.variant.GetIndexOfChildWithName(name) - self.skip_first + + def get_summary(self): + return self.summary + + +class GenericEnumSynthProvider(EnumSynthProvider): + def update(self): + dyn_type_name = self.valobj.GetTypeName() + variant_name = dyn_type_name[dyn_type_name.rfind(':')+1:] + self.variant = self.valobj + + if self.variant.IsValid() and self.variant.GetNumChildren() > self.skip_first: + if self.variant.GetChildAtIndex(self.skip_first).GetName() in ['0', '__0']: + self.summary = variant_name + tuple_summary(self.variant) + else: + self.summary = variant_name + '{...}' + else: + self.summary = variant_name + + +class MsvcTupleSynthProvider(RustSynthProvider): + def update(self): + tparams = get_template_params(self.valobj.GetTypeName()) + self.type_name = '(' + ', '.join(tparams) + ')' + + def has_children(self): + return self.valobj.MightHaveChildren() + + def num_children(self): + return self.valobj.GetNumChildren() + + def get_child_at_index(self, index): + child = self.valobj.GetChildAtIndex(index) + return child.CreateChildAtOffset(str(index), 0, child.GetType()) + + def get_index_of_child(self, name): + return str(name) + + def get_summary(self): + return tuple_summary(self.valobj) + + def get_type_name(self): + return self.type_name + + +class MsvcEnumSynthProvider(EnumSynthProvider): + is_tuple_variant = False + + def update(self): + tparams = get_template_params(self.valobj.GetTypeName()) + if len(tparams) == 1: # Regular enum + discr = gcm(self.valobj, 'discriminant') + self.variant = gcm(self.valobj, 'variant' + str(discr.GetValueAsUnsigned())) + variant_name = discr.GetValue() + else: # Niche enum + dataful_min = int(tparams[1]) + dataful_max = int(tparams[2]) + dataful_var = tparams[3] + discr = gcm(self.valobj, 'discriminant') + if dataful_min <= discr.GetValueAsUnsigned() <= dataful_max: + self.variant = gcm(self.valobj, 'dataful_variant') + variant_name = dataful_var + else: + variant_name = discr.GetValue() + + self.type_name = tparams[0] + + if self.variant.IsValid() and self.variant.GetNumChildren() > self.skip_first: + if self.variant.GetChildAtIndex(self.skip_first).GetName() == '__0': + self.is_tuple_variant = True + self.summary = variant_name + tuple_summary(self.variant, skip_first=self.skip_first) + else: + self.summary = variant_name + '{...}' + else: + self.summary = variant_name + + def get_child_at_index(self, index): + child = self.variant.GetChildAtIndex(index + self.skip_first) + if self.is_tuple_variant: + return child.CreateChildAtOffset(str(index), 0, child.GetType()) + else: + return child + + def get_index_of_child(self, name): + if self.is_tuple_variant: + return int(name) + else: + return self.variant.GetIndexOfChildWithName(name) - self.skip_first + + def get_type_name(self): + return self.type_name + + +class MsvcEnum2SynthProvider(EnumSynthProvider): + is_tuple_variant = False + + def update(self): + tparams = get_template_params(self.valobj.GetTypeName()) + self.type_name = tparams[0] + + def get_child_at_index(self, index): + return self.valobj.GetChildAtIndex(index) + + def get_index_of_child(self, name): + return self.valobj.GetChildIndex(name) + + def get_type_name(self): + return self.type_name + + +################################################################################################################## + + +class StdHashMapSynthProvider(RustSynthProvider): + def update(self): + self.initialize_table(gcm(self.valobj, 'base', 'table')) + + def initialize_table(self, table): + assert table.IsValid() + + if table.type.GetNumberOfTemplateArguments() > 0: + item_ty = table.type.GetTemplateArgumentType(0) + else: # we must be on windows-msvc - try to look up item type by name + table_ty_name = table.GetType().GetName() # "hashbrown::raw::RawTable" + item_ty_name = get_template_params(table_ty_name)[0] + item_ty = table.GetTarget().FindTypes(item_ty_name).GetTypeAtIndex(0) + + if item_ty.IsTypedefType(): + item_ty = item_ty.GetTypedefedType() + + inner_table = table.GetChildMemberWithName('table') + if inner_table.IsValid(): + self.initialize_hashbrown_v2(inner_table, item_ty) # 1.52 <= std_version + else: + if not table.GetChildMemberWithName('data'): + self.initialize_hashbrown_v2(table, item_ty) # ? <= std_version < 1.52 + else: + self.initialize_hashbrown_v1(table, item_ty) # 1.36 <= std_version < ? + + def initialize_hashbrown_v2(self, table, item_ty): + self.num_buckets = gcm(table, 'bucket_mask').GetValueAsUnsigned() + 1 + ctrl_ptr = gcm(table, 'ctrl', 'pointer') + ctrl = ctrl_ptr.GetPointeeData(0, self.num_buckets) + # Buckets are located above `ctrl`, in reverse order. + start_addr = ctrl_ptr.GetValueAsUnsigned() - item_ty.GetByteSize() * self.num_buckets + buckets_ty = item_ty.GetArrayType(self.num_buckets) + self.buckets = self.valobj.CreateValueFromAddress('data', start_addr, buckets_ty) + error = lldb.SBError() + self.valid_indices = [] + for i in range(self.num_buckets): + if ctrl.GetUnsignedInt8(error, i) & 0x80 == 0: + self.valid_indices.append(self.num_buckets - 1 - i) + + def initialize_hashbrown_v1(self, table, item_ty): + self.num_buckets = gcm(table, 'bucket_mask').GetValueAsUnsigned() + 1 + ctrl_ptr = gcm(table, 'ctrl', 'pointer') + ctrl = ctrl_ptr.GetPointeeData(0, self.num_buckets) + buckets_ty = item_ty.GetArrayType(self.num_buckets) + self.buckets = gcm(table, 'data', 'pointer').Dereference().Cast(buckets_ty) + error = lldb.SBError() + self.valid_indices = [] + for i in range(self.num_buckets): + if ctrl.GetUnsignedInt8(error, i) & 0x80 == 0: + self.valid_indices.append(i) + + def has_children(self): + return True + + def num_children(self): + return len(self.valid_indices) + + def get_child_at_index(self, index): + bucket_idx = self.valid_indices[index] + item = self.buckets.GetChildAtIndex(bucket_idx) + return item.CreateChildAtOffset('[%d]' % index, 0, item.GetType()) + + def get_index_of_child(self, name): + return int(name.lstrip('[').rstrip(']')) + + def get_summary(self): + return 'size=%d, capacity=%d' % (self.num_children(), self.num_buckets) + + +class StdHashSetSynthProvider(StdHashMapSynthProvider): + def update(self): + table = gcm(self.valobj, 'base', 'map', 'table') # std_version >= 1.48 + if not table.IsValid(): + table = gcm(self.valobj, 'map', 'base', 'table') # std_version < 1.48 + self.initialize_table(table) + + def get_child_at_index(self, index): + bucket_idx = self.valid_indices[index] + item = self.buckets.GetChildAtIndex(bucket_idx).GetChildAtIndex(0) + return item.CreateChildAtOffset('[%d]' % index, 0, item.GetType()) + +################################################################################################################## + + +def __lldb_init_module(debugger_obj, internal_dict): + log.info('Initializing') + initialize_category(debugger_obj, internal_dict) \ No newline at end of file diff --git a/test-tools/wamr-ide/VSCode-Extension/package.json b/test-tools/wamr-ide/VSCode-Extension/package.json index dfe37961b..ed27eb7c1 100644 --- a/test-tools/wamr-ide/VSCode-Extension/package.json +++ b/test-tools/wamr-ide/VSCode-Extension/package.json @@ -6,7 +6,7 @@ }, "displayName": "WAMR-IDE", "description": "An Integrated Development Environment for WASM", - "version": "1.2.1", + "version": "1.2.2", "engines": { "vscode": "^1.59.0" }, diff --git a/test-tools/wamr-ide/VSCode-Extension/src/debugConfigurationProvider.ts b/test-tools/wamr-ide/VSCode-Extension/src/debugConfigurationProvider.ts index e7b42bf03..657cf59c7 100644 --- a/test-tools/wamr-ide/VSCode-Extension/src/debugConfigurationProvider.ts +++ b/test-tools/wamr-ide/VSCode-Extension/src/debugConfigurationProvider.ts @@ -6,23 +6,47 @@ import * as vscode from 'vscode'; import * as os from 'os'; +/* see https://github.com/llvm/llvm-project/tree/main/lldb/tools/lldb-vscode#attaching-settings */ +export interface WasmDebugConfig { + type: string, + name: string, + request: string, + program? : string, + pid?: string, + stopOnEntry?: boolean, + waitFor?: boolean, + initCommands?: string[], + preRunCommands?: string[], + stopCommands?: string[], + exitCommands?: string[], + terminateCommands?: string[], + attachCommands?: string[] +} + export class WasmDebugConfigurationProvider implements vscode.DebugConfigurationProvider { - private wasmDebugConfig = { + private wasmDebugConfig: WasmDebugConfig = { type: 'wamr-debug', name: 'Attach', request: 'attach', stopOnEntry: true, - initCommands: os.platform() === 'win32' || os.platform() === 'darwin' ? - /* linux and windows has different debug configuration */ - ['platform select remote-linux'] : - undefined, attachCommands: [ /* default port 1234 */ 'process connect -p wasm connect://127.0.0.1:1234', ] }; + constructor(extensionPath: string) { + this.wasmDebugConfig.initCommands = [ + /* Add rust formatters -> https://lldb.llvm.org/use/variable.html */ + `command script import ${extensionPath}/formatters/rust.py` + ]; + + if (os.platform() === 'win32' || os.platform() === 'darwin') { + this.wasmDebugConfig.initCommands.push('platform select remote-linux'); + } + } + public resolveDebugConfiguration( _: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration, diff --git a/test-tools/wamr-ide/VSCode-Extension/src/extension.ts b/test-tools/wamr-ide/VSCode-Extension/src/extension.ts index 523b26b83..d6c1fedfc 100644 --- a/test-tools/wamr-ide/VSCode-Extension/src/extension.ts +++ b/test-tools/wamr-ide/VSCode-Extension/src/extension.ts @@ -170,7 +170,7 @@ export async function activate(context: vscode.ExtensionContext) { } /* register debug configuration */ - wasmDebugConfigProvider = new WasmDebugConfigurationProvider(); + wasmDebugConfigProvider = new WasmDebugConfigurationProvider(context.extensionPath); vscode.debug.registerDebugConfigurationProvider( 'wamr-debug',