I am looking at some malloc issues and am confused by what dtrace is telling me. In a particular core that I captured, I can see the following code path to get me into t_splay: ----------------- lwp# 12 / thread# 12 -------------------- ffffffff7f25f628 t_splay (101a9b6a0, 41, 18, 40, 0, 101a9b6c0) + c8 ffffffff7f25f3d4 t_delete (101a9b6a0, 0, 184c80, ffffffff7f25f8e8, ffffffff7f3e4000, 0) + 60 ffffffff7f25e9e0 _malloc_unlocked (a0, 20, 101a9b6a0, 0, ffffffff7f3eef40, 0) + 1a8 ffffffff7f25e818 malloc (98, 21d8, 185838, ffffffff7f2c364c, ffffffff7f3e4000, 2000) + 5c . . . It seems as though the only way to get to t_splay is through _malloc_unlocked() [1] or t_delete() [2]. 1.http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/gen/malloc.c#259 2.http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/gen/malloc.c#767 What confuses me is: CPU ID FUNCTION:NAME 66 66733 :tick-30sec libc.so.1`t_splay libc.so.1`malloc+0x5c crsd.bin`__1cKRunContextJrunScript6M_v_+0x3c crsd.bin`__1cJrunScript6FrknDstdMbasic_string4Ccn0ALchar_traits4Cc__n0 AJallocator4Cc____rk1ipkc_i_+0x218 crsd.bin`__1cOcaaApplicationNCheckResource6Fpkcpi_i_+0x328 crsd.bin`__1cMRTI_ResourceNCheckResource6M_i_+0x5c crsd.bin`__1cHrunPoll6Fpv_0_+0x1b8 crsd.bin`__1cGPollerDrun6M_v_+0xc crsd.bin`__1cNCAATimerEventPrunDetachedStub6Fpv_v_+0x20 crsd.bin`__1cNCLS_ThreadingNCLSthreadMainIcppStart6Fpv_v_+0x54 libc.so.1`_lwp_start 1 libc.so.1`t_splay libc.so.1`malloc+0x5c libhasgen10.so`crs_qstat_allocresarr+0x54 libhasgen10.so`crs_qstat_get_options+0x88 libhasgen10.so`crs_qstat_ocr+0x94 crsd.bin`crs_qstat_process+0x2bc libc.so.1`_lwp_start 1 libc.so.1`t_splay libc.so.1`malloc+0x5c libc.so.1`calloc+0x44 libocr10.so`lmmstvrt+0x8c libocr10.so`lmmstlrg+0x70 libocr10.so`lmmstmalloc+0x134 libocr10.so`lmmmalloc+0x90 libocr10.so`lmmstvrt+0xe8 libocr10.so`lmmstlrg+0x70 libocr10.so`lmmstmalloc+0x134 libocr10.so`lmmstcalloc+0x34 libocrutl10.so`lmmcalloc+0x164 libocrutl10.so`prou_allocm+0xd8 libocr10.so`proath_procctx_init+0x88 libocr10.so`proath_select_wait_for_request+0x2ec libc.so.1`_lwp_start 1 libc.so.1`t_splay libc.so.1`_malloc_unlocked+0x1a8 libc.so.1`malloc+0x5c 0x239 1 libc.so.1`t_splay libc.so.1`_malloc_unlocked+0x1a8 libc.so.1`malloc+0x5c libCrun.so.1`__1c2n6FL_pv_+0x28 crsd.bin`__1cDstdJallocator4CpnKPermission__Iallocate6MLpv_4_+0x44 (lotsa lines deleted) The script is... #! /usr/sbin/dtrace -s pid$target:libc.so:t_splay: { @splayers[ustack()] = count(); } tick-30sec { printa(@splayers); exit(0); } Why would some of the stacks show: libc.so.1`t_splay libc.so.1`_malloc_unlocked+0x1a8 libc.so.1`malloc+0x5c While others show: libc.so.1`t_splay libc.so.1`malloc+0x5c mdb says that malloc+0x5c should be malloc_unlocked:> libc.so.1`malloc+0x5c::dis -n 1libc.so.1`malloc+0x58: ldx [%i4 + %l4], %o0 libc.so.1`malloc+0x5c: call +0x20 <libc.so.1`_malloc_unlocked> libc.so.1`malloc+0x60: mov %i0, %o0 What am I missing? FWIW, I am running S10U2, 118833-23. Mike
Mike, you''re probably running on sparc HW, and probably running code that has been "tail-call" optimized, in which case if the last thing a function does before exiting is to call another function, then the optimization lets the called function use the callers register window and stack frame space - hence the stack backtrace no longer contains the true caller... This has an overall longterm benefit of less window spill-fill traps which is good for performance. the optimization can be performed differently in different code paths through a function, so if in some code paths malloc_unlocked calls t_splay as its very last operation, that call gets tail-call optimized (the stack backtrace will not show malloc_unlocked), and if on other paths through malloc_unlocked there is additional code to execute after t_splay returns then tail-call-opt cann''t be applied on that path (the stack backtrace will show malloc_unlocked). if you''re not running on sparc then disregard this email! -Pete Lawrence. Gerdts wrote On 02/28/07 04:45 PM,:>I am looking at some malloc issues and am confused by what dtrace is >telling me. In a particular core that I captured, I can see the >following code path to get me into t_splay: > >----------------- lwp# 12 / thread# 12 -------------------- > ffffffff7f25f628 t_splay (101a9b6a0, 41, 18, 40, 0, 101a9b6c0) + c8 > ffffffff7f25f3d4 t_delete (101a9b6a0, 0, 184c80, ffffffff7f25f8e8, >ffffffff7f3e4000, 0) + 60 > ffffffff7f25e9e0 _malloc_unlocked (a0, 20, 101a9b6a0, 0, >ffffffff7f3eef40, 0) + 1a8 > ffffffff7f25e818 malloc (98, 21d8, 185838, ffffffff7f2c364c, >ffffffff7f3e4000, 2000) + 5c >. . . > >It seems as though the only way to get to t_splay is through >_malloc_unlocked() [1] or t_delete() [2]. > >1.http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/gen/malloc.c#259 >2.http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/gen/malloc.c#767 > >What confuses me is: > >CPU ID FUNCTION:NAME > 66 66733 :tick-30sec > > libc.so.1`t_splay > libc.so.1`malloc+0x5c > crsd.bin`__1cKRunContextJrunScript6M_v_+0x3c > crsd.bin`__1cJrunScript6FrknDstdMbasic_string4Ccn0ALchar_traits4Cc__n0 >AJallocator4Cc____rk1ipkc_i_+0x218 > crsd.bin`__1cOcaaApplicationNCheckResource6Fpkcpi_i_+0x328 > crsd.bin`__1cMRTI_ResourceNCheckResource6M_i_+0x5c > crsd.bin`__1cHrunPoll6Fpv_0_+0x1b8 > crsd.bin`__1cGPollerDrun6M_v_+0xc > crsd.bin`__1cNCAATimerEventPrunDetachedStub6Fpv_v_+0x20 > crsd.bin`__1cNCLS_ThreadingNCLSthreadMainIcppStart6Fpv_v_+0x54 > libc.so.1`_lwp_start > 1 > > libc.so.1`t_splay > libc.so.1`malloc+0x5c > libhasgen10.so`crs_qstat_allocresarr+0x54 > libhasgen10.so`crs_qstat_get_options+0x88 > libhasgen10.so`crs_qstat_ocr+0x94 > crsd.bin`crs_qstat_process+0x2bc > libc.so.1`_lwp_start > 1 > > libc.so.1`t_splay > libc.so.1`malloc+0x5c > libc.so.1`calloc+0x44 > libocr10.so`lmmstvrt+0x8c > libocr10.so`lmmstlrg+0x70 > libocr10.so`lmmstmalloc+0x134 > libocr10.so`lmmmalloc+0x90 > libocr10.so`lmmstvrt+0xe8 > libocr10.so`lmmstlrg+0x70 > libocr10.so`lmmstmalloc+0x134 > libocr10.so`lmmstcalloc+0x34 > libocrutl10.so`lmmcalloc+0x164 > libocrutl10.so`prou_allocm+0xd8 > libocr10.so`proath_procctx_init+0x88 > libocr10.so`proath_select_wait_for_request+0x2ec > libc.so.1`_lwp_start > 1 > > libc.so.1`t_splay > libc.so.1`_malloc_unlocked+0x1a8 > libc.so.1`malloc+0x5c > 0x239 > 1 > > libc.so.1`t_splay > libc.so.1`_malloc_unlocked+0x1a8 > libc.so.1`malloc+0x5c > libCrun.so.1`__1c2n6FL_pv_+0x28 > crsd.bin`__1cDstdJallocator4CpnKPermission__Iallocate6MLpv_4_+0x44 > >(lotsa lines deleted) > >The script is... > >#! /usr/sbin/dtrace -s > >pid$target:libc.so:t_splay: >{ > @splayers[ustack()] = count(); >} > >tick-30sec >{ > printa(@splayers); > exit(0); >} > >Why would some of the stacks show: > > libc.so.1`t_splay > libc.so.1`_malloc_unlocked+0x1a8 > libc.so.1`malloc+0x5c > >While others show: > > libc.so.1`t_splay > libc.so.1`malloc+0x5c > >mdb says that malloc+0x5c should be malloc_unlocked: > > > >>libc.so.1`malloc+0x5c::dis -n 1 >> >> >libc.so.1`malloc+0x58: ldx [%i4 + %l4], %o0 >libc.so.1`malloc+0x5c: call +0x20 ><libc.so.1`_malloc_unlocked> >libc.so.1`malloc+0x60: mov %i0, %o0 > > >What am I missing? FWIW, I am running S10U2, 118833-23. > >Mike >_______________________________________________ >dtrace-discuss mailing list >dtrace-discuss at opensolaris.org > >
On 2/28/07, Peter Lawrence <Peter.Lawrence at sun.com> wrote:> Mike, > you''re probably running on sparc HW, and probably running > code that has been "tail-call" optimized, in which case if > the last thing a function does before exiting is to call another > function, then the optimization lets the called function use > the callers register window and stack frame space - hence the > stack backtrace no longer contains the true caller... This has > an overall longterm benefit of less window spill-fill traps which > is good for performance.Yep, running on sparc. While the code I''m looking at has no terribly obvious code paths that do what you suggest, I can see at least one case where optimization could pick this up. Thanks! Mike
It might be a case of dtrace ustack() being tricked by it''s own instrumentation. The script below instruments all instructions of t_splay. t_splay is a ''leaf function'' which means that register %o7 should be included in the ucallstack (points to _malloc_unlocked). But if the heuristic wrongly decides that t_splay is a non-leaf, then it discard %o7 from the stack (_malloc_unlocked is skipped). The disassembly of t_splay on my system seems to indicate that it is a leaf function which falls in the covered dtrace heuristic. However once t_splay is instrumented by the d-script something seems to break.>pid$target:libc.so:t_splay: >{ > @splayers[ustack()] = count(); >} >See dtrace_getpcstack(): http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/sparc/dtrace/dtrace_isa.c You might have better luck by having less instrumentation in t_splay: pid$target:libc.so:t_splay:entry { @splayers[ustack()] = count(); } -r
> What am I missing? FWIW, I am running S10U2, 118833-23.Hey Mike, As Roch mentioned, you probably want to be using the entry probe for t_splay rather than _every_ probe, but the problem you''re seeing is this known bug: 6517308 ustack() results may vary with pid provider entry probes The first call to ustack() will produce slightly different results than the second call namely that the first call will include the correct calling frame whereas the second will skip it. But, you may observe, you''re apparently only invoking ustack() once per probe! Since you''ve enabled _all_ probes, this includes both the entry probe and offset 0 probe which correspond to the same logical tracepoint. While there''s nothing wrong with your script per se, using just the entry probe should resolve your problem. If it doesn''t let me know -- this may be another bug. Adam On Wed, Feb 28, 2007 at 06:45:53PM -0600, Mike Gerdts wrote:> I am looking at some malloc issues and am confused by what dtrace is > telling me. In a particular core that I captured, I can see the > following code path to get me into t_splay: > > ----------------- lwp# 12 / thread# 12 -------------------- > ffffffff7f25f628 t_splay (101a9b6a0, 41, 18, 40, 0, 101a9b6c0) + c8 > ffffffff7f25f3d4 t_delete (101a9b6a0, 0, 184c80, ffffffff7f25f8e8, > ffffffff7f3e4000, 0) + 60 > ffffffff7f25e9e0 _malloc_unlocked (a0, 20, 101a9b6a0, 0, > ffffffff7f3eef40, 0) + 1a8 > ffffffff7f25e818 malloc (98, 21d8, 185838, ffffffff7f2c364c, > ffffffff7f3e4000, 2000) + 5c > . . . > > It seems as though the only way to get to t_splay is through > _malloc_unlocked() [1] or t_delete() [2]. > > 1.http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/gen/malloc.c#259 > 2.http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/gen/malloc.c#767 > > What confuses me is: > > CPU ID FUNCTION:NAME > 66 66733 :tick-30sec > > libc.so.1`t_splay > libc.so.1`malloc+0x5c > crsd.bin`__1cKRunContextJrunScript6M_v_+0x3c > crsd.bin`__1cJrunScript6FrknDstdMbasic_string4Ccn0ALchar_traits4Cc__n0 > AJallocator4Cc____rk1ipkc_i_+0x218 > crsd.bin`__1cOcaaApplicationNCheckResource6Fpkcpi_i_+0x328 > crsd.bin`__1cMRTI_ResourceNCheckResource6M_i_+0x5c > crsd.bin`__1cHrunPoll6Fpv_0_+0x1b8 > crsd.bin`__1cGPollerDrun6M_v_+0xc > crsd.bin`__1cNCAATimerEventPrunDetachedStub6Fpv_v_+0x20 > crsd.bin`__1cNCLS_ThreadingNCLSthreadMainIcppStart6Fpv_v_+0x54 > libc.so.1`_lwp_start > 1 > > libc.so.1`t_splay > libc.so.1`malloc+0x5c > libhasgen10.so`crs_qstat_allocresarr+0x54 > libhasgen10.so`crs_qstat_get_options+0x88 > libhasgen10.so`crs_qstat_ocr+0x94 > crsd.bin`crs_qstat_process+0x2bc > libc.so.1`_lwp_start > 1 > > libc.so.1`t_splay > libc.so.1`malloc+0x5c > libc.so.1`calloc+0x44 > libocr10.so`lmmstvrt+0x8c > libocr10.so`lmmstlrg+0x70 > libocr10.so`lmmstmalloc+0x134 > libocr10.so`lmmmalloc+0x90 > libocr10.so`lmmstvrt+0xe8 > libocr10.so`lmmstlrg+0x70 > libocr10.so`lmmstmalloc+0x134 > libocr10.so`lmmstcalloc+0x34 > libocrutl10.so`lmmcalloc+0x164 > libocrutl10.so`prou_allocm+0xd8 > libocr10.so`proath_procctx_init+0x88 > libocr10.so`proath_select_wait_for_request+0x2ec > libc.so.1`_lwp_start > 1 > > libc.so.1`t_splay > libc.so.1`_malloc_unlocked+0x1a8 > libc.so.1`malloc+0x5c > 0x239 > 1 > > libc.so.1`t_splay > libc.so.1`_malloc_unlocked+0x1a8 > libc.so.1`malloc+0x5c > libCrun.so.1`__1c2n6FL_pv_+0x28 > crsd.bin`__1cDstdJallocator4CpnKPermission__Iallocate6MLpv_4_+0x44 > > (lotsa lines deleted) > > The script is... > > #! /usr/sbin/dtrace -s > > pid$target:libc.so:t_splay: > { > @splayers[ustack()] = count(); > } > > tick-30sec > { > printa(@splayers); > exit(0); > } > > Why would some of the stacks show: > > libc.so.1`t_splay > libc.so.1`_malloc_unlocked+0x1a8 > libc.so.1`malloc+0x5c > > While others show: > > libc.so.1`t_splay > libc.so.1`malloc+0x5c > > mdb says that malloc+0x5c should be malloc_unlocked: > > >libc.so.1`malloc+0x5c::dis -n 1 > libc.so.1`malloc+0x58: ldx [%i4 + %l4], %o0 > libc.so.1`malloc+0x5c: call +0x20 > <libc.so.1`_malloc_unlocked> > libc.so.1`malloc+0x60: mov %i0, %o0 > > > What am I missing? FWIW, I am running S10U2, 118833-23. > > Mike > _______________________________________________ > dtrace-discuss mailing list > dtrace-discuss at opensolaris.org-- Adam Leventhal, Solaris Kernel Development http://blogs.sun.com/ahl
On Thu, Mar 01, 2007 at 05:46:21PM -0800, Adam Leventhal wrote:> As Roch mentioned, you probably want to be using the entry probe for t_splay > rather than _every_ probe, but the problem you''re seeing is this known bug: > > 6517308 ustack() results may vary with pid provider entry probesI neglected to mention that this bug only exists in S10 -- it has been fixed in Solaris Nevada and OpenSolaris. Adam -- Adam Leventhal, Solaris Kernel Development http://blogs.sun.com/ahl
On 3/1/07, Adam Leventhal <ahl at eng.sun.com> wrote:> While there''s nothing wrong with your script per se, using just the entry > probe should resolve your problem. If it doesn''t let me know -- this may be > another bug.I started with: dtrace -n pid\$target:libc.so:t_splay:''{}'' -p <pid> Which was recommended by two different kernel engineers... That takes 20 seconds to generate 100,000 lines of output. The script I posted here was my first attempt at summarizing the data a bit. Perhaps there is a commonly used support document that could be updated a bit. I still need to dig into the source referenced by Roch to understand what all of the targets other than entry and return are. Haven''t seen that before. Mike -- Mike Gerdts http://mgerdts.blogspot.com/
On Thu, Mar 01, 2007 at 08:33:48PM -0600, Mike Gerdts wrote:> On 3/1/07, Adam Leventhal <ahl at eng.sun.com> wrote: > >While there''s nothing wrong with your script per se, using just the entry > >probe should resolve your problem. If it doesn''t let me know -- this may be > >another bug. > > I started with: > > dtrace -n pid\$target:libc.so:t_splay:''{}'' -p <pid> > > Which was recommended by two different kernel engineers...You''re right to keep them safely anonymous ;-)> Perhaps > there is a commonly used support document that could be updated a bit.The DTrace manual seems pretty clear on this sort of use of the pid provider in the chapter on User Land Tracing.> I still need to dig into the source referenced by Roch to understand > what all of the targets other than entry and return are. Haven''t seen > that before.Take a look at the chapter in the Solaris Dynamic Tracing Guide on the pid provider. Adam -- Adam Leventhal, Solaris Kernel Development http://blogs.sun.com/ahl
Mike Gerdts wrote:> On 3/1/07, Adam Leventhal <ahl at eng.sun.com> wrote: >> While there''s nothing wrong with your script per se, using just the entry >> probe should resolve your problem. If it doesn''t let me know -- this >> may be >> another bug. > > I started with: > > dtrace -n pid\$target:libc.so:t_splay:''{}'' -p <pid> > > Which was recommended by two different kernel engineers...it wasn''t me, but: this is an appropriate sort of line for kernel dtrace, since the only defined probes are likely to be entry and return. For the pid provider, it''s....different. :) (that is, I can imagine how the kernel engineers missed that point.)