com.samsung.memoryanalysis.traceparser.TraceAnalysisRunner.java Source code

Java tutorial

Introduction

Here is the source code for com.samsung.memoryanalysis.traceparser.TraceAnalysisRunner.java

Source

/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * 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.
 */
/*
 * Copyright 2014 Samsung Information Systems America, Inc.
 *
 * 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 com.samsung.memoryanalysis.traceparser;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.functions.VoidFunction;
import com.samsung.memoryanalysis.traceparser.SourceMap.SourceLocId;

/**
 *
 * @author s.jensen
 */
public class TraceAnalysisRunner {

    private final DataInputStream trace;

    private final JsonParser parser = new JsonParser();

    private final FreeVariables fvMap;

    private final SourceMap iidMap;

    private final ProgressMonitor progress;

    private int traceSize = 0;

    /**
     * if set to true, ignore the last use entries in the input trace
     * @see EnhancedTraceAnalysisRunner
     */
    protected boolean ignoreLastUse = false;

    protected boolean ignoreUpdIID = false;

    public TraceAnalysisRunner(InputStream trace, ProgressMonitor progress, File dir)
            throws FileNotFoundException, IOException {
        this.trace = new DataInputStream(trace);
        fvMap = buildFVMap(dir);
        iidMap = new SourceMap();//SourceMap.parseIIDFile(dir);
        this.progress = progress;

    }

    //    /**
    //     *
    //     * @param arr
    //     * @return arr[idx] as int
    //     */
    //    private int getInt(JsonArray arr, int idx) {
    //        return arr.get(idx).getAsInt();
    //    }
    //
    //    /**
    //     *
    //     * @param arr
    //     * @param idx
    //     * @return arr[idx] as String
    //     */
    //    private String getString(JsonArray arr, int idx) {
    //        return arr.get(idx).getAsString();
    //    }

    private FreeVariables buildFVMap(File dir) throws IOException {
        File fvFile = new File(dir, "freevars.json");
        FreeVariables res = new FreeVariables();
        if (fvFile.exists()) {
            JsonObject json = null;
            try {
                json = parser.parse(new BufferedReader(new FileReader(fvFile))).getAsJsonObject();// parser.parse(js.get());
            } catch (JsonParseException ex) {
                throw new IOException("Invalid free-variables map file: " + ex.getMessage());
            }

            for (Map.Entry<String, JsonElement> entry : json.entrySet()) {
                JsonElement o = entry.getValue();
                if (o.isJsonPrimitive() && o.getAsJsonPrimitive().isString())
                    res.put(Integer.parseInt(entry.getKey()), FreeVariables.ANY);
                else {
                    JsonArray a = o.getAsJsonArray();
                    final Set<String> freeVars = HashSetFactory.make();
                    for (int i = 0; i < a.size(); i++) {
                        final JsonElement jsonElement = a.get(i);
                        freeVars.add(jsonElement.getAsString());
                    }
                    res.put(Integer.parseInt(entry.getKey()), freeVars);
                }
            }
        }
        return res;
    }

    private byte[] buf = new byte[4];
    private ByteBuffer byteBuf = ByteBuffer.wrap(buf);

    private int readInt() throws IOException {
        byteBuf.rewind();
        trace.readFully(buf);

        return byteBuf.getInt();
    }

    private String readString() throws IOException {
        int length = readInt();
        byte[] strData = new byte[length];
        trace.readFully(strData);

        return new String(strData, "UnicodeLittleUnmarked");
    }

    public <T> T runAnalysis(TraceAnalysis<T> a) throws FileNotFoundException, IOException {
        TraceTimer timer = new TraceTimer();
        a.init(timer, iidMap);
        int counter = 0;
        int currentScriptId = -1;
        if (progress != null)
            progress.start(this.traceSize);
        int evtTypeInt = 0;
        TraceEntry evtType;
        while ((evtTypeInt = trace.read()) != -1) {
            evtType = TraceEntry.values()[evtTypeInt];
            switch (evtType) {
            case DECLARE: {
                int iid = readInt();
                String name = readString();
                int objId = readInt();
                a.declare(new SourceLocId(currentScriptId, iid), name, objId);
                break;
            }
            case CREATE_OBJ: {
                int iid = readInt();
                int objId = readInt();
                invokeCreateCallback(a, currentScriptId, iid, objId);
                break;
            }
            case CREATE_FUN: {
                int iid = readInt();
                int funcEnterIID = readInt();
                int objId = readInt();
                int protoId = objId + 1;
                a.createFun(new SourceLocId(currentScriptId, iid), objId, protoId,
                        new SourceLocId(currentScriptId, funcEnterIID), fvMap.getFreeVariables(funcEnterIID));
                break;
            }
            case PUTFIELD: {
                int iid = readInt();
                int baseid = readInt();
                String propName = null;
                propName = readString();
                int objId = readInt();
                a.putField(new SourceLocId(currentScriptId, iid), baseid, propName, objId);
                break;
            }
            case WRITE: {
                int iid = readInt();
                String name = readString();
                int objId = readInt();
                a.write(new SourceLocId(currentScriptId, iid), name, objId);
                break;
            }
            case LAST_USE: {
                int objId = readInt();
                int time = readInt();
                //                        assert time == timer.currentTime();
                String[] theSplit = readString().split(":");
                SourceLocId slId = new SourceLocId(Integer.parseInt(theSplit[0]), Integer.parseInt(theSplit[1]));
                if (!ignoreLastUse)
                    a.lastUse(objId, slId, time);
                break;
            }
            case FUNCTION_ENTER: {
                int iid = readInt();//getInt(arr, 1);
                int funId = readInt();//getInt(arr, 2);
                a.functionEnter(new SourceLocId(currentScriptId, iid), funId, SourceMap.UNKNOWN_ID);
                break;
            }
            case FUNCTION_EXIT: {
                // HACK: for function exit, we want the effects of this time to be taken into
                // account before we actually process a function exit.  E.g., if a client is
                // maintaining a call stack in its functionExit() callback, we want events associated
                // with the current function to be processed before the call stack is updated
                handleTime(timer.currentTime(), a);
                int iid = readInt();
                a.functionExit(new SourceLocId(currentScriptId, iid));
                break;
            }
            case TOP_LEVEL_FLUSH: {
                String[] theSplit = readString().split(":");
                a.topLevelFlush(new SourceLocId(Integer.parseInt(theSplit[0]), Integer.parseInt(theSplit[1])));
                break;
            }
            case UPDATE_IID: {
                int objId = readInt();//getInt(arr, 1);
                int newIID = readInt();//getInt(arr, 2);
                if (!ignoreUpdIID)
                    a.updateIID(objId, new SourceLocId(currentScriptId, newIID));
                break;
            }
            case DEBUG: {
                int iid = readInt();//getInt(arr, 1);
                int o = readInt();//getInt(arr, 2);
                a.debug(new SourceLocId(currentScriptId, iid), o);
                break;
            }
            case RETURN: {
                int retVal = readInt();//getInt(arr, 1);
                a.returnStmt(retVal);
                break;
            }
            case CREATE_DOM_NODE: {
                int iid = readInt();//getInt(arr, 1);
                int o = readInt();//getInt(arr, 2);
                a.createDomNode(new SourceLocId(currentScriptId, iid), o);
                break;
            }
            case ADD_DOM_CHILD: {
                int parent = readInt();//getInt(arr,1);
                int child = readInt();//getInt(arr,2);
                a.addDOMChild(parent, child);
                break;
            }
            case REMOVE_DOM_CHILD: {
                int parent = readInt();//getInt(arr,1);
                int child = readInt();//getInt(arr,2);
                a.removeDOMChild(parent, child);
                break;
            }
            case ADD_TO_CHILD_SET: {
                int iid = readInt();//getInt(arr,1);
                int parent = readInt();//getInt(arr,2);
                String name = readString();//getString(arr,3);
                int child = readInt();//getInt(arr,4);
                a.addToChildSet(new SourceLocId(currentScriptId, iid), parent, name, child);
                break;
            }
            case REMOVE_FROM_CHILD_SET: {
                int iid = readInt();//getInt(arr,1);
                int parent = readInt();//getInt(arr,2);
                String name = readString();//getString(arr,3);
                int child = readInt();//getInt(arr,4);
                a.removeFromChildSet(new SourceLocId(currentScriptId, iid), parent, name, child);
                break;
            }
            case DOM_ROOT: {
                int nodeId = readInt();//getInt(arr, 1);
                a.domRoot(nodeId);
                break;
            }
            case CALL: {
                int iid = readInt();//getInt(arr,1);
                int funObjId = readInt();//getInt(arr,2);
                int funEnterIID = readInt();//getInt(arr,3);
                int funSID = readInt();
                a.functionEnter(new SourceLocId(funSID, funEnterIID), funObjId,
                        new SourceMap.SourceLocId(currentScriptId, iid));
                break;
            }
            case SCRIPT_ENTER: {
                int iid = readInt();
                int sid = readInt();
                String filename = readString();
                iidMap.addScriptMapping(sid, filename);
                a.scriptEnter(new SourceLocId(sid, iid), filename);
                break;
            }
            case SCRIPT_EXIT: {
                int iid = readInt();//getInt(arr, 1);
                a.scriptExit(new SourceLocId(currentScriptId, iid));
                break;
            }
            case FREE_VARS: {
                int iid = readInt();//getInt(arr, 1);
                int len = readInt();
                if (len == -1) {
                    //string
                    readString();
                    fvMap.put(iid, FreeVariables.ANY);
                } else {
                    Set<String> names = HashSetFactory.make(len);
                    for (int i = 0; i < len; i++) {
                        names.add(readString());
                    }
                    fvMap.put(iid, names);
                }
                break;
            }
            case SOURCE_MAPPING:
                int iid = readInt();
                int startLine = readInt();
                int startColumn = readInt();
                int endLine = readInt();
                int endColumn = readInt();
                iidMap.addMapping(new SourceMap.SourceLocId(currentScriptId, iid), startLine, startColumn, endLine,
                        endColumn);
                break;
            case UPDATE_CURRENT_SCRIPT:
                currentScriptId = readInt();
                break;
            case END_LAST_USE:
                if (!ignoreLastUse)
                    a.endLastUse();
                break;
            case UNREACHABLE:
                break;
            }
            // don't tick timer for metadata entries
            if (!METADATA_ENTRIES.contains(evtType.ordinal())) {
                // if it wasn't a metadata entry, it was the event that
                // actually corresponds to the current time
                if (!evtType.equals(TraceEntry.FUNCTION_EXIT)) { // FUNCTION_EXIT handled above
                    handleTime(timer.currentTime(), a);
                }
                timer.tick();
            }
            if (this.progress != null)
                progress.tick(counter);
            counter++;
        }
        if (this.progress != null)
            progress.tick(this.traceSize);
        trace.close();
        // we need to undo the last tick, as execution is now over
        // and we want the current time to correspond to the final entry
        timer.rewindOneTick();
        return a.endExecution();
    }

