/**
   * method to extract the next monitor entry from the instrumentation memory. assumes that
   * nextEntry is the offset into the byte array at which to start the search for the next entry.
   * method leaves next entry pointing to the next entry or to the end of data.
   */
  protected Monitor getNextMonitorEntry() throws MonitorException {
    Monitor monitor = null;

    // entries are always 4 byte aligned.
    if ((nextEntry % 4) != 0) {
      throw new MonitorStructureException("Entry index not properly aligned: " + nextEntry);
    }

    // protect against a corrupted shared memory region.
    if ((nextEntry < 0) || (nextEntry > buffer.limit())) {
      throw new MonitorStructureException(
          "Entry index out of bounds: nextEntry = " + nextEntry + ", limit = " + buffer.limit());
    }

    // check for the end of the buffer
    if (nextEntry == buffer.limit()) {
      lognl("getNextMonitorEntry():" + " nextEntry == buffer.limit(): returning");
      return null;
    }

    buffer.position(nextEntry);

    int entryStart = buffer.position();
    int entryLength = buffer.getInt();

    // check for valid entry length
    if ((entryLength < 0) || (entryLength > buffer.limit())) {
      throw new MonitorStructureException("Invalid entry length: entryLength = " + entryLength);
    }

    // check if last entry occurs before the eof.
    if ((entryStart + entryLength) > buffer.limit()) {
      throw new MonitorStructureException(
          "Entry extends beyond end of buffer: "
              + " entryStart = "
              + entryStart
              + " entryLength = "
              + entryLength
              + " buffer limit = "
              + buffer.limit());
    }

    if (entryLength == 0) {
      // end of data
      return null;
    }

    int nameLength = buffer.getInt();
    int vectorLength = buffer.getInt();
    byte dataType = buffer.get();
    byte flags = buffer.get();
    Units u = Units.toUnits(buffer.get());
    Variability v = Variability.toVariability(buffer.get());
    boolean supported = (flags & 0x01) != 0;

    // defend against corrupt entries
    if ((nameLength <= 0) || (nameLength > entryLength)) {
      throw new MonitorStructureException("Invalid Monitor name length: " + nameLength);
    }

    if ((vectorLength < 0) || (vectorLength > entryLength)) {
      throw new MonitorStructureException("Invalid Monitor vector length: " + vectorLength);
    }

    // read in the perfData item name, casting bytes to chars. skip the
    // null terminator
    //
    byte[] nameBytes = new byte[nameLength - 1];
    for (int i = 0; i < nameLength - 1; i++) {
      nameBytes[i] = buffer.get();
    }

    // convert name into a String
    String name = new String(nameBytes, 0, nameLength - 1);

    if (v == Variability.INVALID) {
      throw new MonitorDataException(
          "Invalid variability attribute:" + " entry index = " + perfDataItem + " name = " + name);
    }
    if (u == Units.INVALID) {
      throw new MonitorDataException(
          "Invalid units attribute: " + " entry index = " + perfDataItem + " name = " + name);
    }

    int offset;
    if (vectorLength == 0) {
      // scalar Types
      if (dataType == BasicType.LONG.intValue()) {
        offset = entryStart + entryLength - 8; /* 8 = sizeof(long) */
        buffer.position(offset);
        LongBuffer lb = buffer.asLongBuffer();
        lb.limit(1);
        monitor = new PerfLongMonitor(name, u, v, supported, lb);
        perfDataItem++;
      } else {
        // bad data types.
        throw new MonitorTypeException(
            "Invalid Monitor type:"
                + " entry index = "
                + perfDataItem
                + " name = "
                + name
                + " type = "
                + dataType);
      }
    } else {
      // vector types
      if (dataType == BasicType.BYTE.intValue()) {
        if (u != Units.STRING) {
          // only byte arrays of type STRING are currently supported
          throw new MonitorTypeException(
              "Invalid Monitor type:"
                  + " entry index = "
                  + perfDataItem
                  + " name = "
                  + name
                  + " type = "
                  + dataType);
        }

        offset = entryStart + PERFDATA_NAME_OFFSET + nameLength;
        buffer.position(offset);
        ByteBuffer bb = buffer.slice();
        bb.limit(vectorLength);
        bb.position(0);

        if (v == Variability.CONSTANT) {
          monitor = new PerfStringConstantMonitor(name, supported, bb);
        } else if (v == Variability.VARIABLE) {
          monitor = new PerfStringVariableMonitor(name, supported, bb, vectorLength - 1);
        } else {
          // Monotonically increasing byte arrays are not supported
          throw new MonitorDataException(
              "Invalid variability attribute:"
                  + " entry index = "
                  + perfDataItem
                  + " name = "
                  + name
                  + " variability = "
                  + v);
        }
        perfDataItem++;
      } else {
        // bad data types.
        throw new MonitorTypeException(
            "Invalid Monitor type:"
                + " entry index = "
                + perfDataItem
                + " name = "
                + name
                + " type = "
                + dataType);
      }
    }

    // setup index to next entry for next iteration of the loop.
    nextEntry = entryStart + entryLength;
    return monitor;
  }
  /**
   * method to repair the 1.4.2 parallel scavenge counters that are incorrectly initialized by the
   * JVM when UseAdaptiveSizePolicy is set. This bug couldn't be fixed for 1.4.2 FCS due to putback
   * restrictions.
   */
  private void kludgeMantis(Map<String, Monitor> map, StringMonitor args) {
    /*
     * the HotSpot 1.4.2 JVM with the +UseParallelGC option along
     * with its default +UseAdaptiveSizePolicy option has a bug with
     * the initialization of the sizes of the eden and survivor spaces.
     * See bugid 4890736.
     *
     * note - use explicit 1.4.2 counter names here - don't update
     * to latest counter names or attempt to find aliases.
     */

    String cname = "hotspot.gc.collector.0.name";
    StringMonitor collector = (StringMonitor) map.get(cname);

    if (collector.stringValue().compareTo("PSScavenge") == 0) {
      boolean adaptiveSizePolicy = true;

      /*
       * HotSpot processes the -XX:Flags/.hotspotrc arguments prior to
       * processing the command line arguments. This allows the command
       * line arguments to override any defaults set in .hotspotrc
       */
      cname = "hotspot.vm.flags";
      StringMonitor flags = (StringMonitor) map.get(cname);
      String allArgs = flags.stringValue() + " " + args.stringValue();

      /*
       * ignore the -XX: prefix as it only applies to the arguments
       * passed from the command line (i.e. the invocation api).
       * arguments passed through .hotspotrc omit the -XX: prefix.
       */
      int ahi = allArgs.lastIndexOf("+AggressiveHeap");
      int aspi = allArgs.lastIndexOf("-UseAdaptiveSizePolicy");

      if (ahi != -1) {
        /*
         * +AggressiveHeap was set, check if -UseAdaptiveSizePolicy
         * is set after +AggressiveHeap.
         */
        //
        if ((aspi != -1) && (aspi > ahi)) {
          adaptiveSizePolicy = false;
        }
      } else {
        /*
         * +AggressiveHeap not set, must be +UseParallelGC. The
         * relative position of -UseAdaptiveSizePolicy is not
         * important in this case, as it will override the
         * UseParallelGC default (+UseAdaptiveSizePolicy) if it
         * appears anywhere in the JVM arguments.
         */
        if (aspi != -1) {
          adaptiveSizePolicy = false;
        }
      }

      if (adaptiveSizePolicy) {
        // adjust the buggy AdaptiveSizePolicy size counters.

        // first remove the real counters.
        String eden_size = "hotspot.gc.generation.0.space.0.size";
        String s0_size = "hotspot.gc.generation.0.space.1.size";
        String s1_size = "hotspot.gc.generation.0.space.2.size";
        map.remove(eden_size);
        map.remove(s0_size);
        map.remove(s1_size);

        // get the maximum new generation size
        String new_max_name = "hotspot.gc.generation.0.capacity.max";
        LongMonitor new_max = (LongMonitor) map.get(new_max_name);

        /*
         * replace the real counters with pseudo counters that are
         * initialized to to the correct values. The maximum size of
         * the eden and survivor spaces are supposed to be:
         *    max_eden_size = new_size - (2*alignment).
         *    max_survivor_size = new_size - (2*alignment).
         * since we don't know the alignment value used, and because
         * of other parallel scavenge bugs that result in oversized
         * spaces, we just set the maximum size of each space to the
         * full new gen size.
         */
        Monitor monitor = null;

        LongBuffer lb = LongBuffer.allocate(1);
        lb.put(new_max.longValue());
        monitor = new PerfLongMonitor(eden_size, Units.BYTES, Variability.CONSTANT, false, lb);
        map.put(eden_size, monitor);

        monitor = new PerfLongMonitor(s0_size, Units.BYTES, Variability.CONSTANT, false, lb);
        map.put(s0_size, monitor);

        monitor = new PerfLongMonitor(s1_size, Units.BYTES, Variability.CONSTANT, false, lb);
        map.put(s1_size, monitor);
      }
    }
  }