资讯专栏INFORMATION COLUMN

线上问题排查常见脚本工具

vvpvvp / 953人阅读

摘要:从所有的进程中找出最消耗的线程缺省个,打印出其线程栈用法要显示的线程栈数要显示的线程栈数指定的脚本找出库,即文件或目录类目录中的重复类。通过脚本参数指定目录,查找目录下文件,收集文件中文件以分析重复类。

show-busy-java-threads.sh

从所有的 Java 进程中找出最消耗 CPU 的线程(缺省5个),打印出其线程栈

用法
./show-busy-java-threads.sh
./show-busy-java-threads.sh -c <要显示的线程栈数>
./show-busy-java-threads.sh -c <要显示的线程栈数> -p <指定的Java Process>
脚本
#!/bin/bash
# @Function
# Find out the highest cpu consumed threads of java, and print the stack of these threads.
#
# @Usage
#   $ ./show-busy-java-threads.sh
#
# @author Jerry Lee

readonly PROG=`basename $0`
readonly -a COMMAND_LINE=("$0" "$@")

usage() {
    cat < /dev/null; then
    [ -z "$JAVA_HOME" ] && {
        redEcho "Error: jstack not found on PATH!"
        exit 1
    }
    ! [ -f "$JAVA_HOME/bin/jstack" ] && {
        redEcho "Error: jstack not found on PATH and $JAVA_HOME/bin/jstack file does NOT exists!"
        exit 1
    }
    ! [ -x "$JAVA_HOME/bin/jstack" ] && {
        redEcho "Error: jstack not found on PATH and $JAVA_HOME/bin/jstack is NOT executalbe!"
        exit 1
    }
    export PATH="$JAVA_HOME/bin:$PATH"
fi

readonly uuid=`date +%s`_${RANDOM}_$$

cleanupWhenExit() {
    rm /tmp/${uuid}_* &> /dev/null
}
trap "cleanupWhenExit" EXIT

printStackOfThreads() {
    local line
    local count=1
    while IFS=" " read -a line ; do
        local pid=${line[0]}
        local threadId=${line[1]}
        local threadId0x="0x`printf %x ${threadId}`"
        local user=${line[2]}
        local pcpu=${line[4]}

        local jstackFile=/tmp/${uuid}_${pid}

        [ ! -f "${jstackFile}" ] && {
            {
                if [ "${user}" == "${USER}" ]; then
                    jstack ${pid} > ${jstackFile}
                else
                    if [ $UID == 0 ]; then
                        sudo -u ${user} jstack ${pid} > ${jstackFile}
                    else
                        redEcho "[$((count++))] Fail to jstack Busy(${pcpu}%) thread(${threadId}/${threadId0x}) stack of java process(${pid}) under user(${user})."
                        redEcho "User of java process($user) is not current user($USER), need sudo to run again:"
                        yellowEcho "    sudo ${COMMAND_LINE[@]}"
                        echo
                        continue
                    fi
                fi
            } || {
                redEcho "[$((count++))] Fail to jstack Busy(${pcpu}%) thread(${threadId}/${threadId0x}) stack of java process(${pid}) under user(${user})."
                echo
                rm ${jstackFile}
                continue
            }
        }
        blueEcho "[$((count++))] Busy(${pcpu}%) thread(${threadId}/${threadId0x}) stack of java process(${pid}) under user(${user}):"
        sed "/nid=${threadId0x} /,/^$/p" -n ${jstackFile}
    done
}


ps -Leo pid,lwp,user,comm,pcpu --no-headers | {
    [ -z "${pid}" ] &&
    awk "$4=="java"{print $0}" ||
    awk -v "pid=${pid}" "$1==pid,$4=="java"{print $0}"
} | sort -k5 -r -n | head --lines "${count}" | printStackOfThreads
show-duplicate-java-classes

找出Java Lib(Java库,即Jar文件)或Class目录(类目录)中的重复类。

通过脚本参数指定Libs目录,查找目录下Jar文件,
收集Jar文件中Class文件以分析重复类。可以指定多个Libs目录。

注意,只会查找这个目录下Jar文件,不会查找子目录下Jar文件。
因为Libs目录一般不会用子目录再放Jar,这样也避免把去查找不期望Jar。
通过 -c 选项指定 Class 目录,直接收集这个目录下的 Class 文件以分析重复类。可以指定多个目录。

用法
# 查找当前目录下所有Jar中的重复类
./show-duplicate-java-classes

查找多个指定目录下所有Jar中的重复类
./show-duplicate-java-classes path/to/lib_dir1 /path/to/lib_dir2

查找多个指定Class目录下的重复类。 class 目录 通过 -c 选项指定
./show-duplicate-java-classes -c path/to/class_dir1 -c /path/to/class_dir2

查找指定Class目录和指定目录下所有Jar中的重复类的Jar
./show-duplicate-java-classes path/to/lib_dir1 /path/to/lib_dir2 -c path/to/class_dir1 -c path/to/class_dir2
脚本
#!/usr/bin/env python
# -*- coding: utf-8 -*-

__author__ = "tg123"

from glob import glob
from os import walk
from zipfile import ZipFile
from os.path import relpath, isdir
from optparse import OptionParser


def list_jar_file_under_lib_dirs(libs):
    jar_files = set()
    for lib in libs:
        if isdir(lib):
            jar_files |= {f for f in glob(lib + "/*.jar")}
        else:
            jar_files.add(lib)
    return jar_files


def list_class_under_jar_file(jar_file):
    return {f for f in ZipFile(jar_file).namelist() if f.lower().endswith(".class")}


def list_class_under_class_dir(class_dir):
    return {relpath(dir_path + "/" + filename, class_dir)
            for dir_path, _, file_names in walk(class_dir)
            for filename in file_names if filename.lower().endswith(".class")}


def expand_2_class_path(jar_files, class_dirs):
    java_class_2_class_paths = {}
    # list all classes in jar files
    for jar_file in jar_files:
        for class_file in list_class_under_jar_file(jar_file):
            java_class_2_class_paths.setdefault(class_file, set()).add(jar_file)
    # list all classes in class dir
    for class_dir in class_dirs:
        for class_file in list_class_under_class_dir(class_dir):
            java_class_2_class_paths.setdefault(class_file, set()).add(class_dir)

    return java_class_2_class_paths, jar_files | set(class_dirs)


def find_duplicate_classes(java_class_2_class_paths):
    class_path_2_duplicate_classes = {}

    for java_class, class_paths in list(java_class_2_class_paths.items()):
        if len(class_paths) > 1:
            classes = class_path_2_duplicate_classes.setdefault(frozenset(class_paths), set())
            classes.add(java_class)

    return class_path_2_duplicate_classes


def print_class_paths(class_paths):
    print()
    print("=" * 80)
    print("class paths to find:")
    print("=" * 80)
    for idx, class_path in enumerate(class_paths):
        print(("%-3d: %s" % (idx + 1, class_path)))


if __name__ == "__main__":
    optionParser = OptionParser("usage: %prog "
                                "[-c class-dir1 [-c class-dir2] ...] "
                                "[lib-dir1|jar-file1 [lib-dir2|jar-file2] ...]")
    optionParser.add_option("-c", "--class-dir", dest="class_dirs", default=[], action="append", help="add class dir")
    options, libs = optionParser.parse_args()

    if not options.class_dirs and not libs:
        libs = ["."]

    java_class_2_class_paths, class_paths = expand_2_class_path(
        list_jar_file_under_lib_dirs(libs), options.class_dirs)

    class_path_2_duplicate_classes = find_duplicate_classes(java_class_2_class_paths)

    if not class_path_2_duplicate_classes:
        print("COOL! No duplicate classes found!")
        print_class_paths(class_paths)
        exit()

    print("Found duplicate classes in below class path:")
    for idx, jars in enumerate(class_path_2_duplicate_classes):
        print("%-3d(%d@%d): %s" % (idx + 1, len(class_path_2_duplicate_classes[jars]), len(jars), " ".join(jars)))

    print()
    print("=" * 80)
    print("Duplicate classes detail info:")
    print("=" * 80)
    for idx, (jars, classes) in enumerate(class_path_2_duplicate_classes.items()):
        print("%-3d(%d@%d): %s" % (idx + 1, len(class_path_2_duplicate_classes[jars]), len(jars), " ".join(jars)))
        for i, c in enumerate(classes):
            print("	%-3d %s" % (i + 1, c))

    print_class_paths(class_paths)
    exit(1)

参考:https://github.com/oldratlee/...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70305.html

相关文章

  • 通过btrace排查线上频繁Full GC的case

    摘要:概述又是一次因为线上报警机制开启的排查问题之旅。的常见使用场景有分析哪些方法调用,获取其调用栈接口性能差,分析耗时情况当出现异常时,分析方法的运行时参数线上有一个大对象,查看其内容安装使用依赖于,首先要安装好并配置的环境变量。 概述 又是一次因为线上报警机制开启的排查问题之旅。某日,钉钉机器人疯狂报警:showImg(https://segmentfault.com/img/bVbal...

    mochixuan 评论0 收藏0
  • 不改一行代码定位线上性能问题

    摘要:背景最近时运不佳,几乎天天被线上问题骚扰。工具分析所以最好的方式就是不改动一行代码把这个问题分析出来。我们选用了阿里以前开源的来使用。因为这个项目阿里多年没有维护了,还残留一些我在它原有的基础上修复了个影响使用的,同时做了一些优化。 showImg(https://segmentfault.com/img/remote/1460000016978923?w=1920&h=1080); ...

    DangoSky 评论0 收藏0
  • 一次线上问题排查所引发的思考

    摘要:直到有一天你会碰到线上奇奇怪怪的问题,如线程执行一个任务迟迟没有返回,应用假死。正好这次借助之前的一次生产问题来聊聊如何排查和解决问题。本地模拟上文介绍的是线程相关问题,现在来分析下内存的问题。尽可能的减少多线程竞争锁。 showImg(https://segmentfault.com/img/remote/1460000015568421?w=2048&h=1150); 前言 之前或...

    levy9527 评论0 收藏0
  • 线上系统性问题定位与方法论

    摘要:很显然对于不同规模,不同功能的系统,这个问题无法一概而论。生产事件上报客服上报此类问题往往来自用户投诉,最重要的就是问题现象的复现。线上问题处理的核心是快速修复。以上说的都是问题发生后的消极应对措施。 前言一线程序员在工作中经常需要处理线上的问题或者故障,但工作几年下来发现,有些同事其实并不知道该如何去分析和解决这些问题,毫无章法的猜测和尝试,虽然在很多时候可以最终解决问题,但往往也会浪费大...

    leonardofed 评论0 收藏0
  • [ 好文分享 ] 美团酒店Node全栈开发实践

    摘要:我所在的美团酒店事业部去年月份成立,新的业务新的开发团队,这一切使得我们的前后端分离推进的很彻底。日志监控平台日志监控平台是美团内部的一个日志收集系统,目前美团统一使用收集日志,具有接收格式日志的能力,而日志监控平台也是以格式日志来收集。 转自:美团技术团队 作者:美团技术团队 分享理由:很好的分享,可见,基于Node的前后端分离的架构是越显流行和重要,前端攻城狮们,No...

    wangdai 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<