    protected <T> void invokeCreateCallback(TraceAnalysis<T> a, int currentScriptId, int iid, int objId) {
        a.create(new SourceLocId(currentScriptId, iid), objId);
    }

    protected <T> void handleTime(long currentTime, TraceAnalysis<T> a) {
        // override in subclasses
    }

    private static class TraceTimer implements Timer {

        private long timeInternal = 0;

        //TODO: Use a PriorityQueue
        private SortedMap<Long, List<VoidFunction<Long>>> alarms = null;

        @Override
        public long currentTime() {
            return timeInternal;
        }

        public void rewindOneTick() {
            timeInternal--;
        }

        @Override
        public void registerAlarm(long atTime, VoidFunction<Long> callback) {
            if (alarms == null)
                alarms = new TreeMap<Long, List<VoidFunction<Long>>>();
            List<VoidFunction<Long>> cbs = alarms.get(atTime);
            if (cbs == null) {
                cbs = new ArrayList<VoidFunction<Long>>();
                alarms.put(atTime, cbs);
            }
            cbs.add(callback);
        }

        private void tick() {
            if (alarms != null && alarms.firstKey() == timeInternal) {
                List<VoidFunction<Long>> list = alarms.get(timeInternal);
                for (VoidFunction<Long> c : list) {
                    c.apply(timeInternal);
                }
                alarms.remove(timeInternal);
                if (alarms.isEmpty())
                    alarms = null;
            }
            timeInternal++;
        }

    }

