Linux中如何获得进程的运行时堆栈
创始人
2024-02-21 02:22:22
0

关于这个话题,我们一般是为了处理一下生产环境中程序出现死循环或者死锁等问题。我们一般想到的方法就是gdb attach上一个运行中的进程。但是这个需要手动交互。通过网上查找和实践,可以有以下几种选择:

  • 第一种:pstack 进程ID    (pstack就是一个利用gdb实现的shell脚本)
  • 第二种:gcore 进程IP    (gcore也是一个gdb实现的脚本)
  • (看来目前现成的工具都是站在gdb的肩膀上了,除非我们利用ptrace()API参考gdb的源码自己写一个)
  • 第三种:利用fork()的方式,继承一个进程,然后再新的子进程里面直接使用异常信号产生coredump。
描述优点/缺点
pstack依赖系统中的gdb,会是程序短暂的停止运行。

优点:不需要对原有程序做任何改变,直接可以产看运行时。

缺点: 依赖gdb

gcore依赖系统中的gdb,会是程序短暂的停止运行。

优点:不需要对原有程序做任何改变,直接可以产看运行时。

缺点: 依赖gdb

fork()需要改造原有程序,增加事件代码触发fork()动作

优点:不依赖gdb。

缺点: 需要修改源程序

相关代码:

  1. pstack

[root@localhost ~]# cat /usr/bin/gstack#!/bin/shif test $# -ne 1; thenecho "Usage: `basename $0 .sh` " 1>&2exit 1fiif test ! -r /proc/$1; thenecho   "Process $1 not found."   1>&2exit 1fi# GDB doesn't allow "thread apply all bt" when the process isn't# threaded; need to peek at the process to determine if that or the# simpler "bt" should be used.backtrace="bt"if test -d /proc/$1/task ; then# Newer kernel; has a task/ directory.if test `/bin/ls /proc/$1/task | /usr/bin/wc -l` -gt 1 2>/dev/null ; thenbacktrace="thread apply all bt"fielif test -f /proc/$1/maps ; then# Older kernel; go by it loading libpthread.if /bin/grep -e libpthread /proc/$1/maps > /dev/null 2>&1 ; thenbacktrace="thread apply all bt"fifiGDB=${GDB:-/usr/bin/gdb}if $GDB -nx --quiet --batch --readnever > /dev/null 2>&1; thenreadnever=--readneverelsereadnever=    fi# Run GDB, strip out unwanted noise.$GDB --quiet $readnever -nx /proc/$1/exe $1 <&1 |$backtraceEOF/bin/sed -n \       -e  's/^(gdb) //'  \     -e  '/^#/p'        \-e  '/^Thread/p'       [root@localhost ~]#

2. gcore

root@xxx:/App/Log# cat /usr/bin/gcore
#!/bin/sh#   Copyright (C) 2003-2016 Free Software Foundation, Inc.# This program 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.
#
# This program 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.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see .#
# Script to generate a core file of a running program.
# It starts up gdb, attaches to the given PID and invokes the gcore command.
#if [ "$#" -eq "0" ]
thenecho "usage:  $0 [-o filename] pid"exit 2
fi# Need to check for -o option, but set default basename to "core".
name_tail=`date +"%Y-%m-%d-%H.%M.%S"`
tmp_name=gcore-"$name_tail"name=gcore-"$name_tail"if [ "$1" = "-o" ]
thenif [ "$#" -lt "3" ]then# Not enough arguments.echo "usage:  gcore [-o filename] pid"exit 2finame=$2# Shift over to start of pid listshift; shift
fiecho "tmpfile:$tmp_name, outfile:$name"# Attempt to fetch the absolute path to the gcore script that was
# called.
#binary_path=`dirname "$0"`
binary_path="/usr/bin"if test "x$binary_path" = x. ; then# We got "." back as a path.  This means the user executed# the gcore script locally (i.e. ./gcore) or called the# script via a shell interpreter (i.e. sh gcore).binary_basename=`basename "$0"`# If the gcore script was called like "sh gcore" and the script# lives in the current directory, "which" will not give us "gcore".# So first we check if the script is in the current directory# before using the output of "which".if test -f "$binary_basename" ; then# We have a local gcore script in ".".  This covers the case of# doing "./gcore" or "sh gcore".binary_path="."else# The gcore script was not found in ".", which means the script# was called from somewhere else in $PATH by "sh gcore".# Extract the correct path now.binary_path_from_env=`which "$0"`binary_path=`dirname "$binary_path_from_env"`fi
fi# Check if the GDB binary is in the expected path.  If not, just
# quit with a message.
if [ ! -f "$binary_path"/gdb ]; thenecho "gcore: GDB binary (${binary_path}/gdb) not found"exit 1
fi# Initialise return code.
rc=0
echo "---------------------------"
# Loop through pids
for pid in $*
do# `

Note: 我们可以一些参数控制gcore参数的coredump文件的大小

3. 使用fork()  (代码略)

参考:

如何获取运行时进程堆栈

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...