Skip to content

Commit b6eb331

Browse files
committed
pprof_mac_fix: fix for OS X Mavericks
1 parent 1074c53 commit b6eb331

File tree

7 files changed

+160
-52
lines changed

7 files changed

+160
-52
lines changed

cmd/pprof_mac_fix/main.go

Lines changed: 120 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,17 @@
2525
// OS X 10.6 Snow Leopard / Darwin 10.8 / i386 only
2626
// OS X 10.7 Lion / Darwin 11.4 / i386 and x86_64
2727
// OS X 10.8 Mountain Lion / Darwin 12.4 / x86_64 only
28+
// OS X 10.9 Mavericks / Darwin 13.0 / x86_64 only
2829
//
2930
// Snow Leopard x86_64 may work too but is untried.
3031
//
31-
// This program has shipped in the past with a patch for
32-
// OS X 10.9 Mavericks/Darwin 13.0, but the patch can cause
33-
// rare system crashes during profiling and has been removed.
34-
// People who applied the 10.9 patch are encouraged to copy
35-
// the standard kernel back (cp /mach_kernel0 /mach_kernel).
32+
// An earlier version of this program shipped with a buggy patch
33+
// for OS X 10.9 Mavericks/Darwin 13.0. If you used the old patch
34+
// you are encouraged to restore your old kernel and patch using
35+
// an updated copy of this program.
36+
//
37+
// If running http://swtch.com/~rsc/macbug.c crashes your system,
38+
// you have the buggy patch applied.
3639
//
3740
// Installation
3841
//
@@ -479,24 +482,25 @@ func (f *fix) apply(current_thread []byte, bsd_ast []byte) error {
479482
timers = append(timers, m)
480483
}
481484

482-
if total != 2 {
483-
if total == 0 {
484-
return fmt.Errorf("cannot match bsd_ast timer call")
485-
}
486-
if total == 1 {
487-
return fmt.Errorf("1 match for bsd_ast timer call %v, want 2", timers)
488-
}
489-
return fmt.Errorf("%d matches for bsd_ast timer call %v, want 2", total, timers)
490-
}
491-
492485
var replace [][]byte
493486
if f.version >= "13." {
487+
if total != 3 || len(timers) != 2 || len(timers[0]) != 2 {
488+
return fmt.Errorf("cannot match bsd_ast 13 timer call %d %d", total, len(timers[0]))
489+
}
494490
var err error
495491
replace, err = f.apply13(tlsOff, bsd_ast, timers)
496492
if err != nil {
497493
return err
498494
}
499495
goto done
496+
} else if total != 2 {
497+
if total == 0 {
498+
return fmt.Errorf("cannot match bsd_ast timer call")
499+
}
500+
if total == 1 {
501+
return fmt.Errorf("1 match for bsd_ast timer call %v, want 2", timers)
502+
}
503+
return fmt.Errorf("%d matches for bsd_ast timer call %v, want 2", total, timers)
500504
}
501505

