views:

416

answers:

3

There are Mac GUI applications which provide a front-end to more geeky commandline tools (often included as a part of the application package). I would like to look at what is happening under the hood of such GUIs.

How to "attach" to an application, monitor it for calls to command line utilities and log a filename and command line parameters of these calls?

A solution can also be an application that logs execution of all applications on Mac OS X (filtering out the most common system calls).

Example GUI frontend: http://xact.sourceforge.net/ (since it is open source one can just debug it, but xACT is just an example. let's pretend we have just a ready-made *.app to monitor).

Update: dtrace can monitor exec calls and print name of the command called. that's a half of the solution, the other half is getting its command line arguments. that's unsolved yet (until someone confirms they have got dtrace to do this).

+3  A: 

You could use dtrace to monitor the exec*() system calls and display the arguments when they're invoked. dtrace is documented here: http://wikis.sun.com/display/DTrace/Documentation

Graham Lee
see a comment in the my answer below. could you give an example dtrace script?
CaptSolo
That sounds too much like "pls to give me teh codez", but I'll tell you this - dtrace can print the arguments to any syscall. The parameters to the executable run in exec*() are available in the arguments.
Graham Lee
Cool, thanks. I don't think it is wrong to ask someone "show me the code" as often example code is the best explanation and of most value to others who look for answers to this question.(Well, unless someone is paid to write the code and then asks others to do it instead. Not in this case.)
CaptSolo
+1  A: 

Graham: dtrace would be perfect here. could you (or anyone else here) show a dtrace script that would print the commandline of the process?

This oneliner prints names of processes being executed:

dtrace -qn 'syscall::exec*:return { printf("%Y %s\n",walltimestamp,curpsinfo->pr_psargs); }'

But how to get / print their command line arguments?

CaptSolo
dtrace can print out the arguments to a syscall. The arguments to exec*() are the argv[] of the program to be run.
Graham Lee
this doesn't work. seems like there is a bug in dtrace on Mac OS X in that it does not expose command line parameters. a script meant to do this also only shows just the name of the program (w/o params): http://www.brendangregg.com/DTrace/execsnoop.d
CaptSolo
here's a reference re dtrace not exposing the whole commandline: "curpsinfo->ps_args doesn’t contain the entire command-line of the process; it only contains the first word"http://benjamin.smedbergs.us/blog/2008-11-20/282/
CaptSolo
"sudo newproc.d" will print command line arguments. Read the script for more of the gritty details.
Joey Hagedorn
@Joey - sudo newproc.d doesn't do it either. It still just prints curpsinfo->ps_args, which still only contains the first word.
Barry Kelly
@Barry the newproc.d included in /usr/bin/newproc.d does not use curpsinfo->ps_args, but instead curpsinfo->pr_argc and curpsinfo->pr_argv https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/newproc.d.1m.html
Joey Hagedorn
@Joey: this is the effective source to /usr/bin/newproc.d (10.5): `proc:::exec-success { trace(stringof(curpsinfo->pr_psargs)); }`. As you can see, it contains no mention of `pr_arg[cv]`. But thanks for the tip, I'll try making my own DTrace script with those.
Barry Kelly
@Barry: No, it has been updated for Mac OS X 10.6. See here: http://www.opensource.apple.com/source/dtrace/dtrace-78/DTTk/Bin/newproc.d
Joey Hagedorn
@Joey - good stuff. However, that script is limited because it only does 5 arguments, and runs out of scratch space if you try to do more with the same technique, because it builds up a buffer. I'll add an answer with the script I'm using, which works for many more.
Barry Kelly
@Joey I added my script, or rather, a script which generates my script, based on the one you linked to. My version works much better for long command lines.
Barry Kelly
+1  A: 

DTrace can do the job. Based on the discussion I had with Joey Hagedorn in the comments elsewhere on this question, the script that comes with 10.6 can be improved to work with a reasonable number of arguments (50+). Because the script has a lot of repetition, I'll include here a script which outputs the DTrace script which works well. This one does up to 50 arguments; you may want to extend the number of arguments by changing the for-loop.

#!/bin/bash

cat <<HEADER
#!/usr/sbin/dtrace -s
/*
 * newproc.d - snoop new processes as they are executed. DTrace OneLiner.
 *
 * This is a DTrace OneLiner from the DTraceToolkit.
 *
 * 15-May-2005  Brendan Gregg   Created this.
 */

/*
 * Updated to capture arguments in OS X. Unfortunately this isn't straight forward...
 */

#pragma D option quiet

this unsigned long long argv_ptr; /* Wide enough for 64 bit user procs */

proc:::exec-success
{
    print_pid[pid] = 1; /* This pid emerged from an exec, make a note of that. */
}

/*
 * The "this" variables are local to (all) of the following syscall::mmap:return probes,
 * and only those probes. They must be initialized before use in each new firing.
 */
syscall::mmap:return
{
    this->argc = 0; /* Disable argument collection until we notice an exec-success */
}

syscall::mmap:return
/ print_pid[pid] /
{
    print_pid[pid] = 0;

    this->is64Bit = curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? 0 : 1;
    this->wordsize = this->is64Bit ? 8 : 4;

    this->argc = curpsinfo->pr_argc; 
    this->argc = (this->argc < 0) ? 0 : this->argc; /* Safety */

    this->argv_ptr = curpsinfo->pr_argv;

    printf("%d %s ", pid, this->is64Bit ? "64b" : "32b");
}

HEADER

for ((i=0;i<50;++i)); do

cat <<REPEAT
syscall::mmap:return
/ this->argc /
{
    this->here_argv = copyin(this->argv_ptr, this->wordsize);
    this->arg = this->is64Bit ? *(unsigned long long*)(this->here_argv) : *(unsigned long*)(this->here_argv);
    printf("%s ", copyinstr(this->arg));
    this->argv_ptr += this->wordsize;
    this->argc--;
}

REPEAT
done

cat <<FOOTER
syscall::mmap:return
/ this->argv_ptr /
{
    printf("%s\n", this->argc > 0 ? "(...)" : "");
    this->argc = 0;
    this->argv_ptr = 0;
}
FOOTER
Barry Kelly