unstable: MemBatch is broken
lunixbochs opened this issue · comments
Ryan Hileman commented
0x8875271: rep movsd dword ptr es:[edi], dword ptr [esi] | ecx = 0x00000000 | R 888466a
+ edi = 0xbffff373 + W bffff363
+ esi = 0x0888467a + R 888466e
R 0x08884668: 7573 [us ] R
W 0xbffff361: 7573722f 6c69 [usr/li ] W
R 0x0888466a: 722f6c69 [r/li ] R
R 0x0888466e: 622f6c69 [b/li ] R
W 0xbffff367: 622f6c69 [b/li ] W
R 0x08884672: 62632e73 [bc.s ] R
W 0xbffff36b: 62632e73 [bc.s ] W
R 0x08884676: 6f2e3600 [o.6. ] R
W 0xbffff36f: 6f2e3600 [o.6. ] W
Note the overlapping IO that should be batched here.
Grazfather commented
It seems to be because rep
triggers three ops on each: A read, a write, and a jmp. The jmp triggers the Flush
.
Simple ELF to test:
python -c "print '7f454c4601010100000000000000000002000300010000007490040834000000a400000000000000340020000200280003000200010000000000000000900408009004088c0000008c000000070000000010000051e5746400000000000000000000000000000000000000000700000010000000b90400000089e783ef4089e6f3a531dbb801000000cd8000002e7368737472746162002e7368656c6c636f6465000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000010000000700000074900408740000001800000000000000000000000100000000000000010000000300000000000000000000008c0000001600000000000000000000000100000000000000'.decode('hex')" > x
Ryan Hileman commented
Looks exactly right, here's a partial patch detecting self-jmp (which still inadvertently flushes on the first rep invocation):
diff --git a/go/models/trace/filter_membatch.go b/go/models/trace/filter_membatch.go
index 80c9e68..d1f3d94 100644
--- a/go/models/trace/filter_membatch.go
+++ b/go/models/trace/filter_membatch.go
@@ -42,12 +42,24 @@ func (o *OpMemBatch) Render(mem *cpu.Mem) string {
type MemBatch struct {
memOps []models.Op
+ pc uint64
+ lastPC uint64
}
func (m *MemBatch) Filter(op models.Op) []models.Op {
- switch op.(type) {
+ switch v := op.(type) {
case *OpJmp:
- return append(m.Flush(), op)
+ fmt.Printf("jmp: m.pc=%#x m.lastPC=%#x jmp.addr=%#x\n", m.pc, m.lastPC, v.Addr)
+ selfJmp := v.Addr == m.lastPC
+ m.lastPC = m.pc
+ m.pc = v.Addr
+ if !selfJmp {
+ return append(m.Flush(), op)
+ }
+ case *OpStep:
+ fmt.Printf("step: m.pc=%#x m.lastPC=%#x step.size=%d\n", m.pc, m.lastPC, v.Size)
+ m.lastPC = m.pc
+ m.pc += uint64(v.Size)
case *OpMemRead, *OpMemWrite:
m.memOps = append(m.memOps, op)
}
@@ -55,6 +67,7 @@ func (m *MemBatch) Filter(op models.Op) []models.Op {
}
func (m *MemBatch) Flush() []models.Op {
+ fmt.Println("membatch flush")
log := &MemLog{}
// Build a log of all reads and writes
Output:
membatch flush
jmp: m.pc=0x0 m.lastPC=0x0 jmp.addr=0x8049074
membatch flush
step: m.pc=0x8049074 m.lastPC=0x0 step.size=5
0x8049074: mov ecx, 4 | ecx = 0x00000004
step: m.pc=0x8049079 m.lastPC=0x8049074 step.size=2
0x8049079: mov edi, esp | edi = 0xbffff9c8
step: m.pc=0x804907b m.lastPC=0x8049079 step.size=3
0x804907b: sub edi, 0x40 | edi = 0xbffff988
+ eflags = 0x00000084
step: m.pc=0x804907e m.lastPC=0x804907b step.size=2
0x804907e: mov esi, esp | esi = 0xbffff9c8
jmp: m.pc=0x8049080 m.lastPC=0x804907e jmp.addr=0x8049080
membatch flush
jmp: m.pc=0x8049080 m.lastPC=0x8049080 jmp.addr=0x8049080
jmp: m.pc=0x8049080 m.lastPC=0x8049080 jmp.addr=0x8049080
jmp: m.pc=0x8049080 m.lastPC=0x8049080 jmp.addr=0x8049080
step: m.pc=0x8049080 m.lastPC=0x8049080 step.size=2
0x8049080: rep movsd dword ptr es:[edi], dword ptr [esi] | ecx = 0x00000000 | R bffff9c8
+ edi = 0xbffff998 + W bffff988
+ esi = 0xbffff9d8 + R bffff9cc
R 0xbffff9c8: 00000000 [.... ] R
W 0xbffff988: 01000000 [.... ] W
jmp: m.pc=0x8049082 m.lastPC=0x8049080 jmp.addr=0x8049082
membatch flush
step: m.pc=0x8049082 m.lastPC=0x8049082 step.size=2
0x8049082: xor ebx, ebx | eflags = 0x00000044
R 0xbffff9cc: 00000000 [.... ] R
W 0xbffff98c: e9faffbf 00000000 c3ffffbf [............ ] W
R 0xbffff9d0: 00000000 [.... ] R
R 0xbffff9d4: 00000000 [.... ] R
step: m.pc=0x8049084 m.lastPC=0x8049082 step.size=5
0x8049084: mov eax, 1 | eax = 0x00000001
step: m.pc=0x8049089 m.lastPC=0x8049084 step.size=2
Ryan Hileman commented
This seems to patch it but doesn't flush on exit, which is a newly found bug:
diff --git a/go/models/trace/filter_membatch.go b/go/models/trace/filter_membatch.go
index 80c9e68..786fe5e 100644
--- a/go/models/trace/filter_membatch.go
+++ b/go/models/trace/filter_membatch.go
@@ -42,12 +42,21 @@ func (o *OpMemBatch) Render(mem *cpu.Mem) string {
type MemBatch struct {
memOps []models.Op
+ pc uint64
}
func (m *MemBatch) Filter(op models.Op) []models.Op {
- switch op.(type) {
+ switch v := op.(type) {
case *OpJmp:
- return append(m.Flush(), op)
+ fmt.Printf("jmp: m.pc=%#x jmp.addr=%#x\n", m.pc, v.Addr)
+ selfJmp := v.Addr == m.pc
+ m.pc = v.Addr
+ if !selfJmp {
+ return append(m.Flush(), op)
+ }
+ case *OpStep:
+ fmt.Printf("step: m.pc=%#x step.size=%d\n", m.pc, v.Size)
+ m.pc += uint64(v.Size)
case *OpMemRead, *OpMemWrite:
m.memOps = append(m.memOps, op)
}
@@ -55,6 +64,7 @@ func (m *MemBatch) Filter(op models.Op) []models.Op {
}
func (m *MemBatch) Flush() []models.Op {
+ fmt.Println("membatch flush")
log := &MemLog{}
// Build a log of all reads and writes
Ryan Hileman commented
fixed in unstable, wow that shook out a lot of tracing errata
Grazfather commented
💥