Package com.insightfullogic.honest_profiler.core.collector

Source Code of com.insightfullogic.honest_profiler.core.collector.LogCollector

/**
* Copyright (c) 2014 Richard Warburton (richard.warburton@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
package com.insightfullogic.honest_profiler.core.collector;

import com.insightfullogic.honest_profiler.core.ProfileListener;
import com.insightfullogic.honest_profiler.core.parser.EventListener;
import com.insightfullogic.honest_profiler.core.parser.Method;
import com.insightfullogic.honest_profiler.core.parser.StackFrame;
import com.insightfullogic.honest_profiler.core.parser.TraceStart;

import java.util.*;

import static java.util.Comparator.comparing;
import static java.util.Map.Entry;
import static java.util.stream.Collectors.toList;

/**
* NB: Stack trace elements come in the opposite way to the profile
*/
public class LogCollector implements EventListener {

    private static final Comparator<Entry<Long,CallCounts>> sortByCount = comparing((Entry<Long, CallCounts> entry) -> entry.getValue().timeInvokingThis)
                                                                         .reversed();
    public static final int NOT_AWAITING = -1;

    private final ProfileListener listener;

    private final Map<Long, Method> methodNames;
    private final Map<Long, CallCounts> callCounts;
    private final Map<Long, NodeCollector> treesByThread;
    private final Stack<StackFrame> reversalStack;

    private long currentThread;
    private NodeCollector currentTreeNode;

    private int traceCount;
    private boolean immediatelyEmitProfile;

    public LogCollector(ProfileListener listener, boolean immediatelyEmitProfile) {
        this.listener = listener;

        methodNames = new HashMap<>();
        callCounts = new HashMap<>();
        treesByThread = new HashMap<>();
        reversalStack = new Stack<>();

        this.immediatelyEmitProfile = immediatelyEmitProfile;
        traceCount = 0;
        currentTreeNode = null;
    }

    @Override
    public void handle(TraceStart traceStart) {
        collectThreadDump();
        emitProfileIfNeeded();
        traceCount++;
        // TODO: log traceStart.getNumberOfFrames();
        reversalStack.clear();
        currentThread = traceStart.getThreadId();
        currentTreeNode = null;
    }

    @Override
    public void handle(StackFrame stackFrame) {
        reversalStack.push(stackFrame);
    }

    private void collectThreadDump() {
        while (!reversalStack.empty()) {
            collectStackFrame(reversalStack.size() == 1, reversalStack.pop());
        }
    }

    private void collectStackFrame(boolean endOfTrace, StackFrame stackFrame) {
        long methodId = stackFrame.getMethodId();
        callCounts.compute(methodId, (key, callCounts) -> {
            if (callCounts == null) {
                callCounts = new CallCounts(0, 0);
            }

            callCounts.timeAppeared++;
            if (endOfTrace)
                callCounts.timeInvokingThis++;
            return callCounts;
        });

        if (currentTreeNode == null) {
            // might be null if the method hasn't been seen before
            // if this is the case then the handle(Method) will patch it
            currentTreeNode = treesByThread.compute(currentThread, (id, previous) -> {
                if (previous != null)
                    return previous.callAgain();

                return new NodeCollector(methodId);
            });
        } else {
            currentTreeNode = currentTreeNode.newChildCall(methodId);
        }
    }

    @Override
    public void handle(Method newMethod) {
        methodNames.put(newMethod.getMethodId(), newMethod);
        emitProfileIfNeeded();
    }

    @Override
    public void endOfLog() {
        collectThreadDump();
        emitProfile();
    }

    private void emitProfileIfNeeded() {
        if (traceCount > 0 && immediatelyEmitProfile) {
            emitProfile();
        }
    }

    private void emitProfile() {
        List<FlatProfileEntry> flatProfile = buildFlatProfile();
        List<ProfileTree> trees = buildTreeProfile();
        Profile profile = new Profile(traceCount, flatProfile, trees);
        listener.accept(profile);
    }

    private List<ProfileTree> buildTreeProfile() {
        return treesByThread.entrySet()
                            .stream()
                            .map(node -> {
                                final Long threadId = node.getKey();
                                final NodeCollector collector = node.getValue();
                                return new ProfileTree(threadId, collector.normalise(methodNames::get), collector.getNumberOfVisits());
                            })
                            .collect(toList());
    }

    private List<FlatProfileEntry> buildFlatProfile() {
        return callCounts.entrySet()
                         .stream()
                         .sorted(sortByCount)
                         .map(this::toFlatProfileEntry)
                         .collect(toList());
    }

    private FlatProfileEntry toFlatProfileEntry(Entry<Long, CallCounts> entry) {
        Method method = methodNames.get(entry.getKey());
        final CallCounts callCounts = entry.getValue();
        double totalTimeShare = (double) callCounts.timeAppeared / traceCount;
        double selfTimeShare = (double) callCounts.timeInvokingThis / traceCount;
        return new FlatProfileEntry(method, totalTimeShare, selfTimeShare);
    }

}
TOP

Related Classes of com.insightfullogic.honest_profiler.core.collector.LogCollector

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.