Skip to content

Commit f09a660

Browse files
authored
Merge pull request #1 from binary1230/network-tracelogging
Network tracelogging
2 parents 445078f + e30dfc7 commit f09a660

File tree

13 files changed

+4491
-22
lines changed

13 files changed

+4491
-22
lines changed

bsnes/Makefile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
common := ../common
22
nall := $(common)/nall
3+
zlib := $(common)/zlib
4+
35
include $(nall)/Makefile
46
snes := snes
57
ifeq ($(ui),)
@@ -86,8 +88,27 @@ all: build plugins;
8688
include $(snes)/Makefile
8789
include $(ui)/Makefile
8890

91+
92+
############
93+
### zlib ###
94+
############
95+
96+
$(objdir)/adler32.o : $(zlib)/adler32.c $(zlib)/*
97+
$(objdir)/crc32.o : $(zlib)/crc32.c $(zlib)/*
98+
$(objdir)/inffast.o : $(zlib)/inffast.c $(zlib)/*
99+
$(objdir)/inflate.o : $(zlib)/inflate.c $(zlib)/*
100+
$(objdir)/inftrees.o: $(zlib)/inftrees.c $(zlib)/*
101+
$(objdir)/zutil.o : $(zlib)/zutil.c $(zlib)/*
102+
$(objdir)/compress.o : $(zlib)/compress.c $(zlib)/*
103+
$(objdir)/deflate.o : $(zlib)/deflate.c $(zlib)/*
104+
$(objdir)/trees.o : $(zlib)/trees.c $(zlib)/*
105+
106+
# zlib
107+
objects += adler32 crc32 inffast inflate inftrees zutil compress deflate trees
108+
89109
objects := $(patsubst %,$(objdir)/%.o,$(objects))
90110

111+
91112
# targets
92113
build: ui_build $(objects)
93114
ifeq ($(platform),osx)

bsnes/snes/cpu/core/disassembler/disassembler.cpp

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ void CPUcore::disassemble_opcode_ex(CPUcore::Opcode &opcode, uint32 addr, bool e
154154
}
155155
}
156156

157+
// create a human-readable text version of the current opcode
157158
void CPUcore::disassemble_opcode(char *output, uint32 addr, bool hclocks) {
158159
static reg24_t pc;
159160
char t[256];
@@ -213,4 +214,81 @@ void CPUcore::disassemble_opcode(char *output, uint32 addr, bool hclocks) {
213214
strcat(s, t);
214215
}
215216

216-
#endif
217+
// disassemble current opcode but represent as a binary format instead of text
218+
//
219+
// goals:
220+
// 1) be really fast
221+
// 2) be fixed length and easy to parse out later
222+
// 3) be as compact as possible. (abridgedFormat cuts even more stuff)
223+
// 4) be extensible (use a header length so we can add more info later)
224+
void CPUcore::disassemble_opcode_bin(char* buf, uint32 addr, int &len_out, bool abridgedFormat) {
225+
static reg24_t pc;
226+
227+
pc.d = addr;
228+
uint8 opcode = dreadb(pc.d);
229+
unsigned opcode_len = SNESCPU::getOpcodeLength((regs.e || regs.p.m), (regs.e || regs.p.x), opcode);
230+
231+
int i = 0;
232+
233+
// --- header (2 bytes) ---
234+
235+
// watermark identifying the type of data coming next
236+
buf[i++] = abridgedFormat ? 0xEE : 0xEF;
237+
238+
// size in bytes of data starting after this byte (we will populate final size at the end)
239+
int sizeIdx = i; i++;
240+
241+
// --- data ---
242+
buf[i++] = (addr >> 0) & 0xFF;
243+
buf[i++] = (addr >> 8) & 0xFF;
244+
buf[i++] = (addr >> 16) & 0xFF;
245+
246+
// # of bytes for the instruction we're looking at
247+
buf[i++] = opcode_len; // valid values: 1,2,3,4
248+
249+
buf[i++] = (regs.d.w >> 0) & 0xFF;
250+
buf[i++] = (regs.d.w >> 8) & 0xFF;
251+
252+
buf[i++] = (regs.db) & 0xFF;
253+
254+
buf[i++] = (regs.p) & 0xFF; // 8 flags stored as bitmask in 1 byte
255+
256+
if (!abridgedFormat) {
257+
// we'll always transmit 4 bytes, but, consumers should only use up to 'opcode_len'
258+
// and discard the remaining bytes.
259+
// i.e. if opcode_len is 2,
260+
// then a consumer should USE opcode and operand0 (2 bytes)
261+
// then read but discard the remaining 2 bytes (operand1 and operand2 will be garbage)
262+
pc.w++;
263+
uint8 operand0 = dreadb(pc.d); pc.w++;
264+
uint8 operand1 = dreadb(pc.d); pc.w++;
265+
uint8 operand2 = dreadb(pc.d);
266+
267+
buf[i++] = opcode; // always valid (opcode_len >= 1)
268+
buf[i++] = operand0; // valid if opcode_len >= 2
269+
buf[i++] = operand1; // valid if opcode_len >= 3
270+
buf[i++] = operand2; // valid if opcode_len == 4
271+
272+
buf[i++] = (regs.a.w >> 0) & 0xFF;
273+
buf[i++] = (regs.a.w >> 8) & 0xFF;
274+
275+
buf[i++] = (regs.x.w >> 0) & 0xFF;
276+
buf[i++] = (regs.x.w >> 8) & 0xFF;
277+
278+
buf[i++] = (regs.y.w >> 0) & 0xFF;
279+
buf[i++] = (regs.y.w >> 8) & 0xFF;
280+
281+
buf[i++] = (regs.s.w >> 0) & 0xFF;
282+
buf[i++] = (regs.s.w >> 8) & 0xFF;
283+
284+
buf[i++] = (regs.e) & 0xFF; // emu flag
285+
286+
// TODO: hclocks/etc if we want them.
287+
}
288+
289+
// put the length of everything back in the header
290+
len_out = i;
291+
buf[sizeIdx] = len_out - sizeIdx - 1;
292+
}
293+
294+
#endif

bsnes/snes/cpu/core/disassembler/disassembler.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ struct Opcode {
7373

7474
void disassemble_opcode(char *output, uint32 addr, bool hclocks = false);
7575
void disassemble_opcode_ex(Opcode &opcode, uint32 addr, bool e, bool m, bool x);
76+
void disassemble_opcode_bin(char* buf, uint32 addr, int &len_out, bool abridgedFormat=true);
7677
uint8 dreadb(uint32 addr);
7778
uint16 dreadw(uint32 addr);
7879
uint32 dreadl(uint32 addr);

bsnes/ui-qt/debugger/debugger.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
#include <winsock2.h>
2+
#include <windows.h>
3+
#include <ws2tcpip.h>
4+
#include <stdlib.h>
5+
#include <stdio.h>
6+
7+
// TODO: for demo only. not the right way to do this.
8+
#pragma comment (lib, "Ws2_32.lib")
9+
110
#include "../ui-base.hpp"
211

312
#if defined(DEBUGGER)
@@ -687,6 +696,7 @@ void Debugger::frameTick() {
687696

688697
if (frame < frameCounter) {
689698
autoUpdate();
699+
tracer->flushTraceOutput();
690700
} else {
691701
// update memory editor every time since once per second isn't very useful
692702
// (TODO: and PPU viewers, maybe?)

bsnes/ui-qt/debugger/tracer.cpp

Lines changed: 138 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,111 @@
11
#include "tracer.moc"
22
Tracer *tracer;
33

4+
#include "w32_socket.cpp"
5+
6+
// TODO: demo only: make these checkboxes in the UI or config options
7+
8+
// tracer output info format
9+
// false: binary format (small/faster),
10+
// true: text format (easier to parse / but slower and HUGE))
11+
const bool traceOutputFormatIsText = false;
12+
13+
// where trace output will be sent
14+
// true: listen on a socket port and stream data to a client
15+
// false: output via a logfile on disk
16+
const bool traceOutputMediumIsSocket = true;
17+
18+
#define DEFAULT_TRACE_SERVER_LISTEN_PORT "27015"
19+
20+
void Tracer::outputTrace(const char* buf, int len) {
21+
outputTraceToFile(buf, len);
22+
outputTraceToSocket(buf, len);
23+
}
24+
25+
void Tracer::outputTraceToFile(const char *buf, int len) {
26+
if (!tracefile.open())
27+
return;
28+
29+
// perf: without trask masking, this is SUPER SLOW, grinds the emulation to 2FPS when enabled.
30+
// TODO: there's probably some easy way to improve performance here, like buffering and async IO calls.
31+
// this chews on gigs of data quickly, so whatever you do, be mindful of performance.
32+
//
33+
tracefile.write(reinterpret_cast<const uint8_t *>(buf), len);
34+
if (traceOutputFormatIsText)
35+
tracefile.print("\n");
36+
}
37+
38+
void Tracer::outputTraceToSocket(const char *buf, int len) {
39+
if (!traceServer.IsInitialized())
40+
return;
41+
42+
traceServer.Push((const uint8_t*)buf, len);
43+
}
44+
45+
void Tracer::outputCpuTrace() {
46+
char buf[256]; int len; // TODO: bounds check buf/len, make sure we don't overflow
47+
if (traceOutputFormatIsText) {
48+
SNES::cpu.disassemble_opcode(buf, SNES::cpu.regs.pc, config().debugger.showHClocks); // text
49+
len = strlen(buf) + 1; // null terminator
50+
} else {
51+
SNES::cpu.disassemble_opcode_bin(buf, SNES::cpu.regs.pc, len); // binary
52+
}
53+
outputTrace(buf, len);
54+
}
55+
56+
void Tracer::outputSmpTrace() {
57+
char buf[256]; int len;
58+
if (traceOutputFormatIsText) {
59+
SNES::smp.disassemble_opcode(buf, SNES::cpu.regs.pc);
60+
len = strlen(buf) + 1; // byte size = string + null term
61+
} else {
62+
// TODO: implement // SNES::smp.disassemble_opcode_bin(buf, SNES::cpu.regs.pc, len); // binary
63+
return; // TODO: not supported just yet
64+
}
65+
outputTrace(buf, len);
66+
}
67+
68+
void Tracer::outputSa1Trace() {
69+
char buf[256]; int len;
70+
if (traceOutputFormatIsText) {
71+
SNES::sa1.disassemble_opcode(buf, SNES::cpu.regs.pc, config().debugger.showHClocks);
72+
len = strlen(buf) + 1; // byte size = string + null term
73+
} else {
74+
// TODO: implement // SNES::sa1.disassemble_opcode_bin(buf, SNES::cpu.regs.pc, config().debugger.showHClocks, len); // binary
75+
return; // TODO: not supported just yet
76+
}
77+
outputTrace(buf, len);
78+
}
79+
80+
void Tracer::outputSfxTrace() {
81+
char buf[256]; int len;
82+
if (traceOutputFormatIsText) {
83+
SNES::superfx.disassemble_opcode(buf, SNES::cpu.regs.pc);
84+
len = strlen(buf) + 1; // byte size = string + null term
85+
} else {
86+
// TODO: implement // SNES::superfx.disassemble_opcode_bin(buf, SNES::cpu.regs.pc, len); // binary
87+
return; // TODO: not supported just yet
88+
}
89+
outputTrace(buf, len);
90+
}
91+
92+
void Tracer::outputSgbTrace() {
93+
char buf[256]; int len;
94+
if (traceOutputFormatIsText) {
95+
SNES::supergameboy.disassemble_opcode(buf, SNES::cpu.regs.pc);
96+
len = strlen(buf) + 1; // byte size = string + null term
97+
} else {
98+
// TODO: implement // SNES::supergameboy.disassemble_opcode_bin(buf, SNES::cpu.regs.pc, len); // binary
99+
return; // TODO: not supported just yet
100+
}
101+
outputTrace(buf, len);
102+
}
103+
4104
void Tracer::stepCpu() {
5105
if(traceCpu) {
6106
unsigned addr = SNES::cpu.regs.pc;
7107
if(!traceMask || !(traceMaskCPU[addr >> 3] & (0x80 >> (addr & 7)))) {
8-
char text[256];
9-
SNES::cpu.disassemble_opcode(text, addr, config().debugger.showHClocks);
10-
tracefile.print(string() << text << "\n");
108+
outputCpuTrace();
11109
}
12110
traceMaskCPU[addr >> 3] |= 0x80 >> (addr & 7);
13111
}
@@ -17,9 +115,7 @@ void Tracer::stepSmp() {
17115
if(traceSmp) {
18116
unsigned addr = SNES::smp.regs.pc;
19117
if(!traceMask || !(traceMaskSMP[addr >> 3] & (0x80 >> (addr & 7)))) {
20-
char text[256];
21-
SNES::smp.disassemble_opcode(text, addr);
22-
tracefile.print(string() << text << "\n");
118+
outputSmpTrace();
23119
}
24120
traceMaskSMP[addr >> 3] |= 0x80 >> (addr & 7);
25121
}
@@ -29,9 +125,7 @@ void Tracer::stepSa1() {
29125
if(traceSa1) {
30126
unsigned addr = SNES::sa1.regs.pc;
31127
if(!traceMask || !(traceMaskSA1[addr >> 3] & (0x80 >> (addr & 7)))) {
32-
char text[256];
33-
SNES::sa1.disassemble_opcode(text, addr, config().debugger.showHClocks);
34-
tracefile.print(string() << text << "\n");
128+
outputSa1Trace();
35129
}
36130
traceMaskSA1[addr >> 3] |= 0x80 >> (addr & 7);
37131
}
@@ -41,9 +135,7 @@ void Tracer::stepSfx() {
41135
if(traceSfx) {
42136
unsigned addr = SNES::superfx.opcode_pc;
43137
if(!traceMask || !(traceMaskSFX[addr >> 3] & (0x80 >> (addr & 7)))) {
44-
char text[256];
45-
SNES::superfx.disassemble_opcode(text, addr);
46-
tracefile.print(string() << text << "\n");
138+
outputSfxTrace();
47139
}
48140
traceMaskSFX[addr >> 3] |= 0x80 >> (addr & 7);
49141
}
@@ -53,30 +145,51 @@ void Tracer::stepSgb() {
53145
if(traceSgb) {
54146
unsigned addr = SNES::supergameboy.opcode_pc;
55147
if(!traceMask || !(traceMaskSGB[addr >> 3] & (0x80 >> (addr & 7)))) {
56-
char text[256];
57-
SNES::supergameboy.disassemble_opcode(text, addr);
58-
tracefile.print(string() << text << "\n");
148+
outputSgbTrace();
59149
}
60150
traceMaskSGB[addr >> 3] |= 0x80 >> (addr & 7);
61151
}
62152
}
63153

64154
void Tracer::resetTraceState() {
65155
tracefile.close();
156+
traceServer.Shutdown();
157+
66158
setTraceState(traceCpu || traceSmp || traceSa1 || traceSfx || traceSgb);
67159

68160
// reset trace masks
69161
if (traceMask)
70162
setTraceMaskState(true);
71163
}
72164

165+
void Tracer::ensureTraceOutputReady() {
166+
if (!traceOutputMediumIsSocket && !tracefile.open()) {
167+
string name = filepath(nall::basename(cartridge.fileName), config().path.data);
168+
name << "-trace.log";
169+
tracefile.open(name, file::mode::write);
170+
}
171+
172+
if (traceOutputMediumIsSocket && !traceServer.IsInitialized()) {
173+
// demo: this blocks the entire UI while waiting for a client to connect (not great, better ways to handle)
174+
traceServer.Init(DEFAULT_TRACE_SERVER_LISTEN_PORT);
175+
}
176+
}
177+
178+
void Tracer::ensureTraceOutputShutdown() {
179+
if (tracefile.open()) {
180+
tracefile.close();
181+
}
182+
183+
if (traceServer.IsInitialized()) {
184+
traceServer.Shutdown();
185+
}
186+
}
187+
73188
void Tracer::setTraceState(bool state) {
74-
if(state && !tracefile.open() && SNES::cartridge.loaded()) {
75-
string name = filepath(nall::basename(cartridge.fileName), config().path.data);
76-
name << "-trace.log";
77-
tracefile.open(name, file::mode::write);
78-
} else if(!traceCpu && !traceSmp && !traceSa1 && !traceSfx && !traceSgb && tracefile.open()) {
79-
tracefile.close();
189+
if(state && SNES::cartridge.loaded()) {
190+
ensureTraceOutputReady();
191+
} else if(!traceCpu && !traceSmp && !traceSa1 && !traceSfx && !traceSgb) {
192+
ensureTraceOutputShutdown();
80193
}
81194
}
82195

@@ -146,3 +259,7 @@ Tracer::~Tracer() {
146259
delete[] traceMaskSGB;
147260
if(tracefile.open()) tracefile.close();
148261
}
262+
263+
void Tracer::flushTraceOutput() {
264+
traceServer.FlushWorkingBuffer();
265+
}

0 commit comments

Comments
 (0)