Skip to content

Commit a6dced8

Browse files
committed
Initial GDB implementation
1 parent e092805 commit a6dced8

26 files changed

Lines changed: 1044 additions & 241 deletions

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ lld-link.exe
2121
stats/
2222
bin/Git
2323
**/dist/*
24+
!**/dist/gdb
2425
BeefySysLib/third_party/*
2526
BeefTools/RandoCode/*
2627
jbuild*/
@@ -36,4 +37,5 @@ install/
3637
JEMalloc/
3738
.cache
3839
.vscode
39-
*_Done.txt
40+
*_Done.txt
41+
extern/temp/

BeefySysLib/platform/win/WinBFApp.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ static BOOL KeyboardLayoutHasAltGr(HKL layout)
100100
return hasAltGr;
101101
}
102102

103-
WinBFWindow::WinBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags)
103+
WinBFWindow::WinBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int64 windowFlags)
104104
{
105105
//OutputDebugStrF("Wnd %p Create\n", this);
106106

BeefySysLib/platform/win/WinBFApp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class WinBFWindow : public BFWindow
7272
void GotFocus();
7373

7474
public:
75-
WinBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags);
75+
WinBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int64 windowFlags);
7676
~WinBFWindow();
7777

7878
virtual void* GetUnderlying() override;

CLAUDE.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
1-
C++ style rules:
1+
C++ rules:
22
Use NULL instead of nullptr
33
Instead of the stdint types such as "int32_t" use our int type aliases such as "int32" (the same without the _t).
4+
BeefySysLib contains many system abstractions and "helper classes". The lowest level interfaces are declared in PlatformInterface.h, including file IO, threads, process and spawning.
5+
6+
When spawning a process, we can interact with it's stdio/stdout/stderr by using BfpSpawn_Create with BfpSpawnFlag_RedirectStdOutput | BfpSpawnFlag_RedirectStdError. It should probably also be made invisible with BfpSpawnFlag_NoWindow. Since you can't use async reading on those handles (use BfpSpawn_GetStdHandles), you must uses threads (BfpThread_Create) to read/write from those. In the read thread use BfpFile_Read, where acceptable results are BfpFileResult_Ok or BfpFileResult_PartialData, otherwise the process has died and you can exit. When BfpSpawn_WaitFor indicates thes process is dead, close those handles (which will cause the reads/writes in the threads to return errors and stop blocking), then BfpThread_WaitFor, BfpThread_Release, and BfpSpawn_Release.
7+
8+
Use Beefy::String (BeefySysLib/util/String.h) for strings. When used as arguments we generally pass those as "const StringImpl&".
9+
10+
Use Beefy::Array (BeefySysLib/util/Array.h) for an auto-resizing contiguous collection, similar to std::vector.
11+
12+
Use Beefy::Dictionary (BeefySysLib/util/Dictionary.h) for a hashed key/value dictionary.
13+
14+
For thread synchronization, we generally use critical sections rather than mutexes, and that functionality is wrapped up in Beefy::CritSect (BeefySysLib\util\CritSect). We usually use AutoCrit to get RAII critical scoped sections instead of manually calling Lock/Unlock. For thread "wait events", we wrap that in Beefy::SyncEvent.
415

516
BeefLang and C++ rules:
617
When you have a statement such as "if (a == b || c == d)", use extra parentheses such as "if ((a == b) || (c == d))"

Debugger64/Debugger64.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
<ClInclude Include="..\IDEHelper\DbgModule.h" />
3232
<ClInclude Include="..\IDEHelper\DWARFInfo.h" />
3333
<ClInclude Include="..\IDEHelper\DwAutoComplete.h" />
34+
<ClInclude Include="..\IDEHelper\GDBDebugger.h" />
3435
<ClInclude Include="..\IDEHelper\HandleDbg.h" />
3536
<ClInclude Include="..\IDEHelper\HotHeap.h" />
3637
<ClInclude Include="..\IDEHelper\HotScanner.h" />
@@ -48,6 +49,7 @@
4849
<ClCompile Include="..\IDEHelper\DbgTypeMap.cpp" />
4950
<ClCompile Include="..\IDEHelper\DebugTarget.cpp" />
5051
<ClCompile Include="..\IDEHelper\DbgModule.cpp" />
52+
<ClCompile Include="..\IDEHelper\GDBDebugger.cpp" />
5153
<ClCompile Include="..\IDEHelper\HandleDbg.cpp" />
5254
<ClCompile Include="..\IDEHelper\HotHeap.cpp" />
5355
<ClCompile Include="..\IDEHelper\HotScanner.cpp" />

