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