• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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