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,35 +336,49 @@ private void createAndUpload(){
309
336
frame .setVisible (true );
310
337
}
311
338
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 ;
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 (':' ));
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 ;
335
352
}
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 " ;
341
382
}
342
383
343
384
public void run () {
@@ -357,9 +398,31 @@ private void parseException(){
357
398
}
358
399
}
359
400
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 ) {
361
403
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" );
363
426
int count = 0 ;
364
427
Matcher m = p .matcher (content );
365
428
while (m .find ()) {
@@ -368,24 +431,119 @@ private void parseText(){
368
431
if (count == 0 ){
369
432
return ;
370
433
}
371
- String command [] = new String [4 + count ];
434
+ String command [] = new String [7 + count * 2 ];
372
435
int i = 0 ;
373
436
command [i ++] = tool .getAbsolutePath ();
374
- command [i ++] = "-aipfC" ;
375
- command [i ++] = "-e" ;
437
+ command [i ++] = "--batch" ;
376
438
command [i ++] = elf .getAbsolutePath ();
439
+ command [i ++] = "-ex" ;
440
+ command [i ++] = "set listsize 1" ;
377
441
m = p .matcher (content );
378
442
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 ());
380
445
}
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 " ;
382
449
sysExec (command );
383
450
}
384
451
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
+
385
531
private void runParser (){
386
532
outputText = "<html><pre>\n " ;
533
+ // Main error cause
387
534
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 );
389
547
}
390
548
391
549
private class CommitAction extends AbstractAction {
0 commit comments