Skip to content

Commit

Permalink
Replace addr2line with GDB to get accurate line #s
Browse files Browse the repository at this point in the history
Addr2line on the xtensa seems to have trouble with identifying the
proper file:line number for an exception address.  Until there is a
fix, the workaround is to use GDB to locate them.

See jcmvbkbc/binutils-gdb-xtensa#5 .

Replace the addr2line processing/formatting with gdb's formatting,
leaving the output identical with one exception:  addresses with
no source code (i.e. not code, but constant data somewhere or just
random variables on the stack) *will not print*.  They're silently
ignored in the output.

This also now presents the EPC1/PC and EXCVADDR on their own
lines hilighted at the top of the dump.
  • Loading branch information
earlephilhower committed Jan 24, 2018
1 parent 8ba299b commit 8a7d6ff
Showing 1 changed file with 161 additions and 60 deletions.
221 changes: 161 additions & 60 deletions src/EspExceptionDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
//import processing.app.SketchData;
import processing.app.debug.TargetPlatform;
import processing.app.helpers.FileUtils;
import processing.app.helpers.OSUtils;
import processing.app.helpers.ProcessUtils;
import processing.app.tools.Tool;

Expand Down Expand Up @@ -96,9 +97,35 @@ public String getMenuTitle() {
return "ESP Exception Decoder";
}

// Original code from processing.app.helpers.ProcessUtils.exec()
// Need custom version to redirect STDERR to STDOUT for GDB processing
public static Process execRedirected(String[] command) throws IOException {
ProcessBuilder pb;

// No problems on linux and mac
if (!OSUtils.isWindows()) {
pb = new ProcessBuilder(command);
} else {
// Brutal hack to workaround windows command line parsing.
// http://stackoverflow.com/questions/5969724/java-runtime-exec-fails-to-escape-characters-properly
// http://msdn.microsoft.com/en-us/library/a1y7w461.aspx
// http://bugs.sun.com/view_bug.do?bug_id=6468220
// http://bugs.sun.com/view_bug.do?bug_id=6518827
String[] cmdLine = new String[command.length];
for (int i = 0; i < command.length; i++)
cmdLine[i] = command[i].replace("\"", "\\\"");
pb = new ProcessBuilder(cmdLine);
Map<String, String> env = pb.environment();
env.put("CYGWIN", "nodosfilewarning");
}
pb.redirectErrorStream(true);

return pb.start();
}

