Skip to content

Commit afcc935

Browse files
authored
Merge pull request #31 from earlephilhower/use_gdb
Replace addr2line with GDB to get accurate line #s
2 parents 2a89b34 + a78672d commit afcc935

File tree

1 file changed

+199
-41
lines changed

1 file changed

+199
-41
lines changed

src/EspExceptionDecoder.java

Lines changed: 199 additions & 41 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;
335352
}
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";
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;
373+
}
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,31 @@ 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, String stripAfter) {
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+
if (stripAfter != null) {
417+
Pattern after = Pattern.compile(stripAfter);
418+
Matcher afterMatch = after.matcher(content);
419+
if (afterMatch.find()) {
420+
content = content.substring(0, afterMatch.start());
421+
}
422+
}
423+
424+
// Anything looking like an instruction address, dump!
425+
Pattern p = Pattern.compile("40[0-2](\\d|[a-f]|[A-F]){5}\\b");
363426
int count = 0;
364427
Matcher m = p.matcher(content);
365428
while(m.find()) {
@@ -368,24 +431,119 @@ private void parseText(){
368431
if(count == 0){
369432
return;
370433
}
371-
String command[] = new String[4+count];
434+
String command[] = new String[7 + count*2];
372435
int i = 0;
373436
command[i++] = tool.getAbsolutePath();
374-
command[i++] = "-aipfC";
375-
command[i++] = "-e";
437+
command[i++] = "--batch";
376438
command[i++] = elf.getAbsolutePath();
439+
command[i++] = "-ex";
440+
command[i++] = "set listsize 1";
377441
m = p.matcher(content);
378442
while(m.find()) {
379-
command[i++] = content.substring(m.start(), m.end());
443+
command[i++] = "-ex";
444+
command[i++] = "l *0x"+content.substring(m.start(), m.end());
380445
}
381-
outputText += "<i>Decoding "+count+" results</i>\n";
446+
command[i++] = "-ex";
447+
command[i++] = "q";
448+
outputText += "\n<i>Decoding stack results</i>\n";
382449
sysExec(command);
383450
}
384451

452+
// Heavyweight call GDB, run list on address, and return result if it succeeded
453+
private String decodeFunctionAtAddress( String addr ) {
454+
String command[] = new String[9];
455+
command[0] = tool.getAbsolutePath();
456+
command[1] = "--batch";
457+
command[2] = elf.getAbsolutePath();
458+
command[3] = "-ex";
459+
command[4] = "set listsize 1";
460+
command[5] = "-ex";
461+
command[6] = "l *0x" + addr;
462+
command[7] = "-ex";
463+
command[8] = "q";
464+
465+
try {
466+
final Process proc = execRedirected(command);
467+
InputStreamReader reader = new InputStreamReader(proc.getInputStream());
468+
int c;
469+
String line = "";
470+
while ((c = reader.read()) != -1){
471+
if((char)c == '\r')
472+
continue;
473+
if((char)c == '\n' && line != ""){
474+
reader.close();
475+
return prettyPrintGDBLine(line);
476+
} else {
477+
line += (char)c;
478+
}
479+
}
480+
reader.close();
481+
} catch (Exception er) { }
482+
// Something went wrong
483+
return null;
484+
}
485+
486+
// Scan and report the last failed memory allocation attempt, if present on the ESP8266
487+
private void parseAlloc() {
488+
String content = inputArea.getText();
489+
Pattern p = Pattern.compile("last failed alloc call: 40[0-2](\\d|[a-f]|[A-F]){5}\\((\\d)+\\)");
490+
Matcher m = p.matcher(content);
491+
if (m.find()) {
492+
String fs = content.substring(m.start(), m.end());
493+
Pattern p2 = Pattern.compile("40[0-2](\\d|[a-f]|[A-F]){5}\\b");
494+
Matcher m2 = p2.matcher(fs);
495+
if (m2.find()) {
496+
String addr = fs.substring(m2.start(), m2.end());
497+
Pattern p3 = Pattern.compile("\\((\\d)+\\)");
498+
Matcher m3 = p3.matcher(fs);
499+
if (m3.find()) {
500+
String size = fs.substring(m3.start()+1, m3.end()-1);
501+
String line = decodeFunctionAtAddress(addr);
502+
if (line != null) {
503+
outputText += "Memory allocation of " + size + " bytes failed at " + line + "\n";
504+
}
505+
}
506+
}
507+
}
508+
}
509+
510+
// Filter out a register output given a regex (ESP8266/ESP32 differ in format)
511+
private void parseRegister(String regName, String prettyName) {
512+
String content = inputArea.getText();
513+
Pattern p = Pattern.compile(regName + "(\\d|[a-f]|[A-F]){8}\\b");
514+
Matcher m = p.matcher(content);
515+
if (m.find()) {
516+
String fs = content.substring(m.start(), m.end());
517+
Pattern p2 = Pattern.compile("(\\d|[a-f]|[A-F]){8}\\b");
518+
Matcher m2 = p2.matcher(fs);
519+
if (m2.find()) {
520+
String addr = fs.substring(m2.start(), m2.end());
521+
String line = decodeFunctionAtAddress(addr);
522+
if (line != null) {
523+
outputText += prettyName + ": " + line + "\n";
524+
} else {
525+
outputText += prettyName + ": <font color=\"green\">0x" + addr + "</font>\n";
526+
}
527+
}
528+
}
529+
}
530+
385531
private void runParser(){
386532
outputText = "<html><pre>\n";
533+
// Main error cause
387534
parseException();
388-
parseText();
535+
// ESP8266 register format
536+
parseRegister("epc1=0x", "<font color=\"red\">PC</font>");
537+
parseRegister("excvaddr=0x", "<font color=\"red\">EXCVADDR</font>");
538+
// ESP32 register format
539+
parseRegister("PC\\s*:\\s*(0x)?", "<font color=\"red\">PC</font>");
540+
parseRegister("EXCVADDR\\s*:\\s*(0x)?", "<font color=\"red\">EXCVADDR</font>");
541+
// Last memory allocation failure
542+
parseAlloc();
543+
// The stack on ESP8266, multiline
544+
parseStackOrBacktrace(">>>stack>>>(.)*", true, "<<<stack<<<");
545+
// The backtrace on ESP32, one-line only
546+
parseStackOrBacktrace("Backtrace:(.)*", false, null);
389547
}
390548

391549
private class CommitAction extends AbstractAction {

0 commit comments

Comments
 (0)