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

org.glowroot.agent.live.ThreadDumpService Maven / Gradle / Ivy

There is a newer version: 0.9.28
Show newest version
/*
 * Copyright 2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.glowroot.agent.live;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;

import org.glowroot.agent.shaded.google.common.collect.Lists;
import org.glowroot.agent.shaded.google.common.collect.Maps;
import org.glowroot.agent.shaded.google.common.collect.Ordering;
import org.glowroot.agent.shaded.google.common.primitives.Ints;
import org.glowroot.agent.shaded.google.common.primitives.Longs;

import org.glowroot.agent.impl.TransactionCollector;
import org.glowroot.agent.impl.TransactionRegistry;
import org.glowroot.agent.model.Transaction;
import org.glowroot.common.live.ImmutableAllThreads;
import org.glowroot.common.live.ImmutableOneThread;
import org.glowroot.common.live.LiveJvmService.AllThreads;
import org.glowroot.common.live.LiveJvmService.OneThread;

import static org.glowroot.agent.shaded.google.common.base.Preconditions.checkNotNull;

class ThreadDumpService {

    private final TransactionRegistry transactionRegistry;
    private final TransactionCollector transactionCollector;

    ThreadDumpService(TransactionRegistry transactionRegistry,
            TransactionCollector transactionCollector) {
        this.transactionRegistry = transactionRegistry;
        this.transactionCollector = transactionCollector;
    }

    AllThreads getAllThreads() {
        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        Map transactionsBefore = Maps.newHashMap();
        for (Transaction transaction : transactionRegistry.getTransactions()) {
            transactionsBefore.put(transaction.getThreadId(), transaction);
        }
        ThreadInfo[] threadInfos =
                threadBean.getThreadInfo(threadBean.getAllThreadIds(), Integer.MAX_VALUE);
        final Map matchedTransactions = Maps.newHashMap();
        for (Transaction transaction : transactionRegistry.getTransactions()) {
            if (transactionsBefore.get(transaction.getThreadId()) == transaction) {
                matchedTransactions.put(transaction.getThreadId(), transaction);
            }
        }
        long currentThreadId = Thread.currentThread().getId();
        ThreadInfo currentThreadInfo = null;
        List matchedThreadInfos = Lists.newArrayList();
        List unmatchedThreadInfos = Lists.newArrayList();
        for (ThreadInfo threadInfo : threadInfos) {
            long threadId = threadInfo.getThreadId();
            if (threadId == currentThreadId) {
                currentThreadInfo = threadInfo;
            } else if (matchedTransactions.containsKey(threadId)) {
                matchedThreadInfos.add(threadInfo);
            } else {
                unmatchedThreadInfos.add(threadInfo);
            }
        }
        // sort descending by total time
        Collections.sort(matchedThreadInfos, new MatchedThreadInfoOrdering(matchedTransactions));
        // sort descending by stack trace length
        Collections.sort(unmatchedThreadInfos, new UnmatchedThreadInfoOrdering());

        List matchedThreads = Lists.newArrayList();
        for (ThreadInfo threadInfo : matchedThreadInfos) {
            Transaction matchedTransaction = matchedTransactions.get(threadInfo.getThreadId());
            matchedThreads.add(createOneThread(threadInfo, matchedTransaction));
        }

        List unmatchedThreads = Lists.newArrayList();
        for (ThreadInfo threadInfo : unmatchedThreadInfos) {
            unmatchedThreads.add(createOneThread(threadInfo, null));
        }

        OneThread currentThread = null;
        if (currentThreadInfo != null) {
            Transaction matchedTransaction =
                    matchedTransactions.get(currentThreadInfo.getThreadId());
            currentThread = createOneThread(currentThreadInfo, matchedTransaction);
        }

        return ImmutableAllThreads.builder()
                .matchedThreads(matchedThreads)
                .unmatchedThreads(unmatchedThreads)
                .currentThread(currentThread)
                .build();
    }

    private OneThread createOneThread(ThreadInfo threadInfo,
            @Nullable Transaction matchedTransaction) {
        ImmutableOneThread.Builder builder = ImmutableOneThread.builder();
        builder.name(threadInfo.getThreadName());
        builder.state(threadInfo.getThreadState().name());
        builder.lockName(threadInfo.getLockName());
        for (StackTraceElement stackTraceElement : threadInfo.getStackTrace()) {
            builder.addStackTraceElements(stackTraceElement.toString());
        }

        if (matchedTransaction != null) {
            builder.transactionType(matchedTransaction.getTransactionType());
            builder.transactionName(matchedTransaction.getTransactionName());
            builder.transactionTotalNanos(matchedTransaction.getDurationNanos());
            if (transactionCollector.shouldStoreSlow(matchedTransaction)) {
                builder.traceId(matchedTransaction.getId());
            }
        }
        return builder.build();
    }

    private static class MatchedThreadInfoOrdering extends Ordering {

        private final Map matchedTransactions;

        private MatchedThreadInfoOrdering(Map matchedTransactions) {
            this.matchedTransactions = matchedTransactions;
        }

        @Override
        public int compare(ThreadInfo left, ThreadInfo right) {
            Transaction leftTransaction = matchedTransactions.get(left.getThreadId());
            Transaction rightTransaction = matchedTransactions.get(right.getThreadId());
            // left and right are from matchedThreadInfos so have corresponding transactions
            checkNotNull(leftTransaction);
            checkNotNull(rightTransaction);
            return Longs.compare(rightTransaction.getDurationNanos(),
                    leftTransaction.getDurationNanos());
        }
    }

    private static class UnmatchedThreadInfoOrdering extends Ordering {
        @Override
        public int compare(ThreadInfo left, ThreadInfo right) {
            if (left.getThreadId() == Thread.currentThread().getId()) {
                return 1;
            } else if (right.getThreadId() == Thread.currentThread().getId()) {
                return -1;
            }
            int result = Ints.compare(right.getStackTrace().length, left.getStackTrace().length);
            if (result == 0) {
                return left.getThreadName().compareToIgnoreCase(right.getThreadName());
            }
            return result;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy