#! /bin/sh
+set -e
+
usage() {
echo "EXAMPLE (whole tree) : $0 | neato -T png > ps.png && open ps.png"
echo "EXAMPLE (user clusters): $0 | sdp -T png > ps.png && open ps.png"
}
-compile() {
+ps2dot() {
awk -v kernel="$(uname -v)" -v whoami="$(whoami)" \
'
+ function num_scale(src_cur, src_max, dst_min, dst_max) {
+ return dst_min + ((src_cur * (dst_max - dst_min)) / src_max)
+ }
+
+ function vert_print(v, _color, _fontcolor, _shape, _state, _size, _height, _label, _label_base, _label_ext) {
+ _state = child2state[v]
+ _style = "filled,solid"
+
+ # -----------------------------------------------------------------
+ # Sleeping/idling
+ # -----------------------------------------------------------------
+ # D uninterruptible sleep (usually IO)
+ if (_state == "D") {
+ _shape = "circle"
+ # I Idle kernel thread
+ } else if (_state == "I") {
+ _shape = "circle"
+ # S interruptible sleep (waiting for an event to complete)
+ } else if (_state == "S") {
+ _shape = "circle"
+ # -----------------------------------------------------------------
+ # Running
+ # -----------------------------------------------------------------
+ # R running or runnable (on run queue)
+ } else if (_state == "R") {
+ _shape = "rarrow"
+ # -----------------------------------------------------------------
+ # Stopped
+ # -----------------------------------------------------------------
+ # T stopped by job control signal
+ } else if (_state == "T") {
+ _shape = "square"
+ # t stopped by debugger during the tracing
+ } else if (_state == "t") {
+ _shape = "square"
+ # -----------------------------------------------------------------
+ # Dead
+ # -----------------------------------------------------------------
+ # Z defunct ("zombie") process, terminated but not reaped by its parent
+ } else if (_state == "Z") {
+ _shape = "Msquare"
+ _style = "solid"
+ # -----------------------------------------------------------------
+ # UNKNOWN STATE
+ # -----------------------------------------------------------------
+ } else {
+ _shape = "doublecircle"
+ }
+
+ _color =\
+ num_scale(\
+ child2cpu[v],
+ max_cpu,
+ VERT_COLORSCHEME_MAX,
+ VERT_COLORSCHEME_MIN\
+ )
+ _size =\
+ num_scale(\
+ child2mem[v],
+ max_mem,
+ 1,
+ 4\
+ ) / 4
+ _height = _size
+ _width = _size
+ _fontcolor = \
+ _color == VERT_COLORSCHEME_MAX || _color == VERT_COLORSCHEME_MIN \
+ ? sprintf("/%s/%d", VERT_COLORSCHEME, VERT_COLORSCHEME_MID) \
+ : sprintf("/%s/%d", "greys9", 9)
+ _fontcolor = \
+ _size < 0.5 \
+ ? sprintf("/%s/%d", "greys9", 9) \
+ : _fontcolor
+ _label_base = \
+ sprintf("%s\n%d", child2comm[v], v)
+ _label_ext = \
+ _size >= 0.5 \
+ ? sprintf("\ncpu: %.1f%%\nmem: %.1f%%", child2cpu[v], child2mem[v]) \
+ : ""
+ _label = _label_base _label_ext
+ printf(\
+ "\"%d\"\
+ [ fontsize=8 \
+ , fixedsize=true \
+ , height=%f \
+ , width=%f \
+ , border=1 \
+ , style=\"%s\" \
+ , fontname=Helvetica \
+ , label=\"%s\" \
+ , shape=\"%s\" \
+ , fillcolor=\"/%s/%d\" \
+ , fontcolor=\"%s\" \
+ ];",
+ v,
+ _height,
+ _width,
+ _style,
+ _label,
+ _shape,
+ VERT_COLORSCHEME,
+ _color,
+ _fontcolor\
+ )
+ }
+
+ function edge_print(child, _parent) {
+ _parent = child2parent[child]
+ printf(\
+ "\"%s\" -> \"%s\"\
+ [ fontsize=8 \
+ , fontname=Helvetica \
+ , len=2.0 \
+ , color=\"%s\" \
+ ];\n",
+ _parent,
+ child,
+ EDGE_COLOR\
+ )
+ }
+
BEGIN {
- p2cmd[0] = "swapper/sched"
- color_base = 20
- color_incr = 30
- color_hi = sprintf("grey%d", (color_base + (color_incr * 0)))
- color_mid = sprintf("grey%d", (color_base + (color_incr * 1)))
- color_low = sprintf("grey%d", (color_base + (color_incr * 2)))
+ # Hot->Cold gradual colorschemes:
+ # - rdbu11
+ # - rdbu9
+ # - rdbu8
+ # - rdylgn10 # 3 - 11
+
+ # Light->Dark gradual colorschemes:
+ # - reds9
+ # - blues9
+ # - orrd9
+ # - oranges9
+ # - bupu9
+ # - greys9
+
+ VERT_COLORSCHEME_MIN = 1
+ VERT_COLORSCHEME_MID = 4
+ VERT_COLORSCHEME_MAX = 8
+ VERT_COLORSCHEME = "rdylgn10"
+
+ EDGE_COLOR = "/ylorbr9/3"
+
+ child2comm[0] = "swapper/sched"
}
NR > 1 {
- p2pp[$1]=$2;
- p2user_id[$1]=$3
- p2user_name[$1]=$4
- p2cmd[$1]=$5
- user_names[$4]=1
+ parent2child_count[$2]++
+ max_children = \
+ parent2child_count[$2] > max_children\
+ ? parent2child_count[$2]\
+ : max_children
+ child2parent[$1] = $2
+ child2user_id[$1] = $3
+ child2user_name[$1] = $4
+ child2nice[$1] = $5
+ child2state[$1] = $6
+ child2cpu[$1] = $7
+ child2mem[$1] = $8
+ child2comm[$1] = $9
+ user_names[$4] = 1
+ max_cpu = $7 > max_cpu ? $7 : max_cpu
+ max_mem = $8 > max_mem ? $8 : max_mem
}
END {
print "strict digraph G {";
print "start=0;";
- print "colorscheme=brewer;";
print "fontsize=8;";
print "fontname=Helvetica;";
print "label=\"" kernel "\";";
-
- printf(\
- "edge [ fontsize=8 \
- , fontname=Helvetica \
- , len=2.0 \
- , color=%s \
- ];",
- color_low\
- )
-
- print "node [ fontsize=8 \
- , fontname=Helvetica \
- , shape=ellipse \
- ]; \
- ";
+ print "fontcolor=\"/greys9/9\";"
##### Vertices (clustered by user)
- for (user in user_names) {
- printf "subgraph \"cluster_%s\" {\n", user
- printf "label=\"%s\"\n", user
- for (p in p2pp) {
- if (p2user_name[p] == user) {
- color = p2user_name[p] == whoami ? color_hi : color_low
- fontcolor = p2user_name[p] == whoami ? color_hi : color_mid
- printf(\
- "\"%d\"\
- [ fontsize=8 \
- , fontname=Helvetica \
- , shape=ellipse \
- , label=\"%s\n%d\" \
- , color=\"%s\" \
- , fontcolor=\"%s\" \
- ];",
- p,
- p2cmd[p],
- p,
- color,
- fontcolor\
- )
- }
- }
+ for (user_name in user_names) {
+ printf "subgraph \"cluster_%s\" {\n", user_name
+ printf "label=\"%s\"\n", user_name
+ for (c in child2parent)
+ if (child2user_name[c] == user_name)
+ vert_print(c)
print "}"
}
##### Vertices (without a user)
- for (p in p2cmd) {
- if (!p2user_name[p]) {
- printf(\
- "\"%d\"\
- [ fontsize=8 \
- , fontname=Helvetica \
- , shape=ellipse \
- , label=\"%s\n%d\" \
- , color=\"%s\" \
- , fontcolor=\"%s\" \
- ];",
- p,
- p2cmd[p],
- p,
- color_low,
- color_mid\
- )
- }
- }
+ for (c in child2comm)
+ if (!child2user_name[c])
+ vert_print(c)
##### Edges (across clusters)
- for (p in p2pp) {
- printf "\"%d\" -> \"%d\";\n", p2pp[p], p, p
- }
+ for (c in child2parent)
+ edge_print(c)
print "}";
}
procs() {
if [ "$(uname)" = 'Linux' ]; then
- ps -eo pid,ppid,euid,euser,comm
+ ps -eo 'pid,ppid,euid,euser,nice,s,%cpu,%mem,comm'
else
- ps -eco pid,ppid,euid,euser,comm
+ ps -eco 'pid,ppid,euid,euser,nice,s,%cpu,%mem,comm'
fi
}
main() {
case "$1" in
- '--help') usage
+ '--help')
+ usage
;;
- *) procs | grep "$1" | compile
+ *)
+ timestamp="$(date +'%Y-%m-%d_%H:%M:%S%z')"
+ host="$(hostname)"
+ kernel="$(uname -s | awk '{print tolower($0)}')"
+ filename_base=$(mktemp "ps.$host.$kernel.$timestamp.XXXXX")
+ file_log="$filename_base.log"
+ file_dot="$filename_base.dot"
+ mv "$filename_base" "$file_log"
+ procs | grep "$1" | ps2dot 2> "$file_log" > "$file_dot"
+ time neato -T png "$file_dot" 2> "$file_log" > "$filename_base.neato.png"
+ time fdp -T png "$file_dot" 2> "$file_log" > "$filename_base.fdp.png"
+ time dot -T png "$file_dot" 2> "$file_log" > "$filename_base.dot.png"
+ ls -1 "$filename_base"*
;;
esac
}