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
loadDebugInfo(JNIEnv * env,jclass clazz)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
patternMatch(char * classname,const char * pattern)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
searchOneSourceName(int sti,char * p1)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      */
searchAllSourceNames(JNIEnv * env,jclass clazz,char * p1)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
convertLineNumberTable(JNIEnv * env,jclass clazz,jint * entryCountPtr,jvmtiLineNumberEntry ** tablePtr)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
setGlobalStratumId(char * id)284     setGlobalStratumId(char *id) {
285         globalDefaultStratumId = id;
286     }
287 
288 
syntax(String msg)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 
sdePeek(void)301     private char sdePeek(void) {
302         if (*sdePos == 0) {
303             syntax("unexpected EOF");
304         }
305         return *sdePos;
306     }
307 
sdeRead(void)308     private char sdeRead(void) {
309         if (*sdePos == 0) {
310             syntax("unexpected EOF");
311         }
312         return *sdePos++;
313     }
314 
sdeAdvance(void)315     private void sdeAdvance(void) {
316         sdePos++;
317     }
318 
assureLineTableSize(void)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 
assureFileTableSize(void)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 
assureStratumTableSize(void)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 
readLine(void)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 
defaultStratumTableIndex(void)413     private int defaultStratumTableIndex(void) {
414         if ((defaultStratumIndex == -1) && (defaultStratumId != null)) {
415             defaultStratumIndex =
416                 stratumTableIndex(defaultStratumId);
417         }
418         return defaultStratumIndex;
419     }
420 
stratumTableIndex(String stratumId)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 
ignoreWhite(void)465     private void ignoreWhite(void) {
466         char ch;
467 
468         while (((ch = sdePeek()) == ' ') || (ch == '\t')) {
469             sdeAdvance();
470         }
471     }
472 
ignoreLine(void)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 
readNumber(void)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 
storeFile(int fileId,String sourceName,String sourcePath)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 
fileLine(void)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 
storeLine(int jplsStart,int jplsEnd,int jplsLineInc,int njplsStart,int njplsEnd,int fileId)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      */
lineLine(void)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      */
storeStratum(String stratumId)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      */
stratumSection(void)609     private void stratumSection(void) {
610         storeStratum(readLine());
611     }
612 
fileSection(void)613     private void fileSection(void) {
614         ignoreLine();
615         while (sdePeek() != '*') {
616             fileLine();
617         }
618     }
619 
lineSection(void)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      */
ignoreSection(void)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      */
createJavaStratum(void)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      */
decode(void)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 
stiLineTableIndex(int sti,int jplsLine)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 
stiLineNumber(int sti,int lti,int jplsLine)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 
fileTableIndex(int sti,int fileId)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 
stiFileTableIndex(int sti,int lti)731     private int stiFileTableIndex(int sti, int lti) {
732         return fileTableIndex(sti, lineTable[lti].fileId);
733     }
734 
isValid(void)735     private jboolean isValid(void) {
736         return sourceMapIsValid;
737     }
738