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