How to trace a function array argument in DTrace
- by uejio
I still use dtrace just about every day in my job and found that I had to print an argument to a function which was an array of strings. The array was variable length up to about 10 items. I'm not sure if the is the right way to do it, but it seems to work and is not too painful if the array size is small.Here's an example. Suppose in your application, you have the following function, where n is number of item in the array s.void arraytest(int n, char **s){ /* Loop thru s[0] to s[n-1] */}How do you use DTrace to print out the values of s[i] or of s[0] to s[n-1]? DTrace does not have if-then blocks or for loops, so you can't do something like: for i=0; i<arg0; i++ trace arg1[i]; It turns out that you can use probe ordering as a kind of iterator. Probes with the same name will fire in the order that they appear in the script, so I can save the value of "n" in the first probe and then use it as part of the predicate of the next probe to determine if the other probe should fire or not. So the first probe for tracing the arraytest function is:pid$target::arraytest:entry{ self->n = arg0;}Then, if I want to print out the first few items of the array, I first check the value of n. If it's greater than the index that I want to print out, then I can print that index. For example, if I want to print out the 3rd element of the array, I would do something like:pid$target::arraytest:entry/self->n > 2/{ printf("%s",stringof(arg1 + 2 * sizeof(pointer)));}Actually, that doesn't quite work because arg1 is a pointer to an array of pointers and needs to be copied twice from the user process space to the kernel space (which is where dtrace is). Also, the sizeof(char *) is 8, but for some reason, I have to use 4 which is the sizeof(uint32_t). (I still don't know how that works.) So, the script that prints the 3rd element of the array should look like:pid$target::arraytest:entry{ /* first, save the size of the array so that we don't get invalid address errors when indexing arg1+n. */ self->n = arg0;}pid$target::arraytest:entry/self->n > 2/{ /* print the 3rd element (index = 2) of the second arg. */ i = 2; size = 4; self->a_t = copyin(arg1+size*i,size); printf("%s: a[%d]=%s",probefunc,i,copyinstr(*(uint32_t *)self->a_t));}If your array is large, then it's quite painful since you have to write one probe for every array index. For example, here's the full script for printing the first 5 elements of the array:#!/usr/sbin/dtrace -spid$target::arraytest:entry{ /* first, save the size of the array so that we don't get invalid address errors when indexing arg1+n. */ self->n = arg0;}pid$target::arraytest:entry/self->n > 0/{ i = 0; size = sizeof(uint32_t); self->a_t = copyin(arg1+size*i,size); printf("%s: a[%d]=%s",probefunc,i,copyinstr(*(uint32_t *)self->a_t));}pid$target::arraytest:entry/self->n > 1/{ i = 1; size = sizeof(uint32_t); self->a_t = copyin(arg1+size*i,size); printf("%s: a[%d]=%s",probefunc,i,copyinstr(*(uint32_t *)self->a_t));}pid$target::arraytest:entry/self->n > 2/{ i = 2; size = sizeof(uint32_t); self->a_t = copyin(arg1+size*i,size); printf("%s: a[%d]=%s",probefunc,i,copyinstr(*(uint32_t *)self->a_t));}pid$target::arraytest:entry/self->n > 3/{ i = 3; size = sizeof(uint32_t); self->a_t = copyin(arg1+size*i,size); printf("%s: a[%d]=%s",probefunc,i,copyinstr(*(uint32_t *)self->a_t));}pid$target::arraytest:entry/self->n > 4/{ i = 4; size = sizeof(uint32_t); self->a_t = copyin(arg1+size*i,size); printf("%s: a[%d]=%s",probefunc,i,copyinstr(*(uint32_t *)self->a_t));}
If the array is large, then your script will also have to be very long to print out all values of the array.