From 14a9c1caf6f2b080a73b48c5a2b1f9154b57be1f Mon Sep 17 00:00:00 2001 From: Manuel Coenen Date: Mon, 30 Nov 2020 12:57:17 +0100 Subject: Add stackanalyzer to find a fault location from a M122 output and a map file --- Tools/stackanalyzer/go.mod | 3 + Tools/stackanalyzer/linux/stackanalyzer | Bin 0 -> 2339240 bytes Tools/stackanalyzer/macos/stackanalyzer | Bin 0 -> 2344376 bytes Tools/stackanalyzer/stackanalyzer.go | 172 ++++++++++++++++++++++++++++++ Tools/stackanalyzer/win/stackanalyzer.exe | Bin 0 -> 2467840 bytes 5 files changed, 175 insertions(+) create mode 100644 Tools/stackanalyzer/go.mod create mode 100755 Tools/stackanalyzer/linux/stackanalyzer create mode 100755 Tools/stackanalyzer/macos/stackanalyzer create mode 100644 Tools/stackanalyzer/stackanalyzer.go create mode 100755 Tools/stackanalyzer/win/stackanalyzer.exe (limited to 'Tools') diff --git a/Tools/stackanalyzer/go.mod b/Tools/stackanalyzer/go.mod new file mode 100644 index 00000000..735571df --- /dev/null +++ b/Tools/stackanalyzer/go.mod @@ -0,0 +1,3 @@ +module github.com/Duet3D/RepRapFirmware/Tools/stackanalyzer + +go 1.15 diff --git a/Tools/stackanalyzer/linux/stackanalyzer b/Tools/stackanalyzer/linux/stackanalyzer new file mode 100755 index 00000000..bd309c67 Binary files /dev/null and b/Tools/stackanalyzer/linux/stackanalyzer differ diff --git a/Tools/stackanalyzer/macos/stackanalyzer b/Tools/stackanalyzer/macos/stackanalyzer new file mode 100755 index 00000000..d00a2b1f Binary files /dev/null and b/Tools/stackanalyzer/macos/stackanalyzer differ diff --git a/Tools/stackanalyzer/stackanalyzer.go b/Tools/stackanalyzer/stackanalyzer.go new file mode 100644 index 00000000..91581a16 --- /dev/null +++ b/Tools/stackanalyzer/stackanalyzer.go @@ -0,0 +1,172 @@ +package main + +import ( + "bufio" + "errors" + "flag" + "fmt" + "io" + "log" + "os" + "strconv" + "strings" +) + +const ( + assertionHeader = iota + assertionLineNo + assertionFunction +) +const ( + header = iota + register0 + register1 + register2 + register3 + returnControl + returnAddress + faultAddress +) + +func main() { + mapFile := flag.String("mapFile", "", "Path to .map file") + m122 := flag.String("m122", "-", "Path to file with M122 output (even partial) or \"-\" for stdin") + flag.Parse() + + if *m122 == "" { + log.Fatal("-m122 needs to be provided") + } + if *mapFile == "" { + log.Fatal("-mapFile needs to be provided") + } + + var r io.Reader + if *m122 == "-" { + r = bufio.NewReader(os.Stdin) + } else { + f, err := os.Open(*m122) + if err != nil { + log.Fatal(err) + } + defer f.Close() + r = f + } + + var err error + faultedAddress := uint64(0) + scanner := bufio.NewScanner(r) + isAssertion := false + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + + if line == "" { + continue + } + if strings.HasPrefix(line, "Last software reset") { + isAssertion = strings.Contains(line, "reason: Assertion") + continue + } + if strings.HasPrefix(line, "Stack:") { + stackElems := strings.Fields(line) + var faultElem string + if isAssertion { + faultElem = stackElems[assertionFunction] + } else { + faultElem = stackElems[faultAddress] + } + + // Get the fault address + faultedAddress, err = strconv.ParseUint(faultElem, 16, 64) + if err != nil { + log.Fatal(err) + } + + faultLocation, lastAddress, err := getFailingBlock(*mapFile, faultedAddress) + if err != nil { + log.Fatal(err) + } + + fmt.Println("Faulted at:") + for _, l := range faultLocation { + fmt.Println(l) + } + if isAssertion { + lineNo, _ := strconv.ParseUint(stackElems[assertionLineNo], 16, 64) + fmt.Printf("Error is at line: %d\n", lineNo) + } else { + fmt.Printf("Error is at offset: %#x\n", (faultedAddress - lastAddress)) + } + } + } +} + +func getFailingBlock(mapFile string, faultedAddress uint64) ([]string, uint64, error) { + // Open the map file + mf, err := os.Open(mapFile) + if err != nil { + return nil, 0, err + } + defer mf.Close() + mapScanner := bufio.NewScanner(mf) + + var faultLocation []string + var prevFaultFunction string + var faultFunction string + lastAddress := uint64(0) + foundStart := false + addressOfLine := uint64(0) + for mapScanner.Scan() { + mapLine := mapScanner.Text() + + // Advance to the interesting section + if !foundStart { + if !strings.HasPrefix(mapLine, ".text") { + continue + } + foundStart = true + } + fields := strings.Fields(mapLine) + + // Lines with only one field indicate the next function + if len(fields) < 2 { + prevFaultFunction = faultFunction + faultFunction = fields[0] + continue + } + + // Get the address of the current line + var addressField string + for _, f := range fields { + if strings.HasPrefix(f, "0x") { + addressField = f[2:] + break + } + } + + // Still no address + if addressField == "" { + continue + } + + addressOfLine, err = strconv.ParseUint(addressField, 16, 64) + if err != nil { + return nil, 0, errors.New(fmt.Sprintf("Could not parse line: %s", mapLine)) + } + + if addressOfLine <= faultedAddress { + + // Reset the slice of lines if we see a new address + if addressOfLine != lastAddress { + lastAddress = addressOfLine + faultLocation = make([]string, 0, 0) + faultLocation = append(faultLocation, prevFaultFunction) + } + faultLocation = append(faultLocation, mapLine) + } else { + // We reached the function after our fault + break + } + } + + return faultLocation, lastAddress, nil +} diff --git a/Tools/stackanalyzer/win/stackanalyzer.exe b/Tools/stackanalyzer/win/stackanalyzer.exe new file mode 100755 index 00000000..1e9c41af Binary files /dev/null and b/Tools/stackanalyzer/win/stackanalyzer.exe differ -- cgit v1.2.3