You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by lk...@apache.org on 2020/10/10 16:02:03 UTC

[netbeans] branch master updated: Demonstrating problems with multiple segments

This is an automated email from the ASF dual-hosted git repository.

lkishalmi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new 8ca1858  Demonstrating problems with multiple segments
8ca1858 is described below

commit 8ca18583d1f58b19bf8e87171f22e76f20acbf33
Author: Jaroslav Tulach <ja...@oracle.com>
AuthorDate: Fri Sep 18 17:47:09 2020 +0200

    Demonstrating problems with multiple segments
---
 .../lib/profiler/heap/HeapSegmentTest.java         | 122 ++++++
 .../org/netbeans/lib/profiler/heap/HeapUtils.java  | 439 +++++++++++++++++++++
 2 files changed, 561 insertions(+)

diff --git a/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapSegmentTest.java b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapSegmentTest.java
new file mode 100644
index 0000000..6141829
--- /dev/null
+++ b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapSegmentTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.heap;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import org.junit.Test;
+import org.netbeans.lib.profiler.heap.HeapUtils.HprofGenerator;
+
+public class HeapSegmentTest {
+    @Test
+    public void singleObject() throws IOException {
+        singleObject(false);
+    }
+    
+    @Test
+    public void singleObjectMultipleSegments() throws IOException {
+        singleObject(true);
+    }
+    
+    private static void singleObject(boolean flush) throws IOException {
+        File mydump = File.createTempFile("mydump", ".hprof");
+        generateSingleObject(new FileOutputStream(mydump), flush);
+        Heap heap = HeapFactory.createHeap(mydump);
+        List<JavaClass> allClasses = heap.getAllClasses();
+        assertEquals(5, allClasses.size());
+        assertEquals("java.lang.String", allClasses.get(0).getName());
+        assertEquals("char[]", allClasses.get(1).getName());
+        assertEquals("text.HelloWorld", allClasses.get(2).getName());
+
+        Collection<GCRoot> roots = new ArrayList<>(heap.getGCRoots());
+        assertEquals("Thread & two locals", 5, roots.size());
+        {
+            Iterator<GCRoot> it = roots.iterator();
+            while (it.hasNext()) {
+                if (it.next() instanceof ThreadObjectGCRoot) {
+                    continue;
+                }
+                it.remove();
+            }
+        }
+        assertEquals("Only one thread", 2, roots.size());
+        final Iterator<GCRoot> it = roots.iterator();
+        final Instance thread = it.next().getInstance();
+
+        Object daemon = thread.getValueOfField("daemon");
+        assertNotNull("daemon field found", daemon);
+        Instance value = (Instance) thread.getValueOfField("name");
+        assertNotNull("name assigned", value);
+        assertEquals("java.lang.String", value.getJavaClass().getName());
+        assertEquals(Boolean.class, daemon.getClass());
+        assertFalse("It is not daemon", (Boolean) daemon);
+    }
+
+    private static void generateSingleObject(OutputStream os, boolean flush) throws IOException {
+        try (HprofGenerator gen = new HprofGenerator(os)) {
+            gen.writeHeapSegment(new SampleDumpMemory(), flush);
+            gen.writeHeapSegment(new SampleDumpMemory2(), flush);
+        }
+    }
+    
+    private static class SampleDumpMemory implements HprofGenerator.Generator<HprofGenerator.HeapSegment> {
+        @Override
+        public void generate(HprofGenerator.HeapSegment seg) throws IOException {
+            int mainId = seg.dumpString("main");
+
+            HprofGenerator.ClassInstance clazz = seg.newClass("text.HelloWorld")
+                    .addField("daemon", Boolean.TYPE)
+                    .addField("name", String.class)
+                    .addField("priority", int.class)
+                    .dumpClass();
+
+            int threadOne = seg.dumpInstance(clazz);
+            int threadTwo = seg.dumpInstance(clazz, "daemon", 0, "name", mainId, "priority", 10);
+
+            int threadId = seg.newThread("main")
+                    .addStackFrame("HelloWorld", "HelloWorld.js", 11, mainId, threadOne)
+                    .addStackFrame(":program", "HelloWorld.js", 32, threadTwo)
+                    .dumpThread();
+
+            seg.dumpPrimitive(threadId);
+        }
+    }
+
+    private static class SampleDumpMemory2 implements HprofGenerator.Generator<HprofGenerator.HeapSegment> {
+        @Override
+        public void generate(HprofGenerator.HeapSegment seg) throws IOException {
+            int threadId = seg.newThread("main2")
+                    .addStackFrame("HelloWorld2", "HelloWorld2.js", 23)
+                    .addStackFrame(":program2", "HelloWorld2.js", 32)
+                    .dumpThread();
+
+            seg.dumpPrimitive(threadId);
+        }
+    }
+    
+}
diff --git a/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapUtils.java b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapUtils.java
new file mode 100644
index 0000000..6f7a57c
--- /dev/null
+++ b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapUtils.java
@@ -0,0 +1,439 @@
+/*
+ * 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.heap;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+final class HeapUtils {
+    static final class HprofGenerator implements Closeable {
+
+        private static final String MAGIC_WITH_SEGMENTS = "JAVA PROFILE 1.0.2";
+
+        private final Map<String, Integer> wholeStrings = new HashMap<>();
+        private final Map<String, Integer> heapStrings = new HashMap<>();
+        private final Map<Class<?>, ClassInstance> primitiveClasses = new HashMap<>();
+        private final Map<Object, Integer> primitives = new HashMap<>();
+        private final DataOutputStream whole;
+        private final ByteArrayOutputStream rawHeap = new ByteArrayOutputStream();
+        private int objectCounter;
+        private ClassInstance typeString;
+        private ClassInstance typeThread;
+
+        HprofGenerator(OutputStream os) throws IOException {
+            this.whole = new DataOutputStream(os);
+            whole.write(MAGIC_WITH_SEGMENTS.getBytes());
+            whole.write(0);
+            whole.writeInt(4);
+            whole.writeLong(System.currentTimeMillis());
+        }
+
+        interface Generator<T> {
+
+            void generate(T data) throws IOException;
+        }
+
+        public final class HeapSegment {
+
+            private final DataOutputStream heap;
+            private final boolean dumpHeapOnClose;
+
+            private HeapSegment(OutputStream out, boolean dumpHeapOnClose) {
+                this.heap = new DataOutputStream(out);
+                this.dumpHeapOnClose = dumpHeapOnClose;
+            }
+
+            public ClassBuilder newClass(String name) throws IOException {
+                int classId = writeLoadClass(0, name);
+                return new ClassBuilder(classId);
+            }
+
+            public ThreadBuilder newThread(String name) throws IOException {
+                return new ThreadBuilder(name);
+            }
+
+            private void close() throws IOException {
+                heap.close();
+                if (dumpHeapOnClose) {
+                    dumpHeap();
+                }
+            }
+
+            public int dumpString(String text) throws IOException {
+                if (text == null) {
+                    return 0;
+                }
+                Integer id = heapStrings.get(text);
+                if (id != null) {
+                    return id;
+                }
+
+                int instanceId = ++objectCounter;
+
+                heap.writeByte(0x23);
+                heap.writeInt(instanceId);
+                heap.writeInt(instanceId); // serial number
+                heap.writeInt(text.length()); // number of elements
+                heap.writeByte(0x05); // char
+                for (char ch : text.toCharArray()) {
+                    heap.writeChar(ch);
+                }
+                int stringId = dumpInstance(typeString, "value", instanceId, "hash", 0);
+
+                heapStrings.put(text, stringId);
+                return stringId;
+            }
+
+            public int dumpInstance(ClassInstance clazz, Object... stringValueSeq) throws IOException {
+                HashMap<String, Object> values = new HashMap<>();
+                for (int i = 0; i < stringValueSeq.length; i += 2) {
+                    values.put((String) stringValueSeq[i], stringValueSeq[i + 1]);
+                }
+
+                int instanceId = ++objectCounter;
+                heap.writeByte(0x21);
+                heap.writeInt(instanceId);
+                heap.writeInt(instanceId); // serial number
+                heap.writeInt(clazz.id);
+                heap.writeInt(clazz.fieldBytes);
+                for (Map.Entry<String, Class<?>> entry : clazz.fieldNamesAndTypes.entrySet()) {
+                    final Class<?> type = entry.getValue();
+                    final Object ref = values.get(entry.getKey());
+                    if (type == Boolean.TYPE || type == Byte.TYPE) {
+                        heap.writeByte(ref == null ? 0 : ((Number) ref).byteValue());
+                    } else if (entry.getValue() == Short.TYPE) {
+                        heap.writeShort(ref == null ? 0 : ((Number) ref).shortValue());
+                    } else if (entry.getValue() == Long.TYPE) {
+                        heap.writeLong(ref == null ? 0 : ((Number) ref).longValue());
+                    } else if (entry.getValue() == Float.TYPE) {
+                        heap.writeFloat(ref == null ? 0 : ((Number) ref).floatValue());
+                    } else if (entry.getValue() == Double.TYPE) {
+                        heap.writeDouble(ref == null ? 0 : ((Number) ref).doubleValue());
+                    } else if (entry.getValue() == Character.TYPE) {
+                        heap.writeChar(ref == null ? 0 : ((Character) ref));
+                    } else {
+                        heap.writeInt(ref == null ? 0 : ((Number) ref).intValue());
+                    }
+                }
+                return instanceId;
+            }
+
+            public int dumpPrimitive(Object obj) throws IOException {
+                Integer id = primitives.get(obj);
+                if (id != null) {
+                    return id;
+                }
+
+                final Class<? extends Object> clazz = obj.getClass();
+                ClassInstance wrapperClass = primitiveClasses.get(clazz);
+                if (wrapperClass == null) {
+                    try {
+                        assert clazz.getName().startsWith("java.lang.");
+                        Class<?> primitiveType = clazz.getDeclaredField("value").getType();
+                        assert primitiveType.isPrimitive();
+
+                        wrapperClass = newClass(clazz.getName())
+                                .addField("value", primitiveType)
+                                .dumpClass();
+                        primitiveClasses.put(clazz, wrapperClass);
+                    } catch (ReflectiveOperationException ex) {
+                        throw new IOException("Processing " + obj, ex);
+                    }
+                }
+                int instanceId = dumpInstance(wrapperClass, "value", obj);
+                primitives.put(obj, instanceId);
+                return instanceId;
+            }
+
+            public final class ThreadBuilder {
+
+                private String groupName;
+                private final List<Object[]> stacks;
+                private final String name;
+
+                private ThreadBuilder(String name) {
+                    this.stacks = new ArrayList<>();
+                    this.name = name;
+                }
+
+                public ThreadBuilder group(String name) {
+                    this.groupName = name;
+                    return this;
+                }
+
+                public ThreadBuilder addStackFrame(String rootName, String sourceFile, int lineNumber, int... locals) {
+                    stacks.add(new Object[]{rootName, sourceFile, lineNumber, locals});
+                    return this;
+                }
+
+                public int dumpThread() throws IOException {
+                    if (typeThread == null) {
+                        typeThread = newClass("java.lang.Thread")
+                                .addField("daemon", Boolean.TYPE)
+                                .addField("name", String.class)
+                                .addField("priority", Integer.TYPE)
+                                .dumpClass();
+                    }
+                    int nameId = dumpString(name);
+                    int threadId = dumpInstance(typeThread, "daemon", 0, "name", nameId, "priority", 0);
+
+                    int[] frameIds = new int[stacks.size()];
+                    int cnt = 0;
+                    for (Object[] frame : stacks) {
+                        frameIds[cnt++] = writeStackFrame((String) frame[0], (String) frame[1], (Integer) frame[2]);
+                    }
+                    int stackTraceId = writeStackTrace(threadId, frameIds);
+                    writeThreadStarted(threadId, name, groupName, stackTraceId);
+
+                    heap.writeByte(0x08);
+                    heap.writeInt(threadId); // object ID
+                    heap.writeInt(threadId); // serial #
+                    heap.writeInt(stackTraceId); // stacktrace #
+
+                    cnt = 0;
+                    for (Object[] frame : stacks) {
+                        int[] locals = (int[]) frame[3];
+                        for (int objId : locals) {
+                            heap.writeByte(0x03); // frame GC root
+                            heap.writeInt(objId);
+                            heap.writeInt(threadId); // thread serial #
+                            heap.writeInt(cnt); // frame number
+                        }
+                        cnt++;
+                    }
+
+                    return threadId;
+                }
+            }
+
+            public final class ClassBuilder {
+
+                private final int classId;
+                private TreeMap<String, Class<?>> fieldNamesAndTypes = new TreeMap<>();
+
+                private ClassBuilder(int id) {
+                    this.classId = id;
+                }
+
+                public ClassBuilder addField(String name, Class<?> type) {
+                    fieldNamesAndTypes.put(name, type);
+                    return this;
+                }
+
+                public ClassInstance dumpClass() throws IOException {
+                    heap.writeByte(0x20);
+                    heap.writeInt(classId); // class ID
+                    heap.writeInt(classId); // stacktrace serial number
+                    heap.writeInt(0); // superclass ID
+                    heap.writeInt(0); // classloader ID
+                    heap.writeInt(0); // signers ID
+                    heap.writeInt(0); // protection domain ID
+                    heap.writeInt(0); // reserved 1
+                    heap.writeInt(0); // reserved 2
+                    heap.writeInt(0); // instance size
+                    heap.writeShort(0); // # of constant pool entries
+                    heap.writeShort(0); // # of static fields
+                    heap.writeShort(fieldNamesAndTypes.size()); // # of instance fields
+                    int fieldBytes = 0;
+                    for (Map.Entry<String, Class<?>> entry : fieldNamesAndTypes.entrySet()) {
+                        int nId = writeString(entry.getKey());
+                        heap.writeInt(nId);
+                        final Class<?> type = entry.getValue();
+                        if (type.isPrimitive()) {
+                            if (type == Boolean.TYPE) {
+                                heap.writeByte(0x04);
+                                fieldBytes++;
+                            } else if (type == Character.TYPE) {
+                                heap.writeByte(0x05);
+                                fieldBytes += 2;
+                            } else if (type == Float.TYPE) {
+                                heap.writeByte(0x06);
+                                fieldBytes += 4;
+                            } else if (type == Double.TYPE) {
+                                heap.writeByte(0x07);
+                                fieldBytes += 8;
+                            } else if (type == Byte.TYPE) {
+                                heap.writeByte(0x08);
+                                fieldBytes++;
+                            } else if (type == Short.TYPE) {
+                                heap.writeByte(0x09);
+                                fieldBytes += 2;
+                            } else if (type == Integer.TYPE) {
+                                heap.writeByte(0x0a);
+                                fieldBytes += 4;
+                            } else if (type == Long.TYPE) {
+                                heap.writeByte(0x0b);
+                                fieldBytes += 8;
+                            } else {
+                                throw new IllegalStateException("Unsupported primitive type: " + type);
+                            }
+                        } else {
+                            heap.writeByte(0x02); // object
+                            fieldBytes += 4;
+                        }
+                    }
+                    ClassInstance inst = new ClassInstance(classId, fieldNamesAndTypes, fieldBytes);
+                    fieldNamesAndTypes = new TreeMap<>();
+                    return inst;
+                }
+            }
+        }
+
+        public final class ClassInstance {
+
+            private final int id;
+            private final TreeMap<String, Class<?>> fieldNamesAndTypes;
+            private final int fieldBytes;
+
+            private ClassInstance(int id, TreeMap<String, Class<?>> fieldNamesAndTypes, int fieldBytes) {
+                this.id = id;
+                this.fieldNamesAndTypes = fieldNamesAndTypes;
+                this.fieldBytes = fieldBytes;
+            }
+        }
+
+        public void writeHeapSegment(Generator<HeapSegment> generator, boolean flushSegmentsFrequently) throws IOException {
+            HeapSegment seg = new HeapSegment(rawHeap, flushSegmentsFrequently);
+            if (typeString == null) {
+                typeString = seg.newClass("java.lang.String")
+                        .addField("value", char[].class)
+                        .addField("hash", Integer.TYPE)
+                        .dumpClass();
+                seg.newClass("char[]")
+                        .dumpClass();
+            }
+            generator.generate(seg);
+            seg.close();
+        }
+
+        @Override
+        public void close() throws IOException {
+            dumpHeap();
+        }
+
+        private void dumpHeap() throws IOException {
+            if (rawHeap.size() > 0) {
+                whole.writeByte(0x1c);
+                whole.writeInt(0); // ms
+                final byte[] bytes = rawHeap.toByteArray();
+                whole.writeInt(bytes.length);
+                whole.write(bytes);
+                whole.close();
+                rawHeap.reset();
+            }
+        }
+
+        // internal primitives
+        private void writeThreadStarted(int id, String threadName, String groupName, int stackTraceId) throws IOException {
+            int threadNameId = writeString(threadName);
+            int groupNameId = writeString(groupName);
+
+            whole.writeByte(0x0A);
+            whole.writeInt(0); // ms
+            whole.writeInt(6 * 4);
+            whole.writeInt(id); // serial number
+            whole.writeInt(id); // object id
+            whole.writeInt(stackTraceId); // stacktrace serial number
+            whole.writeInt(threadNameId);
+            whole.writeInt(groupNameId);
+            whole.writeInt(0); // parent group
+        }
+
+        private int writeStackFrame(String rootName, String sourceFile, int lineNumber) throws IOException {
+            int id = ++objectCounter;
+
+            int rootNameId = writeString(rootName);
+            int signatureId = 0;
+            int sourceFileId = writeString(sourceFile);
+
+            whole.writeByte(0x04);
+            whole.writeInt(0); // ms
+            whole.writeInt(6 * 4);
+            whole.writeInt(id);
+            whole.writeInt(rootNameId);
+            whole.writeInt(signatureId);
+            whole.writeInt(sourceFileId);
+            whole.writeInt(++objectCounter); // class serial #
+            whole.writeInt(lineNumber);
+
+            return id;
+        }
+
+        private int writeStackTrace(int threadId, int... frames) throws IOException {
+            int id = ++objectCounter;
+
+            whole.writeByte(0x05);
+            whole.writeInt(0); // ms
+            whole.writeInt(12 + 4 * frames.length);
+            whole.writeInt(id);
+            whole.writeInt(threadId);
+            whole.writeInt(frames.length);
+            for (int fId : frames) {
+                whole.writeInt(fId);
+            }
+
+            return id;
+        }
+
+        private int writeLoadClass(int stackTrace, String className) throws IOException {
+            int classId = ++objectCounter;
+            int classNameId = writeString(className);
+
+            whole.writeByte(0x02);
+            whole.writeInt(0); // ms
+            whole.writeInt(4 * 4);
+            whole.writeInt(classId); // class serial number
+            whole.writeInt(classId); // class object ID
+            whole.writeInt(stackTrace); // stack trace serial number
+            whole.writeInt(classNameId); // class name string ID
+
+            return classId;
+        }
+
+        private int writeString(String text) throws IOException {
+            if (text == null) {
+                return 0;
+            }
+            Integer prevId = wholeStrings.get(text);
+            if (prevId != null) {
+                return prevId;
+            }
+            int stringId = ++objectCounter;
+            whole.writeByte(0x01);
+            whole.writeInt(0); // ms
+            byte[] utf8 = text.getBytes(StandardCharsets.UTF_8);
+            whole.writeInt(4 + utf8.length);
+            whole.writeInt(stringId);
+            whole.write(utf8);
+
+            wholeStrings.put(text, stringId);
+            return stringId;
+        }
+    }
+    
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists