— modern ops stuff —
DTrace notes 03: the pid and profile providers
08 October 2011 // Solaris

pid

The main thing to remember here is the format of the probe name

# dtrace -n pidx:library:function:entry/return

pidx means the literal string pid followed by the PID of the process you’re interested in. e.g. pid482. Library is as simple as libc or libsocket.

You can get the objects in a running process by starting the debugger with mdb -p PID, then doing ::objects, or running pldd on the process. The functions in a library can be got at with nm /path/to/library.so. If you want to trace inside the executable itself you use the executable name rather than a library name

PID probes are created dynamically, when you try to find them.

profile

Profile probes fire at given time intervals. You can’t choose any time you can think of, but there are quite a few preset ones.

$ dtrace -lP profile

lists them. By default the number after the dash means how many times each second the probe will fire (so, Hz, I guess), but you can add on ms, us, or ns to have them fire at an interval of that many milli-, micro-, or nano-seconds. Or use m, h and d to have them fire so many minutes, hours, or days.

The default numbers are chosen to as not to line-up with system clocks, which tend to tick on frequency multiples of 10.

The tick- probes fire on a single CPU; the profile- probes fire on all CPUs.

arg0 and arg1 store the program counter of the current instruction. This is also called the instruction pointer, and it’s a processor register that keeps the memory address of the next instruction to be executed. If the thread is in kernel space, the PC is in arg0, if it’s in user space, the PC is arg1.

syscall

When a system call fails (i.e. returns -1), a global variable is set to the value defined in sys/errno.h. These are the errors you look for when you truss something. You know the kind of thing:

$ truss ls /nosuchfile
...
lstat64("/nosuchfile", 0x080464E0)                Err#2 ENOENT
...
$ grep ENOENT /usr/include/sys/errno.h
#define ENOENT  2       /* No such file or directory            */

When the syscall:::return probe fires, and there’s been an error, arg0 is set to -1, so if you’re looking for them your predicate would be /arg0 == -1/.

Kernel Variables and data structures

It’s important to know curpsinfo, which is a pointer to the psinfo data structure for the current thread. You can find the full definition for it in sys/procfs.h, but here it is anyway, because it’s so useful.

typedef struct psinfo {
    int       pr_flag;           /* process flags (DEPRECATED; do not use) */
    int       pr_nlwp;           /* number of active lwps in the process */
    pid_t     pr_pid;            /* unique process id */
    pid_t     pr_ppid;           /* process id of parent */
    pid_t     pr_pgid;           /* pid of process group leader */
    pid_t     pr_sid;            /* session id */
    uid_t     pr_uid;            /* real user id */
    uid_t     pr_euid;           /* effective user id */
    gid_t     pr_gid;            /* real group id */
    gid_t     pr_egid;           /* effective group id */
    uintptr_t pr_addr;           /* address of process */
    size_t    pr_size;           /* size of process image in Kbytes */
    size_t    pr_rssize;         /* resident set size in Kbytes */
    size_t    pr_pad1;
    dev_t     pr_ttydev;         /* controlling tty device (or PRNODEV) */
    ushort_t  pr_pctcpu;         /* % of recent cpu time used by all lwps */
    ushort_t  pr_pctmem;         /* % of system memory used by process */
    timestruc_t pr_start;        /* process start time, from the epoch */
    timestruc_t pr_time;         /* usr+sys cpu time for this process */
    timestruc_t pr_ctime;        /* usr+sys cpu time for reaped children */
    char      pr_fname[PRFNSZ];  /* name of execed file */
    char      pr_psargs[PRARGSZ];/* initial characters of arg list */
    int       pr_wstat;          /* if zombie, the wait() status */
    int       pr_argc;           /* initial argument count */
    uintptr_t pr_argv;           /* address of initial argument vector */
    uintptr_t pr_envp;           /* address of initial environment vector */
    char      pr_dmodel;         /* data model of the process */
    char      pr_pad2[3];
    taskid_t  pr_taskid;         /* task id */
    projid_t  pr_projid;         /* project id */
    int       pr_nzomb;          /* number of zombie lwps in the process */
    poolid_t  pr_poolid;         /* pool id */
    zoneid_t  pr_zoneid;         /* zone id */
    id_t      pr_contract;       /* process contract */
    int       pr_filler[1];      /* reserved for future use */
    lwpsinfo_t pr_lwp;           /* information for representative lwp */
} psinfo_t;

As you can see, that’s the place to go for pretty much any information on a process.

curlwpsinfo points to the lwpsinfo structure, which is also in sys/procfs.h. I haven’t reprinted it here because I haven’t had call to use it. There’s also a variable called curcpu which points to the cpuinfo data structure. That tells you which CPU and processor set is running your thread.

Though you can get to this same info by using curthread, which points to kthread_t, these methods are considered “better” because they use a proper API presented by the proc interface. (Check out /proc, it’s a bit like the Linux version, but only refers to processes. Each directory has a bunch of files in which describe things to do with the process, such as the open file descriptors or process map. Unlike Linux you can’t get things like the number of processors out of /proc, and it’s read-only.)

Tags: