Mercurial > hg > Members > kono > jpf-core
diff src/main/gov/nasa/jpf/listener/HeapTracker.java @ 0:61d41facf527
initial v8 import (history reset)
author | Peter Mehlitz <Peter.C.Mehlitz@nasa.gov> |
---|---|
date | Fri, 23 Jan 2015 10:14:01 -0800 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/gov/nasa/jpf/listener/HeapTracker.java Fri Jan 23 10:14:01 2015 -0800 @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2014, United States Government, as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All rights reserved. + * + * The Java Pathfinder core (jpf-core) platform is licensed 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 gov.nasa.jpf.listener; + +import gov.nasa.jpf.Config; +import gov.nasa.jpf.JPF; +import gov.nasa.jpf.PropertyListenerAdapter; +import gov.nasa.jpf.report.ConsolePublisher; +import gov.nasa.jpf.report.Publisher; +import gov.nasa.jpf.search.Search; +import gov.nasa.jpf.util.DynamicObjectArray; +import gov.nasa.jpf.util.Misc; +import gov.nasa.jpf.util.SourceRef; +import gov.nasa.jpf.util.StringSetMatcher; +import gov.nasa.jpf.vm.ClassInfo; +import gov.nasa.jpf.vm.ElementInfo; +import gov.nasa.jpf.vm.Heap; +import gov.nasa.jpf.vm.VM; +import gov.nasa.jpf.vm.MethodInfo; +import gov.nasa.jpf.vm.ThreadInfo; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +/** + * HeapTracker - property-listener class to check heap utilization along all + * execution paths (e.g. to verify heap bounds) + */ +public class HeapTracker extends PropertyListenerAdapter { + + static class PathStat implements Cloneable { + int nNew = 0; + int nReleased = 0; + int heapSize = 0; // in bytes + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } + } + + static class TypeStat { + String typeName; + int nAlloc; + int nReleased; + + TypeStat (String typeName){ + this.typeName = typeName; + } + } + + PathStat stat = new PathStat(); + Stack<PathStat> pathStats = new Stack<PathStat>(); + + DynamicObjectArray<SourceRef> loc = new DynamicObjectArray<SourceRef>(); + + HashMap<String,TypeStat> typeStat = new HashMap<String,TypeStat>(); + + int maxState; + int nForward; + int nBacktrack; + + int nElemTotal; + int nGcTotal; + int nSharedTotal; + int nImmutableTotal; + + int nElemMax = Integer.MIN_VALUE; + int nElemMin = Integer.MAX_VALUE; + int nElemAv; + + int pElemSharedMax = Integer.MIN_VALUE; + int pElemSharedMin = Integer.MAX_VALUE; + int pElemSharedAv; + + int pElemImmutableMax = Integer.MIN_VALUE; + int pElemImmutableMin = Integer.MAX_VALUE; + int pElemImmutableAv; + + int nReleased; + int nReleasedTotal; + int nReleasedAv; + int nReleasedMax = Integer.MIN_VALUE; + int nReleasedMin = Integer.MAX_VALUE; + + int maxPathHeap = Integer.MIN_VALUE; + int maxPathNew = Integer.MIN_VALUE; + int maxPathReleased = Integer.MIN_VALUE; + int maxPathAlive = Integer.MIN_VALUE; + + int initHeap = 0; + int initNew = 0; + int initReleased = 0; + int initAlive = 0; + + + boolean showTypeStats; + int maxTypesShown; + + // used as a property check + int maxHeapSizeLimit; + int maxLiveLimit; + boolean throwOutOfMemory = false; + + StringSetMatcher includes, excludes; + + void updateMaxPathValues() { + if (stat.heapSize > maxPathHeap) { + maxPathHeap = stat.heapSize; + } + + if (stat.nNew > maxPathNew) { + maxPathNew = stat.nNew; + } + + if (stat.nReleased > maxPathReleased) { + maxPathReleased = stat.nReleased; + } + + int nAlive = stat.nNew - stat.nReleased; + if (nAlive > maxPathAlive) { + maxPathAlive = nAlive; + } + } + + void allocTypeStats (ElementInfo ei) { + String typeName = ei.getClassInfo().getName(); + TypeStat ts = typeStat.get(typeName); + if (ts == null) { + ts = new TypeStat(typeName); + typeStat.put(typeName, ts); + } + ts.nAlloc++; + } + + void releaseTypeStats (ElementInfo ei) { + String typeName = ei.getClassInfo().getName(); + TypeStat ts = typeStat.get(typeName); + if (ts != null) { + ts.nReleased++; + } + } + + + public HeapTracker (Config config, JPF jpf) { + maxHeapSizeLimit = config.getInt("heap.size_limit", -1); + maxLiveLimit = config.getInt("heap.live_limit", -1); + throwOutOfMemory = config.getBoolean("heap.throw_exception"); + showTypeStats = config.getBoolean("heap.show_types"); + maxTypesShown = config.getInt("heap.max_types", 20); + + includes = StringSetMatcher.getNonEmpty(config.getStringArray("heap.include")); + excludes = StringSetMatcher.getNonEmpty(config.getStringArray("heap.exclude")); + + jpf.addPublisherExtension(ConsolePublisher.class, this); + } + + /******************************************* abstract Property *****/ + + /** + * return 'false' if property is violated + */ + @Override + public boolean check (Search search, VM vm) { + if (throwOutOfMemory) { + // in this case we don't want to stop the program, but see if it + // behaves gracefully - don't report a property violation + return true; + } else { + if ((maxHeapSizeLimit >= 0) && (stat.heapSize > maxHeapSizeLimit)) { + return false; + } + if ((maxLiveLimit >=0) && ((stat.nNew - stat.nReleased) > maxLiveLimit)) { + return false; + } + + return true; + } + } + + @Override + public String getErrorMessage () { + return "heap limit exceeded: " + stat.heapSize + " > " + maxHeapSizeLimit; + } + + /******************************************* SearchListener interface *****/ + @Override + public void searchStarted(Search search) { + super.searchStarted(search); + + updateMaxPathValues(); + pathStats.push(stat); + + initHeap = stat.heapSize; + initNew = stat.nNew; + initReleased = stat.nReleased; + initAlive = initNew - initReleased; + + stat = (PathStat)stat.clone(); + } + + @Override + public void stateAdvanced(Search search) { + + if (search.isNewState()) { + int id = search.getStateId(); + + if (id > maxState) maxState = id; + + updateMaxPathValues(); + pathStats.push(stat); + stat = (PathStat)stat.clone(); + + nForward++; + } + } + + @Override + public void stateBacktracked(Search search) { + nBacktrack++; + + if (!pathStats.isEmpty()){ + stat = pathStats.pop(); + } + } + + /******************************************* PublisherExtension interface ****/ + @Override + public void publishFinished (Publisher publisher) { + PrintWriter pw = publisher.getOut(); + publisher.publishTopicStart("heap statistics"); + + pw.println("heap statistics:"); + pw.println(" states: " + maxState); + pw.println(" forwards: " + nForward); + pw.println(" backtrack: " + nBacktrack); + pw.println(); + pw.println(" gc cycles: " + nGcTotal); + pw.println(); + pw.println(" max Objects: " + nElemMax); + pw.println(" min Objects: " + nElemMin); + pw.println(" avg Objects: " + nElemAv); + pw.println(); + pw.println(" max% shared: " + pElemSharedMax); + pw.println(" min% shared: " + pElemSharedMin); + pw.println(" avg% shared: " + pElemSharedAv); + pw.println(); + pw.println(" max% immutable: " + pElemImmutableMax); + pw.println(" min% immutable: " + pElemImmutableMin); + pw.println(" avg% immutable: " + pElemImmutableAv); + pw.println(); + pw.println(" max released: " + nReleasedMax); + pw.println(" min released: " + nReleasedMin); + pw.println(" avg released: " + nReleasedAv); + + pw.println(); + pw.print( " max path heap (B): " + maxPathHeap); + pw.println(" / " + (maxPathHeap - initHeap)); + pw.print( " max path alive: " + maxPathAlive); + pw.println(" / " + (maxPathAlive - initAlive)); + pw.print( " max path new: " + maxPathNew); + pw.println(" / " + (maxPathNew - initNew)); + pw.print( " max path released: " + maxPathReleased); + pw.println(" / " + (maxPathReleased - initReleased)); + + if (showTypeStats) { + pw.println(); + pw.println(" type allocation statistics:"); + + ArrayList<Map.Entry<String,TypeStat>> list = + Misc.createSortedEntryList(typeStat, new Comparator<Map.Entry<String,TypeStat>>() { + @Override + public int compare (Map.Entry<String,TypeStat> e1, + Map.Entry<String,TypeStat> e2) { + return Integer.signum(e1.getValue().nAlloc - e2.getValue().nAlloc); + }}); + + int i=0; + for (Map.Entry<String,TypeStat> e : list) { + TypeStat ts = e.getValue(); + pw.print(" "); + pw.print(String.format("%1$9d : ", ts.nAlloc)); + pw.println(ts.typeName); + + if (i++ > maxTypesShown) { + pw.println(" ..."); + break; + } + } + } + } + + + /******************************************* VMListener interface *********/ + @Override + public void gcBegin(VM vm) { + /** + System.out.println(); + System.out.println( "----- gc cycle: " + vm.getDynamicArea().getGcNumber() + + ", state: " + vm.getStateId()); + **/ + } + + @Override + public void gcEnd(VM vm) { + Heap heap = vm.getHeap(); + + int n = 0; + int nShared = 0; + int nImmutable = 0; + + for (ElementInfo ei : heap.liveObjects()) { + n++; + + if (ei.isShared()) nShared++; + if (ei.isImmutable()) nImmutable++; + + //printElementInfo(ei); + } + + nElemTotal += n; + nGcTotal++; + + if (n > nElemMax) nElemMax = n; + if (n < nElemMin) nElemMin = n; + + int pShared = (nShared * 100) / n; + int pImmutable = (nImmutable * 100) / n; + + if (pShared > pElemSharedMax) pElemSharedMax = pShared; + if (pShared < pElemSharedMin) pElemSharedMin = pShared; + + nSharedTotal += nShared; + nImmutableTotal += nImmutable; + + pElemSharedAv = (nSharedTotal * 100) / nElemTotal; + pElemImmutableAv = (nImmutableTotal * 100) / nElemTotal; + + if (pImmutable > pElemImmutableMax) pElemImmutableMax = pImmutable; + if (pImmutable < pElemImmutableMin) pElemImmutableMin = pImmutable; + + nElemAv = nElemTotal / nGcTotal; + nReleasedAv = nReleasedTotal / nGcTotal; + + if (nReleased > nReleasedMax) nReleasedMax = nReleased; + if (nReleased < nReleasedMin) nReleasedMin = nReleased; + + nReleased = 0; + } + + boolean isRelevantType (ElementInfo ei) { + String clsName = ei.getClassInfo().getName(); + return StringSetMatcher.isMatch(clsName, includes, excludes); + } + + @Override + public void objectCreated(VM vm, ThreadInfo ti, ElementInfo ei) { + int idx = ei.getObjectRef(); + int line = ti.getLine(); + MethodInfo mi = ti.getTopFrameMethodInfo(); + SourceRef sr = null; + + if (!isRelevantType(ei)) { + return; + } + + if (mi != null) { + ClassInfo mci = mi.getClassInfo(); + if (mci != null) { + String file = mci.getSourceFileName(); + if (file != null) { + sr = new SourceRef(file, line); + } else { + sr = new SourceRef(mci.getName(), line); + } + } + } + + // means references with null loc are from synthetic methods + loc.set(idx, sr); + + stat.nNew++; + stat.heapSize += ei.getHeapSize(); + + // update the type statistics + if (showTypeStats) { + allocTypeStats(ei); + } + + + // check if we should simulate an OutOfMemoryError + if (throwOutOfMemory) { + if (((maxHeapSizeLimit >=0) && (stat.heapSize > maxHeapSizeLimit)) || + ((maxLiveLimit >=0) && ((stat.nNew - stat.nReleased) > maxLiveLimit))){ + vm.getHeap().setOutOfMemory(true); + } + } + } + + @Override + public void objectReleased(VM vm, ThreadInfo ti, ElementInfo ei) { + + if (!isRelevantType(ei)) { + return; + } + + nReleasedTotal++; + nReleased++; + + if (showTypeStats) { + releaseTypeStats(ei); + } + + stat.nReleased++; + stat.heapSize -= ei.getHeapSize(); + } + + /****************************************** private stuff ******/ + protected void printElementInfo(ElementInfo ei) { + boolean first = false; + + System.out.print( ei.getObjectRef()); + System.out.print( ": "); + System.out.print( ei.getClassInfo().getName()); + System.out.print( " ["); + + if (ei.isShared()) { + System.out.print( "shared"); + first = false; + } + if (ei.isImmutable()) { + if (!first) System.out.print(' '); + System.out.print( "immutable"); + } + System.out.print( "] "); + + SourceRef sr = loc.get(ei.getObjectRef()); + if (sr != null) { + System.out.println(sr); + } else { + System.out.println("?"); + } + } + + + static void printUsage () { + System.out.println("HeapTracker - a JPF listener tool to report and check heap utilization"); + System.out.println("usage: java gov.nasa.jpf.tools.HeapTracker <jpf-options> <heapTracker-options> <class>"); + System.out.println(" +heap.size_limit=<num> : report property violation if heap exceeds <num> bytes"); + System.out.println(" +heap.live_limit=<num> : report property violation if more than <num> live objects"); + System.out.println(" +heap.classes=<regEx> : only report instances of classes matching <regEx>"); + System.out.println(" +heap.throw_exception=<bool>: throw a OutOfMemoryError instead of reporting property violation"); + } +} +