1 /* 2 * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include <setjmp.h> 27 28 #include "util.h" 29 #include "SDE.h" 30 31 #ifdef __APPLE__ 32 /* use setjmp/longjmp versions that do not save/restore the signal mask */ 33 #define setjmp _setjmp 34 #define longjmp _longjmp 35 #endif 36 37 /** 38 * This SourceDebugExtension code does not 39 * allow concurrent translation - due to caching method. 40 * A separate thread setting the default stratum ID 41 * is, however, fine. 42 */ 43 44 #define INIT_SIZE_FILE 10 45 #define INIT_SIZE_LINE 100 46 #define INIT_SIZE_STRATUM 3 47 48 #define BASE_STRATUM_NAME "Java" 49 50 #define null NULL 51 #define String char * 52 #define private static 53 54 typedef struct { 55 int fileId; 56 String sourceName; 57 String sourcePath; // do not read - use accessor 58 int isConverted; 59 } FileTableRecord; 60 61 typedef struct { 62 int jplsStart; 63 int jplsEnd; 64 int jplsLineInc; 65 int njplsStart; 66 int njplsEnd; 67 int fileId; 68 } LineTableRecord; 69 70 typedef struct { 71 String id; 72 int fileIndex; 73 int lineIndex; 74 } StratumTableRecord; 75 76 /* back-end wide value for default stratum */ 77 private String globalDefaultStratumId = null; 78 79 /* reference type default */ 80 private String defaultStratumId = null; 81 82 private jclass cachedClass = NULL; 83 84 private FileTableRecord* fileTable; 85 private LineTableRecord* lineTable; 86 private StratumTableRecord* stratumTable; 87 88 private int fileTableSize; 89 private int lineTableSize; 90 private int stratumTableSize; 91 92 private int fileIndex; 93 private int lineIndex; 94 private int stratumIndex = 0; 95 private int currentFileId; 96 97 private int defaultStratumIndex; 98 private int baseStratumIndex; 99 private char* sdePos; 100 101 private char* jplsFilename = null; 102 private char* NullString = null; 103 104 /* mangled in parse, cannot be parsed. Must be kept. */ 105 private String sourceDebugExtension; 106 107 private jboolean sourceMapIsValid; 108 109 private jmp_buf jmp_buf_env; 110 111 private int stratumTableIndex(String stratumId); 112 private int stiLineTableIndex(int sti, int jplsLine); 113 private int stiLineNumber(int sti, int lti, int jplsLine); 114 private void decode(void); 115 private void ignoreWhite(void); 116 private jboolean isValid(void); 117 118 private void 119 loadDebugInfo(JNIEnv *env, jclass clazz) { 120 121 if (!isSameObject(env, clazz, cachedClass)) { 122 /* Not the same - swap out the info */ 123 124 /* Delete existing info */ 125 if ( cachedClass != null ) { 126 tossGlobalRef(env, &cachedClass); 127 cachedClass = null; 128 } 129 if ( sourceDebugExtension!=null ) { 130 jvmtiDeallocate(sourceDebugExtension); 131 } 132 sourceDebugExtension = null; 133 134 /* Init info */ 135 lineTable = null; 136 fileTable = null; 137 stratumTable = null; 138 lineTableSize = 0; 139 fileTableSize = 0; 140 stratumTableSize = 0; 141 fileIndex = 0; 142 lineIndex = 0; 143 stratumIndex = 0; 144 currentFileId = 0; 145 defaultStratumId = null; 146 defaultStratumIndex = -1; 147 baseStratumIndex = -2; /* so as not to match -1 above */ 148 sourceMapIsValid = JNI_FALSE; 149 150 if (getSourceDebugExtension(clazz, &sourceDebugExtension) == 151 JVMTI_ERROR_NONE) { 152 sdePos = sourceDebugExtension; 153 if (setjmp(jmp_buf_env) == 0) { 154 /* this is the initial (non-error) case, do parse */ 155 decode(); 156 } 157 } 158 159 cachedClass = null; 160 saveGlobalRef(env, clazz, &cachedClass); 161 } 162 } 163 164 /* Return 1 if match, 0 if no match */ 165 private int 166 patternMatch(char *classname, const char *pattern) { 167 int pattLen; 168 int compLen; 169 char *start; 170 int offset; 171 172 if (pattern == NULL || classname == NULL) { 173 return 0; 174 } 175 pattLen = (int)strlen(pattern); 176 177 if ((pattern[0] != '*') && (pattern[pattLen-1] != '*')) { 178 return strcmp(pattern, classname) == 0; 179 } 180 181 compLen = pattLen - 1; 182 offset = (int)strlen(classname) - compLen; 183 if (offset < 0) { 184 return 0; 185 } 186 if (pattern[0] == '*') { 187 pattern++; 188 start = classname + offset; 189 } else { 190 start = classname; 191 } 192 return strncmp(pattern, start, compLen) == 0; 193 } 194 195 /** 196 * Return 1 if p1 is a SourceName for stratum sti, 197 * else, return 0. 198 */ 199 private int 200 searchOneSourceName(int sti, char *p1) { 201 int fileIndexStart = stratumTable[sti].fileIndex; 202 /* one past end */ 203 int fileIndexEnd = stratumTable[sti+1].fileIndex; 204 int ii; 205 for (ii = fileIndexStart; ii < fileIndexEnd; ++ii) { 206 if (patternMatch(fileTable[ii].sourceName, p1)) { 207 return 1; 208 } 209 } 210 return 0; 211 } 212 213 /** 214 * Return 1 if p1 is a SourceName for any stratum 215 * else, return 0. 216 */ 217 int searchAllSourceNames(JNIEnv *env, 218 jclass clazz, 219 char *p1) { 220 int ii; 221 loadDebugInfo(env, clazz); 222 if (!isValid()) { 223 return 0; /* no SDE or not SourceMap */ 224 } 225 226 for (ii = 0; ii < stratumIndex - 1; ++ii) { 227 if (searchOneSourceName(ii, p1) == 1) { 228 return 1; 229 } 230 } 231 return 0; 232 } 233 234 /** 235 * Convert a line number table, as returned by the JVMTI 236 * function GetLineNumberTable, to one for another stratum. 237 * Conversion is by overwrite. 238 * Actual line numbers are not returned - just a unique 239 * number (file ID in top 16 bits, line number in 240 * bottom 16 bits) - this is all stepping needs. 241 */ 242 void 243 convertLineNumberTable(JNIEnv *env, jclass clazz, 244 jint *entryCountPtr, 245 jvmtiLineNumberEntry **tablePtr) { 246 jvmtiLineNumberEntry *fromEntry = *tablePtr; 247 jvmtiLineNumberEntry *toEntry = *tablePtr; 248 int cnt = *entryCountPtr; 249 int lastLn = 0; 250 int sti; 251 252 loadDebugInfo(env, clazz); 253 if (!isValid()) { 254 return; /* no SDE or not SourceMap - return unchanged */ 255 } 256 sti = stratumTableIndex(globalDefaultStratumId); 257 if (sti == baseStratumIndex) { 258 return; /* Java stratum - return unchanged */ 259 } 260 LOG_MISC(("SDE is re-ordering the line table")); 261 for (; cnt-->0; ++fromEntry) { 262 int jplsLine = fromEntry->line_number; 263 int lti = stiLineTableIndex(sti, jplsLine); 264 if (lti >= 0) { 265 int fileId = lineTable[lti].fileId; 266 int ln = stiLineNumber(sti, lti, jplsLine); 267 ln += (fileId << 16); /* create line hash */ 268 if (ln != lastLn) { 269 lastLn = ln; 270 toEntry->start_location = fromEntry->start_location; 271 toEntry->line_number = ln; 272 ++toEntry; 273 } 274 } 275 } 276 /*LINTED*/ 277 *entryCountPtr = (int)(toEntry - *tablePtr); 278 } 279 280 /** 281 * Set back-end wide default stratum ID . 282 */ 283 void 284 setGlobalStratumId(char *id) { 285 globalDefaultStratumId = id; 286 } 287 288 289 private void syntax(String msg) { 290 char buf[200]; 291 (void)snprintf(buf, sizeof(buf), 292 "bad SourceDebugExtension syntax - position %d - %s\n", 293 /*LINTED*/ 294 (int)(sdePos-sourceDebugExtension), 295 msg); 296 JDI_ASSERT_FAILED(buf); 297 298 longjmp(jmp_buf_env, 1); /* abort parse */ 299 } 300 301 private char sdePeek(void) { 302 if (*sdePos == 0) { 303 syntax("unexpected EOF"); 304 } 305 return *sdePos; 306 } 307 308 private char sdeRead(void) { 309 if (*sdePos == 0) { 310 syntax("unexpected EOF"); 311 } 312 return *sdePos++; 313 } 314 315 private void sdeAdvance(void) { 316 sdePos++; 317 } 318 319 private void assureLineTableSize(void) { 320 if (lineIndex >= lineTableSize) { 321 size_t allocSize; 322 LineTableRecord* new_lineTable; 323 int new_lineTableSize; 324 325 new_lineTableSize = lineTableSize == 0? 326 INIT_SIZE_LINE : 327 lineTableSize * 2; 328 allocSize = new_lineTableSize * (int)sizeof(LineTableRecord); 329 new_lineTable = jvmtiAllocate((jint)allocSize); 330 if ( new_lineTable == NULL ) { 331 EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY, "SDE line table"); 332 } 333 if ( lineTable!=NULL ) { 334 (void)memcpy(new_lineTable, lineTable, 335 lineTableSize * (int)sizeof(LineTableRecord)); 336 jvmtiDeallocate(lineTable); 337 } 338 lineTable = new_lineTable; 339 lineTableSize = new_lineTableSize; 340 } 341 } 342 343 private void assureFileTableSize(void) { 344 if (fileIndex >= fileTableSize) { 345 size_t allocSize; 346 FileTableRecord* new_fileTable; 347 int new_fileTableSize; 348 349 new_fileTableSize = fileTableSize == 0? 350 INIT_SIZE_FILE : 351 fileTableSize * 2; 352 allocSize = new_fileTableSize * (int)sizeof(FileTableRecord); 353 new_fileTable = jvmtiAllocate((jint)allocSize); 354 if ( new_fileTable == NULL ) { 355 EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY, "SDE file table"); 356 } 357 if ( fileTable!=NULL ) { 358 (void)memcpy(new_fileTable, fileTable, 359 fileTableSize * (int)sizeof(FileTableRecord)); 360 jvmtiDeallocate(fileTable); 361 } 362 fileTable = new_fileTable; 363 fileTableSize = new_fileTableSize; 364 } 365 } 366 367 private void assureStratumTableSize(void) { 368 if (stratumIndex >= stratumTableSize) { 369 size_t allocSize; 370 StratumTableRecord* new_stratumTable; 371 int new_stratumTableSize; 372 373 new_stratumTableSize = stratumTableSize == 0? 374 INIT_SIZE_STRATUM : 375 stratumTableSize * 2; 376 allocSize = new_stratumTableSize * (int)sizeof(StratumTableRecord); 377 new_stratumTable = jvmtiAllocate((jint)allocSize); 378 if ( new_stratumTable == NULL ) { 379 EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY, "SDE stratum table"); 380 } 381 if ( stratumTable!=NULL ) { 382 (void)memcpy(new_stratumTable, stratumTable, 383 stratumTableSize * (int)sizeof(StratumTableRecord)); 384 jvmtiDeallocate(stratumTable); 385 } 386 stratumTable = new_stratumTable; 387 stratumTableSize = new_stratumTableSize; 388 } 389 } 390 391 private String readLine(void) { 392 char *initialPos; 393 char ch; 394 395 ignoreWhite(); 396 initialPos = sdePos; 397 while (((ch = *sdePos) != '\n') && (ch != '\r')) { 398 if (ch == 0) { 399 syntax("unexpected EOF"); 400 } 401 ++sdePos; 402 } 403 *sdePos++ = 0; /* null terminate string - mangles SDE */ 404 405 /* check for CR LF */ 406 if ((ch == '\r') && (*sdePos == '\n')) { 407 ++sdePos; 408 } 409 ignoreWhite(); /* leading white */ 410 return initialPos; 411 } 412 413 private int defaultStratumTableIndex(void) { 414 if ((defaultStratumIndex == -1) && (defaultStratumId != null)) { 415 defaultStratumIndex = 416 stratumTableIndex(defaultStratumId); 417 } 418 return defaultStratumIndex; 419 } 420 421 private int stratumTableIndex(String stratumId) { 422 int i; 423 424 if (stratumId == null) { 425 return defaultStratumTableIndex(); 426 } 427 for (i = 0; i < (stratumIndex-1); ++i) { 428 if (strcmp(stratumTable[i].id, stratumId) == 0) { 429 return i; 430 } 431 } 432 return defaultStratumTableIndex(); 433 } 434 435 436 /***************************** 437 * below functions/methods are written to compile under either Java or C 438 * 439 * Needed support functions: 440 * sdePeek() 441 * sdeRead() 442 * sdeAdvance() 443 * readLine() 444 * assureLineTableSize() 445 * assureFileTableSize() 446 * assureStratumTableSize() 447 * syntax(String) 448 * 449 * stratumTableIndex(String) 450 * 451 * Needed support variables: 452 * lineTable 453 * lineIndex 454 * fileTable 455 * fileIndex 456 * currentFileId 457 * 458 * Needed types: 459 * String 460 * 461 * Needed constants: 462 * NullString 463 */ 464 465 private void ignoreWhite(void) { 466 char ch; 467 468 while (((ch = sdePeek()) == ' ') || (ch == '\t')) { 469 sdeAdvance(); 470 } 471 } 472 473 private void ignoreLine(void) { 474 char ch; 475 476 do { 477 ch = sdeRead(); 478 } while ((ch != '\n') && (ch != '\r')); 479 480 /* check for CR LF */ 481 if ((ch == '\r') && (sdePeek() == '\n')) { 482 sdeAdvance(); 483 } 484 ignoreWhite(); /* leading white */ 485 } 486 487 private int readNumber(void) { 488 int value = 0; 489 char ch; 490 491 ignoreWhite(); 492 while (((ch = sdePeek()) >= '0') && (ch <= '9')) { 493 sdeAdvance(); 494 value = (value * 10) + ch - '0'; 495 } 496 ignoreWhite(); 497 return value; 498 } 499 500 private void storeFile(int fileId, String sourceName, String sourcePath) { 501 assureFileTableSize(); 502 fileTable[fileIndex].fileId = fileId; 503 fileTable[fileIndex].sourceName = sourceName; 504 fileTable[fileIndex].sourcePath = sourcePath; 505 ++fileIndex; 506 } 507 508 private void fileLine(void) { 509 int hasAbsolute = 0; /* acts as boolean */ 510 int fileId; 511 String sourceName; 512 String sourcePath = null; 513 514 /* is there an absolute filename? */ 515 if (sdePeek() == '+') { 516 sdeAdvance(); 517 hasAbsolute = 1; 518 } 519 fileId = readNumber(); 520 sourceName = readLine(); 521 if (hasAbsolute == 1) { 522 sourcePath = readLine(); 523 } 524 storeFile(fileId, sourceName, sourcePath); 525 } 526 527 private void storeLine(int jplsStart, int jplsEnd, int jplsLineInc, 528 int njplsStart, int njplsEnd, int fileId) { 529 assureLineTableSize(); 530 lineTable[lineIndex].jplsStart = jplsStart; 531 lineTable[lineIndex].jplsEnd = jplsEnd; 532 lineTable[lineIndex].jplsLineInc = jplsLineInc; 533 lineTable[lineIndex].njplsStart = njplsStart; 534 lineTable[lineIndex].njplsEnd = njplsEnd; 535 lineTable[lineIndex].fileId = fileId; 536 ++lineIndex; 537 } 538 539 /** 540 * Parse line translation info. Syntax is 541 * <NJ-start-line> [ # <file-id> ] [ , <line-count> ] : 542 * <J-start-line> [ , <line-increment> ] CR 543 */ 544 private void lineLine(void) { 545 int lineCount = 1; 546 int lineIncrement = 1; 547 int njplsStart; 548 int jplsStart; 549 550 njplsStart = readNumber(); 551 552 /* is there a fileID? */ 553 if (sdePeek() == '#') { 554 sdeAdvance(); 555 currentFileId = readNumber(); 556 } 557 558 /* is there a line count? */ 559 if (sdePeek() == ',') { 560 sdeAdvance(); 561 lineCount = readNumber(); 562 } 563 564 if (sdeRead() != ':') { 565 syntax("expected ':'"); 566 } 567 jplsStart = readNumber(); 568 if (sdePeek() == ',') { 569 sdeAdvance(); 570 lineIncrement = readNumber(); 571 } 572 ignoreLine(); /* flush the rest */ 573 574 storeLine(jplsStart, 575 jplsStart + (lineCount * lineIncrement) -1, 576 lineIncrement, 577 njplsStart, 578 njplsStart + lineCount -1, 579 currentFileId); 580 } 581 582 /** 583 * Until the next stratum section, everything after this 584 * is in stratumId - so, store the current indicies. 585 */ 586 private void storeStratum(String stratumId) { 587 /* remove redundant strata */ 588 if (stratumIndex > 0) { 589 if ((stratumTable[stratumIndex-1].fileIndex 590 == fileIndex) && 591 (stratumTable[stratumIndex-1].lineIndex 592 == lineIndex)) { 593 /* nothing changed overwrite it */ 594 --stratumIndex; 595 } 596 } 597 /* store the results */ 598 assureStratumTableSize(); 599 stratumTable[stratumIndex].id = stratumId; 600 stratumTable[stratumIndex].fileIndex = fileIndex; 601 stratumTable[stratumIndex].lineIndex = lineIndex; 602 ++stratumIndex; 603 currentFileId = 0; 604 } 605 606 /** 607 * The beginning of a stratum's info 608 */ 609 private void stratumSection(void) { 610 storeStratum(readLine()); 611 } 612 613 private void fileSection(void) { 614 ignoreLine(); 615 while (sdePeek() != '*') { 616 fileLine(); 617 } 618 } 619 620 private void lineSection(void) { 621 ignoreLine(); 622 while (sdePeek() != '*') { 623 lineLine(); 624 } 625 } 626 627 /** 628 * Ignore a section we don't know about. 629 */ 630 private void ignoreSection(void) { 631 ignoreLine(); 632 while (sdePeek() != '*') { 633 ignoreLine(); 634 } 635 } 636 637 /** 638 * A base "Java" stratum is always available, though 639 * it is not in the SourceDebugExtension. 640 * Create the base stratum. 641 */ 642 private void createJavaStratum(void) { 643 baseStratumIndex = stratumIndex; 644 storeStratum(BASE_STRATUM_NAME); 645 storeFile(1, jplsFilename, NullString); 646 /* JPL line numbers cannot exceed 65535 */ 647 storeLine(1, 65536, 1, 1, 65536, 1); 648 storeStratum("Aux"); /* in case they don't declare */ 649 } 650 651 /** 652 * Decode a SourceDebugExtension which is in SourceMap format. 653 * This is the entry point into the recursive descent parser. 654 */ 655 private void decode(void) { 656 /* check for "SMAP" - allow EOF if not ours */ 657 if (strlen(sourceDebugExtension) <= 4 || 658 (sdeRead() != 'S') || 659 (sdeRead() != 'M') || 660 (sdeRead() != 'A') || 661 (sdeRead() != 'P')) { 662 return; /* not our info */ 663 } 664 ignoreLine(); /* flush the rest */ 665 jplsFilename = readLine(); 666 defaultStratumId = readLine(); 667 createJavaStratum(); 668 while (1) { 669 if (sdeRead() != '*') { 670 syntax("expected '*'"); 671 } 672 switch (sdeRead()) { 673 case 'S': 674 stratumSection(); 675 break; 676 case 'F': 677 fileSection(); 678 break; 679 case 'L': 680 lineSection(); 681 break; 682 case 'E': 683 /* set end points */ 684 storeStratum("*terminator*"); 685 sourceMapIsValid = JNI_TRUE; 686 return; 687 default: 688 ignoreSection(); 689 } 690 } 691 } 692 693 /***************** query functions ***********************/ 694 695 private int stiLineTableIndex(int sti, int jplsLine) { 696 int i; 697 int lineIndexStart; 698 int lineIndexEnd; 699 700 lineIndexStart = stratumTable[sti].lineIndex; 701 /* one past end */ 702 lineIndexEnd = stratumTable[sti+1].lineIndex; 703 for (i = lineIndexStart; i < lineIndexEnd; ++i) { 704 if ((jplsLine >= lineTable[i].jplsStart) && 705 (jplsLine <= lineTable[i].jplsEnd)) { 706 return i; 707 } 708 } 709 return -1; 710 } 711 712 private int stiLineNumber(int sti, int lti, int jplsLine) { 713 return lineTable[lti].njplsStart + 714 (((jplsLine - lineTable[lti].jplsStart) / 715 lineTable[lti].jplsLineInc)); 716 } 717 718 private int fileTableIndex(int sti, int fileId) { 719 int i; 720 int fileIndexStart = stratumTable[sti].fileIndex; 721 /* one past end */ 722 int fileIndexEnd = stratumTable[sti+1].fileIndex; 723 for (i = fileIndexStart; i < fileIndexEnd; ++i) { 724 if (fileTable[i].fileId == fileId) { 725 return i; 726 } 727 } 728 return -1; 729 } 730 731 private int stiFileTableIndex(int sti, int lti) { 732 return fileTableIndex(sti, lineTable[lti].fileId); 733 } 734 735 private jboolean isValid(void) { 736 return sourceMapIsValid; 737 }