Package com.jitlogic.zorka.core.spy

Source Code of com.jitlogic.zorka.core.spy.TraceBuilder

/**
* Copyright 2012-2014 Rafal Lewczuk <rafal.lewczuk@jitlogic.com>
* <p/>
* This is free software. You can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
* <p/>
* This software 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 for more details.
* <p/>
* You should have received a copy of the GNU General Public License
* along with this software. If not, see <http://www.gnu.org/licenses/>.
*/

package com.jitlogic.zorka.core.spy;


import com.jitlogic.zorka.common.stats.AgentDiagnostics;
import com.jitlogic.zorka.common.tracedata.SymbolRegistry;
import com.jitlogic.zorka.common.tracedata.TraceMarker;
import com.jitlogic.zorka.common.tracedata.TraceRecord;
import com.jitlogic.zorka.common.util.ZorkaLog;
import com.jitlogic.zorka.common.util.ZorkaLogger;

/**
* This class receives loose tracer submissions from single thread
* and constructs traces.
*
* @author rafal.lewczuk@jitlogic.com
*/
public class TraceBuilder {

    private final static ZorkaLog log = ZorkaLogger.getLog(TraceBuilder.class);


    /**
     * Output
     */
    private TracerOutput output;

    private SymbolRegistry symbols;

    /**
     * Top of trace records stack.
     */
    private TraceRecord ttop = new TraceRecord(null);

    private boolean disabled;

    /**
     * Number of records collected so far
     */
    private int numRecords = 0;


    /**
     * Creates new trace builder object.
     *
     * @param output object completed traces will be submitted to
     */
    public TraceBuilder(TracerOutput output, SymbolRegistry symbols) {
        this.output = output;
        this.symbols = symbols;
    }


    public void traceBegin(int traceId, long clock, int flags) {

        if (ttop == null) {
            log.error(ZorkaLogger.ZTR_TRACE_ERRORS, "Attempt to set trace marker on an non-traced method.");
            return;
        }

        if (ttop.hasFlag(TraceRecord.TRACE_BEGIN)) {
            log.error(ZorkaLogger.ZTR_TRACE_ERRORS, "Trace marker already set on current frame. Skipping.");
            return;
        } else {
            ttop.setMarker(new TraceMarker(ttop, traceId, clock));
            ttop.markFlag(TraceRecord.TRACE_BEGIN);
            ttop.getMarker().markFlags(flags);
        }
    }


    public void traceEnter(int classId, int methodId, int signatureId, long tstamp) {

        if (disabled) {
            return;
        }

        if (ZorkaLogger.isLogLevel(ZorkaLogger.ZTR_TRACER_DBG)) {
            if (ZorkaLogger.isLogLevel(ZorkaLogger.ZTR_TRACE_CALLS) ||
                    (ttop.inTrace() && ttop.getMarker().hasFlag(TraceMarker.TRACE_CALLS))) {
                log.trace(ZorkaLogger.ZTR_TRACER_DBG, "traceEnter("
                        + symbols.symbolName(classId) + "." + symbols.symbolName(methodId) + ")");
            }
        }

        if (!ttop.isEmpty()) {
            if (ttop.inTrace()) {
                ttop = new TraceRecord(ttop);
                numRecords++;
            } else {
                ttop.clean();
                numRecords = 0;
            }
        } else {
            numRecords++;
        }

        ttop.setClassId(classId);
        ttop.setMethodId(methodId);
        ttop.setSignatureId(signatureId);
        ttop.setTime(tstamp);
        ttop.setCalls(ttop.getCalls() + 1);

        if (numRecords > Tracer.getMaxTraceRecords()) {
            ttop.markFlag(TraceRecord.OVERFLOW_FLAG);
        }

    }


