| #!/usr/bin/env perl | |
| use strict; | |
| #---------------------------------------------------------------------- | |
| # Globals | |
| #---------------------------------------------------------------------- | |
| our $unimplemented_str = "UNIMPLEMENTED"; | |
| our $success_str = "OK"; | |
| our $swap = 1; | |
| our $addr_size = 4; | |
| our $thread_suffix_supported = 0; | |
| our $max_bytes_per_line = 32; | |
| our $addr_format = sprintf("0x%%%u.%ux", $addr_size*2, $addr_size*2); | |
| our $pid_format = "%04.4x"; | |
| our $tid_format = "%04.4x"; | |
| our $reg8_href = { extract => \&get8, format => "0x%2.2x" }; | |
| our $reg16_href = { extract => \&get16, format => "0x%4.4x" }; | |
| our $reg32_href = { extract => \&get32, format => "0x%8.8x" }; | |
| our $reg64_href = { extract => \&get64, format => "0x%s" }; | |
| our $reg80_href = { extract => \&get80, format => "0x%s" }; | |
| our $reg128_href = { extract => \&get128, format => "0x%s" }; | |
| our $reg256_href = { extract => \&get256, format => "0x%s" }; | |
| our $float32_href = { extract => \&get32, format => "0x%8.8x" }; | |
| our $float64_href = { extract => \&get64, format => "0x%s" }; | |
| our $float96_href = { extract => \&get96, format => "0x%s" }; | |
| our $curr_cmd = undef; | |
| our $curr_full_cmd = undef; | |
| our %packet_times; | |
| our $curr_time = 0.0; | |
| our $last_time = 0.0; | |
| our $base_time = 0.0; | |
| our $packet_start_time = 0.0; | |
| our $reg_cmd_reg; | |
| our %reg_map = ( | |
| 'i386-gdb' => [ | |
| { name => 'eax', info => $reg32_href }, | |
| { name => 'ecx', info => $reg32_href }, | |
| { name => 'edx', info => $reg32_href }, | |
| { name => 'ebx', info => $reg32_href }, | |
| { name => 'esp', info => $reg32_href }, | |
| { name => 'ebp', info => $reg32_href }, | |
| { name => 'esi', info => $reg32_href }, | |
| { name => 'edi', info => $reg32_href }, | |
| { name => 'eip', info => $reg32_href }, | |
| { name => 'eflags', info => $reg32_href }, | |
| { name => 'cs', info => $reg32_href }, | |
| { name => 'ss', info => $reg32_href }, | |
| { name => 'ds', info => $reg32_href }, | |
| { name => 'es', info => $reg32_href }, | |
| { name => 'fs', info => $reg32_href }, | |
| { name => 'gs', info => $reg32_href }, | |
| { name => 'st0', info => $reg80_href }, | |
| { name => 'st1', info => $reg80_href }, | |
| { name => 'st2', info => $reg80_href }, | |
| { name => 'st3', info => $reg80_href }, | |
| { name => 'st4', info => $reg80_href }, | |
| { name => 'st5', info => $reg80_href }, | |
| { name => 'st6', info => $reg80_href }, | |
| { name => 'st7', info => $reg80_href }, | |
| { name => 'fctrl', info => $reg32_href }, | |
| { name => 'fstat', info => $reg32_href }, | |
| { name => 'ftag', info => $reg32_href }, | |
| { name => 'fiseg', info => $reg32_href }, | |
| { name => 'fioff', info => $reg32_href }, | |
| { name => 'foseg', info => $reg32_href }, | |
| { name => 'fooff', info => $reg32_href }, | |
| { name => 'fop', info => $reg32_href }, | |
| { name => 'xmm0', info => $reg128_href }, | |
| { name => 'xmm1', info => $reg128_href }, | |
| { name => 'xmm2', info => $reg128_href }, | |
| { name => 'xmm3', info => $reg128_href }, | |
| { name => 'xmm4', info => $reg128_href }, | |
| { name => 'xmm5', info => $reg128_href }, | |
| { name => 'xmm6', info => $reg128_href }, | |
| { name => 'xmm7', info => $reg128_href }, | |
| { name => 'mxcsr', info => $reg32_href }, | |
| { name => 'mm0', info => $reg64_href }, | |
| { name => 'mm1', info => $reg64_href }, | |
| { name => 'mm2', info => $reg64_href }, | |
| { name => 'mm3', info => $reg64_href }, | |
| { name => 'mm4', info => $reg64_href }, | |
| { name => 'mm5', info => $reg64_href }, | |
| { name => 'mm6', info => $reg64_href }, | |
| { name => 'mm7', info => $reg64_href }, | |
| ], | |
| 'i386-lldb' => [ | |
| { name => 'eax', info => $reg32_href }, | |
| { name => 'ebx', info => $reg32_href }, | |
| { name => 'ecx', info => $reg32_href }, | |
| { name => 'edx', info => $reg32_href }, | |
| { name => 'edi', info => $reg32_href }, | |
| { name => 'esi', info => $reg32_href }, | |
| { name => 'ebp', info => $reg32_href }, | |
| { name => 'esp', info => $reg32_href }, | |
| { name => 'ss', info => $reg32_href }, | |
| { name => 'eflags', info => $reg32_href }, | |
| { name => 'eip', info => $reg32_href }, | |
| { name => 'cs', info => $reg32_href }, | |
| { name => 'ds', info => $reg32_href }, | |
| { name => 'es', info => $reg32_href }, | |
| { name => 'fs', info => $reg32_href }, | |
| { name => 'gs', info => $reg32_href }, | |
| { name => 'fctrl', info => $reg16_href }, | |
| { name => 'fstat', info => $reg16_href }, | |
| { name => 'ftag', info => $reg8_href }, | |
| { name => 'fop', info => $reg16_href }, | |
| { name => 'fioff', info => $reg32_href }, | |
| { name => 'fiseg', info => $reg16_href }, | |
| { name => 'fooff', info => $reg32_href }, | |
| { name => 'foseg', info => $reg16_href }, | |
| { name => 'mxcsr', info => $reg32_href }, | |
| { name => 'mxcsrmask', info => $reg32_href }, | |
| { name => 'stmm0', info => $reg80_href }, | |
| { name => 'stmm1', info => $reg80_href }, | |
| { name => 'stmm2', info => $reg80_href }, | |
| { name => 'stmm3', info => $reg80_href }, | |
| { name => 'stmm4', info => $reg80_href }, | |
| { name => 'stmm5', info => $reg80_href }, | |
| { name => 'stmm6', info => $reg80_href }, | |
| { name => 'stmm7', info => $reg80_href }, | |
| { name => 'xmm0', info => $reg128_href }, | |
| { name => 'xmm1', info => $reg128_href }, | |
| { name => 'xmm2', info => $reg128_href }, | |
| { name => 'xmm3', info => $reg128_href }, | |
| { name => 'xmm4', info => $reg128_href }, | |
| { name => 'xmm5', info => $reg128_href }, | |
| { name => 'xmm6', info => $reg128_href }, | |
| { name => 'xmm7', info => $reg128_href }, | |
| { name => 'trapno', info => $reg32_href }, | |
| { name => 'err', info => $reg32_href }, | |
| { name => 'faultvaddr', info => $reg32_href }, | |
| ], | |
| 'arm-gdb' => [ | |
| { name => 'r0' , info => $reg32_href }, | |
| { name => 'r1' , info => $reg32_href }, | |
| { name => 'r2' , info => $reg32_href }, | |
| { name => 'r3' , info => $reg32_href }, | |
| { name => 'r4' , info => $reg32_href }, | |
| { name => 'r5' , info => $reg32_href }, | |
| { name => 'r6' , info => $reg32_href }, | |
| { name => 'r7' , info => $reg32_href }, | |
| { name => 'r8' , info => $reg32_href }, | |
| { name => 'r9' , info => $reg32_href }, | |
| { name => 'r10' , info => $reg32_href }, | |
| { name => 'r11' , info => $reg32_href }, | |
| { name => 'r12' , info => $reg32_href }, | |
| { name => 'sp' , info => $reg32_href }, | |
| { name => 'lr' , info => $reg32_href }, | |
| { name => 'pc' , info => $reg32_href }, | |
| { name => 'f0' , info => $float96_href }, | |
| { name => 'f1' , info => $float96_href }, | |
| { name => 'f2' , info => $float96_href }, | |
| { name => 'f3' , info => $float96_href }, | |
| { name => 'f4' , info => $float96_href }, | |
| { name => 'f5' , info => $float96_href }, | |
| { name => 'f6' , info => $float96_href }, | |
| { name => 'f7' , info => $float96_href }, | |
| { name => 'fps' , info => $reg32_href }, | |
| { name => 'cpsr' , info => $reg32_href }, | |
| { name => 's0' , info => $float32_href }, | |
| { name => 's1' , info => $float32_href }, | |
| { name => 's2' , info => $float32_href }, | |
| { name => 's3' , info => $float32_href }, | |
| { name => 's4' , info => $float32_href }, | |
| { name => 's5' , info => $float32_href }, | |
| { name => 's6' , info => $float32_href }, | |
| { name => 's7' , info => $float32_href }, | |
| { name => 's8' , info => $float32_href }, | |
| { name => 's9' , info => $float32_href }, | |
| { name => 's10' , info => $float32_href }, | |
| { name => 's11' , info => $float32_href }, | |
| { name => 's12' , info => $float32_href }, | |
| { name => 's13' , info => $float32_href }, | |
| { name => 's14' , info => $float32_href }, | |
| { name => 's15' , info => $float32_href }, | |
| { name => 's16' , info => $float32_href }, | |
| { name => 's17' , info => $float32_href }, | |
| { name => 's18' , info => $float32_href }, | |
| { name => 's19' , info => $float32_href }, | |
| { name => 's20' , info => $float32_href }, | |
| { name => 's21' , info => $float32_href }, | |
| { name => 's22' , info => $float32_href }, | |
| { name => 's23' , info => $float32_href }, | |
| { name => 's24' , info => $float32_href }, | |
| { name => 's25' , info => $float32_href }, | |
| { name => 's26' , info => $float32_href }, | |
| { name => 's27' , info => $float32_href }, | |
| { name => 's28' , info => $float32_href }, | |
| { name => 's29' , info => $float32_href }, | |
| { name => 's30' , info => $float32_href }, | |
| { name => 's31' , info => $float32_href }, | |
| { name => 'fpscr' , info => $reg32_href }, | |
| { name => 'd16' , info => $float64_href }, | |
| { name => 'd17' , info => $float64_href }, | |
| { name => 'd18' , info => $float64_href }, | |
| { name => 'd19' , info => $float64_href }, | |
| { name => 'd20' , info => $float64_href }, | |
| { name => 'd21' , info => $float64_href }, | |
| { name => 'd22' , info => $float64_href }, | |
| { name => 'd23' , info => $float64_href }, | |
| { name => 'd24' , info => $float64_href }, | |
| { name => 'd25' , info => $float64_href }, | |
| { name => 'd26' , info => $float64_href }, | |
| { name => 'd27' , info => $float64_href }, | |
| { name => 'd28' , info => $float64_href }, | |
| { name => 'd29' , info => $float64_href }, | |
| { name => 'd30' , info => $float64_href }, | |
| { name => 'd31' , info => $float64_href }, | |
| ], | |
| 'arm-lldb' => [ | |
| { name => 'r0' , info => $reg32_href }, | |
| { name => 'r1' , info => $reg32_href }, | |
| { name => 'r2' , info => $reg32_href }, | |
| { name => 'r3' , info => $reg32_href }, | |
| { name => 'r4' , info => $reg32_href }, | |
| { name => 'r5' , info => $reg32_href }, | |
| { name => 'r6' , info => $reg32_href }, | |
| { name => 'r7' , info => $reg32_href }, | |
| { name => 'r8' , info => $reg32_href }, | |
| { name => 'r9' , info => $reg32_href }, | |
| { name => 'r10' , info => $reg32_href }, | |
| { name => 'r11' , info => $reg32_href }, | |
| { name => 'r12' , info => $reg32_href }, | |
| { name => 'sp' , info => $reg32_href }, | |
| { name => 'lr' , info => $reg32_href }, | |
| { name => 'pc' , info => $reg32_href }, | |
| { name => 'cpsr' , info => $reg32_href }, | |
| { name => 's0' , info => $float32_href }, | |
| { name => 's1' , info => $float32_href }, | |
| { name => 's2' , info => $float32_href }, | |
| { name => 's3' , info => $float32_href }, | |
| { name => 's4' , info => $float32_href }, | |
| { name => 's5' , info => $float32_href }, | |
| { name => 's6' , info => $float32_href }, | |
| { name => 's7' , info => $float32_href }, | |
| { name => 's8' , info => $float32_href }, | |
| { name => 's9' , info => $float32_href }, | |
| { name => 's10' , info => $float32_href }, | |
| { name => 's11' , info => $float32_href }, | |
| { name => 's12' , info => $float32_href }, | |
| { name => 's13' , info => $float32_href }, | |
| { name => 's14' , info => $float32_href }, | |
| { name => 's15' , info => $float32_href }, | |
| { name => 's16' , info => $float32_href }, | |
| { name => 's17' , info => $float32_href }, | |
| { name => 's18' , info => $float32_href }, | |
| { name => 's19' , info => $float32_href }, | |
| { name => 's20' , info => $float32_href }, | |
| { name => 's21' , info => $float32_href }, | |
| { name => 's22' , info => $float32_href }, | |
| { name => 's23' , info => $float32_href }, | |
| { name => 's24' , info => $float32_href }, | |
| { name => 's25' , info => $float32_href }, | |
| { name => 's26' , info => $float32_href }, | |
| { name => 's27' , info => $float32_href }, | |
| { name => 's28' , info => $float32_href }, | |
| { name => 's29' , info => $float32_href }, | |
| { name => 's30' , info => $float32_href }, | |
| { name => 's31' , info => $float32_href }, | |
| { name => 'd0' , info => $float64_href }, | |
| { name => 'd1' , info => $float64_href }, | |
| { name => 'd2' , info => $float64_href }, | |
| { name => 'd3' , info => $float64_href }, | |
| { name => 'd4' , info => $float64_href }, | |
| { name => 'd5' , info => $float64_href }, | |
| { name => 'd6' , info => $float64_href }, | |
| { name => 'd7' , info => $float64_href }, | |
| { name => 'd8' , info => $float64_href }, | |
| { name => 'd9' , info => $float64_href }, | |
| { name => 'd10' , info => $float64_href }, | |
| { name => 'd11' , info => $float64_href }, | |
| { name => 'd12' , info => $float64_href }, | |
| { name => 'd13' , info => $float64_href }, | |
| { name => 'd14' , info => $float64_href }, | |
| { name => 'd15' , info => $float64_href }, | |
| { name => 'd16' , info => $float64_href }, | |
| { name => 'd17' , info => $float64_href }, | |
| { name => 'd18' , info => $float64_href }, | |
| { name => 'd19' , info => $float64_href }, | |
| { name => 'd20' , info => $float64_href }, | |
| { name => 'd21' , info => $float64_href }, | |
| { name => 'd22' , info => $float64_href }, | |
| { name => 'd23' , info => $float64_href }, | |
| { name => 'd24' , info => $float64_href }, | |
| { name => 'd25' , info => $float64_href }, | |
| { name => 'd26' , info => $float64_href }, | |
| { name => 'd27' , info => $float64_href }, | |
| { name => 'd28' , info => $float64_href }, | |
| { name => 'd29' , info => $float64_href }, | |
| { name => 'd30' , info => $float64_href }, | |
| { name => 'd31' , info => $float64_href }, | |
| { name => 'fpscr' , info => $reg32_href }, | |
| { name => 'exc' , info => $reg32_href }, | |
| { name => 'fsr' , info => $reg32_href }, | |
| { name => 'far' , info => $reg32_href }, | |
| ], | |
| 'x86_64-gdb' => [ | |
| { name => 'rax' , info => $reg64_href }, | |
| { name => 'rbx' , info => $reg64_href }, | |
| { name => 'rcx' , info => $reg64_href }, | |
| { name => 'rdx' , info => $reg64_href }, | |
| { name => 'rsi' , info => $reg64_href }, | |
| { name => 'rdi' , info => $reg64_href }, | |
| { name => 'rbp' , info => $reg64_href }, | |
| { name => 'rsp' , info => $reg64_href }, | |
| { name => 'r8' , info => $reg64_href }, | |
| { name => 'r9' , info => $reg64_href }, | |
| { name => 'r10' , info => $reg64_href }, | |
| { name => 'r11' , info => $reg64_href }, | |
| { name => 'r12' , info => $reg64_href }, | |
| { name => 'r13' , info => $reg64_href }, | |
| { name => 'r14' , info => $reg64_href }, | |
| { name => 'r15' , info => $reg64_href }, | |
| { name => 'rip' , info => $reg64_href }, | |
| { name => 'eflags' , info => $reg32_href }, | |
| { name => 'cs' , info => $reg32_href }, | |
| { name => 'ss' , info => $reg32_href }, | |
| { name => 'ds' , info => $reg32_href }, | |
| { name => 'es' , info => $reg32_href }, | |
| { name => 'fs' , info => $reg32_href }, | |
| { name => 'gs' , info => $reg32_href }, | |
| { name => 'stmm0' , info => $reg80_href }, | |
| { name => 'stmm1' , info => $reg80_href }, | |
| { name => 'stmm2' , info => $reg80_href }, | |
| { name => 'stmm3' , info => $reg80_href }, | |
| { name => 'stmm4' , info => $reg80_href }, | |
| { name => 'stmm5' , info => $reg80_href }, | |
| { name => 'stmm6' , info => $reg80_href }, | |
| { name => 'stmm7' , info => $reg80_href }, | |
| { name => 'fctrl' , info => $reg32_href }, | |
| { name => 'fstat' , info => $reg32_href }, | |
| { name => 'ftag' , info => $reg32_href }, | |
| { name => 'fiseg' , info => $reg32_href }, | |
| { name => 'fioff' , info => $reg32_href }, | |
| { name => 'foseg' , info => $reg32_href }, | |
| { name => 'fooff' , info => $reg32_href }, | |
| { name => 'fop' , info => $reg32_href }, | |
| { name => 'xmm0' , info => $reg128_href }, | |
| { name => 'xmm1' , info => $reg128_href }, | |
| { name => 'xmm2' , info => $reg128_href }, | |
| { name => 'xmm3' , info => $reg128_href }, | |
| { name => 'xmm4' , info => $reg128_href }, | |
| { name => 'xmm5' , info => $reg128_href }, | |
| { name => 'xmm6' , info => $reg128_href }, | |
| { name => 'xmm7' , info => $reg128_href }, | |
| { name => 'xmm8' , info => $reg128_href }, | |
| { name => 'xmm9' , info => $reg128_href }, | |
| { name => 'xmm10' , info => $reg128_href }, | |
| { name => 'xmm11' , info => $reg128_href }, | |
| { name => 'xmm12' , info => $reg128_href }, | |
| { name => 'xmm13' , info => $reg128_href }, | |
| { name => 'xmm14' , info => $reg128_href }, | |
| { name => 'xmm15' , info => $reg128_href }, | |
| { name => 'mxcsr' , info => $reg32_href }, | |
| ], | |
| 'x86_64-lldb' => [ | |
| { name => 'rax' , info => $reg64_href }, | |
| { name => 'rbx' , info => $reg64_href }, | |
| { name => 'rcx' , info => $reg64_href }, | |
| { name => 'rdx' , info => $reg64_href }, | |
| { name => 'rdi' , info => $reg64_href }, | |
| { name => 'rsi' , info => $reg64_href }, | |
| { name => 'rbp' , info => $reg64_href }, | |
| { name => 'rsp' , info => $reg64_href }, | |
| { name => 'r8 ' , info => $reg64_href }, | |
| { name => 'r9 ' , info => $reg64_href }, | |
| { name => 'r10' , info => $reg64_href }, | |
| { name => 'r11' , info => $reg64_href }, | |
| { name => 'r12' , info => $reg64_href }, | |
| { name => 'r13' , info => $reg64_href }, | |
| { name => 'r14' , info => $reg64_href }, | |
| { name => 'r15' , info => $reg64_href }, | |
| { name => 'rip' , info => $reg64_href }, | |
| { name => 'rflags' , info => $reg64_href }, | |
| { name => 'cs' , info => $reg64_href }, | |
| { name => 'fs' , info => $reg64_href }, | |
| { name => 'gs' , info => $reg64_href }, | |
| { name => 'fctrl' , info => $reg16_href }, | |
| { name => 'fstat' , info => $reg16_href }, | |
| { name => 'ftag' , info => $reg8_href }, | |
| { name => 'fop' , info => $reg16_href }, | |
| { name => 'fioff' , info => $reg32_href }, | |
| { name => 'fiseg' , info => $reg16_href }, | |
| { name => 'fooff' , info => $reg32_href }, | |
| { name => 'foseg' , info => $reg16_href }, | |
| { name => 'mxcsr' , info => $reg32_href }, | |
| { name => 'mxcsrmask' , info => $reg32_href }, | |
| { name => 'stmm0' , info => $reg80_href }, | |
| { name => 'stmm1' , info => $reg80_href }, | |
| { name => 'stmm2' , info => $reg80_href }, | |
| { name => 'stmm3' , info => $reg80_href }, | |
| { name => 'stmm4' , info => $reg80_href }, | |
| { name => 'stmm5' , info => $reg80_href }, | |
| { name => 'stmm6' , info => $reg80_href }, | |
| { name => 'stmm7' , info => $reg80_href }, | |
| { name => 'xmm0' , info => $reg128_href }, | |
| { name => 'xmm1' , info => $reg128_href }, | |
| { name => 'xmm2' , info => $reg128_href }, | |
| { name => 'xmm3' , info => $reg128_href }, | |
| { name => 'xmm4' , info => $reg128_href }, | |
| { name => 'xmm5' , info => $reg128_href }, | |
| { name => 'xmm6' , info => $reg128_href }, | |
| { name => 'xmm7' , info => $reg128_href }, | |
| { name => 'xmm8' , info => $reg128_href }, | |
| { name => 'xmm9' , info => $reg128_href }, | |
| { name => 'xmm10' , info => $reg128_href }, | |
| { name => 'xmm11' , info => $reg128_href }, | |
| { name => 'xmm12' , info => $reg128_href }, | |
| { name => 'xmm13' , info => $reg128_href }, | |
| { name => 'xmm14' , info => $reg128_href }, | |
| { name => 'xmm15' , info => $reg128_href }, | |
| { name => 'trapno' , info => $reg32_href }, | |
| { name => 'err' , info => $reg32_href }, | |
| { name => 'faultvaddr' , info => $reg64_href }, | |
| ] | |
| ); | |
| our $max_register_name_len = 0; | |
| calculate_max_register_name_length(); | |
| our @point_types = ( "software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp" ); | |
| our $opt_v = 0; # verbose | |
| our $opt_g = 0; # debug | |
| our $opt_q = 0; # quiet | |
| our $opt_r = undef; | |
| use Getopt::Std; | |
| getopts('gvqr:'); | |
| our $registers_aref = undef; | |
| if (length($opt_r)) | |
| { | |
| if (exists $reg_map{$opt_r}) | |
| { | |
| $registers_aref = $reg_map{$opt_r}; | |
| } | |
| else | |
| { | |
| die "Can't get registers group for '$opt_r'\n"; | |
| } | |
| } | |
| sub extract_key_value_pairs | |
| { | |
| my $kv_href = {}; | |
| my $arrayref = shift; | |
| my $str = join('',@$arrayref); | |
| my @kv_strs = split(/;/, $str); | |
| foreach my $kv_str (@kv_strs) | |
| { | |
| my ($key, $value) = split(/:/, $kv_str); | |
| $kv_href->{$key} = $value; | |
| } | |
| return $kv_href; | |
| } | |
| sub get_thread_from_thread_suffix | |
| { | |
| if ($thread_suffix_supported) | |
| { | |
| my $arrayref = shift; | |
| # Skip leading semi-colon if needed | |
| $$arrayref[0] == ';' and shift @$arrayref; | |
| my $thread_href = extract_key_value_pairs ($arrayref); | |
| if (exists $thread_href->{thread}) | |
| { | |
| return $thread_href->{thread}; | |
| } | |
| } | |
| return undef; | |
| } | |
| sub calculate_max_register_name_length | |
| { | |
| $max_register_name_len = 7; | |
| foreach my $reg_href (@$registers_aref) | |
| { | |
| my $name_len = length($reg_href->{name}); | |
| if ($max_register_name_len < $name_len) | |
| { | |
| $max_register_name_len = $name_len; | |
| } | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # Hash that maps command characters to the appropriate functions using | |
| # the command character as the key and the value being a reference to | |
| # the dump function for dumping the command itself. | |
| #---------------------------------------------------------------------- | |
| our %cmd_callbacks = | |
| ( | |
| '?' => \&dump_last_signal_cmd, | |
| 'H' => \&dump_set_thread_cmd, | |
| 'T' => \&dump_thread_is_alive_cmd, | |
| 'q' => \&dump_general_query_cmd, | |
| 'Q' => \&dump_general_set_cmd, | |
| 'g' => \&dump_read_regs_cmd, | |
| 'G' => \&dump_write_regs_cmd, | |
| 'p' => \&dump_read_single_register_cmd, | |
| 'P' => \&dump_write_single_register_cmd, | |
| 'm' => \&dump_read_mem_cmd, | |
| 'M' => \&dump_write_mem_cmd, | |
| 'X' => \&dump_write_mem_binary_cmd, | |
| 'Z' => \&dump_bp_wp_command, | |
| 'z' => \&dump_bp_wp_command, | |
| 'k' => \&dump_kill_cmd, | |
| 'A' => \&dump_A_command, | |
| 'c' => \&dump_continue_cmd, | |
| 's' => \&dump_continue_cmd, | |
| 'C' => \&dump_continue_with_signal_cmd, | |
| 'S' => \&dump_continue_with_signal_cmd, | |
| '_M' => \&dump_allocate_memory_cmd, | |
| '_m' => \&dump_deallocate_memory_cmd, | |
| # extended commands | |
| 'v' => \&dump_extended_cmd | |
| ); | |
| #---------------------------------------------------------------------- | |
| # Hash that maps command characters to the appropriate functions using | |
| # the command character as the key and the value being a reference to | |
| # the dump function for the response to the command. | |
| #---------------------------------------------------------------------- | |
| our %rsp_callbacks = | |
| ( | |
| 'c' => \&dump_stop_reply_packet, | |
| 's' => \&dump_stop_reply_packet, | |
| 'C' => \&dump_stop_reply_packet, | |
| '?' => \&dump_stop_reply_packet, | |
| 'T' => \&dump_thread_is_alive_rsp, | |
| 'H' => \&dump_set_thread_rsp, | |
| 'q' => \&dump_general_query_rsp, | |
| 'g' => \&dump_read_regs_rsp, | |
| 'p' => \&dump_read_single_register_rsp, | |
| 'm' => \&dump_read_mem_rsp, | |
| '_M' => \&dump_allocate_memory_rsp, | |
| # extended commands | |
| 'v' => \&dump_extended_rsp, | |
| ); | |
| sub dump_register_value | |
| { | |
| my $indent = shift; | |
| my $arrayref = shift; | |
| my $reg_num = shift; | |
| if ($reg_num >= @$registers_aref) | |
| { | |
| printf("\tinvalid register index %d\n", $reg_num); | |
| return; | |
| } | |
| my $reg_href = $$registers_aref[$reg_num]; | |
| my $reg_name = $reg_href->{name}; | |
| if ($$arrayref[0] eq '#') | |
| { | |
| printf("\t%*s: error: EOS reached when trying to read register %d\n", $max_register_name_len, $reg_name, $reg_num); | |
| } | |
| my $reg_info = $reg_href->{info}; | |
| my $reg_extract = $reg_info->{extract}; | |
| my $reg_format = $reg_info->{format}; | |
| my $reg_val = &$reg_extract($arrayref); | |
| if ($indent) { | |
| printf("\t%*s = $reg_format", $max_register_name_len, $reg_name, $reg_val); | |
| } else { | |
| printf("%s = $reg_format", $reg_name, $reg_val); | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # Extract the command into an array of ASCII char strings for easy | |
| # processing | |
| #---------------------------------------------------------------------- | |
| sub extract_command | |
| { | |
| my $cmd_str = shift; | |
| my @cmd_chars = split(/ */, $cmd_str); | |
| if ($cmd_chars[0] ne '$') | |
| { | |
| # only set the current command if it isn't a reply | |
| $curr_cmd = $cmd_chars[0]; | |
| } | |
| return @cmd_chars; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Strip the 3 checksum array entries after we don't need them anymore | |
| #---------------------------------------------------------------------- | |
| sub strip_checksum | |
| { | |
| my $arrayref = shift; | |
| splice(@$arrayref, -3); | |
| } | |
| #---------------------------------------------------------------------- | |
| # Dump all strings in array by joining them together with no space | |
| # between them | |
| #---------------------------------------------------------------------- | |
| sub dump_chars | |
| { | |
| print join('',@_); | |
| } | |
| #---------------------------------------------------------------------- | |
| # Check if the response is an error 'EXX' | |
| #---------------------------------------------------------------------- | |
| sub is_error_response | |
| { | |
| if ($_[0] eq 'E') | |
| { | |
| shift; | |
| print "ERROR = " . join('',@_) . "\n"; | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'H' command | |
| #---------------------------------------------------------------------- | |
| sub dump_set_thread_cmd | |
| { | |
| my $cmd = shift; | |
| my $mod = shift; | |
| print "set_thread ( $mod, " . join('',@_) . " )\n"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'T' command | |
| #---------------------------------------------------------------------- | |
| our $T_cmd_tid = -1; | |
| sub dump_thread_is_alive_cmd | |
| { | |
| my $cmd = shift; | |
| $T_cmd_tid = get_hex(\@_); | |
| printf("thread_is_alive ( $tid_format )\n", $T_cmd_tid); | |
| } | |
| sub dump_thread_is_alive_rsp | |
| { | |
| my $rsp = join('',@_); | |
| printf("thread_is_alive ( $tid_format ) =>", $T_cmd_tid); | |
| if ($rsp eq 'OK') | |
| { | |
| print " alive.\n"; | |
| } | |
| else | |
| { | |
| print " dead.\n"; | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'H' response | |
| #---------------------------------------------------------------------- | |
| sub dump_set_thread_rsp | |
| { | |
| if (!is_error_response(@_)) | |
| { | |
| print join('',@_) . "\n"; | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'q' command | |
| #---------------------------------------------------------------------- | |
| our $gen_query_cmd; | |
| our $qRegisterInfo_reg_num = -1; | |
| sub dump_general_query_cmd | |
| { | |
| $gen_query_cmd = join('',@_); | |
| if ($gen_query_cmd eq 'qC') | |
| { | |
| print 'get_current_pid ()'; | |
| } | |
| elsif ($gen_query_cmd eq 'qfThreadInfo') | |
| { | |
| print 'get_first_active_threads ()'; | |
| } | |
| elsif ($gen_query_cmd eq 'qsThreadInfo') | |
| { | |
| print 'get_subsequent_active_threads ()'; | |
| } | |
| elsif (index($gen_query_cmd, 'qThreadExtraInfo') == 0) | |
| { | |
| # qThreadExtraInfo,id | |
| print 'get_thread_extra_info ()'; | |
| } | |
| elsif (index($gen_query_cmd, 'qThreadStopInfo') == 0) | |
| { | |
| # qThreadStopInfoXXXX | |
| @_ = splice(@_, length('qThreadStopInfo')); | |
| my $tid = get_addr(\@_); | |
| printf('get_thread_stop_info ( thread = 0x%4.4x )', $tid); | |
| } | |
| elsif (index($gen_query_cmd, 'qSymbol:') == 0) | |
| { | |
| # qCRC:addr,length | |
| print 'gdb_ready_to_serve_symbol_lookups ()'; | |
| } | |
| elsif (index($gen_query_cmd, 'qCRC:') == 0) | |
| { | |
| # qCRC:addr,length | |
| @_ = splice(@_, length('qCRC:')); | |
| my $address = get_addr(\@_); | |
| shift @_; | |
| my $length = join('', @_); | |
| printf("compute_crc (addr = $addr_format, length = $length)", $address); | |
| } | |
| elsif (index($gen_query_cmd, 'qGetTLSAddr:') == 0) | |
| { | |
| # qGetTLSAddr:thread-id,offset,lm | |
| @_ = splice(@_, length('qGetTLSAddr:')); | |
| my ($tid, $offset, $lm) = split (/,/, join('', @_)); | |
| print "get_thread_local_storage_addr (thread-id = $tid, offset = $offset, lm = $lm)"; | |
| } | |
| elsif ($gen_query_cmd eq 'qOffsets') | |
| { | |
| print 'get_section_offsets ()'; | |
| } | |
| elsif (index($gen_query_cmd, 'qRegisterInfo') == 0) | |
| { | |
| @_ = splice(@_, length('qRegisterInfo')); | |
| $qRegisterInfo_reg_num = get_hex(\@_); | |
| printf "get_dynamic_register_info ($qRegisterInfo_reg_num)"; | |
| } | |
| else | |
| { | |
| print $gen_query_cmd; | |
| } | |
| print "\n"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'q' response | |
| #---------------------------------------------------------------------- | |
| sub dump_general_query_rsp | |
| { | |
| my $gen_query_rsp = join('',@_); | |
| my $gen_query_rsp_len = length ($gen_query_rsp); | |
| if ($gen_query_cmd eq 'qC' and index($gen_query_rsp, 'QC') == 0) | |
| { | |
| shift @_; shift @_; | |
| my $pid = get_hex(\@_); | |
| printf("pid = $pid_format\n", $pid); | |
| return; | |
| } | |
| elsif (index($gen_query_cmd, 'qRegisterInfo') == 0) | |
| { | |
| if ($gen_query_rsp_len == 0) | |
| { | |
| print "$unimplemented_str\n"; | |
| } | |
| else | |
| { | |
| if (index($gen_query_rsp, 'name') == 0) | |
| { | |
| $qRegisterInfo_reg_num == 0 and $registers_aref = []; | |
| my @name_and_values = split (/;/, $gen_query_rsp); | |
| my $reg_name = undef; | |
| my $byte_size = 0; | |
| my $pseudo = 0; | |
| foreach (@name_and_values) | |
| { | |
| my ($name, $value) = split /:/; | |
| if ($name eq "name") { $reg_name = $value; } | |
| elsif ($name eq "bitsize") { $byte_size = $value / 8; } | |
| elsif ($name eq "container-regs") { $pseudo = 1; } | |
| } | |
| if (defined $reg_name and $byte_size > 0) | |
| { | |
| if ($byte_size == 4) {push @$registers_aref, { name => $reg_name, info => $reg32_href , pseudo => $pseudo };} | |
| elsif ($byte_size == 8) {push @$registers_aref, { name => $reg_name, info => $reg64_href , pseudo => $pseudo };} | |
| elsif ($byte_size == 1) {push @$registers_aref, { name => $reg_name, info => $reg8_href , pseudo => $pseudo };} | |
| elsif ($byte_size == 2) {push @$registers_aref, { name => $reg_name, info => $reg16_href , pseudo => $pseudo };} | |
| elsif ($byte_size == 10) {push @$registers_aref, { name => $reg_name, info => $reg80_href , pseudo => $pseudo };} | |
| elsif ($byte_size == 12) {push @$registers_aref, { name => $reg_name, info => $float96_href , pseudo => $pseudo };} | |
| elsif ($byte_size == 16) {push @$registers_aref, { name => $reg_name, info => $reg128_href , pseudo => $pseudo };} | |
| elsif ($byte_size == 32) {push @$registers_aref, { name => $reg_name, info => $reg256_href , pseudo => $pseudo };} | |
| } | |
| } | |
| elsif ($gen_query_rsp_len == 3 and index($gen_query_rsp, 'E') == 0) | |
| { | |
| calculate_max_register_name_length(); | |
| } | |
| } | |
| } | |
| elsif ($gen_query_cmd =~ 'qThreadStopInfo') | |
| { | |
| dump_stop_reply_packet (@_); | |
| } | |
| if (dump_standard_response(\@_)) | |
| { | |
| # Do nothing... | |
| } | |
| else | |
| { | |
| print join('',@_) . "\n"; | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'Q' command | |
| #---------------------------------------------------------------------- | |
| our $gen_set_cmd; | |
| sub dump_general_set_cmd | |
| { | |
| $gen_query_cmd = join('',@_); | |
| if ($gen_query_cmd eq 'QStartNoAckMode') | |
| { | |
| print "StartNoAckMode ()" | |
| } | |
| elsif ($gen_query_cmd eq 'QThreadSuffixSupported') | |
| { | |
| $thread_suffix_supported = 1; | |
| print "ThreadSuffixSupported ()" | |
| } | |
| elsif (index($gen_query_cmd, 'QSetMaxPayloadSize:') == 0) | |
| { | |
| @_ = splice(@_, length('QSetMaxPayloadSize:')); | |
| my $max_payload_size = get_hex(\@_); | |
| # QSetMaxPayloadSize:XXXX where XXXX is a hex length of the max | |
| # packet payload size supported by gdb | |
| printf("SetMaxPayloadSize ( 0x%x (%u))", $max_payload_size, $max_payload_size); | |
| } | |
| elsif (index ($gen_query_cmd, 'QSetSTDIN:') == 0) | |
| { | |
| @_ = splice(@_, length('QSetSTDIN:')); | |
| printf ("SetSTDIN (path ='%s')\n", get_hex_string (\@_)); | |
| } | |
| elsif (index ($gen_query_cmd, 'QSetSTDOUT:') == 0) | |
| { | |
| @_ = splice(@_, length('QSetSTDOUT:')); | |
| printf ("SetSTDOUT (path ='%s')\n", get_hex_string (\@_)); | |
| } | |
| elsif (index ($gen_query_cmd, 'QSetSTDERR:') == 0) | |
| { | |
| @_ = splice(@_, length('QSetSTDERR:')); | |
| printf ("SetSTDERR (path ='%s')\n", get_hex_string (\@_)); | |
| } | |
| else | |
| { | |
| print $gen_query_cmd; | |
| } | |
| print "\n"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'k' command | |
| #---------------------------------------------------------------------- | |
| sub dump_kill_cmd | |
| { | |
| my $cmd = shift; | |
| print "kill (" . join('',@_) . ")\n"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'g' command | |
| #---------------------------------------------------------------------- | |
| sub dump_read_regs_cmd | |
| { | |
| my $cmd = shift; | |
| print "read_registers ()\n"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'G' command | |
| #---------------------------------------------------------------------- | |
| sub dump_write_regs_cmd | |
| { | |
| print "write_registers:\n"; | |
| my $cmd = shift; | |
| foreach my $reg_href (@$registers_aref) | |
| { | |
| last if ($_[0] eq '#'); | |
| if ($reg_href->{pseudo} == 0) | |
| { | |
| my $reg_info_href = $reg_href->{info}; | |
| my $reg_name = $reg_href->{name}; | |
| my $reg_extract = $reg_info_href->{extract}; | |
| my $reg_format = $reg_info_href->{format}; | |
| my $reg_val = &$reg_extract(\@_); | |
| printf("\t%*s = $reg_format\n", $max_register_name_len, $reg_name, $reg_val); | |
| } | |
| } | |
| } | |
| sub dump_read_regs_rsp | |
| { | |
| print "read_registers () =>\n"; | |
| if (!is_error_response(@_)) | |
| { | |
| # print join('',@_) . "\n"; | |
| foreach my $reg_href (@$registers_aref) | |
| { | |
| last if ($_[0] eq '#'); | |
| if ($reg_href->{pseudo} == 0) | |
| { | |
| my $reg_info_href = $reg_href->{info}; | |
| my $reg_name = $reg_href->{name}; | |
| my $reg_extract = $reg_info_href->{extract}; | |
| my $reg_format = $reg_info_href->{format}; | |
| my $reg_val = &$reg_extract(\@_); | |
| printf("\t%*s = $reg_format\n", $max_register_name_len, $reg_name, $reg_val); | |
| } | |
| } | |
| } | |
| } | |
| sub dump_read_single_register_rsp | |
| { | |
| dump_register_value(0, \@_, $reg_cmd_reg); | |
| print "\n"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # '_M' - allocate memory command (LLDB extension) | |
| # | |
| # Command: '_M' | |
| # Arg1: Hex byte size as big endian hex string | |
| # Separator: ',' | |
| # Arg2: permissions as string that must be a string that contains any | |
| # combination of 'r' (readable) 'w' (writable) or 'x' (executable) | |
| # | |
| # Returns: The address that was allocated as a big endian hex string | |
| # on success, else an error "EXX" where XX are hex bytes | |
| # that indicate an error code. | |
| # | |
| # Examples: | |
| # _M10,rw # allocate 16 bytes with read + write permissions | |
| # _M100,rx # allocate 256 bytes with read + execute permissions | |
| #---------------------------------------------------------------------- | |
| sub dump_allocate_memory_cmd | |
| { | |
| shift; shift; # shift off the '_' and the 'M' | |
| my $byte_size = get_addr(\@_); | |
| shift; # Skip ',' | |
| printf("allocate_memory ( byte_size = %u (0x%x), permissions = %s)\n", $byte_size, $byte_size, join('',@_)); | |
| } | |
| sub dump_allocate_memory_rsp | |
| { | |
| if (@_ == 3 and $_[0] == 'E') | |
| { | |
| printf("allocated memory addr = ERROR (%s))\n", join('',@_)); | |
| } | |
| else | |
| { | |
| printf("allocated memory addr = 0x%s\n", join('',@_)); | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # '_m' - deallocate memory command (LLDB extension) | |
| # | |
| # Command: '_m' | |
| # Arg1: Hex address as big endian hex string | |
| # | |
| # Returns: "OK" on success "EXX" on error | |
| # | |
| # Examples: | |
| # _m201000 # Free previously allocated memory at address 0x201000 | |
| #---------------------------------------------------------------------- | |
| sub dump_deallocate_memory_cmd | |
| { | |
| shift; shift; # shift off the '_' and the 'm' | |
| printf("deallocate_memory ( addr = 0x%s)\n", join('',@_)); | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'p' command | |
| #---------------------------------------------------------------------- | |
| sub dump_read_single_register_cmd | |
| { | |
| my $cmd = shift; | |
| $reg_cmd_reg = get_hex(\@_); | |
| my $thread = get_thread_from_thread_suffix (\@_); | |
| my $reg_href = $$registers_aref[$reg_cmd_reg]; | |
| if (defined $thread) | |
| { | |
| print "read_register ( reg = \"$reg_href->{name}\", thread = $thread )\n"; | |
| } | |
| else | |
| { | |
| print "read_register ( reg = \"$reg_href->{name}\" )\n"; | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'P' command | |
| #---------------------------------------------------------------------- | |
| sub dump_write_single_register_cmd | |
| { | |
| my $cmd = shift; | |
| my $reg_num = get_hex(\@_); | |
| shift (@_); # Discard the '=' | |
| print "write_register ( "; | |
| dump_register_value(0, \@_, $reg_num); | |
| my $thread = get_thread_from_thread_suffix (\@_); | |
| if (defined $thread) | |
| { | |
| print ", thread = $thread"; | |
| } | |
| print " )\n"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'm' command | |
| #---------------------------------------------------------------------- | |
| our $read_mem_address = 0; | |
| sub dump_read_mem_cmd | |
| { | |
| my $cmd = shift; | |
| $read_mem_address = get_addr(\@_); | |
| shift; # Skip ',' | |
| printf("read_mem ( $addr_format, %s )\n", $read_mem_address, join('',@_)); | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'm' response | |
| #---------------------------------------------------------------------- | |
| sub dump_read_mem_rsp | |
| { | |
| # If the memory read was 2 or 4 bytes, print it out in native format | |
| # instead of just as bytes. | |
| my $num_nibbles = @_; | |
| if ($num_nibbles == 2) | |
| { | |
| printf(" 0x%2.2x", get8(\@_)); | |
| } | |
| elsif ($num_nibbles == 4) | |
| { | |
| printf(" 0x%4.4x", get16(\@_)); | |
| } | |
| elsif ($num_nibbles == 8) | |
| { | |
| printf(" 0x%8.8x", get32(\@_)); | |
| } | |
| elsif ($num_nibbles == 16) | |
| { | |
| printf(" 0x%s", get64(\@_)); | |
| } | |
| else | |
| { | |
| my $curr_address = $read_mem_address; | |
| my $nibble; | |
| my $nibble_offset = 0; | |
| my $max_nibbles_per_line = 2 * $max_bytes_per_line; | |
| foreach $nibble (@_) | |
| { | |
| if (($nibble_offset % $max_nibbles_per_line) == 0) | |
| { | |
| ($nibble_offset > 0) and print "\n "; | |
| printf("$addr_format: ", $curr_address + $nibble_offset/2); | |
| } | |
| (($nibble_offset % 2) == 0) and print ' '; | |
| print $nibble; | |
| $nibble_offset++; | |
| } | |
| } | |
| print "\n"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'c' or 's' command | |
| #---------------------------------------------------------------------- | |
| sub dump_continue_cmd | |
| { | |
| my $cmd = shift; | |
| my $cmd_str; | |
| $cmd eq 'c' and $cmd_str = 'continue'; | |
| $cmd eq 's' and $cmd_str = 'step'; | |
| my $address = -1; | |
| if (@_) | |
| { | |
| my $address = get_addr(\@_); | |
| printf("%s ($addr_format)\n", $cmd_str, $address); | |
| } | |
| else | |
| { | |
| printf("%s ()\n", $cmd_str); | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'Css' continue (C) with signal (ss where 'ss' is two hex digits) | |
| # 'Sss' step (S) with signal (ss where 'ss' is two hex digits) | |
| #---------------------------------------------------------------------- | |
| sub dump_continue_with_signal_cmd | |
| { | |
| my $cmd = shift; | |
| my $address = -1; | |
| my $cmd_str; | |
| $cmd eq 'c' and $cmd_str = 'continue'; | |
| $cmd eq 's' and $cmd_str = 'step'; | |
| my $signal = get_hex(\@_); | |
| if (@_) | |
| { | |
| my $address = 0; | |
| if (@_ && $_[0] == ';') | |
| { | |
| shift; | |
| $address = get_addr(\@_); | |
| } | |
| } | |
| if ($address != -1) | |
| { | |
| printf("%s_with_signal (signal = 0x%2.2x, address = $addr_format)\n", $cmd_str, $signal, $address); | |
| } | |
| else | |
| { | |
| printf("%s_with_signal (signal = 0x%2.2x)\n", $cmd_str, $signal); | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'A' command | |
| #---------------------------------------------------------------------- | |
| sub dump_A_command | |
| { | |
| my $cmd = get_expected_char(\@_, 'A') or print "error: incorrect command letter for argument packet, expected 'A'\n"; | |
| printf("set_program_arguments (\n"); | |
| do | |
| { | |
| my $arg_len = get_uint(\@_); | |
| get_expected_char(\@_, ',') or die "error: missing comma after argument length...?\n"; | |
| my $arg_idx = get_uint(\@_); | |
| get_expected_char(\@_, ',') or die "error: missing comma after argument number...?\n"; | |
| my $arg = ''; | |
| my $num_hex8_bytes = $arg_len/2; | |
| for (1 .. $num_hex8_bytes) | |
| { | |
| $arg .= sprintf("%c", get8(\@_)) | |
| } | |
| printf(" <%3u> argv[%u] = '%s'\n", $arg_len, $arg_idx, $arg); | |
| if (@_ > 0) | |
| { | |
| get_expected_char(\@_, ',') or die "error: missing comma after argument argument ASCII hex bytes...?\n"; | |
| } | |
| } while (@_ > 0); | |
| printf(" )\n"); | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'z' and 'Z' command | |
| #---------------------------------------------------------------------- | |
| sub dump_bp_wp_command | |
| { | |
| my $cmd = shift; | |
| my $type = shift; | |
| shift; # Skip ',' | |
| my $address = get_addr(\@_); | |
| shift; # Skip ',' | |
| my $length = join('',@_); | |
| if ($cmd eq 'z') | |
| { | |
| printf("remove $point_types[$type]($addr_format, %d)\n", $address, $length); | |
| } | |
| else | |
| { | |
| printf("insert $point_types[$type]($addr_format, %d)\n", $address, $length); | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'X' command | |
| #---------------------------------------------------------------------- | |
| sub dump_write_mem_binary_cmd | |
| { | |
| my $cmd = shift; | |
| my $address = get_addr(\@_); | |
| shift; # Skip ',' | |
| my ($length, $binary) = split(/:/, join('',@_)); | |
| printf("write_mem_binary ( $addr_format, %d, %s)\n", $address, $length, $binary); | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'M' command | |
| #---------------------------------------------------------------------- | |
| sub dump_write_mem_cmd | |
| { | |
| my $cmd = shift; | |
| my $address = get_addr(\@_); | |
| shift; # Skip ',' | |
| my ($length, $hex_bytes) = split(/:/, join('',@_)); | |
| # printf("write_mem ( $addr_format, %d, %s)\n", $address, $length, $hex_bytes); | |
| printf("write_mem ( addr = $addr_format, len = %d (0x%x), bytes = ", $address, $length, $length); | |
| splice(@_, 0, length($length)+1); | |
| my $curr_address = $address; | |
| my $nibble; | |
| my $nibble_count = 0; | |
| my $max_nibbles_per_line = 2 * $max_bytes_per_line; | |
| foreach $nibble (@_) | |
| { | |
| (($nibble_count % 2) == 0) and print ' '; | |
| print $nibble; | |
| $nibble_count++; | |
| } | |
| # If the memory to write is 2 or 4 bytes, print it out in native format | |
| # instead of just as bytes. | |
| if (@_ == 4) | |
| { | |
| printf(" ( 0x%4.4x )", get16(\@_)); | |
| } | |
| elsif (@_ == 8) | |
| { | |
| printf(" ( 0x%8.8x )", get32(\@_)); | |
| } | |
| print " )\n"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'v' command | |
| #---------------------------------------------------------------------- | |
| our $extended_rsp_callback = 0; | |
| sub dump_extended_cmd | |
| { | |
| $extended_rsp_callback = 0; | |
| if (join('', @_[0..4]) eq "vCont") | |
| { | |
| dump_extended_continue_cmd(splice(@_,5)); | |
| } | |
| elsif (join('', @_[0..7]) eq 'vAttach;') | |
| { | |
| dump_attach_command (splice(@_,8)); | |
| } | |
| elsif (join('', @_[0..11]) eq 'vAttachWait;') | |
| { | |
| dump_attach_wait_command (splice(@_,12)); | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'v' response | |
| #---------------------------------------------------------------------- | |
| sub dump_extended_rsp | |
| { | |
| if ($extended_rsp_callback) | |
| { | |
| &$extended_rsp_callback(@_); | |
| } | |
| $extended_rsp_callback = 0; | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'vAttachWait' command | |
| #---------------------------------------------------------------------- | |
| sub dump_attach_wait_command | |
| { | |
| print "attach_wait ( "; | |
| while (@_) | |
| { | |
| printf("%c", get8(\@_)) | |
| } | |
| printf " )\n"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'vAttach' command | |
| #---------------------------------------------------------------------- | |
| sub dump_attach_command | |
| { | |
| printf("attach ( pid = %i )", get_hex(\@_)); | |
| $extended_rsp_callback = \&dump_stop_reply_packet; | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'vCont' command | |
| #---------------------------------------------------------------------- | |
| sub dump_extended_continue_cmd | |
| { | |
| print "extended_continue ( "; | |
| my $cmd = shift; | |
| if ($cmd eq '?') | |
| { | |
| print "list supported modes )\n"; | |
| $extended_rsp_callback = \&dump_extended_continue_rsp; | |
| } | |
| elsif ($cmd eq ';') | |
| { | |
| $extended_rsp_callback = \&dump_stop_reply_packet; | |
| my $i = 0; | |
| while ($#_ >= 0) | |
| { | |
| if ($i > 0) | |
| { | |
| print ", "; | |
| } | |
| my $continue_cmd = shift; | |
| my $tmp; | |
| if ($continue_cmd eq 'c') | |
| { | |
| print "continue"; | |
| } | |
| elsif ($continue_cmd eq 'C') | |
| { | |
| print "continue with signal "; | |
| print shift; | |
| print shift; | |
| } | |
| elsif ($continue_cmd eq 's') | |
| { | |
| print "step"; | |
| } | |
| elsif ($continue_cmd eq 'S') | |
| { | |
| print "step with signal "; | |
| print shift; | |
| print shift; | |
| } | |
| if ($_[0] eq ':') | |
| { | |
| shift; # Skip ':' | |
| print " for thread "; | |
| while ($#_ >= 0) | |
| { | |
| $tmp = shift; | |
| if (length($tmp) > 0 && $tmp ne ';') { | |
| print $tmp; | |
| } else { | |
| last; | |
| } | |
| } | |
| } | |
| $i++; | |
| } | |
| printf " )\n"; | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # 'vCont' response | |
| #---------------------------------------------------------------------- | |
| sub dump_extended_continue_rsp | |
| { | |
| if (scalar(@_) == 0) | |
| { | |
| print "$unimplemented_str\n"; | |
| } | |
| else | |
| { | |
| print "extended_continue supports " . join('',@_) . "\n"; | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # Dump the command ascii for any unknown commands | |
| #---------------------------------------------------------------------- | |
| sub dump_other_cmd | |
| { | |
| print "other = " . join('',@_) . "\n"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Check to see if the response was unsupported with appropriate checksum | |
| #---------------------------------------------------------------------- | |
| sub rsp_is_unsupported | |
| { | |
| return join('',@_) eq "#00"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Check to see if the response was "OK" with appropriate checksum | |
| #---------------------------------------------------------------------- | |
| sub rsp_is_OK | |
| { | |
| return join('',@_) eq "OK#9a"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Dump a response for an unknown command | |
| #---------------------------------------------------------------------- | |
| sub dump_other_rsp | |
| { | |
| print "other = " . join('',@_) . "\n"; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Get a byte from the ascii string assuming that the 2 nibble ascii | |
| # characters are in hex. | |
| # | |
| # The argument for this function needs to be a reference to an array | |
| # that contains single character strings and the array will get | |
| # updated by shifting characters off the front of it (no leading # "0x") | |
| #---------------------------------------------------------------------- | |
| sub get8 | |
| { | |
| my $arrayref = shift; | |
| my $val = hex(shift(@$arrayref) . shift(@$arrayref)); | |
| return $val; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Get a 16 bit integer and swap if $swap global is set to a non-zero | |
| # value. | |
| # | |
| # The argument for this function needs to be a reference to an array | |
| # that contains single character strings and the array will get | |
| # updated by shifting characters off the front of it (no leading # "0x") | |
| #---------------------------------------------------------------------- | |
| sub get16 | |
| { | |
| my $arrayref = shift; | |
| my $val = 0; | |
| if ($swap) | |
| { | |
| $val = get8($arrayref) | | |
| get8($arrayref) << 8; | |
| } | |
| else | |
| { | |
| $val = get8($arrayref) << 8 | | |
| get8($arrayref) ; | |
| } | |
| return $val; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Get a 32 bit integer and swap if $swap global is set to a non-zero | |
| # value. | |
| # | |
| # The argument for this function needs to be a reference to an array | |
| # that contains single character strings and the array will get | |
| # updated by shifting characters off the front of it (no leading # "0x") | |
| #---------------------------------------------------------------------- | |
| sub get32 | |
| { | |
| my $arrayref = shift; | |
| my $val = 0; | |
| if ($swap) | |
| { | |
| $val = get8($arrayref) | | |
| get8($arrayref) << 8 | | |
| get8($arrayref) << 16 | | |
| get8($arrayref) << 24 ; | |
| } | |
| else | |
| { | |
| $val = get8($arrayref) << 24 | | |
| get8($arrayref) << 16 | | |
| get8($arrayref) << 8 | | |
| get8($arrayref) ; | |
| } | |
| return $val; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Get a 64 bit hex value as a string | |
| # | |
| # The argument for this function needs to be a reference to an array | |
| # that contains single character strings and the array will get | |
| # updated by shifting characters off the front of it (no leading # "0x") | |
| #---------------------------------------------------------------------- | |
| sub get64 | |
| { | |
| my $arrayref = shift; | |
| my $val = ''; | |
| my @nibbles; | |
| if ($swap) | |
| { | |
| push @nibbles, splice(@$arrayref, 14, 2); | |
| push @nibbles, splice(@$arrayref, 12, 2); | |
| push @nibbles, splice(@$arrayref, 10, 2); | |
| push @nibbles, splice(@$arrayref, 8, 2); | |
| push @nibbles, splice(@$arrayref, 6, 2); | |
| push @nibbles, splice(@$arrayref, 4, 2); | |
| push @nibbles, splice(@$arrayref, 2, 2); | |
| push @nibbles, splice(@$arrayref, 0, 2); | |
| } | |
| else | |
| { | |
| (@nibbles) = splice(@$arrayref, 0, ((64/8) * 2)); | |
| } | |
| $val = join('', @nibbles); | |
| return $val; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Get a 80 bit hex value as a string | |
| # | |
| # The argument for this function needs to be a reference to an array | |
| # that contains single character strings and the array will get | |
| # updated by shifting characters off the front of it (no leading # "0x") | |
| #---------------------------------------------------------------------- | |
| sub get80 | |
| { | |
| my $arrayref = shift; | |
| my $val = ''; | |
| my @nibbles; | |
| if ($swap) | |
| { | |
| push @nibbles, splice(@$arrayref, 18, 2); | |
| push @nibbles, splice(@$arrayref, 16, 2); | |
| push @nibbles, splice(@$arrayref, 14, 2); | |
| push @nibbles, splice(@$arrayref, 12, 2); | |
| push @nibbles, splice(@$arrayref, 10, 2); | |
| push @nibbles, splice(@$arrayref, 8, 2); | |
| push @nibbles, splice(@$arrayref, 6, 2); | |
| push @nibbles, splice(@$arrayref, 4, 2); | |
| push @nibbles, splice(@$arrayref, 2, 2); | |
| push @nibbles, splice(@$arrayref, 0, 2); | |
| } | |
| else | |
| { | |
| (@nibbles) = splice(@$arrayref, 0, ((80/8) * 2)); | |
| } | |
| $val = join('', @nibbles); | |
| return $val; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Get a 96 bit hex value as a string | |
| # | |
| # The argument for this function needs to be a reference to an array | |
| # that contains single character strings and the array will get | |
| # updated by shifting characters off the front of it (no leading # "0x") | |
| #---------------------------------------------------------------------- | |
| sub get96 | |
| { | |
| my $arrayref = shift; | |
| my $val = ''; | |
| my @nibbles; | |
| if ($swap) | |
| { | |
| push @nibbles, splice(@$arrayref, 22, 2); | |
| push @nibbles, splice(@$arrayref, 20, 2); | |
| push @nibbles, splice(@$arrayref, 18, 2); | |
| push @nibbles, splice(@$arrayref, 16, 2); | |
| push @nibbles, splice(@$arrayref, 14, 2); | |
| push @nibbles, splice(@$arrayref, 12, 2); | |
| push @nibbles, splice(@$arrayref, 10, 2); | |
| push @nibbles, splice(@$arrayref, 8, 2); | |
| push @nibbles, splice(@$arrayref, 6, 2); | |
| push @nibbles, splice(@$arrayref, 4, 2); | |
| push @nibbles, splice(@$arrayref, 2, 2); | |
| push @nibbles, splice(@$arrayref, 0, 2); | |
| } | |
| else | |
| { | |
| (@nibbles) = splice(@$arrayref, 0, ((96/8) * 2)); | |
| } | |
| $val = join('', @nibbles); | |
| return $val; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Get a 128 bit hex value as a string | |
| # | |
| # The argument for this function needs to be a reference to an array | |
| # that contains single character strings and the array will get | |
| # updated by shifting characters off the front of it (no leading # "0x") | |
| #---------------------------------------------------------------------- | |
| sub get128 | |
| { | |
| my $arrayref = shift; | |
| my $val = ''; | |
| my @nibbles; | |
| if ($swap) | |
| { | |
| push @nibbles, splice(@$arrayref, 30, 2); | |
| push @nibbles, splice(@$arrayref, 28, 2); | |
| push @nibbles, splice(@$arrayref, 26, 2); | |
| push @nibbles, splice(@$arrayref, 24, 2); | |
| push @nibbles, splice(@$arrayref, 22, 2); | |
| push @nibbles, splice(@$arrayref, 20, 2); | |
| push @nibbles, splice(@$arrayref, 18, 2); | |
| push @nibbles, splice(@$arrayref, 16, 2); | |
| push @nibbles, splice(@$arrayref, 14, 2); | |
| push @nibbles, splice(@$arrayref, 12, 2); | |
| push @nibbles, splice(@$arrayref, 10, 2); | |
| push @nibbles, splice(@$arrayref, 8, 2); | |
| push @nibbles, splice(@$arrayref, 6, 2); | |
| push @nibbles, splice(@$arrayref, 4, 2); | |
| push @nibbles, splice(@$arrayref, 2, 2); | |
| push @nibbles, splice(@$arrayref, 0, 2); | |
| } | |
| else | |
| { | |
| (@nibbles) = splice(@$arrayref, 0, ((128/8) * 2)); | |
| } | |
| $val = join('', @nibbles); | |
| return $val; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Get a 256 bit hex value as a string | |
| # | |
| # The argument for this function needs to be a reference to an array | |
| # that contains single character strings and the array will get | |
| # updated by shifting characters off the front of it (no leading # "0x") | |
| #---------------------------------------------------------------------- | |
| sub get256 | |
| { | |
| my $arrayref = shift; | |
| my $val = ''; | |
| my @nibbles; | |
| if ($swap) | |
| { | |
| push @nibbles, splice(@$arrayref, 62, 2); | |
| push @nibbles, splice(@$arrayref, 60, 2); | |
| push @nibbles, splice(@$arrayref, 58, 2); | |
| push @nibbles, splice(@$arrayref, 56, 2); | |
| push @nibbles, splice(@$arrayref, 54, 2); | |
| push @nibbles, splice(@$arrayref, 52, 2); | |
| push @nibbles, splice(@$arrayref, 50, 2); | |
| push @nibbles, splice(@$arrayref, 48, 2); | |
| push @nibbles, splice(@$arrayref, 46, 2); | |
| push @nibbles, splice(@$arrayref, 44, 2); | |
| push @nibbles, splice(@$arrayref, 42, 2); | |
| push @nibbles, splice(@$arrayref, 40, 2); | |
| push @nibbles, splice(@$arrayref, 38, 2); | |
| push @nibbles, splice(@$arrayref, 36, 2); | |
| push @nibbles, splice(@$arrayref, 34, 2); | |
| push @nibbles, splice(@$arrayref, 32, 2); | |
| push @nibbles, splice(@$arrayref, 30, 2); | |
| push @nibbles, splice(@$arrayref, 28, 2); | |
| push @nibbles, splice(@$arrayref, 26, 2); | |
| push @nibbles, splice(@$arrayref, 24, 2); | |
| push @nibbles, splice(@$arrayref, 22, 2); | |
| push @nibbles, splice(@$arrayref, 20, 2); | |
| push @nibbles, splice(@$arrayref, 18, 2); | |
| push @nibbles, splice(@$arrayref, 16, 2); | |
| push @nibbles, splice(@$arrayref, 14, 2); | |
| push @nibbles, splice(@$arrayref, 12, 2); | |
| push @nibbles, splice(@$arrayref, 10, 2); | |
| push @nibbles, splice(@$arrayref, 8, 2); | |
| push @nibbles, splice(@$arrayref, 6, 2); | |
| push @nibbles, splice(@$arrayref, 4, 2); | |
| push @nibbles, splice(@$arrayref, 2, 2); | |
| push @nibbles, splice(@$arrayref, 0, 2); | |
| } | |
| else | |
| { | |
| (@nibbles) = splice(@$arrayref, 0, ((256/8) * 2)); | |
| } | |
| $val = join('', @nibbles); | |
| return $val; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Get an unsigned integer value by grabbing items off the front of | |
| # the array stopping when a non-digit char string is encountered. | |
| # | |
| # The argument for this function needs to be a reference to an array | |
| # that contains single character strings and the array will get | |
| # updated by shifting characters off the front of it | |
| #---------------------------------------------------------------------- | |
| sub get_uint | |
| { | |
| my $arrayref = shift; | |
| @$arrayref == 0 and return 0; | |
| my $val = 0; | |
| while ($$arrayref[0] =~ /[0-9]/) | |
| { | |
| $val = $val * 10 + int(shift(@$arrayref)); | |
| } | |
| return $val; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Check the first character in the array and if it matches the expected | |
| # character, return that character, else return undef; | |
| # | |
| # The argument for this function needs to be a reference to an array | |
| # that contains single character strings and the array will get | |
| # updated by shifting characters off the front of it. If the expected | |
| # character doesn't match, it won't touch the array. If the first | |
| # character does match, it will shift it off and return it. | |
| #---------------------------------------------------------------------- | |
| sub get_expected_char | |
| { | |
| my $arrayref = shift; | |
| my $expected_char = shift; | |
| if ($expected_char eq $$arrayref[0]) | |
| { | |
| return shift(@$arrayref); | |
| } | |
| return undef; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Get a hex value by grabbing items off the front of the array and | |
| # stopping when a non-hex char string is encountered. | |
| # | |
| # The argument for this function needs to be a reference to an array | |
| # that contains single character strings and the array will get | |
| # updated by shifting characters off the front of it (no leading # "0x") | |
| #---------------------------------------------------------------------- | |
| sub get_hex | |
| { | |
| my $arrayref = shift; | |
| my $my_swap = @_ ? shift : 0; | |
| my $shift = 0; | |
| my $val = 0; | |
| while ($$arrayref[0] =~ /[0-9a-fA-F]/) | |
| { | |
| if ($my_swap) | |
| { | |
| my $byte = hex(shift(@$arrayref)) << 4 | hex(shift(@$arrayref)); | |
| $val |= $byte << $shift; | |
| $shift += 8; | |
| } | |
| else | |
| { | |
| $val <<= 4; | |
| $val |= hex(shift(@$arrayref)); | |
| } | |
| } | |
| return $val; | |
| } | |
| #---------------------------------------------------------------------- | |
| # Get an address value by grabbing items off the front of the array. | |
| # | |
| # The argument for this function needs to be a reference to an array | |
| # that contains single character strings and the array will get | |
| # updated by shifting characters off the front of it (no leading # "0x") | |
| #---------------------------------------------------------------------- | |
| sub get_addr | |
| { | |
| get_hex(shift); | |
| } | |
| sub get_hex_string | |
| { | |
| my $arrayref = shift; | |
| my $str = ''; | |
| while ($$arrayref[0] =~ /[0-9a-fA-F]/ and $$arrayref[1] =~ /[0-9a-fA-F]/) | |
| { | |
| my $hi_nibble = hex(shift(@$arrayref)); | |
| my $lo_nibble = hex(shift(@$arrayref)); | |
| my $byte = ($hi_nibble << 4) | $lo_nibble; | |
| $str .= chr($byte); | |
| } | |
| return $str; | |
| } | |
| sub dump_stop_reply_data | |
| { | |
| while ($#_ >= 0) | |
| { | |
| last unless ($_[0] ne '#'); | |
| my $key = ''; | |
| my $value = ''; | |
| my $comment = ''; | |
| if ($_[0] =~ /[0-9a-fA-F]/ && $_[1] =~ /[0-9a-fA-F]/) | |
| { | |
| my $reg_num = get8(\@_); | |
| shift(@_); # Skip ':' | |
| if (defined ($registers_aref) && $reg_num < @$registers_aref) | |
| { | |
| dump_register_value(1, \@_, $reg_num); | |
| print "\n"; | |
| shift(@_); # Skip ';' | |
| next; | |
| } | |
| $key = sprintf("reg %u", $reg_num); | |
| } | |
| my $char; | |
| if (length($key) == 0) | |
| { | |
| while (1) | |
| { | |
| $char = shift(@_); | |
| if (length($char) == 0 or $char eq ':' or $char eq '#') { last; } | |
| $key .= $char; | |
| } | |
| } | |
| while (1) | |
| { | |
| $char = shift(@_); | |
| if (length($char) == 0 or $char eq ';' or $char eq '#') { last; } | |
| $value .= $char; | |
| } | |
| if ($key eq 'metype') | |
| { | |
| our %metype_to_name = ( | |
| '1' => ' (EXC_BAD_ACCESS)', | |
| '2' => ' (EXC_BAD_INSTRUCTION)', | |
| '3' => ' (EXC_ARITHMETIC)', | |
| '4' => ' (EXC_EMULATION)', | |
| '5' => ' (EXC_SOFTWARE)', | |
| '6' => ' (EXC_BREAKPOINT)', | |
| '7' => ' (EXC_SYSCALL)', | |
| '8' => ' (EXC_MACH_SYSCALL)', | |
| '9' => ' (EXC_RPC_ALERT)', | |
| '10' => ' (EXC_CRASH)' | |
| ); | |
| if (exists $metype_to_name{$value}) | |
| { | |
| $comment = $metype_to_name{$value}; | |
| } | |
| } | |
| printf("\t%*s = %s$comment\n", $max_register_name_len, $key, $value); | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # Dumps a Stop Reply Packet which happens in response to a step, | |
| # continue, last signal, and probably a few other commands. | |
| #---------------------------------------------------------------------- | |
| sub dump_stop_reply_packet | |
| { | |
| my $what = shift(@_); | |
| if ($what eq 'S' or $what eq 'T') | |
| { | |
| my $signo = get8(\@_); | |
| our %signo_to_name = ( | |
| '1' => ' SIGHUP', | |
| '2' => ' SIGINT', | |
| '3' => ' SIGQUIT', | |
| '4' => ' SIGILL', | |
| '5' => ' SIGTRAP', | |
| '6' => ' SIGABRT', | |
| '7' => ' SIGPOLL/SIGEMT', | |
| '8' => ' SIGFPE', | |
| '9' => ' SIGKILL', | |
| '10' => ' SIGBUS', | |
| '11' => ' SIGSEGV', | |
| '12' => ' SIGSYS', | |
| '13' => ' SIGPIPE', | |
| '14' => ' SIGALRM', | |
| '15' => ' SIGTERM', | |
| '16' => ' SIGURG', | |
| '17' => ' SIGSTOP', | |
| '18' => ' SIGTSTP', | |
| '19' => ' SIGCONT', | |
| '20' => ' SIGCHLD', | |
| '21' => ' SIGTTIN', | |
| '22' => ' SIGTTOU', | |
| '23' => ' SIGIO', | |
| '24' => ' SIGXCPU', | |
| '25' => ' SIGXFSZ', | |
| '26' => ' SIGVTALRM', | |
| '27' => ' SIGPROF', | |
| '28' => ' SIGWINCH', | |
| '29' => ' SIGINFO', | |
| '30' => ' SIGUSR1', | |
| '31' => ' SIGUSR2', | |
| '145' => ' TARGET_EXC_BAD_ACCESS', # 0x91 | |
| '146' => ' TARGET_EXC_BAD_INSTRUCTION', # 0x92 | |
| '147' => ' TARGET_EXC_ARITHMETIC', # 0x93 | |
| '148' => ' TARGET_EXC_EMULATION', # 0x94 | |
| '149' => ' TARGET_EXC_SOFTWARE', # 0x95 | |
| '150' => ' TARGET_EXC_BREAKPOINT' # 0x96 | |
| ); | |
| my $signo_str = sprintf("%i", $signo); | |
| my $signo_name = ''; | |
| if (exists $signo_to_name{$signo_str}) | |
| { | |
| $signo_name = $signo_to_name{$signo_str}; | |
| } | |
| printf ("signal (signo=%u$signo_name)\n", $signo); | |
| dump_stop_reply_data (@_); | |
| } | |
| elsif ($what eq 'W') | |
| { | |
| print 'process_exited( ' . shift(@_) . shift(@_) . " )\n"; | |
| } | |
| elsif ($what eq 'X') | |
| { | |
| print 'process_terminated( ' . shift(@_) . shift(@_) . " )\n"; | |
| } | |
| elsif ($what eq 'O') | |
| { | |
| my $console_output = ''; | |
| my $num_hex8_bytes = @_/2; | |
| for (1 .. $num_hex8_bytes) | |
| { | |
| $console_output .= sprintf("%c", get8(\@_)) | |
| } | |
| print "program_console_output('$console_output')\n"; | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # '?' command | |
| #---------------------------------------------------------------------- | |
| sub dump_last_signal_cmd | |
| { | |
| my $cmd = shift; | |
| print 'last_signal (' . join('',@_) . ")\n"; | |
| } | |
| sub dump_raw_command | |
| { | |
| my $cmd_aref = shift; | |
| my $callback_ref; | |
| $curr_cmd = $$cmd_aref[0]; | |
| if ($curr_cmd eq 'q' or $curr_cmd eq 'Q' or $curr_cmd eq '_') | |
| { | |
| $curr_full_cmd = ''; | |
| foreach my $ch (@$cmd_aref) | |
| { | |
| $ch !~ /[A-Za-z_]/ and last; | |
| $curr_full_cmd .= $ch; | |
| } | |
| } | |
| else | |
| { | |
| $curr_full_cmd = $curr_cmd; | |
| } | |
| $curr_cmd eq '_' and $curr_cmd .= $$cmd_aref[1]; | |
| $callback_ref = $cmd_callbacks{$curr_cmd}; | |
| if ($callback_ref) | |
| { | |
| &$callback_ref(@$cmd_aref); | |
| } | |
| else | |
| { | |
| # Strip the command byte for responses since we injected that above | |
| dump_other_cmd(@$cmd_aref); | |
| } | |
| } | |
| sub dump_standard_response | |
| { | |
| my $cmd_aref = shift; | |
| my $cmd_len = scalar(@$cmd_aref); | |
| if ($cmd_len == 0) | |
| { | |
| print "$unimplemented_str\n"; | |
| return 1; | |
| } | |
| my $response = join('', @$cmd_aref); | |
| if ($response eq 'OK') | |
| { | |
| print "$success_str\n"; | |
| return 1; | |
| } | |
| if ($cmd_len == 3 and index($response, 'E') == 0) | |
| { | |
| print "ERROR: " . substr($response, 1) . "\n"; | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| sub dump_raw_response | |
| { | |
| my $cmd_aref = shift; | |
| my $callback_ref; | |
| if ($packet_start_time != 0.0) | |
| { | |
| if (length($curr_full_cmd) > 0) | |
| { | |
| $packet_times{$curr_full_cmd} += $curr_time - $packet_start_time; | |
| } | |
| else | |
| { | |
| $packet_times{$curr_cmd} += $curr_time - $packet_start_time; | |
| } | |
| $packet_start_time = 0.0; | |
| } | |
| $callback_ref = $rsp_callbacks{$curr_cmd}; | |
| if ($callback_ref) | |
| { | |
| &$callback_ref(@$cmd_aref); | |
| } | |
| else | |
| { | |
| dump_standard_response($cmd_aref) or dump_other_rsp(@$cmd_aref); | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # Dumps any command and handles simple error checking on the responses | |
| # for commands that are unsupported or OK. | |
| #---------------------------------------------------------------------- | |
| sub dump_command | |
| { | |
| my $cmd_str = shift; | |
| # Dump the original command string if verbose is on | |
| if ($opt_v) | |
| { | |
| print "dump_command($cmd_str)\n "; | |
| } | |
| my @cmd_chars = extract_command($cmd_str); | |
| my $is_cmd = 1; | |
| my $cmd = $cmd_chars[0]; | |
| if ($cmd eq '$') | |
| { | |
| $is_cmd = 0; # Note that this is a reply | |
| $cmd = $curr_cmd; # set the command byte appropriately | |
| shift @cmd_chars; # remove the '$' from the cmd bytes | |
| } | |
| # Check for common responses across all commands and handle them | |
| # if we can | |
| if ( $is_cmd == 0 ) | |
| { | |
| if (rsp_is_unsupported(@cmd_chars)) | |
| { | |
| print "$unimplemented_str\n"; | |
| return; | |
| } | |
| elsif (rsp_is_OK(@cmd_chars)) | |
| { | |
| print "$success_str\n"; | |
| return; | |
| } | |
| # Strip the checksum information for responses | |
| strip_checksum(\@cmd_chars); | |
| } | |
| my $callback_ref; | |
| if ($is_cmd) { | |
| $callback_ref = $cmd_callbacks{$cmd}; | |
| } else { | |
| $callback_ref = $rsp_callbacks{$cmd}; | |
| } | |
| if ($callback_ref) | |
| { | |
| &$callback_ref(@cmd_chars); | |
| } | |
| else | |
| { | |
| # Strip the command byte for responses since we injected that above | |
| if ($is_cmd) { | |
| dump_other_cmd(@cmd_chars); | |
| } else { | |
| dump_other_rsp(@cmd_chars); | |
| } | |
| } | |
| } | |
| #---------------------------------------------------------------------- | |
| # Process a gdbserver log line by looking for getpkt and putkpt and | |
| # tossing any other lines. | |
| #---------------------------------------------------------------------- | |
| sub process_log_line | |
| { | |
| my $line = shift; | |
| #($opt_v and $opt_g) and print "# $line"; | |
| my $extract_cmd = 0; | |
| my $delta_time = 0.0; | |
| if ($line =~ /^(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$/) | |
| { | |
| my $leading_space = $1; | |
| $curr_time = $2; | |
| $line = $3; | |
| if ($base_time == 0.0) | |
| { | |
| $base_time = $curr_time; | |
| } | |
| else | |
| { | |
| $delta_time = $curr_time - $last_time; | |
| } | |
| printf ("(%.6f, %+.6f): ", $curr_time - $base_time, $delta_time); | |
| $last_time = $curr_time; | |
| } | |
| else | |
| { | |
| $curr_time = 0.0 | |
| } | |
| if ($line =~ /getpkt /) | |
| { | |
| $extract_cmd = 1; | |
| print "\n--> "; | |
| $packet_start_time = $curr_time; | |
| } | |
| elsif ($line =~ /putpkt /) | |
| { | |
| $extract_cmd = 1; | |
| print "<-- "; | |
| } | |
| elsif ($line =~ /.*Sent: \[[0-9]+\.[0-9]+[:0-9]*\] (.*)/) | |
| { | |
| $opt_g and print "maintenance dump-packets command: $1\n"; | |
| my @raw_cmd_bytes = split(/ */, $1); | |
| $packet_start_time = $curr_time; | |
| print "\n--> "; | |
| dump_raw_command(\@raw_cmd_bytes); | |
| process_log_line($2); | |
| } | |
| elsif ($line =~ /.*Recvd: \[[0-9]+\.[0-9]+[:0-9]*\] (.*)/) | |
| { | |
| $opt_g and print "maintenance dump-packets reply: $1\n"; | |
| my @raw_rsp_bytes = split(/ */, $1); | |
| print "<-- "; | |
| dump_raw_response(\@raw_rsp_bytes); | |
| print "\n"; | |
| } | |
| elsif ($line =~ /getpkt: (.*)/) | |
| { | |
| if ($1 =~ /\$([^#]+)#[0-9a-fA-F]{2}/) | |
| { | |
| $opt_g and print "command: $1\n"; | |
| my @raw_cmd_bytes = split(/ */, $1); | |
| print "--> "; | |
| $packet_start_time = $curr_time; | |
| dump_raw_command(\@raw_cmd_bytes); | |
| } | |
| elsif ($1 =~ /\+/) | |
| { | |
| #print "--> ACK\n"; | |
| } | |
| elsif ($1 =~ /-/) | |
| { | |
| #print "--> NACK\n"; | |
| } | |
| } | |
| elsif ($line =~ /putpkt: (.*)/) | |
| { | |
| if ($1 =~ /\$([^#]+)#[0-9a-fA-F]{2}/) | |
| { | |
| $opt_g and print "response: $1\n"; | |
| my @raw_rsp_bytes = split(/ */, $1); | |
| print "<-- "; | |
| dump_raw_response(\@raw_rsp_bytes); | |
| print "\n"; | |
| } | |
| elsif ($1 =~ /\+/) | |
| { | |
| #print "<-- ACK\n"; | |
| } | |
| elsif ($1 =~ /-/) | |
| { | |
| #print "<-- NACK\n"; | |
| } | |
| } | |
| elsif ($line =~ /send packet: (.*)/) | |
| { | |
| if ($1 =~ /\$([^#]+)#[0-9a-fA-F]{2}/) | |
| { | |
| $opt_g and print "command: $1\n"; | |
| my @raw_cmd_bytes = split(/ */, $1); | |
| print "--> "; | |
| $packet_start_time = $curr_time; | |
| dump_raw_command(\@raw_cmd_bytes); | |
| } | |
| elsif ($1 =~ /\+/) | |
| { | |
| #print "--> ACK\n"; | |
| } | |
| elsif ($1 =~ /-/) | |
| { | |
| #print "--> NACK\n"; | |
| } | |
| } | |
| elsif ($line =~ /read packet: (.*)/) | |
| { | |
| if ($1 =~ /\$([^#]*)#[0-9a-fA-F]{2}/) | |
| { | |
| $opt_g and print "response: $1\n"; | |
| my @raw_rsp_bytes = split(/ */, $1); | |
| print "<-- "; | |
| dump_raw_response(\@raw_rsp_bytes); | |
| print "\n"; | |
| } | |
| elsif ($1 =~ /\+/) | |
| { | |
| #print "<-- ACK\n"; | |
| } | |
| elsif ($1 =~ /-/) | |
| { | |
| #print "<-- NACK\n"; | |
| } | |
| } | |
| elsif ($line =~ /Sending packet: \$([^#]+)#[0-9a-fA-F]{2}\.\.\.(.*)/) | |
| { | |
| $opt_g and print "command: $1\n"; | |
| my @raw_cmd_bytes = split(/ */, $1); | |
| print "\n--> "; | |
| $packet_start_time = $curr_time; | |
| dump_raw_command(\@raw_cmd_bytes); | |
| process_log_line($2); | |
| } | |
| elsif ($line =~ /Packet received: (.*)/) | |
| { | |
| $opt_g and print "response: $1\n"; | |
| my @raw_rsp_bytes = split(/ */, $1); | |
| print "<-- "; | |
| dump_raw_response(\@raw_rsp_bytes); | |
| print "\n"; | |
| } | |
| if ($extract_cmd) | |
| { | |
| my $beg = index($line, '("') + 2; | |
| my $end = rindex($line, '");'); | |
| $packet_start_time = $curr_time; | |
| dump_command(substr($line, $beg, $end - $beg)); | |
| } | |
| } | |
| our $line_num = 0; | |
| while(<>) | |
| { | |
| $line_num++; | |
| $opt_q or printf("# %5d: $_", $line_num); | |
| process_log_line($_); | |
| } | |
| if (%packet_times) | |
| { | |
| print "----------------------------------------------------------------------\n"; | |
| print "Packet timing summary:\n"; | |
| print "----------------------------------------------------------------------\n"; | |
| print "Packet Time %\n"; | |
| print "---------------------- -------- ------\n"; | |
| my @packet_names = keys %packet_times; | |
| my $total_packet_times = 0.0; | |
| foreach my $key (@packet_names) | |
| { | |
| $total_packet_times += $packet_times{$key}; | |
| } | |
| foreach my $value (sort {$packet_times{$b} cmp $packet_times{$a}} @packet_names) | |
| { | |
| my $percent = ($packet_times{$value} / $total_packet_times) * 100.0; | |
| if ($percent < 10.0) | |
| { | |
| printf("%22s %1.6f %2.2f\n", $value, $packet_times{$value}, $percent); | |
| } | |
| else | |
| { | |
| printf("%22s %1.6f %2.2f\n", $value, $packet_times{$value}, $percent); | |
| } | |
| } | |
| print "---------------------- -------- ------\n"; | |
| printf (" Total %1.6f 100.00\n", $total_packet_times); | |
| } | |