/*
* Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.max.vma.tools.qa;
import static com.oracle.max.vm.ext.vma.store.txt.VMATextStoreFormat.*;
import static com.oracle.max.vma.tools.qa.TransientVMAdviceHandlerTypes.*;
import static com.oracle.max.vma.tools.qa.TransientVMAdviceHandlerTypes.RecordType.*;
import java.io.*;
import java.util.*;
import com.oracle.max.vm.ext.vma.*;
import com.oracle.max.vm.ext.vma.store.*;
import com.oracle.max.vm.ext.vma.store.txt.*;
import com.oracle.max.vma.tools.log.*;
import com.oracle.max.vma.tools.log.ConvertLog.MergeCommand.PushRecord;
import com.sun.max.program.*;
/**
* Reads a log file in the format generated by {@link CSFVMATextStore}.
*
* Object identifiers in the log may be reused owing to garbage collection. Object identifiers in the {@link #objects}
* map are unique and always qualified by the allocation (gc) epoch, {@link #allocationEpoch). I.e., an id X in the log is
* stored as "X:allocationEpoch", e.g. "X:0, X:3, ..."
*
* Note that although the map may contain many instances of X:n, at any point in time there can only be one active
* instance, which is the value of "n" given by the {@link #maxAllocationEpoch} map.
*
* The log must be time ordered for the main body of the code to work properly.
* An unordered log is detected automatically and converted using {@link ConvertLog}.
*
*
*/
public class ProcessLog {
static class TraceException extends Exception {
static final long serialVersionUID = 0;
TraceException(String s) {
super(s);
}
}
public static abstract class RecordReader {
public abstract String[] readLine() throws IOException;
public abstract void close() throws IOException;
}
private static class BufferedRecordReader extends RecordReader {
private BufferedReader reader;
BufferedRecordReader(BufferedReader reader) {
this.reader = reader;
}
@Override
public String[] readLine() throws IOException {
String line = reader.readLine();
if (line == null) {
return null;
}
return ConvertLog.split(textKeyMode, line);
}
@Override
public void close() throws IOException {
reader.close();
}
}
private static class PushReader extends RecordReader implements PushRecord {
private static final int LENGTH = 1024;
private String[][]lineParts = new String[LENGTH][];
private int count;
private int writeIndex;
private int readIndex;
@Override
public synchronized String[] readLine() throws IOException {
while (count == 0) {
try {
wait();
} catch (InterruptedException ex) {
}
}
count--;
notify();
String[] result = lineParts[readIndex];
readIndex = (readIndex + 1) % LENGTH;
return result;
}
@Override
public void close() throws IOException {
}
@Override
public synchronized void pushRecord(String[] lineParts) {
while (count >= LENGTH) {
try {
wait();
} catch (InterruptedException ex) {
}
}
this.lineParts[writeIndex] = lineParts;
writeIndex = (writeIndex + 1) % LENGTH;
count++;
notify();
}
}
private static class PushReaderThread extends Thread {
File[] files;
PushReader pushReader;
PushReaderThread(File[] files, PushReader pushReader) {
this.pushReader = pushReader;
this.files = files;
setName("PushReader");
}
@Override
public void run() {
ConvertLog.MergeCommand command = new ConvertLog.MergeCommand(pushReader);
command.execute(files, null);
}
}
/**
* Uniquely identifies a class by its name and classloader id.
*/
static class ClassNameId implements Comparable {
String className;
String clId;
ClassNameId(String name, String id) {
this.className = name;
this.clId = id;
}
public int compareTo(Object other) {
ClassNameId otherClassName = (ClassNameId) other;
if (otherClassName.className.equals(className)) {
return clId.compareTo(otherClassName.clId);
} else {
return className.compareTo(otherClassName.className);
}
}
@Override
public int hashCode() {
return className.hashCode() ^ clId.hashCode();
}
@Override
public boolean equals(Object other) {
return compareTo(other) == 0;
}
@Override
public String toString() {
return className + "(cl:" + clId + ")";
}
}
private boolean verbose = false;
private int maxLines;
/**
* Experimentally determined, using {@code cv -stats}, used to set the size of the {@link #adviceRecordList}.
*/
private static final int AVG_LINE_LENGTH = 12; //
private Map<String, ObjectRecord> objects = new HashMap<String, ObjectRecord>(1024 * 1024);
private SortedMap<ClassNameId, ClassRecord> classMap = new TreeMap<ClassNameId, ClassRecord>();
private Map<String, ThreadRecord> threadMap = new HashMap<String, ThreadRecord>();
private Map<String, FieldRecord> fieldMap = new HashMap<String, FieldRecord>();
private Map<String, MethodRecord> methodMap = new HashMap<String, MethodRecord>();
private ArrayList<AllocationEpoch> allocationEpochs = new ArrayList<AllocationEpoch>();
private long objectCount = 0;
private long arrayCount = 0;
private int missingConstructorCount = 0;
private long startTime; // absolute value in trace
private long lastTime; // absolute value computed from startTime + increments in the trace records
private boolean absTime; // if log uses absolute time
/**
* bytecode index of associated bytecode instruction.
*/
private short bci;
private AllocationEpoch allocationEpoch;
private AllocationEpoch prevAllocationEpoch;
/**
* Key is an unqualified id and the value is the last allocation epoch that id was defined (allocated) in.
* I.e., to get a qualified id, look up the value mapped from the unqualified id and append it.
*/
private Map<String, Integer> maxAllocationEpoch = new HashMap<String, Integer>();
private int startRemoval = -1;
private int endRemoval;
private Map<String, String> lastId = new HashMap<String, String>();
// Following fields hold the values for the current trace (if appropriate)
private ObjectRecord objectRecord;
private FieldRecord fieldRecord;
private MethodRecord methodRecord;
private ThreadRecord threadRecord;
private ClassRecord classRecord;
private Map<String, ObjectRecord> missingConstructors = new HashMap<String, ObjectRecord>();
/* These maps hold forward references that can occur in logs that were generated by
* the per-thread batching generator. Even when time ordered, one thread may use
* a short form that is defined later in another thread. This is because there is
* no relationship between the time the thread buffer is flushed and the time the
* short form is created.
*/
/**
* A map from the class short form to the full name and class loader id.
*/
private Map<String, ClassNameId> classShortFormsToFull = new HashMap<String, ClassNameId>();
/**
* A map from the thread short form to the full name.
*/
private Map<String, String> threadShortFormsToFull = new HashMap<String, String>();
/**
* Records the forward references to class short forms, allowing a placeholder
* {@link ClassRecord} to be created using a fake {@link ClassNameId} which
* can be fixed up later when the definition is encountered.
*/
private Map<String, ClassNameId> classForwardRefs = new HashMap<String, ClassNameId>();
private static final String FORWARD_PREFIX = "Forward:";
/*
* Fields/methods share common behavior so we have a generic handler class.
*
*/
private interface Factory<T> {
T create(ClassRecord cr, String name);
}
private static class FieldRecordFactory implements Factory<FieldRecord> {
@Override
public FieldRecord create(ClassRecord cr, String name) {
return new FieldRecord(cr, name);
}
}
private static class MethodRecordFactory implements Factory<MethodRecord> {
@Override
public MethodRecord create(ClassRecord cr, String name) {
return new MethodRecord(cr, name);
}
}
private static class QualName {
ClassRecord cr;
String name;
QualName(ClassRecord cr, String name) {
this.cr = cr;
this.name = name;
}
}
private class ShortFormHandler<T extends MemberRecord> {
/**
* The key is the short form and the value (when defined) is the actual name.
*/
private Map<String, QualName> shortFormToFull = new HashMap<String, QualName>();
/**
* Set of forward references.
*/
private Set<String> forwardRefs = new HashSet<String>();
/**
* Key is short name (which is unique, unlike the member name); value is an instance of T,
* but {@code classRecord} field may be a forward ref also if the class has not yet been defined.
*/
private Map<String, T> kindMap = new HashMap<String, T>();
/**
* factory for creating instances of {@link T}.
*/
private final Factory<T> factory;
ShortFormHandler(String kind, Factory<T> factory) {
this.factory = factory;
}
/**
* Gets an instance of T for short form arguments.
* If these short forms have already been defined then the result
* will not need fixing up later.
* @param shortName
*/
T getRecord(String shortName) {
QualName qualName = shortFormToFull.get(shortName);
if (qualName == null) {
// forward reference
if (!forwardRefs.contains(shortName)) {
// new forward reference
forwardRefs.add(shortName);
}
}
T kindRecord = kindMap.get(shortName);
if (kindRecord == null) {
kindRecord = factory.create(qualName == null ? null : qualName.cr, qualName == null ? null : qualName.name);
kindMap.put(shortName, kindRecord);
}
return kindRecord;
}
/**
* Definition of a member (field/method).
* N.B. The definition uses a class short form which may not yet be defined.
*/
void define() {
String classShortForm = recordParts[DEFINE_ARG_INDEX];
ClassRecord cr = getClassRecord(classShortForm); // may be forward
String definition = recordParts[DEFINE_ARG_INDEX + 1];
String shortName = recordParts[DEFINE_ARG_INDEX + 2];
shortFormToFull.put(shortName, new QualName(cr, definition));
T t;
if (forwardRefs.contains(shortName)) {
t = kindMap.remove(shortName);
assert t != null;
t.setName(definition);
t.setClassRecord(cr);
} else {
// no forward refs to this member
t = factory.create(cr, definition);
}
kindMap.put(shortName, t);
}
}
private ShortFormHandler<FieldRecord> fieldShortFormHandler = new ShortFormHandler<FieldRecord>("Field", new FieldRecordFactory());
private ShortFormHandler<MethodRecord> methodShortFormHandler = new ShortFormHandler<MethodRecord>("Method", new MethodRecordFactory());
/**
* List of all the records in the trace using the same format as {@link TransientVMAdviceHandlerTypes}.
*/
private ArrayList<AdviceRecord> adviceRecordList;
/**
* The parts of a log record split at space boundaries.
*/
private String[] recordParts;
private int lineNumber;
private static boolean textKeyMode;
private ProcessLog(boolean verbose, int maxLines) throws IOException {
this.verbose = verbose;
this.maxLines = maxLines;
}
public static TraceRun processTrace(String dataDir, boolean verbose, int maxLines) throws IOException {
ProcessLog pt = new ProcessLog(verbose, maxLines);
return pt.doProcessTrace(dataDir);
}
private int estimateRecordCount(File[] files) {
int length = 0;
for (File file : files) {
length += file.length();
}
return length / AVG_LINE_LENGTH;
}
private TraceRun doProcessTrace(String dataDirName) throws IOException {
long chunkStartTime = System.currentTimeMillis();
long processStartTime = chunkStartTime;
File dataDir = new File(dataDirName);
File dataFile = null;
if (dataDir.isDirectory()) {
dataFile = new File(dataDir, VMAStoreFile.GLOBAL_STORE);
} else {
dataFile = dataDir;
}
int adviceRecordListCountEstimate = 0;
RecordReader reader = null;
if (dataFile.exists()) {
adviceRecordListCountEstimate = estimateRecordCount(new File[] {dataFile});
reader = checkTimeOrdered(dataFile);
} else {
// either a per-thread store or an error
if (dataDir.isDirectory()) {
adviceRecordListCountEstimate = estimateRecordCount(dataDir.listFiles());
PushReader pushReader = new PushReader();
reader = pushReader;
new PushReaderThread(dataDir.listFiles(), pushReader).start();
} else {
throw new FileNotFoundException(dataDirName);
}
}
if (verbose) {
System.out.println("processing trace file " + dataDirName + " starting");
}
adviceRecordList = new ArrayList<AdviceRecord>(adviceRecordListCountEstimate);
lineNumber = 1;
boolean checked = false;
try {
while (true) {
recordParts = reader.readLine();
if (recordParts == null) {
break;
}
if (recordParts.length == 0 || recordParts[0].charAt(0) == '#') {
continue;
}
if (!checked) {
checked = true;
}
try {
processTraceRecord();
} catch (TraceException e) {
System.err.println("line " + lineNumber + ": " + e);
}
lineNumber++;
if (lineNumber >= maxLines) {
System.out.println("reached max line count - terminating processing");
break;
}
if (verbose && ((lineNumber % 100000) == 0)) {
long endTime = System.currentTimeMillis();
System.out.printf("processed %d traces in %d ms (%d)%n", lineNumber, endTime - processStartTime, endTime - chunkStartTime);
chunkStartTime = endTime;
}
}
} finally {
reader.close();
}
if (verbose) {
System.out.println("processing trace file " + dataDirName + " complete");
}
checkSorted();
// create a map from classloaders to classes loaded by them
Map<String, SortedMap<String, ClassRecord>> classLoaders = new HashMap<String, SortedMap<String, ClassRecord>>();
for (ClassRecord cr : classMap.values()) {
SortedMap<String, ClassRecord> clMap = classLoaders.get(cr.getClassLoaderId());
if (clMap == null) {
clMap = new TreeMap<String, ClassRecord>();
classLoaders.put(cr.getClassLoaderId(), clMap);
}
clMap.put(cr.getName(), cr);
}
fixupEndCreationRecords();
TraceRun result = new TraceRun(dataDirName, adviceRecordList, objects, threadMap, classLoaders,
missingConstructors, objectCount, arrayCount,
missingConstructorCount, allocationEpochs, startTime, lastTime);
return result;
}
private void checkSorted() {
AdviceRecord last = null;
for (int i = 0; i < adviceRecordList.size(); i++) {
AdviceRecord ar = adviceRecordList.get(i);
if (last != null) {
if (last.time > ar.time) {
System.err.println("advice record list is not sorted by time, at record " + i);
System.exit(1);
}
}
last = ar;
}
}
/**
* Check that the header line is not corrupt and set the key mode.
*/
private void checkStoreHeader() {
assert recordParts.length == 4;
textKeyMode = (Integer.parseInt(recordParts[3]) & TEXT_KEY) != 0;
assert VMATextStoreFormat.getCommand(textKeyMode, recordParts[0]) == Key.INITIALIZE_STORE;
}
private RecordReader checkTimeOrdered(File file) throws IOException {
BufferedRecordReader reader = new BufferedRecordReader(new BufferedReader(new FileReader(file)));
recordParts = reader.readLine();
checkStoreHeader();
reader.close();
int mode = Integer.parseInt(recordParts[3]);
if ((mode & BATCHED) != 0) {
// not time ordered, run the converter to a temp file
if (verbose) {
System.out.println("creating time ordered log from per-thread batched log");
}
File tempFile = File.createTempFile("vma", null);
try {
ConvertLog.main(new String[] {"-f", file.getAbsolutePath(), "-o", tempFile.getAbsolutePath(), "-unbatch"});
} catch (IOException ex) {
throw ex;
} catch (Exception ex) {
ProgramError.unexpected("failed to convert: " + file.getAbsolutePath(), ex);
}
file = tempFile;
}
return new BufferedRecordReader(new BufferedReader(new FileReader(file)));
}
/**
* Fixup the end creation record for object records.
* If the trace has INVOKESPECIAL/AFTER records, this just means fixing up
* the case where the AFTER <init> was a forward reference.
*
* Otherwise, where we only have METHOD_ENTRY/RETURN pairs it means locating
* the matching RETURN for the <init> after the NEW.
*/
private void fixupEndCreationRecords() {
for (ObjectRecord objectRecord : objects.values()) {
if (objectRecord.endCreationRecord == null) {
/* TODO depends on INVOKE AFTER advice
// we scan backwards to find the last <init> return
for (int i = adviceRecordList.size() - 1; i > 0; i--) {
AdviceRecord ar = adviceRecordList.get(i);
RecordType rt = ar.getRecordType();
if (rt == InvokeSpecial && ar.getAdviceMode() == AdviceMode.AFTER.ordinal()) {
ObjectRecord invokeObject = AdviceRecordHelper.getObjectRecord(ar);
if (objectRecord.id.equals(invokeObject.id)) {
MethodRecord mr = AdviceRecordHelper.getMethod(ar);
if (mr.name.equals("<init>")) {
objectRecord.setEndCreationRecord(ar);
}
}
}
}
*/
int index = getRecordListIndex(objectRecord.beginCreationRecord);
assert index >= 0 : "failed to find creation record index";
int mIndex = getInitMethodEntry(objectRecord, index + 1);
// We define end creation to be the RETURN that matches this constructor invocation.
// There may be an arbitrary number of other method invocations in between
AdviceRecord endCreationRecord = null;
if (mIndex > 0) {
int depth = 0;
for (int i = mIndex + 1; i < adviceRecordList.size(); i++) {
AdviceRecord ar = adviceRecordList.get(i);
RecordType art = ar.getRecordType();
if (art == MethodEntry) {
depth++;
} else if (art == Return || art == ReturnDouble || art == ReturnFloat || art == ReturnLong || art == ReturnObject || art == ReturnByThrow) {
if (depth == 0) {
endCreationRecord = ar;
break;
} else {
if (art == ReturnByThrow) {
int pop = ar.getPackedValue();
depth -= pop;
} else {
depth--;
}
}
}
}
} else {
endCreationRecord = adviceRecordList.get(index + 1);
}
if (endCreationRecord == null) {
System.err.printf("failed to find end creation record for %s%n", objectRecord);
endCreationRecord = adviceRecordList.get(index + 1);
}
objectRecord.setEndCreationRecord(endCreationRecord);
}
}
}
/**
* Attempt to locate the constructor METHOD_ENTRY for {@code objectRecord} starting at {@code index}.
* There may not be one if the class was not instrumented.
* @param objectRecord
* @param index
* @return index of METHOD_ENTRY revcord or -1 of not found
*/
private int getInitMethodEntry(ObjectRecord objectRecord, int index) {
for (int i = index; i < adviceRecordList.size(); i++) {
AdviceRecord ar = adviceRecordList.get(i);
if (ar.getRecordType() == MethodEntry) {
ObjectRecord methodEntryObject = AdviceRecordHelper.getObjectRecord(ar);
if (methodEntryObject != null && objectRecord.id.equals(methodEntryObject.id)) {
MethodRecord mr = AdviceRecordHelper.getMethod(ar);
if (mr.name.equals("<init>")) {
return i;
}
}
}
}
return -1;
}
private int getRecordListIndex(AdviceRecord ar) {
return AdviceRecordHelper.getRecordListIndex(adviceRecordList, ar);
}
private void objectsPut(String id, ObjectRecord td) {
final ObjectRecord old = objects.put(td.getId(), td);
assert old == null;
// remember max epoch for resolving unqualified ids
maxAllocationEpoch.put(id, allocationEpoch.epoch);
}
/**
* Gets the {@link ObjectRecord} associated with an unqualified id. The qualified id is found using the
* {@link #maxEpochMap}.
*
* @param id
* @throws TraceException
*/
private ObjectRecord getTraceRecord(String id) throws TraceException {
if (id.equals("0")) {
return null;
}
final Integer thisMaxAllocationEpoch = maxAllocationEpoch.get(id);
assert thisMaxAllocationEpoch != null;
final String uid = ObjectRecord.getMapId(id, thisMaxAllocationEpoch);
ObjectRecord td = objects.get(uid);
if (td == null) {
throw new TraceException("no creation record for id = " + uid + ", at line " + lineNumber);
}
td.traceOccurrences++;
return td;
}
/**
* Sets (and returns) {@link #classRecord}, handling forward references.
* In the latter case, the name field in the ClassRecord will be based on {@value CLASS_FORWARD_PREFIX}
* and will be fixed up later.
* @param shortClassName in the trace
* @return a {@link ClassRecord}
*/
private ClassRecord getClassRecord(String shortClassName) {
ClassNameId classNameId = classShortFormsToFull.get(shortClassName);
if (classNameId == null) {
// forward reference
classNameId = classForwardRefs.get(shortClassName);
if (classNameId == null) {
// new forward reference
String forwardName = FORWARD_PREFIX + shortClassName;
// the class definition will contain the classloader id;
// at this stage we create a don't care value, as it isn't used
classNameId = new ClassNameId(forwardName, ObjectRecord.getMapId("0", allocationEpoch.epoch));
classForwardRefs.put(shortClassName, classNameId);
}
}
// Have we created a ClassRecord for this class?
classRecord = classMap.get(classNameId);
if (classRecord == null) {
classRecord = new ClassRecord(classNameId.className, classNameId.clId);
classMap.put(classNameId, classRecord);
}
return classRecord;
}
/*
*/
/**
* Sets (and returns) {@link #fieldRecord} handling forward references.
* @param shortFieldName
*/
private FieldRecord getFieldRecord(String shortFieldName) {
fieldRecord = fieldShortFormHandler.getRecord(shortFieldName);
return fieldRecord;
}
/**
* Sets (and returns) {@link #methodRecord} handling forward references.
* @param shortMethodName
*/
private MethodRecord getMethodRecord(String shortMethodName) {
methodRecord = methodShortFormHandler.getRecord(shortMethodName);
return methodRecord;
}
/**
* Class definition, may resolve a previous forward reference.
*/
private void defineClass(String name, long classLoaderId, String shortForm) {
// class definition
ClassNameId classNameId = new ClassNameId(name, getClassLoaderIdAsString(classLoaderId));
classShortFormsToFull.put(shortForm, classNameId);
// fix up forward reference
ClassNameId forwardName = classForwardRefs.get(shortForm);
if (forwardName != null) {
// find class record in the classMap under the forward name, remove it,
// patch name/id and add back under new name.
ClassRecord cr = classMap.remove(forwardName);
assert cr != null;
cr.setName(name, getClassLoaderIdAsString(classLoaderId));
classMap.put(classNameId, cr);
}
}
private String getClassLoaderIdAsString(long clId) {
String clIdString = Long.toString(clId);
Integer epoch = maxAllocationEpoch.get(clIdString);
if (epoch == null) {
// This is a special case for the self referring bootstrap class loader
epoch = allocationEpoch.epoch;
}
return ObjectRecord.getMapId(Long.toString(clId), epoch);
}
private void defineField() {
fieldShortFormHandler.define();
}
private void defineMethod() {
methodShortFormHandler.define();
}
private long getTime() throws TraceException {
long timeValue = Long.parseLong(recordParts[TIME_INDEX]);
if (absTime) {
lastTime = timeValue;
} else {
lastTime += timeValue;
}
return lastTime;
}
private long expectNumber(String arg) throws TraceException {
if (!(arg.charAt(0) >= '0' || arg.charAt(0) <= '9')) {
throw new TraceException("number expected at line " + lineNumber);
} else {
return Long.parseLong(arg);
}
}
private Key expectValidTraceStart() throws TraceException {
Key result = VMATextStoreFormat.getCommand(textKeyMode, recordParts[KEY_INDEX]);
if (result == null) {
throw new TraceException("unknown trace command at line " + lineNumber);
} else {
return result;
}
}
private void getTimeAndThread() throws TraceException {
getTime();
// N.B. there can be no forward references to threads
String threadName = threadShortFormsToFull.get(recordParts[THREAD_INDEX]);
assert threadName != null;
threadRecord = threadMap.get(threadName);
}
private String arg(int slot) {
if (slot < recordParts.length) {
return recordParts[slot];
} else {
return null;
}
}
private void processTraceRecord() throws TraceException {
String arg1 = arg(1);
String arg2 = arg(2);
String bciArg = arg(3);
String threadArg = arg2;
String objIdArg = "???";
int adviceModeInt = -1;
Key key = VMATextStoreFormat.getCommand(textKeyMode, recordParts[0]);
if (VMATextStoreFormat.hasTimeAndThread(key)) {
getTimeAndThread();
} else if (VMATextStoreFormat.hasTime(key)) {
getTime();
}
if (VMATextStoreFormat.hasBci(key)) {
bci = (short) Integer.parseInt(bciArg);
}
if (VMATextStoreFormat.hasId(key)) {
if (arg(OBJ_ID_INDEX).charAt(0) == REPEAT_ID) {
objIdArg = lastId.get(threadArg);
} else {
objIdArg = arg(OBJ_ID_INDEX);
lastId.put(threadArg, objIdArg);
}
}
AdviceRecord adviceRecord = null;
switch (key) {
case INITIALIZE_STORE:
startTime = Long.parseLong(arg1);
lastTime = startTime;
allocationEpoch = new AllocationEpoch(startTime);
allocationEpochs.add(allocationEpoch);
absTime = Boolean.parseBoolean(arg2);
return;
case FINALIZE_STORE: {
long t = Long.parseLong(arg1);
lastTime = absTime ? t : lastTime + t;
allocationEpoch.endTime = lastTime;
return;
}
case THREAD_SWITCH:
throw new TraceException("batched log is not supported - use ConvertLog -unbatch");
case CLASS_DEFINITION: {
defineClass(ClassRecord.getCanonicalName(recordParts[DEFINE_ARG_INDEX]),
expectNumber(recordParts[DEFINE_ARG_INDEX + 1]), recordParts[DEFINE_ARG_INDEX + 2]);
return;
}
case FIELD_DEFINITION: {
defineField();
return;
}
case METHOD_DEFINITION: {
defineMethod();
return;
}
case THREAD_DEFINITION: {
ThreadRecord tr = new ThreadRecord(recordParts[DEFINE_ARG_INDEX]);
threadMap.put(tr.name, tr);
threadShortFormsToFull.put(recordParts[DEFINE_ARG_INDEX + 1], tr.name);
return;
}
case ADVISE_BEFORE_THROW:
case ADVISE_BEFORE_MONITOR_ENTER:
case ADVISE_BEFORE_MONITOR_EXIT: {
objectRecord = getTraceRecord(objIdArg);
ObjectAdviceRecord objectAdviceRecord = (ObjectAdviceRecord) createAdviceRecordAndSetTimeAndThread(keyToRecordType(key), AdviceMode.BEFORE, bci);
objectAdviceRecord.value = objectRecord;
objectRecord.addTraceElement(objectAdviceRecord);
adviceRecord = objectAdviceRecord;
break;
}
case UNSEEN:
case ADVISE_AFTER_NEW_ARRAY:
case ADVISE_AFTER_NEW: {
ObjectAdviceRecord objectAdviceRecord = (ObjectAdviceRecord) createAdviceRecordAndSetTimeAndThread(keyToRecordType(key), AdviceMode.AFTER, bci);
getClassRecord(recordParts[NEW_CLASSNAME_INDEX]);
objectRecord = new ObjectRecord(objIdArg, allocationEpoch.epoch, classRecord, threadRecord, objectAdviceRecord);
classRecord.addObject(objectRecord);
objectsPut(objIdArg, objectRecord);
objectAdviceRecord.value = objectRecord;
if (key == Key.UNSEEN) {
// We don't know how or when this was constructed, but give it an end creation time of now.
objectRecord.setEndCreationRecord(objectAdviceRecord);
missingConstructors.put(objectRecord.getId(), objectRecord);
missingConstructorCount++;
} else if (key == Key.ADVISE_AFTER_NEW_ARRAY) {
objectAdviceRecord.setPackedValue(Integer.parseInt(arg(NEW_ARRAY_LENGTH_INDEX))); // array length
objectRecord.setEndCreationRecord(objectAdviceRecord);
}
if (classRecord.isArray()) {
arrayCount++;
} else {
objectCount++;
}
adviceRecord = objectAdviceRecord;
break;
}
case ADVISE_BEFORE_CONST_LOAD: {
adviceRecord = createAdviceRecordAndSetTimeThreadValue("ConstLoad", AdviceMode.BEFORE, arg(CONST_LOAD_VALUE_INDEX), arg(CONST_LOAD_VALUE_INDEX + 1));
break;
}
case ADVISE_BEFORE_LOAD: {
adviceRecord = createAdviceRecordAndSetTimeAndThread(Load, AdviceMode.BEFORE, bci);
adviceRecord.setPackedValue(Integer.parseInt(arg(LOADSTORE_DISP_INDEX)));
break;
}
case ADVISE_AFTER_LOAD:
case ADVISE_BEFORE_STORE: {
adviceRecord = createAdviceRecordAndSetTimeThreadValue(key == Key.ADVISE_AFTER_LOAD ? "Load" : "Store", AdviceMode.BEFORE, arg(LOADSTORE_DISP_INDEX + 1), arg(LOADSTORE_DISP_INDEX + 2));
if (adviceRecord.getRecordType() == StoreObject || adviceRecord.getRecordType() == LoadObject) {
ObjectRecord or = AdviceRecordHelper.getObjectRecord(adviceRecord);
if (or != null) {
or.addTraceElement(adviceRecord);
}
}
adviceRecord.setPackedValue(Integer.parseInt(arg(LOADSTORE_DISP_INDEX)));
break;
}
case ADVISE_BEFORE_ARRAY_LOAD: {
objectRecord = getTraceRecord(objIdArg);
int arrayIndex = (int) expectNumber(arg(ARRAY_INDEX_INDEX));
ObjectAdviceRecord objectAdviceRecord = (ObjectAdviceRecord) createAdviceRecordAndSetTimeAndThread(keyToRecordType(key), AdviceMode.BEFORE, bci);
objectAdviceRecord.value = objectRecord;
objectAdviceRecord.setPackedValue(arrayIndex);
objectRecord.addTraceElement(objectAdviceRecord);
adviceRecord = objectAdviceRecord;
break;
}
case ADVISE_AFTER_ARRAY_LOAD:
case ADVISE_BEFORE_ARRAY_STORE: {
objectRecord = getTraceRecord(objIdArg);
int arrayIndex = (int) expectNumber(arg(ARRAY_INDEX_INDEX));
ObjectAdviceRecord objectAdviceRecord = (ObjectAdviceRecord) createAdviceRecordAndSetTimeThreadValue(
key == Key.ADVISE_BEFORE_ARRAY_STORE ? "ArrayStore" : "ArrayLoad",
AdviceMode.BEFORE, arg(ARRAY_INDEX_INDEX + 1), arg(ARRAY_INDEX_INDEX + 2));
objectAdviceRecord.value = objectRecord;
objectAdviceRecord.setPackedValue(arrayIndex);
objectRecord.addTraceElement(objectAdviceRecord);
if (objectAdviceRecord.getRecordType() == ArrayStoreObject || objectAdviceRecord.getRecordType() == ArrayLoadObject) {
ObjectRecord object1 = getTraceRecord(arg(ARRAY_INDEX_INDEX + 2));
if (object1 != null) {
object1.addTraceElement(objectAdviceRecord);
}
}
adviceRecord = objectAdviceRecord;
break;
}
case ADVISE_BEFORE_ARRAY_LENGTH: {
objectRecord = getTraceRecord(objIdArg);
ObjectAdviceRecord objectAdviceRecord = (ObjectAdviceRecord) createAdviceRecordAndSetTimeAndThread(ArrayLength, AdviceMode.BEFORE, bci);
objectAdviceRecord.value = objectRecord;
objectAdviceRecord.setPackedValue(Integer.parseInt(arg(ARRAY_LENGTH_INDEX)));
objectRecord.addTraceElement(objectAdviceRecord);
adviceRecord = objectAdviceRecord;
break;
}
case ADVISE_BEFORE_GET_STATIC: {
getFieldRecord(arg(STATIC_FIELDNAME_INDEX));
ObjectFieldAdviceRecord objectFieldAdviceRecord = (ObjectFieldAdviceRecord) createAdviceRecordAndSetTimeAndThread(GetStatic, AdviceMode.BEFORE, bci);
objectFieldAdviceRecord.value = classRecord;
objectFieldAdviceRecord.field = fieldRecord;
classRecord.addTraceElement(objectFieldAdviceRecord);
adviceRecord = objectFieldAdviceRecord;
break;
}
case ADVISE_BEFORE_PUT_STATIC: {
getFieldRecord(arg(STATIC_FIELDNAME_INDEX));
ObjectFieldAdviceRecord objectFieldAdviceRecord = (ObjectFieldAdviceRecord) createAdviceRecordAndSetTimeThreadValue("PutStatic", AdviceMode.BEFORE,
arg(STATIC_FIELDNAME_INDEX + 1), arg(STATIC_FIELDNAME_INDEX + 2));
objectFieldAdviceRecord.value = classRecord;
objectFieldAdviceRecord.field = fieldRecord;
classRecord.addTraceElement(objectFieldAdviceRecord);
adviceRecord = objectFieldAdviceRecord;
break;
}
case ADVISE_BEFORE_GET_FIELD: {
objectRecord = getTraceRecord(objIdArg);
getFieldRecord(arg(ID_FIELDNAME_INDEX));
ObjectFieldAdviceRecord objectFieldAdviceRecord = (ObjectFieldAdviceRecord) createAdviceRecordAndSetTimeAndThread(GetField, AdviceMode.BEFORE, bci);
objectFieldAdviceRecord.value = objectRecord;
objectFieldAdviceRecord.field = fieldRecord;
objectRecord.addTraceElement(objectFieldAdviceRecord);
adviceRecord = objectFieldAdviceRecord;
break;
}
case ADVISE_BEFORE_PUT_FIELD: {
objectRecord = getTraceRecord(objIdArg);
getFieldRecord(arg(ID_FIELDNAME_INDEX));
ObjectFieldAdviceRecord objectFieldAdviceRecord = (ObjectFieldAdviceRecord) createAdviceRecordAndSetTimeThreadValue("PutField", AdviceMode.BEFORE,
arg(ID_FIELDNAME_INDEX + 1), arg(ID_FIELDNAME_INDEX + 2));
objectFieldAdviceRecord.value = objectRecord;
objectFieldAdviceRecord.field = fieldRecord;
objectRecord.addTraceElement(objectFieldAdviceRecord);
adviceRecord = objectFieldAdviceRecord;
break;
}
case ADVISE_BEFORE_IF: {
if (arg(IF_OPCODE_INDEX + 1).equals("J")) {
LongLongTBciAdviceRecord longLongAdviceRecord = (LongLongTBciAdviceRecord) createAdviceRecordAndSetTimeAndThread(IfInt, AdviceMode.BEFORE, bci);
longLongAdviceRecord.value = Long.parseLong(arg(IF_OPCODE_INDEX + 2));
longLongAdviceRecord.value2 = Long.parseLong(arg(IF_OPCODE_INDEX + 3));
longLongAdviceRecord.targetBci = Short.parseShort(arg(IF_OPCODE_INDEX + 4));
adviceRecord = longLongAdviceRecord;
} else {
ObjectObjectTBciAdviceRecord objectObjectAdviceRecord = (ObjectObjectTBciAdviceRecord) createAdviceRecordAndSetTimeAndThread(IfObject, AdviceMode.BEFORE, bci);
ObjectRecord object1 = getTraceRecord(arg(IF_OPCODE_INDEX + 2));
ObjectRecord object2 = getTraceRecord(arg(IF_OPCODE_INDEX + 3));
objectObjectAdviceRecord.value = object1;
objectObjectAdviceRecord.value2 = object2;
objectObjectAdviceRecord.targetBci = Short.parseShort(arg(IF_OPCODE_INDEX + 4));
if (object1 != null) {
object1.addTraceElement(objectObjectAdviceRecord);
}
if (object2 != null) {
object2.addTraceElement(objectObjectAdviceRecord);
}
adviceRecord = objectObjectAdviceRecord;
}
adviceRecord.setPackedValue(Integer.parseInt(arg(IF_OPCODE_INDEX)));
break;
}
case ADVISE_BEFORE_OPERATION: {
adviceRecord = createAdviceRecordAndSetTimeThreadValue("Operation", AdviceMode.BEFORE, arg(OP_VALUES_INDEX), arg(OP_VALUES_INDEX + 1));
adviceRecord.setPackedValue(Integer.parseInt(arg(OP_OPCODE_INDEX)));
switch (arg(OP_VALUES_INDEX).charAt(0)) {
case LONG_VALUE:
((LongLongAdviceRecord) adviceRecord).value2 = Long.parseLong(arg(OP_VALUES_INDEX + 2));
break;
case FLOAT_VALUE:
((FloatFloatAdviceRecord) adviceRecord).value2 = Float.parseFloat(arg(OP_VALUES_INDEX + 2));
break;
case DOUBLE_VALUE:
((DoubleDoubleAdviceRecord) adviceRecord).value2 = Double.parseDouble(arg(OP_VALUES_INDEX + 2));
break;
default:
throw new IllegalArgumentException("bad type " + arg(OP_VALUES_INDEX).charAt(0) + " in value");
}
break;
}
case ADVISE_BEFORE_INSTANCE_OF:
case ADVISE_BEFORE_CHECK_CAST: {
objectRecord = getTraceRecord(objIdArg);
getClassRecord(arg(ID_CLASSNAME_INDEX));
ObjectObjectAdviceRecord objectObjectAdviceRecord = (ObjectObjectAdviceRecord) createAdviceRecordAndSetTimeAndThread(keyToRecordType(key), AdviceMode.BEFORE, bci);
objectObjectAdviceRecord.value = objectRecord;
objectObjectAdviceRecord.value2 = classRecord;
if (objectRecord != null) {
objectRecord.addTraceElement(objectObjectAdviceRecord);
}
adviceRecord = objectObjectAdviceRecord;
break;
}
case ADVISE_BEFORE_CONVERSION: {
adviceRecord = createAdviceRecordAndSetTimeThreadValue("Conversion", AdviceMode.BEFORE, arg(CONV_OPCODE_INDEX + 1), arg(CONV_OPCODE_INDEX + 2));
adviceRecord.setPackedValue(Integer.parseInt(arg(CONV_OPCODE_INDEX)));
break;
}
case ADVISE_BEFORE_GC: {
allocationEpoch.setEndTime(lastTime);
adviceRecord = createAdviceRecordAndSetTimeAndThread(keyToRecordType(key), AdviceMode.BEFORE, bci);
break;
}
case ADVISE_AFTER_GC: {
prevAllocationEpoch = allocationEpoch;
allocationEpoch = new AllocationEpoch(lastTime);
allocationEpochs.add(allocationEpoch);
adviceRecord = createAdviceRecordAndSetTimeAndThread(keyToRecordType(key), AdviceMode.AFTER, bci);
break;
}
case REMOVAL: {
if (startRemoval < 0) {
startRemoval = adviceRecordList.size();
}
endRemoval = adviceRecordList.size();
objIdArg = arg1;
// This object may have been created in some earlier epoch, so can't use current.
// It must be the death of id recorded by maxAllocationEpoch
objectRecord = objects.get(ObjectRecord.getMapId(objIdArg, maxAllocationEpoch.get(objIdArg)));
ObjectAdviceRecord objectAdviceRecord = (ObjectAdviceRecord) createAdviceRecordAndSetTimeAndThread(Removal, AdviceMode.AFTER, bci);
objectAdviceRecord.value = objectRecord;
objectRecord.setRemovalRecord(objectAdviceRecord);
adviceRecord = objectAdviceRecord;
break;
}
case ADVISE_BEFORE_INVOKE_INTERFACE:
case ADVISE_BEFORE_INVOKE_STATIC:
case ADVISE_BEFORE_INVOKE_VIRTUAL:
case ADVISE_BEFORE_INVOKE_SPECIAL:
adviceModeInt = AdviceMode.BEFORE.ordinal();
// Checkstyle: stop
/*
case ADVISE_AFTER_INVOKE_INTERFACE:
case ADVISE_AFTER_INVOKE_STATIC:
case ADVISE_AFTER_INVOKE_VIRTUAL:
case ADVISE_AFTER_INVOKE_SPECIAL:
*/
case ADVISE_AFTER_METHOD_ENTRY: {
// Checkstyle: resume
if (adviceModeInt == -1) {
adviceModeInt = AdviceMode.AFTER.ordinal();
}
objectRecord = getTraceRecord(objIdArg);
getMethodRecord(arg(ID_MEMBERNAME_INDEX));
ObjectMethodAdviceRecord objectAdviceRecord = (ObjectMethodAdviceRecord) createAdviceRecordAndSetTimeAndThread(keyToRecordType(key), AdviceMode.values()[adviceModeInt], bci);
if (key == Key.ADVISE_BEFORE_INVOKE_STATIC /*|| key == Key.ADVISE_AFTER_INVOKE_STATIC*/) {
objectAdviceRecord.value = classRecord;
} else {
objectAdviceRecord.value = objectRecord;
if (objectRecord != null) {
objectRecord.addTraceElement(objectAdviceRecord);
}
}
objectAdviceRecord.value2 = methodRecord;
/*
if (key == Key.ADVISE_AFTER_INVOKE_SPECIAL) {
if (methodRecord.name.equals("<init>")) {
objectRecord.setEndCreationRecord(objectAdviceRecord);
}
}
*/
adviceRecord = objectAdviceRecord;
break;
}
case ADVISE_BEFORE_RETURN: {
if (arg(RETURN_VALUE_INDEX) != null) {
adviceRecord = createAdviceRecordAndSetTimeThreadValue("Return", AdviceMode.BEFORE, arg(RETURN_VALUE_INDEX), arg(RETURN_VALUE_INDEX + 1));
if (adviceRecord.getRecordType() == ReturnObject) {
ObjectRecord or = AdviceRecordHelper.getObjectRecord(adviceRecord);
if (or != null) {
or.addTraceElement(adviceRecord);
}
}
} else {
adviceRecord = createAdviceRecordAndSetTimeAndThread(Return, AdviceMode.BEFORE, bci);
}
break;
}
case ADVISE_BEFORE_RETURN_BY_THROW: {
objectRecord = getTraceRecord(objIdArg);
ObjectLongAdviceRecord objectAdviceRecord = (ObjectLongAdviceRecord) createAdviceRecordAndSetTimeAndThread(ReturnByThrow, AdviceMode.BEFORE, bci);
objectAdviceRecord.setPackedValue(Integer.parseInt(arg(RETURN_THROW_POP_INDEX)));
objectAdviceRecord.value = objectRecord;
adviceRecord = objectAdviceRecord;
break;
}
case ADVISE_BEFORE_STACK_ADJUST: {
adviceRecord = createAdviceRecordAndSetTimeAndThread(keyToRecordType(key), AdviceMode.BEFORE, bci);
adviceRecord.setPackedValue(Integer.parseInt(arg(STACK_ADJUST_INDEX)));
break;
}
case ADVISE_BEFORE_THREAD_TERMINATING:
case ADVISE_BEFORE_THREAD_STARTING:
adviceRecord = createAdviceRecordAndSetTimeAndThread(keyToRecordType(key), AdviceMode.BEFORE, bci);
if (key == Key.ADVISE_BEFORE_THREAD_STARTING) {
threadRecord.startTime = lastTime;
} else {
threadRecord.endTime = lastTime;
}
break;
case ADVISE_AFTER_MULTI_NEW_ARRAY:
assert false : key + " unexpected";
break;
case ADVISE_BEFORE_GOTO:
adviceRecord = createAdviceRecordAndSetTimeAndThread(keyToRecordType(key), AdviceMode.BEFORE, bci);
adviceRecord.setPackedValue(Integer.parseInt(arg(GOTO_TARGET_INDEX)));
break;
default:
throw new TraceException("unimplemented key: " + key);
}
if (key != Key.REMOVAL && startRemoval >= 0) {
prevAllocationEpoch.setRemovalRange(startRemoval, endRemoval);
startRemoval = -1;
}
assert adviceRecord != null;
adviceRecordList.add(adviceRecord);
}
private AdviceRecord createAdviceRecordAndSetTimeThreadValue(String rtPrefix, AdviceMode adviceMode, String valueKey, String value) throws TraceException {
RecordType rt = getRecordTypeForValue(rtPrefix, valueKey);
AdviceRecord adviceRecord = createAdviceRecordAndSetTimeAndThread(rt, adviceMode, bci);
switch (valueKey.charAt(0)) {
case OBJ_VALUE: {
ObjectRecord or = getTraceRecord(value);
switch (rt) {
case PutFieldObject:
case PutStaticObject:
((ObjectFieldObjectAdviceRecord) adviceRecord).value = or;
break;
case ConstLoadObject:
case StoreObject:
case LoadObject:
case ReturnObject:
((ObjectAdviceRecord) adviceRecord).value = or;
break;
default:
((ObjectObjectAdviceRecord) adviceRecord).value2 = or;
}
break;
}
case LONG_VALUE: {
long v = Long.parseLong(value);
switch (rt) {
case PutFieldLong:
case PutStaticLong:
((ObjectFieldLongAdviceRecord) adviceRecord).value2 = v;
break;
case ConstLoadLong:
case StoreLong:
case ConversionLong:
case ReturnLong:
((LongAdviceRecord) adviceRecord).value = v;
break;
case OperationLong:
((LongLongAdviceRecord) adviceRecord).value = v;
break;
default:
((ObjectLongAdviceRecord) adviceRecord).value2 = v;
}
break;
}
case FLOAT_VALUE: {
float v = Float.parseFloat(value);
switch (rt) {
case PutFieldFloat:
case PutStaticFloat:
((ObjectFieldFloatAdviceRecord) adviceRecord).value2 = v;
break;
case ConstLoadFloat:
case StoreFloat:
case ConversionFloat:
case ReturnFloat:
((FloatAdviceRecord) adviceRecord).value = v;
break;
case OperationFloat:
((FloatFloatAdviceRecord) adviceRecord).value = v;
break;
default:
((ObjectFloatAdviceRecord) adviceRecord).value2 = v;
}
break;
}
case DOUBLE_VALUE: {
double v = Double.parseDouble(value);
switch (rt) {
case PutFieldDouble:
case PutStaticDouble:
((ObjectFieldDoubleAdviceRecord) adviceRecord).value2 = v;
break;
case ConstLoadDouble:
case StoreDouble:
case ConversionDouble:
case ReturnDouble:
((DoubleAdviceRecord) adviceRecord).value = v;
break;
case OperationDouble:
((DoubleDoubleAdviceRecord) adviceRecord).value = v;
break;
default:
((ObjectDoubleAdviceRecord) adviceRecord).value2 = v;
}
break;
}
default:
assert false;
}
return adviceRecord;
}
private AdviceRecord createAdviceRecordAndSetTimeAndThread(RecordType rt, AdviceMode adviceMode, short bci) {
AdviceRecord adviceRecord;
// Handle the change of record type for PUT/GET/FIELD/STATIC and REMOVAL types
switch (rt) {
case PutFieldLong:
case PutStaticLong:
adviceRecord = new ObjectFieldLongAdviceRecord();
break;
case PutFieldFloat:
case PutStaticFloat:
adviceRecord = new ObjectFieldFloatAdviceRecord();
break;
case PutFieldDouble:
case PutStaticDouble:
adviceRecord = new ObjectFieldDoubleAdviceRecord();
break;
case PutFieldObject:
case PutStaticObject:
adviceRecord = new ObjectFieldObjectAdviceRecord();
break;
case GetField:
case GetStatic:
adviceRecord = new ObjectFieldAdviceRecord();
break;
case Removal:
adviceRecord = new ObjectAdviceRecord();
break;
default:
adviceRecord = rt.newAdviceRecord();
}
adviceRecord.setCodeModeBci(rt, adviceMode, bci);
adviceRecord.time = lastTime;
adviceRecord.thread = threadRecord;
return adviceRecord;
}
private static RecordType getRecordTypeForValue(String rtPrefix, String valueKey) {
String suffix;
switch (valueKey.charAt(0)) {
case OBJ_VALUE:
suffix = "Object";
break;
case LONG_VALUE:
suffix = "Long";
break;
case FLOAT_VALUE:
suffix = "Float";
break;
case DOUBLE_VALUE:
suffix = "Double";
break;
default:
throw new IllegalArgumentException("bad type " + valueKey.charAt(0) + " in value");
}
return RecordType.valueOf(rtPrefix + suffix);
}
private static RecordType keyToRecordType(Key key) throws TraceException {
switch (key) {
case ADVISE_BEFORE_THROW:
return Throw;
case ADVISE_BEFORE_INSTANCE_OF:
return InstanceOf;
case ADVISE_BEFORE_ARRAY_LENGTH:
return ArrayLength;
case ADVISE_BEFORE_CHECK_CAST:
return CheckCast;
case ADVISE_BEFORE_GET_STATIC:
return GetStatic;
case ADVISE_BEFORE_GET_FIELD:
return GetField;
case ADVISE_BEFORE_MONITOR_ENTER:
return MonitorEnter;
case ADVISE_BEFORE_MONITOR_EXIT:
return MonitorExit;
/*case ADVISE_AFTER_INVOKE_INTERFACE:*/
case ADVISE_BEFORE_INVOKE_INTERFACE:
return InvokeInterface;
/*case ADVISE_AFTER_INVOKE_STATIC:*/
case ADVISE_BEFORE_INVOKE_STATIC:
return InvokeStatic;
/*case ADVISE_AFTER_INVOKE_SPECIAL:*/
case ADVISE_BEFORE_INVOKE_SPECIAL:
return InvokeSpecial;
case ADVISE_BEFORE_INVOKE_VIRTUAL:
/*case ADVISE_AFTER_INVOKE_VIRTUAL:*/
return InvokeVirtual;
case ADVISE_AFTER_METHOD_ENTRY:
return MethodEntry;
case ADVISE_BEFORE_STACK_ADJUST:
return StackAdjust;
case ADVISE_AFTER_GC:
case ADVISE_BEFORE_GC:
return GC;
case ADVISE_BEFORE_THREAD_TERMINATING:
return ThreadTerminating;
case ADVISE_BEFORE_THREAD_STARTING:
return ThreadStarting;
case ADVISE_BEFORE_ARRAY_LOAD:
return ArrayLoad;
case ADVISE_AFTER_NEW:
return New;
case ADVISE_AFTER_NEW_ARRAY:
return NewArray;
case REMOVAL:
return Removal;
case UNSEEN:
return Unseen;
case ADVISE_BEFORE_RETURN:
return Return;
case ADVISE_BEFORE_GOTO:
return Goto;
case ADVISE_AFTER_MULTI_NEW_ARRAY:
// These are all value based so do not have a simple static mapping
case ADVISE_BEFORE_CONVERSION:
case ADVISE_BEFORE_PUT_FIELD:
case ADVISE_BEFORE_PUT_STATIC:
case ADVISE_BEFORE_ARRAY_STORE:
case ADVISE_AFTER_ARRAY_LOAD:
case ADVISE_BEFORE_IF:
case ADVISE_BEFORE_OPERATION:
case ADVISE_BEFORE_LOAD:
case ADVISE_AFTER_LOAD:
case ADVISE_BEFORE_STORE:
case ADVISE_BEFORE_CONST_LOAD:
}
throw new TraceException("unimplemented case");
}
public static void main(String[] args) {
QueryAnalysis.main(args);
}
}