1 // Copyright (c) 2012- PPSSPP Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
18 #include "Core/Core.h"
19 #include "Core/Debugger/Breakpoints.h"
20 #include "Core/Debugger/SymbolMap.h"
21 #include "Core/Host.h"
22 #include "Core/MIPS/MIPSAnalyst.h"
23 #include "Core/MIPS/JitCommon/NativeJit.h"
24 #include "Core/CoreTiming.h"
27 std::vector
<BreakPoint
> CBreakPoints::breakPoints_
;
28 u32
CBreakPoints::breakSkipFirstAt_
= 0;
29 u64
CBreakPoints::breakSkipFirstTicks_
= 0;
30 std::vector
<MemCheck
> CBreakPoints::memChecks_
;
31 std::vector
<MemCheck
*> CBreakPoints::cleanupMemChecks_
;
38 void MemCheck::Log(u32 addr
, bool write
, int size
, u32 pc
)
40 if (result
& MEMCHECK_LOG
)
41 NOTICE_LOG(MEMMAP
, "CHK %s%i at %08x (%s), PC=%08x (%s)", write
? "Write" : "Read", size
* 8, addr
, symbolMap
.GetDescription(addr
).c_str(), pc
, symbolMap
.GetDescription(pc
).c_str());
44 void MemCheck::Action(u32 addr
, bool write
, int size
, u32 pc
)
46 int mask
= write
? MEMCHECK_WRITE
: MEMCHECK_READ
;
51 Log(addr
, write
, size
, pc
);
52 if (result
& MEMCHECK_BREAK
)
54 Core_EnableStepping(true);
55 host
->SetDebugMode(true);
60 void MemCheck::JitBefore(u32 addr
, bool write
, int size
, u32 pc
)
62 int mask
= MEMCHECK_WRITE
| MEMCHECK_WRITE_ONCHANGE
;
63 if (write
&& (cond
& mask
) == mask
)
69 // We have to break to find out if it changed.
70 Core_EnableStepping(true);
75 Action(addr
, write
, size
, pc
);
79 void MemCheck::JitCleanup()
81 if (lastAddr
== 0 || lastPC
== 0)
84 // Here's the tricky part: would this have changed memory?
85 // Note that it did not actually get written.
86 bool changed
= MIPSAnalyst::OpWouldChangeMemory(lastPC
, lastAddr
, lastSize
);
90 Log(lastAddr
, true, lastSize
, lastPC
);
93 // Resume if it should not have gone to stepping, or if it did not change.
94 if ((!(result
& MEMCHECK_BREAK
) || !changed
) && coreState
== CORE_STEPPING
)
96 CBreakPoints::SetSkipFirst(lastPC
);
97 Core_EnableStepping(false);
100 host
->SetDebugMode(true);
103 size_t CBreakPoints::FindBreakpoint(u32 addr
, bool matchTemp
, bool temp
)
105 size_t found
= INVALID_BREAKPOINT
;
106 for (size_t i
= 0; i
< breakPoints_
.size(); ++i
)
108 const auto &bp
= breakPoints_
[i
];
109 if (bp
.addr
== addr
&& (!matchTemp
|| bp
.temporary
== temp
))
113 // Hold out until the first enabled one.
114 if (found
== INVALID_BREAKPOINT
)
122 size_t CBreakPoints::FindMemCheck(u32 start
, u32 end
)
124 for (size_t i
= 0; i
< memChecks_
.size(); ++i
)
126 if (memChecks_
[i
].start
== start
&& memChecks_
[i
].end
== end
)
130 return INVALID_MEMCHECK
;
133 bool CBreakPoints::IsAddressBreakPoint(u32 addr
)
135 size_t bp
= FindBreakpoint(addr
);
136 return bp
!= INVALID_BREAKPOINT
&& breakPoints_
[bp
].enabled
;
139 bool CBreakPoints::IsAddressBreakPoint(u32 addr
, bool* enabled
)
141 size_t bp
= FindBreakpoint(addr
);
142 if (bp
== INVALID_BREAKPOINT
) return false;
143 if (enabled
!= NULL
) *enabled
= breakPoints_
[bp
].enabled
;
147 bool CBreakPoints::IsTempBreakPoint(u32 addr
)
149 size_t bp
= FindBreakpoint(addr
, true, true);
150 return bp
!= INVALID_BREAKPOINT
;
153 bool CBreakPoints::RangeContainsBreakPoint(u32 addr
, u32 size
)
155 const u32 end
= addr
+ size
;
156 for (const auto &bp
: breakPoints_
)
158 if (bp
.addr
>= addr
&& bp
.addr
< end
)
165 void CBreakPoints::AddBreakPoint(u32 addr
, bool temp
)
167 size_t bp
= FindBreakpoint(addr
, true, temp
);
168 if (bp
== INVALID_BREAKPOINT
)
175 breakPoints_
.push_back(pt
);
178 else if (!breakPoints_
[bp
].enabled
)
180 breakPoints_
[bp
].enabled
= true;
181 breakPoints_
[bp
].hasCond
= false;
186 void CBreakPoints::RemoveBreakPoint(u32 addr
)
188 size_t bp
= FindBreakpoint(addr
);
189 if (bp
!= INVALID_BREAKPOINT
)
191 breakPoints_
.erase(breakPoints_
.begin() + bp
);
193 // Check again, there might've been an overlapping temp breakpoint.
194 bp
= FindBreakpoint(addr
);
195 if (bp
!= INVALID_BREAKPOINT
)
196 breakPoints_
.erase(breakPoints_
.begin() + bp
);
202 void CBreakPoints::ChangeBreakPoint(u32 addr
, bool status
)
204 size_t bp
= FindBreakpoint(addr
);
205 if (bp
!= INVALID_BREAKPOINT
)
207 breakPoints_
[bp
].enabled
= status
;
212 void CBreakPoints::ClearAllBreakPoints()
214 if (!breakPoints_
.empty())
216 breakPoints_
.clear();
221 void CBreakPoints::ClearTemporaryBreakPoints()
223 if (breakPoints_
.empty())
227 for (int i
= (int)breakPoints_
.size()-1; i
>= 0; --i
)
229 if (breakPoints_
[i
].temporary
)
231 breakPoints_
.erase(breakPoints_
.begin() + i
);
240 void CBreakPoints::ChangeBreakPointAddCond(u32 addr
, const BreakPointCond
&cond
)
242 size_t bp
= FindBreakpoint(addr
, true, false);
243 if (bp
!= INVALID_BREAKPOINT
)
245 breakPoints_
[bp
].hasCond
= true;
246 breakPoints_
[bp
].cond
= cond
;
251 void CBreakPoints::ChangeBreakPointRemoveCond(u32 addr
)
253 size_t bp
= FindBreakpoint(addr
, true, false);
254 if (bp
!= INVALID_BREAKPOINT
)
256 breakPoints_
[bp
].hasCond
= false;
261 BreakPointCond
*CBreakPoints::GetBreakPointCondition(u32 addr
)
263 size_t bp
= FindBreakpoint(addr
, true, false);
264 if (bp
!= INVALID_BREAKPOINT
&& breakPoints_
[bp
].hasCond
)
265 return &breakPoints_
[bp
].cond
;
269 void CBreakPoints::AddMemCheck(u32 start
, u32 end
, MemCheckCondition cond
, MemCheckResult result
)
271 // This will ruin any pending memchecks.
272 cleanupMemChecks_
.clear();
274 size_t mc
= FindMemCheck(start
, end
);
275 if (mc
== INVALID_MEMCHECK
)
281 check
.result
= result
;
283 memChecks_
.push_back(check
);
288 memChecks_
[mc
].cond
= (MemCheckCondition
)(memChecks_
[mc
].cond
| cond
);
289 memChecks_
[mc
].result
= (MemCheckResult
)(memChecks_
[mc
].result
| result
);
294 void CBreakPoints::RemoveMemCheck(u32 start
, u32 end
)
296 // This will ruin any pending memchecks.
297 cleanupMemChecks_
.clear();
299 size_t mc
= FindMemCheck(start
, end
);
300 if (mc
!= INVALID_MEMCHECK
)
302 memChecks_
.erase(memChecks_
.begin() + mc
);
307 void CBreakPoints::ChangeMemCheck(u32 start
, u32 end
, MemCheckCondition cond
, MemCheckResult result
)
309 size_t mc
= FindMemCheck(start
, end
);
310 if (mc
!= INVALID_MEMCHECK
)
312 memChecks_
[mc
].cond
= cond
;
313 memChecks_
[mc
].result
= result
;
318 void CBreakPoints::ClearAllMemChecks()
320 // This will ruin any pending memchecks.
321 cleanupMemChecks_
.clear();
323 if (!memChecks_
.empty())
330 static inline u32
NotCached(u32 val
)
332 // Remove the cached part of the address.
333 return val
& ~0x40000000;
336 MemCheck
*CBreakPoints::GetMemCheck(u32 address
, int size
)
338 std::vector
<MemCheck
>::iterator iter
;
339 for (iter
= memChecks_
.begin(); iter
!= memChecks_
.end(); ++iter
)
341 MemCheck
&check
= *iter
;
344 if (NotCached(address
+ size
) > NotCached(check
.start
) && NotCached(address
) < NotCached(check
.end
))
349 if (NotCached(check
.start
) == NotCached(address
))
358 void CBreakPoints::ExecMemCheck(u32 address
, bool write
, int size
, u32 pc
)
360 auto check
= GetMemCheck(address
, size
);
362 check
->Action(address
, write
, size
, pc
);
365 void CBreakPoints::ExecMemCheckJitBefore(u32 address
, bool write
, int size
, u32 pc
)
367 auto check
= GetMemCheck(address
, size
);
369 check
->JitBefore(address
, write
, size
, pc
);
370 cleanupMemChecks_
.push_back(check
);
374 void CBreakPoints::ExecMemCheckJitCleanup()
376 for (auto it
= cleanupMemChecks_
.begin(), end
= cleanupMemChecks_
.end(); it
!= end
; ++it
) {
380 cleanupMemChecks_
.clear();
383 void CBreakPoints::SetSkipFirst(u32 pc
)
385 breakSkipFirstAt_
= pc
;
386 breakSkipFirstTicks_
= CoreTiming::GetTicks();
388 u32
CBreakPoints::CheckSkipFirst()
390 u32 pc
= breakSkipFirstAt_
;
391 if (breakSkipFirstTicks_
== CoreTiming::GetTicks())
396 const std::vector
<MemCheck
> CBreakPoints::GetMemCheckRanges()
398 std::vector
<MemCheck
> ranges
= memChecks_
;
399 for (auto it
= memChecks_
.begin(), end
= memChecks_
.end(); it
!= end
; ++it
)
401 MemCheck check
= *it
;
402 // Toggle the cached part of the address.
403 check
.start
^= 0x40000000;
405 check
.end
^= 0x40000000;
406 ranges
.push_back(check
);
412 const std::vector
<MemCheck
> CBreakPoints::GetMemChecks()
417 const std::vector
<BreakPoint
> CBreakPoints::GetBreakpoints()
422 void CBreakPoints::Update(u32 addr
)
427 if (Core_IsStepping() == false)
429 Core_EnableStepping(true);
430 Core_WaitInactive(200);
434 // In case this is a delay slot, clear the previous instruction too.
436 MIPSComp::jit
->InvalidateCacheAt(addr
- 4, 8);
438 MIPSComp::jit
->InvalidateCache();
441 Core_EnableStepping(false);
444 // Redraw in order to show the breakpoint.
445 host
->UpdateDisassembly();