Last active
May 22, 2023 23:05
-
-
Save bcap/4e0e9b3198d889ce19d0c026ec8ada12 to your computer and use it in GitHub Desktop.
small script to capture profiling data from a go process. The process needs to have the pprof web handler installed (https://pkg.go.dev/net/http/pprof)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
FLAMEGRAPH_PATH="${FLAMEGRAPH_PATH:-$HOME/code/github.com/brendangregg/FlameGraph/}" | |
SECONDS=10 | |
GEN_PROFILE=true | |
GEN_GOROUTINE=false | |
GEN_BLOCK=false | |
GEN_MUTEX=false | |
GEN_ALLOC=false | |
GEN_HEAP=false | |
GEN_THREAD_CREATE=false | |
MKDIR=false | |
usage() { | |
cat <<EOF | |
$(basename $0) [-h] <source> | |
Positionals: | |
<source> The source to fetch the debugging data from. Example: | |
http://ip-172-16-54-3.ec2.internal/debug/pprof | |
Options: | |
-s --seconds For how long to capture data. Default: $SECONDS | |
-g --goroutine Also profile goroutines | |
-b --block Also profile blocking calls on sync primitives | |
-m --mutex Also profile mutexes | |
-a --alloc Also profile memory allocations | |
-e --heap Also profile heap live objects | |
-t --thread-create Also profile thread creations | |
-P --no-profile Do not generate profile. Useful if only capturing others like goroutines or mutexes | |
-d --mkdir Instead of dumping result files into current dir, create a more organized directory structure and store results there | |
-h --help Print this help. | |
Env Vars: | |
FLAMEGRAPH_PATH Path where the flamegraph repo is cloned (https://github.com/brendangregg/FlameGraph). | |
Default: $FLAMEGRAPH_PATH | |
EOF | |
} | |
while [[ $# > 0 ]]; do | |
case "$1" in | |
-s|--seconds) SECONDS=$2; shift ;; | |
-g|--goroutine) GEN_GOROUTINE=true ;; | |
-b|--block) GEN_BLOCK=true ;; | |
-m|--mutex) GEN_MUTEX=true ;; | |
-a|--alloc) GEN_ALLOC=true ;; | |
-e|--heap) GEN_HEAP=true ;; | |
-t|--thread-create) GEN_THREAD_CREATE=true ;; | |
-P|--no-profile) GEN_PROFILE=false ;; | |
-d|--mkdir) MKDIR=true ;; | |
-h|--help) usage; exit 0 ;; | |
-*) usage; exit 1 ;; | |
*) SOURCE=$1 ;; | |
esac | |
shift | |
done | |
[[ -z $SOURCE ]] && usage && exit 1 | |
function log() { | |
echo "$@" >&2 | |
} | |
TS="$(date +%Y-%m-%d-%H-%M-%S)" | |
if [ "$MKDIR" == "true" ]; then | |
SOURCE_ID=$(echo $SOURCE | sed -e 's/http:\/\///' -e 's/https:\/\///' | cut -d '/' -f 1) | |
DIR="$SOURCE_ID/$TS/" | |
mkdir -p $DIR | |
else | |
DIR="" | |
fi | |
PROFILE="${DIR}profile.$TS.${SECONDS}s.pb.gz" | |
PROFILE_FLAMEGRAPH="${DIR}profile.$TS.${SECONDS}s.flamegraph.svg" | |
GOROUTINE="${DIR}goroutine.$TS.${SECONDS}s.pb.gz" | |
GOROUTINE_DUMP1="${DIR}goroutine.$TS.stack-dump-level1.txt" | |
GOROUTINE_DUMP2="${DIR}goroutine.$TS.stack-dump-level2.txt" | |
BLOCK="${DIR}block-sync.$TS.${SECONDS}s.pb.gz" | |
MUTEX="${DIR}mutex-contentions.$TS.${SECONDS}s.pb.gz" | |
ALLOC="${DIR}allocations.$TS.${SECONDS}s.pb.gz" | |
HEAP="${DIR}heap-live.$TS.${SECONDS}s.pb.gz" | |
THREAD_CREATE="${DIR}thread-create.$TS.${SECONDS}s.pb.gz" | |
function gen_profile() { | |
go tool pprof -proto -output=$PROFILE "$SOURCE/profile?seconds=$SECONDS" | |
go tool pprof -raw $PROFILE | | |
$FLAMEGRAPH_PATH/stackcollapse-go.pl | | |
$FLAMEGRAPH_PATH/flamegraph.pl > $PROFILE_FLAMEGRAPH | |
} | |
function gen_goroutine() { | |
go tool pprof -proto -output=$GOROUTINE "$SOURCE/goroutine?seconds=$SECONDS" & | |
curl -s "$SOURCE/goroutine?debug=1" > $GOROUTINE_DUMP1 & | |
curl -s "$SOURCE/goroutine?debug=2" > $GOROUTINE_DUMP2 & | |
wait | |
} | |
function gen_block() { | |
go tool pprof -proto -output=$BLOCK "$SOURCE/block?seconds=$SECONDS" | |
} | |
function gen_mutex() { | |
go tool pprof -proto -output=$MUTEX "$SOURCE/mutex?seconds=$SECONDS" | |
} | |
function gen_alloc() { | |
go tool pprof -proto -output=$ALLOC "$SOURCE/allocs?seconds=$SECONDS" | |
} | |
function gen_heap() { | |
go tool pprof -proto -output=$HEAP "$SOURCE/heap?seconds=$SECONDS" | |
} | |
function gen_thread_create() { | |
go tool pprof -proto -output=$THREAD_CREATE "$SOURCE/threadcreate?seconds=$SECONDS" | |
} | |
if [ "$GEN_PROFILE" == "true" ]; then | |
gen_profile & | |
fi | |
if [ "$GEN_GOROUTINE" == "true" ]; then | |
gen_goroutine & | |
fi | |
if [ "$GEN_BLOCK" == "true" ]; then | |
gen_block & | |
fi | |
if [ "$GEN_MUTEX" == "true" ]; then | |
gen_mutex & | |
fi | |
if [ "$GEN_ALLOC" == "true" ]; then | |
gen_alloc & | |
fi | |
if [ "$GEN_HEAP" == "true" ]; then | |
gen_heap & | |
fi | |
if [ "$GEN_THREAD_CREATE" == "true" ]; then | |
gen_thread_create & | |
fi | |
wait | |
log | |
log "-------" | |
log "RESULTS" | |
if [ "$GEN_PROFILE" == "true" ]; then | |
log | |
log "PROFILE" | |
log "generated profile file at $PROFILE and profile flamegraph file at $PROFILE_FLAMEGRAPH" | |
log "execute the following command to open an interactive visualization of the profile file:" | |
log " go tool pprof -http 0.0.0.0: $PROFILE" | |
echo $PROFILE | |
echo $PROFILE_FLAMEGRAPH | |
fi | |
if [ "$GEN_GOROUTINE" == "true" ]; then | |
log | |
log "GOROUTINE" | |
log "generated goroutine file at $GOROUTINE as well text dumps of current state at $GOROUTINE_DUMP1 and $GOROUTINE_DUMP2" | |
log "execute the following command to open an interactive visualization of the goroutine file:" | |
log " go tool pprof -http 0.0.0.0: $GOROUTINE" | |
echo $GOROUTINE | |
echo $GOROUTINE_DUMP1 | |
echo $GOROUTINE_DUMP2 | |
fi | |
if [ "$GEN_BLOCK" == "true" ]; then | |
log | |
log "BLOCK" | |
log "generated blocking on synchronizations file at $BLOCK" | |
log "execute the following command to open an interactive visualization of the blcking on synchronizations file:" | |
log " go tool pprof -http 0.0.0.0: $BLOCK" | |
echo $BLOCK | |
fi | |
if [ "$GEN_MUTEX" == "true" ]; then | |
log | |
log "MUTEX" | |
log "generated mutex contentions file at $MUTEX" | |
log "execute the following command to open an interactive visualization of the mutex contentions file:" | |
log " go tool pprof -http 0.0.0.0: $MUTEX" | |
echo $MUTEX | |
fi | |
if [ "$GEN_ALLOC" == "true" ]; then | |
log | |
log "ALLOC" | |
log "generated memory allocations file at $ALLOC" | |
log "execute the following command to open an interactive visualization of the memory allocations file:" | |
log " go tool pprof -http 0.0.0.0: $ALLOC" | |
echo $ALLOC | |
fi | |
if [ "$GEN_HEAP" == "true" ]; then | |
log | |
log "HEAP" | |
log "generated heap live objects file at $HEAP" | |
log "execute the following command to open an interactive visualization of the heap live objects file:" | |
log " go tool pprof -http 0.0.0.0: $HEAP" | |
echo $HEAP | |
fi | |
if [ "$GEN_THREAD_CREATE" == "true" ]; then | |
log | |
log "THREAD_CREATE" | |
log "generated thread creations file at $THREAD_CREATE" | |
log "execute the following command to open an interactive visualization of the thread creations file:" | |
log " go tool pprof -http 0.0.0.0: $THREAD_CREATE" | |
echo $THREAD_CREATE | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment