最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

performance - In bash, how can an explicitly forked process determine that it was explicitly forked (versus, say, run in a subsh

programmeradmin4浏览0评论

Im working on a bash profiling tool called timep that gives per-command runtime profile for bash scripts and functions. It does this using a DEBUG trap to record start time and stop time for each command plus various info/metadata ($LINENO, $BASHPID, $FUNCNAME, $BASH_COMMAND, etc.) about the command being run. The current version can be found on github.

Im trying to group together the commands so that the profile(s) generated match the way the code being profiled is written. In particular:

  1. function calls are split off into a separate line-by-line profile, and the profile for the caller gets a single line for the function call that includes the total time spent running each command run in the function (i.e., function runtime without timing instrumentation time)
  2. commands that get forked but are automatically waited on by the shell (subshells, command substitutions, pipelines, etc.) will NOT be split off into separate profiles. These commands will, however, list their PIDs hierarchically (PID0->PID1[->PID2[...]])
  3. commands that are explicitly forked and not automatically waited on (cmd &, coproc cmd) will be split off into separate profiles and their runtime (except for the time taken by the forking operation) will not be included in the caller's profile

#3 is the one im trying to figure out here. Specifically, how can the forked process immediately identify (e.g., the first time its debug trap fires) that it was just forked so that it can write its timing log data to a different tmpfile?

I can figure this out in the parent function by recording $! in a variable every time the debug trap fires and if this doesnt match $! in the next debug trap then I know something was forked. But, $! isnt updated in the forked process, only in the parent. I could have the parent save a list of forked processes in a tmpfile and then have all processes check for their $BASHPID in this list. This almost work, but the first few debug trap events in the forked process tend to get missed.

EDIT: here is an example of the sort of time profile im trying to create. In this example there is something not quite right with how subshells get accounted for, but the issue in trying to figure out a fix for with this post is that all the forked echo _d ommands should be split off into their own profiles, since their parent isnt waiting for them and thus their runtime shouldnt be included in the parents runtime.

Im working on a bash profiling tool called timep that gives per-command runtime profile for bash scripts and functions. It does this using a DEBUG trap to record start time and stop time for each command plus various info/metadata ($LINENO, $BASHPID, $FUNCNAME, $BASH_COMMAND, etc.) about the command being run. The current version can be found on github.

Im trying to group together the commands so that the profile(s) generated match the way the code being profiled is written. In particular:

  1. function calls are split off into a separate line-by-line profile, and the profile for the caller gets a single line for the function call that includes the total time spent running each command run in the function (i.e., function runtime without timing instrumentation time)
  2. commands that get forked but are automatically waited on by the shell (subshells, command substitutions, pipelines, etc.) will NOT be split off into separate profiles. These commands will, however, list their PIDs hierarchically (PID0->PID1[->PID2[...]])
  3. commands that are explicitly forked and not automatically waited on (cmd &, coproc cmd) will be split off into separate profiles and their runtime (except for the time taken by the forking operation) will not be included in the caller's profile

#3 is the one im trying to figure out here. Specifically, how can the forked process immediately identify (e.g., the first time its debug trap fires) that it was just forked so that it can write its timing log data to a different tmpfile?

I can figure this out in the parent function by recording $! in a variable every time the debug trap fires and if this doesnt match $! in the next debug trap then I know something was forked. But, $! isnt updated in the forked process, only in the parent. I could have the parent save a list of forked processes in a tmpfile and then have all processes check for their $BASHPID in this list. This almost work, but the first few debug trap events in the forked process tend to get missed.

EDIT: here is an example of the sort of time profile im trying to create. In this example there is something not quite right with how subshells get accounted for, but the issue in trying to figure out a fix for with this post is that all the forked echo _d ommands should be split off into their own profiles, since their parent isnt waiting for them and thus their runtime shouldnt be included in the parents runtime.

Share Improve this question edited Mar 26 at 12:58 jkool702 asked Mar 25 at 16:00 jkool702jkool702 373 bronze badges 4
  • What makes you think that an arbitrary child process has anything resembling a debug trap? That's a shell feature, and only a subset of commands executed via a shell involve an additional level of shell. Maybe I just don't grasp what you're describing, but it seems to me that it would present a problem for it to require the participation of the commands being launched. – John Bollinger Commented Mar 25 at 19:13
  • @JohnBollinger - When the process is forked the DEBUG trap is copied (along with the rest of the current environment) into a forked bash environment, which in turn calls whatever you forked. This requires set -T. You can test via ( set -T; trap 'echo "$BASH_COMMAND --from-- $BASHPID" >>/tmp/test.log' DEBUG; { sleep 1; trap -p; echo $BASHPID; } & echo $BASHPID; ); sleep 1; cat /tmp/test.log. Also, I edited in an example into the main post that hopefully clarifies what Im trying to do. – jkool702 Commented Mar 26 at 13:24
  • No. Per the Bash manual: "When a simple command other than a builtin or shell function is to be executed, it is invoked in a separate execution environment, [in which ...] traps caught by the shell are reset to the values inherited from the shell’s parent [...]". That is not about subshells. set -T overrides that with respect to shell functions for DEBUG and RETURN traps, but not with respect to other simple commands. Maybe that's ok with you, though. – John Bollinger Commented Mar 26 at 19:01
  • @JohnBollinger - also from the Bash manual entry on set -T: "If set, any trap on DEBUG and RETURN are inherited by shell functions, command substitutions, and commands executed in a subshell environment." Forked commands and coprocs arent explicitly listed, but the test code in my previous comment pretty clearly indicated they should be included in that list. – jkool702 Commented Mar 26 at 22:18
Add a comment  | 

1 Answer 1

Reset to default 2

Use one of these builtin variables:

BASH_SUBSHELL

Incremented by one within each subshell or subshell environment when the shell begins executing in that environment. The initial value is 0. If BASH_SUBSHELL is unset, it loses its special properties, even if it is subsequently reset.

SHLVL

Incremented by one each time a new instance of Bash is started. This is intended to be a count of how deeply your Bash shells are nested.

发布评论

评论列表(0)

  1. 暂无评论