Skip to content

Commit 018168f

Browse files
committed
Fix breakpoint handler exceptions
1 parent 969e76a commit 018168f

File tree

3 files changed

+89
-25
lines changed

3 files changed

+89
-25
lines changed

pybag/dbgeng/breakpoints.py

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,45 +24,56 @@ def __getitem__(self, id):
2424

2525
def __call__(self, *args):
2626
rawbp = DebugBreakpoint(args[0])
27+
bpid = rawbp.GetId()
28+
2729
try:
28-
bpfn = self.__getitem__(rawbp.GetId())
30+
(count,bpfn) = self.__getitem__(bpid)
2931
except KeyError:
32+
count = 0
3033
bpfn = None
3134
if bpfn:
3235
ret = bpfn(rawbp)
3336
else:
3437
ret = DbgEng.DEBUG_STATUS_NO_CHANGE
3538

36-
if rawbp.GetFlags() & DbgEng.DEBUG_BREAKPOINT_ONE_SHOT:
37-
self._bp.pop(rawbp.GetId(), None)
38-
self._control.RemoveBreakpoint(rawbp)
39-
39+
count -= 1
40+
if count == 0:
41+
# We can disable here and but not delete
42+
rawbp.RemoveFlags(DbgEng.DEBUG_BREAKPOINT_ENABLED)
4043
return ret
4144

4245
def set(self, expr, handler=None, type=None, windbgcmd=None, oneshot=False,
43-
passcount=None, threadid=None, size=None, access=None):
44-
# XXX - would be nice to check if bp with expr already exists
45-
# if so, then enable existing, else add
46-
if type is None:
47-
type = DbgEng.DEBUG_BREAKPOINT_CODE
48-
if isinstance(expr, int):
49-
expr = b"0x%x" % expr
50-
elif isinstance(expr, str):
51-
expr = expr.encode()
52-
bp = self._control.AddBreakpoint(type)
53-
id = bp.GetId()
54-
bp.SetOffsetExpression(expr)
55-
if windbgcmd:
56-
pass
46+
passcount=None, threadid=None, size=None, access=None,
47+
count=0xffffffff):
48+
49+
existingid = self.find(expr)
50+
if existingid != -1:
51+
# Just enable
52+
self.enable(existingid)
53+
return
54+
else:
55+
if type is None:
56+
type = DbgEng.DEBUG_BREAKPOINT_CODE
57+
if isinstance(expr, int):
58+
expr = b"0x%x" % expr
59+
elif isinstance(expr, str):
60+
expr = expr.encode()
61+
bp = self._control.AddBreakpoint(type)
62+
id = bp.GetId()
63+
bp.SetOffsetExpression(expr)
64+
if windbgcmd:
65+
pass
66+
if type == DbgEng.DEBUG_BREAKPOINT_DATA:
67+
bp.SetDataParameters(size, access)
5768
if passcount is not None:
5869
bp.SetPassCount(passcount)
5970
if threadid:
6071
bp.SetMatchThreadId(threadid)
61-
if type == DbgEng.DEBUG_BREAKPOINT_DATA:
62-
bp.SetDataParameters(size, access)
6372
if oneshot:
64-
bp.AddFlags(DbgEng.DEBUG_BREAKPOINT_ONE_SHOT)
65-
self._bp[id] = handler
73+
count = 1
74+
# comtypes can't properly handle true one shot
75+
#bp.AddFlags(DbgEng.DEBUG_BREAKPOINT_ONE_SHOT)
76+
self._bp[id] = (count,handler)
6677
if not bp.GetFlags() & DbgEng.DEBUG_BREAKPOINT_DEFERRED:
6778
self.enable(id)
6879
return id
@@ -98,3 +109,45 @@ def remove_all(self):
98109
for bpid in self:
99110
self.remove(bpid)
100111

112+
def find_id(self, fid):
113+
for bpid in self:
114+
if bpid == fid:
115+
return fid
116+
return -1
117+
118+
def find_expr(self, expr):
119+
ids = [bpid for bpid in self]
120+
for bpid in ids:
121+
try:
122+
bp = self._control.GetBreakpointById(bpid)
123+
except exception.E_NOINTERFACE_Error:
124+
self.breakpoints._remove_stale(bpid)
125+
continue
126+
127+
bpexpr = bp.GetOffsetExpression()
128+
print(bpexpr)
129+
#expr = self.get_name_by_offset(bp.GetOffset())
130+
if bpexpr == expr:
131+
return bpid
132+
return -1
133+
134+
def find_offset(self, offset):
135+
ids = [bpid for bpid in self]
136+
for bpid in ids:
137+
try:
138+
bp = self._control.GetBreakpointById(bpid)
139+
except exception.E_NOINTERFACE_Error:
140+
self.breakpoints._remove_stale(bpid)
141+
continue
142+
143+
bpoff = bp.GetOffset()
144+
if bpoff == offset:
145+
return bpid
146+
return -1
147+
148+
149+
def find(self, expr):
150+
bpid = self.find_expr(expr)
151+
if bpid == -1:
152+
bpid = self.find_offset(expr)
153+
return bpid

pybag/dbgeng/idebugcontrol.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
from .idebugbreakpoint import DebugBreakpoint
77
from .util import logger
88

9+
from comtypes.gen.DbgEng import IDebugBreakpoint2
10+
from comtypes import _compointer_base
11+
912

1013
class DebugControl(object):
1114
def __init__(self, controlobj):
@@ -392,7 +395,15 @@ def AddBreakpoint(self, type=DbgEng.DEBUG_BREAKPOINT_CODE):
392395
return DebugBreakpoint(bp)
393396

394397
def RemoveBreakpoint(self, bp):
395-
hr = self._ctrl.RemoveBreakpoint(bp._bp)
398+
# We have to let comtypes get out of the way
399+
# So we get the raw pointer value, release the object, and call with that
400+
tmp = super(_compointer_base, bp._bp).value
401+
del bp._bp
402+
import gc
403+
gc.collect()
404+
#print(f"{tmp=:#016x}")
405+
hr = self._ctrl.RemoveBreakpoint(cast(tmp, POINTER(IDebugBreakpoint2)))
406+
#hr = self._ctrl.RemoveBreakpoint(bp._bp)
396407
exception.check_err(hr)
397408

398409
def AddExtension(self):

pybag/pydbg.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ def dispatch_events(self):
256256
def _worker_wait(self, msg, timeout=DbgEng.WAIT_INFINITE, args=None):
257257
if timeout == -1:
258258
timeout = 0xffffffff
259-
elif timeout != 0xffffffff:
259+
if timeout != 0xffffffff:
260260
timeout *= 1000
261261

262262
item = WorkItem(msg, timeout, args)

0 commit comments

Comments
 (0)