4343//import processing.app.SketchData;
4444import processing .app .debug .TargetPlatform ;
4545import processing .app .helpers .FileUtils ;
46+ import processing .app .helpers .OSUtils ;
4647import processing .app .helpers .ProcessUtils ;
4748import 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