1 /*
2  * Copyright (c) 2004, 2014, 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 package sun.jvmstat.perfdata.monitor.v1_0;
27 
28 import sun.jvmstat.monitor.*;
29 import sun.jvmstat.perfdata.monitor.*;
30 import java.util.*;
31 import java.util.regex.*;
32 import java.nio.*;
33 
34 /**
35  * The concrete implementation of version 1.0 of the HotSpot PerfData
36  * Instrumentation buffer. This class is responsible for parsing the
37  * instrumentation memory and constructing the necessary objects to
38  * represent and access the instrumentation objects contained in the
39  * memory buffer.
40  *
41  * @author Brian Doherty
42  * @since 1.5
43  * @see AbstractPerfDataBuffer
44  */
45 public class PerfDataBuffer extends PerfDataBufferImpl {
46 
47     // 8028357 removed old, inefficient debug logging
48 
49     private static final int syncWaitMs =
50             Integer.getInteger("sun.jvmstat.perdata.syncWaitMs", 5000);
51     private static final ArrayList<Monitor> EMPTY_LIST = new ArrayList<Monitor>(0);
52 
53     /*
54      * the following constants must be kept in sync with struct
55      * PerfDataEntry in perfMemory.hpp
56      */
57     private final static int PERFDATA_ENTRYLENGTH_OFFSET=0;
58     private final static int PERFDATA_ENTRYLENGTH_SIZE=4;   // sizeof(int)
59     private final static int PERFDATA_NAMELENGTH_OFFSET=4;
60     private final static int PERFDATA_NAMELENGTH_SIZE=4;    // sizeof(int)
61     private final static int PERFDATA_VECTORLENGTH_OFFSET=8;
62     private final static int PERFDATA_VECTORLENGTH_SIZE=4;  // sizeof(int)
63     private final static int PERFDATA_DATATYPE_OFFSET=12;
64     private final static int PERFDATA_DATATYPE_SIZE=1;      // sizeof(byte)
65     private final static int PERFDATA_FLAGS_OFFSET=13;
66     private final static int PERFDATA_FLAGS_SIZE=1;        // sizeof(byte)
67     private final static int PERFDATA_DATAUNITS_OFFSET=14;
68     private final static int PERFDATA_DATAUNITS_SIZE=1;     // sizeof(byte)
69     private final static int PERFDATA_DATAATTR_OFFSET=15;
70     private final static int PERFDATA_DATAATTR_SIZE=1;      // sizeof(byte)
71     private final static int PERFDATA_NAME_OFFSET=16;
72 
73     PerfDataBufferPrologue prologue;
74     int nextEntry;
75     int pollForEntry;
76     int perfDataItem;
77     long lastModificationTime;
78     int lastUsed;
79     IntegerMonitor overflow;
80     ArrayList<Monitor> insertedMonitors;
81 
82     /**
83      * Construct a PerfDataBufferImpl instance.
84      * <p>
85      * This class is dynamically loaded by
86      * {@link AbstractPerfDataBuffer#createPerfDataBuffer}, and this
87      * constructor is called to instantiate the instance.
88      *
89      * @param buffer the buffer containing the instrumentation data
90      * @param lvmid the Local Java Virtual Machine Identifier for this
91      *              instrumentation buffer.
92      */
PerfDataBuffer(ByteBuffer buffer, int lvmid)93     public PerfDataBuffer(ByteBuffer buffer, int lvmid)
94            throws MonitorException {
95         super(buffer, lvmid);
96         prologue = new PerfDataBufferPrologue(buffer);
97         this.buffer.order(prologue.getByteOrder());
98     }
99 
100     /**
101      * {@inheritDoc}
102      */
buildMonitorMap(Map<String, Monitor> map)103     protected void buildMonitorMap(Map<String, Monitor> map) throws MonitorException {
104         assert Thread.holdsLock(this);
105 
106         // start at the beginning of the buffer
107         buffer.rewind();
108 
109         // create pseudo monitors
110         buildPseudoMonitors(map);
111 
112         // position buffer to start of the data section
113         buffer.position(prologue.getSize());
114         nextEntry = buffer.position();
115         perfDataItem = 0;
116 
117         int used = prologue.getUsed();
118         long modificationTime = prologue.getModificationTimeStamp();
119 
120         Monitor m = getNextMonitorEntry();
121         while (m != null) {
122             map.put(m.getName(), m);
123             m = getNextMonitorEntry();
124         }
125 
126         /*
127          * set the last modification data. These are set to the values
128          * recorded before parsing the data structure. This allows the
129          * the data structure to be modified while the Map is being built.
130          * The Map may contain more entries than indicated based on the
131          * time stamp, but this is handled by ignoring duplicate entries
132          * when the Map is updated in getNewMonitors().
133          */
134         lastUsed = used;
135         lastModificationTime = modificationTime;
136 
137         // synchronize with the target jvm
138         synchWithTarget(map);
139 
140         // work around 1.4.2 counter inititization bugs
141         kludge(map);
142 
143         insertedMonitors = new ArrayList<Monitor>(map.values());
144     }
145 
146     /**
147      * {@inheritDoc}
148      */
getNewMonitors(Map<String, Monitor> map)149     protected void getNewMonitors(Map<String, Monitor> map) throws MonitorException {
150         assert Thread.holdsLock(this);
151 
152         int used = prologue.getUsed();
153         long modificationTime = prologue.getModificationTimeStamp();
154 
155         if ((used > lastUsed) || (lastModificationTime > modificationTime)) {
156 
157             lastUsed = used;
158             lastModificationTime = modificationTime;
159 
160             Monitor monitor = getNextMonitorEntry();
161             while (monitor != null) {
162                 String name = monitor.getName();
163 
164                 // guard against duplicate entries
165                 if (!map.containsKey(name)) {
166                     map.put(name, monitor);
167 
168                     /*
169                      * insertedMonitors is null when called from pollFor()
170                      * via buildMonitorMap(). Since we update insertedMonitors
171                      * at the end of buildMonitorMap(), it's ok to skip the
172                      * add here.
173                      */
174                     if (insertedMonitors != null) {
175                         insertedMonitors.add(monitor);
176                     }
177                 }
178                 monitor = getNextMonitorEntry();
179             }
180         }
181     }
182 
183     /**
184      * {@inheritDoc}
185      */
getMonitorStatus(Map<String, Monitor> map)186     protected MonitorStatus getMonitorStatus(Map<String, Monitor> map) throws MonitorException {
187         assert Thread.holdsLock(this);
188         assert insertedMonitors != null;
189 
190         // load any new monitors
191         getNewMonitors(map);
192 
193         // current implementation doesn't support deletion or reuse of entries
194         ArrayList<Monitor> removed = EMPTY_LIST;
195         ArrayList<Monitor> inserted = insertedMonitors;
196 
197         insertedMonitors = new ArrayList<Monitor>();
198         return new MonitorStatus(inserted, removed);
199     }
200 
201     /**
202      * Build the pseudo monitors used to map the prolog data into counters.
203      */
buildPseudoMonitors(Map<String, Monitor> map)204     protected void buildPseudoMonitors(Map<String, Monitor> map) {
205         Monitor monitor = null;
206         String name = null;
207         IntBuffer ib = null;
208 
209         name = PerfDataBufferPrologue.PERFDATA_MAJOR_NAME;
210         ib = prologue.majorVersionBuffer();
211         monitor = new PerfIntegerMonitor(name, Units.NONE,
212                                          Variability.CONSTANT, false, ib);
213         map.put(name, monitor);
214 
215         name = PerfDataBufferPrologue.PERFDATA_MINOR_NAME;
216         ib = prologue.minorVersionBuffer();
217         monitor = new PerfIntegerMonitor(name, Units.NONE,
218                                          Variability.CONSTANT, false, ib);
219         map.put(name, monitor);
220 
221         name = PerfDataBufferPrologue.PERFDATA_BUFFER_SIZE_NAME;
222         ib = prologue.sizeBuffer();
223         monitor = new PerfIntegerMonitor(name, Units.BYTES,
224                                          Variability.MONOTONIC, false, ib);
225         map.put(name, monitor);
226 
227         name = PerfDataBufferPrologue.PERFDATA_BUFFER_USED_NAME;
228         ib = prologue.usedBuffer();
229         monitor = new PerfIntegerMonitor(name, Units.BYTES,
230                                          Variability.MONOTONIC, false, ib);
231         map.put(name, monitor);
232 
233         name = PerfDataBufferPrologue.PERFDATA_OVERFLOW_NAME;
234         ib = prologue.overflowBuffer();
235         monitor = new PerfIntegerMonitor(name, Units.BYTES,
236                                          Variability.MONOTONIC, false, ib);
237         map.put(name, monitor);
238         this.overflow = (IntegerMonitor)monitor;
239 
240         name = PerfDataBufferPrologue.PERFDATA_MODTIMESTAMP_NAME;
241         LongBuffer lb = prologue.modificationTimeStampBuffer();
242         monitor = new PerfLongMonitor(name, Units.TICKS,
243                                       Variability.MONOTONIC, false, lb);
244         map.put(name, monitor);
245     }
246 
247     /**
248      * Method to provide a gross level of synchronization with the
249      * target monitored jvm.
250      *
251      * gross synchronization works by polling for the hotspot.rt.hrt.ticks
252      * counter, which is the last counter created by the StatSampler
253      * initialization code. The counter is updated when the watcher thread
254      * starts scheduling tasks, which is the last thing done in vm
255      * initialization.
256      */
synchWithTarget(Map<String, Monitor> map)257     protected void synchWithTarget(Map<String, Monitor> map) throws MonitorException {
258         /*
259          * synch must happen with syncWaitMs from now. Default is 5 seconds,
260          * which is reasonabally generous and should provide for extreme
261          * situations like startup delays due to allocation of large ISM heaps.
262          */
263         long timeLimit = System.currentTimeMillis() + syncWaitMs;
264 
265         String name = "hotspot.rt.hrt.ticks";
266         LongMonitor ticks = (LongMonitor)pollFor(map, name, timeLimit);
267 
268         /*
269          * loop waiting for the ticks counter to be non zero. This is
270          * an indication that the jvm is initialized.
271          */
272         while (ticks.longValue() == 0) {
273             try { Thread.sleep(20); } catch (InterruptedException e) { }
274 
275             if (System.currentTimeMillis() > timeLimit) {
276                 throw new MonitorException("Could Not Synchronize with target");
277             }
278         }
279     }
280 
281     /**
282      * Method to poll the instrumentation memory for a counter with
283      * the given name. The polling period is bounded by the timeLimit
284      * argument.
285      */
pollFor(Map<String, Monitor> map, String name, long timeLimit)286     protected Monitor pollFor(Map<String, Monitor> map, String name, long timeLimit)
287                       throws MonitorException {
288         Monitor monitor = null;
289 
290         pollForEntry = nextEntry;
291         while ((monitor = map.get(name)) == null) {
292 
293             try { Thread.sleep(20); } catch (InterruptedException e) { }
294 
295             long t = System.currentTimeMillis();
296             if ((t > timeLimit) || (overflow.intValue() > 0)) {
297                 throw new MonitorException("Could not find expected counter");
298             }
299 
300             getNewMonitors(map);
301         }
302         return monitor;
303     }
304 
305     /**
306      * method to make adjustments for known counter problems. This
307      * method depends on the availability of certain counters, which
308      * is generally guaranteed by the synchWithTarget() method.
309      */
kludge(Map<String, Monitor> map)310     protected void kludge(Map<String, Monitor> map) {
311         if (Boolean.getBoolean("sun.jvmstat.perfdata.disableKludge")) {
312             // bypass all kludges
313             return;
314         }
315 
316         String name = "java.vm.version";
317         StringMonitor jvm_version = (StringMonitor)map.get(name);
318         if (jvm_version == null) {
319             jvm_version = (StringMonitor)findByAlias(name);
320         }
321 
322         name = "java.vm.name";
323         StringMonitor jvm_name = (StringMonitor)map.get(name);
324         if (jvm_name == null) {
325             jvm_name = (StringMonitor)findByAlias(name);
326         }
327 
328         name = "hotspot.vm.args";
329         StringMonitor args = (StringMonitor)map.get(name);
330         if (args == null) {
331             args = (StringMonitor)findByAlias(name);
332         }
333 
334         assert ((jvm_name != null) && (jvm_version != null) && (args != null));
335 
336         if (jvm_name.stringValue().indexOf("HotSpot") >= 0) {
337             if (jvm_version.stringValue().startsWith("1.4.2")) {
338                 kludgeMantis(map, args);
339             }
340         }
341     }
342 
343     /**
344      * method to repair the 1.4.2 parallel scavenge counters that are
345      * incorrectly initialized by the JVM when UseAdaptiveSizePolicy
346      * is set. This bug couldn't be fixed for 1.4.2 FCS due to putback
347      * restrictions.
348      */
kludgeMantis(Map<String, Monitor> map, StringMonitor args)349     private void kludgeMantis(Map<String, Monitor> map, StringMonitor args) {
350         /*
351          * the HotSpot 1.4.2 JVM with the +UseParallelGC option along
352          * with its default +UseAdaptiveSizePolicy option has a bug with
353          * the initialization of the sizes of the eden and survivor spaces.
354          * See bugid 4890736.
355          *
356          * note - use explicit 1.4.2 counter names here - don't update
357          * to latest counter names or attempt to find aliases.
358          */
359 
360         String cname = "hotspot.gc.collector.0.name";
361         StringMonitor collector = (StringMonitor)map.get(cname);
362 
363         if (collector.stringValue().compareTo("PSScavenge") == 0) {
364             boolean adaptiveSizePolicy = true;
365 
366             /*
367              * HotSpot processes the -XX:Flags/.hotspotrc arguments prior to
368              * processing the command line arguments. This allows the command
369              * line arguments to override any defaults set in .hotspotrc
370              */
371             cname = "hotspot.vm.flags";
372             StringMonitor flags = (StringMonitor)map.get(cname);
373             String allArgs = flags.stringValue() + " " + args.stringValue();
374 
375             /*
376              * ignore the -XX: prefix as it only applies to the arguments
377              * passed from the command line (i.e. the invocation api).
378              * arguments passed through .hotspotrc omit the -XX: prefix.
379              */
380             int ahi = allArgs.lastIndexOf("+AggressiveHeap");
381             int aspi = allArgs.lastIndexOf("-UseAdaptiveSizePolicy");
382 
383             if (ahi != -1) {
384                 /*
385                  * +AggressiveHeap was set, check if -UseAdaptiveSizePolicy
386                  * is set after +AggressiveHeap.
387                  */
388                 //
389                 if ((aspi != -1) && (aspi > ahi)) {
390                     adaptiveSizePolicy = false;
391                 }
392             } else {
393                 /*
394                  * +AggressiveHeap not set, must be +UseParallelGC. The
395                  * relative position of -UseAdaptiveSizePolicy is not
396                  * important in this case, as it will override the
397                  * UseParallelGC default (+UseAdaptiveSizePolicy) if it
398                  * appears anywhere in the JVM arguments.
399                  */
400                 if (aspi != -1) {
401                     adaptiveSizePolicy = false;
402                 }
403             }
404 
405             if (adaptiveSizePolicy) {
406                 // adjust the buggy AdaptiveSizePolicy size counters.
407 
408                 // first remove the real counters.
409                 String eden_size = "hotspot.gc.generation.0.space.0.size";
410                 String s0_size = "hotspot.gc.generation.0.space.1.size";
411                 String s1_size = "hotspot.gc.generation.0.space.2.size";
412                 map.remove(eden_size);
413                 map.remove(s0_size);
414                 map.remove(s1_size);
415 
416                 // get the maximum new generation size
417                 String new_max_name = "hotspot.gc.generation.0.capacity.max";
418                 LongMonitor new_max = (LongMonitor)map.get(new_max_name);
419 
420                 /*
421                  * replace the real counters with pseudo counters that are
422                  * initialized to the correct values. The maximum size of
423                  * the eden and survivor spaces are supposed to be:
424                  *    max_eden_size = new_size - (2*alignment).
425                  *    max_survivor_size = new_size - (2*alignment).
426                  * since we don't know the alignment value used, and because
427                  * of other parallel scavenge bugs that result in oversized
428                  * spaces, we just set the maximum size of each space to the
429                  * full new gen size.
430                  */
431                 Monitor monitor = null;
432 
433                 LongBuffer lb = LongBuffer.allocate(1);
434                 lb.put(new_max.longValue());
435                 monitor = new PerfLongMonitor(eden_size, Units.BYTES,
436                                               Variability.CONSTANT, false, lb);
437                 map.put(eden_size, monitor);
438 
439                 monitor = new PerfLongMonitor(s0_size, Units.BYTES,
440                                               Variability.CONSTANT, false, lb);
441                 map.put(s0_size, monitor);
442 
443                 monitor = new PerfLongMonitor(s1_size, Units.BYTES,
444                                               Variability.CONSTANT, false, lb);
445                 map.put(s1_size, monitor);
446             }
447         }
448     }
449 
450     /**
451      * method to extract the next monitor entry from the instrumentation memory.
452      * assumes that nextEntry is the offset into the byte array
453      * at which to start the search for the next entry. method leaves
454      * next entry pointing to the next entry or to the end of data.
455      */
getNextMonitorEntry()456     protected Monitor getNextMonitorEntry() throws MonitorException {
457         Monitor monitor = null;
458 
459         // entries are always 4 byte aligned.
460         if ((nextEntry % 4) != 0) {
461             throw new MonitorStructureException(
462                    "Entry index not properly aligned: " + nextEntry);
463         }
464 
465         // protect against a corrupted shared memory region.
466         if ((nextEntry < 0) || (nextEntry > buffer.limit())) {
467             throw new MonitorStructureException(
468                    "Entry index out of bounds: nextEntry = " + nextEntry
469                    + ", limit = " + buffer.limit());
470         }
471 
472         // check for the end of the buffer
473         if (nextEntry == buffer.limit()) {
474             return null;
475         }
476 
477         buffer.position(nextEntry);
478 
479         int entryStart = buffer.position();
480         int entryLength = buffer.getInt();
481 
482         // check for valid entry length
483         if ((entryLength < 0) || (entryLength > buffer.limit())) {
484             throw new MonitorStructureException(
485                    "Invalid entry length: entryLength = " + entryLength);
486         }
487 
488         // check if last entry occurs before the eof.
489         if ((entryStart + entryLength) > buffer.limit()) {
490             throw new MonitorStructureException(
491                    "Entry extends beyond end of buffer: "
492                    + " entryStart = " + entryStart
493                    + " entryLength = " + entryLength
494                    + " buffer limit = " + buffer.limit());
495         }
496 
497         if (entryLength == 0) {
498             // end of data
499             return null;
500         }
501 
502         int nameLength = buffer.getInt();
503         int vectorLength = buffer.getInt();
504         byte dataType = buffer.get();
505         byte flags = buffer.get();
506         Units u = Units.toUnits(buffer.get());
507         Variability v = Variability.toVariability(buffer.get());
508         boolean supported = (flags & 0x01) != 0;
509 
510         // defend against corrupt entries
511         if ((nameLength <= 0) || (nameLength > entryLength)) {
512             throw new MonitorStructureException(
513                    "Invalid Monitor name length: " + nameLength);
514         }
515 
516         if ((vectorLength < 0) || (vectorLength > entryLength)) {
517             throw new MonitorStructureException(
518                    "Invalid Monitor vector length: " + vectorLength);
519         }
520 
521         // read in the perfData item name, casting bytes to chars. skip the
522         // null terminator
523         //
524         byte[] nameBytes = new byte[nameLength-1];
525         for (int i = 0; i < nameLength-1; i++) {
526             nameBytes[i] = buffer.get();
527         }
528 
529         // convert name into a String
530         String name = new String(nameBytes, 0, nameLength-1);
531 
532         if (v == Variability.INVALID) {
533             throw new MonitorDataException("Invalid variability attribute:"
534                                            + " entry index = " + perfDataItem
535                                            + " name = " + name);
536         }
537         if (u == Units.INVALID) {
538             throw new MonitorDataException("Invalid units attribute: "
539                                            + " entry index = " + perfDataItem
540                                            + " name = " + name);
541         }
542 
543         int offset;
544         if (vectorLength == 0) {
545             // scalar Types
546             if (dataType == BasicType.LONG.intValue()) {
547                 offset = entryStart + entryLength - 8;  /* 8 = sizeof(long) */
548                 buffer.position(offset);
549                 LongBuffer lb = buffer.asLongBuffer();
550                 lb.limit(1);
551                 monitor = new PerfLongMonitor(name, u, v, supported, lb);
552                 perfDataItem++;
553             } else {
554                 // bad data types.
555                 throw new MonitorTypeException("Invalid Monitor type:"
556                                     + " entry index = " + perfDataItem
557                                     + " name = " + name
558                                     + " type = " + dataType);
559             }
560         } else {
561             // vector types
562             if (dataType == BasicType.BYTE.intValue()) {
563                 if (u != Units.STRING) {
564                     // only byte arrays of type STRING are currently supported
565                     throw new MonitorTypeException("Invalid Monitor type:"
566                                       + " entry index = " + perfDataItem
567                                       + " name = " + name
568                                       + " type = " + dataType);
569                 }
570 
571                 offset = entryStart + PERFDATA_NAME_OFFSET + nameLength;
572                 buffer.position(offset);
573                 ByteBuffer bb = buffer.slice();
574                 bb.limit(vectorLength);
575                 bb.position(0);
576 
577                 if (v == Variability.CONSTANT) {
578                     monitor = new PerfStringConstantMonitor(name, supported,
579                                                             bb);
580                 } else if (v == Variability.VARIABLE) {
581                     monitor = new PerfStringVariableMonitor(name, supported,
582                                                             bb, vectorLength-1);
583                 } else {
584                     // Monotonically increasing byte arrays are not supported
585                     throw new MonitorDataException(
586                             "Invalid variability attribute:"
587                             + " entry index = " + perfDataItem
588                             + " name = " + name
589                             + " variability = " + v);
590                 }
591                 perfDataItem++;
592             } else {
593                 // bad data types.
594                 throw new MonitorTypeException(
595                         "Invalid Monitor type:" + " entry index = "
596                         + perfDataItem + " name = " + name
597                         + " type = " + dataType);
598             }
599         }
600 
601         // setup index to next entry for next iteration of the loop.
602         nextEntry = entryStart + entryLength;
603         return monitor;
604     }
605 }
606