502506
for i, timer1 := range timers {
@@ -837,25 +841,20 @@ var fixes = []*fix{
837841
&fix_11_4_2,
838842
&fix_11_4_2_i386,
839843
&fix_12_4_0,
840-
// &fix_13_0_0, BUGGY!
844+
&fix_13_0_0,
841845
}
842846

843847
// Darwin 13.0.0 (Mavericks)
844848
//
845-
// Mavericks does not have the call to task_vtimer_clear so we cannot use the
846-
// usual space optimization. Instead, build a parameterized subroutine in the
847-
// middle of the SIGPROF and SIGVTALRM bodies and change them to call it.
848-
//
849-
// NOTE: This patch is buggy. It does work fairly reliably but on occasion
850-
// another section of the signal handler can try to jump into the middle
851-
// of the rewritten section, and of course it doesn't land where it expects
852-
// and things go south rather quickly.
853-
// This is disabled until it can be fixed.
849+
// Mavericks has the call to task_vtimer_clear at the end of the function,
850+
// not contiguous with the other code we need to rewrite.
851+
// To make room we have to build paramterized subroutines in the two sections
852+
// and call them.
854853

855854
var fix_13_0_0 = fix{
856855
"13.0.0",
857856
current_thread_pop,
858-
[]*pattern{bsd_ast_13_0_0},
857+
[]*pattern{bsd_ast_13_0_0, bsd_ast_13_0_0_end},
859858
}
860859

861860
var bsd_ast_13_0_0 = mustCompile(`
@@ -870,17 +869,31 @@ var bsd_ast_13_0_0 = mustCompile(`
870869
0xe8 0x00/0x00 0x00/0x00 0x00/0x00 0x00/0x00 * // 29 call psignal_internal
871870
`)
872871

872+
var bsd_ast_13_0_0_end = mustCompile(`
873+
* 0x49 0x8b 0x7f 0x18 // 0 mov 0x18(%r15), %rdi
874+
0xbe 0x01 0x00 0x00 0x00 // 4 mov $0x1, %esi
875+
0xe8 0x00/0x00 0x00/0x00 0x00/0x00 0x00/0x00 * // 9 call task_vtimer_clear
876+
0xe9 0x00/0x00 0x00/0x00 0x00/0x00 0x00/0x00 * // 14 jmp back
877+
0x49 0x8b 0x7f 0x18 // 0 mov 0x18(%r15), %rdi
878+
0xbe 0x02 0x00 0x00 0x00 // 4 mov $0x2, %esi
879+
0xe8 0x00/0x00 0x00/0x00 0x00/0x00 0x00/0x00 * // 9 call task_vtimer_clear
880+
0xe9 0x00/0x00 0x00/0x00 0x00/0x00 0x00/0x00 * // 14 jmp back
881+
0x66 0x66 0x66 0x66 0x66 0x2e // 19 14-byte-nop
882+
0x0f 0x1f 0x84 0x00 0x00 0x00 0x00 0x00 *
883+
`)
884+
873885
func (f *fix) apply13(tlsOff uint32, bsd_ast []byte, timers [][]int) ([][]byte, error) {
874886
p := f.bsd_ast[0]
875887
match1 := p.matchStart(bsd_ast, timers[0][0])
876888
match2 := p.matchStart(bsd_ast, timers[0][1])
877-
if match1 == nil || match2 == nil {
889+
match3 := f.bsd_ast[1].matchStart(bsd_ast, timers[1][0])
890+
if match1 == nil || match2 == nil || match3 == nil {
878891
// shouldn't happen - we found the offset above
879892
return nil, fmt.Errorf("cannot re-match bsd_ast timer")
880893
}
881894

882895
const asmLen = 34
883-
if match1[0] != timers[0][0] || match2[0] != timers[0][1] || match1[4]-match1[0] != asmLen || match2[4]-match2[0] != asmLen {
896+
if match1[0] != timers[0][0] || match2[0] != timers[0][1] || match3[0] != timers[1][0] || match1[4]-match1[0] != asmLen || match2[4]-match2[0] != asmLen {
884897
return nil, fmt.Errorf("bsd_ast match mismatch")
885898
}
886899

@@ -927,7 +940,9 @@ func (f *fix) apply13(tlsOff uint32, bsd_ast []byte, timers [][]int) ([][]byte,
927940
0xe8, 0x00, 0x00, 0x00, 0x00,
928941
)
929942
le.PutUint32(repl1[len(repl1)-4:], call1-uint32(match1[0]+len(repl1)))
943+
l0 := uint32(match1[0] + len(repl1))
930944
repl1 = append(repl1,
945+
// L0:
931946
// xor %edi, %edi
932947
0x31, 0xff,
933948
// xor %esi, %esi
@@ -981,5 +996,81 @@ func (f *fix) apply13(tlsOff uint32, bsd_ast []byte, timers [][]int) ([][]byte,
981996
return nil, fmt.Errorf("bsd_ast repl1 bad math %d %d", len(repl2), asmLen)
982997
}
983998

984-
return [][]byte{repl1, repl2}, nil
999+
// call task_vtimer_clear
1000+
call3 := le.Uint32(bsd_ast[match3[1]-4:]) + uint32(match3[1])
1001+
call3a := le.Uint32(bsd_ast[match3[3]-4:]) + uint32(match3[3])
1002+
if call3 != call3a {
1003+
return nil, fmt.Errorf("bsd_ast call task_vtimer_clear mismatch %#x %#x", call3, call3a)
1004+
}
1005+
1006+
jmp1 := le.Uint32(bsd_ast[match3[2]-4:]) + uint32(match3[2])
1007+
jmp2 := le.Uint32(bsd_ast[match3[4]-4:]) + uint32(match3[4])
1008+
if jmp1 != uint32(match1[2]) {
1009+
return nil, fmt.Errorf("bsd_ast jmp1 mismatch %#x %#x", jmp1, match1[2])
1010+
}
1011+
if jmp2 != uint32(match2[2]) {
1012+
return nil, fmt.Errorf("bsd_ast jmp2 mismatch %#x %#x", jmp2, match2[2])
1013+
}
1014+
1015+
var repl3 []byte
1016+
repl3 = append(repl3,
1017+
// mov $1, %esi
1018+
0xbe, 0x01, 0x00, 0x00, 0x00,
1019+
// mov %esi, %ebx (caller save)
1020+
0x89, 0xf3,
1021+
// call L1
1022+
0xe8, 0x18, 0x00, 0x00, 0x00,
1023+
// jmp back
1024+
0xe9, 0x00, 0x00, 0x00, 0x00,
1025+
)
1026+
le.PutUint32(repl3[len(repl3)-4:], uint32(match1[0]+len(repl1))-uint32(match3[0]+len(repl3)))
1027+
1028+
repl3 = append(repl3,
1029+
// nop
1030+
0x90, 0x90,
1031+
)
1032+
1033+
if len(repl3) != match3[2]-match3[0] {
1034+
return nil, fmt.Errorf("bsd_ast bad repl3 half-len %d %d", len(repl3), match3[2]-match3[0])
1035+
}
1036+
1037+
repl3 = append(repl3,
1038+
// mov $2, %esi
1039+
0xbe, 0x02, 0x00, 0x00, 0x00,
1040+
// mov %esi, %ebx (caller save)
1041+
0x89, 0xf3,
1042+
// call L1
1043+
0xe8, 0x05, 0x00, 0x00, 0x00,
1044+
// jmp back
1045+
0xe9, 0x00, 0x00, 0x00, 0x00,
1046+
)
1047+
le.PutUint32(repl3[len(repl3)-4:], uint32(match2[0]+len(repl2))-uint32(match3[0]+len(repl3)))
1048+
1049+
repl3 = append(repl3,
1050+
// L1:
1051+
// mov 0x18(%r15), %rdi
1052+
0x49, 0x8b, 0x7f, 0x18,
1053+
// call task_vtimer_clear
1054+
0xe8, 0x00, 0x00, 0x00, 0x00,
1055+
)
1056+
le.PutUint32(repl3[len(repl3)-4:], call3-uint32(match3[0]+len(repl3)))
1057+
1058+
repl3 = append(repl3,
1059+
// call L0
1060+
0xe8, 0x00, 0x00, 0x00, 0x00,
1061+
)
1062+
le.PutUint32(repl3[len(repl3)-4:], l0-uint32(match3[0]+len(repl3)))
1063+
1064+
repl3 = append(repl3,
1065+
// ret
1066+
0xc3,
1067+
// nop
1068+
0x90,
1069+
)
1070+
1071+
if len(repl3) != match3[5]-match3[0] {
1072+
return nil, fmt.Errorf("bsd_ast bad repl3 len %d %d", len(repl3), match3[5]-match3[0])
1073+
}
1074+
1075+
return [][]byte{repl1, repl2, repl3}, nil
9851076
}

cmd/pprof_mac_fix/main_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var tests = []string{
1818
"testdata/mach_kernel_10_8_0_i386",
1919
"testdata/mach_kernel_11_4_2",
2020
"testdata/mach_kernel_12_4_0",
21-
// "testdata/mach_kernel_13_0_0",
21+
"testdata/mach_kernel_13_0_0",
2222
}
2323

2424
func TestAll(t *testing.T) {
@@ -35,6 +35,14 @@ func TestAll(t *testing.T) {
3535
k1 := loadKernel(tt + "_fix")
3636
if !bytes.Equal(k.bsd_ast, k1.bsd_ast) {
3737
t.Errorf("%s: rewrite is incorrect\n", tt)
38+
if len(k.bsd_ast) != len(k1.bsd_ast) {
39+
t.Errorf("length mismatch %d vs %d", len(k.bsd_ast), len(k1.bsd_ast))
40+
if len(k.bsd_ast) > len(k1.bsd_ast) {
41+
k.bsd_ast = k.bsd_ast[:len(k1.bsd_ast)]
42+
} else {
43+
k1.bsd_ast = k1.bsd_ast[:len(k.bsd_ast)]
44+
}
45+
}
3846
n := 0
3947
for i := range k.bsd_ast {
4048
if k.bsd_ast[i] != k1.bsd_ast[i] {

cmd/pprof_mac_fix/testdata/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ all: \
77
mach_kernel_11_4_2_fix \
88
mach_kernel_12_4_0 \
99
mach_kernel_12_4_0_fix \
10+
mach_kernel_13_0_0 \
11+
mach_kernel_13_0_0_fix \
1012

1113
%_i386: %_i386.s
1214
gcc -m32 -o $*_i386 $*_i386.s
124 KB
Binary file not shown.

cmd/pprof_mac_fix/testdata/mach_kernel_13_0_0.s

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ _current_thread:
3333
.byte 0x90;
3434

3535
.globl _bsd_ast
36+
.align 16
3637
_bsd_ast:
3738
// 0xffffff80005df3a0 <bsd_ast+0>: push %rbp
3839
.byte 0x55;
Binary file not shown.

cmd/pprof_mac_fix/testdata/mach_kernel_13_0_0_fix.s

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ _current_thread:
3333
.byte 0x90;
3434

3535
.globl _bsd_ast
36+
.align 16
3637
_bsd_ast:
3738
// 0xffffff80005df3a0 <bsd_ast+0>: push %rbp
3839
.byte 0x55;
@@ -124,9 +125,9 @@ _bsd_ast:
124125
.byte 0x75; .byte 0x0e;
125126
// 0xffffff80005df450 <bsd_ast+176>: cmpl $0x0,0x1d0(%r15)
126127
.byte 0x41; .byte 0x83; .byte 0xbf; .byte 0xd0; .byte 0x01; .byte 0x00; .byte 0x00; .byte 0x00;
127-
// 0xffffff80005df458 <bsd_ast+184>: je 0xffffff80005df6fc <bsd_ast+860>
128-
.byte 0x0f; .byte 0x84; .byte 0x9e; .byte 0x02; .byte 0x00; .byte 0x00;
129-
128+
129+
je 5f
130+
130131
mov $0x1, %esi
131132
0:
132133
call 1f
@@ -135,6 +136,7 @@ _bsd_ast:
135136
mov 0x18(%r15), %rdi
136137
mov %esi, %ebx
137138
call _task_vtimer_set
139+
10:
138140
xor %edi, %edi
139141
xor %esi, %esi
140142
mov $4, %ecx
@@ -177,8 +179,8 @@ _bsd_ast:
177179
.byte 0x75; .byte 0x0e;
178180
// 0xffffff80005df4c9 <bsd_ast+297>: cmpl $0x0,0x1f0(%r15)
179181
.byte 0x41; .byte 0x83; .byte 0xbf; .byte 0xf0; .byte 0x01; .byte 0x00; .byte 0x00; .byte 0x00;
180-
// 0xffffff80005df4d1 <bsd_ast+305>: je 0xffffff80005df70f <bsd_ast+879>
181-
.byte 0x0f; .byte 0x84; .byte 0x38; .byte 0x02; .byte 0x00; .byte 0x00;
182+
183+
je 6f
182184

183185
mov $0x2, %esi
184186
call 1b
@@ -430,21 +432,25 @@ _bsd_ast:
430432
.byte 0x5d;
431433
// 0xffffff80005df6fb <bsd_ast+859>: retq
432434
.byte 0xc3;
433-
// 0xffffff80005df6fc <bsd_ast+860>: mov 0x18(%r15),%rdi
434-
.byte 0x49; .byte 0x8b; .byte 0x7f; .byte 0x18;
435-
// 0xffffff80005df700 <bsd_ast+864>: mov $0x1,%esi
436-
.byte 0xbe; .byte 0x01; .byte 0x00; .byte 0x00; .byte 0x00;
437-
// 0xffffff80005df705 <bsd_ast+869>: callq 0xffffff800023f7f0 <task_vtimer_clear>
438-
call _task_vtimer_clear
439-
// 0xffffff80005df70a <bsd_ast+874>: jmpq 0xffffff80005df46c <bsd_ast+204>
440-
.byte 0xe9; .byte 0x5d; .byte 0xfd; .byte 0xff; .byte 0xff;
441-
// 0xffffff80005df70f <bsd_ast+879>: mov 0x18(%r15),%rdi
442-
.byte 0x49; .byte 0x8b; .byte 0x7f; .byte 0x18;
443-
// 0xffffff80005df713 <bsd_ast+883>: mov $0x2,%esi
444-
.byte 0xbe; .byte 0x02; .byte 0x00; .byte 0x00; .byte 0x00;
445-
// 0xffffff80005df718 <bsd_ast+888>: callq 0xffffff800023f7f0 <task_vtimer_clear>
435+
436+
5:
437+
mov $0x1, %esi
438+
mov %esi, %ebx
439+
call 7f
440+
jmp 2b
441+
nop
442+
nop
443+
444+
6:
445+
mov $0x2, %esi
446+
mov %esi, %ebx
447+
call 7f
448+
jmp 4b
449+
450+
7:
451+
mov 0x18(%r15), %rdi
446452
call _task_vtimer_clear
447-
// 0xffffff80005df71d <bsd_ast+893>: jmpq 0xffffff80005df4e5 <bsd_ast+325>
448-
.byte 0xe9; .byte 0xc3; .byte 0xfd; .byte 0xff; .byte 0xff;
449-
// 0xffffff80005df722 <bsd_ast+898>: nopw %cs:0x0(%rax,%rax,1)
450-
.byte 0x66; .byte 0x66; .byte 0x66; .byte 0x66; .byte 0x66; .byte 0x2e; .byte 0x0f; .byte 0x1f; .byte 0x84; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00;
453+
call 10b
454+
ret
455+
nop
456+

0 commit comments

Comments
 (0)