Debugger64/Debugger64.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
<ClInclude Include="..\IDEHelper\LLDBDebugger.h">
7070
<Filter>Source Files</Filter>
7171
</ClInclude>
72+
<ClInclude Include="..\IDEHelper\GDBDebugger.h">
73+
<Filter>Source Files</Filter>
74+
</ClInclude>
7275
</ItemGroup>
7376
<ItemGroup>
7477
<ClCompile Include="..\IDEHelper\DbgExprEvaluator.cpp">
@@ -113,5 +116,8 @@
113116
<ClCompile Include="..\IDEHelper\LLDBDebugger.cpp">
114117
<Filter>Source Files</Filter>
115118
</ClCompile>
119+
<ClCompile Include="..\IDEHelper\GDBDebugger.cpp">
120+
<Filter>Source Files</Filter>
121+
</ClCompile>
116122
</ItemGroup>
117123
</Project>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# System_Collections_List.py
2+
#
3+
# GDB pretty-printer for System.Collections.List<T> (Beef language)
4+
#
5+
# Memory layout (!BF_LARGE_COLLECTIONS, i.e. int32 mode):
6+
#
7+
# class List<T> {
8+
# T* mItems; // pointer to element array
9+
# int_cosize mSize; // current element count
10+
# int_cosize mAllocSizeAndFlags; // alloc size (lower 31 bits) | DynAllocFlag
11+
# };
12+
#
13+
# SizeFlags = 0x7FFFFFFF -- mask to extract allocated capacity
14+
# DynAllocFlag = 0x80000000 -- set when mItems is a heap-allocated buffer
15+
#
16+
# Children:
17+
# [Count] mSize
18+
# [AllocSize] mAllocSizeAndFlags & SizeFlags
19+
# [0]..[N-1] elements read from mItems
20+
21+
import gdb
22+
import gdb.printing
23+
24+
_SizeFlags = 0x7FFFFFFF
25+
_DynAllocFlag = 0x80000000
26+
27+
28+
def _safe_int(value, default=0):
29+
try:
30+
return int(value)
31+
except Exception:
32+
return default
33+
34+
35+
class BeefListPrinter:
36+
def __init__(self, val):
37+
self.val = val
38+
39+
def to_string(self):
40+
try:
41+
size = _safe_int(self.val["mSize"], 0)
42+
return "size={}".format(size)
43+
except Exception as e:
44+
return "<List error: {}>".format(e)
45+
46+
def display_hint(self):
47+
return "array"
48+
49+
def children(self):
50+
try:
51+
size = _safe_int(self.val["mSize"], 0)
52+
alloc_flags = _safe_int(self.val["mAllocSizeAndFlags"], 0)
53+
alloc_size = alloc_flags & _SizeFlags
54+
except Exception:
55+
return
56+
57+
try:
58+
yield ("[Ptr]", self.val["mItems"])
59+
except Exception:
60+
pass
61+
62+
try:
63+
yield ("[Count]", self.val["mSize"])
64+
except Exception:
65+
pass
66+
67+
yield ("[AllocSize]", alloc_size)
68+
69+
if size <= 0:
70+
return
71+
72+
try:
73+
items_ptr = self.val["mItems"]
74+
if _safe_int(items_ptr, 0) == 0:
75+
return
76+
for i in range(size):
77+
yield ("[{}]".format(i), items_ptr[i])
78+
except Exception as e:
79+
yield ("<error>", str(e))
80+
81+
82+
def _get_or_create_beef_collection():
83+
"""Return the existing global 'Beef' printer collection (or create it).
84+
Returns (collection, needs_registration)."""
85+
for pp in gdb.pretty_printers:
86+
if getattr(pp, 'name', None) == 'Beef':
87+
return pp, False
88+
return gdb.printing.RegexpCollectionPrettyPrinter("Beef"), True
89+
90+
91+
_beef_pp, _needs_reg = _get_or_create_beef_collection()
92+
_beef_pp.add_printer(
93+
"System::Collections::List",
94+
"^System::Collections::List<.*>$",
95+
BeefListPrinter,
96+
)
97+
if _needs_reg:
98+
gdb.printing.register_pretty_printer(None, _beef_pp)
99+
100+
print("[List] pretty-printers registered")

