Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/GPU/Debugger/Breakpoints.cpp
3186 views
1
// Copyright (c) 2013- 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 <functional>
19
#include <mutex>
20
#include <set>
21
#include <unordered_map>
22
#include <vector>
23
24
#include "Common/CommonFuncs.h"
25
#include "GPU/Debugger/Breakpoints.h"
26
#include "GPU/Debugger/GECommandTable.h"
27
#include "GPU/GPUState.h"
28
29
// These are commands we run before breaking on a texture.
30
// They are commands that affect the decoding of the texture.
31
const static u8 textureRelatedCmds[] = {
32
GE_CMD_TEXADDR0, GE_CMD_TEXADDR1, GE_CMD_TEXADDR2, GE_CMD_TEXADDR3, GE_CMD_TEXADDR4, GE_CMD_TEXADDR5, GE_CMD_TEXADDR6, GE_CMD_TEXADDR7,
33
GE_CMD_TEXBUFWIDTH0, GE_CMD_TEXBUFWIDTH1, GE_CMD_TEXBUFWIDTH2, GE_CMD_TEXBUFWIDTH3, GE_CMD_TEXBUFWIDTH4, GE_CMD_TEXBUFWIDTH5, GE_CMD_TEXBUFWIDTH6, GE_CMD_TEXBUFWIDTH7,
34
GE_CMD_TEXSIZE0, GE_CMD_TEXSIZE1, GE_CMD_TEXSIZE2, GE_CMD_TEXSIZE3, GE_CMD_TEXSIZE4, GE_CMD_TEXSIZE5, GE_CMD_TEXSIZE6, GE_CMD_TEXSIZE7,
35
36
GE_CMD_CLUTADDR, GE_CMD_CLUTADDRUPPER, GE_CMD_LOADCLUT, GE_CMD_CLUTFORMAT,
37
GE_CMD_TEXFORMAT, GE_CMD_TEXMODE, GE_CMD_TEXTUREMAPENABLE,
38
GE_CMD_TEXFILTER, GE_CMD_TEXWRAP,
39
GE_CMD_TEXLEVEL,
40
41
// Sometimes found between clut/texture params.
42
GE_CMD_TEXFLUSH, GE_CMD_TEXSYNC,
43
};
44
45
GPUBreakpoints::GPUBreakpoints() {
46
ClearAllBreakpoints();
47
48
nonTextureCmds.clear();
49
nonTextureCmds.resize(256, true);
50
for (size_t i = 0; i < ARRAY_SIZE(textureRelatedCmds); ++i) {
51
nonTextureCmds[textureRelatedCmds[i]] = false;
52
}
53
}
54
55
void GPUBreakpoints::AddNonTextureTempBreakpoints() {
56
for (int i = 0; i < 256; ++i) {
57
if (nonTextureCmds[i]) {
58
AddCmdBreakpoint(i, true);
59
}
60
}
61
}
62
63
static u32 GetAdjustedTextureAddress(u32 op) {
64
const u8 cmd = op >> 24;
65
bool interesting = (cmd >= GE_CMD_TEXADDR0 && cmd <= GE_CMD_TEXADDR7);
66
interesting = interesting || (cmd >= GE_CMD_TEXBUFWIDTH0 && cmd <= GE_CMD_TEXBUFWIDTH7);
67
68
if (!interesting) {
69
return (u32)-1;
70
}
71
72
int level = cmd <= GE_CMD_TEXADDR7 ? cmd - GE_CMD_TEXADDR0 : cmd - GE_CMD_TEXBUFWIDTH0;
73
u32 addr;
74
75
// Okay, so would this op modify the low or high part?
76
if (cmd <= GE_CMD_TEXADDR7) {
77
addr = (op & 0xFFFFF0) | ((gstate.texbufwidth[level] << 8) & 0x0F000000);
78
} else {
79
addr = (gstate.texaddr[level] & 0xFFFFF0) | ((op << 8) & 0x0F000000);
80
}
81
82
return addr;
83
}
84
85
static u32 GetAdjustedRenderTargetAddress(u32 op) {
86
const u8 cmd = op >> 24;
87
switch (cmd) {
88
case GE_CMD_FRAMEBUFPTR:
89
case GE_CMD_ZBUFPTR:
90
return op & 0x001FFFF0;
91
}
92
93
return (u32)-1;
94
}
95
96
// Note: this now always returns false, but still needs to be called.
97
void GPUBreakpoints::CheckForTextureChange(u32 op, u32 addr) {
98
if (!textureChangeTemp) {
99
return;
100
}
101
102
const u8 cmd = op >> 24;
103
bool enabled = gstate.isTextureMapEnabled();
104
105
// Only for level 0.
106
if (cmd != GE_CMD_TEXADDR0 && cmd != GE_CMD_TEXBUFWIDTH0) {
107
// But we don't break when it's not enabled.
108
if (cmd == GE_CMD_TEXTUREMAPENABLE) {
109
enabled = (op & 1) != 0;
110
} else {
111
return;
112
}
113
}
114
if (enabled && addr != lastTexture) {
115
textureChangeTemp = false;
116
lastTexture = addr;
117
118
// Silently convert to a primitive breakpoint, so we stop on use.
119
// Note: this may cause "spurious" breaks if the tex is changed and the changed back.
120
AddCmdBreakpoint(GE_CMD_PRIM, true);
121
AddCmdBreakpoint(GE_CMD_BEZIER, true);
122
AddCmdBreakpoint(GE_CMD_SPLINE, true);
123
AddCmdBreakpoint(GE_CMD_VAP, true);
124
}
125
}
126
127
bool GPUBreakpoints::IsTextureCmdBreakpoint(u32 op) {
128
const u32 addr = GetAdjustedTextureAddress(op);
129
if (addr != (u32)-1) {
130
CheckForTextureChange(op, addr);
131
return IsTextureBreakpoint(addr);
132
} else {
133
CheckForTextureChange(op, gstate.getTextureAddress(0));
134
return false;
135
}
136
}
137
138
bool GPUBreakpoints::IsRenderTargetCmdBreakpoint(u32 op) {
139
const u32 addr = GetAdjustedRenderTargetAddress(op);
140
if (addr != (u32)-1) {
141
return IsRenderTargetBreakpoint(addr);
142
}
143
return false;
144
}
145
146
static bool HitBreakpointCond(GPUBreakpoints::BreakpointInfo &bp, u32 op) {
147
u8 cmd = op >> 24;
148
149
// Temporarily set the value while running the breakpoint.
150
// It makes more intuitive sense for the referenced data to already be set.
151
// Note this won't perform actions, like matrix uploads.
152
u32 diff = gstate.cmdmem[cmd] ^ op;
153
gstate.cmdmem[cmd] ^= diff;
154
155
u32 result = 1;
156
if (!GPUDebugExecExpression(gpuDebug, bp.expression, result))
157
result = 0;
158
159
gstate.cmdmem[cmd] ^= diff;
160
return result != 0;
161
}
162
163
bool GPUBreakpoints::HitAddressBreakpoint(u32 pc, u32 op) {
164
if (breakPCsCount == 0)
165
return false;
166
167
std::lock_guard<std::mutex> guard(breaksLock);
168
auto entry = breakPCs.find(pc);
169
if (entry == breakPCs.end())
170
return false;
171
172
if (entry->second.isConditional) {
173
return HitBreakpointCond(entry->second, op);
174
}
175
return true;
176
}
177
178
bool GPUBreakpoints::HitOpBreakpoint(u32 op) {
179
u8 cmd = op >> 24;
180
if (!IsCmdBreakpoint(cmd))
181
return false;
182
183
if (breakCmdsInfo[cmd].isConditional) {
184
std::lock_guard<std::mutex> guard(breaksLock);
185
return HitBreakpointCond(breakCmdsInfo[cmd], op);
186
}
187
188
return true;
189
}
190
191
bool GPUBreakpoints::IsBreakpoint(u32 pc, u32 op) {
192
if (HitAddressBreakpoint(pc, op) || HitOpBreakpoint(op)) {
193
return true;
194
}
195
196
if ((breakTexturesCount != 0 || textureChangeTemp) && IsTextureCmdBreakpoint(op)) {
197
// Break on the next non-texture.
198
AddNonTextureTempBreakpoints();
199
}
200
if (breakRenderTargetsCount != 0 && IsRenderTargetCmdBreakpoint(op)) {
201
return true;
202
}
203
204
return false;
205
}
206
207
bool GPUBreakpoints::IsAddressBreakpoint(u32 addr, bool &temp) {
208
if (breakPCsCount == 0) {
209
temp = false;
210
return false;
211
}
212
213
std::lock_guard<std::mutex> guard(breaksLock);
214
temp = breakPCsTemp.find(addr) != breakPCsTemp.end();
215
return breakPCs.find(addr) != breakPCs.end();
216
}
217
218
bool GPUBreakpoints::IsAddressBreakpoint(u32 addr) {
219
if (breakPCsCount == 0) {
220
return false;
221
}
222
223
std::lock_guard<std::mutex> guard(breaksLock);
224
return breakPCs.find(addr) != breakPCs.end();
225
}
226
227
bool GPUBreakpoints::IsTextureBreakpoint(u32 addr, bool &temp) {
228
if (breakTexturesCount == 0) {
229
temp = false;
230
return false;
231
}
232
233
std::lock_guard<std::mutex> guard(breaksLock);
234
temp = breakTexturesTemp.find(addr) != breakTexturesTemp.end();
235
return breakTextures.find(addr) != breakTextures.end();
236
}
237
238
bool GPUBreakpoints::IsTextureBreakpoint(u32 addr) {
239
if (breakTexturesCount == 0) {
240
return false;
241
}
242
243
std::lock_guard<std::mutex> guard(breaksLock);
244
return breakTextures.find(addr) != breakTextures.end();
245
}
246
247
bool GPUBreakpoints::IsRenderTargetBreakpoint(u32 addr, bool &temp) {
248
if (breakRenderTargetsCount == 0) {
249
temp = false;
250
return false;
251
}
252
253
addr &= 0x001FFFF0;
254
255
std::lock_guard<std::mutex> guard(breaksLock);
256
temp = breakRenderTargetsTemp.find(addr) != breakRenderTargetsTemp.end();
257
return breakRenderTargets.find(addr) != breakRenderTargets.end();
258
}
259
260
bool GPUBreakpoints::IsRenderTargetBreakpoint(u32 addr) {
261
if (breakRenderTargetsCount == 0) {
262
return false;
263
}
264
265
addr &= 0x001FFFF0;
266
267
std::lock_guard<std::mutex> guard(breaksLock);
268
return breakRenderTargets.find(addr) != breakRenderTargets.end();
269
}
270
271
bool GPUBreakpoints::IsOpBreakpoint(u32 op, bool &temp) const {
272
return IsCmdBreakpoint(op >> 24, temp);
273
}
274
275
bool GPUBreakpoints::IsOpBreakpoint(u32 op) const {
276
return IsCmdBreakpoint(op >> 24);
277
}
278
279
bool GPUBreakpoints::IsCmdBreakpoint(u8 cmd, bool &temp) const {
280
temp = breakCmdsTemp[cmd];
281
return breakCmds[cmd];
282
}
283
284
bool GPUBreakpoints::IsCmdBreakpoint(u8 cmd) const {
285
return breakCmds[cmd];
286
}
287
288
bool GPUBreakpoints::HasAnyBreakpoints() const {
289
if (breakPCsCount != 0 || breakTexturesCount != 0 || breakRenderTargetsCount != 0)
290
return true;
291
if (textureChangeTemp)
292
return true;
293
294
for (int i = 0; i < 256; ++i) {
295
if (breakCmds[i] || breakCmdsTemp[i])
296
return true;
297
}
298
299
return false;
300
}
301
302
void GPUBreakpoints::AddAddressBreakpoint(u32 addr, bool temp) {
303
std::lock_guard<std::mutex> guard(breaksLock);
304
305
if (temp) {
306
if (breakPCs.find(addr) == breakPCs.end()) {
307
breakPCsTemp.insert(addr);
308
breakPCs[addr].isConditional = false;
309
}
310
// Already normal breakpoint, let's not make it temporary.
311
} else {
312
// Remove the temporary marking.
313
breakPCsTemp.erase(addr);
314
breakPCs.emplace(addr, BreakpointInfo{});
315
}
316
317
breakPCsCount = breakPCs.size();
318
hasBreakpoints_ = true;
319
}
320
321
void GPUBreakpoints::AddCmdBreakpoint(u8 cmd, bool temp) {
322
if (temp) {
323
if (!breakCmds[cmd]) {
324
breakCmdsTemp[cmd] = true;
325
breakCmds[cmd] = true;
326
breakCmdsInfo[cmd].isConditional = false;
327
}
328
// Ignore adding a temp breakpoint when a normal one exists.
329
} else {
330
// This is no longer temporary.
331
breakCmdsTemp[cmd] = false;
332
if (!breakCmds[cmd]) {
333
breakCmds[cmd] = true;
334
breakCmdsInfo[cmd].isConditional = false;
335
}
336
}
337
hasBreakpoints_ = true;
338
}
339
340
void GPUBreakpoints::AddTextureBreakpoint(u32 addr, bool temp) {
341
std::lock_guard<std::mutex> guard(breaksLock);
342
343
if (temp) {
344
if (breakTextures.find(addr) == breakTextures.end()) {
345
breakTexturesTemp.insert(addr);
346
breakTextures.insert(addr);
347
}
348
} else {
349
breakTexturesTemp.erase(addr);
350
breakTextures.insert(addr);
351
}
352
353
breakTexturesCount = breakTextures.size();
354
hasBreakpoints_ = true;
355
}
356
357
void GPUBreakpoints::AddRenderTargetBreakpoint(u32 addr, bool temp) {
358
std::lock_guard<std::mutex> guard(breaksLock);
359
360
addr &= 0x001FFFF0;
361
362
if (temp) {
363
if (breakRenderTargets.find(addr) == breakRenderTargets.end()) {
364
breakRenderTargetsTemp.insert(addr);
365
breakRenderTargets.insert(addr);
366
}
367
} else {
368
breakRenderTargetsTemp.erase(addr);
369
breakRenderTargets.insert(addr);
370
}
371
372
breakRenderTargetsCount = breakRenderTargets.size();
373
hasBreakpoints_ = true;
374
}
375
376
void GPUBreakpoints::AddTextureChangeTempBreakpoint() {
377
textureChangeTemp = true;
378
hasBreakpoints_ = true;
379
}
380
381
void GPUBreakpoints::AddAnyTempBreakpoint() {
382
for (int i = 0; i < 256; ++i) {
383
AddCmdBreakpoint(i, true);
384
}
385
hasBreakpoints_ = true;
386
}
387
388
void GPUBreakpoints::RemoveAddressBreakpoint(u32 addr) {
389
std::lock_guard<std::mutex> guard(breaksLock);
390
391
breakPCsTemp.erase(addr);
392
breakPCs.erase(addr);
393
394
breakPCsCount = breakPCs.size();
395
hasBreakpoints_ = HasAnyBreakpoints();
396
}
397
398
void GPUBreakpoints::RemoveCmdBreakpoint(u8 cmd) {
399
std::lock_guard<std::mutex> guard(breaksLock);
400
401
breakCmdsTemp[cmd] = false;
402
breakCmds[cmd] = false;
403
hasBreakpoints_ = HasAnyBreakpoints();
404
}
405
406
void GPUBreakpoints::RemoveTextureBreakpoint(u32 addr) {
407
std::lock_guard<std::mutex> guard(breaksLock);
408
409
breakTexturesTemp.erase(addr);
410
breakTextures.erase(addr);
411
412
breakTexturesCount = breakTextures.size();
413
hasBreakpoints_ = HasAnyBreakpoints();
414
}
415
416
void GPUBreakpoints::RemoveRenderTargetBreakpoint(u32 addr) {
417
std::lock_guard<std::mutex> guard(breaksLock);
418
419
addr &= 0x001FFFF0;
420
421
breakRenderTargetsTemp.erase(addr);
422
breakRenderTargets.erase(addr);
423
424
breakRenderTargetsCount = breakRenderTargets.size();
425
hasBreakpoints_ = HasAnyBreakpoints();
426
}
427
428
void GPUBreakpoints::RemoveTextureChangeTempBreakpoint() {
429
std::lock_guard<std::mutex> guard(breaksLock);
430
431
textureChangeTemp = false;
432
hasBreakpoints_ = HasAnyBreakpoints();
433
}
434
435
static bool SetupCond(GPUBreakpoints::BreakpointInfo &bp, const std::string &expression, std::string *error) {
436
bool success = true;
437
if (expression.length() != 0) {
438
if (GPUDebugInitExpression(gpuDebug, expression.c_str(), bp.expression)) {
439
bp.isConditional = true;
440
bp.expressionString = expression;
441
} else {
442
// Don't change if it failed.
443
if (error)
444
*error = getExpressionError();
445
success = false;
446
}
447
} else {
448
bp.isConditional = false;
449
}
450
return success;
451
}
452
453
bool GPUBreakpoints::SetAddressBreakpointCond(u32 addr, const std::string &expression, std::string *error) {
454
// Must have one in the first place, make sure it's not temporary.
455
AddAddressBreakpoint(addr);
456
457
std::lock_guard<std::mutex> guard(breaksLock);
458
auto &bp = breakPCs[addr];
459
return SetupCond(breakPCs[addr], expression, error);
460
}
461
462
bool GPUBreakpoints::GetAddressBreakpointCond(u32 addr, std::string *expression) {
463
std::lock_guard<std::mutex> guard(breaksLock);
464
auto entry = breakPCs.find(addr);
465
if (entry != breakPCs.end() && entry->second.isConditional) {
466
if (expression)
467
*expression = entry->second.expressionString;
468
return true;
469
}
470
return false;
471
}
472
473
bool GPUBreakpoints::SetCmdBreakpointCond(u8 cmd, const std::string &expression, std::string *error) {
474
// Must have one in the first place, make sure it's not temporary.
475
AddCmdBreakpoint(cmd);
476
477
std::lock_guard<std::mutex> guard(breaksLock);
478
return SetupCond(breakCmdsInfo[cmd], expression, error);
479
}
480
481
bool GPUBreakpoints::GetCmdBreakpointCond(u8 cmd, std::string *expression) {
482
if (breakCmds[cmd] && breakCmdsInfo[cmd].isConditional) {
483
if (expression) {
484
std::lock_guard<std::mutex> guard(breaksLock);
485
*expression = breakCmdsInfo[cmd].expressionString;
486
}
487
return true;
488
}
489
return false;
490
}
491
492
void GPUBreakpoints::UpdateLastTexture(u32 addr) {
493
lastTexture = addr;
494
}
495
496
void GPUBreakpoints::ClearAllBreakpoints() {
497
std::lock_guard<std::mutex> guard(breaksLock);
498
499
for (int i = 0; i < 256; ++i) {
500
breakCmds[i] = false;
501
breakCmdsTemp[i] = false;
502
}
503
breakPCs.clear();
504
breakTextures.clear();
505
breakRenderTargets.clear();
506
507
breakPCsTemp.clear();
508
breakTexturesTemp.clear();
509
breakRenderTargetsTemp.clear();
510
511
breakPCsCount = breakPCs.size();
512
breakTexturesCount = breakTextures.size();
513
breakRenderTargetsCount = breakRenderTargets.size();
514
515
textureChangeTemp = false;
516
hasBreakpoints_ = false;
517
}
518
519
void GPUBreakpoints::ClearTempBreakpoints() {
520
std::lock_guard<std::mutex> guard(breaksLock);
521
522
// Reset ones that were temporary back to non-breakpoints in the primary arrays.
523
for (int i = 0; i < 256; ++i) {
524
if (breakCmdsTemp[i]) {
525
breakCmds[i] = false;
526
breakCmdsTemp[i] = false;
527
}
528
}
529
530
for (auto it = breakPCsTemp.begin(), end = breakPCsTemp.end(); it != end; ++it) {
531
breakPCs.erase(*it);
532
}
533
breakPCsTemp.clear();
534
breakPCsCount = breakPCs.size();
535
536
for (auto it = breakTexturesTemp.begin(), end = breakTexturesTemp.end(); it != end; ++it) {
537
breakTextures.erase(*it);
538
}
539
breakTexturesTemp.clear();
540
breakTexturesCount = breakTextures.size();
541
542
for (auto it = breakRenderTargetsTemp.begin(), end = breakRenderTargetsTemp.end(); it != end; ++it) {
543
breakRenderTargets.erase(*it);
544
}
545
breakRenderTargetsTemp.clear();
546
breakRenderTargetsCount = breakRenderTargets.size();
547
548
textureChangeTemp = false;
549
hasBreakpoints_ = HasAnyBreakpoints();
550
}
551
552
bool GPUBreakpoints::ToggleCmdBreakpoint(const GECmdInfo &info) {
553
if (IsCmdBreakpoint(info.cmd)) {
554
RemoveCmdBreakpoint(info.cmd);
555
if (info.otherCmd)
556
RemoveCmdBreakpoint(info.otherCmd);
557
if (info.otherCmd2)
558
RemoveCmdBreakpoint(info.otherCmd2);
559
return false;
560
}
561
562
AddCmdBreakpoint(info.cmd);
563
if (info.otherCmd)
564
AddCmdBreakpoint(info.otherCmd);
565
if (info.otherCmd2)
566
AddCmdBreakpoint(info.otherCmd2);
567
return true;
568
}
569
570
571