43
43
//import processing.app.SketchData;
44
44
import processing .app .debug .TargetPlatform ;
45
45
import processing .app .helpers .FileUtils ;
46
+ import processing .app .helpers .OSUtils ;
46
47
import processing .app .helpers .ProcessUtils ;
47
48
import processing .app .tools .Tool ;
48
49
@@ -96,9 +97,35 @@ public String getMenuTitle() {
96
97
return "ESP Exception Decoder" ;
97
98
}
98
99
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
+
99
126
private int listenOnProcess (String [] arguments ){
100
127
try {
101
- final Process p = ProcessUtils . exec (arguments );
128
+ final Process p = execRedirected (arguments );
102
129
Thread thread = new Thread () {
103
130
public void run () {
104
131
try {
@@ -246,16 +273,16 @@ private void createAndUpload(){
246
273
gccPath = platform .getFolder () + "/tools/xtensa-" +tc +"-elf" ;
247
274
}
248
275
249
- String addr2line ;
276
+ String gdb ;
250
277
if (PreferencesData .get ("runtime.os" ).contentEquals ("windows" ))
251
- addr2line = "xtensa-" +tc +"-elf-addr2line .exe" ;
278
+ gdb = "xtensa-" +tc +"-elf-gdb .exe" ;
252
279
else
253
- addr2line = "xtensa-" +tc +"-elf-addr2line " ;
280
+ gdb = "xtensa-" +tc +"-elf-gdb " ;
254
281
255
- tool = new File (gccPath + "/bin" , addr2line );
282
+ tool = new File (gccPath + "/bin" , gdb );
256
283
if (!tool .exists () || !tool .isFile ()) {
257
284
System .err .println ();
258
- editor .statusError ("ERROR: " +addr2line +" not found!" );
285
+ editor .statusError ("ERROR: " +gdb +" not found!" );
259
286
return ;
260
287
}
261
288
@@ -309,21 +336,25 @@ private void createAndUpload(){
309
336
frame .setVisible (true );
310
337
}
311
338
312
- private void printLine (String line ){
339
+ private String prettyPrintGDBLine (String line ) {
313
340
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 ;
320
344
}
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 (':' ));
327
358
if (file .length () > 0 ){
328
359
int lastfs = file .lastIndexOf ('/' );
329
360
int lastbs = file .lastIndexOf ('\\' );
@@ -337,7 +368,13 @@ private void printLine(String line){
337
368
String html = "" +
338
369
"<font color=green>" + address + ": </font>" +
339
370
"<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 " ;
341
378
}
342
379
343
380
public void run () {
@@ -357,9 +394,23 @@ private void parseException(){
357
394
}
358
395
}
359
396
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 ){
361
399
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" );
363
414
int count = 0 ;
364
415
Matcher m = p .matcher (content );
365
416
while (m .find ()) {
@@ -368,69 +419,119 @@ private void parseText(){
368
419
if (count == 0 ){
369
420
return ;
370
421
}
371
- String command [] = new String [4 + count ];
422
+ String command [] = new String [7 + count * 2 ];
372
423
int i = 0 ;
373
424
command [i ++] = tool .getAbsolutePath ();
374
- command [i ++] = "-aipfC" ;
375
- command [i ++] = "-e" ;
425
+ command [i ++] = "--batch" ;
376
426
command [i ++] = elf .getAbsolutePath ();
427
+ command [i ++] = "-ex" ;
428
+ command [i ++] = "set listsize 1" ;
377
429
m = p .matcher (content );
378
430
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 ());
380
433
}
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 " ;
382
437
sysExec (command );
383
438
}
384
439
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
385
475
private void parseAlloc () {
386
476
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)+\\ )" );
388
478
Matcher m = p .matcher (content );
389
479
if (m .find ()) {
390
480
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" );
392
482
Matcher m2 = p2 .matcher (fs );
393
483
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 " ;
424
492
}
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
+ }
425
515
}
426
516
}
427
517
}
428
518
429
519
private void runParser (){
430
520
outputText = "<html><pre>\n " ;
521
+ // Main error cause
431
522
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
432
530
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 );
434
535
}
435
536
436
537
private class CommitAction extends AbstractAction {
0 commit comments