/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.google.monitoring.runtime.instrumentation;

import java.util.HashMap;

/**
 *
 * @author Viktor Karabut
 */
public class AllocationSiteSampler implements Sampler {
    private final class AllocationEntry {
        public String desc;
        public StackTraceElement frame1;
        public StackTraceElement frame2;
        public StackTraceElement frame3;
        public StackTraceElement frame4;
        public long size = 0; // total size of allocated objects
        public int count = 0; // number of allocated object

        @Override
        public int hashCode() {
            return desc.hashCode()^frame1.hashCode()^frame2.hashCode()^frame3.hashCode()^frame4.hashCode();
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o instanceof AllocationEntry) {
                AllocationEntry e = (AllocationEntry)o;
                return desc.equals(e.desc)
                        && frame1.equals(e.frame1)
                        && frame2.equals(e.frame2)
                        && frame3.equals(e.frame3)
                        && frame4.equals(e.frame4);
            } else {
                return false;
            }
        }

        @Override
        protected AllocationEntry clone() {
            AllocationEntry c = new AllocationEntry();
            c.desc = desc;
            c.frame1 = frame1;
            c.frame2 = frame2;
            c.frame3 = frame3;
            c.frame4 = frame4;
            return c;
        }

        @Override
        public String toString() {
            return desc + '\n' + frame1 + '\n' + frame2 + '\n' + frame3 + '\n' + frame4;
        }

    }

    private final static int FIRST_STACK_FRAME = 3;
    private long counter = 0;
    private final long allocatorThread;
    
    private final static StackTraceElement EMPTY_FRAME = new StackTraceElement("null", "null", "null", 0);
    private final AllocationEntry DUMMY_ENTRY = new AllocationEntry();
    private final HashMap<AllocationEntry, AllocationEntry> map = new HashMap<AllocationEntry, AllocationEntry>(10000);
    private final Object lock = new Object();
    private long lockedThread = -1;

    public AllocationSiteSampler() {
        allocatorThread = Thread.currentThread().getId();
    }

public void sampleAllocation(int count, String desc, Object newObj, long size) {
    long threadId = Thread.currentThread().getId();

    synchronized (lock) {
        if (threadId == lockedThread) return;
        lockedThread = threadId;

        DUMMY_ENTRY.desc = desc;
        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
        DUMMY_ENTRY.frame1 = trace.length <= FIRST_STACK_FRAME     ? EMPTY_FRAME:trace[FIRST_STACK_FRAME   ];
        DUMMY_ENTRY.frame2 = trace.length <= FIRST_STACK_FRAME + 1 ? EMPTY_FRAME:trace[FIRST_STACK_FRAME + 1];
        DUMMY_ENTRY.frame3 = trace.length <= FIRST_STACK_FRAME + 2 ? EMPTY_FRAME:trace[FIRST_STACK_FRAME + 2];
        DUMMY_ENTRY.frame4 = trace.length <= FIRST_STACK_FRAME + 3 ? EMPTY_FRAME:trace[FIRST_STACK_FRAME + 3];

        AllocationEntry entry = map.get(DUMMY_ENTRY);
        if (entry == null) {
            entry = DUMMY_ENTRY.clone();
            map.put(entry, entry);
        }
        entry.count++;
        entry.size+=size;

        counter++;
        if (counter > 1000000) {
            System.out.println("Site count: " + map.size());
            counter = 0;
        }
        lockedThread = -1;
    }
}

}
