From owner-svn-src-all@FreeBSD.ORG Sun Aug 19 09:21:21 2012 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 755D11065672; Sun, 19 Aug 2012 09:21:21 +0000 (UTC) (envelope-from mm@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 5EC828FC1A; Sun, 19 Aug 2012 09:21:21 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q7J9LL27056880; Sun, 19 Aug 2012 09:21:21 GMT (envelope-from mm@svn.freebsd.org) Received: (from mm@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q7J9LL8v056876; Sun, 19 Aug 2012 09:21:21 GMT (envelope-from mm@svn.freebsd.org) Message-Id: <201208190921.q7J9LL8v056876@svn.freebsd.org> From: Martin Matuska Date: Sun, 19 Aug 2012 09:21:21 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-vendor@freebsd.org X-SVN-Group: vendor-sys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r239386 - in vendor-sys/illumos/dist/uts/common: dtrace sys X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 19 Aug 2012 09:21:21 -0000 Author: mm Date: Sun Aug 19 09:21:20 2012 New Revision: 239386 URL: http://svn.freebsd.org/changeset/base/239386 Log: Update vendor-sys/illumos to illumos-gate 13758:23432da34147 (dtrace) References: https://www.illumos.org/issues/3021 https://www.illumos.org/issues/3022 https://www.illumos.org/issues/3023 https://www.illumos.org/issues/3024 https://www.illumos.org/issues/3025 https://www.illumos.org/issues/3026 Obtained from: ssh://anonhg@hg.illumos.org/illumos-gate Modified: vendor-sys/illumos/dist/uts/common/dtrace/dtrace.c vendor-sys/illumos/dist/uts/common/sys/dtrace.h vendor-sys/illumos/dist/uts/common/sys/dtrace_impl.h Modified: vendor-sys/illumos/dist/uts/common/dtrace/dtrace.c ============================================================================== --- vendor-sys/illumos/dist/uts/common/dtrace/dtrace.c Sun Aug 19 09:20:22 2012 (r239385) +++ vendor-sys/illumos/dist/uts/common/dtrace/dtrace.c Sun Aug 19 09:21:20 2012 (r239386) @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, Joyent, Inc. All rights reserved. + * Copyright (c) 2012 by Delphix. All rights reserved. */ /* @@ -2401,9 +2402,10 @@ dtrace_speculation_commit(dtrace_state_t { dtrace_speculation_t *spec; dtrace_buffer_t *src, *dest; - uintptr_t daddr, saddr, dlimit; + uintptr_t daddr, saddr, dlimit, slimit; dtrace_speculation_state_t current, new; intptr_t offs; + uint64_t timestamp; if (which == 0) return; @@ -2479,7 +2481,37 @@ dtrace_speculation_commit(dtrace_state_t } /* - * We have the space; copy the buffer across. (Note that this is a + * We have sufficient space to copy the speculative buffer into the + * primary buffer. First, modify the speculative buffer, filling + * in the timestamp of all entries with the current time. The data + * must have the commit() time rather than the time it was traced, + * so that all entries in the primary buffer are in timestamp order. + */ + timestamp = dtrace_gethrtime(); + saddr = (uintptr_t)src->dtb_tomax; + slimit = saddr + src->dtb_offset; + while (saddr < slimit) { + size_t size; + dtrace_rechdr_t *dtrh = (dtrace_rechdr_t *)saddr; + + if (dtrh->dtrh_epid == DTRACE_EPIDNONE) { + saddr += sizeof (dtrace_epid_t); + continue; + } + ASSERT3U(dtrh->dtrh_epid, <=, state->dts_necbs); + size = state->dts_ecbs[dtrh->dtrh_epid - 1]->dte_size; + + ASSERT3U(saddr + size, <=, slimit); + ASSERT3U(size, >=, sizeof (dtrace_rechdr_t)); + ASSERT3U(DTRACE_RECORD_LOAD_TIMESTAMP(dtrh), ==, UINT64_MAX); + + DTRACE_RECORD_STORE_TIMESTAMP(dtrh, timestamp); + + saddr += size; + } + + /* + * Copy the buffer across. (Note that this is a * highly subobtimal bcopy(); in the unlikely event that this becomes * a serious performance issue, a high-performance DTrace-specific * bcopy() should obviously be invented.) @@ -5951,7 +5983,7 @@ dtrace_probe(dtrace_id_t id, uintptr_t a if (now - state->dts_alive > dtrace_deadman_timeout) { /* * We seem to be dead. Unless we (a) have kernel - * destructive permissions (b) have expicitly enabled + * destructive permissions (b) have explicitly enabled * destructive actions and (c) destructive actions have * not been disabled, we're going to transition into * the KILLED state, from which no further processing @@ -5979,8 +6011,18 @@ dtrace_probe(dtrace_id_t id, uintptr_t a tomax = buf->dtb_tomax; ASSERT(tomax != NULL); - if (ecb->dte_size != 0) - DTRACE_STORE(uint32_t, tomax, offs, ecb->dte_epid); + if (ecb->dte_size != 0) { + dtrace_rechdr_t dtrh; + if (!(mstate.dtms_present & DTRACE_MSTATE_TIMESTAMP)) { + mstate.dtms_timestamp = dtrace_gethrtime(); + mstate.dtms_present |= DTRACE_MSTATE_TIMESTAMP; + } + ASSERT3U(ecb->dte_size, >=, sizeof (dtrace_rechdr_t)); + dtrh.dtrh_epid = ecb->dte_epid; + DTRACE_RECORD_STORE_TIMESTAMP(&dtrh, + mstate.dtms_timestamp); + *((dtrace_rechdr_t *)(tomax + offs)) = dtrh; + } mstate.dtms_epid = ecb->dte_epid; mstate.dtms_present |= DTRACE_MSTATE_EPID; @@ -6144,7 +6186,9 @@ dtrace_probe(dtrace_id_t id, uintptr_t a continue; switch (act->dta_kind) { - case DTRACEACT_SPECULATE: + case DTRACEACT_SPECULATE: { + dtrace_rechdr_t *dtrh; + ASSERT(buf == &state->dts_buffer[cpuid]); buf = dtrace_speculation_buffer(state, cpuid, val); @@ -6166,10 +6210,23 @@ dtrace_probe(dtrace_id_t id, uintptr_t a tomax = buf->dtb_tomax; ASSERT(tomax != NULL); - if (ecb->dte_size != 0) - DTRACE_STORE(uint32_t, tomax, offs, - ecb->dte_epid); + if (ecb->dte_size == 0) + continue; + + ASSERT3U(ecb->dte_size, >=, + sizeof (dtrace_rechdr_t)); + dtrh = ((void *)(tomax + offs)); + dtrh->dtrh_epid = ecb->dte_epid; + /* + * When the speculation is committed, all of + * the records in the speculative buffer will + * have their timestamps set to the commit + * time. Until then, it is set to a sentinel + * value, for debugability. + */ + DTRACE_RECORD_STORE_TIMESTAMP(dtrh, UINT64_MAX); continue; + } case DTRACEACT_CHILL: if (dtrace_priv_kernel_destructive(state)) @@ -9369,9 +9426,9 @@ dtrace_ecb_add(dtrace_state_t *state, dt /* * The default size is the size of the default action: recording - * the epid. + * the header. */ - ecb->dte_size = ecb->dte_needed = sizeof (dtrace_epid_t); + ecb->dte_size = ecb->dte_needed = sizeof (dtrace_rechdr_t); ecb->dte_alignment = sizeof (dtrace_epid_t); epid = state->dts_epid++; @@ -9470,122 +9527,89 @@ dtrace_ecb_enable(dtrace_ecb_t *ecb) static void dtrace_ecb_resize(dtrace_ecb_t *ecb) { - uint32_t maxalign = sizeof (dtrace_epid_t); - uint32_t align = sizeof (uint8_t), offs, diff; dtrace_action_t *act; - int wastuple = 0; + uint32_t curneeded = UINT32_MAX; uint32_t aggbase = UINT32_MAX; - dtrace_state_t *state = ecb->dte_state; /* - * If we record anything, we always record the epid. (And we always - * record it first.) + * If we record anything, we always record the dtrace_rechdr_t. (And + * we always record it first.) */ - offs = sizeof (dtrace_epid_t); - ecb->dte_size = ecb->dte_needed = sizeof (dtrace_epid_t); + ecb->dte_size = sizeof (dtrace_rechdr_t); + ecb->dte_alignment = sizeof (dtrace_epid_t); for (act = ecb->dte_action; act != NULL; act = act->dta_next) { dtrace_recdesc_t *rec = &act->dta_rec; + ASSERT(rec->dtrd_size > 0 || rec->dtrd_alignment == 1); - if ((align = rec->dtrd_alignment) > maxalign) - maxalign = align; - - if (!wastuple && act->dta_intuple) { - /* - * This is the first record in a tuple. Align the - * offset to be at offset 4 in an 8-byte aligned - * block. - */ - diff = offs + sizeof (dtrace_aggid_t); - - if (diff = (diff & (sizeof (uint64_t) - 1))) - offs += sizeof (uint64_t) - diff; - - aggbase = offs - sizeof (dtrace_aggid_t); - ASSERT(!(aggbase & (sizeof (uint64_t) - 1))); - } - - /*LINTED*/ - if (rec->dtrd_size != 0 && (diff = (offs & (align - 1)))) { - /* - * The current offset is not properly aligned; align it. - */ - offs += align - diff; - } - - rec->dtrd_offset = offs; - - if (offs + rec->dtrd_size > ecb->dte_needed) { - ecb->dte_needed = offs + rec->dtrd_size; - - if (ecb->dte_needed > state->dts_needed) - state->dts_needed = ecb->dte_needed; - } + ecb->dte_alignment = MAX(ecb->dte_alignment, + rec->dtrd_alignment); if (DTRACEACT_ISAGG(act->dta_kind)) { dtrace_aggregation_t *agg = (dtrace_aggregation_t *)act; - dtrace_action_t *first = agg->dtag_first, *prev; - ASSERT(rec->dtrd_size != 0 && first != NULL); - ASSERT(wastuple); + ASSERT(rec->dtrd_size != 0); + ASSERT(agg->dtag_first != NULL); + ASSERT(act->dta_prev->dta_intuple); ASSERT(aggbase != UINT32_MAX); + ASSERT(curneeded != UINT32_MAX); agg->dtag_base = aggbase; - while ((prev = first->dta_prev) != NULL && - DTRACEACT_ISAGG(prev->dta_kind)) { - agg = (dtrace_aggregation_t *)prev; - first = agg->dtag_first; - } - - if (prev != NULL) { - offs = prev->dta_rec.dtrd_offset + - prev->dta_rec.dtrd_size; - } else { - offs = sizeof (dtrace_epid_t); - } - wastuple = 0; + curneeded = P2ROUNDUP(curneeded, rec->dtrd_alignment); + rec->dtrd_offset = curneeded; + curneeded += rec->dtrd_size; + ecb->dte_needed = MAX(ecb->dte_needed, curneeded); + + aggbase = UINT32_MAX; + curneeded = UINT32_MAX; + } else if (act->dta_intuple) { + if (curneeded == UINT32_MAX) { + /* + * This is the first record in a tuple. Align + * curneeded to be at offset 4 in an 8-byte + * aligned block. + */ + ASSERT(act->dta_prev == NULL || + !act->dta_prev->dta_intuple); + ASSERT3U(aggbase, ==, UINT32_MAX); + curneeded = P2PHASEUP(ecb->dte_size, + sizeof (uint64_t), sizeof (dtrace_aggid_t)); + + aggbase = curneeded - sizeof (dtrace_aggid_t); + ASSERT(IS_P2ALIGNED(aggbase, + sizeof (uint64_t))); + } + curneeded = P2ROUNDUP(curneeded, rec->dtrd_alignment); + rec->dtrd_offset = curneeded; + curneeded += rec->dtrd_size; } else { - if (!act->dta_intuple) - ecb->dte_size = offs + rec->dtrd_size; - - offs += rec->dtrd_size; + /* tuples must be followed by an aggregation */ + ASSERT(act->dta_prev == NULL || + !act->dta_prev->dta_intuple); + + ecb->dte_size = P2ROUNDUP(ecb->dte_size, + rec->dtrd_alignment); + rec->dtrd_offset = ecb->dte_size; + ecb->dte_size += rec->dtrd_size; + ecb->dte_needed = MAX(ecb->dte_needed, ecb->dte_size); } - - wastuple = act->dta_intuple; } if ((act = ecb->dte_action) != NULL && !(act->dta_kind == DTRACEACT_SPECULATE && act->dta_next == NULL) && - ecb->dte_size == sizeof (dtrace_epid_t)) { + ecb->dte_size == sizeof (dtrace_rechdr_t)) { /* - * If the size is still sizeof (dtrace_epid_t), then all + * If the size is still sizeof (dtrace_rechdr_t), then all * actions store no data; set the size to 0. */ - ecb->dte_alignment = maxalign; ecb->dte_size = 0; - - /* - * If the needed space is still sizeof (dtrace_epid_t), then - * all actions need no additional space; set the needed - * size to 0. - */ - if (ecb->dte_needed == sizeof (dtrace_epid_t)) - ecb->dte_needed = 0; - - return; } - /* - * Set our alignment, and make sure that the dte_size and dte_needed - * are aligned to the size of an EPID. - */ - ecb->dte_alignment = maxalign; - ecb->dte_size = (ecb->dte_size + (sizeof (dtrace_epid_t) - 1)) & - ~(sizeof (dtrace_epid_t) - 1); - ecb->dte_needed = (ecb->dte_needed + (sizeof (dtrace_epid_t) - 1)) & - ~(sizeof (dtrace_epid_t) - 1); - ASSERT(ecb->dte_size <= ecb->dte_needed); + ecb->dte_size = P2ROUNDUP(ecb->dte_size, sizeof (dtrace_epid_t)); + ecb->dte_needed = P2ROUNDUP(ecb->dte_needed, (sizeof (dtrace_epid_t))); + ecb->dte_state->dts_needed = MAX(ecb->dte_state->dts_needed, + ecb->dte_needed); } static dtrace_action_t * @@ -9955,7 +9979,7 @@ dtrace_ecb_action_add(dtrace_ecb_t *ecb, break; case DTRACEACT_SPECULATE: - if (ecb->dte_size > sizeof (dtrace_epid_t)) + if (ecb->dte_size > sizeof (dtrace_rechdr_t)) return (EINVAL); if (dp == NULL) @@ -10068,7 +10092,7 @@ dtrace_ecb_action_remove(dtrace_ecb_t *e ecb->dte_action = NULL; ecb->dte_action_last = NULL; - ecb->dte_size = sizeof (dtrace_epid_t); + ecb->dte_size = 0; } static void @@ -10339,12 +10363,13 @@ dtrace_buffer_switch(dtrace_buffer_t *bu caddr_t tomax = buf->dtb_tomax; caddr_t xamot = buf->dtb_xamot; dtrace_icookie_t cookie; - hrtime_t now = dtrace_gethrtime(); + hrtime_t now; ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH)); ASSERT(!(buf->dtb_flags & DTRACEBUF_RING)); cookie = dtrace_interrupt_disable(); + now = dtrace_gethrtime(); buf->dtb_tomax = xamot; buf->dtb_xamot = tomax; buf->dtb_xamot_drops = buf->dtb_drops; @@ -10639,7 +10664,7 @@ dtrace_buffer_reserve(dtrace_buffer_t *b if (epid == DTRACE_EPIDNONE) { size = sizeof (uint32_t); } else { - ASSERT(epid <= state->dts_necbs); + ASSERT3U(epid, <=, state->dts_necbs); ASSERT(state->dts_ecbs[epid - 1] != NULL); size = state->dts_ecbs[epid - 1]->dte_size; @@ -15623,6 +15648,7 @@ dtrace_ioctl(dev_t dev, int cmd, intptr_ desc.dtbd_drops = buf->dtb_drops; desc.dtbd_errors = buf->dtb_errors; desc.dtbd_oldest = buf->dtb_xamot_offset; + desc.dtbd_timestamp = dtrace_gethrtime(); mutex_exit(&dtrace_lock); @@ -15675,6 +15701,7 @@ dtrace_ioctl(dev_t dev, int cmd, intptr_ desc.dtbd_drops = buf->dtb_xamot_drops; desc.dtbd_errors = buf->dtb_xamot_errors; desc.dtbd_oldest = 0; + desc.dtbd_timestamp = buf->dtb_switched; mutex_exit(&dtrace_lock); Modified: vendor-sys/illumos/dist/uts/common/sys/dtrace.h ============================================================================== --- vendor-sys/illumos/dist/uts/common/sys/dtrace.h Sun Aug 19 09:20:22 2012 (r239385) +++ vendor-sys/illumos/dist/uts/common/sys/dtrace.h Sun Aug 19 09:21:20 2012 (r239386) @@ -26,6 +26,7 @@ /* * Copyright (c) 2011, Joyent, Inc. All rights reserved. + * Copyright (c) 2012 by Delphix. All rights reserved. */ #ifndef _SYS_DTRACE_H @@ -919,10 +920,10 @@ typedef struct dtrace_ecbdesc { * DTrace Metadata Description Structures * * DTrace separates the trace data stream from the metadata stream. The only - * metadata tokens placed in the data stream are enabled probe identifiers - * (EPIDs) or (in the case of aggregations) aggregation identifiers. In order - * to determine the structure of the data, DTrace consumers pass the token to - * the kernel, and receive in return a corresponding description of the enabled + * metadata tokens placed in the data stream are the dtrace_rechdr_t (EPID + + * timestamp) or (in the case of aggregations) aggregation identifiers. To + * determine the structure of the data, DTrace consumers pass the token to the + * kernel, and receive in return a corresponding description of the enabled * probe (via the dtrace_eprobedesc structure) or the aggregation (via the * dtrace_aggdesc structure). Both of these structures are expressed in terms * of record descriptions (via the dtrace_recdesc structure) that describe the @@ -1017,7 +1018,8 @@ typedef struct dtrace_fmtdesc { #define DTRACEOPT_AGGSORTREV 24 /* reverse-sort aggregations */ #define DTRACEOPT_AGGSORTPOS 25 /* agg. position to sort on */ #define DTRACEOPT_AGGSORTKEYPOS 26 /* agg. key position to sort on */ -#define DTRACEOPT_MAX 27 /* number of options */ +#define DTRACEOPT_TEMPORAL 27 /* temporally ordered output */ +#define DTRACEOPT_MAX 28 /* number of options */ #define DTRACEOPT_UNSET (dtrace_optval_t)-2 /* unset option */ @@ -1037,7 +1039,9 @@ typedef struct dtrace_fmtdesc { * where user-level wishes the kernel to snapshot the buffer to (the * dtbd_data field). The kernel uses the same structure to pass back some * information regarding the buffer: the size of data actually copied out, the - * number of drops, the number of errors, and the offset of the oldest record. + * number of drops, the number of errors, the offset of the oldest record, + * and the time of the snapshot. + * * If the buffer policy is a "switch" policy, taking a snapshot of the * principal buffer has the additional effect of switching the active and * inactive buffers. Taking a snapshot of the aggregation buffer _always_ has @@ -1050,9 +1054,30 @@ typedef struct dtrace_bufdesc { uint64_t dtbd_drops; /* number of drops */ DTRACE_PTR(char, dtbd_data); /* data */ uint64_t dtbd_oldest; /* offset of oldest record */ + uint64_t dtbd_timestamp; /* hrtime of snapshot */ } dtrace_bufdesc_t; /* + * Each record in the buffer (dtbd_data) begins with a header that includes + * the epid and a timestamp. The timestamp is split into two 4-byte parts + * so that we do not require 8-byte alignment. + */ +typedef struct dtrace_rechdr { + dtrace_epid_t dtrh_epid; /* enabled probe id */ + uint32_t dtrh_timestamp_hi; /* high bits of hrtime_t */ + uint32_t dtrh_timestamp_lo; /* low bits of hrtime_t */ +} dtrace_rechdr_t; + +#define DTRACE_RECORD_LOAD_TIMESTAMP(dtrh) \ + ((dtrh)->dtrh_timestamp_lo + \ + ((uint64_t)(dtrh)->dtrh_timestamp_hi << 32)) + +#define DTRACE_RECORD_STORE_TIMESTAMP(dtrh, hrtime) { \ + (dtrh)->dtrh_timestamp_lo = (uint32_t)hrtime; \ + (dtrh)->dtrh_timestamp_hi = hrtime >> 32; \ +} + +/* * DTrace Status * * The status of DTrace is relayed via the dtrace_status structure. This Modified: vendor-sys/illumos/dist/uts/common/sys/dtrace_impl.h ============================================================================== --- vendor-sys/illumos/dist/uts/common/sys/dtrace_impl.h Sun Aug 19 09:20:22 2012 (r239385) +++ vendor-sys/illumos/dist/uts/common/sys/dtrace_impl.h Sun Aug 19 09:21:20 2012 (r239386) @@ -26,6 +26,7 @@ /* * Copyright (c) 2011, Joyent, Inc. All rights reserved. + * Copyright (c) 2012 by Delphix. All rights reserved. */ #ifndef _SYS_DTRACE_IMPL_H @@ -199,15 +200,18 @@ typedef struct dtrace_hash { * predicate is non-NULL, the DIF object is executed. If the result is * non-zero, the action list is processed, with each action being executed * accordingly. When the action list has been completely executed, processing - * advances to the next ECB. processing advances to the next ECB. If the - * result is non-zero; For each ECB, it first determines the The ECB - * abstraction allows disjoint consumers to multiplex on single probes. + * advances to the next ECB. The ECB abstraction allows disjoint consumers + * to multiplex on single probes. + * + * Execution of the ECB results in consuming dte_size bytes in the buffer + * to record data. During execution, dte_needed bytes must be available in + * the buffer. This space is used for both recorded data and tuple data. */ struct dtrace_ecb { dtrace_epid_t dte_epid; /* enabled probe ID */ uint32_t dte_alignment; /* required alignment */ - size_t dte_needed; /* bytes needed */ - size_t dte_size; /* total size of payload */ + size_t dte_needed; /* space needed for execution */ + size_t dte_size; /* size of recorded payload */ dtrace_predicate_t *dte_predicate; /* predicate, if any */ dtrace_action_t *dte_action; /* actions, if any */ dtrace_ecb_t *dte_next; /* next ECB on probe */ @@ -265,27 +269,30 @@ typedef struct dtrace_aggregation { * the EPID, the consumer can determine the data layout. (The data buffer * layout is shown schematically below.) By assuring that one can determine * data layout from the EPID, the metadata stream can be separated from the - * data stream -- simplifying the data stream enormously. - * - * base of data buffer ---> +------+--------------------+------+ - * | EPID | data | EPID | - * +------+--------+------+----+------+ - * | data | EPID | data | - * +---------------+------+-----------+ - * | data, cont. | - * +------+--------------------+------+ - * | EPID | data | | - * +------+--------------------+ | - * | || | - * | || | - * | \/ | - * : : - * . . - * . . - * . . - * : : - * | | - * limit of data buffer ---> +----------------------------------+ + * data stream -- simplifying the data stream enormously. The ECB always + * proceeds the recorded data as part of the dtrace_rechdr_t structure that + * includes the EPID and a high-resolution timestamp used for output ordering + * consistency. + * + * base of data buffer ---> +--------+--------------------+--------+ + * | rechdr | data | rechdr | + * +--------+------+--------+----+--------+ + * | data | rechdr | data | + * +---------------+--------+-------------+ + * | data, cont. | + * +--------+--------------------+--------+ + * | rechdr | data | | + * +--------+--------------------+ | + * | || | + * | || | + * | \/ | + * : : + * . . + * . . + * . . + * : : + * | | + * limit of data buffer ---> +--------------------------------------+ * * When evaluating an ECB, dtrace_probe() determines if the ECB's needs of the * principal buffer (both scratch and payload) exceed the available space. If