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