    /**
     * This enum is lifted from the LoggingAnalysis.ts.
     */
    // IID special values: -1 is unknown, -2 corresponds to the initial
    // DOM traversal to attach mutation observers
    public static enum TraceEntry {
        DECLARE, // fields: iid, name, obj-id
        CREATE_OBJ, // fields: iid, obj-id
        CREATE_FUN, // fields: iid, function-enter-iid, obj-id.  NOTE: proto-obj-id is always obj-id + 1
        PUTFIELD, // fields: iid, base-obj-id, prop-name, val-obj-id
        WRITE, // fields: iid, name, obj-id
        LAST_USE, // fields: obj-id, timestamp, sourceId (sid + ':' + iid)
        FUNCTION_ENTER, // fields: iid, function-object-id.  NOTE: only emitted when CALL is not emitted
        FUNCTION_EXIT, // fields: iid
        TOP_LEVEL_FLUSH, // fields: sourceId (sid + ':' + iid)
        UPDATE_IID, // fields: obj-id, new-iid
        DEBUG, // fields: call-iid, obj-id
        RETURN, // fields: obj-id
        CREATE_DOM_NODE, // fields: iid, obj-id
        ADD_DOM_CHILD, // fields: parent-obj-id, child-obj-id
        REMOVE_DOM_CHILD, // fields: parent-obj-id, child-obj-id
        ADD_TO_CHILD_SET, // fields: iid, parent-obj-id, name, child-obj-id
        REMOVE_FROM_CHILD_SET, // fields: iid, parent-obj-id, name, child-obj-id
        DOM_ROOT, // fields: obj-id
        CALL, // fields: iid, function-obj-id, function-enter-iid, fun-sid.  NOTE: only emitted for calls to *instrumented* functions
        SCRIPT_ENTER, // fields: iid, scriptId, filename
        SCRIPT_EXIT, // fields: iid
        FREE_VARS, // fields: iid, array-of-names or ANY
        SOURCE_MAPPING, // fields: iid, startLine, startColumn, endLine, endColumn
        UPDATE_CURRENT_SCRIPT, // fields: scriptID
        END_LAST_USE, // fields: none
        UNREACHABLE // fields: sourceId (sid + ':' + iid), object-id, time
    }

    /**
     * trace entries that are considered "metadata" not corresponding directly
     * to program events.  such entries do not increment the trace timestamp
     */
    public static List<Integer> METADATA_ENTRIES = Collections
            .unmodifiableList(Arrays.asList(TraceEntry.LAST_USE.ordinal(), TraceEntry.FREE_VARS.ordinal(),
                    TraceEntry.SOURCE_MAPPING.ordinal(), TraceEntry.UPDATE_CURRENT_SCRIPT.ordinal(),
                    TraceEntry.END_LAST_USE.ordinal(), TraceEntry.UNREACHABLE.ordinal()));

}