Search code examples
makefileshgnu-makepid

Capture PID of command and use later inside makefile rule


I am struggling to find an elegant way of doing the following:

I want a makefile rule which:

  • Runs QEMU with certain args, this generates a trace file trace-XXXXX where XXXXX is the PID of QEMU when it ran.
  • Captures this PID when qemu is invoked, ie ./qemu ... & echo $! and put this in some variable (where the scope of the variable is within the rule)
  • Waits for QEMU to finish and then runs a script on the output trace file

Currently I have the following:

trace: 
    ./qemu/build/qemu-riscv64 -d plugin,nochain --trace "exec_tb" -plugin ./test_plugin.so ./test_qemu & export TRACE_PID=$$!; \
    echo Trace PID is $$TRACE_PID; \
    wait $$TRACE_PID; \ 
    python3 ./qemu/scripts/simpletrace.py ./qemu/build/trace/trace-events-all trace-$$TRACE_PID

But it doesn't seem to work. I see the "Trace PID is " but then get a command not found on line 1 which I think could be due to the stdout of qemu breaking things.

Is there a clean and elegant way to do this?


Solution

  • Captures this PID when qemu is invoked, ie ./qemu ... & echo $!

    The & causes your qemu to run in the background, but this is counter-productive because you want to wait for it to complete.

    The only real reason to capture the PID seems to be to compute the name of the trace file. If qemu supports it, the best approach would be to choose the name for the trace file yourself, and tell qemu to write there. Then you could just run qemu in the foreground (no wait), and analyze the file afterward. The mktemp command would serve to create a new file with a distinct name, if you need that.

    If you have no way to control the trace file name up front, then you could still do a little better (IMO) by keeping everything in the foreground, something like this:

    trace: 
            TRACE_PID=$$(/bin/sh -c 'echo $$$$; exec ./qemu/build/qemu-riscv64 -d plugin,nochain --trace "exec_tb" -plugin ./test_plugin.so ./test_qemu >qemu.out 2>&1'); \
            echo Trace PID is $$TRACE_PID; \
            python3 ./qemu/scripts/simpletrace.py ./qemu/build/trace/trace-events-all trace-$$TRACE_PID
    

    Notes:

    • the shell will execute the contents of the command substitution $(...) in a subshell, but that's not necessarily running in a different process. We do want a different process for this, so from the subshell we start a separate shell process.

    • the shell running in that separate process reports its PID via an echo command, which the top-level shell will capture. It uses exec to run qemu in the same process, so the PID already reported will be correct for qemu.

    • The qemu command's standard output is redirected to a file to prevent it being captured, too. Its standard error is redirected to the same file for tidiness, and a bit to avoid potential confusion.

    • There is no need to wait for anything because no background jobs have been started. The echo Trace PID ... command does not start until after qemu has finished.

    • There does not appear to be a need to export TRACE_PID, as it appears only to be used in the current shell process, not in any child processes.

    • Neither this version nor yours responds to a qemu failure. This version could more easily be adapted to do that, and probably should be so adapted.

    • You might want to provide a full path to the python3 executable. You might even want to create a make variable for it.