-
Notifications
You must be signed in to change notification settings - Fork 0
/
pmp
executable file
·193 lines (185 loc) · 5.66 KB
/
pmp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!/usr/bin/env bash
# This program is part of Aspersa (http://code.google.com/p/aspersa/)
# ########################################################################
# This script aggregates GDB stack traces for a selected program. By default it
# does mysqld.
#
# Author: Baron Schwartz, based on a script by Domas Mituzas at
# poormansprofiler.org
# ########################################################################
# Print a usage message and exit.
usage() {
if [ "${OPT_ERR}" ]; then
echo "${OPT_ERR}"
fi
cat <<-USAGE
Usage: $0 [OPTIONS] [FILE]
$0 does two things: 1) get a GDB backtrace 2) aggregate it.
If you specify a FILE, then step 1) is not performed.
Options:
-b BINARY Which binary to trace (default mysqld)
-i ITERATIONS How many traces to gather and aggregate (default 1)
-k KEEPFILE Keep the raw traces in this file after aggregation
-l NUMBER Aggregate only first NUMBER functions; 0=infinity (default 0)
-p PID Process ID of the process to trace; overrides -b
-s SLEEPTIME Number of seconds to sleep between iterations (default 0)
USAGE
exit 1
}
# Actually does the aggregation. The arguments are the max number of functions
# to aggregate, and the files to read. If maxlen=0, it means infinity. We have
# to pass the maxlen argument into this function to make maxlen testable.
aggregate_stacktrace() {
maxlen="$1";
cat > /tmp/aspersa.awk <<EOF
BEGIN {
s = "";
}
/^Thread/ {
if ( s != "" ) {
print s;
}
s = "";
c = 0;
}
/^\#/ {
if ( \$2 ~ /0x/ ) {
if ( \$4 ~/void|const/ ) {
targ = \$5;
}
else {
targ = \$4;
}
if ( targ ~ /[<\\(]/ ) {
targ = substr(\$0, index(\$0, " in ") + 4);
if ( targ ~ / from / ) {
targ = substr(targ, 1, index(targ, " from ") - 1);
}
if ( targ ~ / at / ) {
targ = substr(targ, 1, index(targ, " at ") - 1);
}
# Shorten C++ templates, e.g. in t/samples/stacktrace-004.txt
while ( targ ~ />::/ ) {
if ( 0 == gsub(/<[^<>]*>/, "", targ) ) {
break;
}
}
# Further shorten argument lists.
while ( targ ~ /\\(/ ) {
if ( 0 == gsub(/\\([^()]*\\)/, "", targ) ) {
break;
}
}
# Remove void and const decorators.
gsub(/ ?(void|const) ?/, "", targ);
gsub(/ /, "", targ);
}
else if ( targ ~ /\\?\\?/ && \$2 ~ /[1-9]/ ) {
# Substitute ?? by the name of the library.
targ = \$NF;
while ( targ ~ /\\// ) {
targ = substr(targ, index(targ, "/") + 1);
}
targ = substr(targ, 1, index(targ, ".") - 1);
targ = targ "::??";
}
}
else {
targ = \$2;
}
# get rid of long symbol names such as 'pthread_cond_wait@@GLIBC_2.3.2'
if ( targ ~ /@@/ ) {
fname = substr(targ, 1, index(targ, "@@") - 1);
}
else {
fname = targ;
}
if ( ${maxlen:-0} == 0 || c < ${maxlen:-0} ) {
if (s != "" ) {
s = s "," fname;
}
else {
s = fname;
}
}
c++;
}
END {
print s
}
EOF
awk -f /tmp/aspersa.awk "$2" | sort | uniq -c | sort -r -n -k 1,1
rm -f /tmp/aspersa
}
# The main program to run.
main() {
rm -f /tmp/aspersa
# Get command-line options
for o; do
case "${o}" in
--)
shift; break;
;;
--help)
usage;
;;
-b)
shift; OPT_b="${1}"; shift;
;;
-i)
shift; OPT_i="${1}"; shift;
;;
-k)
shift; OPT_k="${1}"; shift;
;;
-l)
shift; OPT_l="${1}"; shift;
;;
-p)
shift; OPT_p="${1}"; shift;
;;
-s)
shift; OPT_s="${1}"; shift;
;;
-*)
OPT_ERR="Unknown option ${o}."
usage
;;
esac
done
export OPT_i="${OPT_i:-1}";
export OPT_k="${OPT_k:-}";
export OPT_l="${OPT_l:-0}";
export OPT_b="${OPT_b:-mysqld}";
export OPT_p="${OPT_p:-}";
export OPT_s="${OPT_s:-0}";
if [ -z "${1}" ]; then
# There's no file to analyze, so we'll make one.
if [ -z "${OPT_p}" ]; then
OPT_p=$(pidof -s "${OPT_b}" 2>/dev/null);
if [ -z "${OPT_p}" ]; then
OPT_p=$(pgrep -o -x "${OPT_b}" 2>/dev/null)
fi
if [ -z "${OPT_p}" ]; then
OPT_p=$(ps -eaf | grep "${OPT_b}" | grep -v grep | awk '{print $2}' | head -n1);
fi
fi
date;
for x in $(seq 1 $OPT_i); do
gdb -ex "set pagination 0" -ex "thread apply all bt" -batch -p $OPT_p >> "${OPT_k:-/tmp/aspersa}"
date +'TS %N.%s %F %T' >> "${OPT_k:-/tmp/aspersa}"
sleep $OPT_s
done
fi
if [ $# -eq 0 ]; then
aggregate_stacktrace "${OPT_l}" "${OPT_k:-/tmp/aspersa}"
rm -f /tmp/aspersa
else
aggregate_stacktrace "${OPT_l}" "$@"
fi
}
# Execute the program if it was not included from another file. This makes it
# possible to include without executing, and thus test.
if [ "$(basename "$0")" = "pmp" ] || [ "$(basename "$0")" = "bash" -a "$_" = "$0" ]; then
main "$@"
fi