    public void traceReturn(long tstamp) {

        if (disabled) {
            return;
        }

        if (ZorkaLogger.isLogLevel(ZorkaLogger.ZTR_TRACER_DBG)) {
            if (ZorkaLogger.isLogLevel(ZorkaLogger.ZTR_TRACE_CALLS) ||
                    (ttop.inTrace() && ttop.getMarker().hasFlag(TraceMarker.TRACE_CALLS))) {
                TraceRecord tr = ttop;
                if (tr.getClassId() == 0 && tr.getParent() != null) {
                    tr = tr.getParent();
                }
                log.trace(ZorkaLogger.ZTR_TRACER_DBG, "traceReturn("
                        + symbols.symbolName(tr.getClassId()) + "." + symbols.symbolName(tr.getMethodId()) + ")");
            }
        }

        while (!(ttop.getClassId() != 0) && ttop.getParent() != null) {
            ttop = ttop.getParent();
        }

        ttop.setTime(tstamp - ttop.getTime());

        pop();
    }


    public void traceError(Object exception, long tstamp) {

        if (disabled) {
            return;
        }

        if (ZorkaLogger.isLogLevel(ZorkaLogger.ZTR_TRACER_DBG)) {
            if (ZorkaLogger.isLogLevel(ZorkaLogger.ZTR_TRACE_EXCEPTIONS) ||
                    (ttop.inTrace() && ttop.getMarker().hasFlag(TraceMarker.TRACE_CALLS))) {
                TraceRecord tr = ttop;
                if (tr.getClassId() == 0 && tr.getParent() != null) {
                    tr = tr.getParent();
                }
                log.trace(ZorkaLogger.ZTR_TRACER_DBG, "traceError(" + symbols.symbolName(tr.getClassId()) +
                        "." + symbols.symbolName(tr.getMethodId()) + ")", (Throwable) exception);
            }
        }

        while (!(ttop.getClassId() != 0) && ttop.getParent() != null) {
            ttop = ttop.getParent();
        }

        ttop.setException(exception);
        ttop.setTime(tstamp - ttop.getTime());
        ttop.setErrors(ttop.getErrors() + 1);

        pop();
    }


    public TraceRecord realTop() {
        if (ttop.isEmpty() && ttop.getParent() != null) {
            return ttop.getParent();
        } else {
            return ttop;
        }
    }


    public Object getAttr(int attrId) {
        return realTop().getAttr(attrId);
    }


    /**
     * Attaches attribute to current trace record (or any other record up the call stack).
     *
     * @param traceId positive number (trace id) if attribute has to be attached to a top record of specific
     *                trace, 0 if attribute has to be attached to a top record of any trace, -1 if attribute
     *                has to be attached to current method;
     * @param attrId  attribute ID
     * @param attrVal attribute value
     */
    public void newAttr(int traceId, int attrId, Object attrVal) {
        TraceRecord tr = realTop();

        while (tr != null) {
            if (traceId == -1 || (tr.hasFlag(TraceRecord.TRACE_BEGIN) &&
                    (traceId == 0 || tr.getMarker().getTraceId() == traceId))) {
                tr.setAttr(attrId, attrVal);
                break;
            }
            tr = tr.getParent();
        }
    }


    public void disable() {
        disabled = true;
    }


    public void enable() {
        disabled = false;
    }


    /**
     * This method it called at method return (normal or error). In general it pops current
     * trace record from top of stack but it also implements quite a bit of logic handling
     * various aspects of handling trace records (filtering, limiting number of records in one
     * frame, reusing trace record if suitable etc.).
     */
    private void pop() {

        boolean clean = true;

        TraceRecord parent = ttop.getParent();

        popException();


        // Submit data if trace marker found
        if (ttop.hasFlag(TraceRecord.TRACE_BEGIN)) {
            int flags = ttop.getMarker().getFlags();
            if ((ttop.getTime() >= ttop.getMarker().getMinimumTime() && 0 == (flags & TraceMarker.DROP_TRACE))
                    || 0 != (flags & TraceMarker.SUBMIT_TRACE)) {
                submit(ttop);
                AgentDiagnostics.inc(AgentDiagnostics.TRACES_SUBMITTED);
                clean = false;
            } else {
                AgentDiagnostics.inc(AgentDiagnostics.TRACES_DROPPED);
            }


            if (parent != null) {
                parent.getMarker().inheritFlags(ttop.getMarker().getFlags());
            }
        }

        // Determine how the top of stack should be rolled back
        if (parent != null) {
            if ((ttop.getTime() > Tracer.getMinMethodTime() || ttop.getErrors() > 0)
                    || 0 != (ttop.getMarker().getFlags() & TraceMarker.ALL_METHODS)) {


                if (!ttop.hasFlag(TraceRecord.OVERFLOW_FLAG)) {
                    reparentTop(parent);

                } else {
                    parent.getMarker().markFlags(TraceMarker.OVERFLOW_FLAG);
                }
                clean = false;
            }
            parent.setCalls(parent.getCalls() + ttop.getCalls());
            parent.setErrors(parent.getErrors() + ttop.getErrors());
        }


        if (clean) {
            ttop.clean();
            numRecords--;
        } else {
            if (parent != null) {
                ttop = parent;
            } else {
                ttop = new TraceRecord(null);
                numRecords = 0;
            }
        }

    }


    private void popException() {
        // Get rid of redundant exception object
        if (ttop.getException() != null && ttop.numChildren() > 0) {
            Object tex = ttop.getException();
            Object cex = ttop.getChild(ttop.numChildren() - 1).getException();
            if (cex == tex) {
                ttop.setException(null);
                ttop.markFlag(TraceRecord.EXCEPTION_PASS);
            } else if (cex == ((Throwable) tex).getCause()) {
                ttop.markFlag(TraceRecord.EXCEPTION_WRAP);
            }
        }
    }


    private void reparentTop(TraceRecord parent) {
        // Drop interim record if necessary
        if (ttop.getMarker().hasFlag(TraceMarker.DROP_INTERIM) && ttop.isInterimDroppable()
                && ttop.getTime() - ttop.getChild(0).getTime() < Tracer.getMinMethodTime()) {
            TraceRecord child = ttop.getChild(0);
            child.setCalls(ttop.getCalls());
            child.setErrors(ttop.getErrors());
            child.markFlag(TraceRecord.DROPPED_PARENT);
            numRecords--;
            parent.addChild(child);
        } else {
            parent.addChild(ttop);
        }
    }


    private void submit(TraceRecord record) {
        record.fixup(symbols);
        if (record.getException() != null || record.hasFlag(TraceRecord.EXCEPTION_PASS)) {
            record.getMarker().markFlags(TraceMarker.ERROR_MARK);
        }
        output.submit(record);
    }


    /**
     * Sets minimum trace execution time for currently recorded trace.
     * If there is no trace being recorded just yet, this method will
     * have no effect.
     *
     * @param minimumTraceTime (in nanoseconds)
     */
    public void setMinimumTraceTime(long minimumTraceTime) {
        TraceRecord top = realTop();
        if (top.inTrace()) {
            top.getMarker().setMinimumTime(minimumTraceTime);
        }
    }


    public void markTraceFlags(int traceId, int flag) {
        for (TraceRecord tr = realTop(); tr != null; tr = tr.getParent()) {
            TraceMarker tm = tr.getMarker();
            if (tm != null && (traceId == 0 || traceId == tm.getTraceId())) {
                tm.markFlags(flag);
                break;
            }
        }
    }


    public boolean isInTrace(int traceId) {
        for (TraceRecord tr = realTop(); tr != null; tr = tr.getParent()) {
            TraceMarker tm = tr.getMarker();
            if (tm != null && tm.getTraceId() == traceId) {
                return true;
            }
        }
        return false;
    }

}
TOP

Related Classes of com.jitlogic.zorka.core.spy.TraceBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.