All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.java.btrace.ext.profiling.MethodInvocationProfiler Maven / Gradle / Ivy

/*
 * Copyright (c) 2007, 2011, 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * 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 net.java.btrace.ext.profiling;

import java.util.HashMap;
import java.util.Map;

/**
 * Implementation of {@linkplain Profiler}
 * @author Jaroslav Bachorik
 */
class MethodInvocationProfiler extends Profiler implements Profiler.MBeanValueProvider {
    private static class MethodInvocationRecorder {
        private int stackSize = 200;
        private int stackPtr = -1;
        private int stackBndr = 150;
        private Record[] stackArr = new Record[stackSize];

        private int measuredSize = 0;
        private int measuredPtr = 0;
        private Record[] measured = new Record[0];

        private long carryOver = 0L;
        private int defaultBufferSize;

        public MethodInvocationRecorder(int expectedBlockCnt) {
            defaultBufferSize = expectedBlockCnt * 10;
            measuredSize = defaultBufferSize;
            measured = new Record[measuredSize];
        }
        
        private synchronized void recordEntry(String blockName) {
            Record r = new Record(blockName);
            addMeasured(r);
            push(r);
            carryOver = 0L; // clear the carryOver; not 2 subsequent calls to recordExit
        }

        private synchronized void recordExit(String blockName, long duration) {
            Record r = pop();
            if (r == null) {
                r = new Record(blockName);
                addMeasured(r);
            }
            r.wallTime = duration;
            r.selfTime += duration - carryOver;

            for(int i=0;i stackBndr) {
                stackSize *= 1.5;
                stackBndr *= 1.5;
                Record[] newStack = new Record[stackSize];
                System.arraycopy(stackArr, 0, newStack, 0, stackPtr + 1);
                stackArr = newStack;
            }
            stackArr[++stackPtr] = r;
            r.onStack = true;
        }

        private Record pop() {
            Record r = stackPtr > -1 ? stackArr[stackPtr--] : null;
            if (r != null) {
                r.onStack = false;
            }
            return r;
        }

        private Record peek() {
            return stackPtr > -1 ? stackArr[stackPtr] : null;
        }

        private void addMeasured(Record r) {
            if (measuredPtr == measuredSize) {
                compactMeasured();
            }
            measured[measuredPtr++] = r;
        }

        private synchronized void reset() {
            Record[] newMeasured = new Record[defaultBufferSize + stackPtr + 1];
            if (stackPtr > -1) {
                System.arraycopy(stackArr, 0, newMeasured, 0, stackPtr + 1);
            }
            measuredPtr = stackPtr + 1;
            measured = newMeasured;
            measuredSize = measured.length;
        }

        private int compactMeasured() {
            Map indexMap = new HashMap();
            int lastIndex = 0;
            for(int i=0;i mr.selfTimeMax ? m.selfTime : mr.selfTimeMax;
                        mr.selfTimeMin = m.selfTime < mr.selfTimeMin ? m.selfTime : mr.selfTimeMin;
                        mr.wallTimeMax = m.wallTime > mr.wallTimeMax ? m.wallTime : mr.wallTimeMax;
                        mr.wallTimeMin = m.wallTime < mr.wallTimeMin ? m.wallTime : mr.wallTimeMin;
                        for(int j=0;j recorders = new HashMap(128);


    volatile private Snapshot lastValidSnapshot = null;

    private int expectedBlockCnt;

    public MethodInvocationProfiler(int expectedMethodCnt) {
        this.expectedBlockCnt = expectedMethodCnt;
    }

    public void recordEntry(String blockName) {
        getThreadSampler().recordEntry(blockName);
    }

    public void recordExit(String blockName, long duration) {
        getThreadSampler().recordExit(blockName, duration);
    }

    public void reset() {
        synchronized(recorders) {
            for(MethodInvocationRecorder r : recorders.values()) {
                r.reset();
            }
        }
    }

    private long lastTs = START_TIME;

    public Snapshot snapshot(boolean reset) {
        synchronized(recorders) {
            Map idMap = new HashMap();

            Record[] mergedRecords = null;
            int mergedEntries = 0, mergedCapacity = 0;
            for(Map.Entry sEntry : recorders.entrySet()) {
                final Record[] records = sEntry.getValue().getRecords(reset);
                if (records == null || records.length == 0) continue; // just skip the empty data

                if (mergedRecords == null) {
                    mergedRecords = records;
                    mergedCapacity = mergedRecords.length;
                    for(int i=0;i mergedCapacity) {
                            mergedCapacity = (int)((mergedEntries + 1) * 1.25);
                            Record[] newRecs = new Record[mergedCapacity];
                            System.arraycopy(mergedRecords, 0, newRecs, 0, mergedEntries - 1);
                            mergedRecords = newRecs;
                        }
                        idMap.put(r.blockName, id);
                        mergedRecords[id] = r;
                    } else {
                        Record merged = mergedRecords[id];
                        merged.invocations += r.invocations;
                        merged.selfTime += r.selfTime;
                        merged.wallTime += r.wallTime;
                    }
                }
            }
            Record[] rslt = new Record[mergedEntries];
            if (mergedRecords != null) {
                System.arraycopy(mergedRecords, 0, rslt, 0, mergedEntries);
            }

            long curTs = System.currentTimeMillis();
            Snapshot snp = new Snapshot(rslt, lastTs, curTs);
            lastTs = curTs;
            lastValidSnapshot = snp;
            return snp;
        }
    }

    public Snapshot getMBeanValue() {
        return lastValidSnapshot;
    }
    
    private MethodInvocationRecorder getThreadSampler() {
        Thread t = Thread.currentThread();
        MethodInvocationRecorder s = recorders.get(t);
        if (s == null) {
            synchronized(recorders) {
                if (!recorders.containsKey(t)) {
                    s = new MethodInvocationRecorder(expectedBlockCnt);
                    recorders.put(t, s);
                }
            }
        }
        return s;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy