Page MenuHomeFreeBSD

D60563.id189318.diff
No OneTemporary

D60563.id189318.diff

diff --git a/share/man/man4/hwpmc.4 b/share/man/man4/hwpmc.4
--- a/share/man/man4/hwpmc.4
+++ b/share/man/man4/hwpmc.4
@@ -28,7 +28,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd July 8, 2023
+.Dd October 9, 2026
.Dt HWPMC 4
.Os
.Sh NAME
@@ -428,7 +428,10 @@
.Bl -tag -width indent
.It Va kern.hwpmc.callchaindepth Pq integer, read-only
The maximum number of call chain records to capture per sample.
-The default is 8.
+The default is 128.
+Each sample slot must also hold the largest multipart payload
+(an 8-byte header, 10 64-bit words and one program counter), so smaller
+values are raised to 12 on 64-bit platforms and 23 on 32-bit platforms.
.It Va kern.hwpmc.debugflags Pq string, read-write
(Only available if the
.Nm
diff --git a/sys/dev/hwpmc/hwpmc_logging.c b/sys/dev/hwpmc/hwpmc_logging.c
--- a/sys/dev/hwpmc/hwpmc_logging.c
+++ b/sys/dev/hwpmc/hwpmc_logging.c
@@ -121,6 +121,7 @@
} \
ph = (struct pmclog_header *)_le; \
ph->pl_header =_PMCLOG_TO_HEADER(TYPE,_len); \
+ ph->pl_spare = 0; \
ph->pl_tsc = (TSC); \
_le += sizeof(*ph) / 4 /* skip over timestamp */
@@ -139,6 +140,7 @@
} \
ph = (struct pmclog_header *)_le; \
ph->pl_header =_PMCLOG_TO_HEADER(TYPE,_len); \
+ ph->pl_spare = 0; \
ph->pl_tsc = tsc; \
_le += sizeof(*ph) / 4 /* skip over timestamp */
diff --git a/sys/dev/hwpmc/hwpmc_mod.c b/sys/dev/hwpmc/hwpmc_mod.c
--- a/sys/dev/hwpmc/hwpmc_mod.c
+++ b/sys/dev/hwpmc/hwpmc_mod.c
@@ -319,6 +319,12 @@
&pmc_stats.pm_overwrites,
"# of times a sample was overwritten before being logged");
+/* Every sample slot must fit the multipart header, payload, and one PC. */
+#define PMC_CALLCHAIN_DEPTH_MIN \
+ ((int)PMC_MULTIPART_SAMPLE_MIN_WORDS_FOR(sizeof(uintptr_t)))
+CTASSERT(PMC_MULTIPART_MAX_PAYLOAD64 ==
+ nitems(((struct pmc_multipart *)0)->pl_mpdata));
+
static int pmc_callchaindepth = PMC_CALLCHAIN_DEPTH;
SYSCTL_INT(_kern_hwpmc, OID_AUTO, callchaindepth, CTLFLAG_RDTUN,
&pmc_callchaindepth, 0,
@@ -4781,6 +4787,7 @@
MPASS(ps->ps_pc != NULL);
MPASS(ps->ps_nsamples_actual != 0);
+ MPASS(length >= 0 && length <= UCHAR_MAX);
hdr = (uint8_t *)ps->ps_pc;
@@ -4799,18 +4806,19 @@
static void
pmc_multipart_copydata(struct pmc_sample *ps, struct pmc_multipart *mp)
{
- int i, scale;
- uint64_t *ps_pc;
+ int scale;
MPASS(ps->ps_pc != NULL);
MPASS(ps->ps_nsamples_actual != 0);
-
- ps_pc = (uint64_t *)ps->ps_pc;
-
- for (i = 0; i < mp->pl_length; i++)
- ps_pc[i + 1] = mp->pl_mpdata[i];
+ MPASS(mp->pl_length >= 0 && mp->pl_length <=
+ PMC_MULTIPART_MAX_PAYLOAD64);
scale = sizeof(uint64_t) / sizeof(uintptr_t);
+ MPASS(ps->ps_nsamples_actual + scale * mp->pl_length <=
+ pmc_callchaindepth);
+ memcpy(ps->ps_pc + ps->ps_nsamples_actual, mp->pl_mpdata,
+ mp->pl_length * sizeof(uint64_t));
+
pmc_multipart_add(ps, mp->pl_type, scale * mp->pl_length);
}
@@ -4877,15 +4885,18 @@
MPASS(ps->ps_pc != NULL);
if (mp != NULL) {
- /* Set multipart flag, clear header and copy data */
+ /* Set multipart flag, clear the full header, and copy data. */
ps->ps_flags |= PMC_CC_F_MULTIPART;
- ps->ps_pc[0] = 0;
- ps->ps_nsamples_actual = 1;
+ bzero(ps->ps_pc, PMC_MULTIPART_HEADER_LENGTH);
+ ps->ps_nsamples_actual = PMC_MULTIPART_HEADER_WORDS_FOR(
+ sizeof(uintptr_t));
pmc_multipart_copydata(ps, mp);
}
if (callchaindepth == 1) {
ps->ps_pc[ps->ps_nsamples_actual] = PMC_TRAPFRAME_TO_PC(tf);
+ /* Count any multipart words or they are lost at logging. */
+ callchaindepth += ps->ps_nsamples_actual;
} else {
/*
* Kernel stack traversals can be done immediately, while we
@@ -5015,9 +5026,9 @@
* Retrieve the callchain and mark the sample buffer
* as 'processable' by the timer tick sweep code.
*/
- if (__predict_true(nsamples < pmc_callchaindepth - 1))
+ if (__predict_true(nsamples < pmc_callchaindepth))
nsamples += pmc_save_user_callchain(ps->ps_pc + nsamples,
- pmc_callchaindepth - nsamples - 1, tf);
+ pmc_callchaindepth - nsamples, tf);
/*
* We have to prevent hardclock from potentially overwriting
@@ -5145,8 +5156,17 @@
*/
if (pm->pm_flags & PMC_F_ATTACHED_TO_OWNER) {
if (ps->ps_flags & PMC_CC_F_USERSPACE) {
+ uint32_t pcidx;
+
+ pcidx = 0;
+ if ((ps->ps_flags & PMC_CC_F_MULTIPART) != 0 &&
+ !pmclog_multipart_callchain_offset(
+ ps->ps_pc, ps->ps_nsamples, &pcidx))
+ goto entrydone;
+ if (pcidx >= ps->ps_nsamples)
+ goto entrydone;
td = FIRST_THREAD_IN_PROC(po->po_owner);
- addupc_intr(td, ps->ps_pc[0], 1);
+ addupc_intr(td, ps->ps_pc[pcidx], 1);
}
} else
pmclog_process_callchain(pm, ps);
@@ -5728,6 +5748,12 @@
PMC_CALLCHAIN_DEPTH_MAX);
pmc_callchaindepth = PMC_CALLCHAIN_DEPTH_MAX;
}
+ if (pmc_callchaindepth < PMC_CALLCHAIN_DEPTH_MIN) {
+ printf("hwpmc: tunable \"callchaindepth\"=%d too small for "
+ "multipart samples - using %d.\n", pmc_callchaindepth,
+ PMC_CALLCHAIN_DEPTH_MIN);
+ pmc_callchaindepth = PMC_CALLCHAIN_DEPTH_MIN;
+ }
md = pmc_md_initialize();
if (md == NULL) {
diff --git a/sys/sys/pmclog.h b/sys/sys/pmclog.h
--- a/sys/sys/pmclog.h
+++ b/sys/sys/pmclog.h
@@ -127,18 +127,77 @@
/*
* If the multipart flag is set, then pl_pc contains multiple data types. The
- * first 8 bytes is a header made up of a 1 byte type and 1 byte length that
- * describes the use of the remaining pl_pc array.
+ * first 8 bytes are a header made up of four 1 byte type and 1 byte length
+ * tuples that describe the use of the remaining pl_pc array. Lengths are
+ * counted in native pointer-sized pl_pc entries; 64-bit payload values consume
+ * two entries on 32-bit platforms.
*/
#define PMC_MULTIPART_HEADER_LENGTH 8
#define PMC_MULTIPART_HEADER_ENTRIES 4
+#define PMC_MULTIPART_HEADER_WORDS_FOR(SZ) \
+ (PMC_MULTIPART_HEADER_LENGTH / (SZ))
+#define PMC_MULTIPART_64BIT_WORDS_FOR(N, SZ) \
+ ((N) * (sizeof(uint64_t) / (SZ)))
+#define PMC_MULTIPART_HEADER_WORDS \
+ PMC_MULTIPART_HEADER_WORDS_FOR(sizeof(uintfptr_t))
+#define PMC_MULTIPART_PAYLOAD_WORDS(N) \
+ PMC_MULTIPART_64BIT_WORDS_FOR((N), sizeof(uintfptr_t))
+#define PMC_MULTIPART_MAX_PAYLOAD64 10
+#define PMC_MULTIPART_SAMPLE_MIN_WORDS_FOR(SZ) \
+ (PMC_MULTIPART_HEADER_WORDS_FOR(SZ) + \
+ PMC_MULTIPART_64BIT_WORDS_FOR(PMC_MULTIPART_MAX_PAYLOAD64, \
+ (SZ)) + 1)
+#define PMC_MULTIPART_SAMPLE_MIN_WORDS \
+ PMC_MULTIPART_SAMPLE_MIN_WORDS_FOR(sizeof(uintfptr_t))
#define PMC_CC_MULTIPART_NONE 0
#define PMC_CC_MULTIPART_CALLCHAIN 1
#define PMC_CC_MULTIPART_IBS_FETCH 2
#define PMC_CC_MULTIPART_IBS_OP 3
+static __inline int
+pmclog_multipart_callchain_offset(const void *pc, uint32_t npc,
+ uint32_t *offsetp)
+{
+ const uint8_t *hdr;
+ uint32_t i, len, offset, type;
+
+ if (offsetp != NULL)
+ *offsetp = 0;
+ if (pc == NULL || npc < PMC_MULTIPART_HEADER_WORDS)
+ return (0);
+
+ hdr = (const uint8_t *)pc;
+ offset = PMC_MULTIPART_HEADER_WORDS;
+ for (i = 0; i < PMC_MULTIPART_HEADER_ENTRIES; i++) {
+ type = hdr[2 * i];
+ len = hdr[2 * i + 1];
+
+ if (type == PMC_CC_MULTIPART_NONE ||
+ type == PMC_CC_MULTIPART_CALLCHAIN)
+ break;
+ if (len > npc - offset)
+ return (0);
+ offset += len;
+ }
+ if (offset > npc)
+ return (0);
+ if (offsetp != NULL)
+ *offsetp = offset;
+ return (1);
+}
+
+static __inline uint32_t
+pmclog_multipart_offset(const void *pc)
+{
+ uint32_t offset;
+
+ if (!pmclog_multipart_callchain_offset(pc, ~(uint32_t)0, &offset))
+ return (~(uint32_t)0);
+ return (offset);
+}
+
struct pmclog_closelog {
PMCLOG_ENTRY_HEADER
};

File Metadata

Mime Type
text/plain
Expires
Sun, Oct 11, 4:14 AM (15 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40583810
Default Alt Text
D60563.id189318.diff (7 KB)

Event Timeline