IDE/dist/gdb/System_String.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# System_String.py
2+
#
3+
# GDB pretty-printer for System::String (Beef language)
4+
#
5+
# Memory layout (!BF_LARGE_STRINGS, i.e. int32/uint32 mode):
6+
#
7+
# class String { // [Ordered] Beef class
8+
# int32 mLength; // character count in char8 units
9+
# uint32 mAllocSizeAndFlags; // alloc size (lower 30 bits) | flags (upper 2 bits)
10+
# char8* mPtrOrBuffer; // pointer when cStrPtrFlag set;
11+
# // otherwise the char8 data lives inline starting
12+
# // at this field's address, extending past it
13+
# };
14+
#
15+
# cSizeFlags = 0x3FFFFFFF -- mask to extract alloc size
16+
# cDynAllocFlag = 0x80000000 -- heap-allocated buffer; mPtrOrBuffer is the pointer
17+
# cStrPtrFlag = 0x40000000 -- mPtrOrBuffer is a pointer (heap OR static/fixed ref)
18+
#
19+
# Children:
20+
# [Length] always present
21+
# [AllocSize] when cDynAllocFlag is set (heap buffer)
22+
# [RefSize] when cStrPtrFlag set, cDynAllocFlag not set (non-owning pointer)
23+
# [InternalSize] when neither flag is set (inline buffer)
24+
25+
import gdb
26+
import gdb.printing
27+
28+
MAX_PREVIEW_BYTES = 256
29+
30+
_cSizeFlags = 0x3FFFFFFF
31+
_cDynAllocFlag = 0x80000000
32+
_cStrPtrFlag = 0x40000000
33+
34+
35+
def _safe_int(value, default=0):
36+
try:
37+
return int(value)
38+
except Exception:
39+
return default
40+
41+
42+
def _escape_bytes(data):
43+
try:
44+
return data.decode("utf-8")
45+
except Exception:
46+
return "".join(
47+
chr(b) if 32 <= b <= 126 and b not in (34, 92)
48+
else "\\x{:02x}".format(b)
49+
for b in data
50+
)
51+
52+
53+
class BeefStringPrinter:
54+
def __init__(self, val):
55+
self.val = val
56+
57+
def _decode(self):
58+
"""Returns (length, has_str_ptr, has_dyn_alloc, alloc_size)."""
59+
length = _safe_int(self.val["mLength"], -1)
60+
alloc_flags = _safe_int(self.val["mAllocSizeAndFlags"], 0)
61+
has_str_ptr = bool(alloc_flags & _cStrPtrFlag)
62+
has_dyn = bool(alloc_flags & _cDynAllocFlag)
63+
alloc_size = alloc_flags & _cSizeFlags
64+
return length, has_str_ptr, has_dyn, alloc_size
65+
66+
def to_string(self):
67+
print("String to_string called")
68+
69+
try:
70+
length, has_str_ptr, has_dyn, alloc_size = self._decode()
71+
except Exception as e:
72+
return "<String field error: {}>".format(e)
73+
74+
try:
75+
if (self.val["mLength"].is_optimized_out or
76+
self.val["mAllocSizeAndFlags"].is_optimized_out):
77+
return "<String optimized out>"
78+
except Exception:
79+
pass
80+
81+
if length < 0:
82+
return "<String invalid length={}>".format(length)
83+
84+
if length == 0:
85+
return ""
86+
87+
preview_len = min(length, MAX_PREVIEW_BYTES)
88+
89+
try:
90+
inferior = gdb.selected_inferior()
91+
92+
if has_str_ptr:
93+
# mPtrOrBuffer holds a real pointer to the character data
94+
ptr_addr = _safe_int(self.val["mPtrOrBuffer"], 0)
95+
if ptr_addr == 0:
96+
return "<String null ptr length={}>".format(length)
97+
mem = inferior.read_memory(ptr_addr, preview_len)
98+
else:
99+
# Inline buffer: char8 data lives at the address of mPtrOrBuffer
100+
# itself and extends past it into appended allocation.
101+
buf_addr = int(self.val["mPtrOrBuffer"].address)
102+
mem = inferior.read_memory(buf_addr, preview_len)
103+
104+
except gdb.MemoryError:
105+
return "<String unreadable memory length={}>".format(length)
106+
except Exception as e:
107+
return "<String read error: {}>".format(e)
108+
109+
escaped = _escape_bytes(bytes(mem))
110+
if length > MAX_PREVIEW_BYTES:
111+
escaped += "..."
112+
return escaped
113+
114+
def display_hint(self):
115+
return "string"
116+
117+
def children(self):
118+
try:
119+
length, has_str_ptr, has_dyn, alloc_size = self._decode()
120+
except Exception:
121+
return
122+
123+
try:
124+
yield ("[Length]", self.val["mLength"])
125+
except Exception:
126+
pass
127+
128+
if has_dyn:
129+
yield ("[AllocSize]", alloc_size)
130+
elif has_str_ptr:
131+
yield ("[RefSize]", alloc_size)
132+
else:
133+
yield ("[InternalSize]", alloc_size)
134+
135+
136+
def _get_or_create_beef_collection():
137+
"""Return the existing global 'Beef' printer collection (or create it).
138+
Returns (collection, needs_registration)."""
139+
for pp in gdb.pretty_printers:
140+
if getattr(pp, 'name', None) == 'Beef':
141+
return pp, False
142+
return gdb.printing.RegexpCollectionPrettyPrinter("Beef"), True
143+
144+
145+
_beef_pp, _needs_reg = _get_or_create_beef_collection()
146+
_beef_pp.add_printer("System::String", "^System::String$", BeefStringPrinter)
147+
if _needs_reg:
148+
gdb.printing.register_pretty_printer(None, _beef_pp)
149+
150+
print("[String] pretty-printers registered")

0 commit comments

Comments
 (0)