// Path: ProfileData/src/main/java/net/jproffa/profiledata/ProfileData.java
// public class ProfileData {
// 
//     /**
//      * Holds information how many times basic block has been called. Indexes are the ones returned by addBasicBlock().
//      */
//     public static long[] callsToBasicBlock;
//     private static int basicBlockAmount = 0;
//     /**
//      * How costly a basic block is. Naive way to count it is by assuming every bytecodes cost is 1 and multiply by
//      * amount of bytecodes in the basic block.
//      */
//     public static long[] basicBlockCost;
//     private static List<Long> basicBlockCostAccumulator = new ArrayList<Long>();
//     private static List<String> basicBlockDesc = new ArrayList<String>();
//     private static Map<String, Integer> methodStarterBlockMap = new HashMap<String, Integer>();
// 
//     private static boolean profilingEnabled = false;
// 
//     /**
//      * Adds basic block with default cost (1).
//      *
//      * @return Index of basic block in arrays.
//      */
//     public static int addBasicBlock() {
//         return addBasicBlock(1L);
//     }
// 
//     /**
//      * Adds a new basic block.
//      *
//      * @param cost Cost of the basic block.
//      * @return Index of the basic block in the arrays.
//      */
//     public static int addBasicBlock(long cost) {
//         return addBasicBlock(cost, "", false);
//     }
// 
//     /**
//      * Adds a new basic block with a description.
//      *
//      * @param cost Cost of the basic block.
//      * @param desc Description of the basic block.
//      * @return Index of the basic block in the arrays.
//      */
//     public static synchronized int addBasicBlock(long cost, String desc, boolean starter) {
//         basicBlockAmount++;
//         basicBlockCostAccumulator.add(cost);
//         basicBlockDesc.add(desc == null ? "" : desc);
//         if (starter) {
//             methodStarterBlockMap.put(desc, basicBlockAmount - 1);
//         }
//         return basicBlockAmount - 1;
//     }
// 
//     public static void incrementCallsToBasicBlock(int index) {
//         if (profilingEnabled) {
//             callsToBasicBlock[index] += 1;
//         }
//     }
// 
//     /**
//      * Reset callsToBasicBlock arrays elements to 0.
//      */
//     public static synchronized void resetCounters() {
//         if (callsToBasicBlock == null) {
//             initialize();
//         }
//         for (int i = 0; i < callsToBasicBlock.length; i++) {
//             callsToBasicBlock[i] = 0L;
//         }
//     }
// 
//     /**
//      * Initialize arrays from added basic blocks. Needs to be called before using the arrays.
//      */
//     public static synchronized void initialize() {
//         long[] newCallsToBasicBlock = new long[basicBlockAmount];
//         if (callsToBasicBlock != null) {
//             System.arraycopy(callsToBasicBlock, 0, newCallsToBasicBlock, 0, callsToBasicBlock.length);
//         }
//         callsToBasicBlock = newCallsToBasicBlock;
// 
//         basicBlockCost = new long[basicBlockCostAccumulator.size()];
//         for (int i = 0; i < basicBlockCostAccumulator.size(); i++) {
//             basicBlockCost[i] = basicBlockCostAccumulator.get(i);
//         }
//     }
// 
//     public static long[] getCallsToBasicBlock() {
//         return callsToBasicBlock;
//     }
// 
//     public static int getBasicBlockAmount() {
//         return basicBlockAmount;
//     }
// 
//     public static long[] getBasicBlockCost() {
//         return basicBlockCost;
//     }
// 
//     public static List<String> getBasicBlockDesc() {
//         return basicBlockDesc;
//     }
// 
// 
//     /**
//      * Are counter increments allowed.
//      *
//      * @return
//      */
//     public static boolean isProfilingEnabled() {
//         return profilingEnabled;
//     }
// 
//     /**
//      * Disallow counter increments.
//      */
//     public static void disableProfiling() {
//         profilingEnabled = false;
//     }
// 
//     /**
//      * Allow counter increments.
//      */
//     public static void enableProfiling() {
//         profilingEnabled = true;
//     }
// 
//     public static Map<String, Integer> getMethodStarterBlockMap() {
//         return methodStarterBlockMap;
//     }
// }


