diff options
author | David Daney <daney@gcc.gnu.org> | 2007-02-15 17:25:24 +0000 |
---|---|---|
committer | David Daney <daney@gcc.gnu.org> | 2007-02-15 17:25:24 +0000 |
commit | 7f40378f06a5723ede9c8abc23775cadcdd0037c (patch) | |
tree | ecd80f4d6cba05c29ed9420459814c84387b36bf /libjava/gnu | |
parent | 31429fbc3c70716cb375dc73d1d151ed769d945e (diff) | |
download | gcc-7f40378f06a5723ede9c8abc23775cadcdd0037c.zip gcc-7f40378f06a5723ede9c8abc23775cadcdd0037c.tar.gz gcc-7f40378f06a5723ede9c8abc23775cadcdd0037c.tar.bz2 |
Make-lang.in (JAVA_MANFILES): Add doc/gc-analyze.1.
gcc/java:
2007-02-15 David Daney <ddaney@avtrex.com>
* Make-lang.in (JAVA_MANFILES): Add doc/gc-analyze.1.
(java.maintainer-clean):Add gc-analyze.1.
(.INTERMEDIATE): Add gc-analyze.pod.
(gc-analyze.pod): New rule.
(java.install-man): Install gc-analyze.1
* gcj.texi: Add new section for the gc-analyze program.
libjava:
2007-02-15 Johannes Schmidt <jschmidt@avtrex.com>
David Daney <ddaney@avtrex.com>
* configure.ac: Create vm-tools-packages file. Add
gnu/gcj/tools/gc_analyze to standard.omit and vm-tools-packages.
Check for /proc/self/maps.
* Makefile.am (bin_PROGRAMS): Added gc-analyze.
(gc_analyze_SOURCES): New.
(gc_analyze_LDFLAGS): New.
(gc_analyze_LINK): New.
(gc_analyze_LDADD): New.
(gc_analyze_DEPENDENCIES): New.
(nat_source_files): Add gnu/gcj/util/natGCInfo.cc.
* Makefile.in: Regenerated.
* configure: Regenerated.
* include/config.h.in: Regenerated.
* sources.am: Regenerated.
* scripts/makemake.tcl: Don't include gc-analyze classes in libgcj.
* gnu/gcj/tools/gc_analyze/SymbolLookup.java: New.
* gnu/gcj/tools/gc_analyze/ObjectMap.java: New.
* gnu/gcj/tools/gc_analyze/MemoryMap.java: New.
* gnu/gcj/tools/gc_analyze/SymbolTable.java: New.
* gnu/gcj/tools/gc_analyze/BlockMap.java: New.
* gnu/gcj/tools/gc_analyze/BytePtr.java: New.
* gnu/gcj/tools/gc_analyze/ItemList.java: New.
* gnu/gcj/tools/gc_analyze/ToolPrefix.java: New.
* gnu/gcj/tools/gc_analyze/MemoryAnalyze.java: New.
* gnu/gcj/util/GCInfo.java: New.
* gnu/gcj/util/GCInfo.h: New.
* gnu/gcj/util/natGCInfo.cc: New.
* gnu/gcj/util/UtilPermission.java: New.
* gnu/gcj/util/UtilPermission.h: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/SymbolTable.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/ObjectMap$ObjectItem.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/MemoryMap$RangeComparator.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/BlockMap$PtrMarks.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/MemoryMap$Range.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/BlockMap.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/BytePtr.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$SubstringComparator.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/ItemList.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/ToolPrefix.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$1$Info.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$1.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$2.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$3.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$4.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$OptionParser.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/BlockMap$SizeKind.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/SymbolLookup.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/ObjectMap.class: New.
* classpath/tools/gnu/gcj/tools/gc_analyze/MemoryMap.class: New.
* classpath/lib/gnu/gcj/util/GCInfo.class: New.
* classpath/lib/gnu/gcj/util/UtilPermission.class: New.
libjava/classpath:
2007-02-15 David Daney <ddaney@avtrex.com>
* tools/Makefile.am (TOOLS_ZIP): Add classes from vm-tools-packages.
* tools/Makefile.in: Regenerated.
From-SVN: r122007
Diffstat (limited to 'libjava/gnu')
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/BlockMap.java | 218 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/BytePtr.java | 115 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/ItemList.java | 72 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java | 458 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java | 359 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java | 140 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java | 112 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java | 198 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java | 45 | ||||
-rw-r--r-- | libjava/gnu/gcj/util/GCInfo.h | 45 | ||||
-rw-r--r-- | libjava/gnu/gcj/util/GCInfo.java | 79 | ||||
-rw-r--r-- | libjava/gnu/gcj/util/UtilPermission.h | 32 | ||||
-rw-r--r-- | libjava/gnu/gcj/util/UtilPermission.java | 20 | ||||
-rw-r--r-- | libjava/gnu/gcj/util/natGCInfo.cc | 454 |
14 files changed, 2347 insertions, 0 deletions
diff --git a/libjava/gnu/gcj/tools/gc_analyze/BlockMap.java b/libjava/gnu/gcj/tools/gc_analyze/BlockMap.java new file mode 100644 index 0000000..6e7adae --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/BlockMap.java @@ -0,0 +1,218 @@ +/* BlockMap.java -- Container for information on GC maintained memory blocks. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Map; +import java.util.TreeMap; + +class BlockMap +{ + static final int HBLKSIZE = 4096; + + class SizeKind implements Comparable<SizeKind> + { + int size; + int kind; + + public SizeKind(int size, int kind) + { + this.size = size; + this.kind = kind; + } + + public int compareTo(SizeKind b) + { + if (this.size != b.size) + return this.size - b.size; + return this.kind - b.kind; + } + } + + class PtrMarks + { + long ptr; + int marks; + + public PtrMarks(long ptr, int marks) + { + this.ptr = ptr; + this.marks = marks; + } + } + + private TreeMap<SizeKind, ArrayList<PtrMarks>> map = + new TreeMap<SizeKind, ArrayList<PtrMarks>>(); + + public BlockMap(BufferedReader reader) throws IOException + { + for (;;) + { + String s = reader.readLine(); + if (s == null) + break; + if (s.charAt(0) == '#') + continue; + if (s.indexOf("Begin block map") >= 0) + { + for (;;) + { + s = reader.readLine(); + if (s.charAt(0) == '#') + continue; + if (s.indexOf("End block map") >= 0) + return; + String[] items = s.split(","); + long ptr = 0; + int kind = 0, size = 0, marks = 0; + for (int i=0; i<items.length; i++) + { + String[] x = items[i].split(" "); + String last = x[x.length - 1]; + switch (i) + { + case 0: + ptr = MemoryMap.parseHexLong(last.substring(2)); + break; + case 1: + kind = Integer.parseInt(last); + break; + case 2: + size = Integer.parseInt(last); + break; + case 3: + marks = Integer.parseInt(last); + break; + } + } + SizeKind sk = new SizeKind(size, kind); + ArrayList<PtrMarks> m = map.get(sk); + if (m == null) + { + m = new ArrayList<PtrMarks>(); + map.put(sk, m); + } + PtrMarks pm = new PtrMarks(ptr, marks); + m.add(pm); + } // inner loop + } // started inner loop + } // outer loop - finding begin + } // memoryMap + + public void dump() + { + System.out.println(); + System.out.println(); + System.out.println("*** Used Blocks ***\n"); + System.out.println(); + System.out.println(" Size Kind Blocks Used Free Wasted"); + System.out.println("------- ------------- ------- ---------- ---------- -------"); + + int total_blocks = 0, total_used = 0, total_free = 0, total_wasted = 0; + + for (Map.Entry<SizeKind, ArrayList<PtrMarks>> me : map.entrySet()) + { + SizeKind sk = me.getKey(); + + System.out.println(MemoryAnalyze.format(sk.size, 7) + " " + + MemoryAnalyze.kindToName(sk.kind)); + + int sub_blocks = 0, sub_used = 0, sub_free = 0, sub_wasted = 0; + int sub_count = 0; + + ArrayList<PtrMarks> v = me.getValue(); + + for (PtrMarks pm : v) + { + int bytes = sk.size; + int blocks = (sk.size + HBLKSIZE - 1) / HBLKSIZE; + int used; + int free; + int wasted; + + if (bytes < HBLKSIZE) + { + used = bytes * pm.marks; + free = bytes * (HBLKSIZE / bytes - pm.marks); + wasted = HBLKSIZE - HBLKSIZE / bytes * bytes; + } + else + { + if (pm.marks != 0) + { + used = bytes; + free = 0; + wasted = (bytes + HBLKSIZE - 1) + / HBLKSIZE * HBLKSIZE - used; + } + else + { + used = 0; + free = bytes; + wasted = 0; + } + } + + StringBuilder sb = new StringBuilder(); + sb.append(" "); + sb.append(MemoryAnalyze.format(blocks, 5)); + sb.append(" "); + sb.append(MemoryAnalyze.format(used, 9)); + sb.append(" "); + sb.append(MemoryAnalyze.format(free, 9)); + sb.append(" "); + sb.append(MemoryAnalyze.format(wasted, 9)); + System.out.println(sb); + + sub_blocks += blocks; + sub_used += used; + sub_free += free; + sub_wasted += wasted; + sub_count++; + + total_blocks += blocks; + total_used += used; + total_free += free; + total_wasted += wasted; + } // blocks with size/kind + if (sub_count > 1) + { + System.out.println( + " ------- ---------- ---------- -------"); + StringBuilder sb = new StringBuilder(); + sb.append(" "); + sb.append(MemoryAnalyze.format(sub_blocks, 5)); + sb.append(" "); + sb.append(MemoryAnalyze.format(sub_used, 9)); + sb.append(" "); + sb.append(MemoryAnalyze.format(sub_free, 9)); + sb.append(" "); + sb.append(MemoryAnalyze.format(sub_wasted, 9)); + System.out.println(sb); + } + } // size/kind + + System.out.println("------- ------------- ------- ---------- ---------- -------"); + StringBuilder sb = new StringBuilder(); + sb.append(" "); + sb.append(MemoryAnalyze.format(total_blocks, 5)); + sb.append(" "); + sb.append(MemoryAnalyze.format(total_used, 9)); + sb.append(" "); + sb.append(MemoryAnalyze.format(total_free, 9)); + sb.append(" "); + sb.append(MemoryAnalyze.format(total_wasted, 9)); + System.out.println(sb); + System.out.println("Total bytes = " + + MemoryAnalyze.format(total_blocks * HBLKSIZE, 10)); + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/BytePtr.java b/libjava/gnu/gcj/tools/gc_analyze/BytePtr.java new file mode 100644 index 0000000..4afceee --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/BytePtr.java @@ -0,0 +1,115 @@ +/* BytePtr.java -- Container for bytes from a memory image. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.nio.ByteBuffer; + +public class BytePtr +{ + ByteBuffer content; + int wordSize; + + BytePtr(ByteBuffer b, int ws) + { + content = b; + wordSize = ws; + } + + public int getsize() + { + return content.limit(); + } + + public int getByte(int offset) + { + return content.get(offset); + } + + public int getInt(int n) + { + return content.getInt(n * 4); + } + + public int getShort(int n) + { + return content.getShort(n * 2); + } + + public long getWord(int n) + { + if (4 == wordSize) + return 0xffffffffL & content.getInt(n * 4); + else + return content.getLong(n * 8); + } + + public int intsPerWord() + { + return (4 == wordSize) ? 1 : 2; + } + + public BytePtr getRegion(int offset, int size) + { + int oldLimit = content.limit(); + content.position(offset); + content.limit(offset + size); + ByteBuffer n = content.slice(); + content.position(0); + content.limit(oldLimit); + + return new BytePtr(n, wordSize); + } + + public void setInt(int a, int n) + { + content.putInt(a * 4, n); + } + + public void dump() + { + // 38 5a f4 2a 50 bd 04 10 10 00 00 00 0e 00 00 00 8Z.*P........... + int i; + StringBuilder b = new StringBuilder(67); + for (i = 0; i < 66; i++) + b.append(' '); + b.append('\n'); + + i = 0; + do + { + for (int j = 0; j < 16; j++) + { + int k = i + j; + + if (k < content.limit()) + { + int v = 0xff & getByte(k); + // hex + int v1 = v/16; + b.setCharAt(j * 3 + 0, + (char)(v1 >= 10 ? 'a' - 10 + v1 : v1 + '0')); + v1 = v % 16; + b.setCharAt(j * 3 + 1, + (char)(v1 >= 10 ? 'a' - 10 + v1 : v1 + '0')); + // ascii + b.setCharAt(j + 50, (char)((v >= 32 && v <= 127) ? v: '.')); + } + else + { + b.setCharAt(j * 3 + 0, ' '); + b.setCharAt(j * 3 + 1, ' '); + b.setCharAt(j + 50, ' '); + } + } + i += 16; + System.out.print(b); + } while (i < content.limit()); + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/ItemList.java b/libjava/gnu/gcj/tools/gc_analyze/ItemList.java new file mode 100644 index 0000000..7912beb --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/ItemList.java @@ -0,0 +1,72 @@ +/* ItemList.java -- Maps all objects keyed by their addresses. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +class ItemList +{ + public ItemList() + { + } + + private TreeMap<Long, HashMap<ObjectMap.ObjectItem, Integer>> map; + + public void add(ObjectMap.ObjectItem item) + { + if (map == null) + map = new TreeMap<Long, HashMap<ObjectMap.ObjectItem, Integer>>(); + Long x = new Long(item.klass); + HashMap<ObjectMap.ObjectItem, Integer> list = map.get(x); + if (list == null) + { + list = new HashMap<ObjectMap.ObjectItem, Integer>(); + map.put(x, list); + } + Integer count = list.get(item); + if (count == null) + list.put(item, new Integer(1)); + else + list.put(item, new Integer(count.intValue() + 1)); + } + + void dump(String title, SymbolLookup lookup) throws IOException + { + if (map == null) + return; + System.out.println(title); + for (Map.Entry<Long, HashMap<ObjectMap.ObjectItem, Integer>> me : + map.entrySet()) + { + HashMap<ObjectMap.ObjectItem, Integer> list = me.getValue(); + boolean first = true; + + for (Map.Entry<ObjectMap.ObjectItem, Integer> me2 : list.entrySet()) + { + ObjectMap.ObjectItem item = me2.getKey(); + Integer count = me2.getValue(); + if (first) + { + String name = + MemoryAnalyze.getSymbolPretty(lookup, item, false); + System.out.println(" " + name + ":"); + first = false; + } + System.out.print(" 0x" + Long.toHexString(item.ptr)); + if (count.intValue() != 1) + System.out.print(" * " + count); + System.out.println(); + } + } + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java b/libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java new file mode 100644 index 0000000..d56a71d --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java @@ -0,0 +1,458 @@ +/* MemoryAnalyze.java -- Analyzes a libgcj heap dump. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +package gnu.gcj.tools.gc_analyze; + +import gnu.classpath.tools.getopt.FileArgumentCallback; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.Parser; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +class MemoryAnalyze +{ + public MemoryAnalyze() + { + } + + private static NumberFormat numberFormat; + private static boolean verbose; + static String format(long number, int digits) + { + if (numberFormat == null) + { + numberFormat = NumberFormat.getNumberInstance(); + numberFormat.setGroupingUsed(true); + } + String temp = numberFormat.format(number); + int spaces = digits - temp.length(); + if (spaces < 0) + spaces = 0; + return " ".substring(0,spaces) + temp; + } + + static void sorted_report(String description, + int total_space, + ArrayList<String> list, + Comparator<String> comparator) + { + System.out.println("*** " + description + " ***"); + System.out.println(); + System.out.println(" Total Size Count Size Description"); + System.out.println("-------------- ----- -------- -----------------------------------"); + Collections.sort(list, comparator); + for (Iterator it = list.iterator(); it.hasNext(); ) + { + String v = (String)it.next(); + System.out.println(stripend(v)); + } + System.out.println("-------------- ----- -------- -----------------------------------"); + System.out.println(format(total_space, 14)); + System.out.println(); + System.out.println(); + } + + private static String stripend(String s) + { + int n = s.lastIndexOf(" /"); + if (n > 0) + return s.substring(0,n); + return s; + } + + static class SubstringComparator implements Comparator<String> + { + private int begin, end; + private boolean reverse; + + SubstringComparator(int begin, int end, boolean reverse) + { + this.begin = begin; + this.end = end; + this.reverse = reverse; + } + + public int compare(String s1, String s2) + { + if (end == 0) + s1 = s1.substring(begin); + else + s1 = s1.substring(begin, end); + + if (end == 0) + s2 = s2.substring(begin); + else + s2 = s2.substring(begin, end); + int i = s1.compareTo(s2); + if (reverse) + return -i; + return i; + } + } + + static class OptionParser extends Parser + { + int filesFound; + + OptionParser() + { + super("gc-analyze", + "gc-analyze (" + System.getProperty("java.vm.version") + ")"); + + add(new Option('d', + "Directory containing runtime objects", + "directory") + { + public void parsed(String argument) throws OptionException + { + ToolPrefix.pathPrefix = argument; + } + }); + + add(new Option('p', + "Binary tool prefix, prepended to nm and readelf to " + + "obtain target specific versions of these commands", + "prefix") + { + public void parsed(String argument) throws OptionException + { + ToolPrefix.toolPrefix = argument; + } + }); + + add(new Option("verbose", 'v', + "Verbose output; requires filename.bytes") + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + + setHeader("usage: gc-analyze [-v] [-p tool-prefix] [-d <directory>] " + + "filename"); + } + + protected void validate() throws OptionException + { + if (filesFound != 1) + throw new OptionException("Must specify exactly one filename"); + } + + public String[] parse(String[] inArgs) + { + final ArrayList<String> fileResult = new ArrayList<String>(); + parse(inArgs, new FileArgumentCallback() + { + public void notifyFile(String fileArgument) + { + filesFound++; + fileResult.add(fileArgument); + } + }); + return fileResult.toArray(new String[1]); + } + } + + public static void main(String[] args) + { + class Info + { + int size; + int count; + } + int total_space = 0; + + Parser optionParser = new OptionParser(); + + String rest[] = optionParser.parse(args); + + String filename = rest[0]; + + try + { + BufferedReader reader = + new BufferedReader(new InputStreamReader(new FileInputStream(filename))); + SymbolLookup lookup = new SymbolLookup(reader, filename + ".bytes"); + ObjectMap objectMap = new ObjectMap(reader); + BlockMap blockMap = new BlockMap(reader); + reader.close(); + + // add info to item(s) + // add item.klass + for (Map.Entry<Long, ObjectMap.ObjectItem> me : objectMap) + { + ObjectMap.ObjectItem item = me.getValue(); + + // try to get a klass (happens with intern'ed strings...) + if (item.klass==0) + { + BytePtr p = lookup.getBytePtr(item.ptr, item.size); + if (p!=null) + { + long vtable = p.getWord(0); + String sym = + lookup.getSymbolViaVtable(vtable - 2 * lookup.memoryMap.wordSize); + if (sym != null) + { + item.typeName = SymbolTable.demangleVTName(sym); + } + else if (vtable != 0) + { + // get klass from vtable + p = lookup.getBytePtr(vtable, + lookup.memoryMap.wordSize); + if (p != null) + { + long klass = p.getWord(0); + item.klass = klass; + } + } + } + } + + // figure out strings + String class_name; + if (null == item.typeName) + { + class_name = + MemoryAnalyze.getSymbolPretty(lookup, item, false); + item.typeName = class_name; + } + else + { + class_name = item.typeName; + } + System.out.print("class_name=[" + class_name + "]"); + + if (class_name.compareTo("_ZTVN4java4lang6StringE")==0 + || class_name.compareTo("java.lang.String")==0) + { + BytePtr p = lookup.getBytePtr(item.ptr, item.size); + long data = p.getWord(1); + int boffset = p.getInt(2 * p.intsPerWord()); + int count = p.getInt(1 + 2 * p.intsPerWord()); + int hash = p.getInt(2 + 2 * p.intsPerWord()); + BytePtr chars = lookup.getBytePtr(data+boffset, count * 2); + StringBuffer sb = new StringBuffer(count); + for (int qq = 0; qq<count; qq++) + sb.append((char)chars.getShort(qq)); + int newhash = sb.toString().hashCode(); + if (newhash!=hash) + { + p.setInt(4, newhash); + } + + item.string = sb.toString(); + System.out.println(" value = \"" + item.string + "\""); + if (data != item.ptr) + { + ObjectMap.ObjectItem next = objectMap.get(data); + if (next != null) + next.stringData = true; + else + System.out.println("String [" + item.string + "] at " + + Long.toHexString(item.ptr) + + " can't find array at " + + Long.toHexString(data)); + } + } + else if (null != item.string) + System.out.println(" value = \"" + item.string + "\""); + else + System.out.println(); + } + + + HashMap<String, Info> map = new HashMap<String, Info>(); + for (Map.Entry<Long, ObjectMap.ObjectItem> me : objectMap) + { + ObjectMap.ObjectItem item = me.getValue(); + String name = getSymbolPretty(lookup, item, true); + Info info = map.get(name); + if (info == null) + { + info = new Info(); + info.count = 0; + info.size = item.size; + map.put(name, info); + } + info.count++; + total_space += item.size; + } + + ArrayList<String> list = new ArrayList<String>(); + for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) + { + Map.Entry me = (Map.Entry)it.next(); + String name = (String)me.getKey(); + Info info = (Info)me.getValue(); + + StringBuffer sb = new StringBuffer(); + sb.append(format(info.count * info.size * 100 / total_space, + 3)); + sb.append("%"); + sb.append(format(info.count * info.size, 10)); + sb.append(" = "); + sb.append(format(info.count, 7)); + sb.append(" * "); + sb.append(format(info.size, 9)); + sb.append(" - "); + sb.append(name); + list.add(sb.toString()); + } + + sorted_report("Memory Usage Sorted by Total Size", + total_space, list, new SubstringComparator(5,14,true)); + sorted_report("Memory Usage Sorted by Description", + total_space, list, new SubstringComparator(39,0,false)); + sorted_report("Memory Usage Sorted by Count", + total_space, list, new SubstringComparator(17,25,true)); + sorted_report("Memory Usage Sorted by Size", + total_space, list, new SubstringComparator(28,37,true)); + + blockMap.dump(); + + // dump raw memory + if (verbose) + { + // analyze references + for (Map.Entry<Long, ObjectMap.ObjectItem> me : objectMap) + { + long ptr = me.getKey(); + ObjectMap.ObjectItem item = me.getValue(); + BytePtr p = lookup.getBytePtr(ptr, item.size); + if (p == null) + System.out.println("can't find ptr 0x" + + Long.toHexString(ptr)); + else if (item.kind != 0) // not GC_PTRFREE + for (int i = 1; + i < item.size / lookup.memoryMap.wordSize; i++) + { + long maybe_ptr = p.getWord(i); + ObjectMap.ObjectItem item2 = objectMap.get(maybe_ptr); + if (item2 != null) + { + item2.pointed_by.add(item); + item.points_to.add(item2); + } + } + } + System.out.println(); + System.out.println("*** All Objects ***"); + System.out.println(); + + for (Map.Entry<Long, ObjectMap.ObjectItem> me : objectMap) + { + long ptr = me.getKey(); + ObjectMap.ObjectItem item = me.getValue(); + String name = getSymbolPretty(lookup, item, false); + System.out.print("0x" + Long.toHexString(ptr) + " - " + name + + " (" + item.size + ")"); + if (item.string != null) + System.out.println(" \"" + item.string + "\""); + else + System.out.println(); + + BytePtr p = lookup.getBytePtr(ptr, item.size); + + if (p == null) + System.out.println( + "can't find memory; recently allocated from free list?"); + else + p.dump(); + + item.points_to.dump(" points to:", lookup); + item.pointed_by.dump(" pointed to by:", lookup); + System.out.println(); + } + } + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + public static String kindToName(int kind) + { + String name; + switch (kind) + { + case 0: + name = "GC_PTRFREE"; + break; + case 1: + name = "GC_NORMAL"; + break; + case 2: + name = "GC_UNCOLLECTABLE"; + break; + case 3: + name = "GC_AUUNCOLLCTABLE"; + break; + case 4: + name = "(Java)"; + break; + case 5: + name = "(Java Debug)"; + break; + case 6: + name = "(Java Array)"; + break; + default: + name = "(Kind " + kind + ")"; + break; + } + return name; + } + + public static String getSymbolPretty(SymbolLookup lookup, + ObjectMap.ObjectItem item, + boolean bsize) + throws IOException + { + + String name = item.typeName; + + if (name == null) + name = lookup.getSymbol(item.klass); + + if (name == null) + { + String v = lookup.decodeUTF8(item.ptr, item.size); + if (null != v) + { + name = "UTF8Const"; + item.string = v; + } + } + + if (name == null) + { + name = kindToName(item.kind); + } + if (item.kind==6) + name += "[" + format(item.data, 0) + "]"; + if (bsize) + name = name + " / " + format(item.size, 7); + return name; + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java b/libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java new file mode 100644 index 0000000..1bc06d5 --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java @@ -0,0 +1,359 @@ +/* MemoryMap.java -- Maps address ranges to their data. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + + This software is copyrighted work licensed under the terms of the + Libgcj License. Please consult the file "LIBGCJ_LICENSE" for + details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.BufferedReader; +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.util.Comparator; +import java.util.HashMap; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * Reads /proc/self/maps output from dump file. + * Creates map of <filename> to Range. + * + * Returns filename given address. + * Returns offset given address. + * Returns BytePtr given address. + * + */ +class MemoryMap +{ + static class RangeComparator implements Comparator<Range> + { + public int compare(Range r1, Range r2) + { + if (r2.end == 0 && r1.end != 0) + return -compare(r2, r1); + + if (r1.begin < r2.begin) + return -1; + else if (r1.begin >= r2.end) + return 1; + else + return 0; + } + } + + static class Range + { + long begin; + long end; + + long offset; + String filename; + Range() + { + } + + Range(long b, long e, String s, long o) + { + begin = b; + end = e; + filename = s; + offset = o; + } + } + + /** + * Parse the string as an unsigned hexadecimal number. This is + * similar to Long.parseInt(s,16), but without the restriction that + * values that have the sign bit set not being allowed. + * + * @param s the number as a String. + * @return the number. + */ + static long parseHexLong(String s) + { + if (s.length() > 16) + throw new NumberFormatException(); + long r = 0; + for (int i = 0; i < s.length(); i++) + { + int digit = 0; + char c = s.charAt(i); + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + digit = c - '0'; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + digit = 10 + c - 'a'; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + digit = 10 + c - 'A'; + break; + default: + throw new NumberFormatException(); + } + r = (r << 4) + digit; + } + return r; + } + + // String filename -> Range + TreeSet<Range> map = new TreeSet<Range>(new RangeComparator()); + HashMap<String, SymbolTable> symbolTables = + new HashMap<String, SymbolTable>(); + ByteOrder byteOrder; + int wordSize; + + public MemoryMap(BufferedReader reader, + String rawFileName) throws IOException + { + FileChannel raw = (new RandomAccessFile(rawFileName, "r")).getChannel(); + ByteBuffer buf = ByteBuffer.allocate(8); + raw.read(buf); + if (buf.hasRemaining()) + { + raw.close(); + throw new EOFException(); + } + buf.flip(); + wordSize = buf.get(); + + if (wordSize == 8 || wordSize == 4) + byteOrder = ByteOrder.LITTLE_ENDIAN; + else + { + byteOrder = ByteOrder.BIG_ENDIAN; + buf.rewind(); + wordSize = buf.getInt(); + if (0 == wordSize) + wordSize = buf.getInt(); + } + switch (wordSize) + { + case 4: + case 8: + break; + default: + throw new IOException("Bad .bytes file header"); + } + buf = ByteBuffer.allocate(3 * wordSize); + buf.order(byteOrder); + raw.position(0L); + + for(;;) + { + // Read the block header. + buf.clear(); + if (-1 == raw.read(buf)) + { + //EOF + raw.close(); + break; + } + if (buf.hasRemaining()) + { + raw.close(); + throw new EOFException(); + } + buf.flip(); + long dummy + = (wordSize == 4) ? (buf.getInt() & 0xffffffffL) : buf.getLong(); + if (dummy != wordSize) + throw new IOException("Bad .bytes file header"); + long start + = wordSize == 4 ? (buf.getInt() & 0xffffffffL) : buf.getLong(); + long length + = wordSize == 4 ? (buf.getInt() & 0xffffffffL) : buf.getLong(); + if (length < 0L) + throw new IOException("Bad .bytes file header"); + + long currentPos = raw.position(); + raw.position(currentPos + length); + + Range range = new Range(start, start + length, + rawFileName, currentPos); + map.add(range); + } + + for (;;) + { + String s = reader.readLine(); + if (s == null) + break; + if (s.indexOf("Begin address map") >= 0) + { + for (;;) + { + s = reader.readLine(); + if (s.indexOf("End address map") >= 0) + { + dump(); + return; + } + int endOfAddress = s.indexOf('-'); + long address = parseHexLong(s.substring(0, endOfAddress)); + int endOfAddress2 = s.indexOf(' ', endOfAddress + 1); + long address2 = parseHexLong(s.substring(endOfAddress + 1, + endOfAddress2)); + int endOfOffset = s.indexOf(' ', endOfAddress2 + 6); + long offset; + try + { + offset = parseHexLong(s.substring(endOfAddress2 + 6, + endOfOffset)); + } + catch (Exception e) + { + offset = 0; + } + int end = s.indexOf('/'); + + if (end > 0) + { + String file = s.substring(end); + if (file.startsWith("/dev/")) + continue; + + Range r = new Range(address, address2, file, offset); + if (offset == 0) + { + // Read the file's symbol table + try + { + File f = ToolPrefix.fileForName(file); + if (f != null) + { + SymbolTable st = new SymbolTable(f.getPath()); + if (st.loadAddr != address) + st.relocation = address - st.loadAddr; + symbolTables.put(file, st); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + map.add(r); + } + } // inner loop + } // started inner loop + } // outer loop - finding begin + } // memoryMap + + + public void dump() + { + System.out.println("MemoryMap:"); + for (Range r : map) + { + System.out.println(Long.toHexString(r.begin) + "-" + + Long.toHexString(r.end) + " -> " + + r.filename + " offset " + + Long.toHexString(r.offset)); + } + } + + Range getRange(long addr) + { + Range r = new Range(); + r.begin = addr; + SortedSet<Range> t = map.tailSet(r); + if (t.isEmpty()) + return null; + Range c = t.first(); + if (c.begin <= addr && addr < c.end) + return c; + return null; + } + + String getFile(long addr) + { + Range r = getRange(addr); + if (null != r) + return r.filename; + return null; + } + + long getOffset(long addr) + { + Range r = getRange(addr); + if (null != r) + return r.offset; + return 0L; + } + + /** + * @return BytePtr which includes given address. + */ + BytePtr getBytePtr(long addr, int length) throws IOException + { + Range r = getRange(addr); + + if (null == r) + return null; + + File f = ToolPrefix.fileForName(r.filename); + if (null == f) + return null; + + if (addr + length > r.end) + length = (int)(r.end - addr); + + ByteBuffer b = ByteBuffer.allocate(length); + b.order(byteOrder); + + FileChannel fc = (new RandomAccessFile(f, "r")).getChannel(); + fc.position(r.offset + addr - r.begin); + int nr = fc.read(b); + fc.close(); + if (nr != length) + return null; + b.flip(); + return new BytePtr(b, wordSize); + } + + public String getSymbol(long addr) + { + Range r = getRange(addr); + + if (r == null) + return null; + + SymbolTable st = symbolTables.get(r.filename); + if (st == null) + return null; + + // Apply relocation + addr -= st.relocation; + + return st.getSymbol(addr); + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java b/libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java new file mode 100644 index 0000000..b55034b --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java @@ -0,0 +1,140 @@ +/* ObjectMap.java -- Contains a map of all objects keyed by their addresses. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + + This software is copyrighted work licensed under the terms of the + Libgcj License. Please consult the file "LIBGCJ_LICENSE" for + details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeMap; + +class ObjectMap implements Iterable<Map.Entry<Long, ObjectMap.ObjectItem>> +{ + + class ObjectItem + { + int used; + int size; + int kind; + long klass; + long data; + long ptr; + String typeName; + String string; // only for string objects + boolean stringData; // character array pointed to by a string + ObjectItem reference; // object at reference points to this + + ItemList points_to = new ItemList(); + ItemList pointed_by = new ItemList(); + } + + private TreeMap<Long, ObjectItem> map = new TreeMap<Long, ObjectItem>(); + + public Iterator<Map.Entry<Long, ObjectItem>> iterator() + { + return map.entrySet().iterator(); + } + + public ObjectItem get(long ptr) + { + ObjectItem item = map.get(ptr); + return item; + } + + public ObjectMap(BufferedReader reader) throws IOException + { + outer_loop: + for (;;) + { + String s = reader.readLine(); + if (s == null) + break; + if (s.indexOf("Begin object map") >= 0) + { + for (;;) + { + s = reader.readLine(); + if (s.indexOf("End object map") >= 0) + break outer_loop; + String[] items = s.split(","); + ObjectItem item = new ObjectItem(); + long ptr = 0; + for (int i=0; i<items.length; i++) + { + String[] x = items[i].split(" "); + String last = x[x.length-1]; + switch (i) + { + case 0: + item.used = Integer.parseInt(last); + break; + case 1: + ptr = MemoryMap.parseHexLong(last.substring(2)); + break; + case 2: + item.size = Integer.parseInt(last); + break; + case 3: + item.kind = Integer.parseInt(last); + break; + case 4: + if (last.length() > 1) + item.klass = + MemoryMap.parseHexLong(last.substring(2)); + else + item.klass = Integer.parseInt(last,16); + break; + case 5: + try + { + item.data = + Integer.parseInt(last.substring(2), 16); + } + catch (Exception e) + { + item.data = 0; + } + break; + } + } + item.ptr = ptr; + map.put(ptr, item); + } // inner loop + } // started inner loop + } // outer loop - finding begin + for (Map.Entry<Long, ObjectItem> me : this) + { + ObjectItem item = me.getValue(); + if (item.data != 0) + { + // see if data is a pointer to a block + ObjectItem referenced = map.get(item.data); + if (referenced != null) + { + referenced.reference = item; + } + } + } + } // memoryMap + + public void dump() + { + for (Map.Entry<Long, ObjectItem> me : this) + { + long ptr = me.getKey(); + ObjectItem item = me.getValue(); + System.out.println("ptr = " + Long.toHexString(ptr) + + ", size = " + item.size + + ", klass = " + Long.toHexString(item.klass) + + ", kind = " + item.kind + + ", data = " + item.data); + } + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java b/libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java new file mode 100644 index 0000000..b3963d8 --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java @@ -0,0 +1,112 @@ +/* SymbolLookup.java -- Finds class names by analyzing memory. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + + This software is copyrighted work licensed under the terms of the + Libgcj License. Please consult the file "LIBGCJ_LICENSE" for + details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.BufferedReader; +import java.io.IOException; + +class SymbolLookup +{ + MemoryMap memoryMap; + + public SymbolLookup(BufferedReader reader, + String rawFileName) + throws IOException + { + memoryMap = new MemoryMap(reader, rawFileName); + } + + public String decodeUTF8(long address) throws IOException + { + return decodeUTF8(address, -1); + } + + public String decodeUTF8(long address, int limit) throws IOException + { + if (address == 0) + return null; + + BytePtr utf8 = memoryMap.getBytePtr(address, 64); + + if (utf8 == null) + return null; + + int len = utf8.getShort(1); + int hash16 = utf8.getShort(0) & 0xffff; + + if (len <= 0 || (limit > 0 && len > (limit - 4))) + return null; + + if (len > utf8.getsize() + 4) + utf8 = memoryMap.getBytePtr(address, len + 4); + + if (utf8 == null) + return null; + + StringBuilder sb = new StringBuilder(len); + int pos = 4; + len += 4; + + while (pos < len) + { + int f = utf8.getByte(pos++); + if ((f & 0x80) == 0) + { + sb.append((char)f); + } + else if ((f & 0xe0) == 0xc0) + { + int s = utf8.getByte(pos++); + char c = (char)(((f & 0x1f) << 6) | (s & 0x80)); + sb.append(c); + } + else if ((f & 0xe0) == 0xe0) + { + int s = utf8.getByte(pos++); + int t = utf8.getByte(pos++); + char c = (char)(((f & 0x0f) << 12) + | ((s & 0x80) << 6) | (t & 0x80)); + sb.append(c); + } + else + break; // Bad utf8 + } + String rv = sb.toString(); + if (hash16 == (rv.hashCode() & 0xffff)) + return rv; + else + return null; + } + + public String getSymbolViaVtable(long address) throws IOException + { + return memoryMap.getSymbol(address); + } + + public String getSymbol(long address) throws IOException + { + String symbol = memoryMap.getSymbol(address); + if (null != symbol) + return symbol; + + BytePtr klass = memoryMap.getBytePtr(address, 3 * memoryMap.wordSize); + if (klass == null) + return null; + + long nameUTF8p = klass.getWord(2); + + return decodeUTF8(nameUTF8p); + } + + BytePtr getBytePtr(long addr, int length) throws IOException + { + return memoryMap.getBytePtr(addr, length); + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java b/libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java new file mode 100644 index 0000000..eb5df76 --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java @@ -0,0 +1,198 @@ +/* SymbolTable.java -- Maintains a mapping of addresses to names. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + + This software is copyrighted work licensed under the terms of the + Libgcj License. Please consult the file "LIBGCJ_LICENSE" for + details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class SymbolTable +{ + // Long address->String name + private HashMap<Long, String> map = new HashMap<Long, String>(); + + // Reverse + // String name -> Long address + // used for RelocateImage + private HashMap<String, Long> reverse = new HashMap<String, Long>(); + + long loadAddr; + long relocation; + + static Matcher interestingSymbol = + Pattern.compile("^([0-9a-fA-F]+)\\s+\\S+\\s+(_Z\\S+)").matcher(""); + static Matcher readelfLoadMatcher = + Pattern.compile("^\\s+LOAD\\s+(\\S+)\\s+(\\S+)\\s.*").matcher(""); + + public SymbolTable(String filename) throws IOException + { + Process p = Runtime.getRuntime().exec(ToolPrefix.toolPrefix + + "nm " + filename); + InputStream es = p.getErrorStream(); + InputStream is = p.getInputStream(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + int count = 0; + + String line; + while ((line = reader.readLine()) != null) + { + interestingSymbol.reset(line); + if (interestingSymbol.matches()) + { + try + { + String name = interestingSymbol.group(2); + String addr = interestingSymbol.group(1); + if (name.startsWith("_ZTVN") || name.endsWith("6class$E")) + { + long address = MemoryMap.parseHexLong(addr); + Long l = new Long(address); + map.put(l, name); + count++; + reverse.put(name, l); + } + } + catch (NumberFormatException e) + { + // ignore it + } + } + } + es.close(); + is.close(); + p.destroy(); + + if (count > 0) + { + // Assume nm read some symbols from it and that + // readelf can tell us something about how it is loaded. + p = Runtime.getRuntime().exec(ToolPrefix.toolPrefix + + "readelf -l " + filename); + es = p.getErrorStream(); + is = p.getInputStream(); + + reader = new BufferedReader(new InputStreamReader(is)); + while ((line = reader.readLine()) != null) + { + readelfLoadMatcher.reset(line); + if (readelfLoadMatcher.matches()) + { + loadAddr + = Long.decode(readelfLoadMatcher.group(2)).longValue(); + break; + } + } + es.close(); + is.close(); + p.destroy(); + } + + System.out.println(ToolPrefix.toolPrefix + "nm " + filename + + " -> " + count + " symbols"); + } + + public static void main(String args[]) + { + try + { + SymbolTable st = new SymbolTable(args[0]); + st.dump(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + public static String demangleVTName(String n) + { + if (n.startsWith("_ZTVN") && n.endsWith("E")) + return demangle(n.substring(5, n.length() - 1)); + else + return null; + } + + public void dump() + { + for (Map.Entry<Long, String> me : map.entrySet()) + { + long address = me.getKey(); + String symbol = me.getValue(); + System.out.println(Long.toHexString(address) + " -> " + symbol); + if (symbol.startsWith("_ZN") && symbol.endsWith("6class$E")) + { + System.out.println(" Class: " + + demangle(symbol.substring(3, symbol.length() + - 8))); + } + else if (symbol.startsWith("_ZTVN") && symbol.endsWith("E")) + { + System.out.println(" VT: " + + demangle(symbol.substring(5, symbol.length() + - 1))); + } + } + } + + private static String demangle(String symbol) + { + StringBuilder sb = new StringBuilder(); + for (int i=0; i<symbol.length(); ) + { + int l = 0; + while (i < symbol.length()) + { + int d = symbol.charAt(i); + if (d < '0' || d > '9') + break; + l = 10 * l + (d - '0'); + i++; + } + if (l == 0) + break; + // copy + if (sb.length() > 0) + sb.append('.'); + while (l > 0 && i < symbol.length()) + { + sb.append(symbol.charAt(i)); + l--; + i++; + } + } + return sb.toString(); + } + + public String getSymbol(long address) + { + String symbol = map.get(address); + if (symbol == null) + return null; + + if (symbol.startsWith("_ZN") && symbol.endsWith("6class$E")) + symbol = demangle(symbol.substring(3, symbol.length() - 8)); + return symbol; + } + + // will return -1 if not found + public long getAddress(String symbol) + { + Long address = reverse.get(symbol); + if (address == null) + return -1; + return address.longValue(); + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java b/libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java new file mode 100644 index 0000000..e8d73ae --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java @@ -0,0 +1,45 @@ +/* ToolPrefix.java -- Container of the toolPrefix String. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.File; + +class ToolPrefix +{ + /** + * Private constructor. No creation allowed. This class has + * Static methods only. + */ + private ToolPrefix() + { + } + + static String toolPrefix = ""; + + static String pathPrefix = ""; + + static File fileForName(String filename) + { + File f = new File(pathPrefix + filename); + if (!f.canRead()) + { + // Try it without the prefix. + f = new File(filename); + if (!f.canRead()) + { + // Try to find it in the current directory. + f = new File(f.getName()); + if (!f.canRead()) + return null; + } + } + return f; + } +} diff --git a/libjava/gnu/gcj/util/GCInfo.h b/libjava/gnu/gcj/util/GCInfo.h new file mode 100644 index 0000000..06e0dd0 --- /dev/null +++ b/libjava/gnu/gcj/util/GCInfo.h @@ -0,0 +1,45 @@ + +// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*- + +#ifndef __gnu_gcj_util_GCInfo__ +#define __gnu_gcj_util_GCInfo__ + +#pragma interface + +#include <java/lang/Object.h> +extern "Java" +{ + namespace gnu + { + namespace gcj + { + namespace util + { + class GCInfo; + } + } + } +} + +class gnu::gcj::util::GCInfo : public ::java::lang::Object +{ + + GCInfo(); + static void checkPermission(); +public: + static void dump(::java::lang::String *); +private: + static void dump0(::java::lang::String *); +public: + static void enumerate(::java::lang::String *); +private: + static void enumerate0(::java::lang::String *); +public: + static void setOOMDump(::java::lang::String *); +private: + static void setOOMDump0(::java::lang::String *); +public: + static ::java::lang::Class class$; +}; + +#endif // __gnu_gcj_util_GCInfo__ diff --git a/libjava/gnu/gcj/util/GCInfo.java b/libjava/gnu/gcj/util/GCInfo.java new file mode 100644 index 0000000..73f4718 --- /dev/null +++ b/libjava/gnu/gcj/util/GCInfo.java @@ -0,0 +1,79 @@ +/* GCInfo.java -- Support for creating heap dumps. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + + This software is copyrighted work licensed under the terms of the + Libgcj License. Please consult the file "LIBGCJ_LICENSE" for + details. */ + +package gnu.gcj.util; + +public class GCInfo +{ + private GCInfo() + { + } + + /** + * @throws SecurityException if there is a SecurityManager installed + * and UtilPermission("dumpHeap") is not granted. + */ + private static void checkPermission() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new UtilPermission("dumpHeap")); + } + + + /** + * Dump a description of the heap state. + * + * @param namePrefix The filename prefix for the dump files. + * + * @throws SecurityException if there is a SecurityManager installed + * and UtilPermission("dumpHeap") is not granted. + */ + public static synchronized void dump(String name) + { + checkPermission(); + dump0(name); + } + + private static native void dump0(String name); + + + /** + * Create a heap dump. + * + * @param namePrefix The filename prefix for the dump files. + * + * @throws SecurityException if there is a SecurityManager installed + * and UtilPermission("dumpHeap") is not granted. + */ + public static synchronized void enumerate(String namePrefix) + { + checkPermission(); + enumerate0(namePrefix); + } + + private static native void enumerate0(String namePrefix); + + /** + * Cause a heap dump if out-of-memory condition occurs. + * + * @param namePrefix The filename prefix for the dump files. If + * null no dumps are created. + * + * @throws SecurityException if there is a SecurityManager installed + * and UtilPermission("dumpHeap") is not granted. + */ + public static synchronized void setOOMDump(String namePrefix) + { + checkPermission(); + setOOMDump0(namePrefix); + } + + private static native void setOOMDump0(String namePrefix); +} diff --git a/libjava/gnu/gcj/util/UtilPermission.h b/libjava/gnu/gcj/util/UtilPermission.h new file mode 100644 index 0000000..4c7b226 --- /dev/null +++ b/libjava/gnu/gcj/util/UtilPermission.h @@ -0,0 +1,32 @@ + +// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*- + +#ifndef __gnu_gcj_util_UtilPermission__ +#define __gnu_gcj_util_UtilPermission__ + +#pragma interface + +#include <java/security/BasicPermission.h> +extern "Java" +{ + namespace gnu + { + namespace gcj + { + namespace util + { + class UtilPermission; + } + } + } +} + +class gnu::gcj::util::UtilPermission : public ::java::security::BasicPermission +{ + +public: + UtilPermission(::java::lang::String *); + static ::java::lang::Class class$; +}; + +#endif // __gnu_gcj_util_UtilPermission__ diff --git a/libjava/gnu/gcj/util/UtilPermission.java b/libjava/gnu/gcj/util/UtilPermission.java new file mode 100644 index 0000000..1ea4cb7 --- /dev/null +++ b/libjava/gnu/gcj/util/UtilPermission.java @@ -0,0 +1,20 @@ +/* GCInfo.java -- Support for creating heap dumps. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + + This software is copyrighted work licensed under the terms of the + Libgcj License. Please consult the file "LIBGCJ_LICENSE" for + details. */ + +package gnu.gcj.util; + +import java.security.BasicPermission; + +public class UtilPermission extends BasicPermission +{ + public UtilPermission(String name) + { + super(name); + } +} diff --git a/libjava/gnu/gcj/util/natGCInfo.cc b/libjava/gnu/gcj/util/natGCInfo.cc new file mode 100644 index 0000000..7e5c6fb --- /dev/null +++ b/libjava/gnu/gcj/util/natGCInfo.cc @@ -0,0 +1,454 @@ +/* natGCInfo.cc -- Native portion of support for creating heap dumps. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + + This software is copyrighted work licensed under the terms of the + Libgcj License. Please consult the file "LIBGCJ_LICENSE" for + details. */ + + +#include <config.h> + +#include <gcj/cni.h> + +#include <gnu/gcj/util/GCInfo.h> + +#ifdef HAVE_PROC_SELF_MAPS +// +// If /proc/self/maps does not exist we assume we are doomed and do nothing. +// +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +// +// Boehm GC includes. +// +#ifdef PACKAGE_NAME +#undef PACKAGE_NAME +#endif + +#ifdef PACKAGE_STRING +#undef PACKAGE_STRING +#endif + +#ifdef PACKAGE_TARNAME +#undef PACKAGE_TARNAME +#endif + +#ifdef PACKAGE_VERSION +#undef PACKAGE_VERSION +#endif + +#ifdef TRUE +#undef TRUE +#endif + +#ifdef FALSE +#undef FALSE +#endif + +extern "C" { +#include "private/dbg_mlc.h" + int GC_n_set_marks(hdr* hhdr); + ptr_t GC_clear_stack(ptr_t p); + extern int GC_gcj_kind; + extern int GC_gcj_debug_kind; +} + +#endif + +#ifdef HAVE_PROC_SELF_MAPS + +static int gc_ok = 1; + +typedef struct gc_debug_info +{ + int used; + int free; + int wasted; + int blocks; + FILE* fp; +}; + +static void +GC_print_debug_callback(hblk *h, word user_data) +{ + hdr *hhdr = HDR(h); + size_t bytes = WORDS_TO_BYTES(hhdr -> hb_sz); + + gc_debug_info *pinfo = (gc_debug_info *)user_data; + + fprintf(pinfo->fp, "ptr = %#lx, kind = %d, size = %zd, marks = %d\n", + (unsigned long)h, hhdr->hb_obj_kind, bytes, GC_n_set_marks(hhdr)); +} + +/* + this next section of definitions shouldn't really be here. + copied from boehmgc/allchblk.c +*/ + +# define UNIQUE_THRESHOLD 32 +# define HUGE_THRESHOLD 256 +# define FL_COMPRESSION 8 +# define N_HBLK_FLS (HUGE_THRESHOLD - UNIQUE_THRESHOLD)/FL_COMPRESSION \ + + UNIQUE_THRESHOLD +#ifndef USE_MUNMAP +extern "C" { + extern word GC_free_bytes[N_HBLK_FLS+1]; +} +#endif + +# ifdef USE_MUNMAP +# define IS_MAPPED(hhdr) (((hhdr) -> hb_flags & WAS_UNMAPPED) == 0) +# else /* !USE_MMAP */ +# define IS_MAPPED(hhdr) 1 +# endif /* USE_MUNMAP */ + +static void +GC_print_hblkfreelist_file(FILE *fp) +{ + struct hblk * h; + word total_free = 0; + hdr * hhdr; + word sz; + int i; + + fprintf(fp, "---------- Begin free map ----------\n"); + for (i = 0; i <= N_HBLK_FLS; ++i) + { + h = GC_hblkfreelist[i]; +#ifdef USE_MUNMAP + if (0 != h) + fprintf (fp, "Free list %ld:\n", (unsigned long)i); +#else + if (0 != h) + fprintf (fp, "Free list %ld (Total size %ld):\n", + (unsigned long)i, + (unsigned long)GC_free_bytes[i]); +#endif + while (h != 0) + { + hhdr = HDR(h); + sz = hhdr -> hb_sz; + fprintf (fp, "\t0x%lx size %lu ", (unsigned long)h, + (unsigned long)sz); + total_free += sz; + + if (GC_is_black_listed (h, HBLKSIZE) != 0) + fprintf (fp, "start black listed\n"); + else if (GC_is_black_listed(h, hhdr -> hb_sz) != 0) + fprintf (fp, "partially black listed\n"); + else + fprintf (fp, "not black listed\n"); + + h = hhdr -> hb_next; + } + } +#ifndef USE_MUNMAP + if (total_free != GC_large_free_bytes) + { + fprintf (fp, "GC_large_free_bytes = %lu (INCONSISTENT!!)\n", + (unsigned long) GC_large_free_bytes); + } +#endif + fprintf (fp, "Total of %lu bytes on free list\n", (unsigned long)total_free); + fprintf (fp, "---------- End free map ----------\n"); +} + +static int GC_dump_count = 1; + +static void +GC_print_debug_info_file(FILE* fp) +{ + gc_debug_info info; + + memset(&info, 0, sizeof info); + info.fp = fp; + + if (gc_ok) + GC_gcollect(); + fprintf(info.fp, "---------- Begin block map ----------\n"); + GC_apply_to_all_blocks(GC_print_debug_callback, (word)(void*)(&info)); + //fprintf(fp, "#Total used %d free %d wasted %d\n", info.used, info.free, info.wasted); + //fprintf(fp, "#Total blocks %d; %dK bytes\n", info.blocks, info.blocks*4); + fprintf(info.fp, "---------- End block map ----------\n"); + + //fprintf(fp, "\n***Free blocks:\n"); + //GC_print_hblkfreelist(); +} + +namespace +{ + class __attribute__ ((visibility ("hidden"))) GC_enumerator + { + public: + GC_enumerator(const char *name); + void enumerate(); + private: + FILE* fp; + int bytes_fd; + + void print_address_map(); + void enumerate_callback(struct hblk *h); + static void enumerate_callback_adaptor(struct hblk *h, word dummy); + }; +} + +GC_enumerator::GC_enumerator(const char *name) +{ + bytes_fd = -1; + fp = fopen (name, "w"); + if (!fp) + { + printf ("GC_enumerator failed to open [%s]\n", name); + return; + } + printf ("GC_enumerator saving summary to [%s]\n", name); + + // open heap file + char bytes_name[strlen(name) + 10]; + sprintf (bytes_name, "%s.bytes", name); + bytes_fd = open (bytes_name, O_CREAT|O_TRUNC|O_WRONLY, 0666); + if (bytes_fd <= 0) + { + printf ("GC_enumerator failed to open [%s]\n", bytes_name); + return; + } + printf ("GC_enumerator saving heap contents to [%s]\n", bytes_name); +} + +/* + sample format of /proc/self/maps + + 0063b000-00686000 rw-p 001fb000 03:01 81993 /avtrex/bin/dumppropapp + 00686000-0072e000 rwxp 00000000 00:00 0 + + These are parsed below as: + start -end xxxx xxxxxxxx a:b xxxxxxxxxxxxxxx + +*/ + + +void +GC_enumerator::print_address_map() +{ + FILE* fm; + char buffer[128]; + + fprintf(fp, "---------- Begin address map ----------\n"); + + fm = fopen("/proc/self/maps", "r"); + if (fm == NULL) + { + if (0 == strerror_r (errno, buffer, sizeof buffer)) + fputs (buffer, fp); + } + else + { + while (fgets (buffer, sizeof buffer, fm) != NULL) + { + fputs (buffer, fp); + char *dash = strchr(buffer, '-'); + char *colon = strchr(buffer, ':'); + if (dash && colon && ((ptrdiff_t)strlen(buffer) > (colon - buffer) + 2)) + { + char *endp; + unsigned long start = strtoul(buffer, NULL, 16); + unsigned long end = strtoul(dash + 1, &endp, 16); + unsigned long a = strtoul(colon - 2, NULL, 16); + unsigned long b = strtoul(colon + 1, NULL, 16); + // If it is an anonymous mapping 00:00 and both readable + // and writeable then dump the contents of the mapping + // to the bytes file. Each block has a header of three + // unsigned longs: + // 0 - The number sizeof(unsigned long) to detect endianness and + // structure layout. + // 1 - The offset in VM. + // 2 - The Length in bytes. + // Followed by the bytes. + if (!a && !b && endp < colon && 'r' == endp[1] && 'w' == endp[2]) + { + unsigned long t = sizeof(unsigned long); + write(bytes_fd, (void*)&t, sizeof(t)); + write(bytes_fd, (void*)&start, sizeof(start)); + t = end - start; + write(bytes_fd, (void*)&t, sizeof(t)); + write(bytes_fd, (void*)start, (end - start)); + } + } + } + fclose(fm); + } + fprintf(fp, "---------- End address map ----------\n"); + fflush(fp); +} + +void +GC_enumerator::enumerate() +{ + print_address_map(); + fprintf(fp, "---------- Begin object map ----------\n"); + if (gc_ok) + GC_gcollect(); + GC_apply_to_all_blocks(enumerate_callback_adaptor, + (word)(void*)(this)); + fprintf(fp, "---------- End object map ----------\n"); + fflush(fp); + + GC_print_debug_info_file(fp); + fflush(fp); + GC_print_hblkfreelist_file(fp); + fflush(fp); + + close(bytes_fd); + fclose(fp); + + GC_clear_stack(0); +} + +void +GC_enumerator::enumerate_callback_adaptor(struct hblk *h, + word dummy) +{ + GC_enumerator* pinfo = (GC_enumerator*)dummy; + pinfo->enumerate_callback(h); +} + +void +GC_enumerator::enumerate_callback(struct hblk *h) +{ + hdr * hhdr = HDR(h); + size_t bytes = WORDS_TO_BYTES(hhdr->hb_sz); + int i; + + for (i = 0; i == 0 || (i + bytes <= HBLKSIZE); i += bytes) + { + int inUse = mark_bit_from_hdr(hhdr,BYTES_TO_WORDS(i)); // in use + char *ptr = (char*)h+i; // address + int kind = hhdr->hb_obj_kind; // kind + void *klass = 0; + void *data = 0; + if (kind == GC_gcj_kind + || kind == GC_gcj_debug_kind + || kind == GC_gcj_debug_kind+1) + { + void* v = *(void **)ptr; + if (v) + { + klass = *(void **)v; + data = *(void **)(ptr + sizeof(void*)); + } + } + if (inUse) + fprintf (fp, "used = %d, ptr = %#lx, size = %zd, kind = %d, " + "klass = %#lx, data = %#lx\n", + inUse, (unsigned long)ptr, bytes, kind, + (unsigned long)klass, (unsigned long)data); + } +} + +/* + * Fill in a char[] with low bytes of the string characters. These + * methods may be called while an OutOfMemoryError is being thrown, so + * we cannot call nice java methods to get the encoding of the string. + */ +static void +J2A(::java::lang::String* str, char *dst) +{ + jchar * pchars = JvGetStringChars(str); + jint len = str->length(); + int i; + for (i=0; i<len; i++) + dst[i] = (char)pchars[i]; + dst[i] = 0; +} + +void +::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name) +{ + char n[name->length() + 1]; + J2A(name, n); + + char temp[name->length() + 20]; + sprintf(temp, "%s%03d", n, GC_dump_count++); + FILE* fp = fopen(temp, "w"); + + GC_print_debug_info_file(fp); + + fclose(fp); +} + +void +::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name) +{ + char n[name->length() + 1]; + J2A(name, n); + char temp[name->length() + 20]; + sprintf(temp, "%s%03d", n, GC_dump_count++); + + GC_enumerator x(temp); + x.enumerate(); +} + +static char *oomDumpName = NULL; + +static void * +nomem_handler(size_t size) +{ + if (oomDumpName) + { + char temp[strlen(oomDumpName) + 20]; + sprintf(temp, "%s%03d", temp, GC_dump_count++); + printf("nomem_handler(%zd) called\n", size); + gc_ok--; + GC_enumerator x(temp); + x.enumerate(); + gc_ok++; + } + return (void*)0; +} + +void +::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name) +{ + char *oldName = oomDumpName; + oomDumpName = NULL; + free (oldName); + + if (NULL == name) + return; + + char *n = (char *)malloc(name->length() + 1); + + J2A(name, n); + oomDumpName = n; + GC_oom_fn = nomem_handler; +} + +#else // HAVE_PROC_SELF_MAPS + +void +::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name) +{ + // Do nothing if dumping not supported. +} + +void +::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name) +{ + // Do nothing if dumping not supported. +} + +void +::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name) +{ + // Do nothing if dumping not supported. +} + +#endif // HAVE_PROC_SELF_MAPS + |