Skip to content

Commit 8a7d6ff

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 8a7d6ff

File tree

1 file changed

+161
-60
lines changed

1 file changed

+161
-60
lines changed

src/EspExceptionDecoder.java

Lines changed: 161 additions & 60 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,21 +336,25 @@ private void createAndUpload(){
309336
frame.setVisible(true);
310337
}
311338

312-
private void printLine(String line){
339+
private String prettyPrintGDBLine(String line) {
313340
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";
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(':'));
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+
354+
method = line.substring(atIndex + 6, line.lastIndexOf('(') - 1);
355+
String fileline = line.substring(line.lastIndexOf('(') + 1, line.lastIndexOf(')')-1);
356+
file = fileline.substring(0, fileline.lastIndexOf(':'));
357+
line = fileline.substring(fileline.lastIndexOf(':'));
327358
if(file.length() > 0){
328359
int lastfs = file.lastIndexOf('/');
329360
int lastbs = file.lastIndexOf('\\');
@@ -337,7 +368,13 @@ private void printLine(String line){
337368
String html = "" +
338369
"<font color=green>" + address + ": </font>" +
339370
"<b><font color=blue>" + method + "</font></b> at " + file + " line <b>" + line + "</b>";
340-
outputText += html +"\n";
371+
return html;
372+
}
373+
374+
private void printLine(String line){
375+
String s = prettyPrintGDBLine(line);
376+
if (s != null)
377+
outputText += s +"\n";
341378
}
342379

343380
public void run() {
@@ -357,9 +394,23 @@ private void parseException(){
357394
}
358395
}
359396

360-
private void parseText(){
397+
// Strip out just the STACK lines or BACKTRACE line, and generate the reference log
398+
private void parseStackOrBacktrace(String regexp, boolean multiLine){
361399
String content = inputArea.getText();
362-
Pattern p = Pattern.compile("40[0-2](\\d|[a-f]){5}\\b");
400+
401+
Pattern strip;
402+
if (multiLine) strip = Pattern.compile(regexp, Pattern.DOTALL);
403+
else strip = Pattern.compile(regexp);
404+
Matcher stripMatch = strip.matcher(content);
405+
if (!stripMatch.find()) {
406+
return; // Didn't find it in the text box.
407+
}
408+
409+
// Strip out just the interesting bits to make RexExp sane
410+
content = content.substring(stripMatch.start(), stripMatch.end());
411+
412+
// Anything looking like an instruction address, dump!
413+
Pattern p = Pattern.compile("40[0-2](\\d|[a-f]|[A-F]){5}\\b");
363414
int count = 0;
364415
Matcher m = p.matcher(content);
365416
while(m.find()) {
@@ -368,69 +419,119 @@ private void parseText(){
368419
if(count == 0){
369420
return;
370421
}
371-
String command[] = new String[4+count];
422+
String command[] = new String[7 + count*2];
372423
int i = 0;
373424
command[i++] = tool.getAbsolutePath();
374-
command[i++] = "-aipfC";
375-
command[i++] = "-e";
425+
command[i++] = "--batch";
376426
command[i++] = elf.getAbsolutePath();
427+
command[i++] = "-ex";
428+
command[i++] = "set listsize 1";
377429
m = p.matcher(content);
378430
while(m.find()) {
379-
command[i++] = content.substring(m.start(), m.end());
431+
command[i++] = "-ex";
432+
command[i++] = "l *0x"+content.substring(m.start(), m.end());
380433
}
381-
outputText += "<i>Decoding "+count+" results</i>\n";
434+
command[i++] = "-ex";
435+
command[i++] = "q";
436+
outputText += "\n<i>Decoding stack results</i>\n";
382437
sysExec(command);
383438
}
384439

440+
// Heavyweight call GDB, run list on address, and return result if it succeeded
441+
private String decodeFunctionAtAddress( String addr ) {
442+
String command[] = new String[9];
443+
command[0] = tool.getAbsolutePath();
444+
command[1] = "--batch";
445+
command[2] = elf.getAbsolutePath();
446+
command[3] = "-ex";
447+
command[4] = "set listsize 1";
448+
command[5] = "-ex";
449+
command[6] = "l *0x" + addr;
450+
command[7] = "-ex";
451+
command[8] = "q";
452+
453+
try {
454+
final Process proc = execRedirected(command);
455+
InputStreamReader reader = new InputStreamReader(proc.getInputStream());
456+
int c;
457+
String line = "";
458+
while ((c = reader.read()) != -1){
459+
if((char)c == '\r')
460+
continue;
461+
if((char)c == '\n' && line != ""){
462+
reader.close();
463+
return prettyPrintGDBLine(line);
464+
} else {
465+
line += (char)c;
466+
}
467+
}
468+
reader.close();
469+
} catch (Exception er) { }
470+
// Something went wrong
471+
return null;
472+
}
473+
474+
// Scan and report the last failed memory allocation attempt, if present on the ESP8266
385475
private void parseAlloc() {
386476
String content = inputArea.getText();
387-
Pattern p = Pattern.compile("last failed alloc call: 40[0-2](\\d|[-A-F]){5}\\((\\d)+\\)");
477+
Pattern p = Pattern.compile("last failed alloc call: 40[0-2](\\d|[a-f]|[A-F]){5}\\((\\d)+\\)");
388478
Matcher m = p.matcher(content);
389479
if (m.find()) {
390480
String fs = content.substring(m.start(), m.end());
391-
Pattern p2 = Pattern.compile("40[0-2](\\d|[A-F]){5}\\b");
481+
Pattern p2 = Pattern.compile("40[0-2](\\d|[a-f]|[A-F]){5}\\b");
392482
Matcher m2 = p2.matcher(fs);
393483
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) { }
484+
String addr = fs.substring(m2.start(), m2.end());
485+
Pattern p3 = Pattern.compile("\\((\\d)+\\)");
486+
Matcher m3 = p3.matcher(fs);
487+
if (m3.find()) {
488+
String size = fs.substring(m3.start()+1, m3.end()-1);
489+
String line = decodeFunctionAtAddress(addr);
490+
if (line != null) {
491+
outputText += "Memory allocation of " + size + " bytes failed at " + line + "\n";
424492
}
493+
}
494+
}
495+
}
496+
}
497+
498+
// Filter out a register output given a regex (ESP8266/ESP32 differ in format)
499+
private void parseRegister(String regName, String prettyName) {
500+
String content = inputArea.getText();
501+
Pattern p = Pattern.compile(regName + "(\\d|[a-f]|[A-F]){8}\\b");
502+
Matcher m = p.matcher(content);
503+
if (m.find()) {
504+
String fs = content.substring(m.start(), m.end());
505+
Pattern p2 = Pattern.compile("(\\d|[a-f]|[A-F]){8}\\b");
506+
Matcher m2 = p2.matcher(fs);
507+
if (m2.find()) {
508+
String addr = fs.substring(m2.start(), m2.end());
509+
String line = decodeFunctionAtAddress(addr);
510+
if (line != null) {
511+
outputText += prettyName + ": " + line + "\n";
512+
} else {
513+
outputText += prettyName + ": <font color=\"green\">0x" + addr + "</font>\n";
514+
}
425515
}
426516
}
427517
}
428518

429519
private void runParser(){
430520
outputText = "<html><pre>\n";
521+
// Main error cause
431522
parseException();
523+
// ESP8266 register format
524+
parseRegister("epc1=0x", "<font color=\"red\">PC</font>");
525+
parseRegister("excvaddr=0x", "<font color=\"red\">EXCVADDR</font>");
526+
// ESP32 register format
527+
parseRegister("PC\\s*:\\s*(0x)?", "<font color=\"red\">PC</font>");
528+
parseRegister("EXCVADDR\\s*:\\s*(0x)?", "<font color=\"red\">EXCVADDR</font>");
529+
// Last memory allocation failure
432530
parseAlloc();
433-
parseText();
531+
// The stack on ESP8266, multiline
532+
parseStackOrBacktrace(">>>stack>>>(.)*<<<stack<<<", true);
533+
// The backtrace on ESP32, one-line only
534+
parseStackOrBacktrace("Backtrace:(.)*", false);
434535
}
435536

436537
private class CommitAction extends AbstractAction {

0 commit comments

Comments
 (0)