0

I'm looking to instrument a recursive Makefile, and I want to see the complete calling tree that got me to where I'm at (which includes all parameters, etc). I don't care about the other processes on the system.

What I'm looking for is essentially pstree -ha, where it only outputs the highlighted parts (plus the current process). Notice that pstree -ha <PID> does not work, as it does not show the parent's parents for some reason (it does not go all the way up to init). I found another SO answer ps -f -g$BINOSPID, but it shows siblings, which I don't want.

To spell out what I'm looking for: I want is this:

~> sh 
sh-4.1$ bash
~> pstree -?? $$

init
`- sshd
   `- bash
      `- sh
         `- bash
            `- pstree -?? 1402

Also, as a side question, pstree -ha automatically truncates the parameter list if it's to long. Is there a way to avoid that?

HardcoreHenry
  • 5,909
  • 2
  • 19
  • 44

1 Answers1

1

Implemented in native bash, depending on an operating system having procfs:

#!/usr/bin/env bash

pid=${1:-${BASHPID:-$$}}
while (( pid )); do
   mutated=0
   cmdline=( )
   while IFS= read -r -d '' piece || { [[ $piece ]] && mutated=1; }; do
       cmdline+=( "$piece" )
   done <"/proc/$pid/cmdline"
   printf '%s\t' "$pid"
   if (( mutated )); then
     printf '%s ' "${cmdline[@]}"
   else
     printf '%q ' "${cmdline[@]}"
   fi
   printf '\n'
   stat_data=$(<"/proc/$pid/stat") || break
   read _ ppid _ <<<"${stat_data##*')'}" || break
   [[ $ppid = "$pid" ]] && break
   pid=$ppid
done

The mutated flag is set if cmdline doesn't end with a NUL delimiter. This implies that the command line was modified by the program that was run, and no longer contains its original value. OpenSSH in particular tends to do this. In that case, rather than treating the command line as a NUL-separated list of literal arguments (and escaping those literals to generate output that could be copied-and-pasted to generate the same command when invoked), we treat it as something that has been formatted for readability by humans.

We're parsing "${stat_data##*')'}" out of paranoia, to prevent an application which contains literal spaces or ) characters in its executable name from throwing off the interpretation of /proc/$pid/stat.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Thanks. I just tried it and it works well. I was hoping there was a simple one liner (and I'm still a bit surprised there isn't). The `stat_data=$(<"/proc/$pid/stat")` syntax is new to me -- just looked it up. Very interesting – HardcoreHenry Feb 06 '18 at 18:35
  • There might well exist some approach that depends on a specific version of `pstree` (particularly if we were willing to munge its output), but given as it isn't a standardized command, trying to rely on it makes things nonportable; using procfs locks us down to Linux, but at least it's well-documented what the Linux kernel puts there (and guaranteed only to be extended in ways that don't harm backwards compatibility with older consumers); tools with output built for human consumption can be extended in future versions in ways that humans will understand but programmatic consumers might not. – Charles Duffy Feb 06 '18 at 19:04