Merge pull request #90 from gizmo98/patch-2
[libretro-ppsspp.git] / Core / Debugger / Breakpoints.cpp
blob82318940548cb4e9dd8c1357f097fa12872eaede
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"
25 #include <cstdio>
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_;
33 MemCheck::MemCheck()
35 numHits = 0;
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;
47 if (cond & mask)
49 ++numHits;
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)
65 lastAddr = addr;
66 lastPC = pc;
67 lastSize = size;
69 // We have to break to find out if it changed.
70 Core_EnableStepping(true);
72 else
74 lastAddr = 0;
75 Action(addr, write, size, pc);
79 void MemCheck::JitCleanup()
81 if (lastAddr == 0 || lastPC == 0)
82 return;
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);
87 if (changed)
89 ++numHits;
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);
99 else
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))
111 if (bp.enabled)
112 return i;
113 // Hold out until the first enabled one.
114 if (found == INVALID_BREAKPOINT)
115 found = i;
119 return found;
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)
127 return i;
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;
144 return true;
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)
159 return true;
162 return false;
165 void CBreakPoints::AddBreakPoint(u32 addr, bool temp)
167 size_t bp = FindBreakpoint(addr, true, temp);
168 if (bp == INVALID_BREAKPOINT)
170 BreakPoint pt;
171 pt.enabled = true;
172 pt.temporary = temp;
173 pt.addr = addr;
175 breakPoints_.push_back(pt);
176 Update(addr);
178 else if (!breakPoints_[bp].enabled)
180 breakPoints_[bp].enabled = true;
181 breakPoints_[bp].hasCond = false;
182 Update(addr);
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);
198 Update(addr);
202 void CBreakPoints::ChangeBreakPoint(u32 addr, bool status)
204 size_t bp = FindBreakpoint(addr);
205 if (bp != INVALID_BREAKPOINT)
207 breakPoints_[bp].enabled = status;
208 Update(addr);
212 void CBreakPoints::ClearAllBreakPoints()
214 if (!breakPoints_.empty())
216 breakPoints_.clear();
217 Update();
221 void CBreakPoints::ClearTemporaryBreakPoints()
223 if (breakPoints_.empty())
224 return;
226 bool update = false;
227 for (int i = (int)breakPoints_.size()-1; i >= 0; --i)
229 if (breakPoints_[i].temporary)
231 breakPoints_.erase(breakPoints_.begin() + i);
232 update = true;
236 if (update)
237 Update();
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;
247 Update();
251 void CBreakPoints::ChangeBreakPointRemoveCond(u32 addr)
253 size_t bp = FindBreakpoint(addr, true, false);
254 if (bp != INVALID_BREAKPOINT)
256 breakPoints_[bp].hasCond = false;
257 Update();
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;
266 return NULL;
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)
277 MemCheck check;
278 check.start = start;
279 check.end = end;
280 check.cond = cond;
281 check.result = result;
283 memChecks_.push_back(check);
284 Update();
286 else
288 memChecks_[mc].cond = (MemCheckCondition)(memChecks_[mc].cond | cond);
289 memChecks_[mc].result = (MemCheckResult)(memChecks_[mc].result | result);
290 Update();
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);
303 Update();
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;
314 Update();
318 void CBreakPoints::ClearAllMemChecks()
320 // This will ruin any pending memchecks.
321 cleanupMemChecks_.clear();
323 if (!memChecks_.empty())
325 memChecks_.clear();
326 Update();
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;
342 if (check.end != 0)
344 if (NotCached(address + size) > NotCached(check.start) && NotCached(address) < NotCached(check.end))
345 return &check;
347 else
349 if (NotCached(check.start) == NotCached(address))
350 return &check;
354 //none found
355 return 0;
358 void CBreakPoints::ExecMemCheck(u32 address, bool write, int size, u32 pc)
360 auto check = GetMemCheck(address, size);
361 if (check)
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);
368 if (check) {
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) {
377 auto check = *it;
378 check->JitCleanup();
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())
392 return pc;
393 return 0;
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;
404 if (check.end != 0)
405 check.end ^= 0x40000000;
406 ranges.push_back(check);
409 return ranges;
412 const std::vector<MemCheck> CBreakPoints::GetMemChecks()
414 return memChecks_;
417 const std::vector<BreakPoint> CBreakPoints::GetBreakpoints()
419 return breakPoints_;
422 void CBreakPoints::Update(u32 addr)
424 if (MIPSComp::jit)
426 bool resume = false;
427 if (Core_IsStepping() == false)
429 Core_EnableStepping(true);
430 Core_WaitInactive(200);
431 resume = true;
434 // In case this is a delay slot, clear the previous instruction too.
435 if (addr != 0)
436 MIPSComp::jit->InvalidateCacheAt(addr - 4, 8);
437 else
438 MIPSComp::jit->InvalidateCache();
440 if (resume)
441 Core_EnableStepping(false);
444 // Redraw in order to show the breakpoint.
445 host->UpdateDisassembly();