#!/usr/bin/tclsh # # Takes a PC trace from simulation and annotates it with function/line number information # # The PC trace is assumed to have one PC per line; the first likely-looking number on each line is assumed to be the PC. # # usage: pctrace-decoder # package require log package require fileutil ################################################################################ # utilities proc fatal {msg} { global errorInfo ::log::log error $msg # if {$errorInfo != ""} { # ::log::log debug $errorInfo # } exit 1 } ################################################################################ proc generateBatch {pctrace batchfile} { ::log::log debug "parsing trace $pctrace" ::fileutil::foreachLine inputLine $pctrace { foreach tok $inputLine { switch -glob $tok { [0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] { set addresses($tok) "" break } 0x[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] { # hex number set addresses([string range $tok 2 end]) "" break } default { continue } } } } if {![array exists addresses]} { fatal "no recognisable data in '$pctrace' (maybe need a smarter parser?)" } set batch [open $batchfile w] ::log::log debug "generating batchfile $batchfile" foreach address [array names addresses] { puts $batch [format "echo &%s&" $address] puts $batch [format "info line *0x%s" $address] } close $batch } ################################################################################ proc runBatch {symfile batchfile batchresult} { ::log::log debug "generating symbol data $batchresult" if {[catch {exec gdb -n -batch -x $batchfile $symfile > $batchresult} result]} { fatal "gdb failed: $result" } } ################################################################################ proc annotateTrace {pctrace batchresult traceoutput condensed} { ::log::log debug "reading batch results" ::fileutil::foreachLine inputLine $batchresult { if {[scan $inputLine "&%\[^&\]&Line %d of \"%\[^\"\]\" starts at address %x <%\[^>\]> and ends at %x <%\[^>\]>" key lineNumber fileName startAddr startSym endAddr endSym] == 7} { if {[scan $startSym "%\[^+\]+%x" sym addr] == 2} { set startSym $sym } if {[scan $endSym "%\[^+\]+%x" sym addr] == 2} { set endSym $sym } if {$startSym == $endSym} { set addrinfo($key) [format "%s:%s:%d" $fileName $startSym $lineNumber] } else { set addrinfo($key) [format "%s:%s/%s:%d" $fileName $startSym $endSym $lineNumber] } continue } if {[scan $inputLine "&%\[^&\]&No line number information available for address %x <%\[^>\]>" key address addrSym] == 3} { if {[scan $addrSym "%\[^+\]+%x" sym addr] == 2} { set addrSym $sym } set addrinfo($key) [format "%s" $addrSym] continue } log::log warning "unexpected batch result '$inputLine'" } set out [open $traceoutput w] set cout [open $condensed w] ::log::log debug "annotating trace" set lastSuffix "" ::fileutil::foreachLine inputLine $pctrace { set address "" foreach tok $inputLine { switch -glob $tok { [0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] { set address $tok break } 0x[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] { # hex number set address [string range $tok 2 end] break } default { continue } } } if {($address != "") && ([array names addrinfo -exact $address] != "")} { set suffix $addrinfo($address) if {$suffix != $lastSuffix} { puts $cout $suffix set lastSuffix $suffix } } else { set suffix "" } puts $out [format "%s -- %s" $inputLine $suffix] } close $out close $cout } ################################################################################ proc main {} { global argv # logging ::log::lvSuppress error 0 ::log::lvSuppress warning 0 ::log::lvSuppress notice 0 ::log::lvSuppress debug 1 set pctrace [lindex $argv 0] set symfile [lindex $argv 1] if {![file readable $pctrace]} { fatal "missing or unreadable PC trace file '$pctrace'" } if {![file readable $symfile]} { fatal "missing or unreadable symbol file '$symfile'" } # generate a gdb batch file ::log::log notice "generating gdb batch from PC trace..." set batchfile [::fileutil::tempfile ptrace_decoder_batch_] generateBatch $pctrace $batchfile # invoke gdb against the batch file ::log::log notice "processing batch..." set batchresult [::fileutil::tempfile ptrace_decoder_result_] runBatch $symfile $batchfile $batchresult file delete $batchfile # annotate the pctrace with results from the result file ::log::log notice "annotating trace..." set traceoutput [format "%s.annotated" $pctrace] set condensed [format "%s.condensed" $pctrace] annotateTrace $pctrace $batchresult $traceoutput $condensed file delete $batchresult ::log::log notice "Annotated output in '$traceoutput', condensed output in '$condensed'" } main