CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/Debugger/Breakpoints.cpp
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
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.
6
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.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <cstdio>
19
#include <atomic>
20
#include <mutex>
21
22
#include "Common/System/System.h"
23
#include "Common/Log.h"
24
#include "Core/Core.h"
25
#include "Core/Debugger/Breakpoints.h"
26
#include "Core/Debugger/MemBlockInfo.h"
27
#include "Core/Debugger/SymbolMap.h"
28
#include "Core/MemMap.h"
29
#include "Core/MIPS/MIPSAnalyst.h"
30
#include "Core/MIPS/MIPSDebugInterface.h"
31
#include "Core/MIPS/JitCommon/JitCommon.h"
32
#include "Core/CoreTiming.h"
33
34
std::atomic<bool> anyBreakPoints_(false);
35
std::atomic<bool> anyMemChecks_(false);
36
37
static std::mutex breakPointsMutex_;
38
std::vector<BreakPoint> CBreakPoints::breakPoints_;
39
u32 CBreakPoints::breakSkipFirstAt_ = 0;
40
u64 CBreakPoints::breakSkipFirstTicks_ = 0;
41
static std::mutex memCheckMutex_;
42
std::vector<MemCheck> CBreakPoints::memChecks_;
43
std::vector<MemCheck> CBreakPoints::memCheckRangesRead_;
44
std::vector<MemCheck> CBreakPoints::memCheckRangesWrite_;
45
46
void MemCheck::Log(u32 addr, bool write, int size, u32 pc, const char *reason) {
47
if (result & BREAK_ACTION_LOG) {
48
const char *type = write ? "Write" : "Read";
49
if (logFormat.empty()) {
50
NOTICE_LOG(Log::MemMap, "CHK %s%i(%s) at %08x (%s), PC=%08x (%s)", type, size * 8, reason, addr, g_symbolMap->GetDescription(addr).c_str(), pc, g_symbolMap->GetDescription(pc).c_str());
51
} else {
52
std::string formatted;
53
CBreakPoints::EvaluateLogFormat(currentDebugMIPS, logFormat, formatted);
54
NOTICE_LOG(Log::MemMap, "CHK %s%i(%s) at %08x: %s", type, size * 8, reason, addr, formatted.c_str());
55
}
56
}
57
}
58
59
BreakAction MemCheck::Apply(u32 addr, bool write, int size, u32 pc) {
60
int mask = write ? MEMCHECK_WRITE : MEMCHECK_READ;
61
if (cond & mask) {
62
if (hasCondition) {
63
if (!condition.Evaluate())
64
return BREAK_ACTION_IGNORE;
65
}
66
67
++numHits;
68
return result;
69
}
70
71
return BREAK_ACTION_IGNORE;
72
}
73
74
BreakAction MemCheck::Action(u32 addr, bool write, int size, u32 pc, const char *reason) {
75
// Conditions have always already been checked if we get here.
76
Log(addr, write, size, pc, reason);
77
if ((result & BREAK_ACTION_PAUSE) && coreState != CORE_POWERUP) {
78
Core_EnableStepping(true, "memory.breakpoint", start);
79
}
80
81
return result;
82
}
83
84
// Note: must lock while calling this.
85
size_t CBreakPoints::FindBreakpoint(u32 addr, bool matchTemp, bool temp)
86
{
87
size_t found = INVALID_BREAKPOINT;
88
for (size_t i = 0; i < breakPoints_.size(); ++i)
89
{
90
const auto &bp = breakPoints_[i];
91
if (bp.addr == addr && (!matchTemp || bp.temporary == temp))
92
{
93
if (bp.IsEnabled())
94
return i;
95
// Hold out until the first enabled one.
96
if (found == INVALID_BREAKPOINT)
97
found = i;
98
}
99
}
100
101
return found;
102
}
103
104
size_t CBreakPoints::FindMemCheck(u32 start, u32 end)
105
{
106
for (size_t i = 0; i < memChecks_.size(); ++i)
107
{
108
if (memChecks_[i].start == start && memChecks_[i].end == end)
109
return i;
110
}
111
112
return INVALID_MEMCHECK;
113
}
114
115
bool CBreakPoints::IsAddressBreakPoint(u32 addr)
116
{
117
if (!anyBreakPoints_)
118
return false;
119
std::lock_guard<std::mutex> guard(breakPointsMutex_);
120
size_t bp = FindBreakpoint(addr);
121
return bp != INVALID_BREAKPOINT && breakPoints_[bp].result != BREAK_ACTION_IGNORE;
122
}
123
124
bool CBreakPoints::IsAddressBreakPoint(u32 addr, bool* enabled)
125
{
126
if (!anyBreakPoints_)
127
return false;
128
std::lock_guard<std::mutex> guard(breakPointsMutex_);
129
size_t bp = FindBreakpoint(addr);
130
if (bp == INVALID_BREAKPOINT) return false;
131
if (enabled != nullptr)
132
*enabled = breakPoints_[bp].IsEnabled();
133
return true;
134
}
135
136
bool CBreakPoints::IsTempBreakPoint(u32 addr)
137
{
138
std::lock_guard<std::mutex> guard(breakPointsMutex_);
139
size_t bp = FindBreakpoint(addr, true, true);
140
return bp != INVALID_BREAKPOINT;
141
}
142
143
bool CBreakPoints::RangeContainsBreakPoint(u32 addr, u32 size)
144
{
145
if (!anyBreakPoints_)
146
return false;
147
std::lock_guard<std::mutex> guard(breakPointsMutex_);
148
const u32 end = addr + size;
149
for (const auto &bp : breakPoints_)
150
{
151
if (bp.addr >= addr && bp.addr < end)
152
return true;
153
}
154
155
return false;
156
}
157
158
void CBreakPoints::AddBreakPoint(u32 addr, bool temp)
159
{
160
std::unique_lock<std::mutex> guard(breakPointsMutex_);
161
size_t bp = FindBreakpoint(addr, true, temp);
162
if (bp == INVALID_BREAKPOINT)
163
{
164
BreakPoint pt;
165
pt.result |= BREAK_ACTION_PAUSE;
166
pt.temporary = temp;
167
pt.addr = addr;
168
169
breakPoints_.push_back(pt);
170
anyBreakPoints_ = true;
171
guard.unlock();
172
Update(addr);
173
}
174
else if (!breakPoints_[bp].IsEnabled())
175
{
176
breakPoints_[bp].result |= BREAK_ACTION_PAUSE;
177
breakPoints_[bp].hasCond = false;
178
guard.unlock();
179
Update(addr);
180
}
181
}
182
183
void CBreakPoints::RemoveBreakPoint(u32 addr)
184
{
185
std::unique_lock<std::mutex> guard(breakPointsMutex_);
186
size_t bp = FindBreakpoint(addr);
187
if (bp != INVALID_BREAKPOINT)
188
{
189
breakPoints_.erase(breakPoints_.begin() + bp);
190
191
// Check again, there might've been an overlapping temp breakpoint.
192
bp = FindBreakpoint(addr);
193
if (bp != INVALID_BREAKPOINT)
194
breakPoints_.erase(breakPoints_.begin() + bp);
195
196
anyBreakPoints_ = !breakPoints_.empty();
197
guard.unlock();
198
Update(addr);
199
}
200
}
201
202
void CBreakPoints::ChangeBreakPoint(u32 addr, bool status)
203
{
204
std::unique_lock<std::mutex> guard(breakPointsMutex_);
205
size_t bp = FindBreakpoint(addr);
206
if (bp != INVALID_BREAKPOINT)
207
{
208
if (status)
209
breakPoints_[bp].result |= BREAK_ACTION_PAUSE;
210
else
211
breakPoints_[bp].result = BreakAction(breakPoints_[bp].result & ~BREAK_ACTION_PAUSE);
212
213
guard.unlock();
214
Update(addr);
215
}
216
}
217
218
void CBreakPoints::ChangeBreakPoint(u32 addr, BreakAction result)
219
{
220
std::unique_lock<std::mutex> guard(breakPointsMutex_);
221
size_t bp = FindBreakpoint(addr);
222
if (bp != INVALID_BREAKPOINT)
223
{
224
breakPoints_[bp].result = result;
225
guard.unlock();
226
Update(addr);
227
}
228
}
229
230
void CBreakPoints::ClearAllBreakPoints()
231
{
232
if (!anyBreakPoints_)
233
return;
234
std::unique_lock<std::mutex> guard(breakPointsMutex_);
235
if (!breakPoints_.empty())
236
{
237
breakPoints_.clear();
238
guard.unlock();
239
Update();
240
}
241
}
242
243
void CBreakPoints::ClearTemporaryBreakPoints()
244
{
245
if (!anyBreakPoints_)
246
return;
247
std::unique_lock<std::mutex> guard(breakPointsMutex_);
248
249
bool update = false;
250
for (int i = (int)breakPoints_.size()-1; i >= 0; --i)
251
{
252
if (breakPoints_[i].temporary)
253
{
254
breakPoints_.erase(breakPoints_.begin() + i);
255
update = true;
256
}
257
}
258
259
guard.unlock();
260
if (update)
261
Update();
262
}
263
264
void CBreakPoints::ChangeBreakPointAddCond(u32 addr, const BreakPointCond &cond)
265
{
266
std::unique_lock<std::mutex> guard(breakPointsMutex_);
267
size_t bp = FindBreakpoint(addr);
268
if (bp != INVALID_BREAKPOINT)
269
{
270
breakPoints_[bp].hasCond = true;
271
breakPoints_[bp].cond = cond;
272
guard.unlock();
273
Update(addr);
274
}
275
}
276
277
void CBreakPoints::ChangeBreakPointRemoveCond(u32 addr)
278
{
279
std::unique_lock<std::mutex> guard(breakPointsMutex_);
280
size_t bp = FindBreakpoint(addr);
281
if (bp != INVALID_BREAKPOINT)
282
{
283
breakPoints_[bp].hasCond = false;
284
guard.unlock();
285
Update(addr);
286
}
287
}
288
289
BreakPointCond *CBreakPoints::GetBreakPointCondition(u32 addr)
290
{
291
std::lock_guard<std::mutex> guard(breakPointsMutex_);
292
size_t bp = FindBreakpoint(addr);
293
if (bp != INVALID_BREAKPOINT && breakPoints_[bp].hasCond)
294
return &breakPoints_[bp].cond;
295
return NULL;
296
}
297
298
void CBreakPoints::ChangeBreakPointLogFormat(u32 addr, const std::string &fmt) {
299
std::unique_lock<std::mutex> guard(breakPointsMutex_);
300
size_t bp = FindBreakpoint(addr, true, false);
301
if (bp != INVALID_BREAKPOINT) {
302
breakPoints_[bp].logFormat = fmt;
303
guard.unlock();
304
Update(addr);
305
}
306
}
307
308
BreakAction CBreakPoints::ExecBreakPoint(u32 addr) {
309
if (!anyBreakPoints_)
310
return BREAK_ACTION_IGNORE;
311
std::unique_lock<std::mutex> guard(breakPointsMutex_);
312
size_t bp = FindBreakpoint(addr, false);
313
if (bp != INVALID_BREAKPOINT) {
314
const BreakPoint &info = breakPoints_[bp];
315
guard.unlock();
316
317
if (info.hasCond) {
318
// Evaluate the breakpoint and abort if necessary.
319
auto cond = CBreakPoints::GetBreakPointCondition(currentMIPS->pc);
320
if (cond && !cond->Evaluate())
321
return BREAK_ACTION_IGNORE;
322
}
323
324
if (info.result & BREAK_ACTION_LOG) {
325
if (info.logFormat.empty()) {
326
NOTICE_LOG(Log::JIT, "BKP PC=%08x (%s)", addr, g_symbolMap->GetDescription(addr).c_str());
327
} else {
328
std::string formatted;
329
CBreakPoints::EvaluateLogFormat(currentDebugMIPS, info.logFormat, formatted);
330
NOTICE_LOG(Log::JIT, "BKP PC=%08x: %s", addr, formatted.c_str());
331
}
332
}
333
if ((info.result & BREAK_ACTION_PAUSE) && coreState != CORE_POWERUP) {
334
Core_EnableStepping(true, "cpu.breakpoint", info.addr);
335
}
336
337
return info.result;
338
}
339
340
return BREAK_ACTION_IGNORE;
341
}
342
343
void CBreakPoints::AddMemCheck(u32 start, u32 end, MemCheckCondition cond, BreakAction result)
344
{
345
std::unique_lock<std::mutex> guard(memCheckMutex_);
346
347
size_t mc = FindMemCheck(start, end);
348
if (mc == INVALID_MEMCHECK)
349
{
350
MemCheck check;
351
check.start = start;
352
check.end = end;
353
check.cond = cond;
354
check.result = result;
355
356
memChecks_.push_back(check);
357
bool hadAny = anyMemChecks_.exchange(true);
358
if (!hadAny)
359
MemBlockOverrideDetailed();
360
guard.unlock();
361
Update();
362
}
363
else
364
{
365
memChecks_[mc].cond = (MemCheckCondition)(memChecks_[mc].cond | cond);
366
memChecks_[mc].result = (BreakAction)(memChecks_[mc].result | result);
367
bool hadAny = anyMemChecks_.exchange(true);
368
if (!hadAny)
369
MemBlockOverrideDetailed();
370
guard.unlock();
371
Update();
372
}
373
}
374
375
void CBreakPoints::RemoveMemCheck(u32 start, u32 end)
376
{
377
std::unique_lock<std::mutex> guard(memCheckMutex_);
378
379
size_t mc = FindMemCheck(start, end);
380
if (mc != INVALID_MEMCHECK)
381
{
382
memChecks_.erase(memChecks_.begin() + mc);
383
bool hadAny = anyMemChecks_.exchange(!memChecks_.empty());
384
if (hadAny)
385
MemBlockReleaseDetailed();
386
guard.unlock();
387
Update();
388
}
389
}
390
391
void CBreakPoints::ChangeMemCheck(u32 start, u32 end, MemCheckCondition cond, BreakAction result)
392
{
393
std::unique_lock<std::mutex> guard(memCheckMutex_);
394
size_t mc = FindMemCheck(start, end);
395
if (mc != INVALID_MEMCHECK)
396
{
397
memChecks_[mc].cond = cond;
398
memChecks_[mc].result = result;
399
guard.unlock();
400
Update();
401
}
402
}
403
404
void CBreakPoints::ClearAllMemChecks()
405
{
406
std::unique_lock<std::mutex> guard(memCheckMutex_);
407
408
if (!memChecks_.empty())
409
{
410
memChecks_.clear();
411
bool hadAny = anyMemChecks_.exchange(false);
412
if (hadAny)
413
MemBlockReleaseDetailed();
414
guard.unlock();
415
Update();
416
}
417
}
418
419
420
void CBreakPoints::ChangeMemCheckAddCond(u32 start, u32 end, const BreakPointCond &cond) {
421
std::unique_lock<std::mutex> guard(memCheckMutex_);
422
size_t mc = FindMemCheck(start, end);
423
if (mc != INVALID_MEMCHECK) {
424
memChecks_[mc].hasCondition = true;
425
memChecks_[mc].condition = cond;
426
guard.unlock();
427
// No need to update jit for a condition add/remove, they're not baked in.
428
Update(-1);
429
}
430
}
431
432
void CBreakPoints::ChangeMemCheckRemoveCond(u32 start, u32 end) {
433
std::unique_lock<std::mutex> guard(memCheckMutex_);
434
size_t mc = FindMemCheck(start, end);
435
if (mc != INVALID_MEMCHECK) {
436
memChecks_[mc].hasCondition = false;
437
guard.unlock();
438
// No need to update jit for a condition add/remove, they're not baked in.
439
Update(-1);
440
}
441
}
442
443
BreakPointCond *CBreakPoints::GetMemCheckCondition(u32 start, u32 end) {
444
std::unique_lock<std::mutex> guard(memCheckMutex_);
445
size_t mc = FindMemCheck(start, end);
446
if (mc != INVALID_MEMCHECK && memChecks_[mc].hasCondition)
447
return &memChecks_[mc].condition;
448
return nullptr;
449
}
450
451
void CBreakPoints::ChangeMemCheckLogFormat(u32 start, u32 end, const std::string &fmt) {
452
std::unique_lock<std::mutex> guard(memCheckMutex_);
453
size_t mc = FindMemCheck(start, end);
454
if (mc != INVALID_MEMCHECK) {
455
memChecks_[mc].logFormat = fmt;
456
guard.unlock();
457
Update();
458
}
459
}
460
461
bool CBreakPoints::GetMemCheck(u32 start, u32 end, MemCheck *check) {
462
std::lock_guard<std::mutex> guard(memCheckMutex_);
463
size_t mc = FindMemCheck(start, end);
464
if (mc != INVALID_MEMCHECK) {
465
*check = memChecks_[mc];
466
return true;
467
}
468
return false;
469
}
470
471
static inline u32 NotCached(u32 val) {
472
// Remove the cached part of the address as well as any mirror.
473
if ((val & 0x3F800000) == 0x04000000)
474
return val & ~0x40600000;
475
return val & ~0x40000000;
476
}
477
478
bool CBreakPoints::GetMemCheckInRange(u32 address, int size, MemCheck *check) {
479
std::lock_guard<std::mutex> guard(memCheckMutex_);
480
auto result = GetMemCheckLocked(address, size);
481
if (result)
482
*check = *result;
483
return result != nullptr;
484
}
485
486
MemCheck *CBreakPoints::GetMemCheckLocked(u32 address, int size) {
487
std::vector<MemCheck>::iterator iter;
488
for (iter = memChecks_.begin(); iter != memChecks_.end(); ++iter)
489
{
490
MemCheck &check = *iter;
491
if (check.end != 0)
492
{
493
if (NotCached(address + size) > NotCached(check.start) && NotCached(address) < NotCached(check.end))
494
return &check;
495
}
496
else
497
{
498
if (NotCached(check.start) == NotCached(address))
499
return &check;
500
}
501
}
502
503
//none found
504
return 0;
505
}
506
507
BreakAction CBreakPoints::ExecMemCheck(u32 address, bool write, int size, u32 pc, const char *reason)
508
{
509
if (!anyMemChecks_)
510
return BREAK_ACTION_IGNORE;
511
std::unique_lock<std::mutex> guard(memCheckMutex_);
512
auto check = GetMemCheckLocked(address, size);
513
if (check) {
514
BreakAction applyAction = check->Apply(address, write, size, pc);
515
if (applyAction == BREAK_ACTION_IGNORE)
516
return applyAction;
517
518
auto copy = *check;
519
guard.unlock();
520
return copy.Action(address, write, size, pc, reason);
521
}
522
return BREAK_ACTION_IGNORE;
523
}
524
525
BreakAction CBreakPoints::ExecOpMemCheck(u32 address, u32 pc)
526
{
527
// Note: currently, we don't check "on changed" for HLE (ExecMemCheck.)
528
// We'd need to more carefully specify memory changes in HLE for that.
529
int size = MIPSAnalyst::OpMemoryAccessSize(pc);
530
if (size == 0 && MIPSAnalyst::OpHasDelaySlot(pc)) {
531
// This means that the delay slot is what tripped us.
532
pc += 4;
533
size = MIPSAnalyst::OpMemoryAccessSize(pc);
534
}
535
536
bool write = MIPSAnalyst::IsOpMemoryWrite(pc);
537
std::unique_lock<std::mutex> guard(memCheckMutex_);
538
auto check = GetMemCheckLocked(address, size);
539
if (check) {
540
int mask = MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE;
541
bool apply = false;
542
if (write && (check->cond & mask) == mask) {
543
if (MIPSAnalyst::OpWouldChangeMemory(pc, address, size)) {
544
apply = true;
545
}
546
} else {
547
apply = true;
548
}
549
if (apply) {
550
BreakAction applyAction = check->Apply(address, write, size, pc);
551
if (applyAction == BREAK_ACTION_IGNORE)
552
return applyAction;
553
554
// Make a copy so we can safely unlock.
555
auto copy = *check;
556
guard.unlock();
557
return copy.Action(address, write, size, pc, "CPU");
558
}
559
}
560
return BREAK_ACTION_IGNORE;
561
}
562
563
void CBreakPoints::SetSkipFirst(u32 pc)
564
{
565
breakSkipFirstAt_ = pc;
566
breakSkipFirstTicks_ = CoreTiming::GetTicks();
567
}
568
u32 CBreakPoints::CheckSkipFirst()
569
{
570
u32 pc = breakSkipFirstAt_;
571
if (breakSkipFirstTicks_ == CoreTiming::GetTicks())
572
return pc;
573
return 0;
574
}
575
576
static MemCheck NotCached(MemCheck mc) {
577
// Toggle the cached part of the address.
578
mc.start ^= 0x40000000;
579
if (mc.end != 0)
580
mc.end ^= 0x40000000;
581
return mc;
582
}
583
584
static MemCheck VRAMMirror(uint8_t mirror, MemCheck mc) {
585
mc.start &= ~0x00600000;
586
mc.start += 0x00200000 * mirror;
587
if (mc.end != 0) {
588
mc.end &= ~0x00600000;
589
mc.end += 0x00200000 * mirror;
590
if (mc.end < mc.start)
591
mc.end += 0x00200000;
592
}
593
return mc;
594
}
595
596
void CBreakPoints::UpdateCachedMemCheckRanges() {
597
std::lock_guard<std::mutex> guard(memCheckMutex_);
598
memCheckRangesRead_.clear();
599
memCheckRangesWrite_.clear();
600
601
auto add = [&](bool read, bool write, const MemCheck &mc) {
602
if (read)
603
memCheckRangesRead_.push_back(mc);
604
if (write)
605
memCheckRangesWrite_.push_back(mc);
606
};
607
608
for (const auto &check : memChecks_) {
609
bool read = (check.cond & MEMCHECK_READ) != 0;
610
bool write = (check.cond & MEMCHECK_WRITE) != 0;
611
612
if (Memory::IsVRAMAddress(check.start) && (check.end == 0 || Memory::IsVRAMAddress(check.end))) {
613
for (uint8_t mirror = 0; mirror < 4; ++mirror) {
614
MemCheck copy = VRAMMirror(mirror, check);
615
add(read, write, copy);
616
add(read, write, NotCached(copy));
617
}
618
} else {
619
add(read, write, check);
620
add(read, write, NotCached(check));
621
}
622
}
623
}
624
625
std::vector<MemCheck> CBreakPoints::GetMemCheckRanges(bool write) {
626
std::lock_guard<std::mutex> guard(memCheckMutex_);
627
if (write)
628
return memCheckRangesWrite_;
629
return memCheckRangesRead_;
630
}
631
632
std::vector<MemCheck> CBreakPoints::GetMemChecks()
633
{
634
std::lock_guard<std::mutex> guard(memCheckMutex_);
635
return memChecks_;
636
}
637
638
std::vector<BreakPoint> CBreakPoints::GetBreakpoints()
639
{
640
std::lock_guard<std::mutex> guard(breakPointsMutex_);
641
return breakPoints_;
642
}
643
644
bool CBreakPoints::HasBreakPoints() {
645
return anyBreakPoints_;
646
}
647
648
bool CBreakPoints::HasMemChecks() {
649
return anyMemChecks_;
650
}
651
652
void CBreakPoints::Update(u32 addr) {
653
if (MIPSComp::jit && addr != -1) {
654
bool resume = false;
655
if (Core_IsStepping() == false) {
656
Core_EnableStepping(true, "cpu.breakpoint.update", addr);
657
Core_WaitInactive(200);
658
resume = true;
659
}
660
661
// In case this is a delay slot, clear the previous instruction too.
662
if (addr != 0)
663
mipsr4k.InvalidateICache(addr - 4, 8);
664
else
665
mipsr4k.ClearJitCache();
666
667
if (resume)
668
Core_EnableStepping(false);
669
}
670
671
if (anyMemChecks_ && addr != -1)
672
UpdateCachedMemCheckRanges();
673
674
// Redraw in order to show the breakpoint.
675
System_Notify(SystemNotification::DISASSEMBLY);
676
}
677
678
bool CBreakPoints::ValidateLogFormat(DebugInterface *cpu, const std::string &fmt) {
679
std::string ignore;
680
return EvaluateLogFormat(cpu, fmt, ignore);
681
}
682
683
bool CBreakPoints::EvaluateLogFormat(DebugInterface *cpu, const std::string &fmt, std::string &result) {
684
PostfixExpression exp;
685
result.clear();
686
687
size_t pos = 0;
688
while (pos < fmt.size()) {
689
size_t next = fmt.find_first_of('{', pos);
690
if (next == fmt.npos) {
691
// End of the string.
692
result += fmt.substr(pos);
693
break;
694
}
695
if (next != pos) {
696
result += fmt.substr(pos, next - pos);
697
pos = next;
698
}
699
700
size_t end = fmt.find_first_of('}', next + 1);
701
if (end == fmt.npos) {
702
// Invalid: every expression needs a { and a }.
703
return false;
704
}
705
706
std::string expression = fmt.substr(next + 1, end - next - 1);
707
if (expression.empty()) {
708
result += "{}";
709
} else {
710
int type = 'x';
711
if (expression.length() > 2 && expression[expression.length() - 2] == ':') {
712
switch (expression[expression.length() - 1]) {
713
case 'd':
714
case 'f':
715
case 'p':
716
case 's':
717
case 'x':
718
type = expression[expression.length() - 1];
719
expression.resize(expression.length() - 2);
720
break;
721
722
default:
723
// Assume a ternary.
724
break;
725
}
726
}
727
728
if (!cpu->initExpression(expression.c_str(), exp)) {
729
return false;
730
}
731
732
union {
733
int i;
734
u32 u;
735
float f;
736
} expResult;
737
char resultString[256];
738
if (!cpu->parseExpression(exp, expResult.u)) {
739
return false;
740
}
741
742
switch (type) {
743
case 'd':
744
snprintf(resultString, sizeof(resultString), "%d", expResult.i);
745
break;
746
case 'f':
747
snprintf(resultString, sizeof(resultString), "%f", expResult.f);
748
break;
749
case 'p':
750
snprintf(resultString, sizeof(resultString), "%08x[%08x]", expResult.u, Memory::IsValidAddress(expResult.u) ? Memory::Read_U32(expResult.u) : 0);
751
break;
752
case 's':
753
snprintf(resultString, sizeof(resultString) - 1, "%s", Memory::IsValidAddress(expResult.u) ? Memory::GetCharPointer(expResult.u) : "(invalid)");
754
break;
755
case 'x':
756
snprintf(resultString, sizeof(resultString), "%08x", expResult.u);
757
break;
758
}
759
result += resultString;
760
}
761
762
// Skip the }.
763
pos = end + 1;
764
}
765
766
return true;
767
}
768
769