private int listenOnProcess(String[] arguments){
try {
final Process p = ProcessUtils.exec(arguments);
final Process p = execRedirected(arguments);
Thread thread = new Thread() {
public void run() {
try {
Expand Down Expand Up @@ -246,16 +273,16 @@ private void createAndUpload(){
gccPath = platform.getFolder() + "/tools/xtensa-"+tc+"-elf";
}

String addr2line;
String gdb;
if(PreferencesData.get("runtime.os").contentEquals("windows"))
addr2line = "xtensa-"+tc+"-elf-addr2line.exe";
gdb = "xtensa-"+tc+"-elf-gdb.exe";
else
addr2line = "xtensa-"+tc+"-elf-addr2line";
gdb = "xtensa-"+tc+"-elf-gdb";

tool = new File(gccPath + "/bin", addr2line);
tool = new File(gccPath + "/bin", gdb);
if (!tool.exists() || !tool.isFile()) {
System.err.println();
editor.statusError("ERROR: "+addr2line+" not found!");
editor.statusError("ERROR: "+gdb+" not found!");
return;
}

Expand Down Expand Up @@ -309,21 +336,25 @@ private void createAndUpload(){
frame.setVisible(true);
}

private void printLine(String line){
private String prettyPrintGDBLine(String line) {
String address = "", method = "", file = "";
if(line.startsWith("0x")){
address = line.substring(0, line.indexOf(':'));
line = line.substring(line.indexOf(':') + 2);
} else if(line.startsWith("(inlined by)")){
line = line.substring(13);
address = "inlined by";

if (!line.startsWith("0x")) {
return null;
}
int atIndex = line.indexOf(" at ");
if(atIndex == -1)
return;
method = line.substring(0, atIndex);
line = line.substring(atIndex + 4);
file = line.substring(0, line.lastIndexOf(':'));

address = line.substring(0, line.indexOf(' '));
line = line.substring(line.indexOf(' ') + 1);

int atIndex = line.indexOf("is in ");
if(atIndex == -1) {
return null;
}

method = line.substring(atIndex + 6, line.lastIndexOf('(') - 1);
String fileline = line.substring(line.lastIndexOf('(') + 1, line.lastIndexOf(')')-1);
file = fileline.substring(0, fileline.lastIndexOf(':'));
line = fileline.substring(fileline.lastIndexOf(':'));
if(file.length() > 0){
int lastfs = file.lastIndexOf('/');
int lastbs = file.lastIndexOf('\\');
Expand All @@ -337,7 +368,13 @@ private void printLine(String line){
String html = "" +
"<font color=green>" + address + ": </font>" +
"<b><font color=blue>" + method + "</font></b> at " + file + " line <b>" + line + "</b>";
outputText += html +"\n";
return html;
}

private void printLine(String line){
String s = prettyPrintGDBLine(line);
if (s != null)
outputText += s +"\n";
}

public void run() {
Expand All @@ -357,9 +394,23 @@ private void parseException(){
}
}

private void parseText(){
// Strip out just the STACK lines or BACKTRACE line, and generate the reference log
private void parseStackOrBacktrace(String regexp, boolean multiLine){
String content = inputArea.getText();
Pattern p = Pattern.compile("40[0-2](\\d|[a-f]){5}\\b");

Pattern strip;
if (multiLine) strip = Pattern.compile(regexp, Pattern.DOTALL);
else strip = Pattern.compile(regexp);
Matcher stripMatch = strip.matcher(content);
if (!stripMatch.find()) {
return; // Didn't find it in the text box.
}

// Strip out just the interesting bits to make RexExp sane
content = content.substring(stripMatch.start(), stripMatch.end());

// Anything looking like an instruction address, dump!
Pattern p = Pattern.compile("40[0-2](\\d|[a-f]|[A-F]){5}\\b");
int count = 0;
Matcher m = p.matcher(content);
while(m.find()) {
Expand All @@ -368,69 +419,119 @@ private void parseText(){
if(count == 0){
return;
}
String command[] = new String[4+count];
String command[] = new String[7 + count*2];
int i = 0;
command[i++] = tool.getAbsolutePath();
command[i++] = "-aipfC";
command[i++] = "-e";
command[i++] = "--batch";
command[i++] = elf.getAbsolutePath();
command[i++] = "-ex";
command[i++] = "set listsize 1";
m = p.matcher(content);
while(m.find()) {
command[i++] = content.substring(m.start(), m.end());
command[i++] = "-ex";
command[i++] = "l *0x"+content.substring(m.start(), m.end());
}
outputText += "<i>Decoding "+count+" results</i>\n";
command[i++] = "-ex";
command[i++] = "q";
outputText += "\n<i>Decoding stack results</i>\n";
sysExec(command);
}

// Heavyweight call GDB, run list on address, and return result if it succeeded
private String decodeFunctionAtAddress( String addr ) {
String command[] = new String[9];
command[0] = tool.getAbsolutePath();
command[1] = "--batch";
command[2] = elf.getAbsolutePath();
command[3] = "-ex";
command[4] = "set listsize 1";
command[5] = "-ex";
command[6] = "l *0x" + addr;
command[7] = "-ex";
command[8] = "q";

try {
final Process proc = execRedirected(command);
InputStreamReader reader = new InputStreamReader(proc.getInputStream());
int c;
String line = "";
while ((c = reader.read()) != -1){
if((char)c == '\r')
continue;
if((char)c == '\n' && line != ""){
reader.close();
return prettyPrintGDBLine(line);
} else {
line += (char)c;
}
}
reader.close();
} catch (Exception er) { }
// Something went wrong
return null;
}

// Scan and report the last failed memory allocation attempt, if present on the ESP8266
private void parseAlloc() {
String content = inputArea.getText();
Pattern p = Pattern.compile("last failed alloc call: 40[0-2](\\d|[-A-F]){5}\\((\\d)+\\)");
Pattern p = Pattern.compile("last failed alloc call: 40[0-2](\\d|[a-f]|[A-F]){5}\\((\\d)+\\)");
Matcher m = p.matcher(content);
if (m.find()) {
String fs = content.substring(m.start(), m.end());
Pattern p2 = Pattern.compile("40[0-2](\\d|[A-F]){5}\\b");
Pattern p2 = Pattern.compile("40[0-2](\\d|[a-f]|[A-F]){5}\\b");
Matcher m2 = p2.matcher(fs);
if (m2.find()) {
String addr = fs.substring(m2.start(), m2.end());
Pattern p3 = Pattern.compile("\\((\\d)+\\)");
Matcher m3 = p3.matcher(fs);
if (m3.find()) {
String size = fs.substring(m3.start()+1, m3.end()-1);

String command[] = new String[5];
command[0] = tool.getAbsolutePath();
command[1] = "-aipfC";
command[2] = "-e";
command[3] = elf.getAbsolutePath();
command[4] = addr;

try {
final Process proc = ProcessUtils.exec(command);
InputStreamReader reader = new InputStreamReader(proc.getInputStream());
int c;
String line = "";
while ((c = reader.read()) != -1){
if((char)c == '\r')
continue;
if((char)c == '\n' && line != ""){
outputText += "Memory allocation of " + size + " bytes failed at " + line + "\n";
break;
} else {
line += (char)c;
}
}
reader.close();
} catch (Exception er) { }
String addr = fs.substring(m2.start(), m2.end());
Pattern p3 = Pattern.compile("\\((\\d)+\\)");
Matcher m3 = p3.matcher(fs);
if (m3.find()) {
String size = fs.substring(m3.start()+1, m3.end()-1);
String line = decodeFunctionAtAddress(addr);
if (line != null) {
outputText += "Memory allocation of " + size + " bytes failed at " + line + "\n";
}
}
}
}
}

// Filter out a register output given a regex (ESP8266/ESP32 differ in format)
private void parseRegister(String regName, String prettyName) {
String content = inputArea.getText();
Pattern p = Pattern.compile(regName + "(\\d|[a-f]|[A-F]){8}\\b");
Matcher m = p.matcher(content);
if (m.find()) {
String fs = content.substring(m.start(), m.end());
Pattern p2 = Pattern.compile("(\\d|[a-f]|[A-F]){8}\\b");
Matcher m2 = p2.matcher(fs);
if (m2.find()) {
String addr = fs.substring(m2.start(), m2.end());
String line = decodeFunctionAtAddress(addr);
if (line != null) {
outputText += prettyName + ": " + line + "\n";
} else {
outputText += prettyName + ": <font color=\"green\">0x" + addr + "</font>\n";
}
}
}
}

private void runParser(){
outputText = "<html><pre>\n";
// Main error cause
parseException();
// ESP8266 register format
parseRegister("epc1=0x", "<font color=\"red\">PC</font>");
parseRegister("excvaddr=0x", "<font color=\"red\">EXCVADDR</font>");
// ESP32 register format
parseRegister("PC\\s*:\\s*(0x)?", "<font color=\"red\">PC</font>");
parseRegister("EXCVADDR\\s*:\\s*(0x)?", "<font color=\"red\">EXCVADDR</font>");
// Last memory allocation failure
parseAlloc();
parseText();
// The stack on ESP8266, multiline
parseStackOrBacktrace(">>>stack>>>(.)*<<<stack<<<", true);
// The backtrace on ESP32, one-line only
parseStackOrBacktrace("Backtrace:(.)*", false);
}

private class CommitAction extends AbstractAction {
Expand Down

0 comments on commit 8a7d6ff

Please sign in to comment.