org.netbeans.lib.profiler.server.HeapHistogramManager Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.lib.profiler.server;
import java.io.BufferedReader;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.netbeans.lib.profiler.wireprotocol.HeapHistogramResponse;
/**
*
* @author Tomas Hurka
*/
class HeapHistogramManager {
private static final String BOOLEAN_TEXT = "boolean"; // NOI18N
private static final String CHAR_TEXT = "char"; // NOI18N
private static final String BYTE_TEXT = "byte"; // NOI18N
private static final String SHORT_TEXT = "short"; // NOI18N
private static final String INT_TEXT = "int"; // NOI18N
private static final String LONG_TEXT = "long"; // NOI18N
private static final String FLOAT_TEXT = "float"; // NOI18N
private static final String DOUBLE_TEXT = "double"; // NOI18N
private static final String VOID_TEXT = "void"; // NOI18N
private static final Map permGenNames = new HashMap();
static {
permGenNames.put("", "Read-Write Method Metadata"); // NOI18N
permGenNames.put("", "Read-Only Method Metadata"); // NOI18N
permGenNames.put("", "Method Profiling Information"); // NOI18N
permGenNames.put("", "Constant Pool Metadata"); // NOI18N
permGenNames.put("", "Class Resolution Optimization Metadata"); // NOI18N
permGenNames.put("", "VM Symbol Metadata"); // NOI18N
permGenNames.put("", "Inline Cache Metadata"); // NOI18N
permGenNames.put("", "Instance Class Metadata"); // NOI18N
permGenNames.put("", "Object Array Class Metadata"); // NOI18N
permGenNames.put("", "Scalar Array Class Metadata"); // NOI18N
permGenNames.put("", "Base Class Metadata"); // NOI18N
permGenNames.put("", "Base Array Class Metadata"); // NOI18N
}
Map classesIdMap = new HashMap(8000);
boolean isJrockitVM;
HeapHistogramManager() {
String vmName = System.getProperty("java.vm.name"); // NOI18N
if (vmName != null) {
if ("BEA JRockit(R)".equals(vmName)) { // NOI18N
isJrockitVM = true;
} else if ("Oracle JRockit(R)".equals(vmName)) { // NOI18N
isJrockitVM = true;
}
}
}
HeapHistogramResponse computeHistogram(InputStream in) {
HeapHistogramResponse histogram;
if (in == null) {
return null;
}
try {
if (isJrockitVM) {
histogram = computeHistogramJRockit(in);
} else { // HotSpot
histogram = computeHistogramImpl(in);
}
in.close();
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
return histogram;
}
private HeapHistogramResponse computeHistogramJRockit(InputStream in) throws IOException {
long totalHeapBytes = 0;
long totalHeapInstances = 0;
long totalInstances = 0;
long totalBytes = 0;
Map classesMap = new HashMap(1024);
Date time = new Date();
Scanner sc = new Scanner(in);
sc.nextLine();
sc.skip('-');
sc.nextLine();
while (sc.hasDigit()) {
JRockitClassInfoImpl newClInfo = new JRockitClassInfoImpl(sc);
if (ProfilerInterface.serverInternalClassName(newClInfo.getName())) {
continue;
}
storeClassInfo(newClInfo, classesMap);
totalHeapBytes += newClInfo.getBytes();
totalHeapInstances += newClInfo.getInstancesCount();
}
totalInstances = totalHeapInstances;
totalBytes = totalHeapBytes;
return getResponse(classesMap, time);
}
private HeapHistogramResponse computeHistogramImpl(InputStream in) throws IOException {
long totalBytes = 0;
long totalInstances = 0;
long totalHeapBytes = 0;
long totalHeapInstances = 0;
long totalPermGenBytes = 0;
long totalPermgenInstances = 0;
Map permGenMap = new HashMap(1024);
Map classesMap = new HashMap(1024);
int[] ids;
long[] instances, bytes;
int[] newids;
String[] newNames;
Map newClassNames;
Date time = new Date();
Scanner sc = new Scanner(in);
while(!sc.hasNext("------------------")) { // NOI18N
sc.nextLine();
}
sc.skip('-'); // NOI18N
sc.nextLine();
while (sc.hasDigit()) {
ClassInfoImpl newClInfo = new ClassInfoImpl(sc);
if (ProfilerInterface.serverInternalClassName(newClInfo.getName())) {
continue;
}
if (newClInfo.isPermGen()) {
storeClassInfo(newClInfo, permGenMap);
totalPermGenBytes += newClInfo.getBytes();
totalPermgenInstances += newClInfo.getInstancesCount();
} else {
storeClassInfo(newClInfo, classesMap);
totalHeapBytes += newClInfo.getBytes();
totalHeapInstances += newClInfo.getInstancesCount();
}
}
if ("Total".equals(sc.next())) { // NOI18N
totalInstances = sc.nextLong();
totalBytes = sc.nextLong();
}
return getResponse(classesMap, time);
}
static void storeClassInfo(final ClassInfoImpl newClInfo, final Map map) {
ClassInfoImpl oldClInfo = (ClassInfoImpl) map.get(newClInfo.getName());
if (oldClInfo == null) {
map.put(newClInfo.getName(), newClInfo);
} else {
oldClInfo.bytes += newClInfo.getBytes();
oldClInfo.instances += newClInfo.getInstancesCount();
}
}
private HeapHistogramResponse getResponse(Map classesMap, Date time) {
Map newClassNames;
int[] ids;
long[] instances;
long[] bytes;
int[] newids;
String[] newNames;
newClassNames = new HashMap(100);
ids = new int[classesMap.size()];
instances = new long[classesMap.size()];
bytes = new long[classesMap.size()];
int outIndex = 0;
Iterator it = classesMap.values().iterator();
while (it.hasNext()) {
ClassInfoImpl ci = (ClassInfoImpl) it.next();
String name = ci.getName();
Integer cindex = (Integer) classesIdMap.get(name);
if (cindex == null) {
cindex = classesIdMap.size();
classesIdMap.put(name, cindex);
newClassNames.put(name, cindex);
}
ids[outIndex] = cindex.intValue();
instances[outIndex] = ci.instances;
bytes[outIndex] = ci.bytes;
outIndex++;
}
newids = new int[newClassNames.size()];
newNames = new String[newClassNames.size()];
outIndex = 0;
it = newClassNames.entrySet().iterator();
while (it.hasNext()) {
Map.Entry newClassEntry = (Map.Entry) it.next();
newids[outIndex] = ((Integer) newClassEntry.getValue()).intValue();
newNames[outIndex] = (String) newClassEntry.getKey();
outIndex++;
}
return new HeapHistogramResponse(time, newNames, newids, ids, instances, bytes);
}
static class ClassInfoImpl {
long instances;
long bytes;
String name;
boolean permGen;
ClassInfoImpl() {
}
ClassInfoImpl(Scanner sc) throws IOException {
String jvmName;
sc.skipWord();
instances = sc.nextLong();
bytes = sc.nextLong();
jvmName = sc.next();
sc.nextLine(); // skip module name on JDK 9
permGen = jvmName.charAt(0) == '<'; // NOI18N
name = convertJVMName(jvmName);
}
public String getName() {
return name;
}
public long getInstancesCount() {
return instances;
}
public long getBytes() {
return bytes;
}
public int hashCode() {
return getName().hashCode();
}
public boolean equals(Object obj) {
if (obj instanceof ClassInfoImpl) {
return getName().equals(((ClassInfoImpl) obj).getName());
}
return false;
}
private boolean isPermGen() {
return permGen;
}
String convertJVMName(String jvmName) {
String name = null;
int index = jvmName.lastIndexOf('[');
if (index != -1) {
switch (jvmName.charAt(index + 1)) {
case ProfilerInterface.BOOLEAN:
name = BOOLEAN_TEXT;
break;
case ProfilerInterface.CHAR:
name = CHAR_TEXT;
break;
case ProfilerInterface.BYTE:
name = BYTE_TEXT;
break;
case ProfilerInterface.SHORT:
name = SHORT_TEXT;
break;
case ProfilerInterface.INT:
name = INT_TEXT;
break;
case ProfilerInterface.LONG:
name = LONG_TEXT;
break;
case ProfilerInterface.FLOAT:
name = FLOAT_TEXT;
break;
case ProfilerInterface.DOUBLE:
name = DOUBLE_TEXT;
break;
case ProfilerInterface.REFERENCE:
name = jvmName.substring(index + 2, jvmName.length() - 1);
break;
default:
System.err.println("Uknown name " + jvmName); // NOI18N
name = jvmName;
}
for (int i = 0; i <= index; i++) {
name = name.concat("[]"); // NOI18N
}
} else if (isPermGen()) {
name = (String) permGenNames.get(jvmName);
}
if (name == null) {
name = jvmName;
}
return name;
}
}
static class JRockitClassInfoImpl extends ClassInfoImpl {
JRockitClassInfoImpl(Scanner sc) throws IOException {
String jvmName;
sc.skipWord();// skip unused 99.9%
bytes = computeBytes(sc);
instances = sc.nextLong();
sc.skipWord(); // diff unused
jvmName = sc.next();
name = convertJVMName(jvmName.replace('/', '.'));
}
private long computeBytes(Scanner sc) throws IOException {
long bytes = sc.nextLong();
char multi = Character.toUpperCase(sc.nextChar());
if ('K' == multi) { // NOI18N
bytes *= 1024;
} else if ('M'== multi) { // NOI18N
bytes *= 1024 * 1024;
} else if ('G' == multi) { // NOI18N
bytes *= 1024 * 1024 * 1024L;
}
return bytes;
}
}
private static class Scanner {
Reader reader;
StringBuffer token;
Scanner(InputStream is) throws UnsupportedEncodingException {
reader = new BufferedReader(new InputStreamReader(new FullInputStream(is), StandardCharsets.UTF_8));
token = new StringBuffer(128);
}
void nextLine() throws IOException {
int ch;
int ch2;
do {
ch = reader.read();
} while (!isNewLine(ch));
reader.mark(1);
ch2 = reader.read();
if (isNewLine(ch2)) {
if (ch2 != ch) {
return;
}
}
reader.reset();
return;
}
void skip(char s) throws IOException {
int ch;
do {
reader.mark(1);
ch = reader.read();
} while (ch == s);
reader.reset();
return;
}
long nextLong() throws IOException {
int ch;
long number = 0;
skipWhitespace();
while (true) {
reader.mark(1);
ch = reader.read();
if (!Character.isDigit(ch)) {
reader.reset();
return number;
}
number *= 10;
number += ch - '0'; // NOI18N
}
}
String next() throws IOException {
int ch;
skipWhitespace();
token.setLength(0);
while (true) {
reader.mark(1);
ch = reader.read();
if (Character.isWhitespace(ch)) {
reader.reset();
return token.toString();
}
token.append((char) ch);
}
}
char nextChar() throws IOException {
return (char) reader.read();
}
void skipWhitespace() throws IOException {
int ch;
do {
reader.mark(1);
ch = reader.read();
} while (Character.isWhitespace(ch));
reader.reset();
}
boolean hasDigit() throws IOException {
boolean digit;
int ch;
skipWhitespace();
reader.mark(1);
ch = reader.read();
digit = Character.isDigit(ch);
reader.reset();
return digit;
}
void skipWord() throws IOException {
int ch;
skipWhitespace();
do {
reader.mark(1);
ch = reader.read();
} while (!Character.isWhitespace(ch));
reader.reset();
}
void close() throws IOException {
reader.close();
}
private boolean isNewLine(int ch) {
if (ch == '\r') { // NOI18N
return true;
}
if (ch == '\n') { // NOI18N
return true;
}
return false;
}
private boolean hasNext(String string) throws IOException {
reader.mark(string.length());
for (int i = 0; i < string.length(); i++) {
int ch = reader.read();
if (ch != string.charAt(i)) {
reader.reset();
return false;
}
}
reader.reset();
return true;
}
}
private static class FullInputStream extends FilterInputStream {
byte[] data;
FullInputStream(InputStream is) {
super(is);
data = new byte[256];
}
public int read(byte[] b, int off, int len) throws IOException {
int n = 0;
while (n < len) {
int count;
int remaining = len - n;
if (remaining > data.length) {
remaining = data.length;
}
count = super.read(data, 0, remaining);
if (count < 0) {
if (n == 0) { // nothing was read -> EOF
return -1;
}
return n;
}
System.arraycopy(data, 0, b, off + n, count);
n += count;
}
return n;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy