DTrace
Experimental DTrace (XD)
language extensions
Matt.Ahrens@delphix.com
D is a (safe) power tool
● Some D concepts (predicates) are unfamiliar
to procedural programmers
● Some simple ideas require many steps
● These steps are (for the most part)
fundamentally necessary but tedious
● Solution: syntactic sugar
if/else in XD
vdev_queue_pending_remove:entry
{
if (stringof(args[1]->io_spa->spa_name) == $$1) {
if (args[1]->io_type == ZIO_TYPE_READ) {
@bytes_read = sum(args[1]->io_size);
} else if (args[1]->io_type == ZIO_TYPE_WRITE &&
args[1]->io_bookmark.zb_level != 2) {
@bytes_written = sum(args[1]->io_size);
}
}
}
if/else converted to D
dtrace:::ERROR{ self->_XD_error = 0x1; }
::vdev_queue_pending_remove:entry{ self->_XD_error = 0x0; }
::vdev_queue_pending_remove:entry /!self->_XD_error/
{ this->_XD_condition1 = 0x1 && stringof(args[1]->io_spa->spa_name) == $$1; }
::vdev_queue_pending_remove:entry /!self->_XD_error/
{ this->_XD_condition2 = this->_XD_condition1 && args[1]->io_type == ZIO_TYPE_READ; }
::vdev_queue_pending_remove:entry /(!self->_XD_error) && this->_XD_condition2/
{ @bytes_read = sum(args[1]->io_size); }
::vdev_queue_pending_remove:entry /!self->_XD_error/
{ this->_XD_condition3 = this->_XD_condition1 && !this->_XD_condition2; }
::vdev_queue_pending_remove:entry /!self->_XD_error/
{ this->_XD_condition4 =
this->_XD_condition3 && args[1]->io_type == ZIO_TYPE_WRITE && args[1]->io_bookmark.zb_level != 2; }
::vdev_queue_pending_remove:entry /!self->_XD_error && this->_XD_condition4/
{ @bytes_written = sum(args[1]->io_size); }
while loop in XD
pid$target::zprop_free_list:entry
{
ll_user = arg0;
printf("-- START --");
while10 (ll_user != NULL) {
ll_kern = copyin(ll_user, sizeof(zprop_list_t));
printf("%d", ll_kern->pl_prop);
ll_user = ll_kern->pl_next;
}
printf("-- END --");
}
entry-> variables in XD
spa_sync:return
{
printf("%s(%s, %u) took %ums",
probefunc,
entry->args[0]->spa_name, entry->args[1],
entry->elapsed_ms);
}
callers[] in XD
zrl_add:entry / callers["resolvepath,traverse"] / {@[stack()] = count()}
● The value of callers[] is a count, not just a toggle
○ Useful for examining recursive functions.
● callers["resolvepath,traverse"]
○ Either function must be in the stack
● callers["resolvepath"] && callers["traverse"]
○ Both functions must be in the stack
● Possible performance considerations
○ Each element means two more probes, be cognizant of enabled probe
effect.
How does it do that?
Changes are primarily in libdtrace
● Create parse tree
○ new nodes for "if", "while"
● Transform parse tree to remove XD
○ create new clauses
○ swap out entry->*, callers[]
● Finish compiling the (now strictly D) parse tree
● No kernel changes
How can I use it?
● https://github.com/ahrens/illumos/tree/dpp
○ fork of illumos
○ 100% libdtrace, should be easy to port
● To see the D generated from your XD
○ dtrace -xd -x tree=8 …
● Examples: https://github.com/ahrens/dtrace
● Bugs?
○ email matt@delphix.com your XD script
Field use
● In production at Delphix since April 2014
● if/else: increases legibility of larger scripts
○ ~50% of scripts use “if”
● while: rarely used
● entry->arg[], entry->elapsed
○ Extremely helpful for one-liners
○ ~75% of scripts use “entry->”
● callers[]
○ Helpful for one-liners
○ ~33% of scripts use “callers[]”
Field use (one-liner example)
● Histograms of time in foo(), broken down by
request size, when called from bar()
dtrace -xd -n ‘
foo:return
/callers[“bar”]/
{
@[entry->args[2]->b_size] =
quantize(entry->elapsed_us);
}’
Integration status
● Pollutes namespace with “_XD_*” variables
○ Should be possible to fix
● if/else: agreement that this is a good idea
○ “if” and “else” are already reserved keywords
● while: not that useful, let’s not bother
● entry->arg[], entry->elapsed
○ Concern about FBT concepts leaking into language
○ Concern about “entry” polluting namespace
○ But sooooo useful! Needs more discussion.
Integration status
● callers[]
○ Concern about dependence on FBT
○ Concern about hiding perf cost
○ Idea: implement by examining stack instead?
■ Pro: linear performance impact
■ Downside: reliance on backtrace accuracy
■ Downside: requires kernel changes
● “Is there an address in the range [x,y] in the backtrace”
What next?
1. Integrate if/else to illumos
2. Figure out entry-> concerns
3. Integrate entry-> to illumos
4. Figure out callers[] FBT vs stack()
5. Integrate callers[]
What next?
● "for" and "do" loops; "break", "continue"(?)
● #pragma D option defaultscope=local
○ bare variables will have probe-local scope
■ i.e. implicit "this->"
○ use "global->" for global variables
○ use "thread->" for thread-local variables
● "inline" functions to replace preprocessor?
● better error diagnosability?
○ dtrace: error on enabled probe ID 463 (ID 33804:
fbt:zfs:put_nvlist:entry): invalid address (0x0) in
action #1 at DIF offset 36
What next? (continued)
● Predicate Scoping
○ Applying a predicate to multiple clauses specified in a block:
/callers["spa_sync"]/
{
zio_read:entry {
stack();
}
metaslab_sync:return {
trace(entry->elapsed_ms);
}
}
DTrace
XD: DTrace language extensions
Matt.Ahrens@delphix.com
if/else in D
vdev_queue_pending_remove:entry
/stringof(args[1]->io_spa->spa_name) == $$1 &&
args[1]->io_type == ZIO_TYPE_READ/
{
@bytes_read = sum(args[1]->io_size);
}
vdev_queue_pending_remove:entry
/stringof(args[1]->io_spa->spa_name) == $$1 &&
args[1]->io_type == ZIO_TYPE_WRITE &&
args[1]->io_bookmark.zb_level != -2/
{
@bytes_written = sum(args[1]->io_size);
if/else in XD
vdev_queue_pending_remove:entry
{
if (stringof(args[1]->io_spa->spa_name) == $$1) {
if (args[1]->io_type == ZIO_TYPE_READ) {
@bytes_read = sum(args[1]->io_size);
} else if (args[1]->io_type == ZIO_TYPE_WRITE &&
args[1]->io_bookmark.zb_level != 2) {
@bytes_written = sum(args[1]->io_size);
}
}
}
if/else converted to D
dtrace:::ERROR{ self->_XD_error = 0x1; }
::vdev_queue_pending_remove:entry{ self->_XD_error = 0x0; }
::vdev_queue_pending_remove:entry /!self->_XD_error/
{ this->_XD_condition1 = 0x1 && stringof(args[1]->io_spa->spa_name) == $$1; }
::vdev_queue_pending_remove:entry /!self->_XD_error/
{ this->_XD_condition2 = this->_XD_condition1 && args[1]->io_type == ZIO_TYPE_READ; }
::vdev_queue_pending_remove:entry /(!self->_XD_error) && this->_XD_condition2/
{ @bytes_read = sum(args[1]->io_size); }
::vdev_queue_pending_remove:entry /!self->_XD_error/
{ this->_XD_condition3 = this->_XD_condition1 && !this->_XD_condition2; }
::vdev_queue_pending_remove:entry /!self->_XD_error/
{ this->_XD_condition4 =
this->_XD_condition3 && args[1]->io_type == ZIO_TYPE_WRITE && args[1]->io_bookmark.zb_level != 2; }
::vdev_queue_pending_remove:entry /!self->_XD_error && this->_XD_condition4/
{ @bytes_written = sum(args[1]->io_size); }
while loop in D
pid$target::zprop_free_list:entry { ll_user = arg0; printf("-- START --");
}
pid$target::zprop_free_list:entry /ll_user != NULL/ {
ll_kern = copyin(ll_user, sizeof(zprop_list_t));
printf("%d", ll_kern->pl_prop);
ll_user = ll_kern->pl_next;
}
pid$target::zprop_free_list:entry /ll_user != NULL/ {
ll_kern = copyin(ll_user, sizeof(zprop_list_t));
printf("%d", ll_kern->pl_prop);
ll_user = ll_kern->pl_next;
}
/* More copies go here */
pid$target::zprop_free_list:entry { printf("-- END --"); }
while loop in XD
pid$target::zprop_free_list:entry
{
ll_user = arg0;
printf("-- START --");
while10 (ll_user != NULL) {
ll_kern = copyin(ll_user, sizeof(zprop_list_t));
printf("%d", ll_kern->pl_prop);
ll_user = ll_kern->pl_next;
}
printf("-- END --");
}
while loop converted to D
pid$target::zprop_free_list:entry { ll_user = arg0; printf("-- START --"); }
pid$target::zprop_free_list:entry {this->_XD_condition2 = (ll_user != 0x0);}
pid$target::zprop_free_list:entry /this->_XD_condition2/ {
ll_kern = copyin(ll_user, sizeof(zprop_list_t));
printf(" %d", ll_kern->pl_prop);
ll_user = ll_kern->pl_next;
}
pid$target::zprop_free_list:entry {this->_XD_condition2 = (ll_user != 0x0);}
pid$target::zprop_free_list:entry /this->_XD_condition2/ {
ll_kern = copyin(ll_user, sizeof(zprop_list_t));
printf(" %d", ll_kern->pl_prop);
ll_user = ll_kern->pl_next;
}
/* ... repeat 8 more times ... */
pid$target::zprop_free_list:entry { printf("-- END --"); }
printing nvlists in XD
● manually-managed stack for nested nvlists
● print "function" implemented as C preprocessor macro
○ implement "inline" XD functions?
● also straightforward to implement nvlist_lookup_
{uint64,string,etc}
● definitely need to increase dtrace_dof_maxsize!
● pseudocode follows
○ assume everything starts with "this->"
elem = list->nvl_first;
while20 (elem != NULL) {
printf("%s", stringof(elem->name));
if (elem->type == DATA_TYPE_UINT64) {
printf(": %un", *(uint64_t *)elem->valuep);
elem = elem->next;
} else if (elem->type == DATA_TYPE_STRING) {
printf(": %sn", stringof((char *)elem->valuep));
elem = elem->next;
} else if (elem->type == DATA_TYPE_NVLIST) {
stack[curframe].elem = elem;
stack[curframe].list = list;
curframe++;
list = (nvlist_t *)elem->nvi_valuep;
elem = list->nvl_first;
}
if (elem == NULL && curframe > 0) {
curframe--;
elem = stack[curframe].elem;
list = stack[curframe].list;
}
}
if (elem != NULL)
entry->* variables in D
spa_sync:entry
{
self->spa = args[0];
self->txg = args[1];
self->start = timestamp;
}
spa_sync:return
/self->start/
{
printf("%s(%s, %u) took %ums",
probefunc, self->spa->spa_name, self->txg,
(timestamp - self->start)/1000/1000);
self->spa = 0;
self->txg = 0;
self->start = 0;
}
Hope this function
isn't recursive!
entry->* variables in XD
spa_sync:return
{
printf("%s(%s, %u) took %ums",
probefunc,
entry->args[0]->spa_name, entry->args[1],
entry->elapsed_ms);
}
entry->* converted to D
::spa_sync:entry
{
self->_XD_entry_args1[stackdepth] = args[1];
self->_XD_entry_args0[stackdepth] = args[0];
self->_XD_entry_timestamp[stackdepth] = timestamp;
}
::spa_sync:return
{ this->_XD_condition1 = 0x1 && self->_XD_entry_timestamp[stackdepth]; }
::spa_sync:return
/this->_XD_condition1/
{
printf("%s(%s, %u) took %ums",
probefunc, self->_XD_entry_args0[stackdepth]->spa_name,
self->_XD_entry_arg1[stackdepth],
(timestamp - self->_XD_entry_timestamp[stackdepth]) / 1000 / 1000);
}
::spa_sync:return
{
self->_XD_entry_arg1[stackdepth] = 0x0;
callers[] in D
resolvepath:entry,traverse:entry
{
self->trace = 1;
}
zrl_add:entry
/ self->trace != 0 /
{
@[stack()] = count();
}
resolvepath:return,traverse:return
{
self->trace = 0;
}
What if resolvepath and traverse are both
on the stack?
callers[] in XD
Now a one-liner:
zrl_add:entry / callers["resolvepath,traverse"] / {@[stack()] = count()}
● The value of callers[] is a count, not just a toggle
○ Useful for examining recursive functions.
● callers["resolvepath,traverse"]
○ Either function must be in the stack
● callers["resolvepath"] && callers["traverse"]
○ Both functions must be in the stack
● Possible performance considerations
○ Each element means two more probes, be cognizant of enabled probe
effect.
callers[] converted to D
::resolvepath:entry,
::traverse:entry
{ ++self->_XD_callers1; }
::zrl_add:entry
{ this->_XD_condition1 = 0x1 && self->_XD_callers1; }
::zrl_add:entry
/this->_XD_condition1/
{ @_[stack()] = count(); }
::resolvepath:return,
::traverse:return
/self->_XD_callers1/
{ --self->_XD_callers1; }

Experimental dtrace

  • 1.
    DTrace Experimental DTrace (XD) languageextensions Matt.Ahrens@delphix.com
  • 2.
    D is a(safe) power tool ● Some D concepts (predicates) are unfamiliar to procedural programmers ● Some simple ideas require many steps ● These steps are (for the most part) fundamentally necessary but tedious ● Solution: syntactic sugar
  • 3.
    if/else in XD vdev_queue_pending_remove:entry { if(stringof(args[1]->io_spa->spa_name) == $$1) { if (args[1]->io_type == ZIO_TYPE_READ) { @bytes_read = sum(args[1]->io_size); } else if (args[1]->io_type == ZIO_TYPE_WRITE && args[1]->io_bookmark.zb_level != 2) { @bytes_written = sum(args[1]->io_size); } } }
  • 4.
    if/else converted toD dtrace:::ERROR{ self->_XD_error = 0x1; } ::vdev_queue_pending_remove:entry{ self->_XD_error = 0x0; } ::vdev_queue_pending_remove:entry /!self->_XD_error/ { this->_XD_condition1 = 0x1 && stringof(args[1]->io_spa->spa_name) == $$1; } ::vdev_queue_pending_remove:entry /!self->_XD_error/ { this->_XD_condition2 = this->_XD_condition1 && args[1]->io_type == ZIO_TYPE_READ; } ::vdev_queue_pending_remove:entry /(!self->_XD_error) && this->_XD_condition2/ { @bytes_read = sum(args[1]->io_size); } ::vdev_queue_pending_remove:entry /!self->_XD_error/ { this->_XD_condition3 = this->_XD_condition1 && !this->_XD_condition2; } ::vdev_queue_pending_remove:entry /!self->_XD_error/ { this->_XD_condition4 = this->_XD_condition3 && args[1]->io_type == ZIO_TYPE_WRITE && args[1]->io_bookmark.zb_level != 2; } ::vdev_queue_pending_remove:entry /!self->_XD_error && this->_XD_condition4/ { @bytes_written = sum(args[1]->io_size); }
  • 5.
    while loop inXD pid$target::zprop_free_list:entry { ll_user = arg0; printf("-- START --"); while10 (ll_user != NULL) { ll_kern = copyin(ll_user, sizeof(zprop_list_t)); printf("%d", ll_kern->pl_prop); ll_user = ll_kern->pl_next; } printf("-- END --"); }
  • 6.
    entry-> variables inXD spa_sync:return { printf("%s(%s, %u) took %ums", probefunc, entry->args[0]->spa_name, entry->args[1], entry->elapsed_ms); }
  • 7.
    callers[] in XD zrl_add:entry/ callers["resolvepath,traverse"] / {@[stack()] = count()} ● The value of callers[] is a count, not just a toggle ○ Useful for examining recursive functions. ● callers["resolvepath,traverse"] ○ Either function must be in the stack ● callers["resolvepath"] && callers["traverse"] ○ Both functions must be in the stack ● Possible performance considerations ○ Each element means two more probes, be cognizant of enabled probe effect.
  • 8.
    How does itdo that? Changes are primarily in libdtrace ● Create parse tree ○ new nodes for "if", "while" ● Transform parse tree to remove XD ○ create new clauses ○ swap out entry->*, callers[] ● Finish compiling the (now strictly D) parse tree ● No kernel changes
  • 9.
    How can Iuse it? ● https://github.com/ahrens/illumos/tree/dpp ○ fork of illumos ○ 100% libdtrace, should be easy to port ● To see the D generated from your XD ○ dtrace -xd -x tree=8 … ● Examples: https://github.com/ahrens/dtrace ● Bugs? ○ email matt@delphix.com your XD script
  • 10.
    Field use ● Inproduction at Delphix since April 2014 ● if/else: increases legibility of larger scripts ○ ~50% of scripts use “if” ● while: rarely used ● entry->arg[], entry->elapsed ○ Extremely helpful for one-liners ○ ~75% of scripts use “entry->” ● callers[] ○ Helpful for one-liners ○ ~33% of scripts use “callers[]”
  • 11.
    Field use (one-linerexample) ● Histograms of time in foo(), broken down by request size, when called from bar() dtrace -xd -n ‘ foo:return /callers[“bar”]/ { @[entry->args[2]->b_size] = quantize(entry->elapsed_us); }’
  • 12.
    Integration status ● Pollutesnamespace with “_XD_*” variables ○ Should be possible to fix ● if/else: agreement that this is a good idea ○ “if” and “else” are already reserved keywords ● while: not that useful, let’s not bother ● entry->arg[], entry->elapsed ○ Concern about FBT concepts leaking into language ○ Concern about “entry” polluting namespace ○ But sooooo useful! Needs more discussion.
  • 13.
    Integration status ● callers[] ○Concern about dependence on FBT ○ Concern about hiding perf cost ○ Idea: implement by examining stack instead? ■ Pro: linear performance impact ■ Downside: reliance on backtrace accuracy ■ Downside: requires kernel changes ● “Is there an address in the range [x,y] in the backtrace”
  • 14.
    What next? 1. Integrateif/else to illumos 2. Figure out entry-> concerns 3. Integrate entry-> to illumos 4. Figure out callers[] FBT vs stack() 5. Integrate callers[]
  • 15.
    What next? ● "for"and "do" loops; "break", "continue"(?) ● #pragma D option defaultscope=local ○ bare variables will have probe-local scope ■ i.e. implicit "this->" ○ use "global->" for global variables ○ use "thread->" for thread-local variables ● "inline" functions to replace preprocessor? ● better error diagnosability? ○ dtrace: error on enabled probe ID 463 (ID 33804: fbt:zfs:put_nvlist:entry): invalid address (0x0) in action #1 at DIF offset 36
  • 16.
    What next? (continued) ●Predicate Scoping ○ Applying a predicate to multiple clauses specified in a block: /callers["spa_sync"]/ { zio_read:entry { stack(); } metaslab_sync:return { trace(entry->elapsed_ms); } }
  • 17.
    DTrace XD: DTrace languageextensions Matt.Ahrens@delphix.com
  • 18.
    if/else in D vdev_queue_pending_remove:entry /stringof(args[1]->io_spa->spa_name)== $$1 && args[1]->io_type == ZIO_TYPE_READ/ { @bytes_read = sum(args[1]->io_size); } vdev_queue_pending_remove:entry /stringof(args[1]->io_spa->spa_name) == $$1 && args[1]->io_type == ZIO_TYPE_WRITE && args[1]->io_bookmark.zb_level != -2/ { @bytes_written = sum(args[1]->io_size);
  • 19.
    if/else in XD vdev_queue_pending_remove:entry { if(stringof(args[1]->io_spa->spa_name) == $$1) { if (args[1]->io_type == ZIO_TYPE_READ) { @bytes_read = sum(args[1]->io_size); } else if (args[1]->io_type == ZIO_TYPE_WRITE && args[1]->io_bookmark.zb_level != 2) { @bytes_written = sum(args[1]->io_size); } } }
  • 20.
    if/else converted toD dtrace:::ERROR{ self->_XD_error = 0x1; } ::vdev_queue_pending_remove:entry{ self->_XD_error = 0x0; } ::vdev_queue_pending_remove:entry /!self->_XD_error/ { this->_XD_condition1 = 0x1 && stringof(args[1]->io_spa->spa_name) == $$1; } ::vdev_queue_pending_remove:entry /!self->_XD_error/ { this->_XD_condition2 = this->_XD_condition1 && args[1]->io_type == ZIO_TYPE_READ; } ::vdev_queue_pending_remove:entry /(!self->_XD_error) && this->_XD_condition2/ { @bytes_read = sum(args[1]->io_size); } ::vdev_queue_pending_remove:entry /!self->_XD_error/ { this->_XD_condition3 = this->_XD_condition1 && !this->_XD_condition2; } ::vdev_queue_pending_remove:entry /!self->_XD_error/ { this->_XD_condition4 = this->_XD_condition3 && args[1]->io_type == ZIO_TYPE_WRITE && args[1]->io_bookmark.zb_level != 2; } ::vdev_queue_pending_remove:entry /!self->_XD_error && this->_XD_condition4/ { @bytes_written = sum(args[1]->io_size); }
  • 21.
    while loop inD pid$target::zprop_free_list:entry { ll_user = arg0; printf("-- START --"); } pid$target::zprop_free_list:entry /ll_user != NULL/ { ll_kern = copyin(ll_user, sizeof(zprop_list_t)); printf("%d", ll_kern->pl_prop); ll_user = ll_kern->pl_next; } pid$target::zprop_free_list:entry /ll_user != NULL/ { ll_kern = copyin(ll_user, sizeof(zprop_list_t)); printf("%d", ll_kern->pl_prop); ll_user = ll_kern->pl_next; } /* More copies go here */ pid$target::zprop_free_list:entry { printf("-- END --"); }
  • 22.
    while loop inXD pid$target::zprop_free_list:entry { ll_user = arg0; printf("-- START --"); while10 (ll_user != NULL) { ll_kern = copyin(ll_user, sizeof(zprop_list_t)); printf("%d", ll_kern->pl_prop); ll_user = ll_kern->pl_next; } printf("-- END --"); }
  • 23.
    while loop convertedto D pid$target::zprop_free_list:entry { ll_user = arg0; printf("-- START --"); } pid$target::zprop_free_list:entry {this->_XD_condition2 = (ll_user != 0x0);} pid$target::zprop_free_list:entry /this->_XD_condition2/ { ll_kern = copyin(ll_user, sizeof(zprop_list_t)); printf(" %d", ll_kern->pl_prop); ll_user = ll_kern->pl_next; } pid$target::zprop_free_list:entry {this->_XD_condition2 = (ll_user != 0x0);} pid$target::zprop_free_list:entry /this->_XD_condition2/ { ll_kern = copyin(ll_user, sizeof(zprop_list_t)); printf(" %d", ll_kern->pl_prop); ll_user = ll_kern->pl_next; } /* ... repeat 8 more times ... */ pid$target::zprop_free_list:entry { printf("-- END --"); }
  • 24.
    printing nvlists inXD ● manually-managed stack for nested nvlists ● print "function" implemented as C preprocessor macro ○ implement "inline" XD functions? ● also straightforward to implement nvlist_lookup_ {uint64,string,etc} ● definitely need to increase dtrace_dof_maxsize! ● pseudocode follows ○ assume everything starts with "this->"
  • 25.
    elem = list->nvl_first; while20(elem != NULL) { printf("%s", stringof(elem->name)); if (elem->type == DATA_TYPE_UINT64) { printf(": %un", *(uint64_t *)elem->valuep); elem = elem->next; } else if (elem->type == DATA_TYPE_STRING) { printf(": %sn", stringof((char *)elem->valuep)); elem = elem->next; } else if (elem->type == DATA_TYPE_NVLIST) { stack[curframe].elem = elem; stack[curframe].list = list; curframe++; list = (nvlist_t *)elem->nvi_valuep; elem = list->nvl_first; } if (elem == NULL && curframe > 0) { curframe--; elem = stack[curframe].elem; list = stack[curframe].list; } } if (elem != NULL)
  • 26.
    entry->* variables inD spa_sync:entry { self->spa = args[0]; self->txg = args[1]; self->start = timestamp; } spa_sync:return /self->start/ { printf("%s(%s, %u) took %ums", probefunc, self->spa->spa_name, self->txg, (timestamp - self->start)/1000/1000); self->spa = 0; self->txg = 0; self->start = 0; } Hope this function isn't recursive!
  • 27.
    entry->* variables inXD spa_sync:return { printf("%s(%s, %u) took %ums", probefunc, entry->args[0]->spa_name, entry->args[1], entry->elapsed_ms); }
  • 28.
    entry->* converted toD ::spa_sync:entry { self->_XD_entry_args1[stackdepth] = args[1]; self->_XD_entry_args0[stackdepth] = args[0]; self->_XD_entry_timestamp[stackdepth] = timestamp; } ::spa_sync:return { this->_XD_condition1 = 0x1 && self->_XD_entry_timestamp[stackdepth]; } ::spa_sync:return /this->_XD_condition1/ { printf("%s(%s, %u) took %ums", probefunc, self->_XD_entry_args0[stackdepth]->spa_name, self->_XD_entry_arg1[stackdepth], (timestamp - self->_XD_entry_timestamp[stackdepth]) / 1000 / 1000); } ::spa_sync:return { self->_XD_entry_arg1[stackdepth] = 0x0;
  • 29.
    callers[] in D resolvepath:entry,traverse:entry { self->trace= 1; } zrl_add:entry / self->trace != 0 / { @[stack()] = count(); } resolvepath:return,traverse:return { self->trace = 0; } What if resolvepath and traverse are both on the stack?
  • 30.
    callers[] in XD Nowa one-liner: zrl_add:entry / callers["resolvepath,traverse"] / {@[stack()] = count()} ● The value of callers[] is a count, not just a toggle ○ Useful for examining recursive functions. ● callers["resolvepath,traverse"] ○ Either function must be in the stack ● callers["resolvepath"] && callers["traverse"] ○ Both functions must be in the stack ● Possible performance considerations ○ Each element means two more probes, be cognizant of enabled probe effect.
  • 31.
    callers[] converted toD ::resolvepath:entry, ::traverse:entry { ++self->_XD_callers1; } ::zrl_add:entry { this->_XD_condition1 = 0x1 && self->_XD_callers1; } ::zrl_add:entry /this->_XD_condition1/ { @_[stack()] = count(); } ::resolvepath:return, ::traverse:return /self->_XD_callers1/ { --self->_XD_callers1; }