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

com.github.olivergondza.dumpling.query.TopContenders Maven / Gradle / Ivy

/*
 * The MIT License
 *
 * Copyright (c) 2014 Red Hat, Inc.
 *
 * 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.github.olivergondza.dumpling.query;

import java.io.PrintStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;

import com.github.olivergondza.dumpling.model.ProcessRuntime;
import com.github.olivergondza.dumpling.model.ProcessThread;
import com.github.olivergondza.dumpling.model.ThreadSet;

/**
 * Detect top-contenders, threads that block largest number of other threads.
 *
 * @author ogondza
 */
public final class TopContenders implements SingleThreadSetQuery> {

    private boolean showStackTraces = false;

    public TopContenders showStackTraces() {
        this.showStackTraces = true;
        return this;
    }

    /**
     * @param threads Thread subset to be considered as a potential contenders. All threads in runtime are considered as blocking threads.
     */
    @Override
    public @Nonnull <
            SetType extends ThreadSet,
            RuntimeType extends ProcessRuntime,
            ThreadType extends ProcessThread
    > Result query(SetType threads) {
        return new Result(threads, showStackTraces);
    }

    public final static class Result<
            SetType extends ThreadSet,
            RuntimeType extends ProcessRuntime,
            ThreadType extends ProcessThread
    > extends SingleThreadSetQuery.Result {

        private final @Nonnull Map contenders;
        private final @Nonnull SetType involved;
        private final @Nonnegative int blocked;

        /*package*/ Result(SetType threads, boolean showStacktraces) {
            super(showStacktraces);
            final Set involved = new LinkedHashSet();
            final Map contenders = new TreeMap(new Comparator() {
                @Override
                public int compare(ThreadType lhs, ThreadType rhs) {
                    int lhsSize = lhs.getBlockedThreads().size();
                    int rhsSize = rhs.getBlockedThreads().size();

                    if (lhsSize > rhsSize) return -1;
                    if (lhsSize < rhsSize) return 1;

                    return 0;
                }
            });

            for (ThreadType thread: threads) {
                SetType blocked = thread.getBlockedThreads();
                if (blocked.isEmpty()) continue;

                contenders.put(thread, blocked);
                involved.add(thread);
                for (ThreadType b: blocked) {
                    involved.add(b);
                }
            }

            this.contenders = Collections.unmodifiableMap(contenders);
            this.involved = threads.derive(involved);
            this.blocked = involved.size() - contenders.size();
        }

        public @Nonnull SetType getBlockers() {
            return involved.derive(contenders.keySet());
        }

        /**
         * Get threads blocked by a thread.
         *
         * @return null when there is none.
         */
        public @CheckForNull SetType blockedBy(ThreadType thread) {
            return contenders.get(thread);
        }

        @Override
        protected void printResult(PrintStream out) {
            for (Entry contention: contenders.entrySet()) {

                out.print("* ");
                out.println(contention.getKey().getHeader());
                int i = 1;
                for (ProcessThread blocked: contention.getValue()) {

                    out.printf("  (%d) ", i++);
                    out.println(blocked.getHeader());
                }
            }
        }

        @Override
        protected SetType involvedThreads() {
            return involved;
        }

        @Override
        protected void printSummary(PrintStream out) {
            int blocking = contenders.size();

            out.printf("Blocking threads: %d; Blocked threads: %d%n", blocking, blocked);
        }

        @Override
        public int exitCode() {
            return contenders.size();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy