Skip to content

Commit a84f9d6

Browse files
Replace addr2line with GDB to get accurate line #s
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.
1 parent 8ba299b commit a84f9d6

File tree

1 file changed

+178
-73
lines changed

1 file changed

+178
-73
lines changed

src/EspExceptionDecoder.java

Lines changed: 178 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
//import processing.app.SketchData;
4444
import processing.app.debug.TargetPlatform;
4545
import processing.app.helpers.FileUtils;
46+
import processing.app.helpers.OSUtils;
4647
import processing.app.helpers.ProcessUtils;
4748
import processing.app.tools.Tool;
4849

@@ -96,9 +97,35 @@ public String getMenuTitle() {
9697
return "ESP Exception Decoder";
9798
}
9899

100+
// Original code from processing.app.helpers.ProcessUtils.exec()
101+
// Need custom version to redirect STDERR to STDOUT for GDB processing
102+
public static Process execRedirected(String[] command) throws IOException {
103+
ProcessBuilder pb;
104+
105+
// No problems on linux and mac
106+
if (!OSUtils.isWindows()) {
107+
pb = new ProcessBuilder(command);
108+
} else {
109+
// Brutal hack to workaround windows command line parsing.
110+
// http://stackoverflow.com/questions/5969724/java-runtime-exec-fails-to-escape-characters-properly
111+
// http://msdn.microsoft.com/en-us/library/a1y7w461.aspx
112+
// http://bugs.sun.com/view_bug.do?bug_id=6468220
113+
// http://bugs.sun.com/view_bug.do?bug_id=6518827
114+
String[] cmdLine = new String[command.length];
115+
for (int i = 0; i < command.length; i++)
116+
cmdLine[i] = command[i].replace("\"", "\\\"");
117+
pb = new ProcessBuilder(cmdLine);
118+
Map<String, String> env = pb.environment();
119+
env.put("CYGWIN", "nodosfilewarning");
120+
}
121+
pb.redirectErrorStream(true);
122+
123+
return pb.start();
124+
}
125+
99126
private int listenOnProcess(String[] arguments){
100127
try {
101-
final Process p = ProcessUtils.exec(arguments);
128+
final Process p = execRedirected(arguments);
102129
Thread thread = new Thread() {
103130
public void run() {
104131
try {
@@ -246,16 +273,16 @@ private void createAndUpload(){
246273
gccPath = platform.getFolder() + "/tools/xtensa-"+tc+"-elf";
247274
}
248275

249-
String addr2line;
276+
String gdb;
250277
if(PreferencesData.get("runtime.os").contentEquals("windows"))
251-
addr2line = "xtensa-"+tc+"-elf-addr2line.exe";
278+
gdb = "xtensa-"+tc+"-elf-gdb.exe";
252279
else
253-
addr2line = "xtensa-"+tc+"-elf-addr2line";
280+
gdb = "xtensa-"+tc+"-elf-gdb";
254281

255-
tool = new File(gccPath + "/bin", addr2line);
282+
tool = new File(gccPath + "/bin", gdb);
256283
if (!tool.exists() || !tool.isFile()) {
257284
System.err.println();
258-
editor.statusError("ERROR: "+addr2line+" not found!");
285+
editor.statusError("ERROR: "+gdb+" not found!");
259286
return;
260287
}
261288

@@ -309,35 +336,49 @@ private void createAndUpload(){
309336
frame.setVisible(true);
310337
}
311338

312-
private void printLine(String line){
313-
String address = "", method = "", file = "";
314-
if(line.startsWith("0x")){
315-
address = line.substring(0, line.indexOf(':'));
316-
line = line.substring(line.indexOf(':') + 2);
317-
} else if(line.startsWith("(inlined by)")){
318-
line = line.substring(13);
319-
address = "inlined by";
339+
private String prettyPrintGDBLine(String line) {
340+
String address = "", method = "", file = "", fileline = "", html = "";
341+
342+
if (!line.startsWith("0x")) {
343+
return null;
320344
}
321-
int atIndex = line.indexOf(" at ");
322-
if(atIndex == -1)
323-
return;
324-
method = line.substring(0, atIndex);
325-
line = line.substring(atIndex + 4);
326-
file = line.substring(0, line.lastIndexOf(':'));
327-
if(file.length() > 0){
328-
int lastfs = file.lastIndexOf('/');
329-
int lastbs = file.lastIndexOf('\\');
330-
int slash = (lastfs > lastbs)?lastfs:lastbs;
331-
if(slash != -1){
332-
String filename = file.substring(slash+1);
333-
file = file.substring(0,slash+1) + "<b>" + filename + "</b>";
334-
}
345+
346+
address = line.substring(0, line.indexOf(' '));
347+
line = line.substring(line.indexOf(' ') + 1);
348+
349+
int atIndex = line.indexOf("is in ");
350+
if(atIndex == -1) {
351+
return null;
352+
}
353+
try {
354+
method = line.substring(atIndex + 6, line.lastIndexOf('(') - 1);
355+
fileline = line.substring(line.lastIndexOf('(') + 1, line.lastIndexOf(')'));
356+
file = fileline.substring(0, fileline.lastIndexOf(':'));
357+
line = fileline.substring(fileline.lastIndexOf(':') + 1);
358+
if(file.length() > 0){
359+
int lastfs = file.lastIndexOf('/');
360+
int lastbs = file.lastIndexOf('\\');
361+
int slash = (lastfs > lastbs)?lastfs:lastbs;
362+
if(slash != -1){
363+
String filename = file.substring(slash+1);
364+
file = file.substring(0,slash+1) + "<b>" + filename + "</b>";
365+
}
366+
}
367+
html = "<font color=green>" + address + ": </font>" +
368+
"<b><font color=blue>" + method + "</font></b> at " +
369+
file + " line <b>" + line + "</b>";
370+
} catch (Exception e) {
371+
// Something weird in the GDB output format, report what we can
372+
html = "<font color=green>" + address + ": </font> " + line;
335373
}
336-
line = line.substring(line.lastIndexOf(':') + 1);
337-
String html = "" +
338-
"<font color=green>" + address + ": </font>" +
339-
"<b><font color=blue>" + method + "</font></b> at " + file + " line <b>" + line + "</b>";
340-
outputText += html +"\n";
374+
375+
return html;
376+
}
377+
378+
private void printLine(String line){
379+
String s = prettyPrintGDBLine(line);
380+
if (s != null)
381+
outputText += s +"\n";
341382
}
342383

343384
public void run() {
@@ -357,9 +398,23 @@ private void parseException(){
357398
}
358399
}
359400

360-
private void parseText(){
401+
// Strip out just the STACK lines or BACKTRACE line, and generate the reference log
402+
private void parseStackOrBacktrace(String regexp, boolean multiLine){
361403
String content = inputArea.getText();
362-
Pattern p = Pattern.compile("40[0-2](\\d|[a-f]){5}\\b");
404+
405+
Pattern strip;
406+
if (multiLine) strip = Pattern.compile(regexp, Pattern.DOTALL);
407+
else strip = Pattern.compile(regexp);
408+
Matcher stripMatch = strip.matcher(content);
409+
if (!stripMatch.find()) {
410+
return; // Didn't find it in the text box.
411+
}
412+
413+
// Strip out just the interesting bits to make RexExp sane
414+
content = content.substring(stripMatch.start(), stripMatch.end());
415+
416+
// Anything looking like an instruction address, dump!
417+
Pattern p = Pattern.compile("40[0-2](\\d|[a-f]|[A-F]){5}\\b");
363418
int count = 0;
364419
Matcher m = p.matcher(content);
365420
while(m.find()) {
@@ -368,69 +423,119 @@ private void parseText(){
368423
if(count == 0){
369424
return;
370425
}
371-
String command[] = new String[4+count];
426+
String command[] = new String[7 + count*2];
372427
int i = 0;
373428
command[i++] = tool.getAbsolutePath();
374-
command[i++] = "-aipfC";
375-
command[i++] = "-e";
429+
command[i++] = "--batch";
376430
command[i++] = elf.getAbsolutePath();
431+
command[i++] = "-ex";
432+
command[i++] = "set listsize 1";
377433
m = p.matcher(content);
378434
while(m.find()) {
379-
command[i++] = content.substring(m.start(), m.end());
435+
command[i++] = "-ex";
436+
command[i++] = "l *0x"+content.substring(m.start(), m.end());
380437
}
381-
outputText += "<i>Decoding "+count+" results</i>\n";
438+
command[i++] = "-ex";
439+
command[i++] = "q";
440+
outputText += "\n<i>Decoding stack results</i>\n";
382441
sysExec(command);
383442
}
384443

444+
// Heavyweight call GDB, run list on address, and return result if it succeeded
445+
private String decodeFunctionAtAddress( String addr ) {
446+
String command[] = new String[9];
447+
command[0] = tool.getAbsolutePath();
448+
command[1] = "--batch";
449+
command[2] = elf.getAbsolutePath();
450+
command[3] = "-ex";
451+
command[4] = "set listsize 1";
452+
command[5] = "-ex";
453+
command[6] = "l *0x" + addr;
454+
command[7] = "-ex";
455+
command[8] = "q";
456+
457+
try {
458+
final Process proc = execRedirected(command);
459+
InputStreamReader reader = new InputStreamReader(proc.getInputStream());
460+
int c;
461+
String line = "";
462+
while ((c = reader.read()) != -1){
463+
if((char)c == '\r')
464+
continue;
465+
if((char)c == '\n' && line != ""){
466+
reader.close();
467+
return prettyPrintGDBLine(line);
468+
} else {
469+
line += (char)c;
470+
}
471+
}
472+
reader.close();
473+
} catch (Exception er) { }
474+
// Something went wrong
475+
return null;
476+
}
477+
478+
// Scan and report the last failed memory allocation attempt, if present on the ESP8266
385479
private void parseAlloc() {
386480
String content = inputArea.getText();
387-
Pattern p = Pattern.compile("last failed alloc call: 40[0-2](\\d|[-A-F]){5}\\((\\d)+\\)");
481+
Pattern p = Pattern.compile("last failed alloc call: 40[0-2](\\d|[a-f]|[A-F]){5}\\((\\d)+\\)");
388482
Matcher m = p.matcher(content);
389483
if (m.find()) {
390484
String fs = content.substring(m.start(), m.end());
391-
Pattern p2 = Pattern.compile("40[0-2](\\d|[A-F]){5}\\b");
485+
Pattern p2 = Pattern.compile("40[0-2](\\d|[a-f]|[A-F]){5}\\b");
392486
Matcher m2 = p2.matcher(fs);
393487
if (m2.find()) {
394-
String addr = fs.substring(m2.start(), m2.end());
395-
Pattern p3 = Pattern.compile("\\((\\d)+\\)");
396-
Matcher m3 = p3.matcher(fs);
397-
if (m3.find()) {
398-
String size = fs.substring(m3.start()+1, m3.end()-1);
399-
400-
String command[] = new String[5];
401-
command[0] = tool.getAbsolutePath();
402-
command[1] = "-aipfC";
403-
command[2] = "-e";
404-
command[3] = elf.getAbsolutePath();
405-
command[4] = addr;
406-
407-
try {
408-
final Process proc = ProcessUtils.exec(command);
409-
InputStreamReader reader = new InputStreamReader(proc.getInputStream());
410-
int c;
411-
String line = "";
412-
while ((c = reader.read()) != -1){
413-
if((char)c == '\r')
414-
continue;
415-
if((char)c == '\n' && line != ""){
416-
outputText += "Memory allocation of " + size + " bytes failed at " + line + "\n";
417-
break;
418-
} else {
419-
line += (char)c;
420-
}
421-
}
422-
reader.close();
423-
} catch (Exception er) { }
488+
String addr = fs.substring(m2.start(), m2.end());
489+
Pattern p3 = Pattern.compile("\\((\\d)+\\)");
490+
Matcher m3 = p3.matcher(fs);
491+
if (m3.find()) {
492+
String size = fs.substring(m3.start()+1, m3.end()-1);
493+
String line = decodeFunctionAtAddress(addr);
494+
if (line != null) {
495+
outputText += "Memory allocation of " + size + " bytes failed at " + line + "\n";
424496
}
497+
}
498+
}
499+
}
500+
}
501+
502+
// Filter out a register output given a regex (ESP8266/ESP32 differ in format)
503+
private void parseRegister(String regName, String prettyName) {
504+
String content = inputArea.getText();
505+
Pattern p = Pattern.compile(regName + "(\\d|[a-f]|[A-F]){8}\\b");
506+
Matcher m = p.matcher(content);
507+
if (m.find()) {
508+
String fs = content.substring(m.start(), m.end());
509+
Pattern p2 = Pattern.compile("(\\d|[a-f]|[A-F]){8}\\b");
510+
Matcher m2 = p2.matcher(fs);
511+
if (m2.find()) {
512+
String addr = fs.substring(m2.start(), m2.end());
513+
String line = decodeFunctionAtAddress(addr);
514+
if (line != null) {
515+
outputText += prettyName + ": " + line + "\n";
516+
} else {
517+
outputText += prettyName + ": <font color=\"green\">0x" + addr + "</font>\n";
518+
}
425519
}
426520
}
427521
}
428522

429523
private void runParser(){
430524
outputText = "<html><pre>\n";
525+
// Main error cause
431526
parseException();
527+
// ESP8266 register format
528+
parseRegister("epc1=0x", "<font color=\"red\">PC</font>");
529+
parseRegister("excvaddr=0x", "<font color=\"red\">EXCVADDR</font>");
530+
// ESP32 register format
531+
parseRegister("PC\\s*:\\s*(0x)?", "<font color=\"red\">PC</font>");
532+
parseRegister("EXCVADDR\\s*:\\s*(0x)?", "<font color=\"red\">EXCVADDR</font>");
533+
// Last memory allocation failure
432534
parseAlloc();
433-
parseText();
535+
// The stack on ESP8266, multiline
536+
parseStackOrBacktrace(">>>stack>>>(.)*<<<stack<<<", true);
537+
// The backtrace on ESP32, one-line only
538+
parseStackOrBacktrace("Backtrace:(.)*", false);
434539
}
435540

436541
private class CommitAction extends AbstractAction {

0 commit comments

Comments
 (0)