Skip to main content

debug-pretty-printers

Debug and develop GDB and LLDB pretty printers for ezEngine C++ types. Use this skill when working with ezEngine debugger visualizers in ezEngine-gdb.py, ezEngine.py, or ezEngine.natvis. Covers testing with VisualizerZoo.cpp, common errors, and debugging techniques.

소스 정보

저장소
ezEngine/ezEngine
최근 소스 활동
2026년 2월 6일 21:03
감지된 SKILL.md 언어
영어
스타
2,038
포크
266

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
debug-pretty-printers
description
Debug and develop GDB and LLDB pretty printers for ezEngine C++ types. Use this skill when working with ezEngine debugger visualizers in ezEngine-gdb.py, ezEngine.py, or ezEngine.natvis. Covers testing with VisualizerZoo.cpp, common errors, and debugging techniques.
# Debugging ezEngine Pretty Printers This skill provides guidance for developing and debugging pretty printers (debugger visualizers) for ezEngine. ## File Locations All pretty printer files are in `Code/Engine/Foundation/`: - **GDB**: `Code/Engine/Foundation/ezEngine-gdb.py` - Python pretty printers for GDB - **LLDB**: `Code/Engine/Foundation/ezEngine.py` - Python pretty printers for LLDB - **Natvis**: `Code/Engine/Foundation/ezEngine.natvis` - Visual Studio/MSVC visualizers (XML) - **Auto-load**: `.gdbinit` in repository root - Automatically loads GDB printers ## Testing with VisualizerZoo.cpp The primary test file for pretty printers is `Code/UnitTests/FoundationTest/CodeUtils/VisualizerZoo.cpp`. This file contains test variables for all visualized types organized in `EZ_TEST_BLOCK` sections. ### Finding test sections Search for `EZ_TEST_BLOCK` macros to find each section: ```bash grep -n "EZ_TEST_BLOCK" Code/UnitTests/FoundationTest/CodeUtils/VisualizerZoo.cpp ``` Eac hsection is named descriptively, e.g. `EZ_TEST_BLOCK(ezTestBlock::Enabled, "Strings")` contains tests for the string types. ### Setting breakpoints Each `EZ_TEST_BLOCK` section ends with `EZ_TEST_BOOL(true);` - this is where you should set your breakpoint. At this point, all variables in the block are initialized and in scope. To find the breakpoint line for a section: ```bash # Find the section start, then look for the EZ_TEST_BOOL(true) before the closing brace grep -n "EZ_TEST_BLOCK.*Strings" Code/UnitTests/FoundationTest/CodeUtils/VisualizerZoo.cpp # Then search forward from that line for EZ_TEST_BOOL(true) ``` Or in an editor, search for `EZ_TEST_BLOCK.*SectionName`, then find the `EZ_TEST_BOOL(true);` at the end of that block. ### Running the test First, ensure FoundationTest is built: ```bash cmake --build Workspace/copilot --target FoundationTest ``` ### Testing GDB printers in batch mode Use this exact command pattern to test printers (run from the repository root): ```bash gdb -batch \ -ex "set pagination off" \ -ex "source Code/Engine/Foundation/ezEngine-gdb.py" \ -ex "break VisualizerZoo.cpp:LINE_NUMBER" \ -ex "run -run -noGui -all" \ -ex "print variableName" \ ./Workspace/copilot-output/Bin/LinuxNinjaGccDebug64/FoundationTest ``` Replace `LINE_NUMBER` with the line of `EZ_TEST_BOOL(true);` in the relevant section. Key points: - Use `timeout 30` prefix if the test might hang - The breakpoint must be on the `EZ_TEST_BOOL(true);` line at the END of the block (all variables initialized) - Add `2>&1 | tail -15` to limit output ### Example: Testing string printers 1. Find the Strings section: ```bash grep -n "EZ_TEST_BLOCK.*Strings" Code/UnitTests/FoundationTest/CodeUtils/VisualizerZoo.cpp ``` 2. Find the `EZ_TEST_BOOL(true);` at the end of that block and note the line number 3. Run GDB with that breakpoint (from repository root): ```bash gdb -batch \ -ex "set pagination off" \ -ex "source Code/Engine/Foundation/ezEngine-gdb.py" \ -ex "break VisualizerZoo.cpp:101" \ -ex "run -run -noGui -all" \ -ex "print string" \ -ex "print stringView" \ ./Workspace/copilot-output/Bin/LinuxNinjaGccDebug64/FoundationTest ``` ### Viewing expanded children Use `set print array on` to see children on separate lines: ```bash gdb -batch \ -ex "set pagination off" \ -ex "set print array on" \ -ex "source Code/Engine/Foundation/ezEngine-gdb.py" \ -ex "break VisualizerZoo.cpp:LINE_NUMBER" \ -ex "run -run -noGui -all" \ -ex "print variableName" \ ./Workspace/copilot-output/Bin/LinuxNinjaGccDebug64/FoundationTest ``` ## ezEngine GDB Printer Structure ### Registration pattern (from ezEngine-gdb.py) ```python def build_pretty_printers(): pp = gdb.printing.RegexpCollectionPrettyPrinter("ezEngine") # Strings pp.add_printer('ezHybridStringBase', r'^ezHybridStringBase<.*>$', ezHybridStringPrinter) pp.add_printer('ezStringBuilder', r'^ezStringBuilder$', ezHybridStringPrinter) # Containers pp.add_printer('ezDynamicArray', r'^ezDynamicArray<.*>$', ezDynamicArrayPrinter) return pp gdb.printing.register_pretty_printer(gdb.current_objfile(), build_pretty_printers()) ``` ### Printer class pattern ```python class ezMyTypePrinter: def __init__(self, val): self.val = val def to_string(self): """Return the display string for the value.""" try: return f"{{ count={count} }}" except Exception as e: return f"<error: {e}>" def children(self): """Yield (name, value) tuples for expandable children.""" try: yield 'm_uiCount', self.val['m_uiCount'] yield 'm_pAllocator', self.val['m_pAllocator'] for i in range(count): yield f'[{i}]', (m_pElements + i).dereference() except Exception as e: yield 'error', str(e) def display_hint(self): return 'array' # or 'string' or 'map' ``` ## Common Errors and Solutions ### "Type is not a template" **Problem**: Calling `val.type.template_argument(0)` on `ezStringBuilder` (not a template). **Solution**: Get size from the actual member instead: ```python # Instead of: local_storage_size = val.type.template_argument(0) local_storage_size = m_Data['m_StaticData'].type.sizeof ``` ### "Trying to read string with inappropriate type" **Problem**: Calling `.string()` on array address instead of `char*`. **Solution**: Cast to `char*` first: ```python char_ptr_type = gdb.lookup_type('char').pointer() ptr = m_Data['m_StaticData'].address.cast(char_ptr_type) result = ptr.string(length=count) ``` ### "There is no member named X" **Problem**: Wrong member name. Check the LLDB or Natvis implementation for correct names. **Solution**: Use GDB to inspect the actual type: ```bash gdb -batch -ex "ptype ezHashedString" ./Output/Bin/LinuxNinjaGccDebug64/FoundationTest ``` ### Children showing as strings instead of expandable values **Problem**: Yielding formatted strings in `children()` instead of GDB values. **Solution**: Yield actual GDB values that have their own printers: ```python # Wrong - yields a string, not expandable yield 'c0', f"{{ x={x}, y={y}, z={z} }}" # Correct - yields the actual value, GDB will use ezVec3Printer on it yield 'c0', elements[0].address.cast(vec3_type.pointer()).dereference() ``` ### ezHybridString inline vs heap storage The `ezHybridStringBase` uses inline storage (`m_StaticData`) when capacity fits, otherwise heap (`m_pElements`): ```python def _get_string_data(self): m_Data = self.val['m_Data'] m_uiCount = int(m_Data['m_uiCount']) m_uiCapacity = int(m_Data['m_uiCapacity']) local_storage_size = m_Data['m_StaticData'].type.sizeof if m_uiCapacity <= local_storage_size: # Using inline storage char_ptr_type = gdb.lookup_type('char').pointer() ptr = m_Data['m_StaticData'].address.cast(char_ptr_type) else: # Using heap storage ptr = m_Data['m_pElements'] return ptr, m_uiCount ``` ### Template type lookup for math types To cast array elements to vector types (e.g., for ezMat3 columns): ```python elements = self.val['m_fElementsCM'] elem_type = elements[0].type vec3_type = gdb.lookup_type(f'ezVec3Template<{elem_type}>') col0 = elements[0].address.cast(vec3_type.pointer()).dereference() ``` ## LLDB Comparison When implementing GDB printers, reference the LLDB implementation in `ezEngine.py`: - LLDB uses `set_fields(['m_uiCount', 'm_uiCapacity', 'm_pAllocator'])` to add member children - GDB equivalent: yield each field in `children()` before array elements - LLDB `GetChildMemberWithName('m_Data')` → GDB `self.val['m_Data']` - LLDB `GetValueAsUnsigned(0)` → GDB `int(self.val['m_Member'])` ## Debugging Workflow 1. **Identify the type**: Check which printer handles it in `build_pretty_printers()` 2. **Find test variable**: Look in VisualizerZoo.cpp for a variable of that type 3. **Set breakpoint after initialization**: Use the table above for correct line numbers 4. **Test to_string() first**: Get the display string working before children() 5. **Add children() incrementally**: Test each child field separately 6. **Compare with LLDB/Natvis**: Use existing implementations as reference for member names ## Auto-loading The `.gdbinit` file in the repository root automatically loads the printers when debugging in the ezEngine directory. It uses path detection relative to the current working directory.
GitHub에서 보기