1*434283fdSDevin Teske# -*- tab-width: 4 -*- ;; Emacs 2*434283fdSDevin Teske# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM 3*434283fdSDevin Teske############################################################ IDENT(1) 4*434283fdSDevin Teske# 5*434283fdSDevin Teske# $Title: dwatch(8) module for malloc(9) type activity $ 6*434283fdSDevin Teske# $Copyright: 2026 Devin Teske. All rights reserved. $ 7*434283fdSDevin Teske# 8*434283fdSDevin Teske############################################################ DESCRIPTION 9*434283fdSDevin Teske# 10*434283fdSDevin Teske# Print kernel malloc(9) and free(9) activity by malloc type, via the 11*434283fdSDevin Teske# dtmalloc provider (one malloc and one free probe per type; see 12*434283fdSDevin Teske# vmstat -m for the types). The default profile prints allocations and 13*434283fdSDevin Teske# frees meeting a size threshold (default 65536 bytes; tunable via 14*434283fdSDevin Teske# DWATCH_MALLOC_MIN in the environment, 0 to show everything), 15*434283fdSDevin Teske# answering "who is allocating huge kernel buffers?" Use 16*434283fdSDevin Teske# dtmalloc-NAME to watch a single type (e.g., dtmalloc-devbuf). 17*434283fdSDevin Teske# The dtmalloc-top profile maintains a running catalog, updated every 18*434283fdSDevin Teske# 3 seconds, of net bytes and outstanding allocation balance by type: 19*434283fdSDevin Teske# a type whose net bytes climb without bound while the system is in 20*434283fdSDevin Teske# steady state is a leak suspect. NB: the catalog reflects activity 21*434283fdSDevin Teske# since the watch began, not preexisting allocations, and caches 22*434283fdSDevin Teske# legitimately hold what they allocate. 23*434283fdSDevin Teske# 24*434283fdSDevin Teske############################################################ PRAGMAS 25*434283fdSDevin Teske 26*434283fdSDevin Teskecase "$PROFILE" in 27*434283fdSDevin Teskedtmalloc-top) 28*434283fdSDevin Teske DTRACE_PRAGMA=" 29*434283fdSDevin Teske option quiet 30*434283fdSDevin Teske option aggsortrev 31*434283fdSDevin Teske " # END-QUOTE 32*434283fdSDevin Teske ;; 33*434283fdSDevin Teskeesac 34*434283fdSDevin Teske 35*434283fdSDevin Teske############################################################ PROBE 36*434283fdSDevin Teske 37*434283fdSDevin Teskecase "$PROFILE" in 38*434283fdSDevin Teskedtmalloc) 39*434283fdSDevin Teske : ${PROBE:=dtmalloc:::malloc, dtmalloc:::free} ;; 40*434283fdSDevin Teskedtmalloc-top) 41*434283fdSDevin Teske : ${PROBE:=profile:::tick-3s} ;; 42*434283fdSDevin Teske*) 43*434283fdSDevin Teske : ${PROBE:=$( echo \ 44*434283fdSDevin Teske dtmalloc::${PROFILE#dtmalloc-}:malloc, \ 45*434283fdSDevin Teske dtmalloc::${PROFILE#dtmalloc-}:free )} ;; 46*434283fdSDevin Teskeesac 47*434283fdSDevin Teske 48*434283fdSDevin Teske############################################################ EVENT ACTION 49*434283fdSDevin Teske 50*434283fdSDevin Teske: ${DWATCH_MALLOC_MIN:=65536} 51*434283fdSDevin Teske 52*434283fdSDevin Teskecase "$DWATCH_MALLOC_MIN" in 53*434283fdSDevin Teske""|*[!0-9]*) die "DWATCH_MALLOC_MIN must be a number" ;; # NOTREACHED 54*434283fdSDevin Teskeesac 55*434283fdSDevin Teske 56*434283fdSDevin Teske[ "$CUSTOM_TEST" ] || case "$PROFILE" in 57*434283fdSDevin Teskedtmalloc-top) ;; 58*434283fdSDevin Teske*) EVENT_TEST="(uint64_t)arg3 >= $DWATCH_MALLOC_MIN" 59*434283fdSDevin Teskeesac 60*434283fdSDevin Teske 61*434283fdSDevin Teske############################################################ ACTIONS 62*434283fdSDevin Teske 63*434283fdSDevin Teskeif [ "$PROFILE" = "dtmalloc-top" ]; then 64*434283fdSDevin Teskeexec 9<<EOF 65*434283fdSDevin Teskethis int64_t dtmalloc_delta; 66*434283fdSDevin Teske 67*434283fdSDevin TeskeBEGIN { printf("Cataloging malloc(9) activity ...") } /* probe ID $ID */ 68*434283fdSDevin Teske 69*434283fdSDevin Teskedtmalloc:::malloc /* probe ID $(( $ID + 1 )) */ 70*434283fdSDevin Teske{ 71*434283fdSDevin Teske this->dtmalloc_delta = (int64_t)arg3; 72*434283fdSDevin Teske} 73*434283fdSDevin Teske 74*434283fdSDevin Teskedtmalloc:::free /* probe ID $(( $ID + 2 )) */ 75*434283fdSDevin Teske{ 76*434283fdSDevin Teske this->dtmalloc_delta = -(int64_t)arg3; 77*434283fdSDevin Teske} 78*434283fdSDevin Teske 79*434283fdSDevin Teskedtmalloc:::malloc, dtmalloc:::free /* probe ID $(( $ID + 3 )) */ 80*434283fdSDevin Teske{ 81*434283fdSDevin Teske @dtmalloc_bytes[probefunc] = sum(this->dtmalloc_delta); 82*434283fdSDevin Teske @dtmalloc_allocs[probefunc] = 83*434283fdSDevin Teske sum(this->dtmalloc_delta > 0 ? 1 : -1); 84*434283fdSDevin Teske} 85*434283fdSDevin TeskeEOF 86*434283fdSDevin TeskeACTIONS=$( cat <&9 ) 87*434283fdSDevin TeskeID=$(( $ID + 4 )) 88*434283fdSDevin Teskefi 89*434283fdSDevin Teske 90*434283fdSDevin Teske############################################################ EVENT TAG 91*434283fdSDevin Teske 92*434283fdSDevin Teske# For the running catalog, override the default `UID.GID CMD[PID]: ' tag 93*434283fdSDevin Teske# with ANSI cursor-homing and screen-clearing codes plus column headers. 94*434283fdSDevin Teske 95*434283fdSDevin Teskeif [ "$PROFILE" = "dtmalloc-top" ]; then 96*434283fdSDevin Teskesize=$( stty size 2> /dev/null ) 97*434283fdSDevin Teskerows="${size%% *}" 98*434283fdSDevin Teskecols="${size#* }" 99*434283fdSDevin Teske 100*434283fdSDevin Teskeexec 9<<EOF 101*434283fdSDevin Teske printf("\033[H"); /* Position the cursor at top-left */ 102*434283fdSDevin Teske printf("\033[J"); /* Clear display from cursor to end */ 103*434283fdSDevin Teske 104*434283fdSDevin Teske /* Header line containing probe (left) and date (right) */ 105*434283fdSDevin Teske printf("%-*s%s%Y%s\n", 106*434283fdSDevin Teske $(( ${cols:-80} - 20 )), "$PROBE", 107*434283fdSDevin Teske console ? "\033[32m" : "", 108*434283fdSDevin Teske walltimestamp, 109*434283fdSDevin Teske console ? "\033[39m" : ""); 110*434283fdSDevin Teske 111*434283fdSDevin Teske /* Column headers */ 112*434283fdSDevin Teske printf("%s%14s %10s %s%s\n", 113*434283fdSDevin Teske console ? "\033[1m" : "", 114*434283fdSDevin Teske "NET(B)", 115*434283fdSDevin Teske "BALANCE", 116*434283fdSDevin Teske "TYPE", 117*434283fdSDevin Teske console ? "\033[22m" : ""); 118*434283fdSDevin TeskeEOF 119*434283fdSDevin TeskeEVENT_TAG=$( cat <&9 ) 120*434283fdSDevin Teskefi 121*434283fdSDevin Teske 122*434283fdSDevin Teske############################################################ EVENT DETAILS 123*434283fdSDevin Teske 124*434283fdSDevin Teskeif [ "$PROFILE" = "dtmalloc-top" ]; then 125*434283fdSDevin Teskeexec 9<<EOF 126*434283fdSDevin Teske /* NB: Cumulative; not truncated between updates */ 127*434283fdSDevin Teske printa("%@14d %@10d %s\n", @dtmalloc_bytes, @dtmalloc_allocs); 128*434283fdSDevin TeskeEOF 129*434283fdSDevin TeskeEVENT_DETAILS=$( cat <&9 ) 130*434283fdSDevin Teskeelif [ ! "$CUSTOM_DETAILS" ]; then 131*434283fdSDevin Teskeexec 9<<EOF 132*434283fdSDevin Teske /* 133*434283fdSDevin Teske * Print malloc(9) activity details 134*434283fdSDevin Teske */ 135*434283fdSDevin Teske printf("%s(9) type %s %d byte%s", 136*434283fdSDevin Teske probename, 137*434283fdSDevin Teske probefunc, 138*434283fdSDevin Teske (long)arg3, 139*434283fdSDevin Teske (long)arg3 == 1 ? "" : "s"); 140*434283fdSDevin TeskeEOF 141*434283fdSDevin TeskeEVENT_DETAILS=$( cat <&9 ) 142*434283fdSDevin Teskefi 143*434283fdSDevin Teske 144*434283fdSDevin Teske################################################################################ 145*434283fdSDevin Teske# END 146*434283fdSDevin Teske################################################################################ 147