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 ;
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