import com.sun.tools.attach.VirtualMachine;
import net.jproffa.profiledata.ProfileData;
import org.apache.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import static org.objectweb.asm.Opcodes.*;
import org.objectweb.asm.tree.*;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.objectweb.asm.Type;
import java.util.Map.Entry;
import static org.objectweb.asm.tree.AbstractInsnNode.*;
     */
    public static void loadAgent() {
        if (agentLoaded) {
            return;
        }
        String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
        String pid = nameOfRunningVM.substring(0, nameOfRunningVM.indexOf('@'));
        // System.out.println("VM pid: " + pid);
        try {
            VirtualMachine vm = VirtualMachine.attach(pid);
            File jarFile = new File(Util.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
            String profilerJarPath = jarFile.getPath();
            vm.loadAgent(profilerJarPath);
            agentLoaded = true;
            vm.detach();
        } catch (NoClassDefFoundError e) {
            throw new RuntimeException("NoClassDefFoundError thrown: tools.jar probably not loaded.", e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Gets total cost. This is counted by multiplying the amount of calls to a basic block by its cost and adding them
     * together.
     *
     * @return Total cost of execution.
     */
    public static long getTotalCost() {
        checkAgentIsLoaded();
// Path: ProfileData/src/main/java/net/jproffa/profiledata/ProfileData.java
// public class ProfileData {
// 
//     /**
//      * Holds information how many times basic block has been called. Indexes are the ones returned by addBasicBlock().
//      */
//     public static long[] callsToBasicBlock;
//     private static int basicBlockAmount = 0;
//     /**
//      * How costly a basic block is. Naive way to count it is by assuming every bytecodes cost is 1 and multiply by
//      * amount of bytecodes in the basic block.
//      */
//     public static long[] basicBlockCost;
//     private static List<Long> basicBlockCostAccumulator = new ArrayList<Long>();
//     private static List<String> basicBlockDesc = new ArrayList<String>();
//     private static Map<String, Integer> methodStarterBlockMap = new HashMap<String, Integer>();
// 
//     private static boolean profilingEnabled = false;
// 
//     /**
//      * Adds basic block with default cost (1).
//      *
//      * @return Index of basic block in arrays.
//      */
//     public static int addBasicBlock() {
//         return addBasicBlock(1L);
//     }
// 
//     /**
//      * Adds a new basic block.
//      *
//      * @param cost Cost of the basic block.
//      * @return Index of the basic block in the arrays.
//      */
//     public static int addBasicBlock(long cost) {
//         return addBasicBlock(cost, "", false);
//     }
// 
//     /**
//      * Adds a new basic block with a description.
//      *
//      * @param cost Cost of the basic block.
//      * @param desc Description of the basic block.
//      * @return Index of the basic block in the arrays.
//      */
//     public static synchronized int addBasicBlock(long cost, String desc, boolean starter) {
//         basicBlockAmount++;
//         basicBlockCostAccumulator.add(cost);
//         basicBlockDesc.add(desc == null ? "" : desc);
//         if (starter) {
//             methodStarterBlockMap.put(desc, basicBlockAmount - 1);
//         }
//         return basicBlockAmount - 1;
//     }
// 
//     public static void incrementCallsToBasicBlock(int index) {
//         if (profilingEnabled) {
//             callsToBasicBlock[index] += 1;
//         }
//     }
// 
//     /**
//      * Reset callsToBasicBlock arrays elements to 0.
//      */
//     public static synchronized void resetCounters() {
//         if (callsToBasicBlock == null) {
//             initialize();
//         }
//         for (int i = 0; i < callsToBasicBlock.length; i++) {
//             callsToBasicBlock[i] = 0L;
//         }
//     }
// 
//     /**
//      * Initialize arrays from added basic blocks. Needs to be called before using the arrays.
//      */
//     public static synchronized void initialize() {
//         long[] newCallsToBasicBlock = new long[basicBlockAmount];
//         if (callsToBasicBlock != null) {
//             System.arraycopy(callsToBasicBlock, 0, newCallsToBasicBlock, 0, callsToBasicBlock.length);
//         }
//         callsToBasicBlock = newCallsToBasicBlock;
// 
//         basicBlockCost = new long[basicBlockCostAccumulator.size()];
//         for (int i = 0; i < basicBlockCostAccumulator.size(); i++) {
//             basicBlockCost[i] = basicBlockCostAccumulator.get(i);
//         }
//     }
// 
//     public static long[] getCallsToBasicBlock() {
//         return callsToBasicBlock;
//     }
// 
//     public static int getBasicBlockAmount() {
//         return basicBlockAmount;
//     }
// 
//     public static long[] getBasicBlockCost() {
//         return basicBlockCost;
//     }
// 
//     public static List<String> getBasicBlockDesc() {
//         return basicBlockDesc;
//     }
// 
// 
//     /**
//      * Are counter increments allowed.
//      *
//      * @return
//      */
//     public static boolean isProfilingEnabled() {
//         return profilingEnabled;
//     }
// 
//     /**
//      * Disallow counter increments.
//      */
//     public static void disableProfiling() {
//         profilingEnabled = false;
//     }
// 
//     /**
//      * Allow counter increments.
//      */
//     public static void enableProfiling() {
//         profilingEnabled = true;
//     }
// 
//     public static Map<String, Integer> getMethodStarterBlockMap() {
//         return methodStarterBlockMap;
//     }
// }


// Path: Profiler/src/main/java/net/jproffa/profiler/Util.java
import com.sun.tools.attach.VirtualMachine;
import net.jproffa.profiledata.ProfileData;
import org.apache.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import static org.objectweb.asm.Opcodes.*;
import org.objectweb.asm.tree.*;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.objectweb.asm.Type;
import java.util.Map.Entry;
import static org.objectweb.asm.tree.AbstractInsnNode.*;

     */
    public static void loadAgent() {
        if (agentLoaded) {
            return;
        }
        String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
        String pid = nameOfRunningVM.substring(0, nameOfRunningVM.indexOf('@'));
        // System.out.println("VM pid: " + pid);
        try {
            VirtualMachine vm = VirtualMachine.attach(pid);
            File jarFile = new File(Util.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
            String profilerJarPath = jarFile.getPath();
            vm.loadAgent(profilerJarPath);
            agentLoaded = true;
            vm.detach();
        } catch (NoClassDefFoundError e) {
            throw new RuntimeException("NoClassDefFoundError thrown: tools.jar probably not loaded.", e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Gets total cost. This is counted by multiplying the amount of calls to a basic block by its cost and adding them
     * together.
     *
     * @return Total cost of execution.
     */
    public static long getTotalCost() {
        checkAgentIsLoaded();
        long[] callsToBasicBlock = ProfileData.getCallsToBasicBlock();