devinteske

dwatch gource-proc profile

Apr 26th, 2018
602
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 8.97 KB | None | 0 0
  1. # -*- tab-width: 4 -*- ;; Emacs
  2. # vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM
  3. ############################################################ IDENT(1)
  4. #
  5. # $Title: dwatch(8) gource module for dtrace_proc(4) activity $
  6. # $Copyright: 2014-2018 Devin Teske. All rights reserved. $
  7. # $FrauBSD$
  8. #
  9. ############################################################ DESCRIPTION
  10. #
  11. # Produce gource custom log format for process activity
  12. #
  13. # NB: Requires FreeBSD 12.0-CURRENT or newer
  14. # NB: spawns single instance of ps(1), date(1), and getent(1) at start
  15. # NB: getent(1) invocation is "getent passwd" for mapping uid to username
  16. #
  17. ############################################################ PROBE
  18.  
  19. : ${PROBE:=proc:::create,proc:::exec-success,proc:::signal-send}
  20.  
  21. ############################################################ GLOBALS
  22.  
  23. : ${LOGFILE:=}
  24.  
  25. _DEBUG=
  26. _EVENT_FMT="%u %s %s"
  27. _EVENT_ARGS="walltimestamp / 1000000000, probealias[probename], this->details"
  28.  
  29. ############################################################ MAIN
  30.  
  31. load_profile proc
  32. EVENT_DETAILS="printf(\"$_EVENT_FMT\", $_EVENT_ARGS);"
  33.  
  34. [ "$LOGFILE$DEBUG$EXIT_AFTER_COMPILE" ] || info "Watching '$PROBE' ..."
  35. [ "$LOGFILE" ] && info "Reading from '$LOGFILE' ..."
  36. {
  37.     data=$( echo $$; ps axd -o pid,ppid,uid,gid,ucomm )
  38.     echo "$data" | awk -v debug="$_DEBUG" '
  39.     ################################################## BEGIN
  40.     BEGIN {
  41.         delete gid
  42.         delete ucomm
  43.         delete uid
  44.         getline __ppid
  45.         getline hdr
  46.         (cmd = "date +\"%s: %Y %b %e %T\"") | getline epoch_dt
  47.         close(cmd)
  48.         epoch = dt = epoch_dt
  49.         sub(/: .*/, "", epoch)
  50.         sub(/.*: /, "", dt)
  51.     }
  52.     ################################################## FUNCTIONS
  53.     function dwatch(event, _uid, _gid, _ucomm, _pid) {
  54.         printf "%s %u.%u %s[%u]: %u " event "\n",
  55.             dt, _uid, _gid, _ucomm, _pid, epoch
  56.         fflush(stdout)
  57.     }
  58.     ################################################## MAIN
  59.     (pid = $1)(ppid = $2)(uid[pid] = $3)(gid[pid] = $4)(ucomm[pid] = $NF) {
  60.         if (pid) dwatch("FORK pid " pid,
  61.             uid[ppid], gid[ppid], ucomm[ppid], ppid)
  62.         dwatch("INIT", uid[pid], gid[pid], ucomm[pid], pid)
  63.         if (ppid == __ppid && ucomm[pid] == "sh") _ppid=pid
  64.         if (ppid == _ppid && ucomm[pid] == "ps") dwatch("EXIT",
  65.             uid[pid], gid[pid], ucomm[pid], pid)
  66.     }
  67.     ################################################## END
  68.     END {
  69.         if (!debug) exit
  70.         print "DEBUG: END OF PS" > "/dev/stderr"
  71.         fflush(stderr)
  72.     }
  73.     ' # END-QUOTE
  74.     if [ "$LOGFILE" ]; then
  75.         cat "$LOGFILE"
  76.     else
  77.         eval dwatch $ARGV -q -E \"\$EVENT_DETAILS\" -X proc
  78.     fi
  79. } | awk -v debug="$_DEBUG" '
  80.     ################################################## BEGIN
  81.     BEGIN {
  82.         delete adder
  83.         delete addbuf
  84.         delete delbuf
  85.         delete name
  86.         delete parent
  87.         delete prefix_cache
  88.         delete proc
  89.         delete remake
  90.         delete sortedbuf
  91.         date = "[[:digit:]]+ [A-Z][a-z][a-z] [0-9 ][0-9]"
  92.         time = "[0-9][0-9]:[0-9][0-9]:[0-9][0-9]"
  93.         while ((cmd = "getent passwd") | getline pwinfo) {
  94.             if (split(pwinfo, pwf, /:/) < 3) continue
  95.             if (name[pwf[3]]) continue
  96.             name[pwf[3]] = pwf[1]
  97.         }
  98.         close(cmd)
  99.         delete pwf
  100.         pwinfo = cmd = ""
  101.     }
  102.     ################################################## FUNCTIONS
  103.     function dprint(text) {
  104.         if (!debug) return
  105.         print "DEBUG: " text > "/dev/stderr"
  106.         fflush(stderr)
  107.     }
  108.     function dprint1(fmt, arg1) { dprint(sprintf(fmt, arg1)) }
  109.     function dprint2(fmt, arg1, arg2) { dprint(sprintf(fmt, arg1, arg2)) }
  110.     function dprint3(fmt, arg1, arg2, arg3) {
  111.         dprint(sprintf(fmt, arg1, arg2, arg3))
  112.     }
  113.     function dprint4(fmt, arg1, arg2, arg3, arg4) {
  114.         dprint(sprintf(fmt, arg1, arg2, arg3, arg4))
  115.     }
  116.     function _asorti(src, dest)
  117.     {
  118.         k = nitems = 0
  119.         for (i in src) dest[++nitems] = i
  120.         for (i = 1; i <= nitems; k = i++) {
  121.             idx = dest[i]
  122.             while ((k > 0) && (dest[k] > idx)) {
  123.                 dest[k+1] = dest[k]; k--
  124.             }
  125.             dest[k+1] = idx
  126.         }
  127.         return nitems
  128.     }
  129.     function gourcestr(epoch, user, type, pid, prefix,        path) {
  130.         dprint4("gourcestr: user=%s type=%s pid=%s prefix=%s",
  131.             user, type, pid, prefix)
  132.         if (!(pid in proc)) return
  133.         if (!prefix) prefix = pid2prefix(pid)
  134.         if (!(path = prefix proc[pid])) return
  135.         sub("/*$", "", path)
  136.         return sprintf("%u|%s|%s|%s", epoch, user, type, path)
  137.     }
  138.     function addstr(user, pid) {
  139.         return gourcestr(epoch, adder[pid] = user, "A", pid)
  140.     }
  141.     function modstr(user, pid) { return gourcestr(epoch, user, "M", pid) }
  142.     function delstr(user, pid, prefix) {
  143.         if (pid in adder && adder[pid] != "") user = adder[pid]
  144.         return gourcestr(epoch, user, "D", pid, prefix)
  145.     }
  146.     function putstr(str) {
  147.         if (!str) return
  148.         print str
  149.         fflush(stdout)
  150.         dprint(str)
  151.     }
  152.     function add(user, pid) { putstr(addstr(user, pid)) }
  153.     function mod(user, pid) { putstr(modstr(user, pid)) }
  154.     function del(user, pid,        thisproc, n, p, i, _buf) {
  155.         dprint1("deleting pid %u", pid)
  156.         delete addbuf
  157.         delete delbuf
  158.         delete remake
  159.         _buf = delstr(user, pid)
  160.         dprint1("removal buffered as %s", _buf)
  161.         delbuf[_buf] = 1
  162.         thisproc = pid in proc ? proc[pid] : ""
  163.         for (p in proc) {
  164.             if (p in remake || p == pid || parent[p] != thisproc)
  165.                 continue
  166.             orphan(user, p)
  167.         }
  168.         n = _asorti(delbuf, sortedbuf)
  169.         for (i = n; i >= 1; i--) putstr(sortedbuf[i])
  170.         delete proc[pid]
  171.         for (p in remake) {
  172.             dprint1("remaking pid %u", p)
  173.             delete prefix_cache[p]
  174.             _buf = addstr(user, p)
  175.             dprint1("addition buffered as %s", _buf)
  176.             addbuf[_buf] = 1
  177.         }
  178.         delete prefix_cache[pid]
  179.         delete parent[pid]
  180.         n = _asorti(addbuf, sortedbuf)
  181.         for (i = 1; i <= n; i++) putstr(sortedbuf[i])
  182.     }
  183.     function orphan(user, pid,        prefix, len, p, _prefix, _buf) {
  184.         dprint1("orphan pid %u", pid)
  185.         remake[pid] = 1
  186.         if ((prefix = pid2prefix(pid)) == "") {
  187.             parent[pid] = proc[1]
  188.             prefix_cache[pid] = prefix_cache[1] proc[1] "/"
  189.             return
  190.         }
  191.         _buf = delstr(user, pid, prefix)
  192.         dprint1("removal buffered as %s", _buf)
  193.         delbuf[_buf] = 1
  194.         len = length(prefix)
  195.         dprint1("begin check for children of orphan pid %u", pid)
  196.         for (p in proc) {
  197.             if (!p || p == pid) continue
  198.             if ((_prefix = pid2prefix(p)) == "") continue
  199.             if (substr(_prefix, 1, len) != prefix) continue
  200.             dprint1("removing old path for %s", proc[p])
  201.             _buf = delstr(user, p, _prefix)
  202.             dprint1("removal buffered as %s", _buf)
  203.             delbuf[_buf] = 1
  204.             remake[p] = 1
  205.         }
  206.         parent[pid] = proc[1]
  207.         prefix_cache[pid] = prefix_cache[1] proc[1] "/"
  208.         dprint1("done checking for children of orphan pid %u", pid)
  209.         return
  210.     }
  211.     function exec(user, pid, newproc) {
  212.         if (!(pid in proc)) return
  213.         if (proc[pid] == newproc) {
  214.             mod(user, pid)
  215.             return
  216.         }
  217.         putstr(delstr(user, pid))
  218.         proc[pid] = newproc
  219.         add(user, pid)
  220.     }
  221.     function pid2prefix(pid,        prefix, thisproc, ppid, _pid_arg) {
  222.         if (pid == 0) return ""
  223.         if (!(pid in proc)) return ""
  224.         thisproc = proc[pid]
  225.         dprint1("constructing prefix for %s", thisproc)
  226.         if (pid in prefix_cache && prefix = prefix_cache[pid]) {
  227.             dprint2("%s cached prefix is %s", thisproc, prefix)
  228.             return prefix
  229.         }
  230.         _pid_arg = pid
  231.         while (pid && pid in parent && thisproc = parent[pid]) {
  232.             if (pid == 0) break
  233.             ppid = thisproc
  234.             sub(/.*\[/, "", ppid)
  235.             sub(/\].*/, "", ppid)
  236.             dprint3("  pid=[%u] ppid=[%u] prefix=[%s]",
  237.                 pid, ppid, prefix)
  238.             if (pid in prefix_cache) {
  239.                 prefix = prefix_cache[pid] prefix
  240.                 dprint2("  pid %u partially cached, prefix %s",
  241.                     _pid_arg, prefix)
  242.                 break
  243.             }
  244.             prefix = thisproc "/" prefix
  245.             dprint3("  pid=[%u] ppid=[%u] prefix=[%s]",
  246.                 pid, ppid, prefix)
  247.             if (!pid || !ppid) {
  248.                 dprint("  breaking")
  249.                 break
  250.             }
  251.             pid = ppid
  252.         }
  253.         prefix_cache[_pid_arg] = prefix
  254.         dprint2("%s prefix cached as %s", proc[_pid_arg], prefix)
  255.         return prefix
  256.     }
  257.     ################################################## MAIN
  258.     length(datetime = substr($0, 1, 20)) != 20 { next }
  259.     datetime !~ "^" date " " time "$" { next }
  260.     debug { dprint($0) }
  261.     (epoch = $7) ~ /^[[:digit:]]+/ {
  262.         uid_gid = uid = gid = $5
  263.         sub(/\..*/, "", uid)
  264.         sub(/.*\./, "", gid)
  265.         user = sprintf("%s[%u]", name[uid], uid)
  266.         curproc = $6
  267.         sub(/:$/, "", curproc)
  268.         pid = curproc
  269.         sub(/.*\[/, "", pid)
  270.         sub(/\]$/, "", pid)
  271.     }
  272.     $8 == "FORK" && childpid = $10 {
  273.         parent[childpid] = proc[childpid] = curproc
  274.         delete prefix_cache[pid]
  275.         sub(/\[[[:digit:]]+\]$/, "[" childpid "]", proc[childpid])
  276.         add(user, childpid)
  277.     }
  278.     $8 == "INIT" { exec(user, pid, curproc) }
  279.     $8 == "SEND" && (signal = $9) !~ /SIGCHLD/ {
  280.         source = pid
  281.         target = $11
  282.         if (source in proc) {
  283.             dprint2("%s sent signal to %s", proc[source],
  284.                 target in proc ? proc[target] : "pid " target)
  285.             mod(user, sender)
  286.         }
  287.         if (target in proc) {
  288.             dprint2("%s received signal from %s", proc[target],
  289.                 sender in proc ? proc[sender] : "pid " sender)
  290.             mod(user, target)
  291.         }
  292.     }
  293.     $8 == "EXIT" { del(user, pid) }
  294.     ################################################## END
  295. ' # END-QUOTE
  296.  
  297. exit
  298.  
  299. ################################################################################
  300. # END
  301. ################################################################################
Add Comment
Please, Sign In to add comment