mirror of
https://github.com/ioacademy-jikim/debugging
synced 2026-08-30 01:09:34 +00:00
first commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1,115 @@
|
||||
callgrind_x86_linux-bb.o: bb.c /usr/include/stdc-predef.h global.h \
|
||||
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h ../include/pub_tool_vki.h \
|
||||
../include/vki/vki-linux.h ../include/vki/vki-posixtypes-x86-linux.h \
|
||||
../include/vki/vki-x86-linux.h ../include/vki/vki-linux-drm.h \
|
||||
../include/vki/vki-xen.h ../include/vki/vki-xen-x86.h \
|
||||
../include/vki/vki-xen-domctl.h ../include/vki/vki-xen-sysctl.h \
|
||||
../include/vki/vki-xen-mmuext.h ../include/vki/vki-xen-schedop.h \
|
||||
../include/vki/vki-xen-memory.h ../include/vki/vki-xen-evtchn.h \
|
||||
../include/vki/vki-xen-gnttab.h ../include/vki/vki-xen-version.h \
|
||||
../include/vki/vki-xen-hvm.h ../include/vki/vki-xen-tmem.h \
|
||||
../include/vki/vki-xen-xsm.h ../include/vki/vki-xen-physdev.h \
|
||||
../include/pub_tool_debuginfo.h ../include/pub_tool_basics.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_libcbase.h \
|
||||
../include/pub_tool_libcassert.h ../include/pub_tool_libcfile.h \
|
||||
../include/pub_tool_vki.h ../include/pub_tool_libcprint.h \
|
||||
../include/pub_tool_libcproc.h ../include/pub_tool_machine.h \
|
||||
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
|
||||
../VEX/pub/libvex_ir.h ../include/pub_tool_mallocfree.h \
|
||||
../include/pub_tool_options.h ../include/pub_tool_tooliface.h \
|
||||
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_clientstate.h events.h \
|
||||
costs.h
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
@@ -0,0 +1,117 @@
|
||||
callgrind_x86_linux-bbcc.o: bbcc.c /usr/include/stdc-predef.h global.h \
|
||||
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h ../include/pub_tool_vki.h \
|
||||
../include/vki/vki-linux.h ../include/vki/vki-posixtypes-x86-linux.h \
|
||||
../include/vki/vki-x86-linux.h ../include/vki/vki-linux-drm.h \
|
||||
../include/vki/vki-xen.h ../include/vki/vki-xen-x86.h \
|
||||
../include/vki/vki-xen-domctl.h ../include/vki/vki-xen-sysctl.h \
|
||||
../include/vki/vki-xen-mmuext.h ../include/vki/vki-xen-schedop.h \
|
||||
../include/vki/vki-xen-memory.h ../include/vki/vki-xen-evtchn.h \
|
||||
../include/vki/vki-xen-gnttab.h ../include/vki/vki-xen-version.h \
|
||||
../include/vki/vki-xen-hvm.h ../include/vki/vki-xen-tmem.h \
|
||||
../include/vki/vki-xen-xsm.h ../include/vki/vki-xen-physdev.h \
|
||||
../include/pub_tool_debuginfo.h ../include/pub_tool_basics.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_libcbase.h \
|
||||
../include/pub_tool_libcassert.h ../include/pub_tool_libcfile.h \
|
||||
../include/pub_tool_vki.h ../include/pub_tool_libcprint.h \
|
||||
../include/pub_tool_libcproc.h ../include/pub_tool_machine.h \
|
||||
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
|
||||
../VEX/pub/libvex_ir.h ../include/pub_tool_mallocfree.h \
|
||||
../include/pub_tool_options.h ../include/pub_tool_tooliface.h \
|
||||
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_clientstate.h events.h \
|
||||
costs.h ../include/pub_tool_threadstate.h
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
|
||||
../include/pub_tool_threadstate.h:
|
||||
@@ -0,0 +1,115 @@
|
||||
callgrind_x86_linux-callstack.o: callstack.c /usr/include/stdc-predef.h \
|
||||
global.h ../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h ../include/pub_tool_vki.h \
|
||||
../include/vki/vki-linux.h ../include/vki/vki-posixtypes-x86-linux.h \
|
||||
../include/vki/vki-x86-linux.h ../include/vki/vki-linux-drm.h \
|
||||
../include/vki/vki-xen.h ../include/vki/vki-xen-x86.h \
|
||||
../include/vki/vki-xen-domctl.h ../include/vki/vki-xen-sysctl.h \
|
||||
../include/vki/vki-xen-mmuext.h ../include/vki/vki-xen-schedop.h \
|
||||
../include/vki/vki-xen-memory.h ../include/vki/vki-xen-evtchn.h \
|
||||
../include/vki/vki-xen-gnttab.h ../include/vki/vki-xen-version.h \
|
||||
../include/vki/vki-xen-hvm.h ../include/vki/vki-xen-tmem.h \
|
||||
../include/vki/vki-xen-xsm.h ../include/vki/vki-xen-physdev.h \
|
||||
../include/pub_tool_debuginfo.h ../include/pub_tool_basics.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_libcbase.h \
|
||||
../include/pub_tool_libcassert.h ../include/pub_tool_libcfile.h \
|
||||
../include/pub_tool_vki.h ../include/pub_tool_libcprint.h \
|
||||
../include/pub_tool_libcproc.h ../include/pub_tool_machine.h \
|
||||
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
|
||||
../VEX/pub/libvex_ir.h ../include/pub_tool_mallocfree.h \
|
||||
../include/pub_tool_options.h ../include/pub_tool_tooliface.h \
|
||||
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_clientstate.h events.h \
|
||||
costs.h
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
@@ -0,0 +1,117 @@
|
||||
callgrind_x86_linux-clo.o: clo.c /usr/include/stdc-predef.h ../config.h \
|
||||
global.h ../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h ../include/pub_tool_vki.h \
|
||||
../include/vki/vki-linux.h ../include/vki/vki-posixtypes-x86-linux.h \
|
||||
../include/vki/vki-x86-linux.h ../include/vki/vki-linux-drm.h \
|
||||
../include/vki/vki-xen.h ../include/vki/vki-xen-x86.h \
|
||||
../include/vki/vki-xen-domctl.h ../include/vki/vki-xen-sysctl.h \
|
||||
../include/vki/vki-xen-mmuext.h ../include/vki/vki-xen-schedop.h \
|
||||
../include/vki/vki-xen-memory.h ../include/vki/vki-xen-evtchn.h \
|
||||
../include/vki/vki-xen-gnttab.h ../include/vki/vki-xen-version.h \
|
||||
../include/vki/vki-xen-hvm.h ../include/vki/vki-xen-tmem.h \
|
||||
../include/vki/vki-xen-xsm.h ../include/vki/vki-xen-physdev.h \
|
||||
../include/pub_tool_debuginfo.h ../include/pub_tool_basics.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_libcbase.h \
|
||||
../include/pub_tool_libcassert.h ../include/pub_tool_libcfile.h \
|
||||
../include/pub_tool_vki.h ../include/pub_tool_libcprint.h \
|
||||
../include/pub_tool_libcproc.h ../include/pub_tool_machine.h \
|
||||
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
|
||||
../VEX/pub/libvex_ir.h ../include/pub_tool_mallocfree.h \
|
||||
../include/pub_tool_options.h ../include/pub_tool_tooliface.h \
|
||||
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_clientstate.h events.h \
|
||||
costs.h
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
../config.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
@@ -0,0 +1,115 @@
|
||||
callgrind_x86_linux-context.o: context.c /usr/include/stdc-predef.h \
|
||||
global.h ../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h ../include/pub_tool_vki.h \
|
||||
../include/vki/vki-linux.h ../include/vki/vki-posixtypes-x86-linux.h \
|
||||
../include/vki/vki-x86-linux.h ../include/vki/vki-linux-drm.h \
|
||||
../include/vki/vki-xen.h ../include/vki/vki-xen-x86.h \
|
||||
../include/vki/vki-xen-domctl.h ../include/vki/vki-xen-sysctl.h \
|
||||
../include/vki/vki-xen-mmuext.h ../include/vki/vki-xen-schedop.h \
|
||||
../include/vki/vki-xen-memory.h ../include/vki/vki-xen-evtchn.h \
|
||||
../include/vki/vki-xen-gnttab.h ../include/vki/vki-xen-version.h \
|
||||
../include/vki/vki-xen-hvm.h ../include/vki/vki-xen-tmem.h \
|
||||
../include/vki/vki-xen-xsm.h ../include/vki/vki-xen-physdev.h \
|
||||
../include/pub_tool_debuginfo.h ../include/pub_tool_basics.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_libcbase.h \
|
||||
../include/pub_tool_libcassert.h ../include/pub_tool_libcfile.h \
|
||||
../include/pub_tool_vki.h ../include/pub_tool_libcprint.h \
|
||||
../include/pub_tool_libcproc.h ../include/pub_tool_machine.h \
|
||||
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
|
||||
../VEX/pub/libvex_ir.h ../include/pub_tool_mallocfree.h \
|
||||
../include/pub_tool_options.h ../include/pub_tool_tooliface.h \
|
||||
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_clientstate.h events.h \
|
||||
costs.h
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
@@ -0,0 +1,115 @@
|
||||
callgrind_x86_linux-costs.o: costs.c /usr/include/stdc-predef.h global.h \
|
||||
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h ../include/pub_tool_vki.h \
|
||||
../include/vki/vki-linux.h ../include/vki/vki-posixtypes-x86-linux.h \
|
||||
../include/vki/vki-x86-linux.h ../include/vki/vki-linux-drm.h \
|
||||
../include/vki/vki-xen.h ../include/vki/vki-xen-x86.h \
|
||||
../include/vki/vki-xen-domctl.h ../include/vki/vki-xen-sysctl.h \
|
||||
../include/vki/vki-xen-mmuext.h ../include/vki/vki-xen-schedop.h \
|
||||
../include/vki/vki-xen-memory.h ../include/vki/vki-xen-evtchn.h \
|
||||
../include/vki/vki-xen-gnttab.h ../include/vki/vki-xen-version.h \
|
||||
../include/vki/vki-xen-hvm.h ../include/vki/vki-xen-tmem.h \
|
||||
../include/vki/vki-xen-xsm.h ../include/vki/vki-xen-physdev.h \
|
||||
../include/pub_tool_debuginfo.h ../include/pub_tool_basics.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_libcbase.h \
|
||||
../include/pub_tool_libcassert.h ../include/pub_tool_libcfile.h \
|
||||
../include/pub_tool_vki.h ../include/pub_tool_libcprint.h \
|
||||
../include/pub_tool_libcproc.h ../include/pub_tool_machine.h \
|
||||
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
|
||||
../VEX/pub/libvex_ir.h ../include/pub_tool_mallocfree.h \
|
||||
../include/pub_tool_options.h ../include/pub_tool_tooliface.h \
|
||||
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_clientstate.h events.h \
|
||||
costs.h
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
@@ -0,0 +1,115 @@
|
||||
callgrind_x86_linux-debug.o: debug.c /usr/include/stdc-predef.h global.h \
|
||||
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h ../include/pub_tool_vki.h \
|
||||
../include/vki/vki-linux.h ../include/vki/vki-posixtypes-x86-linux.h \
|
||||
../include/vki/vki-x86-linux.h ../include/vki/vki-linux-drm.h \
|
||||
../include/vki/vki-xen.h ../include/vki/vki-xen-x86.h \
|
||||
../include/vki/vki-xen-domctl.h ../include/vki/vki-xen-sysctl.h \
|
||||
../include/vki/vki-xen-mmuext.h ../include/vki/vki-xen-schedop.h \
|
||||
../include/vki/vki-xen-memory.h ../include/vki/vki-xen-evtchn.h \
|
||||
../include/vki/vki-xen-gnttab.h ../include/vki/vki-xen-version.h \
|
||||
../include/vki/vki-xen-hvm.h ../include/vki/vki-xen-tmem.h \
|
||||
../include/vki/vki-xen-xsm.h ../include/vki/vki-xen-physdev.h \
|
||||
../include/pub_tool_debuginfo.h ../include/pub_tool_basics.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_libcbase.h \
|
||||
../include/pub_tool_libcassert.h ../include/pub_tool_libcfile.h \
|
||||
../include/pub_tool_vki.h ../include/pub_tool_libcprint.h \
|
||||
../include/pub_tool_libcproc.h ../include/pub_tool_machine.h \
|
||||
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
|
||||
../VEX/pub/libvex_ir.h ../include/pub_tool_mallocfree.h \
|
||||
../include/pub_tool_options.h ../include/pub_tool_tooliface.h \
|
||||
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_clientstate.h events.h \
|
||||
costs.h
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
@@ -0,0 +1,119 @@
|
||||
callgrind_x86_linux-dump.o: dump.c /usr/include/stdc-predef.h ../config.h \
|
||||
global.h ../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h ../include/pub_tool_vki.h \
|
||||
../include/vki/vki-linux.h ../include/vki/vki-posixtypes-x86-linux.h \
|
||||
../include/vki/vki-x86-linux.h ../include/vki/vki-linux-drm.h \
|
||||
../include/vki/vki-xen.h ../include/vki/vki-xen-x86.h \
|
||||
../include/vki/vki-xen-domctl.h ../include/vki/vki-xen-sysctl.h \
|
||||
../include/vki/vki-xen-mmuext.h ../include/vki/vki-xen-schedop.h \
|
||||
../include/vki/vki-xen-memory.h ../include/vki/vki-xen-evtchn.h \
|
||||
../include/vki/vki-xen-gnttab.h ../include/vki/vki-xen-version.h \
|
||||
../include/vki/vki-xen-hvm.h ../include/vki/vki-xen-tmem.h \
|
||||
../include/vki/vki-xen-xsm.h ../include/vki/vki-xen-physdev.h \
|
||||
../include/pub_tool_debuginfo.h ../include/pub_tool_basics.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_libcbase.h \
|
||||
../include/pub_tool_libcassert.h ../include/pub_tool_libcfile.h \
|
||||
../include/pub_tool_vki.h ../include/pub_tool_libcprint.h \
|
||||
../include/pub_tool_libcproc.h ../include/pub_tool_machine.h \
|
||||
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
|
||||
../VEX/pub/libvex_ir.h ../include/pub_tool_mallocfree.h \
|
||||
../include/pub_tool_options.h ../include/pub_tool_tooliface.h \
|
||||
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_clientstate.h events.h \
|
||||
costs.h ../include/pub_tool_threadstate.h
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
../config.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
|
||||
../include/pub_tool_threadstate.h:
|
||||
@@ -0,0 +1,115 @@
|
||||
callgrind_x86_linux-events.o: events.c /usr/include/stdc-predef.h \
|
||||
global.h ../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h ../include/pub_tool_vki.h \
|
||||
../include/vki/vki-linux.h ../include/vki/vki-posixtypes-x86-linux.h \
|
||||
../include/vki/vki-x86-linux.h ../include/vki/vki-linux-drm.h \
|
||||
../include/vki/vki-xen.h ../include/vki/vki-xen-x86.h \
|
||||
../include/vki/vki-xen-domctl.h ../include/vki/vki-xen-sysctl.h \
|
||||
../include/vki/vki-xen-mmuext.h ../include/vki/vki-xen-schedop.h \
|
||||
../include/vki/vki-xen-memory.h ../include/vki/vki-xen-evtchn.h \
|
||||
../include/vki/vki-xen-gnttab.h ../include/vki/vki-xen-version.h \
|
||||
../include/vki/vki-xen-hvm.h ../include/vki/vki-xen-tmem.h \
|
||||
../include/vki/vki-xen-xsm.h ../include/vki/vki-xen-physdev.h \
|
||||
../include/pub_tool_debuginfo.h ../include/pub_tool_basics.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_libcbase.h \
|
||||
../include/pub_tool_libcassert.h ../include/pub_tool_libcfile.h \
|
||||
../include/pub_tool_vki.h ../include/pub_tool_libcprint.h \
|
||||
../include/pub_tool_libcproc.h ../include/pub_tool_machine.h \
|
||||
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
|
||||
../VEX/pub/libvex_ir.h ../include/pub_tool_mallocfree.h \
|
||||
../include/pub_tool_options.h ../include/pub_tool_tooliface.h \
|
||||
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_clientstate.h events.h \
|
||||
costs.h
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
@@ -0,0 +1,115 @@
|
||||
callgrind_x86_linux-fn.o: fn.c /usr/include/stdc-predef.h global.h \
|
||||
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h ../include/pub_tool_vki.h \
|
||||
../include/vki/vki-linux.h ../include/vki/vki-posixtypes-x86-linux.h \
|
||||
../include/vki/vki-x86-linux.h ../include/vki/vki-linux-drm.h \
|
||||
../include/vki/vki-xen.h ../include/vki/vki-xen-x86.h \
|
||||
../include/vki/vki-xen-domctl.h ../include/vki/vki-xen-sysctl.h \
|
||||
../include/vki/vki-xen-mmuext.h ../include/vki/vki-xen-schedop.h \
|
||||
../include/vki/vki-xen-memory.h ../include/vki/vki-xen-evtchn.h \
|
||||
../include/vki/vki-xen-gnttab.h ../include/vki/vki-xen-version.h \
|
||||
../include/vki/vki-xen-hvm.h ../include/vki/vki-xen-tmem.h \
|
||||
../include/vki/vki-xen-xsm.h ../include/vki/vki-xen-physdev.h \
|
||||
../include/pub_tool_debuginfo.h ../include/pub_tool_basics.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_libcbase.h \
|
||||
../include/pub_tool_libcassert.h ../include/pub_tool_libcfile.h \
|
||||
../include/pub_tool_vki.h ../include/pub_tool_libcprint.h \
|
||||
../include/pub_tool_libcproc.h ../include/pub_tool_machine.h \
|
||||
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
|
||||
../VEX/pub/libvex_ir.h ../include/pub_tool_mallocfree.h \
|
||||
../include/pub_tool_options.h ../include/pub_tool_tooliface.h \
|
||||
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_clientstate.h events.h \
|
||||
costs.h
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
@@ -0,0 +1,115 @@
|
||||
callgrind_x86_linux-jumps.o: jumps.c /usr/include/stdc-predef.h global.h \
|
||||
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h ../include/pub_tool_vki.h \
|
||||
../include/vki/vki-linux.h ../include/vki/vki-posixtypes-x86-linux.h \
|
||||
../include/vki/vki-x86-linux.h ../include/vki/vki-linux-drm.h \
|
||||
../include/vki/vki-xen.h ../include/vki/vki-xen-x86.h \
|
||||
../include/vki/vki-xen-domctl.h ../include/vki/vki-xen-sysctl.h \
|
||||
../include/vki/vki-xen-mmuext.h ../include/vki/vki-xen-schedop.h \
|
||||
../include/vki/vki-xen-memory.h ../include/vki/vki-xen-evtchn.h \
|
||||
../include/vki/vki-xen-gnttab.h ../include/vki/vki-xen-version.h \
|
||||
../include/vki/vki-xen-hvm.h ../include/vki/vki-xen-tmem.h \
|
||||
../include/vki/vki-xen-xsm.h ../include/vki/vki-xen-physdev.h \
|
||||
../include/pub_tool_debuginfo.h ../include/pub_tool_basics.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_libcbase.h \
|
||||
../include/pub_tool_libcassert.h ../include/pub_tool_libcfile.h \
|
||||
../include/pub_tool_vki.h ../include/pub_tool_libcprint.h \
|
||||
../include/pub_tool_libcproc.h ../include/pub_tool_machine.h \
|
||||
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
|
||||
../VEX/pub/libvex_ir.h ../include/pub_tool_mallocfree.h \
|
||||
../include/pub_tool_options.h ../include/pub_tool_tooliface.h \
|
||||
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_clientstate.h events.h \
|
||||
costs.h
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
@@ -0,0 +1,132 @@
|
||||
callgrind_x86_linux-main.o: main.c /usr/include/stdc-predef.h ../config.h \
|
||||
callgrind.h ../include/valgrind.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h global.h \
|
||||
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
../include/pub_tool_vki.h ../include/vki/vki-linux.h \
|
||||
../include/vki/vki-posixtypes-x86-linux.h ../include/vki/vki-x86-linux.h \
|
||||
../include/vki/vki-linux-drm.h ../include/vki/vki-xen.h \
|
||||
../include/vki/vki-xen-x86.h ../include/vki/vki-xen-domctl.h \
|
||||
../include/vki/vki-xen-sysctl.h ../include/vki/vki-xen-mmuext.h \
|
||||
../include/vki/vki-xen-schedop.h ../include/vki/vki-xen-memory.h \
|
||||
../include/vki/vki-xen-evtchn.h ../include/vki/vki-xen-gnttab.h \
|
||||
../include/vki/vki-xen-version.h ../include/vki/vki-xen-hvm.h \
|
||||
../include/vki/vki-xen-tmem.h ../include/vki/vki-xen-xsm.h \
|
||||
../include/vki/vki-xen-physdev.h ../include/pub_tool_debuginfo.h \
|
||||
../include/pub_tool_basics.h ../include/pub_tool_xarray.h \
|
||||
../include/pub_tool_libcbase.h ../include/pub_tool_libcassert.h \
|
||||
../include/pub_tool_libcfile.h ../include/pub_tool_vki.h \
|
||||
../include/pub_tool_libcprint.h ../include/pub_tool_libcproc.h \
|
||||
../include/pub_tool_machine.h ../VEX/pub/libvex.h \
|
||||
../VEX/pub/libvex_basictypes.h ../VEX/pub/libvex_ir.h \
|
||||
../include/pub_tool_mallocfree.h ../include/pub_tool_options.h \
|
||||
../include/pub_tool_tooliface.h ../include/pub_tool_errormgr.h \
|
||||
../include/pub_tool_execontext.h ../include/pub_tool_xarray.h \
|
||||
../include/pub_tool_clientstate.h events.h costs.h \
|
||||
../include/pub_tool_threadstate.h ../include/pub_tool_gdbserver.h \
|
||||
../include/pub_tool_transtab.h ../cachegrind/cg_branchpred.c
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
../config.h:
|
||||
|
||||
callgrind.h:
|
||||
|
||||
../include/valgrind.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
|
||||
../include/pub_tool_threadstate.h:
|
||||
|
||||
../include/pub_tool_gdbserver.h:
|
||||
|
||||
../include/pub_tool_transtab.h:
|
||||
|
||||
../cachegrind/cg_branchpred.c:
|
||||
@@ -0,0 +1,119 @@
|
||||
callgrind_x86_linux-sim.o: sim.c /usr/include/stdc-predef.h global.h \
|
||||
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h ../include/pub_tool_vki.h \
|
||||
../include/vki/vki-linux.h ../include/vki/vki-posixtypes-x86-linux.h \
|
||||
../include/vki/vki-x86-linux.h ../include/vki/vki-linux-drm.h \
|
||||
../include/vki/vki-xen.h ../include/vki/vki-xen-x86.h \
|
||||
../include/vki/vki-xen-domctl.h ../include/vki/vki-xen-sysctl.h \
|
||||
../include/vki/vki-xen-mmuext.h ../include/vki/vki-xen-schedop.h \
|
||||
../include/vki/vki-xen-memory.h ../include/vki/vki-xen-evtchn.h \
|
||||
../include/vki/vki-xen-gnttab.h ../include/vki/vki-xen-version.h \
|
||||
../include/vki/vki-xen-hvm.h ../include/vki/vki-xen-tmem.h \
|
||||
../include/vki/vki-xen-xsm.h ../include/vki/vki-xen-physdev.h \
|
||||
../include/pub_tool_debuginfo.h ../include/pub_tool_basics.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_libcbase.h \
|
||||
../include/pub_tool_libcassert.h ../include/pub_tool_libcfile.h \
|
||||
../include/pub_tool_vki.h ../include/pub_tool_libcprint.h \
|
||||
../include/pub_tool_libcproc.h ../include/pub_tool_machine.h \
|
||||
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
|
||||
../VEX/pub/libvex_ir.h ../include/pub_tool_mallocfree.h \
|
||||
../include/pub_tool_options.h ../include/pub_tool_tooliface.h \
|
||||
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_clientstate.h events.h \
|
||||
costs.h ../cachegrind/cg_arch.c ../cachegrind/cg_arch.h
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
|
||||
../cachegrind/cg_arch.c:
|
||||
|
||||
../cachegrind/cg_arch.h:
|
||||
@@ -0,0 +1,117 @@
|
||||
callgrind_x86_linux-threads.o: threads.c /usr/include/stdc-predef.h \
|
||||
global.h ../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h ../include/pub_tool_vki.h \
|
||||
../include/vki/vki-linux.h ../include/vki/vki-posixtypes-x86-linux.h \
|
||||
../include/vki/vki-x86-linux.h ../include/vki/vki-linux-drm.h \
|
||||
../include/vki/vki-xen.h ../include/vki/vki-xen-x86.h \
|
||||
../include/vki/vki-xen-domctl.h ../include/vki/vki-xen-sysctl.h \
|
||||
../include/vki/vki-xen-mmuext.h ../include/vki/vki-xen-schedop.h \
|
||||
../include/vki/vki-xen-memory.h ../include/vki/vki-xen-evtchn.h \
|
||||
../include/vki/vki-xen-gnttab.h ../include/vki/vki-xen-version.h \
|
||||
../include/vki/vki-xen-hvm.h ../include/vki/vki-xen-tmem.h \
|
||||
../include/vki/vki-xen-xsm.h ../include/vki/vki-xen-physdev.h \
|
||||
../include/pub_tool_debuginfo.h ../include/pub_tool_basics.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_libcbase.h \
|
||||
../include/pub_tool_libcassert.h ../include/pub_tool_libcfile.h \
|
||||
../include/pub_tool_vki.h ../include/pub_tool_libcprint.h \
|
||||
../include/pub_tool_libcproc.h ../include/pub_tool_machine.h \
|
||||
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
|
||||
../VEX/pub/libvex_ir.h ../include/pub_tool_mallocfree.h \
|
||||
../include/pub_tool_options.h ../include/pub_tool_tooliface.h \
|
||||
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
|
||||
../include/pub_tool_xarray.h ../include/pub_tool_clientstate.h events.h \
|
||||
costs.h ../include/pub_tool_threadstate.h
|
||||
|
||||
/usr/include/stdc-predef.h:
|
||||
|
||||
global.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/vki/vki-linux.h:
|
||||
|
||||
../include/vki/vki-posixtypes-x86-linux.h:
|
||||
|
||||
../include/vki/vki-x86-linux.h:
|
||||
|
||||
../include/vki/vki-linux-drm.h:
|
||||
|
||||
../include/vki/vki-xen.h:
|
||||
|
||||
../include/vki/vki-xen-x86.h:
|
||||
|
||||
../include/vki/vki-xen-domctl.h:
|
||||
|
||||
../include/vki/vki-xen-sysctl.h:
|
||||
|
||||
../include/vki/vki-xen-mmuext.h:
|
||||
|
||||
../include/vki/vki-xen-schedop.h:
|
||||
|
||||
../include/vki/vki-xen-memory.h:
|
||||
|
||||
../include/vki/vki-xen-evtchn.h:
|
||||
|
||||
../include/vki/vki-xen-gnttab.h:
|
||||
|
||||
../include/vki/vki-xen-version.h:
|
||||
|
||||
../include/vki/vki-xen-hvm.h:
|
||||
|
||||
../include/vki/vki-xen-tmem.h:
|
||||
|
||||
../include/vki/vki-xen-xsm.h:
|
||||
|
||||
../include/vki/vki-xen-physdev.h:
|
||||
|
||||
../include/pub_tool_debuginfo.h:
|
||||
|
||||
../include/pub_tool_basics.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_libcbase.h:
|
||||
|
||||
../include/pub_tool_libcassert.h:
|
||||
|
||||
../include/pub_tool_libcfile.h:
|
||||
|
||||
../include/pub_tool_vki.h:
|
||||
|
||||
../include/pub_tool_libcprint.h:
|
||||
|
||||
../include/pub_tool_libcproc.h:
|
||||
|
||||
../include/pub_tool_machine.h:
|
||||
|
||||
../VEX/pub/libvex.h:
|
||||
|
||||
../VEX/pub/libvex_basictypes.h:
|
||||
|
||||
../VEX/pub/libvex_ir.h:
|
||||
|
||||
../include/pub_tool_mallocfree.h:
|
||||
|
||||
../include/pub_tool_options.h:
|
||||
|
||||
../include/pub_tool_tooliface.h:
|
||||
|
||||
../include/pub_tool_errormgr.h:
|
||||
|
||||
../include/pub_tool_execontext.h:
|
||||
|
||||
../include/pub_tool_xarray.h:
|
||||
|
||||
../include/pub_tool_clientstate.h:
|
||||
|
||||
events.h:
|
||||
|
||||
costs.h:
|
||||
|
||||
../include/pub_tool_threadstate.h:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,90 @@
|
||||
include $(top_srcdir)/Makefile.tool.am
|
||||
|
||||
EXTRA_DIST = \
|
||||
docs/callgrind_annotate-manpage.xml \
|
||||
docs/callgrind_control-manpage.xml \
|
||||
docs/cl-manual.xml \
|
||||
docs/cl-format.xml
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Headers, etc
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
pkginclude_HEADERS = callgrind.h
|
||||
|
||||
bin_SCRIPTS = \
|
||||
callgrind_annotate \
|
||||
callgrind_control
|
||||
|
||||
noinst_HEADERS = \
|
||||
costs.h \
|
||||
events.h \
|
||||
global.h
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# callgrind-<platform>
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
noinst_PROGRAMS = callgrind-@VGCONF_ARCH_PRI@-@VGCONF_OS@
|
||||
if VGCONF_HAVE_PLATFORM_SEC
|
||||
noinst_PROGRAMS += callgrind-@VGCONF_ARCH_SEC@-@VGCONF_OS@
|
||||
endif
|
||||
|
||||
CALLGRIND_SOURCES_COMMON = \
|
||||
bb.c \
|
||||
bbcc.c \
|
||||
callstack.c \
|
||||
clo.c \
|
||||
context.c \
|
||||
costs.c \
|
||||
debug.c \
|
||||
dump.c \
|
||||
events.c \
|
||||
fn.c \
|
||||
jumps.c \
|
||||
main.c \
|
||||
sim.c \
|
||||
threads.c
|
||||
|
||||
# We sneakily include "cg_branchpred.c" and "cg_arch.c" from cachegrind
|
||||
CALLGRIND_CFLAGS_COMMON = -I$(top_srcdir)/cachegrind
|
||||
|
||||
callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_SOURCES = \
|
||||
$(CALLGRIND_SOURCES_COMMON)
|
||||
callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS_@VGCONF_PLATFORM_PRI_CAPS@)
|
||||
callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CFLAGS = \
|
||||
$(AM_CFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) $(CALLGRIND_CFLAGS_COMMON)
|
||||
callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_DEPENDENCIES = \
|
||||
$(TOOL_DEPENDENCIES_@VGCONF_PLATFORM_PRI_CAPS@)
|
||||
callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDADD = \
|
||||
$(TOOL_LDADD_@VGCONF_PLATFORM_PRI_CAPS@)
|
||||
callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDFLAGS = \
|
||||
$(TOOL_LDFLAGS_@VGCONF_PLATFORM_PRI_CAPS@)
|
||||
callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LINK = \
|
||||
$(top_builddir)/coregrind/link_tool_exe_@VGCONF_OS@ \
|
||||
@VALT_LOAD_ADDRESS_PRI@ \
|
||||
$(LINK) \
|
||||
$(callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CFLAGS) \
|
||||
$(callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDFLAGS)
|
||||
|
||||
if VGCONF_HAVE_PLATFORM_SEC
|
||||
callgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_SOURCES = \
|
||||
$(CALLGRIND_SOURCES_COMMON)
|
||||
callgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS_@VGCONF_PLATFORM_SEC_CAPS@)
|
||||
callgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CFLAGS = \
|
||||
$(AM_CFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) $(CALLGRIND_CFLAGS_COMMON)
|
||||
callgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_DEPENDENCIES = \
|
||||
$(TOOL_DEPENDENCIES_@VGCONF_PLATFORM_SEC_CAPS@)
|
||||
callgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDADD = \
|
||||
$(TOOL_LDADD_@VGCONF_PLATFORM_SEC_CAPS@)
|
||||
callgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDFLAGS = \
|
||||
$(TOOL_LDFLAGS_@VGCONF_PLATFORM_SEC_CAPS@)
|
||||
callgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LINK = \
|
||||
$(top_builddir)/coregrind/link_tool_exe_@VGCONF_OS@ \
|
||||
@VALT_LOAD_ADDRESS_SEC@ \
|
||||
$(LINK) \
|
||||
$(callgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CFLAGS) \
|
||||
$(callgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDFLAGS)
|
||||
endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,345 @@
|
||||
/*--------------------------------------------------------------------*/
|
||||
/*--- Callgrind ---*/
|
||||
/*--- bb.c ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
This file is part of Callgrind, a Valgrind tool for call tracing.
|
||||
|
||||
Copyright (C) 2002-2015, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The GNU General Public License is contained in the file COPYING.
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Basic block (BB) operations ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
/* BB hash, resizable */
|
||||
bb_hash bbs;
|
||||
|
||||
void CLG_(init_bb_hash)()
|
||||
{
|
||||
Int i;
|
||||
|
||||
bbs.size = 8437;
|
||||
bbs.entries = 0;
|
||||
bbs.table = (BB**) CLG_MALLOC("cl.bb.ibh.1",
|
||||
bbs.size * sizeof(BB*));
|
||||
|
||||
for (i = 0; i < bbs.size; i++) bbs.table[i] = NULL;
|
||||
}
|
||||
|
||||
bb_hash* CLG_(get_bb_hash)()
|
||||
{
|
||||
return &bbs;
|
||||
}
|
||||
|
||||
/* The hash stores BBs according to
|
||||
* - ELF object (is 0 for code in anonymous mapping)
|
||||
* - BB base as object file offset
|
||||
*/
|
||||
static __inline__
|
||||
UInt bb_hash_idx(obj_node* obj, PtrdiffT offset, UInt size)
|
||||
{
|
||||
return (((Addr)obj) + offset) % size;
|
||||
}
|
||||
|
||||
/* double size of bb table */
|
||||
static
|
||||
void resize_bb_table(void)
|
||||
{
|
||||
Int i, new_size, conflicts1 = 0, conflicts2 = 0;
|
||||
BB **new_table, *curr, *next;
|
||||
UInt new_idx;
|
||||
|
||||
new_size = 2* bbs.size +3;
|
||||
new_table = (BB**) CLG_MALLOC("cl.bb.rbt.1",
|
||||
new_size * sizeof(BB*));
|
||||
|
||||
for (i = 0; i < new_size; i++)
|
||||
new_table[i] = NULL;
|
||||
|
||||
for (i = 0; i < bbs.size; i++) {
|
||||
if (bbs.table[i] == NULL) continue;
|
||||
|
||||
curr = bbs.table[i];
|
||||
while (NULL != curr) {
|
||||
next = curr->next;
|
||||
|
||||
new_idx = bb_hash_idx(curr->obj, curr->offset, new_size);
|
||||
|
||||
curr->next = new_table[new_idx];
|
||||
new_table[new_idx] = curr;
|
||||
if (curr->next) {
|
||||
conflicts1++;
|
||||
if (curr->next->next)
|
||||
conflicts2++;
|
||||
}
|
||||
|
||||
curr = next;
|
||||
}
|
||||
}
|
||||
|
||||
VG_(free)(bbs.table);
|
||||
|
||||
|
||||
CLG_DEBUG(0, "Resize BB Hash: %u => %d (entries %u, conflicts %d/%d)\n",
|
||||
bbs.size, new_size,
|
||||
bbs.entries, conflicts1, conflicts2);
|
||||
|
||||
bbs.size = new_size;
|
||||
bbs.table = new_table;
|
||||
CLG_(stat).bb_hash_resizes++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocate new BB structure (including space for event type list)
|
||||
* Not initialized:
|
||||
* - instr_len, cost_count, instr[]
|
||||
*/
|
||||
static BB* new_bb(obj_node* obj, PtrdiffT offset,
|
||||
UInt instr_count, UInt cjmp_count, Bool cjmp_inverted)
|
||||
{
|
||||
BB* bb;
|
||||
UInt idx, size;
|
||||
|
||||
/* check fill degree of bb hash table and resize if needed (>80%) */
|
||||
bbs.entries++;
|
||||
if (10 * bbs.entries / bbs.size > 8)
|
||||
resize_bb_table();
|
||||
|
||||
size = sizeof(BB) + instr_count * sizeof(InstrInfo)
|
||||
+ (cjmp_count+1) * sizeof(CJmpInfo);
|
||||
bb = (BB*) CLG_MALLOC("cl.bb.nb.1", size);
|
||||
VG_(memset)(bb, 0, size);
|
||||
|
||||
bb->obj = obj;
|
||||
bb->offset = offset;
|
||||
|
||||
bb->instr_count = instr_count;
|
||||
bb->cjmp_count = cjmp_count;
|
||||
bb->cjmp_inverted = cjmp_inverted;
|
||||
bb->jmp = (CJmpInfo*) &(bb->instr[instr_count]);
|
||||
bb->instr_len = 0;
|
||||
bb->cost_count = 0;
|
||||
bb->sect_kind = VG_(DebugInfo_sect_kind)(NULL, offset + obj->offset);
|
||||
bb->fn = 0;
|
||||
bb->line = 0;
|
||||
bb->is_entry = 0;
|
||||
bb->bbcc_list = 0;
|
||||
bb->last_bbcc = 0;
|
||||
|
||||
/* insert into BB hash table */
|
||||
idx = bb_hash_idx(obj, offset, bbs.size);
|
||||
bb->next = bbs.table[idx];
|
||||
bbs.table[idx] = bb;
|
||||
|
||||
CLG_(stat).distinct_bbs++;
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
CLG_DEBUGIF(3) {
|
||||
VG_(printf)(" new_bb (instr %u, jmps %u, inv %s) [now %d]: ",
|
||||
instr_count, cjmp_count,
|
||||
cjmp_inverted ? "yes":"no",
|
||||
CLG_(stat).distinct_bbs);
|
||||
CLG_(print_bb)(0, bb);
|
||||
VG_(printf)("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
CLG_(get_fn_node)(bb);
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
|
||||
/* get the BB structure for a BB start address */
|
||||
static __inline__
|
||||
BB* lookup_bb(obj_node* obj, PtrdiffT offset)
|
||||
{
|
||||
BB* bb;
|
||||
Int idx;
|
||||
|
||||
idx = bb_hash_idx(obj, offset, bbs.size);
|
||||
bb = bbs.table[idx];
|
||||
|
||||
while(bb) {
|
||||
if ((bb->obj == obj) && (bb->offset == offset)) break;
|
||||
bb = bb->next;
|
||||
}
|
||||
|
||||
CLG_DEBUG(5, " lookup_bb (Obj %s, off %#lx): %p\n",
|
||||
obj->name, (UWord)offset, bb);
|
||||
return bb;
|
||||
}
|
||||
|
||||
static __inline__
|
||||
obj_node* obj_of_address(Addr addr)
|
||||
{
|
||||
obj_node* obj;
|
||||
DebugInfo* di;
|
||||
PtrdiffT offset;
|
||||
|
||||
di = VG_(find_DebugInfo)(addr);
|
||||
obj = CLG_(get_obj_node)( di );
|
||||
|
||||
/* Update symbol offset in object if remapped */
|
||||
/* FIXME (or at least check this) 2008 Feb 19: 'offset' is
|
||||
only correct for text symbols, not for data symbols */
|
||||
offset = di ? VG_(DebugInfo_get_text_bias)(di):0;
|
||||
if (obj->offset != offset) {
|
||||
Addr start = di ? VG_(DebugInfo_get_text_avma)(di) : 0;
|
||||
|
||||
CLG_DEBUG(0, "Mapping changed for '%s': %#lx -> %#lx\n",
|
||||
obj->name, obj->start, start);
|
||||
|
||||
/* Size should be the same, and offset diff == start diff */
|
||||
CLG_ASSERT( obj->size == (di ? VG_(DebugInfo_get_text_size)(di) : 0) );
|
||||
CLG_ASSERT( obj->start - start == obj->offset - offset );
|
||||
obj->offset = offset;
|
||||
obj->start = start;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* Get the BB structure for a BB start address.
|
||||
* If the BB has to be created, the IRBB is needed to
|
||||
* compute the event type list for costs, and seen_before is
|
||||
* set to False. Otherwise, seen_before is set to True.
|
||||
*
|
||||
* BBs are never discarded. There are 2 cases where this function
|
||||
* is called from CLG_(instrument)() and a BB already exists:
|
||||
* - The instrumented version was removed from Valgrinds TT cache
|
||||
* - The ELF object of the BB was unmapped and mapped again.
|
||||
* This involves a possibly different address, but is handled by
|
||||
* looking up a BB keyed by (obj_node, file offset).
|
||||
*
|
||||
* bbIn==0 is possible for artificial BB without real code.
|
||||
* Such a BB is created when returning to an unknown function.
|
||||
*/
|
||||
BB* CLG_(get_bb)(Addr addr, IRSB* bbIn, /*OUT*/ Bool *seen_before)
|
||||
{
|
||||
BB* bb;
|
||||
obj_node* obj;
|
||||
UInt n_instrs, n_jmps;
|
||||
Bool cjmp_inverted = False;
|
||||
|
||||
CLG_DEBUG(5, "+ get_bb(BB %#lx)\n", addr);
|
||||
|
||||
obj = obj_of_address(addr);
|
||||
bb = lookup_bb(obj, addr - obj->offset);
|
||||
|
||||
n_instrs = 0;
|
||||
n_jmps = 0;
|
||||
CLG_(collectBlockInfo)(bbIn, &n_instrs, &n_jmps, &cjmp_inverted);
|
||||
|
||||
*seen_before = bb ? True : False;
|
||||
if (*seen_before) {
|
||||
if (bb->instr_count != n_instrs) {
|
||||
VG_(message)(Vg_DebugMsg,
|
||||
"ERROR: BB Retranslation Mismatch at BB %#lx\n", addr);
|
||||
VG_(message)(Vg_DebugMsg,
|
||||
" new: Obj %s, Off %#lx, BBOff %#lx, Instrs %u\n",
|
||||
obj->name, (UWord)obj->offset,
|
||||
addr - obj->offset, n_instrs);
|
||||
VG_(message)(Vg_DebugMsg,
|
||||
" old: Obj %s, Off %#lx, BBOff %#lx, Instrs %u\n",
|
||||
bb->obj->name, (UWord)bb->obj->offset,
|
||||
(UWord)bb->offset, bb->instr_count);
|
||||
CLG_ASSERT(bb->instr_count == n_instrs );
|
||||
}
|
||||
CLG_ASSERT(bb->cjmp_count == n_jmps );
|
||||
CLG_(stat).bb_retranslations++;
|
||||
|
||||
CLG_DEBUG(5, "- get_bb(BB %#lx): seen before.\n", addr);
|
||||
return bb;
|
||||
}
|
||||
|
||||
bb = new_bb(obj, addr - obj->offset, n_instrs, n_jmps, cjmp_inverted);
|
||||
|
||||
CLG_DEBUG(5, "- get_bb(BB %#lx)\n", addr);
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
/* Delete the BB info for the bb with unredirected entry-point
|
||||
address 'addr'. */
|
||||
void CLG_(delete_bb)(Addr addr)
|
||||
{
|
||||
BB *bb, *bp;
|
||||
Int idx, size;
|
||||
|
||||
obj_node* obj = obj_of_address(addr);
|
||||
PtrdiffT offset = addr - obj->offset;
|
||||
|
||||
idx = bb_hash_idx(obj, offset, bbs.size);
|
||||
bb = bbs.table[idx];
|
||||
|
||||
/* bb points at the current bb under consideration, and bp is the
|
||||
one before. */
|
||||
bp = NULL;
|
||||
while(bb) {
|
||||
if ((bb->obj == obj) && (bb->offset == offset)) break;
|
||||
bp = bb;
|
||||
bb = bb->next;
|
||||
}
|
||||
|
||||
if (bb == NULL) {
|
||||
CLG_DEBUG(3, " delete_bb (Obj %s, off %#lx): NOT FOUND\n",
|
||||
obj->name, (UWord)offset);
|
||||
|
||||
/* we didn't find it.
|
||||
* this happens when callgrinds instrumentation mode
|
||||
* was off at BB translation time, ie. no BB was created.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* unlink it from hash table */
|
||||
|
||||
if (bp == NULL) {
|
||||
/* we found the first one in the list. */
|
||||
tl_assert(bb == bbs.table[idx]);
|
||||
bbs.table[idx] = bb->next;
|
||||
} else {
|
||||
tl_assert(bb != bbs.table[idx]);
|
||||
bp->next = bb->next;
|
||||
}
|
||||
|
||||
CLG_DEBUG(3, " delete_bb (Obj %s, off %#lx): %p, BBCC head: %p\n",
|
||||
obj->name, (UWord)offset, bb, bb->bbcc_list);
|
||||
|
||||
if (bb->bbcc_list == 0) {
|
||||
/* can be safely deleted */
|
||||
|
||||
/* Fill the block up with junk and then free it, so we will
|
||||
hopefully get a segfault if it is used again by mistake. */
|
||||
size = sizeof(BB)
|
||||
+ bb->instr_count * sizeof(InstrInfo)
|
||||
+ (bb->cjmp_count+1) * sizeof(CJmpInfo);
|
||||
VG_(memset)( bb, 0xAA, size );
|
||||
CLG_FREE(bb);
|
||||
return;
|
||||
}
|
||||
CLG_DEBUG(3, " delete_bb: BB in use, can not free!\n");
|
||||
}
|
||||
@@ -0,0 +1,917 @@
|
||||
/*--------------------------------------------------------------------*/
|
||||
/*--- Callgrind ---*/
|
||||
/*--- bbcc.c ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
This file is part of Callgrind, a Valgrind tool for call tracing.
|
||||
|
||||
Copyright (C) 2002-2015, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The GNU General Public License is contained in the file COPYING.
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
#include "costs.h"
|
||||
|
||||
#include "pub_tool_threadstate.h"
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- BBCC operations ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
#define N_BBCC_INITIAL_ENTRIES 10437
|
||||
|
||||
/* BBCC table (key is BB/Context), per thread, resizable */
|
||||
bbcc_hash current_bbccs;
|
||||
|
||||
void CLG_(init_bbcc_hash)(bbcc_hash* bbccs)
|
||||
{
|
||||
Int i;
|
||||
|
||||
CLG_ASSERT(bbccs != 0);
|
||||
|
||||
bbccs->size = N_BBCC_INITIAL_ENTRIES;
|
||||
bbccs->entries = 0;
|
||||
bbccs->table = (BBCC**) CLG_MALLOC("cl.bbcc.ibh.1",
|
||||
bbccs->size * sizeof(BBCC*));
|
||||
|
||||
for (i = 0; i < bbccs->size; i++) bbccs->table[i] = NULL;
|
||||
}
|
||||
|
||||
void CLG_(copy_current_bbcc_hash)(bbcc_hash* dst)
|
||||
{
|
||||
CLG_ASSERT(dst != 0);
|
||||
|
||||
dst->size = current_bbccs.size;
|
||||
dst->entries = current_bbccs.entries;
|
||||
dst->table = current_bbccs.table;
|
||||
}
|
||||
|
||||
bbcc_hash* CLG_(get_current_bbcc_hash)()
|
||||
{
|
||||
return ¤t_bbccs;
|
||||
}
|
||||
|
||||
void CLG_(set_current_bbcc_hash)(bbcc_hash* h)
|
||||
{
|
||||
CLG_ASSERT(h != 0);
|
||||
|
||||
current_bbccs.size = h->size;
|
||||
current_bbccs.entries = h->entries;
|
||||
current_bbccs.table = h->table;
|
||||
}
|
||||
|
||||
/*
|
||||
* Zero all costs of a BBCC
|
||||
*/
|
||||
void CLG_(zero_bbcc)(BBCC* bbcc)
|
||||
{
|
||||
Int i;
|
||||
jCC* jcc;
|
||||
|
||||
CLG_ASSERT(bbcc->cxt != 0);
|
||||
CLG_DEBUG(1, " zero_bbcc: BB %#lx, Cxt %u "
|
||||
"(fn '%s', rec %u)\n",
|
||||
bb_addr(bbcc->bb),
|
||||
bbcc->cxt->base_number + bbcc->rec_index,
|
||||
bbcc->cxt->fn[0]->name,
|
||||
bbcc->rec_index);
|
||||
|
||||
if ((bbcc->ecounter_sum ==0) &&
|
||||
(bbcc->ret_counter ==0)) return;
|
||||
|
||||
for(i=0;i<bbcc->bb->cost_count;i++)
|
||||
bbcc->cost[i] = 0;
|
||||
for(i=0;i <= bbcc->bb->cjmp_count;i++) {
|
||||
bbcc->jmp[i].ecounter = 0;
|
||||
for(jcc=bbcc->jmp[i].jcc_list; jcc; jcc=jcc->next_from)
|
||||
CLG_(init_cost)( CLG_(sets).full, jcc->cost );
|
||||
}
|
||||
bbcc->ecounter_sum = 0;
|
||||
bbcc->ret_counter = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CLG_(forall_bbccs)(void (*func)(BBCC*))
|
||||
{
|
||||
BBCC *bbcc, *bbcc2;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < current_bbccs.size; i++) {
|
||||
if ((bbcc=current_bbccs.table[i]) == NULL) continue;
|
||||
while (bbcc) {
|
||||
/* every bbcc should have a rec_array */
|
||||
CLG_ASSERT(bbcc->rec_array != 0);
|
||||
|
||||
for(j=0;j<bbcc->cxt->fn[0]->separate_recursions;j++) {
|
||||
if ((bbcc2 = bbcc->rec_array[j]) == 0) continue;
|
||||
|
||||
(*func)(bbcc2);
|
||||
}
|
||||
bbcc = bbcc->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* All BBCCs for recursion level 0 are inserted into a
|
||||
* thread specific hash table with key
|
||||
* - address of BB structure (unique, as never freed)
|
||||
* - current context (includes caller chain)
|
||||
* BBCCs for other recursion levels are in bbcc->rec_array.
|
||||
*
|
||||
* The hash is used in setup_bb(), i.e. to find the cost
|
||||
* counters to be changed in the execution of a BB.
|
||||
*/
|
||||
|
||||
static __inline__
|
||||
UInt bbcc_hash_idx(BB* bb, Context* cxt, UInt size)
|
||||
{
|
||||
CLG_ASSERT(bb != 0);
|
||||
CLG_ASSERT(cxt != 0);
|
||||
|
||||
return ((Addr)bb + (Addr)cxt) % size;
|
||||
}
|
||||
|
||||
|
||||
/* Lookup for a BBCC in hash.
|
||||
*/
|
||||
static
|
||||
BBCC* lookup_bbcc(BB* bb, Context* cxt)
|
||||
{
|
||||
BBCC* bbcc = bb->last_bbcc;
|
||||
UInt idx;
|
||||
|
||||
/* check LRU */
|
||||
if (bbcc->cxt == cxt) {
|
||||
if (!CLG_(clo).separate_threads) {
|
||||
/* if we don't dump threads separate, tid doesn't have to match */
|
||||
return bbcc;
|
||||
}
|
||||
if (bbcc->tid == CLG_(current_tid)) return bbcc;
|
||||
}
|
||||
|
||||
CLG_(stat).bbcc_lru_misses++;
|
||||
|
||||
idx = bbcc_hash_idx(bb, cxt, current_bbccs.size);
|
||||
bbcc = current_bbccs.table[idx];
|
||||
while (bbcc &&
|
||||
(bb != bbcc->bb ||
|
||||
cxt != bbcc->cxt)) {
|
||||
bbcc = bbcc->next;
|
||||
}
|
||||
|
||||
CLG_DEBUG(2," lookup_bbcc(BB %#lx, Cxt %u, fn '%s'): %p (tid %u)\n",
|
||||
bb_addr(bb), cxt->base_number, cxt->fn[0]->name,
|
||||
bbcc, bbcc ? bbcc->tid : 0);
|
||||
|
||||
CLG_DEBUGIF(2)
|
||||
if (bbcc) CLG_(print_bbcc)(-2,bbcc);
|
||||
|
||||
return bbcc;
|
||||
}
|
||||
|
||||
|
||||
/* double size of hash table 1 (addr->BBCC) */
|
||||
static void resize_bbcc_hash(void)
|
||||
{
|
||||
Int i, new_size, conflicts1 = 0, conflicts2 = 0;
|
||||
BBCC** new_table;
|
||||
UInt new_idx;
|
||||
BBCC *curr_BBCC, *next_BBCC;
|
||||
|
||||
new_size = 2*current_bbccs.size+3;
|
||||
new_table = (BBCC**) CLG_MALLOC("cl.bbcc.rbh.1",
|
||||
new_size * sizeof(BBCC*));
|
||||
|
||||
for (i = 0; i < new_size; i++)
|
||||
new_table[i] = NULL;
|
||||
|
||||
for (i = 0; i < current_bbccs.size; i++) {
|
||||
if (current_bbccs.table[i] == NULL) continue;
|
||||
|
||||
curr_BBCC = current_bbccs.table[i];
|
||||
while (NULL != curr_BBCC) {
|
||||
next_BBCC = curr_BBCC->next;
|
||||
|
||||
new_idx = bbcc_hash_idx(curr_BBCC->bb,
|
||||
curr_BBCC->cxt,
|
||||
new_size);
|
||||
|
||||
curr_BBCC->next = new_table[new_idx];
|
||||
new_table[new_idx] = curr_BBCC;
|
||||
if (curr_BBCC->next) {
|
||||
conflicts1++;
|
||||
if (curr_BBCC->next->next)
|
||||
conflicts2++;
|
||||
}
|
||||
|
||||
curr_BBCC = next_BBCC;
|
||||
}
|
||||
}
|
||||
|
||||
VG_(free)(current_bbccs.table);
|
||||
|
||||
|
||||
CLG_DEBUG(0,"Resize BBCC Hash: %u => %d (entries %u, conflicts %d/%d)\n",
|
||||
current_bbccs.size, new_size,
|
||||
current_bbccs.entries, conflicts1, conflicts2);
|
||||
|
||||
current_bbccs.size = new_size;
|
||||
current_bbccs.table = new_table;
|
||||
CLG_(stat).bbcc_hash_resizes++;
|
||||
}
|
||||
|
||||
|
||||
static __inline
|
||||
BBCC** new_recursion(int size)
|
||||
{
|
||||
BBCC** bbccs;
|
||||
int i;
|
||||
|
||||
bbccs = (BBCC**) CLG_MALLOC("cl.bbcc.nr.1", sizeof(BBCC*) * size);
|
||||
for(i=0;i<size;i++)
|
||||
bbccs[i] = 0;
|
||||
|
||||
CLG_DEBUG(3," new_recursion(size %d): %p\n", size, bbccs);
|
||||
|
||||
return bbccs;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Allocate a new BBCC
|
||||
*
|
||||
* Uninitialized:
|
||||
* cxt, rec_index, rec_array, next_bbcc, next1, next2
|
||||
*/
|
||||
static __inline__
|
||||
BBCC* new_bbcc(BB* bb)
|
||||
{
|
||||
BBCC* bbcc;
|
||||
Int i;
|
||||
|
||||
/* We need cjmp_count+1 JmpData structs:
|
||||
* the last is for the unconditional jump/call/ret at end of BB
|
||||
*/
|
||||
bbcc = (BBCC*)CLG_MALLOC("cl.bbcc.nb.1",
|
||||
sizeof(BBCC) +
|
||||
(bb->cjmp_count+1) * sizeof(JmpData));
|
||||
bbcc->bb = bb;
|
||||
bbcc->tid = CLG_(current_tid);
|
||||
|
||||
bbcc->ret_counter = 0;
|
||||
bbcc->skipped = 0;
|
||||
bbcc->cost = CLG_(get_costarray)(bb->cost_count);
|
||||
for(i=0;i<bb->cost_count;i++)
|
||||
bbcc->cost[i] = 0;
|
||||
for(i=0; i<=bb->cjmp_count; i++) {
|
||||
bbcc->jmp[i].ecounter = 0;
|
||||
bbcc->jmp[i].jcc_list = 0;
|
||||
}
|
||||
bbcc->ecounter_sum = 0;
|
||||
|
||||
/* Init pointer caches (LRU) */
|
||||
bbcc->lru_next_bbcc = 0;
|
||||
bbcc->lru_from_jcc = 0;
|
||||
bbcc->lru_to_jcc = 0;
|
||||
|
||||
CLG_(stat).distinct_bbccs++;
|
||||
|
||||
CLG_DEBUG(3, " new_bbcc(BB %#lx): %p (now %d)\n",
|
||||
bb_addr(bb), bbcc, CLG_(stat).distinct_bbccs);
|
||||
|
||||
return bbcc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts a new BBCC into hashes.
|
||||
* BBCC specific items must be set as this is used for the hash
|
||||
* keys:
|
||||
* fn : current function
|
||||
* tid : current thread ID
|
||||
* from : position where current function is called from
|
||||
*
|
||||
* Recursion level doesn't need to be set as this is not included
|
||||
* in the hash key: Only BBCCs with rec level 0 are in hashes.
|
||||
*/
|
||||
static
|
||||
void insert_bbcc_into_hash(BBCC* bbcc)
|
||||
{
|
||||
UInt idx;
|
||||
|
||||
CLG_ASSERT(bbcc->cxt != 0);
|
||||
|
||||
CLG_DEBUG(3,"+ insert_bbcc_into_hash(BB %#lx, fn '%s')\n",
|
||||
bb_addr(bbcc->bb), bbcc->cxt->fn[0]->name);
|
||||
|
||||
/* check fill degree of hash and resize if needed (>90%) */
|
||||
current_bbccs.entries++;
|
||||
if (100 * current_bbccs.entries / current_bbccs.size > 90)
|
||||
resize_bbcc_hash();
|
||||
|
||||
idx = bbcc_hash_idx(bbcc->bb, bbcc->cxt, current_bbccs.size);
|
||||
bbcc->next = current_bbccs.table[idx];
|
||||
current_bbccs.table[idx] = bbcc;
|
||||
|
||||
CLG_DEBUG(3,"- insert_bbcc_into_hash: %u entries\n",
|
||||
current_bbccs.entries);
|
||||
}
|
||||
|
||||
/* String is returned in a dynamically allocated buffer. Caller is
|
||||
responsible for free'ing it. */
|
||||
static HChar* mangled_cxt(const Context* cxt, Int rec_index)
|
||||
{
|
||||
Int i, p;
|
||||
|
||||
if (!cxt) return VG_(strdup)("cl.bbcc.mcxt", "(no context)");
|
||||
|
||||
/* Overestimate the number of bytes we need to hold the string. */
|
||||
SizeT need = 20; // rec_index + nul-terminator
|
||||
for (i = 0; i < cxt->size; ++i)
|
||||
need += VG_(strlen)(cxt->fn[i]->name) + 1; // 1 for leading '
|
||||
|
||||
HChar *mangled = CLG_MALLOC("cl.bbcc.mcxt", need);
|
||||
p = VG_(sprintf)(mangled, "%s", cxt->fn[0]->name);
|
||||
if (rec_index >0)
|
||||
p += VG_(sprintf)(mangled+p, "'%d", rec_index +1);
|
||||
for(i=1;i<cxt->size;i++)
|
||||
p += VG_(sprintf)(mangled+p, "'%s", cxt->fn[i]->name);
|
||||
|
||||
return mangled;
|
||||
}
|
||||
|
||||
|
||||
/* Create a new BBCC as a copy of an existing one,
|
||||
* but with costs set to 0 and jcc chains empty.
|
||||
*
|
||||
* This is needed when a BB is executed in another context than
|
||||
* the one at instrumentation time of the BB.
|
||||
*
|
||||
* Use cases:
|
||||
* rec_index == 0: clone from a BBCC with differing tid/cxt
|
||||
* and insert into hashes
|
||||
* rec_index >0 : clone from a BBCC with same tid/cxt and rec_index 0
|
||||
* don't insert into hashes
|
||||
*/
|
||||
static BBCC* clone_bbcc(BBCC* orig, Context* cxt, Int rec_index)
|
||||
{
|
||||
BBCC* bbcc;
|
||||
|
||||
CLG_DEBUG(3,"+ clone_bbcc(BB %#lx, rec %d, fn %s)\n",
|
||||
bb_addr(orig->bb), rec_index, cxt->fn[0]->name);
|
||||
|
||||
bbcc = new_bbcc(orig->bb);
|
||||
|
||||
if (rec_index == 0) {
|
||||
|
||||
/* hash insertion is only allowed if tid or cxt is different */
|
||||
CLG_ASSERT((orig->tid != CLG_(current_tid)) ||
|
||||
(orig->cxt != cxt));
|
||||
|
||||
bbcc->rec_index = 0;
|
||||
bbcc->cxt = cxt;
|
||||
bbcc->rec_array = new_recursion(cxt->fn[0]->separate_recursions);
|
||||
bbcc->rec_array[0] = bbcc;
|
||||
|
||||
insert_bbcc_into_hash(bbcc);
|
||||
}
|
||||
else {
|
||||
if (CLG_(clo).separate_threads)
|
||||
CLG_ASSERT(orig->tid == CLG_(current_tid));
|
||||
|
||||
CLG_ASSERT(orig->cxt == cxt);
|
||||
CLG_ASSERT(orig->rec_array);
|
||||
CLG_ASSERT(cxt->fn[0]->separate_recursions > rec_index);
|
||||
CLG_ASSERT(orig->rec_array[rec_index] ==0);
|
||||
|
||||
/* new BBCC will only have differing recursion level */
|
||||
bbcc->rec_index = rec_index;
|
||||
bbcc->cxt = cxt;
|
||||
bbcc->rec_array = orig->rec_array;
|
||||
bbcc->rec_array[rec_index] = bbcc;
|
||||
}
|
||||
|
||||
/* update list of BBCCs for same BB */
|
||||
bbcc->next_bbcc = orig->bb->bbcc_list;
|
||||
orig->bb->bbcc_list = bbcc;
|
||||
|
||||
|
||||
CLG_DEBUGIF(3)
|
||||
CLG_(print_bbcc)(-2, bbcc);
|
||||
|
||||
HChar *mangled_orig = mangled_cxt(orig->cxt, orig->rec_index);
|
||||
HChar *mangled_bbcc = mangled_cxt(bbcc->cxt, bbcc->rec_index);
|
||||
CLG_DEBUG(2,"- clone_BBCC(%p, %d) for BB %#lx\n"
|
||||
" orig %s\n"
|
||||
" new %s\n",
|
||||
orig, rec_index, bb_addr(orig->bb),
|
||||
mangled_orig,
|
||||
mangled_bbcc);
|
||||
CLG_FREE(mangled_orig);
|
||||
CLG_FREE(mangled_bbcc);
|
||||
|
||||
CLG_(stat).bbcc_clones++;
|
||||
|
||||
return bbcc;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Get a pointer to the cost centre structure for given basic block
|
||||
* address. If created, the BBCC is inserted into the BBCC hash.
|
||||
* Also sets BB_seen_before by reference.
|
||||
*
|
||||
*/
|
||||
BBCC* CLG_(get_bbcc)(BB* bb)
|
||||
{
|
||||
BBCC* bbcc;
|
||||
|
||||
CLG_DEBUG(3, "+ get_bbcc(BB %#lx)\n", bb_addr(bb));
|
||||
|
||||
bbcc = bb->bbcc_list;
|
||||
|
||||
if (!bbcc) {
|
||||
bbcc = new_bbcc(bb);
|
||||
|
||||
/* initialize BBCC */
|
||||
bbcc->cxt = 0;
|
||||
bbcc->rec_array = 0;
|
||||
bbcc->rec_index = 0;
|
||||
|
||||
bbcc->next_bbcc = bb->bbcc_list;
|
||||
bb->bbcc_list = bbcc;
|
||||
bb->last_bbcc = bbcc;
|
||||
|
||||
CLG_DEBUGIF(3)
|
||||
CLG_(print_bbcc)(-2, bbcc);
|
||||
}
|
||||
|
||||
CLG_DEBUG(3, "- get_bbcc(BB %#lx): BBCC %p\n",
|
||||
bb_addr(bb), bbcc);
|
||||
|
||||
return bbcc;
|
||||
}
|
||||
|
||||
|
||||
/* Callgrind manages its own call stack for each thread.
|
||||
* When leaving a function, a underflow can happen when
|
||||
* Callgrind's tracing was switched on in the middle of
|
||||
* a run, i.e. when Callgrind was not able to trace the
|
||||
* call instruction.
|
||||
* This function tries to reconstruct the original call.
|
||||
* As we know the return address (the address following
|
||||
* the CALL instruction), we can detect the function
|
||||
* we return back to, but the original call site is unknown.
|
||||
* We suppose a call site at return address - 1.
|
||||
* (TODO: other heuristic: lookup info of instrumented BBs).
|
||||
*/
|
||||
static void handleUnderflow(BB* bb)
|
||||
{
|
||||
/* RET at top of call stack */
|
||||
BBCC* source_bbcc;
|
||||
BB* source_bb;
|
||||
Bool seen_before;
|
||||
fn_node* caller;
|
||||
int fn_number;
|
||||
unsigned *pactive;
|
||||
call_entry* call_entry_up;
|
||||
|
||||
CLG_DEBUG(1," Callstack underflow !\n");
|
||||
|
||||
/* we emulate an old call from the function we return to
|
||||
* by using (<return address> -1) */
|
||||
source_bb = CLG_(get_bb)(bb_addr(bb)-1, 0, &seen_before);
|
||||
source_bbcc = CLG_(get_bbcc)(source_bb);
|
||||
|
||||
/* seen_before can be true if RET from a signal handler */
|
||||
if (!seen_before) {
|
||||
source_bbcc->ecounter_sum = CLG_(current_state).collect ? 1 : 0;
|
||||
}
|
||||
else if (CLG_(current_state).collect)
|
||||
source_bbcc->ecounter_sum++;
|
||||
|
||||
/* Force a new top context, will be set active by push_cxt() */
|
||||
CLG_(current_fn_stack).top--;
|
||||
CLG_(current_state).cxt = 0;
|
||||
caller = CLG_(get_fn_node)(bb);
|
||||
CLG_(push_cxt)( caller );
|
||||
|
||||
if (!seen_before) {
|
||||
/* set rec array for source BBCC: this is at rec level 1 */
|
||||
source_bbcc->rec_array = new_recursion(caller->separate_recursions);
|
||||
source_bbcc->rec_array[0] = source_bbcc;
|
||||
|
||||
CLG_ASSERT(source_bbcc->cxt == 0);
|
||||
source_bbcc->cxt = CLG_(current_state).cxt;
|
||||
insert_bbcc_into_hash(source_bbcc);
|
||||
}
|
||||
CLG_ASSERT(CLG_(current_state).bbcc);
|
||||
|
||||
/* correct active counts */
|
||||
fn_number = CLG_(current_state).bbcc->cxt->fn[0]->number;
|
||||
pactive = CLG_(get_fn_entry)(fn_number);
|
||||
(*pactive)--;
|
||||
|
||||
/* This assertion is not correct for reentrant
|
||||
* signal handlers */
|
||||
/* CLG_ASSERT(*pactive == 0); */
|
||||
|
||||
CLG_(current_state).nonskipped = 0; /* we didn't skip this function */
|
||||
/* back to current context */
|
||||
CLG_(push_cxt)( CLG_(current_state).bbcc->cxt->fn[0] );
|
||||
CLG_(push_call_stack)(source_bbcc, 0, CLG_(current_state).bbcc,
|
||||
(Addr)-1, False);
|
||||
call_entry_up =
|
||||
&(CLG_(current_call_stack).entry[CLG_(current_call_stack).sp -1]);
|
||||
/* assume this call is lasting since last dump or
|
||||
* for a signal handler since it's call */
|
||||
if (CLG_(current_state).sig == 0)
|
||||
CLG_(copy_cost)( CLG_(sets).full, call_entry_up->enter_cost,
|
||||
CLG_(get_current_thread)()->lastdump_cost );
|
||||
else
|
||||
CLG_(zero_cost)( CLG_(sets).full, call_entry_up->enter_cost );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Helper function called at start of each instrumented BB to setup
|
||||
* pointer to costs for current thread/context/recursion level
|
||||
*/
|
||||
|
||||
VG_REGPARM(1)
|
||||
void CLG_(setup_bbcc)(BB* bb)
|
||||
{
|
||||
BBCC *bbcc, *last_bbcc;
|
||||
Bool call_emulation = False, delayed_push = False, skip = False;
|
||||
Addr sp;
|
||||
BB* last_bb;
|
||||
ThreadId tid;
|
||||
ClgJumpKind jmpkind;
|
||||
Bool isConditionalJump;
|
||||
Int passed = 0, csp;
|
||||
Bool ret_without_call = False;
|
||||
Int popcount_on_return = 1;
|
||||
|
||||
CLG_DEBUG(3,"+ setup_bbcc(BB %#lx)\n", bb_addr(bb));
|
||||
|
||||
/* This is needed because thread switches can not reliable be tracked
|
||||
* with callback CLG_(run_thread) only: we have otherwise no way to get
|
||||
* the thread ID after a signal handler returns.
|
||||
* This could be removed again if that bug is fixed in Valgrind.
|
||||
* This is in the hot path but hopefully not to costly.
|
||||
*/
|
||||
tid = VG_(get_running_tid)();
|
||||
#if 1
|
||||
/* CLG_(switch_thread) is a no-op when tid is equal to CLG_(current_tid).
|
||||
* As this is on the hot path, we only call CLG_(switch_thread)(tid)
|
||||
* if tid differs from the CLG_(current_tid).
|
||||
*/
|
||||
if (UNLIKELY(tid != CLG_(current_tid)))
|
||||
CLG_(switch_thread)(tid);
|
||||
#else
|
||||
CLG_ASSERT(VG_(get_running_tid)() == CLG_(current_tid));
|
||||
#endif
|
||||
|
||||
sp = VG_(get_SP)(tid);
|
||||
last_bbcc = CLG_(current_state).bbcc;
|
||||
last_bb = last_bbcc ? last_bbcc->bb : 0;
|
||||
|
||||
if (last_bb) {
|
||||
passed = CLG_(current_state).jmps_passed;
|
||||
CLG_ASSERT(passed <= last_bb->cjmp_count);
|
||||
jmpkind = last_bb->jmp[passed].jmpkind;
|
||||
isConditionalJump = (passed < last_bb->cjmp_count);
|
||||
|
||||
if (CLG_(current_state).collect) {
|
||||
if (!CLG_(current_state).nonskipped) {
|
||||
last_bbcc->ecounter_sum++;
|
||||
last_bbcc->jmp[passed].ecounter++;
|
||||
if (!CLG_(clo).simulate_cache) {
|
||||
/* update Ir cost */
|
||||
UInt instr_count = last_bb->jmp[passed].instr+1;
|
||||
CLG_(current_state).cost[ fullOffset(EG_IR) ] += instr_count;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* do not increment exe counter of BBs in skipped functions, as it
|
||||
* would fool dumping code */
|
||||
if (!CLG_(clo).simulate_cache) {
|
||||
/* update Ir cost */
|
||||
UInt instr_count = last_bb->jmp[passed].instr+1;
|
||||
CLG_(current_state).cost[ fullOffset(EG_IR) ] += instr_count;
|
||||
CLG_(current_state).nonskipped->skipped[ fullOffset(EG_IR) ]
|
||||
+= instr_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CLG_DEBUGIF(4) {
|
||||
CLG_(print_execstate)(-2, &CLG_(current_state) );
|
||||
CLG_(print_bbcc_cost)(-2, last_bbcc);
|
||||
}
|
||||
}
|
||||
else {
|
||||
jmpkind = jk_None;
|
||||
isConditionalJump = False;
|
||||
}
|
||||
|
||||
/* Manipulate JmpKind if needed, only using BB specific info */
|
||||
|
||||
csp = CLG_(current_call_stack).sp;
|
||||
|
||||
/* A return not matching the top call in our callstack is a jump */
|
||||
if ( (jmpkind == jk_Return) && (csp >0)) {
|
||||
Int csp_up = csp-1;
|
||||
call_entry* top_ce = &(CLG_(current_call_stack).entry[csp_up]);
|
||||
|
||||
/* We have a real return if
|
||||
* - the stack pointer (SP) left the current stack frame, or
|
||||
* - SP has the same value as when reaching the current function
|
||||
* and the address of this BB is the return address of last call
|
||||
* (we even allow to leave multiple frames if the SP stays the
|
||||
* same and we find a matching return address)
|
||||
* The latter condition is needed because on PPC, SP can stay
|
||||
* the same over CALL=b(c)l / RET=b(c)lr boundaries
|
||||
*/
|
||||
if (sp < top_ce->sp) popcount_on_return = 0;
|
||||
else if (top_ce->sp == sp) {
|
||||
while(1) {
|
||||
if (top_ce->ret_addr == bb_addr(bb)) break;
|
||||
if (csp_up>0) {
|
||||
csp_up--;
|
||||
top_ce = &(CLG_(current_call_stack).entry[csp_up]);
|
||||
if (top_ce->sp == sp) {
|
||||
popcount_on_return++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
popcount_on_return = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (popcount_on_return == 0) {
|
||||
jmpkind = jk_Jump;
|
||||
ret_without_call = True;
|
||||
}
|
||||
}
|
||||
|
||||
/* Should this jump be converted to call or pop/call ? */
|
||||
if (( jmpkind != jk_Return) &&
|
||||
( jmpkind != jk_Call) && last_bb) {
|
||||
|
||||
/* We simulate a JMP/Cont to be a CALL if
|
||||
* - jump is in another ELF object or section kind
|
||||
* - jump is to first instruction of a function (tail recursion)
|
||||
*/
|
||||
if (ret_without_call ||
|
||||
/* This is for detection of optimized tail recursion.
|
||||
* On PPC, this is only detected as call when going to another
|
||||
* function. The problem is that on PPC it can go wrong
|
||||
* more easily (no stack frame setup needed)
|
||||
*/
|
||||
#if defined(VGA_ppc32)
|
||||
(bb->is_entry && (last_bb->fn != bb->fn)) ||
|
||||
#else
|
||||
bb->is_entry ||
|
||||
#endif
|
||||
(last_bb->sect_kind != bb->sect_kind) ||
|
||||
(last_bb->obj->number != bb->obj->number)) {
|
||||
|
||||
CLG_DEBUG(1," JMP: %s[%s] to %s[%s]%s!\n",
|
||||
last_bb->fn->name, last_bb->obj->name,
|
||||
bb->fn->name, bb->obj->name,
|
||||
ret_without_call?" (RET w/o CALL)":"");
|
||||
|
||||
if (CLG_(get_fn_node)(last_bb)->pop_on_jump && (csp>0)) {
|
||||
|
||||
call_entry* top_ce = &(CLG_(current_call_stack).entry[csp-1]);
|
||||
|
||||
if (top_ce->jcc) {
|
||||
|
||||
CLG_DEBUG(1," Pop on Jump!\n");
|
||||
|
||||
/* change source for delayed push */
|
||||
CLG_(current_state).bbcc = top_ce->jcc->from;
|
||||
sp = top_ce->sp;
|
||||
passed = top_ce->jcc->jmp;
|
||||
CLG_(pop_call_stack)();
|
||||
}
|
||||
else {
|
||||
CLG_ASSERT(CLG_(current_state).nonskipped != 0);
|
||||
}
|
||||
}
|
||||
|
||||
jmpkind = jk_Call;
|
||||
call_emulation = True;
|
||||
}
|
||||
}
|
||||
|
||||
if (jmpkind == jk_Call)
|
||||
skip = CLG_(get_fn_node)(bb)->skip;
|
||||
|
||||
CLG_DEBUGIF(1) {
|
||||
if (isConditionalJump)
|
||||
VG_(printf)("Cond-");
|
||||
switch(jmpkind) {
|
||||
case jk_None: VG_(printf)("Fall-through"); break;
|
||||
case jk_Jump: VG_(printf)("Jump"); break;
|
||||
case jk_Call: VG_(printf)("Call"); break;
|
||||
case jk_Return: VG_(printf)("Return"); break;
|
||||
default: tl_assert(0);
|
||||
}
|
||||
VG_(printf)(" %08lx -> %08lx, SP %08lx\n",
|
||||
last_bb ? bb_jmpaddr(last_bb) : 0,
|
||||
bb_addr(bb), sp);
|
||||
}
|
||||
|
||||
/* Handle CALL/RET and update context to get correct BBCC */
|
||||
|
||||
if (jmpkind == jk_Return) {
|
||||
|
||||
if ((csp == 0) ||
|
||||
((CLG_(current_fn_stack).top > CLG_(current_fn_stack).bottom) &&
|
||||
( *(CLG_(current_fn_stack).top-1)==0)) ) {
|
||||
|
||||
/* On an empty call stack or at a signal separation marker,
|
||||
* a RETURN generates an call stack underflow.
|
||||
*/
|
||||
handleUnderflow(bb);
|
||||
CLG_(pop_call_stack)();
|
||||
}
|
||||
else {
|
||||
CLG_ASSERT(popcount_on_return >0);
|
||||
CLG_(unwind_call_stack)(sp, popcount_on_return);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Int unwind_count = CLG_(unwind_call_stack)(sp, 0);
|
||||
if (unwind_count > 0) {
|
||||
/* if unwinding was done, this actually is a return */
|
||||
jmpkind = jk_Return;
|
||||
}
|
||||
|
||||
if (jmpkind == jk_Call) {
|
||||
delayed_push = True;
|
||||
|
||||
csp = CLG_(current_call_stack).sp;
|
||||
if (call_emulation && csp>0)
|
||||
sp = CLG_(current_call_stack).entry[csp-1].sp;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Change new context if needed, taking delayed_push into account */
|
||||
if ((delayed_push && !skip) || (CLG_(current_state).cxt == 0)) {
|
||||
CLG_(push_cxt)(CLG_(get_fn_node)(bb));
|
||||
}
|
||||
CLG_ASSERT(CLG_(current_fn_stack).top > CLG_(current_fn_stack).bottom);
|
||||
|
||||
/* If there is a fresh instrumented BBCC, assign current context */
|
||||
bbcc = CLG_(get_bbcc)(bb);
|
||||
if (bbcc->cxt == 0) {
|
||||
CLG_ASSERT(bbcc->rec_array == 0);
|
||||
|
||||
bbcc->cxt = CLG_(current_state).cxt;
|
||||
bbcc->rec_array =
|
||||
new_recursion((*CLG_(current_fn_stack).top)->separate_recursions);
|
||||
bbcc->rec_array[0] = bbcc;
|
||||
|
||||
insert_bbcc_into_hash(bbcc);
|
||||
}
|
||||
else {
|
||||
/* get BBCC with current context */
|
||||
|
||||
/* first check LRU of last bbcc executed */
|
||||
|
||||
if (last_bbcc) {
|
||||
bbcc = last_bbcc->lru_next_bbcc;
|
||||
if (bbcc &&
|
||||
((bbcc->bb != bb) ||
|
||||
(bbcc->cxt != CLG_(current_state).cxt)))
|
||||
bbcc = 0;
|
||||
}
|
||||
else
|
||||
bbcc = 0;
|
||||
|
||||
if (!bbcc)
|
||||
bbcc = lookup_bbcc(bb, CLG_(current_state).cxt);
|
||||
if (!bbcc)
|
||||
bbcc = clone_bbcc(bb->bbcc_list, CLG_(current_state).cxt, 0);
|
||||
|
||||
bb->last_bbcc = bbcc;
|
||||
}
|
||||
|
||||
/* save for fast lookup */
|
||||
if (last_bbcc)
|
||||
last_bbcc->lru_next_bbcc = bbcc;
|
||||
|
||||
if ((*CLG_(current_fn_stack).top)->separate_recursions >1) {
|
||||
UInt level, idx;
|
||||
fn_node* top = *(CLG_(current_fn_stack).top);
|
||||
|
||||
level = *CLG_(get_fn_entry)(top->number);
|
||||
|
||||
if (delayed_push && !skip) {
|
||||
if (CLG_(clo).skip_direct_recursion) {
|
||||
/* a call was detected, which means that the source BB != 0 */
|
||||
CLG_ASSERT(CLG_(current_state).bbcc != 0);
|
||||
/* only increment rec. level if called from different function */
|
||||
if (CLG_(current_state).bbcc->cxt->fn[0] != bbcc->cxt->fn[0])
|
||||
level++;
|
||||
}
|
||||
else level++;
|
||||
}
|
||||
if (level> top->separate_recursions)
|
||||
level = top->separate_recursions;
|
||||
|
||||
if (level == 0) {
|
||||
/* can only happen if instrumentation just was switched on */
|
||||
level = 1;
|
||||
*CLG_(get_fn_entry)(top->number) = 1;
|
||||
}
|
||||
|
||||
idx = level -1;
|
||||
if (bbcc->rec_array[idx])
|
||||
bbcc = bbcc->rec_array[idx];
|
||||
else
|
||||
bbcc = clone_bbcc(bbcc, CLG_(current_state).cxt, idx);
|
||||
|
||||
CLG_ASSERT(bbcc->rec_array[bbcc->rec_index] == bbcc);
|
||||
}
|
||||
|
||||
if (delayed_push) {
|
||||
if (!skip && CLG_(current_state).nonskipped) {
|
||||
/* a call from skipped to nonskipped */
|
||||
CLG_(current_state).bbcc = CLG_(current_state).nonskipped;
|
||||
/* FIXME: take the real passed count from shadow stack */
|
||||
passed = CLG_(current_state).bbcc->bb->cjmp_count;
|
||||
}
|
||||
CLG_(push_call_stack)(CLG_(current_state).bbcc, passed,
|
||||
bbcc, sp, skip);
|
||||
}
|
||||
|
||||
if (CLG_(clo).collect_jumps && (jmpkind == jk_Jump)) {
|
||||
|
||||
/* Handle conditional jumps followed, i.e. trace arcs
|
||||
* This uses JCC structures, too */
|
||||
|
||||
jCC* jcc = CLG_(get_jcc)(last_bbcc, passed, bbcc);
|
||||
CLG_ASSERT(jcc != 0);
|
||||
// Change from default, and check if already changed
|
||||
if (jcc->jmpkind == jk_Call)
|
||||
jcc->jmpkind = isConditionalJump ? jk_CondJump : jk_Jump;
|
||||
else {
|
||||
// FIXME: Why can this fail?
|
||||
// CLG_ASSERT(jcc->jmpkind == jmpkind);
|
||||
}
|
||||
|
||||
jcc->call_counter++;
|
||||
if (isConditionalJump)
|
||||
CLG_(stat).jcnd_counter++;
|
||||
else
|
||||
CLG_(stat).jump_counter++;
|
||||
}
|
||||
|
||||
CLG_(current_state).bbcc = bbcc;
|
||||
/* Even though this will be set in instrumented code directly before
|
||||
* side exits, it needs to be set to 0 here in case an exception
|
||||
* happens in first instructions of the BB */
|
||||
CLG_(current_state).jmps_passed = 0;
|
||||
// needed for log_* handlers called in this BB
|
||||
CLG_(bb_base) = bb->obj->offset + bb->offset;
|
||||
CLG_(cost_base) = bbcc->cost;
|
||||
|
||||
CLG_DEBUGIF(1) {
|
||||
VG_(printf)(" ");
|
||||
CLG_(print_bbcc_fn)(bbcc);
|
||||
VG_(printf)("\n");
|
||||
}
|
||||
|
||||
CLG_DEBUG(3,"- setup_bbcc (BB %#lx): Cost %p (Len %u), Instrs %u (Len %u)\n",
|
||||
bb_addr(bb), bbcc->cost, bb->cost_count,
|
||||
bb->instr_count, bb->instr_len);
|
||||
CLG_DEBUGIF(3)
|
||||
CLG_(print_cxt)(-8, CLG_(current_state).cxt, bbcc->rec_index);
|
||||
CLG_DEBUG(3,"\n");
|
||||
|
||||
CLG_(stat).bb_executions++;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,129 @@
|
||||
|
||||
/*
|
||||
----------------------------------------------------------------
|
||||
|
||||
Notice that the following BSD-style license applies to this one
|
||||
file (callgrind.h) only. The rest of Valgrind is licensed under the
|
||||
terms of the GNU General Public License, version 2, unless
|
||||
otherwise indicated. See the COPYING file in the source
|
||||
distribution for details.
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
This file is part of callgrind, a valgrind tool for cache simulation
|
||||
and call tree tracing.
|
||||
|
||||
Copyright (C) 2003-2015 Josef Weidendorfer. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product
|
||||
documentation would be appreciated but is not required.
|
||||
|
||||
3. Altered source versions must be plainly marked as such, and must
|
||||
not be misrepresented as being the original software.
|
||||
|
||||
4. The name of the author may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
Notice that the above BSD-style license applies to this one file
|
||||
(callgrind.h) only. The entire rest of Valgrind is licensed under
|
||||
the terms of the GNU General Public License, version 2. See the
|
||||
COPYING file in the source distribution for details.
|
||||
|
||||
----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __CALLGRIND_H
|
||||
#define __CALLGRIND_H
|
||||
|
||||
#include "valgrind.h"
|
||||
|
||||
/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
|
||||
This enum comprises an ABI exported by Valgrind to programs
|
||||
which use client requests. DO NOT CHANGE THE ORDER OF THESE
|
||||
ENTRIES, NOR DELETE ANY -- add new ones at the end.
|
||||
|
||||
The identification ('C','T') for Callgrind has historical
|
||||
reasons: it was called "Calltree" before. Besides, ('C','G') would
|
||||
clash with cachegrind.
|
||||
*/
|
||||
|
||||
typedef
|
||||
enum {
|
||||
VG_USERREQ__DUMP_STATS = VG_USERREQ_TOOL_BASE('C','T'),
|
||||
VG_USERREQ__ZERO_STATS,
|
||||
VG_USERREQ__TOGGLE_COLLECT,
|
||||
VG_USERREQ__DUMP_STATS_AT,
|
||||
VG_USERREQ__START_INSTRUMENTATION,
|
||||
VG_USERREQ__STOP_INSTRUMENTATION
|
||||
} Vg_CallgrindClientRequest;
|
||||
|
||||
/* Dump current state of cost centers, and zero them afterwards */
|
||||
#define CALLGRIND_DUMP_STATS \
|
||||
VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DUMP_STATS, \
|
||||
0, 0, 0, 0, 0)
|
||||
|
||||
/* Dump current state of cost centers, and zero them afterwards.
|
||||
The argument is appended to a string stating the reason which triggered
|
||||
the dump. This string is written as a description field into the
|
||||
profile data dump. */
|
||||
#define CALLGRIND_DUMP_STATS_AT(pos_str) \
|
||||
VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DUMP_STATS_AT, \
|
||||
pos_str, 0, 0, 0, 0)
|
||||
|
||||
/* Zero cost centers */
|
||||
#define CALLGRIND_ZERO_STATS \
|
||||
VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__ZERO_STATS, \
|
||||
0, 0, 0, 0, 0)
|
||||
|
||||
/* Toggles collection state.
|
||||
The collection state specifies whether the happening of events
|
||||
should be noted or if they are to be ignored. Events are noted
|
||||
by increment of counters in a cost center */
|
||||
#define CALLGRIND_TOGGLE_COLLECT \
|
||||
VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__TOGGLE_COLLECT, \
|
||||
0, 0, 0, 0, 0)
|
||||
|
||||
/* Start full callgrind instrumentation if not already switched on.
|
||||
When cache simulation is done, it will flush the simulated cache;
|
||||
this will lead to an artifical cache warmup phase afterwards with
|
||||
cache misses which would not have happened in reality. */
|
||||
#define CALLGRIND_START_INSTRUMENTATION \
|
||||
VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__START_INSTRUMENTATION, \
|
||||
0, 0, 0, 0, 0)
|
||||
|
||||
/* Stop full callgrind instrumentation if not already switched off.
|
||||
This flushes Valgrinds translation cache, and does no additional
|
||||
instrumentation afterwards, which effectivly will run at the same
|
||||
speed as the "none" tool (ie. at minimal slowdown).
|
||||
Use this to bypass Callgrind aggregation for uninteresting code parts.
|
||||
To start Callgrind in this mode to ignore the setup phase, use
|
||||
the option "--instr-atstart=no". */
|
||||
#define CALLGRIND_STOP_INSTRUMENTATION \
|
||||
VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STOP_INSTRUMENTATION, \
|
||||
0, 0, 0, 0, 0)
|
||||
|
||||
#endif /* __CALLGRIND_H */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,489 @@
|
||||
#! /usr/bin/perl -w
|
||||
##--------------------------------------------------------------------##
|
||||
##--- Control supervision of applications run with callgrind ---##
|
||||
##--- callgrind_control ---##
|
||||
##--------------------------------------------------------------------##
|
||||
|
||||
# This file is part of Callgrind, a cache-simulator and call graph
|
||||
# tracer built on Valgrind.
|
||||
#
|
||||
# Copyright (C) 2003-2015 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
# 02111-1307, USA.
|
||||
|
||||
sub getCallgrindPids {
|
||||
|
||||
@pids = ();
|
||||
open LIST, "vgdb $vgdbPrefixOption -l|";
|
||||
while(<LIST>) {
|
||||
if (/^use --pid=(\d+) for \S*?valgrind\s+(.*?)\s*$/) {
|
||||
$pid = $1;
|
||||
$cmd = $2;
|
||||
if (!($cmd =~ /--tool=callgrind/)) { next; }
|
||||
while($cmd =~ s/^-+\S+\s+//) {}
|
||||
$cmdline{$pid} = $cmd;
|
||||
$cmd =~ s/^(\S*).*/$1/;
|
||||
$cmd{$pid} = $cmd;
|
||||
#print "Found PID $pid, cmd '$cmd{$pid}', cmdline '$cmdline{$pid}'.\n";
|
||||
push(@pids, $pid);
|
||||
}
|
||||
}
|
||||
close LIST;
|
||||
}
|
||||
|
||||
sub printHeader {
|
||||
if ($headerPrinted) { return; }
|
||||
$headerPrinted = 1;
|
||||
|
||||
print "Observe the status and control currently active callgrind runs.\n";
|
||||
print "(C) 2003-2011, Josef Weidendorfer (Josef.Weidendorfer\@gmx.de)\n\n";
|
||||
}
|
||||
|
||||
sub printVersion {
|
||||
print "callgrind_control-3.11.0\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
sub shortHelp {
|
||||
print "See '$0 -h' for help.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
sub printHelp {
|
||||
printHeader;
|
||||
|
||||
print "Usage: callgrind_control [options] [pid|program-name...]\n\n";
|
||||
print "If no pids/names are given, an action is applied to all currently\n";
|
||||
print "active Callgrind runs. Default action is printing short information.\n\n";
|
||||
print "Options:\n";
|
||||
print " -h --help Show this help text\n";
|
||||
print " --version Show version\n";
|
||||
print " -s --stat Show statistics\n";
|
||||
print " -b --back Show stack/back trace\n";
|
||||
print " -e [<A>,...] Show event counters for <A>,... (default: all)\n";
|
||||
print " --dump[=<s>] Request a dump optionally using <s> as description\n";
|
||||
print " -z --zero Zero all event counters\n";
|
||||
print " -k --kill Kill\n";
|
||||
print " -i --instr=on|off Switch instrumentation state on/off\n";
|
||||
print "Uncommon options:\n";
|
||||
print " --vgdb-prefix=<prefix> Only provide this if the same was given to Valgrind\n";
|
||||
print "\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Parts more or less copied from cg_annotate (author: Nicholas Nethercote)
|
||||
#
|
||||
|
||||
sub prepareEvents {
|
||||
|
||||
@events = split(/\s+/, $events);
|
||||
%events = ();
|
||||
$n = 0;
|
||||
foreach $event (@events) {
|
||||
$events{$event} = $n;
|
||||
$n++;
|
||||
}
|
||||
if (@show_events) {
|
||||
foreach my $show_event (@show_events) {
|
||||
(defined $events{$show_event}) or
|
||||
print "Warning: Event `$show_event' is not being collected\n";
|
||||
}
|
||||
} else {
|
||||
@show_events = @events;
|
||||
}
|
||||
@show_order = ();
|
||||
foreach my $show_event (@show_events) {
|
||||
push(@show_order, $events{$show_event});
|
||||
}
|
||||
}
|
||||
|
||||
sub max ($$)
|
||||
{
|
||||
my ($x, $y) = @_;
|
||||
return ($x > $y ? $x : $y);
|
||||
}
|
||||
|
||||
sub line_to_CC ($)
|
||||
{
|
||||
my @CC = (split /\s+/, $_[0]);
|
||||
(@CC <= @events) or die("Line $.: too many event counts\n");
|
||||
return \@CC;
|
||||
}
|
||||
|
||||
sub commify ($) {
|
||||
my ($val) = @_;
|
||||
1 while ($val =~ s/^(\d+)(\d{3})/$1,$2/);
|
||||
return $val;
|
||||
}
|
||||
|
||||
sub compute_CC_col_widths (@)
|
||||
{
|
||||
my @CCs = @_;
|
||||
my $CC_col_widths = [];
|
||||
|
||||
# Initialise with minimum widths (from event names)
|
||||
foreach my $event (@events) {
|
||||
push(@$CC_col_widths, length($event));
|
||||
}
|
||||
|
||||
# Find maximum width count for each column. @CC_col_width positions
|
||||
# correspond to @CC positions.
|
||||
foreach my $CC (@CCs) {
|
||||
foreach my $i (0 .. scalar(@$CC)-1) {
|
||||
if (defined $CC->[$i]) {
|
||||
# Find length, accounting for commas that will be added
|
||||
my $length = length $CC->[$i];
|
||||
my $clength = $length + int(($length - 1) / 3);
|
||||
$CC_col_widths->[$i] = max($CC_col_widths->[$i], $clength);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $CC_col_widths;
|
||||
}
|
||||
|
||||
# Print the CC with each column's size dictated by $CC_col_widths.
|
||||
sub print_CC ($$)
|
||||
{
|
||||
my ($CC, $CC_col_widths) = @_;
|
||||
|
||||
foreach my $i (@show_order) {
|
||||
my $count = (defined $CC->[$i] ? commify($CC->[$i]) : ".");
|
||||
my $space = ' ' x ($CC_col_widths->[$i] - length($count));
|
||||
print("$space$count ");
|
||||
}
|
||||
}
|
||||
|
||||
sub print_events ($)
|
||||
{
|
||||
my ($CC_col_widths) = @_;
|
||||
|
||||
foreach my $i (@show_order) {
|
||||
my $event = $events[$i];
|
||||
my $event_width = length($event);
|
||||
my $col_width = $CC_col_widths->[$i];
|
||||
my $space = ' ' x ($col_width - $event_width);
|
||||
print("$space$event ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Main
|
||||
#
|
||||
|
||||
# To find the list of active pids, we need to have
|
||||
# the --vgdb-prefix option if given.
|
||||
$vgdbPrefixOption = "";
|
||||
foreach $arg (@ARGV) {
|
||||
if ($arg =~ /^--vgdb-prefix=.*$/) {
|
||||
$vgdbPrefixOption=$arg;
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
getCallgrindPids;
|
||||
|
||||
$requestEvents = 0;
|
||||
$requestDump = 0;
|
||||
$switchInstr = 0;
|
||||
$headerPrinted = 0;
|
||||
$dumpHint = "";
|
||||
|
||||
$verbose = 0;
|
||||
|
||||
%spids = ();
|
||||
foreach $arg (@ARGV) {
|
||||
if ($arg =~ /^-/) {
|
||||
if ($requestDump == 1) { $requestDump = 2; }
|
||||
if ($requestEvents == 1) { $requestEvents = 2; }
|
||||
|
||||
if ($arg =~ /^(-h|--help)$/) {
|
||||
printHelp;
|
||||
}
|
||||
elsif ($arg =~ /^--version$/) {
|
||||
printVersion;
|
||||
}
|
||||
elsif ($arg =~ /^--vgdb-prefix=.*$/) {
|
||||
# handled during the initial parsing.
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^-v$/) {
|
||||
$verbose++;
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^(-s|--stat)$/) {
|
||||
$printStatus = 1;
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^(-b|--back)$/) {
|
||||
$printBacktrace = 1;
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^-e$/) {
|
||||
$requestEvents = 1;
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^(-d|--dump)(|=.*)$/) {
|
||||
if ($2 ne "") {
|
||||
$requestDump = 2;
|
||||
$dumpHint = substr($2,1);
|
||||
}
|
||||
else {
|
||||
# take next argument as dump hint
|
||||
$requestDump = 1;
|
||||
}
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^(-z|--zero)$/) {
|
||||
$requestZero = 1;
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^(-k|--kill)$/) {
|
||||
$requestKill = 1;
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^(-i|--instr)(|=on|=off)$/) {
|
||||
$switchInstr = 2;
|
||||
if ($2 eq "=on") {
|
||||
$switchInstrMode = "on";
|
||||
}
|
||||
elsif ($2 eq "=off") {
|
||||
$switchInstrMode = "off";
|
||||
}
|
||||
else {
|
||||
# check next argument for "on" or "off"
|
||||
$switchInstr = 1;
|
||||
}
|
||||
next;
|
||||
}
|
||||
else {
|
||||
print "Error: unknown command line option '$arg'.\n";
|
||||
shortHelp;
|
||||
}
|
||||
}
|
||||
|
||||
if ($arg =~ /^[A-Za-z_]/) {
|
||||
# arguments of -d/-e/-i are non-numeric
|
||||
if ($requestDump == 1) {
|
||||
$requestDump = 2;
|
||||
$dumpHint = $arg;
|
||||
next;
|
||||
}
|
||||
|
||||
if ($requestEvents == 1) {
|
||||
$requestEvents = 2;
|
||||
@show_events = split(/,/, $arg);
|
||||
next;
|
||||
}
|
||||
|
||||
if ($switchInstr == 1) {
|
||||
$switchInstr = 2;
|
||||
if ($arg eq "on") {
|
||||
$switchInstrMode = "on";
|
||||
}
|
||||
elsif ($arg eq "off") {
|
||||
$switchInstrMode = "off";
|
||||
}
|
||||
else {
|
||||
print "Error: need to specify 'on' or 'off' after '-i'.\n";
|
||||
shortHelp;
|
||||
}
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $cmd{$arg}) { $spids{$arg} = 1; next; }
|
||||
$nameFound = 0;
|
||||
foreach $p (@pids) {
|
||||
if ($cmd{$p} =~ /$arg$/) {
|
||||
$nameFound = 1;
|
||||
$spids{$p} = 1;
|
||||
}
|
||||
}
|
||||
if ($nameFound) { next; }
|
||||
|
||||
print "Error: Callgrind task with PID/name '$arg' not detected.\n";
|
||||
shortHelp;
|
||||
}
|
||||
|
||||
|
||||
if ($switchInstr == 1) {
|
||||
print "Error: need to specify 'on' or 'off' after '-i'.\n";
|
||||
shortHelp;
|
||||
}
|
||||
|
||||
if (scalar @pids == 0) {
|
||||
print "No active callgrind runs detected.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
@spids = keys %spids;
|
||||
if (scalar @spids >0) { @pids = @spids; }
|
||||
|
||||
$vgdbCommand = "";
|
||||
$waitForAnswer = 0;
|
||||
if ($requestDump) {
|
||||
$vgdbCommand = "dump";
|
||||
if ($dumpHint ne "") { $vgdbCommand .= " ".$dumpHint; }
|
||||
}
|
||||
if ($requestZero) { $vgdbCommand = "zero"; }
|
||||
if ($requestKill) { $vgdbCommand = "v.kill"; }
|
||||
if ($switchInstr) { $vgdbCommand = "instrumentation ".$switchInstrMode; }
|
||||
if ($printStatus || $printBacktrace || $requestEvents) {
|
||||
$vgdbCommand = "status internal";
|
||||
$waitForAnswer = 1;
|
||||
}
|
||||
|
||||
foreach $pid (@pids) {
|
||||
$pidstr = "PID $pid: ";
|
||||
if ($pid >0) { print $pidstr.$cmdline{$pid}; }
|
||||
|
||||
if ($vgdbCommand eq "") {
|
||||
print "\n";
|
||||
next;
|
||||
}
|
||||
if ($verbose>0) {
|
||||
print " [requesting '$vgdbCommand']\n";
|
||||
} else {
|
||||
print "\n";
|
||||
}
|
||||
open RESULT, "vgdb $vgdbPrefixOption --pid=$pid $vgdbCommand|";
|
||||
|
||||
@tids = ();
|
||||
$ctid = 0;
|
||||
%fcount = ();
|
||||
%func = ();
|
||||
%calls = ();
|
||||
%events = ();
|
||||
@events = ();
|
||||
@threads = ();
|
||||
%totals = ();
|
||||
|
||||
$exec_bbs = 0;
|
||||
$dist_bbs = 0;
|
||||
$exec_calls = 0;
|
||||
$dist_calls = 0;
|
||||
$dist_ctxs = 0;
|
||||
$dist_funcs = 0;
|
||||
$threads = "";
|
||||
$events = "";
|
||||
|
||||
while(<RESULT>) {
|
||||
if (/function-(\d+)-(\d+): (.+)$/) {
|
||||
if ($ctid != $1) {
|
||||
$ctid = $1;
|
||||
push(@tids, $ctid);
|
||||
$fcount{$ctid} = 0;
|
||||
}
|
||||
$fcount{$ctid}++;
|
||||
$func{$ctid,$fcount{$ctid}} = $3;
|
||||
}
|
||||
elsif (/calls-(\d+)-(\d+): (.+)$/) {
|
||||
if ($ctid != $1) { next; }
|
||||
$calls{$ctid,$fcount{$ctid}} = $3;
|
||||
}
|
||||
elsif (/events-(\d+)-(\d+): (.+)$/) {
|
||||
if ($ctid != $1) { next; }
|
||||
$events{$ctid,$fcount{$ctid}} = line_to_CC($3);
|
||||
}
|
||||
elsif (/events-(\d+): (.+)$/) {
|
||||
if (scalar @events == 0) { next; }
|
||||
$totals{$1} = line_to_CC($2);
|
||||
}
|
||||
elsif (/executed-bbs: (\d+)/) { $exec_bbs = $1; }
|
||||
elsif (/distinct-bbs: (\d+)/) { $dist_bbs = $1; }
|
||||
elsif (/executed-calls: (\d+)/) { $exec_calls = $1; }
|
||||
elsif (/distinct-calls: (\d+)/) { $dist_calls = $1; }
|
||||
elsif (/distinct-functions: (\d+)/) { $dist_funcs = $1; }
|
||||
elsif (/distinct-contexts: (\d+)/) { $dist_ctxs = $1; }
|
||||
elsif (/events: (.+)$/) { $events = $1; prepareEvents; }
|
||||
elsif (/threads: (.+)$/) { $threads = $1; @threads = split " ", $threads; }
|
||||
elsif (/instrumentation: (\w+)$/) { $instrumentation = $1; }
|
||||
}
|
||||
|
||||
#if ($? ne "0") { print " Got Error $?\n"; }
|
||||
if (!$waitForAnswer) { print " OK.\n"; next; }
|
||||
|
||||
if ($instrumentation eq "off") {
|
||||
print " No information available as instrumentation is switched off.\n\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($printStatus) {
|
||||
if ($requestEvents <1) {
|
||||
print " Number of running threads: " .($#threads+1). ", thread IDs: $threads\n";
|
||||
print " Events collected: $events\n";
|
||||
}
|
||||
|
||||
print " Functions: ".commify($dist_funcs);
|
||||
print " (executed ".commify($exec_calls);
|
||||
print ", contexts ".commify($dist_ctxs).")\n";
|
||||
|
||||
print " Basic blocks: ".commify($dist_bbs);
|
||||
print " (executed ".commify($exec_bbs);
|
||||
print ", call sites ".commify($dist_calls).")\n";
|
||||
}
|
||||
|
||||
if ($requestEvents >0) {
|
||||
$totals_width = compute_CC_col_widths(values %totals);
|
||||
print "\n Totals:";
|
||||
print_events($totals_width);
|
||||
print("\n");
|
||||
foreach $tid (@tids) {
|
||||
print " Th".substr(" ".$tid,-2)." ";
|
||||
print_CC($totals{$tid}, $totals_width);
|
||||
print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if ($printBacktrace) {
|
||||
|
||||
if ($requestEvents >0) {
|
||||
$totals_width = compute_CC_col_widths(values %events);
|
||||
}
|
||||
|
||||
foreach $tid (@tids) {
|
||||
print "\n Frame: ";
|
||||
if ($requestEvents >0) {
|
||||
print_events($totals_width);
|
||||
}
|
||||
print "Backtrace for Thread $tid\n";
|
||||
|
||||
$i = $fcount{$tid};
|
||||
$c = 0;
|
||||
while($i>0 && $c<100) {
|
||||
$fc = substr(" $c",-2);
|
||||
print " [$fc] ";
|
||||
if ($requestEvents >0) {
|
||||
print_CC($events{$tid,$i-1}, $totals_width);
|
||||
}
|
||||
print $func{$tid,$i};
|
||||
if ($i > 1) {
|
||||
print " (".$calls{$tid,$i-1}." x)";
|
||||
}
|
||||
print "\n";
|
||||
$i--;
|
||||
$c++;
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,489 @@
|
||||
#! /usr/bin/perl -w
|
||||
##--------------------------------------------------------------------##
|
||||
##--- Control supervision of applications run with callgrind ---##
|
||||
##--- callgrind_control ---##
|
||||
##--------------------------------------------------------------------##
|
||||
|
||||
# This file is part of Callgrind, a cache-simulator and call graph
|
||||
# tracer built on Valgrind.
|
||||
#
|
||||
# Copyright (C) 2003-2015 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
# 02111-1307, USA.
|
||||
|
||||
sub getCallgrindPids {
|
||||
|
||||
@pids = ();
|
||||
open LIST, "vgdb $vgdbPrefixOption -l|";
|
||||
while(<LIST>) {
|
||||
if (/^use --pid=(\d+) for \S*?valgrind\s+(.*?)\s*$/) {
|
||||
$pid = $1;
|
||||
$cmd = $2;
|
||||
if (!($cmd =~ /--tool=callgrind/)) { next; }
|
||||
while($cmd =~ s/^-+\S+\s+//) {}
|
||||
$cmdline{$pid} = $cmd;
|
||||
$cmd =~ s/^(\S*).*/$1/;
|
||||
$cmd{$pid} = $cmd;
|
||||
#print "Found PID $pid, cmd '$cmd{$pid}', cmdline '$cmdline{$pid}'.\n";
|
||||
push(@pids, $pid);
|
||||
}
|
||||
}
|
||||
close LIST;
|
||||
}
|
||||
|
||||
sub printHeader {
|
||||
if ($headerPrinted) { return; }
|
||||
$headerPrinted = 1;
|
||||
|
||||
print "Observe the status and control currently active callgrind runs.\n";
|
||||
print "(C) 2003-2011, Josef Weidendorfer (Josef.Weidendorfer\@gmx.de)\n\n";
|
||||
}
|
||||
|
||||
sub printVersion {
|
||||
print "callgrind_control-@VERSION@\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
sub shortHelp {
|
||||
print "See '$0 -h' for help.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
sub printHelp {
|
||||
printHeader;
|
||||
|
||||
print "Usage: callgrind_control [options] [pid|program-name...]\n\n";
|
||||
print "If no pids/names are given, an action is applied to all currently\n";
|
||||
print "active Callgrind runs. Default action is printing short information.\n\n";
|
||||
print "Options:\n";
|
||||
print " -h --help Show this help text\n";
|
||||
print " --version Show version\n";
|
||||
print " -s --stat Show statistics\n";
|
||||
print " -b --back Show stack/back trace\n";
|
||||
print " -e [<A>,...] Show event counters for <A>,... (default: all)\n";
|
||||
print " --dump[=<s>] Request a dump optionally using <s> as description\n";
|
||||
print " -z --zero Zero all event counters\n";
|
||||
print " -k --kill Kill\n";
|
||||
print " -i --instr=on|off Switch instrumentation state on/off\n";
|
||||
print "Uncommon options:\n";
|
||||
print " --vgdb-prefix=<prefix> Only provide this if the same was given to Valgrind\n";
|
||||
print "\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Parts more or less copied from cg_annotate (author: Nicholas Nethercote)
|
||||
#
|
||||
|
||||
sub prepareEvents {
|
||||
|
||||
@events = split(/\s+/, $events);
|
||||
%events = ();
|
||||
$n = 0;
|
||||
foreach $event (@events) {
|
||||
$events{$event} = $n;
|
||||
$n++;
|
||||
}
|
||||
if (@show_events) {
|
||||
foreach my $show_event (@show_events) {
|
||||
(defined $events{$show_event}) or
|
||||
print "Warning: Event `$show_event' is not being collected\n";
|
||||
}
|
||||
} else {
|
||||
@show_events = @events;
|
||||
}
|
||||
@show_order = ();
|
||||
foreach my $show_event (@show_events) {
|
||||
push(@show_order, $events{$show_event});
|
||||
}
|
||||
}
|
||||
|
||||
sub max ($$)
|
||||
{
|
||||
my ($x, $y) = @_;
|
||||
return ($x > $y ? $x : $y);
|
||||
}
|
||||
|
||||
sub line_to_CC ($)
|
||||
{
|
||||
my @CC = (split /\s+/, $_[0]);
|
||||
(@CC <= @events) or die("Line $.: too many event counts\n");
|
||||
return \@CC;
|
||||
}
|
||||
|
||||
sub commify ($) {
|
||||
my ($val) = @_;
|
||||
1 while ($val =~ s/^(\d+)(\d{3})/$1,$2/);
|
||||
return $val;
|
||||
}
|
||||
|
||||
sub compute_CC_col_widths (@)
|
||||
{
|
||||
my @CCs = @_;
|
||||
my $CC_col_widths = [];
|
||||
|
||||
# Initialise with minimum widths (from event names)
|
||||
foreach my $event (@events) {
|
||||
push(@$CC_col_widths, length($event));
|
||||
}
|
||||
|
||||
# Find maximum width count for each column. @CC_col_width positions
|
||||
# correspond to @CC positions.
|
||||
foreach my $CC (@CCs) {
|
||||
foreach my $i (0 .. scalar(@$CC)-1) {
|
||||
if (defined $CC->[$i]) {
|
||||
# Find length, accounting for commas that will be added
|
||||
my $length = length $CC->[$i];
|
||||
my $clength = $length + int(($length - 1) / 3);
|
||||
$CC_col_widths->[$i] = max($CC_col_widths->[$i], $clength);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $CC_col_widths;
|
||||
}
|
||||
|
||||
# Print the CC with each column's size dictated by $CC_col_widths.
|
||||
sub print_CC ($$)
|
||||
{
|
||||
my ($CC, $CC_col_widths) = @_;
|
||||
|
||||
foreach my $i (@show_order) {
|
||||
my $count = (defined $CC->[$i] ? commify($CC->[$i]) : ".");
|
||||
my $space = ' ' x ($CC_col_widths->[$i] - length($count));
|
||||
print("$space$count ");
|
||||
}
|
||||
}
|
||||
|
||||
sub print_events ($)
|
||||
{
|
||||
my ($CC_col_widths) = @_;
|
||||
|
||||
foreach my $i (@show_order) {
|
||||
my $event = $events[$i];
|
||||
my $event_width = length($event);
|
||||
my $col_width = $CC_col_widths->[$i];
|
||||
my $space = ' ' x ($col_width - $event_width);
|
||||
print("$space$event ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Main
|
||||
#
|
||||
|
||||
# To find the list of active pids, we need to have
|
||||
# the --vgdb-prefix option if given.
|
||||
$vgdbPrefixOption = "";
|
||||
foreach $arg (@ARGV) {
|
||||
if ($arg =~ /^--vgdb-prefix=.*$/) {
|
||||
$vgdbPrefixOption=$arg;
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
getCallgrindPids;
|
||||
|
||||
$requestEvents = 0;
|
||||
$requestDump = 0;
|
||||
$switchInstr = 0;
|
||||
$headerPrinted = 0;
|
||||
$dumpHint = "";
|
||||
|
||||
$verbose = 0;
|
||||
|
||||
%spids = ();
|
||||
foreach $arg (@ARGV) {
|
||||
if ($arg =~ /^-/) {
|
||||
if ($requestDump == 1) { $requestDump = 2; }
|
||||
if ($requestEvents == 1) { $requestEvents = 2; }
|
||||
|
||||
if ($arg =~ /^(-h|--help)$/) {
|
||||
printHelp;
|
||||
}
|
||||
elsif ($arg =~ /^--version$/) {
|
||||
printVersion;
|
||||
}
|
||||
elsif ($arg =~ /^--vgdb-prefix=.*$/) {
|
||||
# handled during the initial parsing.
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^-v$/) {
|
||||
$verbose++;
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^(-s|--stat)$/) {
|
||||
$printStatus = 1;
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^(-b|--back)$/) {
|
||||
$printBacktrace = 1;
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^-e$/) {
|
||||
$requestEvents = 1;
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^(-d|--dump)(|=.*)$/) {
|
||||
if ($2 ne "") {
|
||||
$requestDump = 2;
|
||||
$dumpHint = substr($2,1);
|
||||
}
|
||||
else {
|
||||
# take next argument as dump hint
|
||||
$requestDump = 1;
|
||||
}
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^(-z|--zero)$/) {
|
||||
$requestZero = 1;
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^(-k|--kill)$/) {
|
||||
$requestKill = 1;
|
||||
next;
|
||||
}
|
||||
elsif ($arg =~ /^(-i|--instr)(|=on|=off)$/) {
|
||||
$switchInstr = 2;
|
||||
if ($2 eq "=on") {
|
||||
$switchInstrMode = "on";
|
||||
}
|
||||
elsif ($2 eq "=off") {
|
||||
$switchInstrMode = "off";
|
||||
}
|
||||
else {
|
||||
# check next argument for "on" or "off"
|
||||
$switchInstr = 1;
|
||||
}
|
||||
next;
|
||||
}
|
||||
else {
|
||||
print "Error: unknown command line option '$arg'.\n";
|
||||
shortHelp;
|
||||
}
|
||||
}
|
||||
|
||||
if ($arg =~ /^[A-Za-z_]/) {
|
||||
# arguments of -d/-e/-i are non-numeric
|
||||
if ($requestDump == 1) {
|
||||
$requestDump = 2;
|
||||
$dumpHint = $arg;
|
||||
next;
|
||||
}
|
||||
|
||||
if ($requestEvents == 1) {
|
||||
$requestEvents = 2;
|
||||
@show_events = split(/,/, $arg);
|
||||
next;
|
||||
}
|
||||
|
||||
if ($switchInstr == 1) {
|
||||
$switchInstr = 2;
|
||||
if ($arg eq "on") {
|
||||
$switchInstrMode = "on";
|
||||
}
|
||||
elsif ($arg eq "off") {
|
||||
$switchInstrMode = "off";
|
||||
}
|
||||
else {
|
||||
print "Error: need to specify 'on' or 'off' after '-i'.\n";
|
||||
shortHelp;
|
||||
}
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $cmd{$arg}) { $spids{$arg} = 1; next; }
|
||||
$nameFound = 0;
|
||||
foreach $p (@pids) {
|
||||
if ($cmd{$p} =~ /$arg$/) {
|
||||
$nameFound = 1;
|
||||
$spids{$p} = 1;
|
||||
}
|
||||
}
|
||||
if ($nameFound) { next; }
|
||||
|
||||
print "Error: Callgrind task with PID/name '$arg' not detected.\n";
|
||||
shortHelp;
|
||||
}
|
||||
|
||||
|
||||
if ($switchInstr == 1) {
|
||||
print "Error: need to specify 'on' or 'off' after '-i'.\n";
|
||||
shortHelp;
|
||||
}
|
||||
|
||||
if (scalar @pids == 0) {
|
||||
print "No active callgrind runs detected.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
@spids = keys %spids;
|
||||
if (scalar @spids >0) { @pids = @spids; }
|
||||
|
||||
$vgdbCommand = "";
|
||||
$waitForAnswer = 0;
|
||||
if ($requestDump) {
|
||||
$vgdbCommand = "dump";
|
||||
if ($dumpHint ne "") { $vgdbCommand .= " ".$dumpHint; }
|
||||
}
|
||||
if ($requestZero) { $vgdbCommand = "zero"; }
|
||||
if ($requestKill) { $vgdbCommand = "v.kill"; }
|
||||
if ($switchInstr) { $vgdbCommand = "instrumentation ".$switchInstrMode; }
|
||||
if ($printStatus || $printBacktrace || $requestEvents) {
|
||||
$vgdbCommand = "status internal";
|
||||
$waitForAnswer = 1;
|
||||
}
|
||||
|
||||
foreach $pid (@pids) {
|
||||
$pidstr = "PID $pid: ";
|
||||
if ($pid >0) { print $pidstr.$cmdline{$pid}; }
|
||||
|
||||
if ($vgdbCommand eq "") {
|
||||
print "\n";
|
||||
next;
|
||||
}
|
||||
if ($verbose>0) {
|
||||
print " [requesting '$vgdbCommand']\n";
|
||||
} else {
|
||||
print "\n";
|
||||
}
|
||||
open RESULT, "vgdb $vgdbPrefixOption --pid=$pid $vgdbCommand|";
|
||||
|
||||
@tids = ();
|
||||
$ctid = 0;
|
||||
%fcount = ();
|
||||
%func = ();
|
||||
%calls = ();
|
||||
%events = ();
|
||||
@events = ();
|
||||
@threads = ();
|
||||
%totals = ();
|
||||
|
||||
$exec_bbs = 0;
|
||||
$dist_bbs = 0;
|
||||
$exec_calls = 0;
|
||||
$dist_calls = 0;
|
||||
$dist_ctxs = 0;
|
||||
$dist_funcs = 0;
|
||||
$threads = "";
|
||||
$events = "";
|
||||
|
||||
while(<RESULT>) {
|
||||
if (/function-(\d+)-(\d+): (.+)$/) {
|
||||
if ($ctid != $1) {
|
||||
$ctid = $1;
|
||||
push(@tids, $ctid);
|
||||
$fcount{$ctid} = 0;
|
||||
}
|
||||
$fcount{$ctid}++;
|
||||
$func{$ctid,$fcount{$ctid}} = $3;
|
||||
}
|
||||
elsif (/calls-(\d+)-(\d+): (.+)$/) {
|
||||
if ($ctid != $1) { next; }
|
||||
$calls{$ctid,$fcount{$ctid}} = $3;
|
||||
}
|
||||
elsif (/events-(\d+)-(\d+): (.+)$/) {
|
||||
if ($ctid != $1) { next; }
|
||||
$events{$ctid,$fcount{$ctid}} = line_to_CC($3);
|
||||
}
|
||||
elsif (/events-(\d+): (.+)$/) {
|
||||
if (scalar @events == 0) { next; }
|
||||
$totals{$1} = line_to_CC($2);
|
||||
}
|
||||
elsif (/executed-bbs: (\d+)/) { $exec_bbs = $1; }
|
||||
elsif (/distinct-bbs: (\d+)/) { $dist_bbs = $1; }
|
||||
elsif (/executed-calls: (\d+)/) { $exec_calls = $1; }
|
||||
elsif (/distinct-calls: (\d+)/) { $dist_calls = $1; }
|
||||
elsif (/distinct-functions: (\d+)/) { $dist_funcs = $1; }
|
||||
elsif (/distinct-contexts: (\d+)/) { $dist_ctxs = $1; }
|
||||
elsif (/events: (.+)$/) { $events = $1; prepareEvents; }
|
||||
elsif (/threads: (.+)$/) { $threads = $1; @threads = split " ", $threads; }
|
||||
elsif (/instrumentation: (\w+)$/) { $instrumentation = $1; }
|
||||
}
|
||||
|
||||
#if ($? ne "0") { print " Got Error $?\n"; }
|
||||
if (!$waitForAnswer) { print " OK.\n"; next; }
|
||||
|
||||
if ($instrumentation eq "off") {
|
||||
print " No information available as instrumentation is switched off.\n\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($printStatus) {
|
||||
if ($requestEvents <1) {
|
||||
print " Number of running threads: " .($#threads+1). ", thread IDs: $threads\n";
|
||||
print " Events collected: $events\n";
|
||||
}
|
||||
|
||||
print " Functions: ".commify($dist_funcs);
|
||||
print " (executed ".commify($exec_calls);
|
||||
print ", contexts ".commify($dist_ctxs).")\n";
|
||||
|
||||
print " Basic blocks: ".commify($dist_bbs);
|
||||
print " (executed ".commify($exec_bbs);
|
||||
print ", call sites ".commify($dist_calls).")\n";
|
||||
}
|
||||
|
||||
if ($requestEvents >0) {
|
||||
$totals_width = compute_CC_col_widths(values %totals);
|
||||
print "\n Totals:";
|
||||
print_events($totals_width);
|
||||
print("\n");
|
||||
foreach $tid (@tids) {
|
||||
print " Th".substr(" ".$tid,-2)." ";
|
||||
print_CC($totals{$tid}, $totals_width);
|
||||
print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if ($printBacktrace) {
|
||||
|
||||
if ($requestEvents >0) {
|
||||
$totals_width = compute_CC_col_widths(values %events);
|
||||
}
|
||||
|
||||
foreach $tid (@tids) {
|
||||
print "\n Frame: ";
|
||||
if ($requestEvents >0) {
|
||||
print_events($totals_width);
|
||||
}
|
||||
print "Backtrace for Thread $tid\n";
|
||||
|
||||
$i = $fcount{$tid};
|
||||
$c = 0;
|
||||
while($i>0 && $c<100) {
|
||||
$fc = substr(" $c",-2);
|
||||
print " [$fc] ";
|
||||
if ($requestEvents >0) {
|
||||
print_CC($events{$tid,$i-1}, $totals_width);
|
||||
}
|
||||
print $func{$tid,$i};
|
||||
if ($i > 1) {
|
||||
print " (".$calls{$tid,$i-1}." x)";
|
||||
}
|
||||
print "\n";
|
||||
$i--;
|
||||
$c++;
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,437 @@
|
||||
/*--------------------------------------------------------------------*/
|
||||
/*--- Callgrind ---*/
|
||||
/*--- ct_callstack.c ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
This file is part of Callgrind, a Valgrind tool for call tracing.
|
||||
|
||||
Copyright (C) 2002-2015, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The GNU General Public License is contained in the file COPYING.
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Call stack, operations ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
/* Stack of current thread. Gets initialized when switching to 1st thread.
|
||||
*
|
||||
* The artificial call stack is an array of call_entry's, representing
|
||||
* stack frames of the executing program.
|
||||
* Array call_stack and call_stack_esp have same size and grow on demand.
|
||||
* Array call_stack_esp holds SPs of corresponding stack frames.
|
||||
*
|
||||
*/
|
||||
|
||||
#define N_CALL_STACK_INITIAL_ENTRIES 500
|
||||
|
||||
call_stack CLG_(current_call_stack);
|
||||
|
||||
void CLG_(init_call_stack)(call_stack* s)
|
||||
{
|
||||
Int i;
|
||||
|
||||
CLG_ASSERT(s != 0);
|
||||
|
||||
s->size = N_CALL_STACK_INITIAL_ENTRIES;
|
||||
s->entry = (call_entry*) CLG_MALLOC("cl.callstack.ics.1",
|
||||
s->size * sizeof(call_entry));
|
||||
s->sp = 0;
|
||||
s->entry[0].cxt = 0; /* for assertion in push_cxt() */
|
||||
|
||||
for(i=0; i<s->size; i++) s->entry[i].enter_cost = 0;
|
||||
}
|
||||
|
||||
call_entry* CLG_(get_call_entry)(Int sp)
|
||||
{
|
||||
CLG_ASSERT(sp <= CLG_(current_call_stack).sp);
|
||||
return &(CLG_(current_call_stack).entry[sp]);
|
||||
}
|
||||
|
||||
void CLG_(copy_current_call_stack)(call_stack* dst)
|
||||
{
|
||||
CLG_ASSERT(dst != 0);
|
||||
|
||||
dst->size = CLG_(current_call_stack).size;
|
||||
dst->entry = CLG_(current_call_stack).entry;
|
||||
dst->sp = CLG_(current_call_stack).sp;
|
||||
}
|
||||
|
||||
void CLG_(set_current_call_stack)(call_stack* s)
|
||||
{
|
||||
CLG_ASSERT(s != 0);
|
||||
|
||||
CLG_(current_call_stack).size = s->size;
|
||||
CLG_(current_call_stack).entry = s->entry;
|
||||
CLG_(current_call_stack).sp = s->sp;
|
||||
}
|
||||
|
||||
|
||||
static __inline__
|
||||
void ensure_stack_size(Int i)
|
||||
{
|
||||
Int oldsize;
|
||||
call_stack *cs = &CLG_(current_call_stack);
|
||||
|
||||
if (i < cs->size) return;
|
||||
|
||||
oldsize = cs->size;
|
||||
cs->size *= 2;
|
||||
while (i > cs->size) cs->size *= 2;
|
||||
|
||||
cs->entry = (call_entry*) VG_(realloc)("cl.callstack.ess.1",
|
||||
cs->entry,
|
||||
cs->size * sizeof(call_entry));
|
||||
|
||||
for(i=oldsize; i<cs->size; i++)
|
||||
cs->entry[i].enter_cost = 0;
|
||||
|
||||
CLG_(stat).call_stack_resizes++;
|
||||
|
||||
CLG_DEBUGIF(2)
|
||||
VG_(printf)(" call stack enlarged to %u entries\n",
|
||||
CLG_(current_call_stack).size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Called when function entered nonrecursive */
|
||||
static void function_entered(fn_node* fn)
|
||||
{
|
||||
CLG_ASSERT(fn != 0);
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
if (fn->verbosity >=0) {
|
||||
Int old = CLG_(clo).verbose;
|
||||
CLG_(clo).verbose = fn->verbosity;
|
||||
fn->verbosity = old;
|
||||
VG_(message)(Vg_DebugMsg,
|
||||
"Entering %s: Verbosity set to %d\n",
|
||||
fn->name, CLG_(clo).verbose);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fn->dump_before) {
|
||||
HChar trigger[VG_(strlen)(fn->name) + 20];
|
||||
VG_(sprintf)(trigger, "--dump-before=%s", fn->name);
|
||||
CLG_(dump_profile)(trigger, True);
|
||||
}
|
||||
else if (fn->zero_before) {
|
||||
CLG_(zero_all_cost)(True);
|
||||
}
|
||||
|
||||
if (fn->toggle_collect) {
|
||||
CLG_(current_state).collect = !CLG_(current_state).collect;
|
||||
CLG_DEBUG(2," entering %s: toggled collection state to %s\n",
|
||||
fn->name,
|
||||
CLG_(current_state).collect ? "ON" : "OFF");
|
||||
}
|
||||
}
|
||||
|
||||
/* Called when function left (no recursive level active) */
|
||||
static void function_left(fn_node* fn)
|
||||
{
|
||||
CLG_ASSERT(fn != 0);
|
||||
|
||||
if (fn->dump_after) {
|
||||
HChar trigger[VG_(strlen)(fn->name) + 20];
|
||||
VG_(sprintf)(trigger, "--dump-after=%s", fn->name);
|
||||
CLG_(dump_profile)(trigger, True);
|
||||
}
|
||||
if (fn->toggle_collect) {
|
||||
CLG_(current_state).collect = !CLG_(current_state).collect;
|
||||
CLG_DEBUG(2," leaving %s: toggled collection state to %s\n",
|
||||
fn->name,
|
||||
CLG_(current_state).collect ? "ON" : "OFF");
|
||||
}
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
if (fn->verbosity >=0) {
|
||||
Int old = CLG_(clo).verbose;
|
||||
CLG_(clo).verbose = fn->verbosity;
|
||||
fn->verbosity = old;
|
||||
VG_(message)(Vg_DebugMsg,
|
||||
"Leaving %s: Verbosity set back to %d\n",
|
||||
fn->name, CLG_(clo).verbose);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Push call on call stack.
|
||||
*
|
||||
* Increment the usage count for the function called.
|
||||
* A jump from <from> to <to>, with <sp>.
|
||||
* If <skip> is true, this is a call to a function to be skipped;
|
||||
* for this, we set jcc = 0.
|
||||
*/
|
||||
void CLG_(push_call_stack)(BBCC* from, UInt jmp, BBCC* to, Addr sp, Bool skip)
|
||||
{
|
||||
jCC* jcc;
|
||||
UInt* pdepth;
|
||||
call_entry* current_entry;
|
||||
Addr ret_addr;
|
||||
|
||||
/* Ensure a call stack of size <current_sp>+1.
|
||||
* The +1 is needed as push_cxt will store the
|
||||
* context at [current_sp]
|
||||
*/
|
||||
ensure_stack_size(CLG_(current_call_stack).sp +1);
|
||||
current_entry = &(CLG_(current_call_stack).entry[CLG_(current_call_stack).sp]);
|
||||
|
||||
if (skip) {
|
||||
jcc = 0;
|
||||
}
|
||||
else {
|
||||
fn_node* to_fn = to->cxt->fn[0];
|
||||
|
||||
if (CLG_(current_state).nonskipped) {
|
||||
/* this is a jmp from skipped to nonskipped */
|
||||
CLG_ASSERT(CLG_(current_state).nonskipped == from);
|
||||
}
|
||||
|
||||
/* As push_cxt() has to be called before push_call_stack if not
|
||||
* skipping, the old context should already be saved on the stack */
|
||||
CLG_ASSERT(current_entry->cxt != 0);
|
||||
CLG_(copy_cost_lz)( CLG_(sets).full, &(current_entry->enter_cost),
|
||||
CLG_(current_state).cost );
|
||||
|
||||
jcc = CLG_(get_jcc)(from, jmp, to);
|
||||
CLG_ASSERT(jcc != 0);
|
||||
|
||||
pdepth = CLG_(get_fn_entry)(to_fn->number);
|
||||
if (CLG_(clo).skip_direct_recursion) {
|
||||
/* only increment depth if another function is called */
|
||||
if (jcc->from->cxt->fn[0] != to_fn) (*pdepth)++;
|
||||
}
|
||||
else (*pdepth)++;
|
||||
|
||||
if (*pdepth>1)
|
||||
CLG_(stat).rec_call_counter++;
|
||||
|
||||
jcc->call_counter++;
|
||||
CLG_(stat).call_counter++;
|
||||
|
||||
if (*pdepth == 1) function_entered(to_fn);
|
||||
}
|
||||
|
||||
/* return address is only is useful with a real call;
|
||||
* used to detect RET w/o CALL */
|
||||
if (from->bb->jmp[jmp].jmpkind == jk_Call) {
|
||||
UInt instr = from->bb->jmp[jmp].instr;
|
||||
ret_addr = bb_addr(from->bb) +
|
||||
from->bb->instr[instr].instr_offset +
|
||||
from->bb->instr[instr].instr_size;
|
||||
}
|
||||
else
|
||||
ret_addr = 0;
|
||||
|
||||
/* put jcc on call stack */
|
||||
current_entry->jcc = jcc;
|
||||
current_entry->sp = sp;
|
||||
current_entry->ret_addr = ret_addr;
|
||||
current_entry->nonskipped = CLG_(current_state).nonskipped;
|
||||
|
||||
CLG_(current_call_stack).sp++;
|
||||
|
||||
/* To allow for above assertion we set context of next frame to 0 */
|
||||
CLG_ASSERT(CLG_(current_call_stack).sp < CLG_(current_call_stack).size);
|
||||
current_entry++;
|
||||
current_entry->cxt = 0;
|
||||
|
||||
if (!skip)
|
||||
CLG_(current_state).nonskipped = 0;
|
||||
else if (!CLG_(current_state).nonskipped) {
|
||||
/* a call from nonskipped to skipped */
|
||||
CLG_(current_state).nonskipped = from;
|
||||
if (!CLG_(current_state).nonskipped->skipped) {
|
||||
CLG_(init_cost_lz)( CLG_(sets).full,
|
||||
&CLG_(current_state).nonskipped->skipped);
|
||||
CLG_(stat).distinct_skips++;
|
||||
}
|
||||
}
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
CLG_DEBUGIF(0) {
|
||||
if (CLG_(clo).verbose<2) {
|
||||
if (jcc && jcc->to && jcc->to->bb) {
|
||||
const HChar spaces[][41] = {
|
||||
" . . . . . . . . . .",
|
||||
" . . . . . . . . . . ",
|
||||
" . . . . . . . . . . ",
|
||||
". . . . . . . . . . " };
|
||||
|
||||
int s = CLG_(current_call_stack).sp;
|
||||
UInt* pars = (UInt*) sp;
|
||||
|
||||
BB* bb = jcc->to->bb;
|
||||
if (s>40) s=40;
|
||||
VG_(printf)("%s> %s(0x%x, 0x%x, ...) [%s / %#lx]\n", spaces[s%4]+40-s, bb->fn->name,
|
||||
pars ? pars[1]:0,
|
||||
pars ? pars[2]:0,
|
||||
bb->obj->name + bb->obj->last_slash_pos,
|
||||
(UWord)bb->offset);
|
||||
}
|
||||
}
|
||||
else if (CLG_(clo).verbose<4) {
|
||||
VG_(printf)("+ %2d ", CLG_(current_call_stack).sp);
|
||||
CLG_(print_short_jcc)(jcc);
|
||||
VG_(printf)(", SP %#lx, RA %#lx\n", sp, ret_addr);
|
||||
}
|
||||
else {
|
||||
VG_(printf)(" Pushed ");
|
||||
CLG_(print_stackentry)(3, CLG_(current_call_stack).sp-1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Pop call stack and update inclusive sums.
|
||||
* Returns modified fcc.
|
||||
*
|
||||
* If the JCC becomes inactive, call entries are freed if possible
|
||||
*/
|
||||
void CLG_(pop_call_stack)()
|
||||
{
|
||||
jCC* jcc;
|
||||
Int depth = 0;
|
||||
call_entry* lower_entry;
|
||||
|
||||
if (CLG_(current_state).sig >0) {
|
||||
/* Check if we leave a signal handler; this can happen when
|
||||
* calling longjmp() in the handler */
|
||||
CLG_(run_post_signal_on_call_stack_bottom)();
|
||||
}
|
||||
|
||||
lower_entry =
|
||||
&(CLG_(current_call_stack).entry[CLG_(current_call_stack).sp-1]);
|
||||
|
||||
CLG_DEBUG(4,"+ pop_call_stack: frame %d, jcc %p\n",
|
||||
CLG_(current_call_stack).sp, lower_entry->jcc);
|
||||
|
||||
/* jCC item not any more on real stack: pop */
|
||||
jcc = lower_entry->jcc;
|
||||
CLG_(current_state).nonskipped = lower_entry->nonskipped;
|
||||
|
||||
if (jcc) {
|
||||
fn_node* to_fn = jcc->to->cxt->fn[0];
|
||||
UInt* pdepth = CLG_(get_fn_entry)(to_fn->number);
|
||||
if (CLG_(clo).skip_direct_recursion) {
|
||||
/* only decrement depth if another function was called */
|
||||
if (jcc->from->cxt->fn[0] != to_fn) (*pdepth)--;
|
||||
}
|
||||
else (*pdepth)--;
|
||||
depth = *pdepth;
|
||||
|
||||
/* add cost difference to sum */
|
||||
if ( CLG_(add_diff_cost_lz)( CLG_(sets).full, &(jcc->cost),
|
||||
lower_entry->enter_cost,
|
||||
CLG_(current_state).cost) ) {
|
||||
|
||||
/* only count this call if it attributed some cost.
|
||||
* the ret_counter is used to check if a BBCC dump is needed.
|
||||
*/
|
||||
jcc->from->ret_counter++;
|
||||
}
|
||||
CLG_(stat).ret_counter++;
|
||||
|
||||
/* restore context */
|
||||
CLG_(current_state).cxt = lower_entry->cxt;
|
||||
CLG_(current_fn_stack).top =
|
||||
CLG_(current_fn_stack).bottom + lower_entry->fn_sp;
|
||||
CLG_ASSERT(CLG_(current_state).cxt != 0);
|
||||
|
||||
if (depth == 0) function_left(to_fn);
|
||||
}
|
||||
|
||||
/* To allow for an assertion in push_call_stack() */
|
||||
lower_entry->cxt = 0;
|
||||
|
||||
CLG_(current_call_stack).sp--;
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
CLG_DEBUGIF(1) {
|
||||
if (CLG_(clo).verbose<4) {
|
||||
if (jcc) {
|
||||
/* popped JCC target first */
|
||||
VG_(printf)("- %2d %#lx => ",
|
||||
CLG_(current_call_stack).sp,
|
||||
bb_addr(jcc->to->bb));
|
||||
CLG_(print_addr)(bb_jmpaddr(jcc->from->bb));
|
||||
VG_(printf)(", SP %#lx\n",
|
||||
CLG_(current_call_stack).entry[CLG_(current_call_stack).sp].sp);
|
||||
CLG_(print_cost)(10, CLG_(sets).full, jcc->cost);
|
||||
}
|
||||
else
|
||||
VG_(printf)("- %2d [Skipped JCC], SP %#lx\n",
|
||||
CLG_(current_call_stack).sp,
|
||||
CLG_(current_call_stack).entry[CLG_(current_call_stack).sp].sp);
|
||||
}
|
||||
else {
|
||||
VG_(printf)(" Popped ");
|
||||
CLG_(print_stackentry)(7, CLG_(current_call_stack).sp);
|
||||
if (jcc) {
|
||||
VG_(printf)(" returned to ");
|
||||
CLG_(print_addr_ln)(bb_jmpaddr(jcc->from->bb));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Unwind enough CallStack items to sync with current stack pointer.
|
||||
* Returns the number of stack frames unwinded.
|
||||
*/
|
||||
Int CLG_(unwind_call_stack)(Addr sp, Int minpops)
|
||||
{
|
||||
Int csp;
|
||||
Int unwind_count = 0;
|
||||
CLG_DEBUG(4,"+ unwind_call_stack(sp %#lx, minpops %d): frame %d\n",
|
||||
sp, minpops, CLG_(current_call_stack).sp);
|
||||
|
||||
/* We pop old stack frames.
|
||||
* For a call, be p the stack address with return address.
|
||||
* - call_stack_esp[] has SP after the CALL: p-4
|
||||
* - current sp is after a RET: >= p
|
||||
*/
|
||||
|
||||
while( (csp=CLG_(current_call_stack).sp) >0) {
|
||||
call_entry* top_ce = &(CLG_(current_call_stack).entry[csp-1]);
|
||||
|
||||
if ((top_ce->sp < sp) ||
|
||||
((top_ce->sp == sp) && minpops>0)) {
|
||||
|
||||
minpops--;
|
||||
unwind_count++;
|
||||
CLG_(pop_call_stack)();
|
||||
csp=CLG_(current_call_stack).sp;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
CLG_DEBUG(4,"- unwind_call_stack\n");
|
||||
return unwind_count;
|
||||
}
|
||||
@@ -0,0 +1,671 @@
|
||||
/*
|
||||
This file is part of Callgrind, a Valgrind tool for call graph
|
||||
profiling programs.
|
||||
|
||||
Copyright (C) 2002-2015, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
|
||||
|
||||
This tool is derived from and contains lot of code from Cachegrind
|
||||
Copyright (C) 2002-2015 Nicholas Nethercote (njn@valgrind.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The GNU General Public License is contained in the file COPYING.
|
||||
*/
|
||||
|
||||
#include "config.h" // for VG_PREFIX
|
||||
|
||||
#include "global.h"
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Function specific configuration options ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
/* Special value for separate_callers: automatic = adaptive */
|
||||
#define CONFIG_AUTO -1
|
||||
|
||||
#define CONFIG_DEFAULT -1
|
||||
#define CONFIG_FALSE 0
|
||||
#define CONFIG_TRUE 1
|
||||
|
||||
/* Logging configuration for a function */
|
||||
struct _fn_config {
|
||||
Int dump_before;
|
||||
Int dump_after;
|
||||
Int zero_before;
|
||||
Int toggle_collect;
|
||||
|
||||
Int skip; /* Handle CALL to this function as JMP (= Skip)? */
|
||||
Int group; /* don't change caller dependency inside group !=0 */
|
||||
Int pop_on_jump;
|
||||
|
||||
Int separate_callers; /* separate logging dependent on caller */
|
||||
Int separate_recursions; /* separate logging of rec. levels */
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
Int verbosity; /* Change debug verbosity level while in function */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Configurations for function name prefix patterns.
|
||||
* Currently, only very limit patterns are possible:
|
||||
* Exact prefix patterns and "*::" are allowed.
|
||||
* E.g.
|
||||
* - "abc" matches all functions starting with "abc".
|
||||
* - "abc*::def" matches all functions starting with "abc" and
|
||||
* starting with "def" after the first "::" separator.
|
||||
* - "*::print(" matches C++ methods "print" in all classes
|
||||
* without namespace. I.e. "*" doesn't match a "::".
|
||||
*
|
||||
* We build a trie from patterns, and for a given function, we
|
||||
* go down the tree and apply all non-default configurations.
|
||||
*/
|
||||
|
||||
|
||||
#define NODE_DEGREE 30
|
||||
|
||||
/* node of compressed trie search structure */
|
||||
typedef struct _config_node config_node;
|
||||
struct _config_node {
|
||||
Int length;
|
||||
|
||||
fn_config* config;
|
||||
config_node* sub_node[NODE_DEGREE];
|
||||
config_node* next;
|
||||
config_node* wild_star;
|
||||
config_node* wild_char;
|
||||
|
||||
HChar name[1];
|
||||
};
|
||||
|
||||
/* root of trie */
|
||||
static config_node* fn_configs = 0;
|
||||
|
||||
static __inline__
|
||||
fn_config* new_fnc(void)
|
||||
{
|
||||
fn_config* fnc = (fn_config*) CLG_MALLOC("cl.clo.nf.1",
|
||||
sizeof(fn_config));
|
||||
|
||||
fnc->dump_before = CONFIG_DEFAULT;
|
||||
fnc->dump_after = CONFIG_DEFAULT;
|
||||
fnc->zero_before = CONFIG_DEFAULT;
|
||||
fnc->toggle_collect = CONFIG_DEFAULT;
|
||||
fnc->skip = CONFIG_DEFAULT;
|
||||
fnc->pop_on_jump = CONFIG_DEFAULT;
|
||||
fnc->group = CONFIG_DEFAULT;
|
||||
fnc->separate_callers = CONFIG_DEFAULT;
|
||||
fnc->separate_recursions = CONFIG_DEFAULT;
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
fnc->verbosity = CONFIG_DEFAULT;
|
||||
#endif
|
||||
|
||||
return fnc;
|
||||
}
|
||||
|
||||
|
||||
static config_node* new_config(const HChar* name, int length)
|
||||
{
|
||||
int i;
|
||||
config_node* node = (config_node*) CLG_MALLOC("cl.clo.nc.1",
|
||||
sizeof(config_node) + length);
|
||||
|
||||
for(i=0;i<length;i++) {
|
||||
if (name[i] == 0) break;
|
||||
node->name[i] = name[i];
|
||||
}
|
||||
node->name[i] = 0;
|
||||
|
||||
node->length = length;
|
||||
node->config = 0;
|
||||
for(i=0;i<NODE_DEGREE;i++)
|
||||
node->sub_node[i] = 0;
|
||||
node->next = 0;
|
||||
node->wild_char = 0;
|
||||
node->wild_star = 0;
|
||||
|
||||
CLG_DEBUG(3, " new_config('%s', len %d)\n", node->name, length);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static __inline__
|
||||
Bool is_wild(HChar n)
|
||||
{
|
||||
return (n == '*') || (n == '?');
|
||||
}
|
||||
|
||||
/* Recursively build up function matching tree (prefix tree).
|
||||
* Returns function config object for pattern <name>
|
||||
* and starting at tree node <*pnode>.
|
||||
*
|
||||
* Tree nodes (config_node) are created as needed,
|
||||
* tree root is stored into <*pnode>, and the created
|
||||
* leaf (fn_config) for the given pattern is returned.
|
||||
*/
|
||||
static fn_config* get_fnc2(config_node* node, const HChar* name)
|
||||
{
|
||||
config_node *new_sub, *n, *nprev;
|
||||
int offset, len;
|
||||
|
||||
CLG_DEBUG(3, " get_fnc2(%p, '%s')\n", node, name);
|
||||
|
||||
if (name[0] == 0) {
|
||||
if (!node->config) node->config = new_fnc();
|
||||
return node->config;
|
||||
}
|
||||
|
||||
if (is_wild(*name)) {
|
||||
if (*name == '*') {
|
||||
while(name[1] == '*') name++;
|
||||
new_sub = node->wild_star;
|
||||
}
|
||||
else
|
||||
new_sub = node->wild_char;
|
||||
|
||||
if (!new_sub) {
|
||||
new_sub = new_config(name, 1);
|
||||
if (*name == '*')
|
||||
node->wild_star = new_sub;
|
||||
else
|
||||
node->wild_char = new_sub;
|
||||
}
|
||||
|
||||
return get_fnc2( new_sub, name+1);
|
||||
}
|
||||
|
||||
n = node->sub_node[ name[0]%NODE_DEGREE ];
|
||||
nprev = 0;
|
||||
len = 0;
|
||||
while(n) {
|
||||
for(len=0; name[len] == n->name[len]; len++);
|
||||
if (len>0) break;
|
||||
nprev = n;
|
||||
n = n->next;
|
||||
}
|
||||
|
||||
if (!n) {
|
||||
len = 1;
|
||||
while(name[len] && (!is_wild(name[len]))) len++;
|
||||
new_sub = new_config(name, len);
|
||||
new_sub->next = node->sub_node[ name[0]%NODE_DEGREE ];
|
||||
node->sub_node[ name[0]%NODE_DEGREE ] = new_sub;
|
||||
|
||||
if (name[len] == 0) {
|
||||
new_sub->config = new_fnc();
|
||||
return new_sub->config;
|
||||
}
|
||||
|
||||
/* recurse on wildcard */
|
||||
return get_fnc2( new_sub, name+len);
|
||||
}
|
||||
|
||||
if (len < n->length) {
|
||||
|
||||
/* split up the subnode <n> */
|
||||
config_node *new_node;
|
||||
int i;
|
||||
|
||||
new_node = new_config(n->name, len);
|
||||
if (nprev)
|
||||
nprev->next = new_node;
|
||||
else
|
||||
node->sub_node[ n->name[0]%NODE_DEGREE ] = new_node;
|
||||
new_node->next = n->next;
|
||||
|
||||
new_node->sub_node[ n->name[len]%NODE_DEGREE ] = n;
|
||||
|
||||
for(i=0, offset=len; offset < n->length; i++, offset++)
|
||||
n->name[i] = n->name[offset];
|
||||
n->name[i] = 0;
|
||||
n->length = i;
|
||||
|
||||
name += len;
|
||||
offset = 0;
|
||||
while(name[offset] && (!is_wild(name[offset]))) offset++;
|
||||
new_sub = new_config(name, offset);
|
||||
/* this sub_node of new_node could already be set: chain! */
|
||||
new_sub->next = new_node->sub_node[ name[0]%NODE_DEGREE ];
|
||||
new_node->sub_node[ name[0]%NODE_DEGREE ] = new_sub;
|
||||
|
||||
if (name[offset]==0) {
|
||||
new_sub->config = new_fnc();
|
||||
return new_sub->config;
|
||||
}
|
||||
|
||||
/* recurse on wildcard */
|
||||
return get_fnc2( new_sub, name+offset);
|
||||
}
|
||||
|
||||
name += n->length;
|
||||
|
||||
if (name[0] == 0) {
|
||||
/* name and node name are the same */
|
||||
if (!n->config) n->config = new_fnc();
|
||||
return n->config;
|
||||
}
|
||||
|
||||
offset = 1;
|
||||
while(name[offset] && (!is_wild(name[offset]))) offset++;
|
||||
|
||||
new_sub = new_config(name, offset);
|
||||
new_sub->next = n->sub_node[ name[0]%NODE_DEGREE ];
|
||||
n->sub_node[ name[0]%NODE_DEGREE ] = new_sub;
|
||||
|
||||
return get_fnc2(new_sub, name+offset);
|
||||
}
|
||||
|
||||
static void print_config_node(int depth, int hash, config_node* node)
|
||||
{
|
||||
config_node* n;
|
||||
int i;
|
||||
|
||||
if (node != fn_configs) {
|
||||
const HChar sp[] = " ";
|
||||
|
||||
if (depth>40) depth=40;
|
||||
VG_(printf)("%s", sp+40-depth);
|
||||
if (hash >=0) VG_(printf)(" [hash %2d]", hash);
|
||||
else if (hash == -2) VG_(printf)(" [wildc ?]");
|
||||
else if (hash == -3) VG_(printf)(" [wildc *]");
|
||||
VG_(printf)(" '%s' (len %d)\n", node->name, node->length);
|
||||
}
|
||||
for(i=0;i<NODE_DEGREE;i++) {
|
||||
n = node->sub_node[i];
|
||||
while(n) {
|
||||
print_config_node(depth+1, i, n);
|
||||
n = n->next;
|
||||
}
|
||||
}
|
||||
if (node->wild_char) print_config_node(depth+1, -2, node->wild_char);
|
||||
if (node->wild_star) print_config_node(depth+1, -3, node->wild_star);
|
||||
}
|
||||
|
||||
/* get a function config for a name pattern (from command line) */
|
||||
static fn_config* get_fnc(const HChar* name)
|
||||
{
|
||||
fn_config* fnc;
|
||||
|
||||
CLG_DEBUG(3, " +get_fnc(%s)\n", name);
|
||||
if (fn_configs == 0)
|
||||
fn_configs = new_config(name, 0);
|
||||
fnc = get_fnc2(fn_configs, name);
|
||||
|
||||
CLG_DEBUGIF(3) {
|
||||
CLG_DEBUG(3, " -get_fnc(%s):\n", name);
|
||||
print_config_node(3, -1, fn_configs);
|
||||
}
|
||||
return fnc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void update_fn_config1(fn_node* fn, fn_config* fnc)
|
||||
{
|
||||
if (fnc->dump_before != CONFIG_DEFAULT)
|
||||
fn->dump_before = (fnc->dump_before == CONFIG_TRUE);
|
||||
|
||||
if (fnc->dump_after != CONFIG_DEFAULT)
|
||||
fn->dump_after = (fnc->dump_after == CONFIG_TRUE);
|
||||
|
||||
if (fnc->zero_before != CONFIG_DEFAULT)
|
||||
fn->zero_before = (fnc->zero_before == CONFIG_TRUE);
|
||||
|
||||
if (fnc->toggle_collect != CONFIG_DEFAULT)
|
||||
fn->toggle_collect = (fnc->toggle_collect == CONFIG_TRUE);
|
||||
|
||||
if (fnc->skip != CONFIG_DEFAULT)
|
||||
fn->skip = (fnc->skip == CONFIG_TRUE);
|
||||
|
||||
if (fnc->pop_on_jump != CONFIG_DEFAULT)
|
||||
fn->pop_on_jump = (fnc->pop_on_jump == CONFIG_TRUE);
|
||||
|
||||
if (fnc->group != CONFIG_DEFAULT)
|
||||
fn->group = fnc->group;
|
||||
|
||||
if (fnc->separate_callers != CONFIG_DEFAULT)
|
||||
fn->separate_callers = fnc->separate_callers;
|
||||
|
||||
if (fnc->separate_recursions != CONFIG_DEFAULT)
|
||||
fn->separate_recursions = fnc->separate_recursions;
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
if (fnc->verbosity != CONFIG_DEFAULT)
|
||||
fn->verbosity = fnc->verbosity;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Recursively go down the function matching tree,
|
||||
* looking for a match to <name>. For every matching leaf,
|
||||
* <fn> is updated with the pattern config.
|
||||
*/
|
||||
static void update_fn_config2(fn_node* fn, const HChar* name,
|
||||
config_node* node)
|
||||
{
|
||||
config_node* n;
|
||||
|
||||
CLG_DEBUG(3, " update_fn_config2('%s', node '%s'): \n",
|
||||
name, node->name);
|
||||
if ((*name == 0) && node->config) {
|
||||
CLG_DEBUG(3, " found!\n");
|
||||
update_fn_config1(fn, node->config);
|
||||
return;
|
||||
}
|
||||
|
||||
n = node->sub_node[ name[0]%NODE_DEGREE ];
|
||||
while(n) {
|
||||
if (VG_(strncmp)(name, n->name, n->length)==0) break;
|
||||
n = n->next;
|
||||
}
|
||||
if (n) {
|
||||
CLG_DEBUG(3, " '%s' matching at hash %d\n",
|
||||
n->name, name[0]%NODE_DEGREE);
|
||||
update_fn_config2(fn, name+n->length, n);
|
||||
}
|
||||
|
||||
if (node->wild_char) {
|
||||
CLG_DEBUG(3, " skip '%c' for wildcard '?'\n", *name);
|
||||
update_fn_config2(fn, name+1, node->wild_char);
|
||||
}
|
||||
|
||||
if (node->wild_star) {
|
||||
CLG_DEBUG(3, " wildcard '*'\n");
|
||||
while(*name) {
|
||||
update_fn_config2(fn, name, node->wild_star);
|
||||
name++;
|
||||
}
|
||||
update_fn_config2(fn, name, node->wild_star);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update function config according to configs of name prefixes */
|
||||
void CLG_(update_fn_config)(fn_node* fn)
|
||||
{
|
||||
CLG_DEBUG(3, " update_fn_config('%s')\n", fn->name);
|
||||
if (fn_configs)
|
||||
update_fn_config2(fn, fn->name, fn_configs);
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------*/
|
||||
/*--- Command line processing ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
Bool CLG_(process_cmd_line_option)(const HChar* arg)
|
||||
{
|
||||
const HChar* tmp_str;
|
||||
|
||||
if VG_BOOL_CLO(arg, "--skip-plt", CLG_(clo).skip_plt) {}
|
||||
|
||||
else if VG_BOOL_CLO(arg, "--collect-jumps", CLG_(clo).collect_jumps) {}
|
||||
/* compatibility alias, deprecated option */
|
||||
else if VG_BOOL_CLO(arg, "--trace-jump", CLG_(clo).collect_jumps) {}
|
||||
|
||||
else if VG_BOOL_CLO(arg, "--combine-dumps", CLG_(clo).combine_dumps) {}
|
||||
|
||||
else if VG_BOOL_CLO(arg, "--collect-atstart", CLG_(clo).collect_atstart) {}
|
||||
|
||||
else if VG_BOOL_CLO(arg, "--instr-atstart", CLG_(clo).instrument_atstart) {}
|
||||
|
||||
else if VG_BOOL_CLO(arg, "--separate-threads", CLG_(clo).separate_threads) {}
|
||||
|
||||
else if VG_BOOL_CLO(arg, "--compress-strings", CLG_(clo).compress_strings) {}
|
||||
else if VG_BOOL_CLO(arg, "--compress-mangled", CLG_(clo).compress_mangled) {}
|
||||
else if VG_BOOL_CLO(arg, "--compress-pos", CLG_(clo).compress_pos) {}
|
||||
|
||||
else if VG_STR_CLO(arg, "--fn-skip", tmp_str) {
|
||||
fn_config* fnc = get_fnc(tmp_str);
|
||||
fnc->skip = CONFIG_TRUE;
|
||||
}
|
||||
|
||||
else if VG_STR_CLO(arg, "--dump-before", tmp_str) {
|
||||
fn_config* fnc = get_fnc(tmp_str);
|
||||
fnc->dump_before = CONFIG_TRUE;
|
||||
}
|
||||
|
||||
else if VG_STR_CLO(arg, "--zero-before", tmp_str) {
|
||||
fn_config* fnc = get_fnc(tmp_str);
|
||||
fnc->zero_before = CONFIG_TRUE;
|
||||
}
|
||||
|
||||
else if VG_STR_CLO(arg, "--dump-after", tmp_str) {
|
||||
fn_config* fnc = get_fnc(tmp_str);
|
||||
fnc->dump_after = CONFIG_TRUE;
|
||||
}
|
||||
|
||||
else if VG_STR_CLO(arg, "--toggle-collect", tmp_str) {
|
||||
fn_config* fnc = get_fnc(tmp_str);
|
||||
fnc->toggle_collect = CONFIG_TRUE;
|
||||
/* defaults to initial collection off */
|
||||
CLG_(clo).collect_atstart = False;
|
||||
}
|
||||
|
||||
else if VG_INT_CLO(arg, "--separate-recs", CLG_(clo).separate_recursions) {}
|
||||
|
||||
/* change handling of a jump between functions to ret+call */
|
||||
else if VG_XACT_CLO(arg, "--pop-on-jump", CLG_(clo).pop_on_jump, True) {}
|
||||
else if VG_STR_CLO( arg, "--pop-on-jump", tmp_str) {
|
||||
fn_config* fnc = get_fnc(tmp_str);
|
||||
fnc->pop_on_jump = CONFIG_TRUE;
|
||||
}
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
else if VG_INT_CLO(arg, "--ct-verbose", CLG_(clo).verbose) {}
|
||||
else if VG_INT_CLO(arg, "--ct-vstart", CLG_(clo).verbose_start) {}
|
||||
|
||||
else if VG_STREQN(12, arg, "--ct-verbose") {
|
||||
fn_config* fnc;
|
||||
HChar* s;
|
||||
UInt n = VG_(strtoll10)(arg+12, &s);
|
||||
if ((n <= 0) || *s != '=') return False;
|
||||
fnc = get_fnc(s+1);
|
||||
fnc->verbosity = n;
|
||||
}
|
||||
#endif
|
||||
|
||||
else if VG_XACT_CLO(arg, "--separate-callers=auto",
|
||||
CLG_(clo).separate_callers, CONFIG_AUTO) {}
|
||||
else if VG_INT_CLO( arg, "--separate-callers",
|
||||
CLG_(clo).separate_callers) {}
|
||||
|
||||
else if VG_STREQN(10, arg, "--fn-group") {
|
||||
fn_config* fnc;
|
||||
HChar* s;
|
||||
UInt n = VG_(strtoll10)(arg+10, &s);
|
||||
if ((n <= 0) || *s != '=') return False;
|
||||
fnc = get_fnc(s+1);
|
||||
fnc->group = n;
|
||||
}
|
||||
|
||||
else if VG_STREQN(18, arg, "--separate-callers") {
|
||||
fn_config* fnc;
|
||||
HChar* s;
|
||||
UInt n = VG_(strtoll10)(arg+18, &s);
|
||||
if ((n <= 0) || *s != '=') return False;
|
||||
fnc = get_fnc(s+1);
|
||||
fnc->separate_callers = n;
|
||||
}
|
||||
|
||||
else if VG_STREQN(15, arg, "--separate-recs") {
|
||||
fn_config* fnc;
|
||||
HChar* s;
|
||||
UInt n = VG_(strtoll10)(arg+15, &s);
|
||||
if ((n <= 0) || *s != '=') return False;
|
||||
fnc = get_fnc(s+1);
|
||||
fnc->separate_recursions = n;
|
||||
}
|
||||
|
||||
else if VG_STR_CLO(arg, "--callgrind-out-file", CLG_(clo).out_format) {}
|
||||
|
||||
else if VG_BOOL_CLO(arg, "--mangle-names", CLG_(clo).mangle_names) {}
|
||||
|
||||
else if VG_BOOL_CLO(arg, "--skip-direct-rec",
|
||||
CLG_(clo).skip_direct_recursion) {}
|
||||
|
||||
else if VG_BOOL_CLO(arg, "--dump-bbs", CLG_(clo).dump_bbs) {}
|
||||
else if VG_BOOL_CLO(arg, "--dump-line", CLG_(clo).dump_line) {}
|
||||
else if VG_BOOL_CLO(arg, "--dump-instr", CLG_(clo).dump_instr) {}
|
||||
else if VG_BOOL_CLO(arg, "--dump-bb", CLG_(clo).dump_bb) {}
|
||||
|
||||
else if VG_INT_CLO( arg, "--dump-every-bb", CLG_(clo).dump_every_bb) {}
|
||||
|
||||
else if VG_BOOL_CLO(arg, "--collect-alloc", CLG_(clo).collect_alloc) {}
|
||||
else if VG_BOOL_CLO(arg, "--collect-systime", CLG_(clo).collect_systime) {}
|
||||
else if VG_BOOL_CLO(arg, "--collect-bus", CLG_(clo).collect_bus) {}
|
||||
/* for option compatibility with cachegrind */
|
||||
else if VG_BOOL_CLO(arg, "--cache-sim", CLG_(clo).simulate_cache) {}
|
||||
/* compatibility alias, deprecated option */
|
||||
else if VG_BOOL_CLO(arg, "--simulate-cache", CLG_(clo).simulate_cache) {}
|
||||
/* for option compatibility with cachegrind */
|
||||
else if VG_BOOL_CLO(arg, "--branch-sim", CLG_(clo).simulate_branch) {}
|
||||
else {
|
||||
Bool isCachesimOption = (*CLG_(cachesim).parse_opt)(arg);
|
||||
|
||||
/* cache simulator is used if a simulator option is given */
|
||||
if (isCachesimOption)
|
||||
CLG_(clo).simulate_cache = True;
|
||||
|
||||
return isCachesimOption;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
void CLG_(print_usage)(void)
|
||||
{
|
||||
VG_(printf)(
|
||||
"\n dump creation options:\n"
|
||||
" --callgrind-out-file=<f> Output file name [callgrind.out.%%p]\n"
|
||||
" --dump-line=no|yes Dump source lines of costs? [yes]\n"
|
||||
" --dump-instr=no|yes Dump instruction address of costs? [no]\n"
|
||||
" --compress-strings=no|yes Compress strings in profile dump? [yes]\n"
|
||||
" --compress-pos=no|yes Compress positions in profile dump? [yes]\n"
|
||||
" --combine-dumps=no|yes Concat all dumps into same file [no]\n"
|
||||
#if CLG_EXPERIMENTAL
|
||||
" --compress-events=no|yes Compress events in profile dump? [no]\n"
|
||||
" --dump-bb=no|yes Dump basic block address of costs? [no]\n"
|
||||
" --dump-bbs=no|yes Dump basic block info? [no]\n"
|
||||
" --dump-skipped=no|yes Dump info on skipped functions in calls? [no]\n"
|
||||
" --mangle-names=no|yes Mangle separation into names? [yes]\n"
|
||||
#endif
|
||||
|
||||
"\n activity options (for interactivity use callgrind_control):\n"
|
||||
" --dump-every-bb=<count> Dump every <count> basic blocks [0=never]\n"
|
||||
" --dump-before=<func> Dump when entering function\n"
|
||||
" --zero-before=<func> Zero all costs when entering function\n"
|
||||
" --dump-after=<func> Dump when leaving function\n"
|
||||
#if CLG_EXPERIMENTAL
|
||||
" --dump-objs=no|yes Dump static object information [no]\n"
|
||||
#endif
|
||||
|
||||
"\n data collection options:\n"
|
||||
" --instr-atstart=no|yes Do instrumentation at callgrind start [yes]\n"
|
||||
" --collect-atstart=no|yes Collect at process/thread start [yes]\n"
|
||||
" --toggle-collect=<func> Toggle collection on enter/leave function\n"
|
||||
" --collect-jumps=no|yes Collect jumps? [no]\n"
|
||||
" --collect-bus=no|yes Collect global bus events? [no]\n"
|
||||
#if CLG_EXPERIMENTAL
|
||||
" --collect-alloc=no|yes Collect memory allocation info? [no]\n"
|
||||
#endif
|
||||
" --collect-systime=no|yes Collect system call time info? [no]\n"
|
||||
|
||||
"\n cost entity separation options:\n"
|
||||
" --separate-threads=no|yes Separate data per thread [no]\n"
|
||||
" --separate-callers=<n> Separate functions by call chain length [0]\n"
|
||||
" --separate-callers<n>=<f> Separate <n> callers for function <f>\n"
|
||||
" --separate-recs=<n> Separate function recursions up to level [2]\n"
|
||||
" --separate-recs<n>=<f> Separate <n> recursions for function <f>\n"
|
||||
" --skip-plt=no|yes Ignore calls to/from PLT sections? [yes]\n"
|
||||
" --skip-direct-rec=no|yes Ignore direct recursions? [yes]\n"
|
||||
" --fn-skip=<function> Ignore calls to/from function?\n"
|
||||
#if CLG_EXPERIMENTAL
|
||||
" --fn-group<no>=<func> Put function into separation group <no>\n"
|
||||
#endif
|
||||
"\n simulation options:\n"
|
||||
" --branch-sim=no|yes Do branch prediction simulation [no]\n"
|
||||
" --cache-sim=no|yes Do cache simulation [no]\n"
|
||||
);
|
||||
|
||||
(*CLG_(cachesim).print_opts)();
|
||||
|
||||
// VG_(printf)("\n"
|
||||
// " For full callgrind documentation, see\n"
|
||||
// " "VG_PREFIX"/share/doc/callgrind/html/callgrind.html\n\n");
|
||||
}
|
||||
|
||||
void CLG_(print_debug_usage)(void)
|
||||
{
|
||||
VG_(printf)(
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
" --ct-verbose=<level> Verbosity of standard debug output [0]\n"
|
||||
" --ct-vstart=<BB number> Only be verbose after basic block [0]\n"
|
||||
" --ct-verbose<level>=<func> Verbosity while in <func>\n"
|
||||
#else
|
||||
" (none)\n"
|
||||
#endif
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void CLG_(set_clo_defaults)(void)
|
||||
{
|
||||
/* Default values for command line arguments */
|
||||
|
||||
/* dump options */
|
||||
CLG_(clo).out_format = 0;
|
||||
CLG_(clo).combine_dumps = False;
|
||||
CLG_(clo).compress_strings = True;
|
||||
CLG_(clo).compress_mangled = False;
|
||||
CLG_(clo).compress_events = False;
|
||||
CLG_(clo).compress_pos = True;
|
||||
CLG_(clo).mangle_names = True;
|
||||
CLG_(clo).dump_line = True;
|
||||
CLG_(clo).dump_instr = False;
|
||||
CLG_(clo).dump_bb = False;
|
||||
CLG_(clo).dump_bbs = False;
|
||||
|
||||
CLG_(clo).dump_every_bb = 0;
|
||||
|
||||
/* Collection */
|
||||
CLG_(clo).separate_threads = False;
|
||||
CLG_(clo).collect_atstart = True;
|
||||
CLG_(clo).collect_jumps = False;
|
||||
CLG_(clo).collect_alloc = False;
|
||||
CLG_(clo).collect_systime = False;
|
||||
CLG_(clo).collect_bus = False;
|
||||
|
||||
CLG_(clo).skip_plt = True;
|
||||
CLG_(clo).separate_callers = 0;
|
||||
CLG_(clo).separate_recursions = 2;
|
||||
CLG_(clo).skip_direct_recursion = False;
|
||||
|
||||
/* Instrumentation */
|
||||
CLG_(clo).instrument_atstart = True;
|
||||
CLG_(clo).simulate_cache = False;
|
||||
CLG_(clo).simulate_branch = False;
|
||||
|
||||
/* Call graph */
|
||||
CLG_(clo).pop_on_jump = False;
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
CLG_(clo).verbose = 0;
|
||||
CLG_(clo).verbose_start = 0;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
/*--------------------------------------------------------------------*/
|
||||
/*--- Callgrind ---*/
|
||||
/*--- ct_context.c ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
This file is part of Callgrind, a Valgrind tool for call tracing.
|
||||
|
||||
Copyright (C) 2002-2015, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The GNU General Public License is contained in the file COPYING.
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Context operations ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
#define N_FNSTACK_INITIAL_ENTRIES 500
|
||||
#define N_CXT_INITIAL_ENTRIES 2537
|
||||
|
||||
fn_stack CLG_(current_fn_stack);
|
||||
|
||||
void CLG_(init_fn_stack)(fn_stack* s)
|
||||
{
|
||||
CLG_ASSERT(s != 0);
|
||||
|
||||
s->size = N_FNSTACK_INITIAL_ENTRIES;
|
||||
s->bottom = (fn_node**) CLG_MALLOC("cl.context.ifs.1",
|
||||
s->size * sizeof(fn_node*));
|
||||
s->top = s->bottom;
|
||||
s->bottom[0] = 0;
|
||||
}
|
||||
|
||||
void CLG_(copy_current_fn_stack)(fn_stack* dst)
|
||||
{
|
||||
CLG_ASSERT(dst != 0);
|
||||
|
||||
dst->size = CLG_(current_fn_stack).size;
|
||||
dst->bottom = CLG_(current_fn_stack).bottom;
|
||||
dst->top = CLG_(current_fn_stack).top;
|
||||
}
|
||||
|
||||
void CLG_(set_current_fn_stack)(fn_stack* s)
|
||||
{
|
||||
CLG_ASSERT(s != 0);
|
||||
|
||||
CLG_(current_fn_stack).size = s->size;
|
||||
CLG_(current_fn_stack).bottom = s->bottom;
|
||||
CLG_(current_fn_stack).top = s->top;
|
||||
}
|
||||
|
||||
static cxt_hash cxts;
|
||||
|
||||
void CLG_(init_cxt_table)()
|
||||
{
|
||||
Int i;
|
||||
|
||||
cxts.size = N_CXT_INITIAL_ENTRIES;
|
||||
cxts.entries = 0;
|
||||
cxts.table = (Context**) CLG_MALLOC("cl.context.ict.1",
|
||||
cxts.size * sizeof(Context*));
|
||||
|
||||
for (i = 0; i < cxts.size; i++)
|
||||
cxts.table[i] = 0;
|
||||
}
|
||||
|
||||
/* double size of cxt table */
|
||||
static void resize_cxt_table(void)
|
||||
{
|
||||
UInt i, new_size, conflicts1 = 0, conflicts2 = 0;
|
||||
Context **new_table, *curr, *next;
|
||||
UInt new_idx;
|
||||
|
||||
new_size = 2* cxts.size +3;
|
||||
new_table = (Context**) CLG_MALLOC("cl.context.rct.1",
|
||||
new_size * sizeof(Context*));
|
||||
|
||||
for (i = 0; i < new_size; i++)
|
||||
new_table[i] = NULL;
|
||||
|
||||
for (i = 0; i < cxts.size; i++) {
|
||||
if (cxts.table[i] == NULL) continue;
|
||||
|
||||
curr = cxts.table[i];
|
||||
while (NULL != curr) {
|
||||
next = curr->next;
|
||||
|
||||
new_idx = (UInt) (curr->hash % new_size);
|
||||
|
||||
curr->next = new_table[new_idx];
|
||||
new_table[new_idx] = curr;
|
||||
if (curr->next) {
|
||||
conflicts1++;
|
||||
if (curr->next->next)
|
||||
conflicts2++;
|
||||
}
|
||||
|
||||
curr = next;
|
||||
}
|
||||
}
|
||||
|
||||
VG_(free)(cxts.table);
|
||||
|
||||
|
||||
CLG_DEBUG(0, "Resize Context Hash: %u => %u (entries %u, conflicts %u/%u)\n",
|
||||
cxts.size, new_size,
|
||||
cxts.entries, conflicts1, conflicts2);
|
||||
|
||||
cxts.size = new_size;
|
||||
cxts.table = new_table;
|
||||
CLG_(stat).cxt_hash_resizes++;
|
||||
}
|
||||
|
||||
__inline__
|
||||
static UWord cxt_hash_val(fn_node** fn, UInt size)
|
||||
{
|
||||
UWord hash = 0;
|
||||
UInt count = size;
|
||||
while(*fn != 0) {
|
||||
hash = (hash<<7) + (hash>>25) + (UWord)(*fn);
|
||||
fn--;
|
||||
count--;
|
||||
if (count==0) break;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
__inline__
|
||||
static Bool is_cxt(UWord hash, fn_node** fn, Context* cxt)
|
||||
{
|
||||
int count;
|
||||
fn_node** cxt_fn;
|
||||
|
||||
if (hash != cxt->hash) return False;
|
||||
|
||||
count = cxt->size;
|
||||
cxt_fn = &(cxt->fn[0]);
|
||||
while((*fn != 0) && (count>0)) {
|
||||
if (*cxt_fn != *fn) return False;
|
||||
fn--;
|
||||
cxt_fn++;
|
||||
count--;
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate new Context structure
|
||||
*/
|
||||
static Context* new_cxt(fn_node** fn)
|
||||
{
|
||||
Context* cxt;
|
||||
UInt idx, offset;
|
||||
UWord hash;
|
||||
int size, recs;
|
||||
fn_node* top_fn;
|
||||
|
||||
CLG_ASSERT(fn);
|
||||
top_fn = *fn;
|
||||
if (top_fn == 0) return 0;
|
||||
|
||||
size = top_fn->separate_callers +1;
|
||||
recs = top_fn->separate_recursions;
|
||||
if (recs<1) recs=1;
|
||||
|
||||
/* check fill degree of context hash table and resize if needed (>80%) */
|
||||
cxts.entries++;
|
||||
if (10 * cxts.entries / cxts.size > 8)
|
||||
resize_cxt_table();
|
||||
|
||||
cxt = (Context*) CLG_MALLOC("cl.context.nc.1",
|
||||
sizeof(Context)+sizeof(fn_node*)*size);
|
||||
|
||||
// hash value calculation similar to cxt_hash_val(), but additionally
|
||||
// copying function pointers in one run
|
||||
hash = 0;
|
||||
offset = 0;
|
||||
while(*fn != 0) {
|
||||
hash = (hash<<7) + (hash>>25) + (UWord)(*fn);
|
||||
cxt->fn[offset] = *fn;
|
||||
offset++;
|
||||
fn--;
|
||||
if (offset >= size) break;
|
||||
}
|
||||
if (offset < size) size = offset;
|
||||
|
||||
cxt->size = size;
|
||||
cxt->base_number = CLG_(stat).context_counter;
|
||||
cxt->hash = hash;
|
||||
|
||||
CLG_(stat).context_counter += recs;
|
||||
CLG_(stat).distinct_contexts++;
|
||||
|
||||
/* insert into Context hash table */
|
||||
idx = (UInt) (hash % cxts.size);
|
||||
cxt->next = cxts.table[idx];
|
||||
cxts.table[idx] = cxt;
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
CLG_DEBUGIF(3) {
|
||||
VG_(printf)(" new_cxt ox%p: ", cxt);
|
||||
CLG_(print_cxt)(12, cxt, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
return cxt;
|
||||
}
|
||||
|
||||
/* get the Context structure for current context */
|
||||
Context* CLG_(get_cxt)(fn_node** fn)
|
||||
{
|
||||
Context* cxt;
|
||||
UInt size, idx;
|
||||
UWord hash;
|
||||
|
||||
CLG_ASSERT(fn != 0);
|
||||
if (*fn == 0) return 0;
|
||||
size = (*fn)->separate_callers+1;
|
||||
if (size<=0) { size = -size+1; }
|
||||
|
||||
CLG_DEBUG(5, "+ get_cxt(fn '%s'): size %u\n",
|
||||
(*fn)->name, size);
|
||||
|
||||
hash = cxt_hash_val(fn, size);
|
||||
|
||||
if ( ((cxt = (*fn)->last_cxt) != 0) && is_cxt(hash, fn, cxt)) {
|
||||
CLG_DEBUG(5, "- get_cxt: %p\n", cxt);
|
||||
return cxt;
|
||||
}
|
||||
|
||||
CLG_(stat).cxt_lru_misses++;
|
||||
|
||||
idx = (UInt) (hash % cxts.size);
|
||||
cxt = cxts.table[idx];
|
||||
|
||||
while(cxt) {
|
||||
if (is_cxt(hash,fn,cxt)) break;
|
||||
cxt = cxt->next;
|
||||
}
|
||||
|
||||
if (!cxt)
|
||||
cxt = new_cxt(fn);
|
||||
|
||||
(*fn)->last_cxt = cxt;
|
||||
|
||||
CLG_DEBUG(5, "- get_cxt: %p\n", cxt);
|
||||
|
||||
return cxt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Change execution context by calling a new function from current context
|
||||
* Pushing 0x0 specifies a marker for a signal handler entry
|
||||
*/
|
||||
void CLG_(push_cxt)(fn_node* fn)
|
||||
{
|
||||
call_stack* cs = &CLG_(current_call_stack);
|
||||
Int fn_entries;
|
||||
|
||||
CLG_DEBUG(5, "+ push_cxt(fn '%s'): old ctx %d\n",
|
||||
fn ? fn->name : "0x0",
|
||||
CLG_(current_state).cxt ?
|
||||
(Int)CLG_(current_state).cxt->base_number : -1);
|
||||
|
||||
/* save old context on stack (even if not changed at all!) */
|
||||
CLG_ASSERT(cs->sp < cs->size);
|
||||
CLG_ASSERT(cs->entry[cs->sp].cxt == 0);
|
||||
cs->entry[cs->sp].cxt = CLG_(current_state).cxt;
|
||||
cs->entry[cs->sp].fn_sp = CLG_(current_fn_stack).top - CLG_(current_fn_stack).bottom;
|
||||
|
||||
if (fn && (*(CLG_(current_fn_stack).top) == fn)) return;
|
||||
if (fn && (fn->group>0) &&
|
||||
((*(CLG_(current_fn_stack).top))->group == fn->group)) return;
|
||||
|
||||
/* resizing needed ? */
|
||||
fn_entries = CLG_(current_fn_stack).top - CLG_(current_fn_stack).bottom;
|
||||
if (fn_entries == CLG_(current_fn_stack).size-1) {
|
||||
UInt new_size = CLG_(current_fn_stack).size *2;
|
||||
fn_node** new_array = (fn_node**) CLG_MALLOC("cl.context.pc.1",
|
||||
new_size * sizeof(fn_node*));
|
||||
int i;
|
||||
for(i=0;i<CLG_(current_fn_stack).size;i++)
|
||||
new_array[i] = CLG_(current_fn_stack).bottom[i];
|
||||
VG_(free)(CLG_(current_fn_stack).bottom);
|
||||
CLG_(current_fn_stack).top = new_array + fn_entries;
|
||||
CLG_(current_fn_stack).bottom = new_array;
|
||||
|
||||
CLG_DEBUG(0, "Resize Context Stack: %u => %u (pushing '%s')\n",
|
||||
CLG_(current_fn_stack).size, new_size,
|
||||
fn ? fn->name : "0x0");
|
||||
|
||||
CLG_(current_fn_stack).size = new_size;
|
||||
}
|
||||
|
||||
if (fn && (*(CLG_(current_fn_stack).top) == 0)) {
|
||||
UInt *pactive;
|
||||
|
||||
/* this is first function: increment its active count */
|
||||
pactive = CLG_(get_fn_entry)(fn->number);
|
||||
(*pactive)++;
|
||||
}
|
||||
|
||||
CLG_(current_fn_stack).top++;
|
||||
*(CLG_(current_fn_stack).top) = fn;
|
||||
CLG_(current_state).cxt = CLG_(get_cxt)(CLG_(current_fn_stack).top);
|
||||
|
||||
CLG_DEBUG(5, "- push_cxt(fn '%s'): new cxt %d, fn_sp %ld\n",
|
||||
fn ? fn->name : "0x0",
|
||||
CLG_(current_state).cxt ?
|
||||
(Int)CLG_(current_state).cxt->base_number : -1,
|
||||
CLG_(current_fn_stack).top - CLG_(current_fn_stack).bottom + 0L);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*--------------------------------------------------------------------*/
|
||||
/*--- Callgrind ---*/
|
||||
/*--- ct_costs.c ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
This file is part of Callgrind, a Valgrind tool for call tracing.
|
||||
|
||||
Copyright (C) 2002-2015, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The GNU General Public License is contained in the file COPYING.
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
|
||||
#include "pub_tool_mallocfree.h"
|
||||
|
||||
#define COSTCHUNK_SIZE 100000
|
||||
|
||||
UInt CLG_(costarray_entries) = 0;
|
||||
UInt CLG_(costarray_chunks) = 0;
|
||||
static CostChunk* cost_chunk_base = 0;
|
||||
static CostChunk* cost_chunk_current = 0;
|
||||
|
||||
ULong* CLG_(get_costarray)(Int size)
|
||||
{
|
||||
ULong* ptr;
|
||||
|
||||
if (!cost_chunk_current ||
|
||||
(cost_chunk_current->size - cost_chunk_current->used < size)) {
|
||||
CostChunk* cc = (CostChunk*) CLG_MALLOC("cl.costs.gc.1",
|
||||
sizeof(CostChunk) +
|
||||
COSTCHUNK_SIZE * sizeof(ULong));
|
||||
CLG_ASSERT(size < COSTCHUNK_SIZE);
|
||||
|
||||
cc->size = COSTCHUNK_SIZE;
|
||||
cc->used = 0;
|
||||
cc->next = 0;
|
||||
|
||||
if (cost_chunk_current)
|
||||
cost_chunk_current->next = cc;
|
||||
cost_chunk_current = cc;
|
||||
|
||||
if (!cost_chunk_base) cost_chunk_base = cc;
|
||||
|
||||
CLG_(costarray_chunks)++;
|
||||
}
|
||||
|
||||
ptr = &(cost_chunk_current->data[cost_chunk_current->used]);
|
||||
cost_chunk_current->used += size;
|
||||
|
||||
CLG_(costarray_entries) += size;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*--------------------------------------------------------------------*/
|
||||
/*--- Callgrind cost array interface. costs.h ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
This file is part of Valgrind, a dynamic binary instrumentation
|
||||
framework.
|
||||
|
||||
Copyright (C) 2004-2015 Josef Weidendorfer
|
||||
josef.weidendorfer@gmx.de
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The GNU General Public License is contained in the file COPYING.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CLG_COSTS
|
||||
#define CLG_COSTS
|
||||
|
||||
#include "pub_tool_basics.h"
|
||||
|
||||
#define CLG_(str) VGAPPEND(vgCallgrind_,str)
|
||||
|
||||
extern UInt CLG_(costarray_entries);
|
||||
extern UInt CLG_(costarray_chunks);
|
||||
|
||||
/* Array of 64bit costs. This is separated from other structs
|
||||
* to support a dynamic number of costs for a cost item.
|
||||
* Chunks are allocated on demand.
|
||||
*/
|
||||
typedef struct _CostChunk CostChunk;
|
||||
struct _CostChunk {
|
||||
Int size;
|
||||
Int used;
|
||||
CostChunk *next, *prev;
|
||||
ULong data[0];
|
||||
};
|
||||
|
||||
/* Allocate a number of 64bit cost values.
|
||||
* Typically used from ct_events.c */
|
||||
ULong* CLG_(get_costarray)(Int size);
|
||||
|
||||
#endif /* CLG_COSTS */
|
||||
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
This file is part of Callgrind, a Valgrind tool for call graph
|
||||
profiling programs.
|
||||
|
||||
Copyright (C) 2002-2015, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
|
||||
|
||||
This tool is derived from and contains lot of code from Cachegrind
|
||||
Copyright (C) 2002-2015 Nicholas Nethercote (njn@valgrind.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The GNU General Public License is contained in the file COPYING.
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
#include "events.h"
|
||||
|
||||
/* If debugging mode of, dummy functions are provided (see below)
|
||||
*/
|
||||
#if CLG_ENABLE_DEBUG
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Debug output helpers ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
static void print_indent(int s)
|
||||
{
|
||||
/* max of 40 spaces */
|
||||
const HChar sp[] = " ";
|
||||
if (s>40) s=40;
|
||||
VG_(printf)("%s", sp+40-s);
|
||||
}
|
||||
|
||||
void CLG_(print_bb)(int s, BB* bb)
|
||||
{
|
||||
if (s<0) {
|
||||
s = -s;
|
||||
print_indent(s);
|
||||
}
|
||||
|
||||
VG_(printf)("BB %#lx (Obj '%s')", bb_addr(bb), bb->obj->name);
|
||||
}
|
||||
|
||||
static
|
||||
void print_mangled_cxt(Context* cxt, int rec_index)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!cxt)
|
||||
VG_(printf)("(none)");
|
||||
else {
|
||||
VG_(printf)("%s", cxt->fn[0]->name);
|
||||
if (rec_index >0)
|
||||
VG_(printf)("'%d", rec_index +1);
|
||||
for(i=1;i<cxt->size;i++)
|
||||
VG_(printf)("'%s", cxt->fn[i]->name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CLG_(print_cxt)(Int s, Context* cxt, int rec_index)
|
||||
{
|
||||
if (s<0) {
|
||||
s = -s;
|
||||
print_indent(s);
|
||||
}
|
||||
|
||||
if (cxt) {
|
||||
UInt *pactive = CLG_(get_fn_entry)(cxt->fn[0]->number);
|
||||
CLG_ASSERT(rec_index < cxt->fn[0]->separate_recursions);
|
||||
|
||||
VG_(printf)("Cxt %u" ,cxt->base_number + rec_index);
|
||||
if (*pactive>0)
|
||||
VG_(printf)(" [active=%u]", *pactive);
|
||||
VG_(printf)(": ");
|
||||
print_mangled_cxt(cxt, rec_index);
|
||||
VG_(printf)("\n");
|
||||
}
|
||||
else
|
||||
VG_(printf)("(no context)\n");
|
||||
}
|
||||
|
||||
void CLG_(print_execstate)(int s, exec_state* es)
|
||||
{
|
||||
if (s<0) {
|
||||
s = -s;
|
||||
print_indent(s);
|
||||
}
|
||||
|
||||
if (!es) {
|
||||
VG_(printf)("ExecState 0x0\n");
|
||||
return;
|
||||
}
|
||||
|
||||
VG_(printf)("ExecState [Sig %d, collect %s, nonskipped %p]: jmps_passed %d\n",
|
||||
es->sig, es->collect?"yes":"no",
|
||||
es->nonskipped, es->jmps_passed);
|
||||
}
|
||||
|
||||
|
||||
void CLG_(print_bbcc)(int s, BBCC* bbcc)
|
||||
{
|
||||
BB* bb;
|
||||
|
||||
if (s<0) {
|
||||
s = -s;
|
||||
print_indent(s);
|
||||
}
|
||||
|
||||
if (!bbcc) {
|
||||
VG_(printf)("BBCC 0x0\n");
|
||||
return;
|
||||
}
|
||||
|
||||
bb = bbcc->bb;
|
||||
CLG_ASSERT(bb!=0);
|
||||
|
||||
VG_(printf)("%s +%#lx=%#lx, ",
|
||||
bb->obj->name + bb->obj->last_slash_pos,
|
||||
(UWord)bb->offset, bb_addr(bb));
|
||||
CLG_(print_cxt)(s+8, bbcc->cxt, bbcc->rec_index);
|
||||
}
|
||||
|
||||
void CLG_(print_eventset)(int s, EventSet* es)
|
||||
{
|
||||
int i, j;
|
||||
UInt mask;
|
||||
EventGroup* eg;
|
||||
|
||||
if (s<0) {
|
||||
s = -s;
|
||||
print_indent(s);
|
||||
}
|
||||
|
||||
if (!es) {
|
||||
VG_(printf)("(EventSet not set)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
VG_(printf)("EventSet %u (%d groups, size %d):",
|
||||
es->mask, es->count, es->size);
|
||||
|
||||
if (es->count == 0) {
|
||||
VG_(printf)("-\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for(i=0, mask=1; i<MAX_EVENTGROUP_COUNT; i++, mask=mask<<1) {
|
||||
if ((es->mask & mask)==0) continue;
|
||||
eg = CLG_(get_event_group)(i);
|
||||
if (!eg) continue;
|
||||
VG_(printf)(" (%d: %s", i, eg->name[0]);
|
||||
for(j=1; j<eg->size; j++)
|
||||
VG_(printf)(" %s", eg->name[j]);
|
||||
VG_(printf)(")");
|
||||
}
|
||||
VG_(printf)("\n");
|
||||
}
|
||||
|
||||
|
||||
void CLG_(print_cost)(int s, EventSet* es, ULong* c)
|
||||
{
|
||||
Int i, j, pos, off;
|
||||
UInt mask;
|
||||
EventGroup* eg;
|
||||
|
||||
if (s<0) {
|
||||
s = -s;
|
||||
print_indent(s);
|
||||
}
|
||||
|
||||
if (!es) {
|
||||
VG_(printf)("Cost (Nothing, EventSet not set)\n");
|
||||
return;
|
||||
}
|
||||
if (!c) {
|
||||
VG_(printf)("Cost (Null, EventSet %u)\n", es->mask);
|
||||
return;
|
||||
}
|
||||
|
||||
if (es->size == 0) {
|
||||
VG_(printf)("Cost (Nothing, EventSet with len 0)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pos = s;
|
||||
pos += VG_(printf)("Cost [%p]: ", c);
|
||||
off = 0;
|
||||
for(i=0, mask=1; i<MAX_EVENTGROUP_COUNT; i++, mask=mask<<1) {
|
||||
if ((es->mask & mask)==0) continue;
|
||||
eg = CLG_(get_event_group)(i);
|
||||
if (!eg) continue;
|
||||
for(j=0; j<eg->size; j++) {
|
||||
|
||||
if (off>0) {
|
||||
if (pos > 70) {
|
||||
VG_(printf)(",\n");
|
||||
print_indent(s+5);
|
||||
pos = s+5;
|
||||
}
|
||||
else
|
||||
pos += VG_(printf)(", ");
|
||||
}
|
||||
|
||||
pos += VG_(printf)("%s %llu", eg->name[j], c[off++]);
|
||||
}
|
||||
}
|
||||
VG_(printf)("\n");
|
||||
}
|
||||
|
||||
|
||||
void CLG_(print_short_jcc)(jCC* jcc)
|
||||
{
|
||||
if (jcc)
|
||||
VG_(printf)("%#lx => %#lx [calls %llu/Ir %llu, Dr %llu, Dw %llu]",
|
||||
bb_jmpaddr(jcc->from->bb),
|
||||
bb_addr(jcc->to->bb),
|
||||
jcc->call_counter,
|
||||
jcc->cost ? jcc->cost[fullOffset(EG_IR)]:0,
|
||||
jcc->cost ? jcc->cost[fullOffset(EG_DR)]:0,
|
||||
jcc->cost ? jcc->cost[fullOffset(EG_DW)]:0);
|
||||
else
|
||||
VG_(printf)("[Skipped JCC]");
|
||||
}
|
||||
|
||||
void CLG_(print_jcc)(int s, jCC* jcc)
|
||||
{
|
||||
if (s<0) {
|
||||
s = -s;
|
||||
print_indent(s);
|
||||
}
|
||||
|
||||
if (!jcc) {
|
||||
VG_(printf)("JCC to skipped function\n");
|
||||
return;
|
||||
}
|
||||
VG_(printf)("JCC %p from ", jcc);
|
||||
CLG_(print_bbcc)(s+9, jcc->from);
|
||||
print_indent(s+4);
|
||||
VG_(printf)("to ");
|
||||
CLG_(print_bbcc)(s+9, jcc->to);
|
||||
print_indent(s+4);
|
||||
VG_(printf)("Calls %llu\n", jcc->call_counter);
|
||||
print_indent(s+4);
|
||||
CLG_(print_cost)(s+9, CLG_(sets).full, jcc->cost);
|
||||
}
|
||||
|
||||
/* dump out the current call stack */
|
||||
void CLG_(print_stackentry)(int s, int sp)
|
||||
{
|
||||
call_entry* ce;
|
||||
|
||||
if (s<0) {
|
||||
s = -s;
|
||||
print_indent(s);
|
||||
}
|
||||
|
||||
ce = CLG_(get_call_entry)(sp);
|
||||
VG_(printf)("[%-2d] SP %#lx, RA %#lx", sp, ce->sp, ce->ret_addr);
|
||||
if (ce->nonskipped)
|
||||
VG_(printf)(" NonSkipped BB %#lx / %s",
|
||||
bb_addr(ce->nonskipped->bb),
|
||||
ce->nonskipped->cxt->fn[0]->name);
|
||||
VG_(printf)("\n");
|
||||
print_indent(s+5);
|
||||
CLG_(print_jcc)(5,ce->jcc);
|
||||
}
|
||||
|
||||
/* debug output */
|
||||
#if 0
|
||||
static void print_call_stack()
|
||||
{
|
||||
int c;
|
||||
|
||||
VG_(printf)("Call Stack:\n");
|
||||
for(c=0;c<CLG_(current_call_stack).sp;c++)
|
||||
CLG_(print_stackentry)(-2, c);
|
||||
}
|
||||
#endif
|
||||
|
||||
void CLG_(print_bbcc_fn)(BBCC* bbcc)
|
||||
{
|
||||
obj_node* obj;
|
||||
|
||||
if (!bbcc) {
|
||||
VG_(printf)("%08x", 0u);
|
||||
return;
|
||||
}
|
||||
|
||||
VG_(printf)("%08lx/%c %u:", bb_addr(bbcc->bb),
|
||||
(bbcc->bb->sect_kind == Vg_SectText) ? 'T' :
|
||||
(bbcc->bb->sect_kind == Vg_SectData) ? 'D' :
|
||||
(bbcc->bb->sect_kind == Vg_SectBSS) ? 'B' :
|
||||
(bbcc->bb->sect_kind == Vg_SectGOT) ? 'G' :
|
||||
(bbcc->bb->sect_kind == Vg_SectPLT) ? 'P' : 'U',
|
||||
bbcc->cxt->base_number+bbcc->rec_index);
|
||||
print_mangled_cxt(bbcc->cxt, bbcc->rec_index);
|
||||
|
||||
obj = bbcc->cxt->fn[0]->file->obj;
|
||||
if (obj->name[0])
|
||||
VG_(printf)(" %s", obj->name+obj->last_slash_pos);
|
||||
|
||||
if (VG_(strcmp)(bbcc->cxt->fn[0]->file->name, "???") !=0) {
|
||||
VG_(printf)(" %s", bbcc->cxt->fn[0]->file->name);
|
||||
if ((bbcc->cxt->fn[0] == bbcc->bb->fn) && (bbcc->bb->line>0))
|
||||
VG_(printf)(":%u", bbcc->bb->line);
|
||||
}
|
||||
}
|
||||
|
||||
void CLG_(print_bbcc_cost)(int s, BBCC* bbcc)
|
||||
{
|
||||
BB* bb;
|
||||
Int i, cjmpNo;
|
||||
ULong ecounter;
|
||||
|
||||
if (s<0) {
|
||||
s = -s;
|
||||
print_indent(s);
|
||||
}
|
||||
|
||||
if (!bbcc) {
|
||||
VG_(printf)("BBCC 0x0\n");
|
||||
return;
|
||||
}
|
||||
|
||||
bb = bbcc->bb;
|
||||
CLG_ASSERT(bb!=0);
|
||||
|
||||
CLG_(print_bbcc)(s, bbcc);
|
||||
|
||||
ecounter = bbcc->ecounter_sum;
|
||||
|
||||
print_indent(s+2);
|
||||
VG_(printf)("ECounter: sum %llu ", ecounter);
|
||||
for(i=0; i<bb->cjmp_count; i++) {
|
||||
VG_(printf)("[%u]=%llu ",
|
||||
bb->jmp[i].instr, bbcc->jmp[i].ecounter);
|
||||
}
|
||||
VG_(printf)("\n");
|
||||
|
||||
cjmpNo = 0;
|
||||
for(i=0; i<bb->instr_count; i++) {
|
||||
InstrInfo* ii = &(bb->instr[i]);
|
||||
print_indent(s+2);
|
||||
VG_(printf)("[%2d] IOff %2u ecnt %3llu ",
|
||||
i, ii->instr_offset, ecounter);
|
||||
CLG_(print_cost)(s+5, ii->eventset, bbcc->cost + ii->cost_offset);
|
||||
|
||||
/* update execution counter */
|
||||
if (cjmpNo < bb->cjmp_count)
|
||||
if (bb->jmp[cjmpNo].instr == i) {
|
||||
ecounter -= bbcc->jmp[cjmpNo].ecounter;
|
||||
cjmpNo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* dump out an address with source info if available */
|
||||
void CLG_(print_addr)(Addr addr)
|
||||
{
|
||||
const HChar *fn_buf, *fl_buf, *dir_buf;
|
||||
const HChar* obj_name;
|
||||
DebugInfo* di;
|
||||
UInt ln, i=0, opos=0;
|
||||
|
||||
if (addr == 0) {
|
||||
VG_(printf)("%08lx", addr);
|
||||
return;
|
||||
}
|
||||
|
||||
CLG_(get_debug_info)(addr, &dir_buf, &fl_buf, &fn_buf, &ln, &di);
|
||||
|
||||
if (VG_(strcmp)(fn_buf,"???")==0)
|
||||
VG_(printf)("%#lx", addr);
|
||||
else
|
||||
VG_(printf)("%#lx %s", addr, fn_buf);
|
||||
|
||||
if (di) {
|
||||
obj_name = VG_(DebugInfo_get_filename)(di);
|
||||
if (obj_name) {
|
||||
while(obj_name[i]) {
|
||||
if (obj_name[i]=='/') opos = i+1;
|
||||
i++;
|
||||
}
|
||||
if (obj_name[0])
|
||||
VG_(printf)(" %s", obj_name+opos);
|
||||
}
|
||||
}
|
||||
|
||||
if (ln>0) {
|
||||
if (dir_buf[0])
|
||||
VG_(printf)(" (%s/%s:%u)", dir_buf, fl_buf, ln);
|
||||
else
|
||||
VG_(printf)(" (%s:%u)", fl_buf, ln);
|
||||
}
|
||||
}
|
||||
|
||||
void CLG_(print_addr_ln)(Addr addr)
|
||||
{
|
||||
CLG_(print_addr)(addr);
|
||||
VG_(printf)("\n");
|
||||
}
|
||||
|
||||
static ULong bb_written = 0;
|
||||
|
||||
void CLG_(print_bbno)(void)
|
||||
{
|
||||
if (bb_written != CLG_(stat).bb_executions) {
|
||||
bb_written = CLG_(stat).bb_executions;
|
||||
VG_(printf)("BB# %llu\n",CLG_(stat).bb_executions);
|
||||
}
|
||||
}
|
||||
|
||||
void CLG_(print_context)(void)
|
||||
{
|
||||
BBCC* bbcc;
|
||||
|
||||
CLG_DEBUG(0,"In tid %u [%d] ",
|
||||
CLG_(current_tid), CLG_(current_call_stack).sp);
|
||||
bbcc = CLG_(current_state).bbcc;
|
||||
print_mangled_cxt(CLG_(current_state).cxt,
|
||||
bbcc ? bbcc->rec_index : 0);
|
||||
VG_(printf)("\n");
|
||||
}
|
||||
|
||||
void* CLG_(malloc)(const HChar* cc, UWord s, const HChar* f)
|
||||
{
|
||||
CLG_DEBUG(3, "Malloc(%lu) in %s.\n", s, f);
|
||||
return VG_(malloc)(cc,s);
|
||||
}
|
||||
|
||||
#else /* CLG_ENABLE_DEBUG */
|
||||
|
||||
void CLG_(print_bbno)(void) {}
|
||||
void CLG_(print_context)(void) {}
|
||||
void CLG_(print_jcc)(int s, jCC* jcc) {}
|
||||
void CLG_(print_bbcc)(int s, BBCC* bbcc) {}
|
||||
void CLG_(print_bbcc_fn)(BBCC* bbcc) {}
|
||||
void CLG_(print_cost)(int s, EventSet* es, ULong* cost) {}
|
||||
void CLG_(print_bb)(int s, BB* bb) {}
|
||||
void CLG_(print_cxt)(int s, Context* cxt, int rec_index) {}
|
||||
void CLG_(print_short_jcc)(jCC* jcc) {}
|
||||
void CLG_(print_stackentry)(int s, int sp) {}
|
||||
void CLG_(print_addr)(Addr addr) {}
|
||||
void CLG_(print_addr_ln)(Addr addr) {}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0"?> <!-- -*- sgml -*- -->
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
|
||||
[ <!ENTITY % vg-entities SYSTEM "../../docs/xml/vg-entities.xml"> %vg-entities; ]>
|
||||
|
||||
|
||||
<refentry id="callgrind-annotate">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>Callgrind Annotate</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
<refmiscinfo>Release &rel-version;</refmiscinfo>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv id="a-name">
|
||||
<refname>callgrind_annotate</refname>
|
||||
<refpurpose>post-processing tool for the Callgrind</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv id="a-synopsis">
|
||||
<cmdsynopsis>
|
||||
<command>callgrind_annotate</command>
|
||||
<arg choice="opt"><replaceable>options</replaceable></arg>
|
||||
<!-- note the deliberate <arg> nesting here -->
|
||||
<arg choice="opt"><replaceable>callgrind-out-file</replaceable>
|
||||
<arg choice="opt" rep="repeat"><replaceable>source-files</replaceable></arg></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
||||
<refsect1 id="a-description">
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>callgrind_annotate</command> takes an output file produced by
|
||||
the Valgrind tool Callgrind and prints the information in an easy-to-read form.
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1 id="a-options">
|
||||
<title>Options</title>
|
||||
|
||||
<xi:include href="cl-manual.xml" xpointer="callgrind_annotate.opts.list"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude" />
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1 id="a-see_also">
|
||||
<title>See Also</title>
|
||||
|
||||
<para>
|
||||
valgrind(1),
|
||||
<filename>&vg-docs-path;</filename> or
|
||||
<filename>&vg-docs-url;</filename>.
|
||||
</para>
|
||||
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1 id="a-author">
|
||||
<title>Author</title>
|
||||
|
||||
<para>Josef Weidendorfer <&cl-email;>.</para>
|
||||
|
||||
<para>This manual page was written by
|
||||
Philipp Frauenfelder <pfrauenf@debian.org>.</para>
|
||||
</refsect1>
|
||||
|
||||
|
||||
</refentry>
|
||||
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0"?> <!-- -*- sgml -*- -->
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
|
||||
[ <!ENTITY % vg-entities SYSTEM "../../docs/xml/vg-entities.xml"> %vg-entities; ]>
|
||||
|
||||
|
||||
<refentry id="callgrind-control">
|
||||
<refmeta>
|
||||
<refentrytitle>Callgrind Control</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
<refmiscinfo>Release &rel-version;</refmiscinfo>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv id="c-name">
|
||||
<refname>callgrind_control</refname>
|
||||
<refpurpose>observe and control programs being run by Callgrind</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv id="c-synopsis">
|
||||
<cmdsynopsis>
|
||||
<command>callgrind_control</command>
|
||||
<arg choice="opt"><replaceable>options</replaceable></arg>
|
||||
<arg choice="opt" rep="repeat"><replaceable>pid|program-name</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
||||
<refsect1 id="c-description">
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
<command>callgrind_control</command> controls programs being run by the
|
||||
Valgrind tool Callgrind. When a <command>pid/program name</command>
|
||||
argument is not specified, all applications currently being run by Callgrind
|
||||
on this system will be used for actions given by the specified option(s).
|
||||
The default action is to give some brief information about the applications
|
||||
being run by Callgrind.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1 id="c-options">
|
||||
<title>Options</title>
|
||||
|
||||
<xi:include href="cl-manual.xml" xpointer="callgrind_control.opts.list"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude" />
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1 id="c-see_also">
|
||||
<title>See Also</title>
|
||||
|
||||
<para>
|
||||
valgrind(1),
|
||||
<filename>&vg-docs-path;</filename> or
|
||||
<filename>&vg-docs-url;</filename>.
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1 id="c-author">
|
||||
<title>Author</title>
|
||||
|
||||
<para>Josef Weidendorfer <&cl-email;>.</para>
|
||||
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
</refentry>
|
||||
|
||||
@@ -0,0 +1,609 @@
|
||||
<?xml version="1.0"?> <!-- -*- sgml -*- -->
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
|
||||
[ <!ENTITY % vg-entities SYSTEM "../../docs/xml/vg-entities.xml"> %vg-entities; ]>
|
||||
|
||||
<chapter id="cl-format" xreflabel="Callgrind Format Specification">
|
||||
<title>Callgrind Format Specification</title>
|
||||
|
||||
<para>This chapter describes the Callgrind Profile Format, Version 1.</para>
|
||||
|
||||
<para>A synonymous name is "Calltree Profile Format". These names actually mean
|
||||
the same since Callgrind was previously named Calltree.</para>
|
||||
|
||||
<para>The format description is meant for the user to be able to understand the
|
||||
file contents; but more important, it is given for authors of measurement or
|
||||
visualization tools to be able to write and read this format.</para>
|
||||
|
||||
<sect1 id="cl-format.overview" xreflabel="Overview">
|
||||
<title>Overview</title>
|
||||
|
||||
<para>The profile data format is ASCII based.
|
||||
It is written by Callgrind, and it is upwards compatible
|
||||
to the format used by Cachegrind (ie. Cachegrind uses a subset). It can
|
||||
be read by callgrind_annotate and KCachegrind.</para>
|
||||
|
||||
<para>This chapter gives on overview of format features and examples.
|
||||
For detailed syntax, look at the format reference.</para>
|
||||
|
||||
<sect2 id="cl-format.overview.basics" xreflabel="Basic Structure">
|
||||
<title>Basic Structure</title>
|
||||
|
||||
<para>Each file has a header part of an arbitrary number of lines of the
|
||||
format "key: value". After the header, lines specifying profile costs
|
||||
follow. Everywhere, comments on own lines starting with '#' are allowed.
|
||||
The header lines with keys "positions" and "events" define
|
||||
the meaning of cost lines in the second part of the file: the value of
|
||||
"positions" is a list of subpositions, and the value of "events" is a list
|
||||
of event type names. Cost lines consist of subpositions followed by 64-bit
|
||||
counters for the events, in the order specified by the "positions" and "events"
|
||||
header line.</para>
|
||||
|
||||
<para>The "events" header line is always required in contrast to the optional
|
||||
line for "positions", which defaults to "line", i.e. a line number of some
|
||||
source file. In addition, the second part of the file contains position
|
||||
specifications of the form "spec=name". "spec" can be e.g. "fn" for a
|
||||
function name or "fl" for a file name. Cost lines are always related to
|
||||
the function/file specifications given directly before.</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="cl-format.overview.example1" xreflabel="Simple Example">
|
||||
<title>Simple Example</title>
|
||||
|
||||
<para>The event names in the following example are quite arbitrary, and are not
|
||||
related to event names used by Callgrind. Especially, cycle counts matching
|
||||
real processors probably will never be generated by any Valgrind tools, as these
|
||||
are bound to simulations of simple machine models for acceptable slowdown.
|
||||
However, any profiling tool could use the format described in this chapter.</para>
|
||||
|
||||
<para>
|
||||
<screen>events: Cycles Instructions Flops
|
||||
fl=file.f
|
||||
fn=main
|
||||
15 90 14 2
|
||||
16 20 12</screen></para>
|
||||
|
||||
<para>The above example gives profile information for event types "Cycles",
|
||||
"Instructions", and "Flops". Thus, cost lines give the number of CPU cycles
|
||||
passed by, number of executed instructions, and number of floating point
|
||||
operations executed while running code corresponding to some source
|
||||
position. As there is no line specifying the value of "positions", it defaults
|
||||
to "line", which means that the first number of a cost line is always a line
|
||||
number.</para>
|
||||
|
||||
<para>Thus, the first cost line specifies that in line 15 of source file
|
||||
<filename>file.f</filename> there is code belonging to function
|
||||
<function>main</function>. While running, 90 CPU cycles passed by, and 2 of
|
||||
the 14 instructions executed were floating point operations. Similarly, the
|
||||
next line specifies that there were 12 instructions executed in the context
|
||||
of function <function>main</function> which can be related to line 16 in
|
||||
file <filename>file.f</filename>, taking 20 CPU cycles. If a cost line
|
||||
specifies less event counts than given in the "events" line, the rest is
|
||||
assumed to be zero. I.e. there was no floating point instruction executed
|
||||
relating to line 16.</para>
|
||||
|
||||
<para>Note that regular cost lines always give self (also called exclusive)
|
||||
cost of code at a given position. If you specify multiple cost lines for the
|
||||
same position, these will be summed up. On the other hand, in the example above
|
||||
there is no specification of how many times function
|
||||
<function>main</function> actually was
|
||||
called: profile data only contains sums.</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
<sect2 id="cl-format.overview.associations" xreflabel="Associations">
|
||||
<title>Associations</title>
|
||||
|
||||
<para>The most important extension to the original format of Cachegrind is the
|
||||
ability to specify call relationship among functions. More generally, you
|
||||
specify associations among positions. For this, the second part of the
|
||||
file also can contain association specifications. These look similar to
|
||||
position specifications, but consist of two lines. For calls, the format
|
||||
looks like
|
||||
<screen>
|
||||
calls=(Call Count) (Target position)
|
||||
(Source position) (Inclusive cost of call)
|
||||
</screen></para>
|
||||
|
||||
<para>The destination only specifies subpositions like line number. Therefore,
|
||||
to be able to specify a call to another function in another source file, you
|
||||
have to precede the above lines with a "cfn=" specification for the name of the
|
||||
called function, and optionally a "cfi=" specification if the function is in
|
||||
another source file ("cfl=" is an alternative specification for "cfi=" because
|
||||
of historical reasons, and both should be supported by format readers).
|
||||
The second line looks like a regular cost line with the difference
|
||||
that inclusive cost spent inside of the function call has to be specified.</para>
|
||||
|
||||
<para>Other associations are for example (conditional) jumps. See the
|
||||
reference below for details.</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
<sect2 id="cl-format.overview.example2" xreflabel="Extended Example">
|
||||
<title>Extended Example</title>
|
||||
|
||||
<para>The following example shows 3 functions, <function>main</function>,
|
||||
<function>func1</function>, and <function>func2</function>. Function
|
||||
<function>main</function> calls <function>func1</function> once and
|
||||
<function>func2</function> 3 times. <function>func1</function> calls
|
||||
<function>func2</function> 2 times.
|
||||
<screen>events: Instructions
|
||||
|
||||
fl=file1.c
|
||||
fn=main
|
||||
16 20
|
||||
cfn=func1
|
||||
calls=1 50
|
||||
16 400
|
||||
cfi=file2.c
|
||||
cfn=func2
|
||||
calls=3 20
|
||||
16 400
|
||||
|
||||
fn=func1
|
||||
51 100
|
||||
cfi=file2.c
|
||||
cfn=func2
|
||||
calls=2 20
|
||||
51 300
|
||||
|
||||
fl=file2.c
|
||||
fn=func2
|
||||
20 700</screen></para>
|
||||
|
||||
<para>One can see that in <function>main</function> only code from line 16
|
||||
is executed where also the other functions are called. Inclusive cost of
|
||||
<function>main</function> is 820, which is the sum of self cost 20 and costs
|
||||
spent in the calls: 400 for the single call to <function>func1</function>
|
||||
and 400 as sum for the three calls to <function>func2</function>.</para>
|
||||
|
||||
<para>Function <function>func1</function> is located in
|
||||
<filename>file1.c</filename>, the same as <function>main</function>.
|
||||
Therefore, a "cfi=" specification for the call to <function>func1</function>
|
||||
is not needed. The function <function>func1</function> only consists of code
|
||||
at line 51 of <filename>file1.c</filename>, where <function>func2</function>
|
||||
is called.</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
<sect2 id="cl-format.overview.compression1" xreflabel="Name Compression">
|
||||
<title>Name Compression</title>
|
||||
|
||||
<para>With the introduction of association specifications like calls it is
|
||||
needed to specify the same function or same file name multiple times. As
|
||||
absolute filenames or symbol names in C++ can be quite long, it is advantageous
|
||||
to be able to specify integer IDs for position specifications.
|
||||
Here, the term "position" corresponds to a file name (source or object file)
|
||||
or function name.</para>
|
||||
|
||||
<para>To support name compression, a position specification can be not only of
|
||||
the format "spec=name", but also "spec=(ID) name" to specify a mapping of an
|
||||
integer ID to a name, and "spec=(ID)" to reference a previously defined ID
|
||||
mapping. There is a separate ID mapping for each position specification,
|
||||
i.e. you can use ID 1 for both a file name and a symbol name.</para>
|
||||
|
||||
<para>With string compression, the example from 1.4 looks like this:
|
||||
<screen>events: Instructions
|
||||
|
||||
fl=(1) file1.c
|
||||
fn=(1) main
|
||||
16 20
|
||||
cfn=(2) func1
|
||||
calls=1 50
|
||||
16 400
|
||||
cfi=(2) file2.c
|
||||
cfn=(3) func2
|
||||
calls=3 20
|
||||
16 400
|
||||
|
||||
fn=(2)
|
||||
51 100
|
||||
cfi=(2)
|
||||
cfn=(3)
|
||||
calls=2 20
|
||||
51 300
|
||||
|
||||
fl=(2)
|
||||
fn=(3)
|
||||
20 700</screen></para>
|
||||
|
||||
<para>As position specifications carry no information themselves, but only change
|
||||
the meaning of subsequent cost lines or associations, they can appear
|
||||
everywhere in the file without any negative consequence. Especially, you can
|
||||
define name compression mappings directly after the header, and before any cost
|
||||
lines. Thus, the above example can also be written as
|
||||
<screen>events: Instructions
|
||||
|
||||
# define file ID mapping
|
||||
fl=(1) file1.c
|
||||
fl=(2) file2.c
|
||||
# define function ID mapping
|
||||
fn=(1) main
|
||||
fn=(2) func1
|
||||
fn=(3) func2
|
||||
|
||||
fl=(1)
|
||||
fn=(1)
|
||||
16 20
|
||||
...</screen></para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
<sect2 id="cl-format.overview.compression2" xreflabel="Subposition Compression">
|
||||
<title>Subposition Compression</title>
|
||||
|
||||
<para>If a Callgrind data file should hold costs for each assembler instruction
|
||||
of a program, you specify subposition "instr" in the "positions:" header line,
|
||||
and each cost line has to include the address of some instruction. Addresses
|
||||
are allowed to have a size of 64 bits to support 64-bit architectures. Thus,
|
||||
repeating similar, long addresses for almost every line in the data file can
|
||||
enlarge the file size quite significantly, and
|
||||
motivates for subposition compression: instead of every cost line starting with
|
||||
a 16 character long address, one is allowed to specify relative addresses.
|
||||
This relative specification is not only allowed for instruction addresses, but
|
||||
also for line numbers; both addresses and line numbers are called "subpositions".</para>
|
||||
|
||||
<para>A relative subposition always is based on the corresponding subposition
|
||||
of the last cost line, and starts with a "+" to specify a positive difference,
|
||||
a "-" to specify a negative difference, or consists of "*" to specify the same
|
||||
subposition. Because absolute subpositions always are positive (ie. never
|
||||
prefixed by "-"), any relative specification is non-ambiguous; additionally,
|
||||
absolute and relative subposition specifications can be mixed freely.
|
||||
Assume the following example (subpositions can always be specified
|
||||
as hexadecimal numbers, beginning with "0x"):
|
||||
<screen>positions: instr line
|
||||
events: ticks
|
||||
|
||||
fn=func
|
||||
0x80001234 90 1
|
||||
0x80001237 90 5
|
||||
0x80001238 91 6</screen></para>
|
||||
|
||||
<para>With subposition compression, this looks like
|
||||
<screen>positions: instr line
|
||||
events: ticks
|
||||
|
||||
fn=func
|
||||
0x80001234 90 1
|
||||
+3 * 5
|
||||
+1 +1 6</screen></para>
|
||||
|
||||
<para>Remark: For assembler annotation to work, instruction addresses have to
|
||||
be corrected to correspond to addresses found in the original binary. I.e. for
|
||||
relocatable shared objects, often a load offset has to be subtracted.</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
<sect2 id="cl-format.overview.misc" xreflabel="Miscellaneous">
|
||||
<title>Miscellaneous</title>
|
||||
|
||||
<sect3 id="cl-format.overview.misc.summary" xreflabel="Cost Summary Information">
|
||||
<title>Cost Summary Information</title>
|
||||
|
||||
<para>For the visualization to be able to show cost percentage, a sum of the
|
||||
cost of the full run has to be known. Usually, it is assumed that this is the
|
||||
sum of all cost lines in a file. But sometimes, this is not correct. Thus, you
|
||||
can specify a "summary:" line in the header giving the full cost for the
|
||||
profile run. An import filter may use this to show a progress bar
|
||||
while loading a large data file.</para>
|
||||
|
||||
</sect3>
|
||||
|
||||
<sect3 id="cl-format.overview.misc.events" xreflabel="Long Names for Event Types and inherited Types">
|
||||
<title>Long Names for Event Types and inherited Types</title>
|
||||
|
||||
<para>Event types for cost lines are specified in the "events:" line with an
|
||||
abbreviated name. For visualization, it makes sense to be able to specify some
|
||||
longer, more descriptive name. For an event type "Ir" which means "Instruction
|
||||
Fetches", this can be specified the header line
|
||||
<screen>event: Ir : Instruction Fetches
|
||||
events: Ir Dr</screen></para>
|
||||
|
||||
<para>In this example, "Dr" itself has no long name associated. The order of
|
||||
"event:" lines and the "events:" line is of no importance. Additionally,
|
||||
inherited event types can be introduced for which no raw data is available, but
|
||||
which are calculated from given types. Suppose the last example, you could add
|
||||
<screen>event: Sum = Ir + Dr</screen>
|
||||
to specify an additional event type "Sum", which is calculated by adding costs
|
||||
for "Ir and "Dr".</para>
|
||||
|
||||
</sect3>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="cl-format.reference" xreflabel="Reference">
|
||||
<title>Reference</title>
|
||||
|
||||
<sect2 id="cl-format.reference.grammar" xreflabel="Grammar">
|
||||
<title>Grammar</title>
|
||||
|
||||
<para>
|
||||
<screen>ProfileDataFile := FormatVersion? Creator? PartData*</screen>
|
||||
<screen>FormatVersion := "version: 1\n"</screen>
|
||||
<screen>Creator := "creator:" NoNewLineChar* "\n"</screen>
|
||||
<screen>PartData := (HeaderLine "\n")+ (BodyLine "\n")+</screen>
|
||||
<screen>HeaderLine := (empty line)
|
||||
| ('#' NoNewLineChar*)
|
||||
| PartDetail
|
||||
| Description
|
||||
| EventSpecification
|
||||
| CostLineDef</screen>
|
||||
<screen>PartDetail := TargetCommand | TargetID</screen>
|
||||
<screen>TargetCommand := "cmd:" Space* NoNewLineChar*</screen>
|
||||
<screen>TargetID := ("pid"|"thread"|"part") ":" Space* Number</screen>
|
||||
<screen>Description := "desc:" Space* Name Space* ":" NoNewLineChar*</screen>
|
||||
<screen>EventSpecification := "event:" Space* Name InheritedDef? LongNameDef?</screen>
|
||||
<screen>InheritedDef := "=" InheritedExpr</screen>
|
||||
<screen>InheritedExpr := Name
|
||||
| Number Space* ("*" Space*)? Name
|
||||
| InheritedExpr Space* "+" Space* InheritedExpr</screen>
|
||||
<screen>LongNameDef := ":" NoNewLineChar*</screen>
|
||||
<screen>CostLineDef := "events:" Space* Name (Space+ Name)*
|
||||
| "positions:" "instr"? (Space+ "line")?</screen>
|
||||
<screen>BodyLine := (empty line)
|
||||
| ('#' NoNewLineChar*)
|
||||
| CostLine
|
||||
| PositionSpec
|
||||
| CallSpec
|
||||
| UncondJumpSpec
|
||||
| CondJumpSpec</screen>
|
||||
<screen>CostLine := SubPositionList Costs?</screen>
|
||||
<screen>SubPositionList := (SubPosition+ Space+)+</screen>
|
||||
<screen>SubPosition := Number | "+" Number | "-" Number | "*"</screen>
|
||||
<screen>Costs := (Number Space+)+</screen>
|
||||
<screen>PositionSpec := Position "=" Space* PositionName</screen>
|
||||
<screen>Position := CostPosition | CalledPosition</screen>
|
||||
<screen>CostPosition := "ob" | "fl" | "fi" | "fe" | "fn"</screen>
|
||||
<screen>CalledPosition := " "cob" | "cfi" | "cfl" | "cfn"</screen>
|
||||
<screen>PositionName := ( "(" Number ")" )? (Space* NoNewLineChar* )?</screen>
|
||||
<screen>CallSpec := CallLine "\n" CostLine</screen>
|
||||
<screen>CallLine := "calls=" Space* Number Space+ SubPositionList</screen>
|
||||
<screen>UncondJumpSpec := "jump=" Space* Number Space+ SubPositionList</screen>
|
||||
<screen>CondJumpSpec := "jcnd=" Space* Number Space+ Number Space+ SubPositionList</screen>
|
||||
<screen>Space := " " | "\t"</screen>
|
||||
<screen>Number := HexNumber | (Digit)+</screen>
|
||||
<screen>Digit := "0" | ... | "9"</screen>
|
||||
<screen>HexNumber := "0x" (Digit | HexChar)+</screen>
|
||||
<screen>HexChar := "a" | ... | "f" | "A" | ... | "F"</screen>
|
||||
<screen>Name = Alpha (Digit | Alpha)*</screen>
|
||||
<screen>Alpha = "a" | ... | "z" | "A" | ... | "Z"</screen>
|
||||
<screen>NoNewLineChar := all characters without "\n"</screen>
|
||||
</para>
|
||||
|
||||
<para>A profile data file ("ProfileDataFile") starts with basic information
|
||||
such as the version and creator information, and then has a list of parts, where
|
||||
each part has its own header and body. Parts typically are different threads
|
||||
and/or time spans/phases within a profiled application run.</para>
|
||||
|
||||
<para>Note that callgrind_annotate currently only supports profile data files with
|
||||
one part. Callgrind may produce multiple parts for one profile run, but defaults
|
||||
to one output file for each part.</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="cl-format.reference.header" xreflabel="Description of Header Lines">
|
||||
<title>Description of Header Lines</title>
|
||||
|
||||
<para>Basic information in the first lines of a profile data file:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><computeroutput>version: number</computeroutput> [Callgrind]</para>
|
||||
<para>This is used to distinguish future profile data formats. A
|
||||
major version of 0 or 1 is supposed to be upwards compatible with
|
||||
Cachegrind's format. It is optional; if not appearing, version 1
|
||||
is assumed. Otherwise, this has to be the first header line.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>creator: string</computeroutput> [Callgrind]</para>
|
||||
<para>This is an arbitrary string to denote the creator of this file.
|
||||
Optional.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>The header for each part has an arbitrary number of lines of the format
|
||||
"key: value". Possible <emphasis>key</emphasis> values for the header are:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>pid: process id</computeroutput> [Callgrind]</para>
|
||||
<para>Optional. This specifies the process ID of the supervised application
|
||||
for which this profile was generated.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>cmd: program name + args</computeroutput> [Cachegrind]</para>
|
||||
<para>Optional. This specifies the full command line of the supervised
|
||||
application for which this profile was generated.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>part: number</computeroutput> [Callgrind]</para>
|
||||
<para>Optional. This specifies a sequentially incremented number for each dump
|
||||
generated, starting at 1.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>desc: type: value</computeroutput> [Cachegrind]</para>
|
||||
<para>This specifies various information for this dump. For some
|
||||
types, the semantic is defined, but any description type is allowed.
|
||||
Unknown types should be ignored.</para>
|
||||
<para>There are the types "I1 cache", "D1 cache", "LL cache", which
|
||||
specify parameters used for the cache simulator. These are the only
|
||||
types originally used by Cachegrind. Additionally, Callgrind uses
|
||||
the following types: "Timerange" gives a rough range of the basic
|
||||
block counter, for which the cost of this dump was collected.
|
||||
Type "Trigger" states the reason of why this trace was generated.
|
||||
E.g. program termination or forced interactive dump.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>positions: [instr] [line]</computeroutput> [Callgrind]</para>
|
||||
<para>For cost lines, this defines the semantic of the first numbers.
|
||||
Any combination of "instr", "bb" and "line" is allowed, but has to be
|
||||
in this order which corresponds to position numbers at the start of
|
||||
the cost lines later in the file.</para>
|
||||
<para>If "instr" is specified, the position is the address of an
|
||||
instruction whose execution raised the events given later on the
|
||||
line. This address is relative to the offset of the binary/shared
|
||||
library file to not have to specify relocation info. For "line",
|
||||
the position is the line number of a source file, which is
|
||||
responsible for the events raised. Note that the mapping of "instr"
|
||||
and "line" positions are given by the debugging line information
|
||||
produced by the compiler.</para>
|
||||
<para>This header line is optional, defaulting to "positions:
|
||||
line" if not specified.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>events: event type abbreviations</computeroutput> [Cachegrind]</para>
|
||||
<para>A list of short names of the event types logged in cost
|
||||
lines in this part of the profile data file. Arbitrary short
|
||||
names are allowed. The order given specifies the required order
|
||||
in cost lines. Thus, the first event type is the second or third
|
||||
number in a cost line, depending on the value of "positions".
|
||||
Required to appear for each header part exactly once.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>summary: costs</computeroutput> [Callgrind]</para>
|
||||
<para>Optional. This header line specifies a summary cost, which should be
|
||||
equal or larger than a total over all self costs. It may be larger as
|
||||
the cost lines may not represent all cost of the program run.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>totals: costs</computeroutput> [Cachegrind]</para>
|
||||
<para>Optional. Should appear at the end of the file (although
|
||||
looking like a header line). Must give the total of all cost lines,
|
||||
to allow for a consistency check.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="cl-format.reference.body" xreflabel="Description of Body Lines">
|
||||
<title>Description of Body Lines</title>
|
||||
|
||||
<para>The regular body line is a cost line consisting of one or two
|
||||
position numbers (depending on "positions:" header line, see above)
|
||||
and an array of cost numbers. A position number either is a
|
||||
line numbers into a source file or an instruction address within binary
|
||||
code, with source/binary file names specified as position names (see
|
||||
below). The cost numbers get mapped to event types in the same order
|
||||
as specified in the "events:" header line. If less numbers than event
|
||||
types are given, the costs default to zero for the remaining event
|
||||
types.</para>
|
||||
|
||||
<para>Further, there exist lines
|
||||
<computeroutput>spec=position name</computeroutput>. A position name
|
||||
is an arbitrary string. If it starts with "(" and a
|
||||
digit, it's a string in compressed format. Otherwise it's the real
|
||||
position string. This allows for file and symbol names as position
|
||||
strings, as these never start with "(" + <emphasis>digit</emphasis>.
|
||||
The compressed format is either "(" <emphasis>number</emphasis> ")"
|
||||
<emphasis>space</emphasis> <emphasis>position</emphasis> or only
|
||||
"(" <emphasis>number</emphasis> ")". The first relates
|
||||
<emphasis>position</emphasis> to <emphasis>number</emphasis> in the
|
||||
context of the given format specification from this line to the end of
|
||||
the file; it makes the (<emphasis>number</emphasis>) an alias for
|
||||
<emphasis>position</emphasis>. Compressed format is always
|
||||
optional.</para>
|
||||
|
||||
<para>Position specifications allowed:</para>
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>ob=</computeroutput> [Callgrind]</para>
|
||||
<para>The ELF object where the cost of next cost lines happens.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>fl=</computeroutput> [Cachegrind]</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>fi=</computeroutput> [Cachegrind]</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>fe=</computeroutput> [Cachegrind]</para>
|
||||
<para>The source file including the code which is responsible for
|
||||
the cost of next cost lines. "fi="/"fe=" is used when the source
|
||||
file changes inside of a function, i.e. for inlined code.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>fn=</computeroutput> [Cachegrind]</para>
|
||||
<para>The name of the function where the cost of next cost lines
|
||||
happens.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>cob=</computeroutput> [Callgrind]</para>
|
||||
<para>The ELF object of the target of the next call cost lines.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>cfi=</computeroutput> [Callgrind]</para>
|
||||
<para>The source file including the code of the target of the
|
||||
next call cost lines.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>cfl=</computeroutput> [Callgrind]</para>
|
||||
<para>Alternative spelling for <computeroutput>cfi=</computeroutput>
|
||||
specification (because of historical reasons).</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>cfn=</computeroutput> [Callgrind]</para>
|
||||
<para>The name of the target function of the next call cost
|
||||
lines.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>The last type of body line provides specific costs not just
|
||||
related to one position as regular cost lines. It starts with specific
|
||||
strings similar to position name specifications.</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>calls=count target-position</computeroutput> [Callgrind]</para>
|
||||
<para>Call executed "count" times to "target-position".
|
||||
After a "calls=" line there MUST be a cost line. This provides the source position
|
||||
of the call and the cost spent in the called function in total.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>jump=count target-position</computeroutput> [Callgrind]</para>
|
||||
<para>Unconditional jump, executed "count" times, to "target-position".</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><computeroutput>jcnd=exe-count jump-count target-position</computeroutput> [Callgrind]</para>
|
||||
<para>Conditional jump, executed "exe-count" times with "jump-count" jumps
|
||||
happening (rest is fall-through) to "target-position".</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,507 @@
|
||||
/*--------------------------------------------------------------------*/
|
||||
/*--- Callgrind ---*/
|
||||
/*--- events.c ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
This file is part of Callgrind, a Valgrind tool for call tracing.
|
||||
|
||||
Copyright (C) 2002-2015, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The GNU General Public License is contained in the file COPYING.
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
|
||||
/* This should be 2**MAX_EVENTGROUP_COUNT */
|
||||
#define MAX_EVENTSET_COUNT 1024
|
||||
|
||||
static EventGroup* eventGroup[MAX_EVENTGROUP_COUNT];
|
||||
static EventSet* eventSetTable[MAX_EVENTSET_COUNT];
|
||||
static Bool eventSets_initialized = 0;
|
||||
|
||||
static
|
||||
void initialize_event_sets(void)
|
||||
{
|
||||
Int i;
|
||||
|
||||
if (eventSets_initialized) return;
|
||||
|
||||
for(i=0; i< MAX_EVENTGROUP_COUNT; i++)
|
||||
eventGroup[i] = 0;
|
||||
|
||||
for(i=0; i< MAX_EVENTSET_COUNT; i++)
|
||||
eventSetTable[i] = 0;
|
||||
|
||||
eventSets_initialized = 1;
|
||||
}
|
||||
|
||||
static
|
||||
EventGroup* new_event_group(int id, int n)
|
||||
{
|
||||
EventGroup* eg;
|
||||
|
||||
initialize_event_sets();
|
||||
|
||||
CLG_ASSERT(id>=0 && id<MAX_EVENTGROUP_COUNT);
|
||||
CLG_ASSERT(eventGroup[id]==0);
|
||||
|
||||
eg = (EventGroup*) CLG_MALLOC("cl.events.group.1",
|
||||
sizeof(EventGroup) + n * sizeof(HChar*));
|
||||
eg->size = n;
|
||||
eventGroup[id] = eg;
|
||||
return eg;
|
||||
}
|
||||
|
||||
EventGroup* CLG_(register_event_group) (int id, const HChar* n1)
|
||||
{
|
||||
EventGroup* eg = new_event_group(id, 1);
|
||||
eg->name[0] = n1;
|
||||
|
||||
return eg;
|
||||
}
|
||||
|
||||
EventGroup* CLG_(register_event_group2)(int id, const HChar* n1,
|
||||
const HChar* n2)
|
||||
{
|
||||
EventGroup* eg = new_event_group(id, 2);
|
||||
eg->name[0] = n1;
|
||||
eg->name[1] = n2;
|
||||
|
||||
return eg;
|
||||
}
|
||||
|
||||
EventGroup* CLG_(register_event_group3)(int id, const HChar* n1,
|
||||
const HChar* n2, const HChar* n3)
|
||||
{
|
||||
EventGroup* eg = new_event_group(id, 3);
|
||||
eg->name[0] = n1;
|
||||
eg->name[1] = n2;
|
||||
eg->name[2] = n3;
|
||||
|
||||
return eg;
|
||||
}
|
||||
|
||||
EventGroup* CLG_(register_event_group4)(int id, const HChar* n1,
|
||||
const HChar* n2, const HChar* n3,
|
||||
const HChar* n4)
|
||||
{
|
||||
EventGroup* eg = new_event_group(id, 4);
|
||||
eg->name[0] = n1;
|
||||
eg->name[1] = n2;
|
||||
eg->name[2] = n3;
|
||||
eg->name[3] = n4;
|
||||
|
||||
return eg;
|
||||
}
|
||||
|
||||
EventGroup* CLG_(get_event_group)(int id)
|
||||
{
|
||||
CLG_ASSERT(id>=0 && id<MAX_EVENTGROUP_COUNT);
|
||||
|
||||
return eventGroup[id];
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
EventSet* eventset_from_mask(UInt mask)
|
||||
{
|
||||
EventSet* es;
|
||||
Int i, count, offset;
|
||||
|
||||
if (mask >= MAX_EVENTSET_COUNT) return 0;
|
||||
|
||||
initialize_event_sets();
|
||||
if (eventSetTable[mask]) return eventSetTable[mask];
|
||||
|
||||
es = (EventSet*) CLG_MALLOC("cl.events.eventset.1", sizeof(EventSet));
|
||||
es->mask = mask;
|
||||
|
||||
offset = 0;
|
||||
count = 0;
|
||||
for(i=0;i<MAX_EVENTGROUP_COUNT;i++) {
|
||||
es->offset[i] = offset;
|
||||
if ( ((mask & (1u<<i))==0) || (eventGroup[i]==0))
|
||||
continue;
|
||||
|
||||
offset += eventGroup[i]->size;
|
||||
count++;
|
||||
}
|
||||
es->size = offset;
|
||||
es->count = count;
|
||||
|
||||
eventSetTable[mask] = es;
|
||||
return es;
|
||||
}
|
||||
|
||||
EventSet* CLG_(get_event_set)(Int id)
|
||||
{
|
||||
CLG_ASSERT(id>=0 && id<MAX_EVENTGROUP_COUNT);
|
||||
return eventset_from_mask(1u << id);
|
||||
}
|
||||
|
||||
EventSet* CLG_(get_event_set2)(Int id1, Int id2)
|
||||
{
|
||||
CLG_ASSERT(id1>=0 && id1<MAX_EVENTGROUP_COUNT);
|
||||
CLG_ASSERT(id2>=0 && id2<MAX_EVENTGROUP_COUNT);
|
||||
return eventset_from_mask((1u << id1) | (1u << id2));
|
||||
}
|
||||
|
||||
EventSet* CLG_(add_event_group)(EventSet* es, Int id)
|
||||
{
|
||||
CLG_ASSERT(id>=0 && id<MAX_EVENTGROUP_COUNT);
|
||||
if (!es) es = eventset_from_mask(0);
|
||||
return eventset_from_mask(es->mask | (1u << id));
|
||||
}
|
||||
|
||||
EventSet* CLG_(add_event_group2)(EventSet* es, Int id1, Int id2)
|
||||
{
|
||||
CLG_ASSERT(id1>=0 && id1<MAX_EVENTGROUP_COUNT);
|
||||
CLG_ASSERT(id2>=0 && id2<MAX_EVENTGROUP_COUNT);
|
||||
if (!es) es = eventset_from_mask(0);
|
||||
return eventset_from_mask(es->mask | (1u << id1) | (1u << id2));
|
||||
}
|
||||
|
||||
EventSet* CLG_(add_event_set)(EventSet* es1, EventSet* es2)
|
||||
{
|
||||
if (!es1) es1 = eventset_from_mask(0);
|
||||
if (!es2) es2 = eventset_from_mask(0);
|
||||
return eventset_from_mask(es1->mask | es2->mask);
|
||||
}
|
||||
|
||||
|
||||
/* Get cost array for an event set */
|
||||
ULong* CLG_(get_eventset_cost)(EventSet* es)
|
||||
{
|
||||
return CLG_(get_costarray)(es->size);
|
||||
}
|
||||
|
||||
/* Set all costs of an event set to zero */
|
||||
void CLG_(init_cost)(EventSet* es, ULong* cost)
|
||||
{
|
||||
Int i;
|
||||
|
||||
if (!cost) return;
|
||||
|
||||
for(i=0; i<es->size; i++)
|
||||
cost[i] = 0;
|
||||
}
|
||||
|
||||
/* Set all costs of an event set to zero */
|
||||
void CLG_(init_cost_lz)(EventSet* es, ULong** cost)
|
||||
{
|
||||
Int i;
|
||||
|
||||
CLG_ASSERT(cost != 0);
|
||||
if (!(*cost))
|
||||
*cost = CLG_(get_eventset_cost)(es);
|
||||
|
||||
for(i=0; i<es->size; i++)
|
||||
(*cost)[i] = 0;
|
||||
}
|
||||
|
||||
void CLG_(zero_cost)(EventSet* es, ULong* cost)
|
||||
{
|
||||
Int i;
|
||||
|
||||
if (!cost) return;
|
||||
|
||||
for(i=0;i<es->size;i++)
|
||||
cost[i] = 0;
|
||||
}
|
||||
|
||||
Bool CLG_(is_zero_cost)(EventSet* es, ULong* cost)
|
||||
{
|
||||
Int i;
|
||||
|
||||
if (!cost) return True;
|
||||
|
||||
for(i=0; i<es->size; i++)
|
||||
if (cost[i] != 0) return False;
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
void CLG_(copy_cost)(EventSet* es, ULong* dst, ULong* src)
|
||||
{
|
||||
Int i;
|
||||
|
||||
if (!src) {
|
||||
CLG_(zero_cost)(es, dst);
|
||||
return;
|
||||
}
|
||||
CLG_ASSERT(dst != 0);
|
||||
|
||||
for(i=0;i<es->size;i++)
|
||||
dst[i] = src[i];
|
||||
}
|
||||
|
||||
void CLG_(copy_cost_lz)(EventSet* es, ULong** pdst, ULong* src)
|
||||
{
|
||||
Int i;
|
||||
ULong* dst;
|
||||
|
||||
CLG_ASSERT(pdst != 0);
|
||||
|
||||
if (!src) {
|
||||
CLG_(zero_cost)(es, *pdst);
|
||||
return;
|
||||
}
|
||||
dst = *pdst;
|
||||
if (!dst)
|
||||
dst = *pdst = CLG_(get_eventset_cost)(es);
|
||||
|
||||
for(i=0;i<es->size;i++)
|
||||
dst[i] = src[i];
|
||||
}
|
||||
|
||||
void CLG_(add_cost)(EventSet* es, ULong* dst, ULong* src)
|
||||
{
|
||||
Int i;
|
||||
|
||||
if (!src) return;
|
||||
CLG_ASSERT(dst != 0);
|
||||
|
||||
for(i=0; i<es->size; i++)
|
||||
dst[i] += src[i];
|
||||
}
|
||||
|
||||
void CLG_(add_cost_lz)(EventSet* es, ULong** pdst, ULong* src)
|
||||
{
|
||||
Int i;
|
||||
ULong* dst;
|
||||
|
||||
if (!src) return;
|
||||
CLG_ASSERT(pdst != 0);
|
||||
|
||||
dst = *pdst;
|
||||
if (!dst) {
|
||||
dst = *pdst = CLG_(get_eventset_cost)(es);
|
||||
CLG_(copy_cost)(es, dst, src);
|
||||
return;
|
||||
}
|
||||
|
||||
for(i=0; i<es->size; i++)
|
||||
dst[i] += src[i];
|
||||
}
|
||||
|
||||
/* Adds src to dst and zeros src. Returns false if nothing changed */
|
||||
Bool CLG_(add_and_zero_cost)(EventSet* es, ULong* dst, ULong* src)
|
||||
{
|
||||
Int i;
|
||||
Bool is_nonzero = False;
|
||||
|
||||
CLG_ASSERT((es != 0) && (dst != 0));
|
||||
if (!src) return False;
|
||||
|
||||
for(i=0; i<es->size; i++) {
|
||||
if (src[i]==0) continue;
|
||||
dst[i] += src[i];
|
||||
src[i] = 0;
|
||||
is_nonzero = True;
|
||||
}
|
||||
|
||||
return is_nonzero;
|
||||
}
|
||||
|
||||
/* Adds src to dst and zeros src. Returns false if nothing changed */
|
||||
Bool CLG_(add_and_zero_cost2)(EventSet* esDst, ULong* dst,
|
||||
EventSet* esSrc, ULong* src)
|
||||
{
|
||||
Int i,j;
|
||||
Bool is_nonzero = False;
|
||||
UInt mask;
|
||||
EventGroup *eg;
|
||||
ULong *egDst, *egSrc;
|
||||
|
||||
CLG_ASSERT((esDst != 0) && (dst != 0) && (esSrc != 0));
|
||||
if (!src) return False;
|
||||
|
||||
for(i=0, mask=1; i<MAX_EVENTGROUP_COUNT; i++, mask=mask<<1) {
|
||||
if ((esSrc->mask & mask)==0) continue;
|
||||
if (eventGroup[i] ==0) continue;
|
||||
|
||||
/* if src has a subset, dst must have, too */
|
||||
CLG_ASSERT((esDst->mask & mask)>0);
|
||||
eg = eventGroup[i];
|
||||
egSrc = src + esSrc->offset[i];
|
||||
egDst = dst + esDst->offset[i];
|
||||
for(j=0; j<eg->size; j++) {
|
||||
if (egSrc[j]==0) continue;
|
||||
egDst[j] += egSrc[j];
|
||||
egSrc[j] = 0;
|
||||
is_nonzero = True;
|
||||
}
|
||||
}
|
||||
|
||||
return is_nonzero;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Adds difference of new and old to dst, and set old to new.
|
||||
* Returns false if nothing changed */
|
||||
Bool CLG_(add_diff_cost)(EventSet* es, ULong* dst, ULong* old, ULong* new_cost)
|
||||
{
|
||||
Int i;
|
||||
Bool is_nonzero = False;
|
||||
|
||||
CLG_ASSERT((es != 0) && (dst != 0));
|
||||
CLG_ASSERT(old && new_cost);
|
||||
|
||||
for(i=0; i<es->size; i++) {
|
||||
if (new_cost[i] == old[i]) continue;
|
||||
dst[i] += new_cost[i] - old[i];
|
||||
old[i] = new_cost[i];
|
||||
is_nonzero = True;
|
||||
}
|
||||
|
||||
return is_nonzero;
|
||||
}
|
||||
|
||||
Bool CLG_(add_diff_cost_lz)(EventSet* es, ULong** pdst, ULong* old, ULong* new_cost)
|
||||
{
|
||||
Int i;
|
||||
ULong* dst;
|
||||
Bool is_nonzero = False;
|
||||
|
||||
CLG_ASSERT((es != 0) && (pdst != 0));
|
||||
CLG_ASSERT(old && new_cost);
|
||||
|
||||
dst = *pdst;
|
||||
if (!dst) {
|
||||
dst = *pdst = CLG_(get_eventset_cost)(es);
|
||||
CLG_(zero_cost)(es, dst);
|
||||
}
|
||||
|
||||
for(i=0; i<es->size; i++) {
|
||||
if (new_cost[i] == old[i]) continue;
|
||||
dst[i] += new_cost[i] - old[i];
|
||||
old[i] = new_cost[i];
|
||||
is_nonzero = True;
|
||||
}
|
||||
|
||||
return is_nonzero;
|
||||
}
|
||||
|
||||
|
||||
/* Allocate space for an event mapping */
|
||||
EventMapping* CLG_(get_eventmapping)(EventSet* es)
|
||||
{
|
||||
EventMapping* em;
|
||||
|
||||
CLG_ASSERT(es != 0);
|
||||
|
||||
em = (EventMapping*) CLG_MALLOC("cl.events.geMapping.1",
|
||||
sizeof(EventMapping) +
|
||||
sizeof(struct EventMappingEntry) *
|
||||
es->size);
|
||||
em->capacity = es->size;
|
||||
em->size = 0;
|
||||
em->es = es;
|
||||
|
||||
return em;
|
||||
}
|
||||
|
||||
void CLG_(append_event)(EventMapping* em, const HChar* n)
|
||||
{
|
||||
Int i, j, offset = 0;
|
||||
UInt mask;
|
||||
EventGroup* eg;
|
||||
|
||||
CLG_ASSERT(em != 0);
|
||||
for(i=0, mask=1; i<MAX_EVENTGROUP_COUNT; i++, mask=mask<<1) {
|
||||
if ((em->es->mask & mask)==0) continue;
|
||||
if (eventGroup[i] ==0) continue;
|
||||
|
||||
eg = eventGroup[i];
|
||||
for(j=0; j<eg->size; j++, offset++) {
|
||||
if (VG_(strcmp)(n, eg->name[j])!=0)
|
||||
continue;
|
||||
|
||||
CLG_ASSERT(em->capacity > em->size);
|
||||
em->entry[em->size].group = i;
|
||||
em->entry[em->size].index = j;
|
||||
em->entry[em->size].offset = offset;
|
||||
em->size++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Returns pointer to dynamically string. The string will be overwritten
|
||||
with each invocation. */
|
||||
HChar *CLG_(eventmapping_as_string)(const EventMapping* em)
|
||||
{
|
||||
Int i;
|
||||
EventGroup* eg;
|
||||
|
||||
CLG_ASSERT(em != 0);
|
||||
|
||||
XArray *xa = VG_(newXA)(VG_(malloc), "cl.events.emas", VG_(free),
|
||||
sizeof(HChar));
|
||||
|
||||
for(i=0; i< em->size; i++) {
|
||||
if (i > 0) {
|
||||
VG_(xaprintf)(xa, "%c", ' ');
|
||||
}
|
||||
eg = eventGroup[em->entry[i].group];
|
||||
CLG_ASSERT(eg != 0);
|
||||
VG_(xaprintf)(xa, "%s", eg->name[em->entry[i].index]);
|
||||
}
|
||||
VG_(xaprintf)(xa, "%c", '\0'); // zero terminate the string
|
||||
|
||||
HChar *buf = VG_(strdup)("cl.events.emas", VG_(indexXA)(xa, 0));
|
||||
VG_(deleteXA)(xa);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Returns pointer to dynamically allocated string. Caller needs to
|
||||
VG_(free) it. */
|
||||
HChar *CLG_(mappingcost_as_string)(const EventMapping* em, const ULong* c)
|
||||
{
|
||||
Int i, skipped = 0;
|
||||
|
||||
if (!c || em->size==0) return VG_(strdup)("cl.events.mcas", "");
|
||||
|
||||
XArray *xa = VG_(newXA)(VG_(malloc), "cl.events.mcas", VG_(free),
|
||||
sizeof(HChar));
|
||||
|
||||
/* At least one entry */
|
||||
VG_(xaprintf)(xa, "%llu", c[em->entry[0].offset]);
|
||||
|
||||
for(i=1; i<em->size; i++) {
|
||||
if (c[em->entry[i].offset] == 0) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
while(skipped>0) {
|
||||
VG_(xaprintf)(xa, " 0");
|
||||
skipped--;
|
||||
}
|
||||
VG_(xaprintf)(xa, " %llu", c[em->entry[i].offset]);
|
||||
}
|
||||
VG_(xaprintf)(xa, "%c", '\0'); // zero terminate the string
|
||||
|
||||
HChar *buf = VG_(strdup)("cl.events.mas", VG_(indexXA)(xa, 0));
|
||||
VG_(deleteXA)(xa);
|
||||
|
||||
return buf;
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*--------------------------------------------------------------------*/
|
||||
/*--- Callgrind ---*/
|
||||
/*--- events.h ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
This file is part of Callgrind, a Valgrind tool for call tracing.
|
||||
|
||||
Copyright (C) 2002-2015, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The GNU General Public License is contained in the file COPYING.
|
||||
*/
|
||||
|
||||
/* Abstractions for 64-bit cost lists (events.h) */
|
||||
|
||||
#ifndef CLG_EVENTS
|
||||
#define CLG_EVENTS
|
||||
|
||||
#include "pub_tool_basics.h"
|
||||
|
||||
#define CLG_(str) VGAPPEND(vgCallgrind_,str)
|
||||
|
||||
/* Event groups consist of one or more named event types.
|
||||
* Event sets are constructed from such event groups.
|
||||
*
|
||||
* Event groups have to be registered globally with a unique ID
|
||||
* before they can be used in an event set.
|
||||
* A group can appear at most once in a event set.
|
||||
*/
|
||||
|
||||
#define MAX_EVENTGROUP_COUNT 10
|
||||
|
||||
typedef struct _EventGroup EventGroup;
|
||||
struct _EventGroup {
|
||||
Int size;
|
||||
const HChar* name[0];
|
||||
};
|
||||
|
||||
/* return 0 if event group can not be registered */
|
||||
EventGroup* CLG_(register_event_group) (int id, const HChar*);
|
||||
EventGroup* CLG_(register_event_group2)(int id, const HChar*, const HChar*);
|
||||
EventGroup* CLG_(register_event_group3)(int id, const HChar*, const HChar*,
|
||||
const HChar*);
|
||||
EventGroup* CLG_(register_event_group4)(int id, const HChar*, const HChar*,
|
||||
const HChar*, const HChar*);
|
||||
EventGroup* CLG_(get_event_group)(int id);
|
||||
|
||||
/* Event sets are defined by event groups they consist of. */
|
||||
|
||||
typedef struct _EventSet EventSet;
|
||||
struct _EventSet {
|
||||
/* if subset with ID x is in the set, then bit x is set */
|
||||
UInt mask;
|
||||
Int count;
|
||||
Int size;
|
||||
Int offset[MAX_EVENTGROUP_COUNT];
|
||||
};
|
||||
|
||||
/* Same event set is returned when requesting same event groups */
|
||||
EventSet* CLG_(get_event_set)(Int id);
|
||||
EventSet* CLG_(get_event_set2)(Int id1, Int id2);
|
||||
EventSet* CLG_(add_event_group)(EventSet*, Int id);
|
||||
EventSet* CLG_(add_event_group2)(EventSet*, Int id1, Int id2);
|
||||
EventSet* CLG_(add_event_set)(EventSet*, EventSet*);
|
||||
|
||||
|
||||
/* Operations on costs. A cost pointer of 0 means zero cost.
|
||||
* Functions ending in _lz allocate cost arrays only when needed
|
||||
*/
|
||||
ULong* CLG_(get_eventset_cost)(EventSet*);
|
||||
/* Set costs of event set to 0 */
|
||||
void CLG_(init_cost)(EventSet*,ULong*);
|
||||
/* This always allocates counter and sets them to 0 */
|
||||
void CLG_(init_cost_lz)(EventSet*,ULong**);
|
||||
/* Set costs of an event set to zero */
|
||||
void CLG_(zero_cost)(EventSet*,ULong*);
|
||||
Bool CLG_(is_zero_cost)(EventSet*,ULong*);
|
||||
void CLG_(copy_cost)(EventSet*,ULong* dst, ULong* src);
|
||||
void CLG_(copy_cost_lz)(EventSet*,ULong** pdst, ULong* src);
|
||||
void CLG_(add_cost)(EventSet*,ULong* dst, ULong* src);
|
||||
void CLG_(add_cost_lz)(EventSet*,ULong** pdst, ULong* src);
|
||||
/* Adds src to dst and zeros src. Returns false if nothing changed */
|
||||
Bool CLG_(add_and_zero_cost)(EventSet*,ULong* dst, ULong* src);
|
||||
Bool CLG_(add_and_zero_cost2)(EventSet*,ULong* dst,EventSet*,ULong* src);
|
||||
/* Adds difference of new and old to to dst, and set old to new.
|
||||
* Returns false if nothing changed */
|
||||
Bool CLG_(add_diff_cost)(EventSet*,ULong* dst, ULong* old, ULong* new_cost);
|
||||
Bool CLG_(add_diff_cost_lz)(EventSet*,ULong** pdst, ULong* old, ULong* new_cost);
|
||||
|
||||
/* EventMapping: An ordered subset of events from an event set.
|
||||
* This is used to print out part of an EventSet, or in another order.
|
||||
*/
|
||||
struct EventMappingEntry {
|
||||
Int group;
|
||||
Int index;
|
||||
Int offset;
|
||||
};
|
||||
typedef struct _EventMapping EventMapping;
|
||||
struct _EventMapping {
|
||||
EventSet* es;
|
||||
Int size;
|
||||
Int capacity;
|
||||
struct EventMappingEntry entry[0];
|
||||
};
|
||||
|
||||
/* Allocate space for an event mapping */
|
||||
EventMapping* CLG_(get_eventmapping)(EventSet*);
|
||||
void CLG_(append_event)(EventMapping*, const HChar*);
|
||||
/* Returns event mapping as a character string. That string is dynamically
|
||||
allocated and it is the caller's responsibility to free it.
|
||||
The function never returns NULL. */
|
||||
HChar *CLG_(eventmapping_as_string)(const EventMapping*);
|
||||
/* Returns mapping cost as a character string. That string is dynamically
|
||||
allocated and it is the caller's responsibility to free it.
|
||||
The function never returns NULL. */
|
||||
HChar *CLG_(mappingcost_as_string)(const EventMapping*, const ULong*);
|
||||
|
||||
#endif /* CLG_EVENTS */
|
||||
@@ -0,0 +1,686 @@
|
||||
/*--------------------------------------------------------------------*/
|
||||
/*--- Callgrind ---*/
|
||||
/*--- ct_fn.c ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
This file is part of Callgrind, a Valgrind tool for call tracing.
|
||||
|
||||
Copyright (C) 2002-2015, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The GNU General Public License is contained in the file COPYING.
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
|
||||
#define N_INITIAL_FN_ARRAY_SIZE 10071
|
||||
|
||||
static fn_array current_fn_active;
|
||||
|
||||
static Addr runtime_resolve_addr = 0;
|
||||
static int runtime_resolve_length = 0;
|
||||
|
||||
// a code pattern is a list of tuples (start offset, length)
|
||||
struct chunk_t { int start, len; };
|
||||
struct pattern
|
||||
{
|
||||
const HChar* name;
|
||||
int len;
|
||||
struct chunk_t chunk[];
|
||||
};
|
||||
|
||||
/* Scan for a pattern in the code of an ELF object.
|
||||
* If found, return true and set runtime_resolve_{addr,length}
|
||||
*/
|
||||
__attribute__((unused)) // Possibly; depends on the platform.
|
||||
static Bool check_code(obj_node* obj,
|
||||
UChar code[], struct pattern* pat)
|
||||
{
|
||||
Bool found;
|
||||
Addr addr, end;
|
||||
int chunk, start, len;
|
||||
|
||||
/* first chunk of pattern should always start at offset 0 and
|
||||
* have at least 3 bytes */
|
||||
CLG_ASSERT((pat->chunk[0].start == 0) && (pat->chunk[0].len >2));
|
||||
|
||||
CLG_DEBUG(1, "check_code: %s, pattern %s, check %d bytes of [%x %x %x...]\n",
|
||||
obj->name, pat->name, pat->chunk[0].len, code[0], code[1], code[2]);
|
||||
|
||||
end = obj->start + obj->size - pat->len;
|
||||
addr = obj->start;
|
||||
while(addr < end) {
|
||||
found = (VG_(memcmp)( (void*)addr, code, pat->chunk[0].len) == 0);
|
||||
|
||||
if (found) {
|
||||
chunk = 1;
|
||||
while(1) {
|
||||
start = pat->chunk[chunk].start;
|
||||
len = pat->chunk[chunk].len;
|
||||
if (len == 0) break;
|
||||
|
||||
CLG_ASSERT(len >2);
|
||||
CLG_DEBUG(1, " found chunk %d at %#lx, checking %d bytes "
|
||||
"of [%x %x %x...]\n",
|
||||
chunk-1, addr - obj->start, len,
|
||||
code[start], code[start+1], code[start+2]);
|
||||
|
||||
if (VG_(memcmp)( (void*)(addr+start), code+start, len) != 0) {
|
||||
found = False;
|
||||
break;
|
||||
}
|
||||
chunk++;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
CLG_DEBUG(1, "found at offset %#lx.\n", addr - obj->start);
|
||||
if (VG_(clo_verbosity) > 1)
|
||||
VG_(message)(Vg_DebugMsg, "Found runtime_resolve (%s): "
|
||||
"%s +%#lx=%#lx, length %d\n",
|
||||
pat->name, obj->name + obj->last_slash_pos,
|
||||
addr - obj->start, addr, pat->len);
|
||||
|
||||
runtime_resolve_addr = addr;
|
||||
runtime_resolve_length = pat->len;
|
||||
return True;
|
||||
}
|
||||
}
|
||||
addr++;
|
||||
}
|
||||
CLG_DEBUG(1, " found nothing.\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
|
||||
/* _ld_runtime_resolve, located in ld.so, needs special handling:
|
||||
* The jump at end into the resolved function should not be
|
||||
* represented as a call (as usually done in callgrind with jumps),
|
||||
* but as a return + call. Otherwise, the repeated existance of
|
||||
* _ld_runtime_resolve in call chains will lead to huge cycles,
|
||||
* making the profile almost worthless.
|
||||
*
|
||||
* If ld.so is stripped, the symbol will not appear. But as this
|
||||
* function is handcrafted assembler, we search for it.
|
||||
*
|
||||
* We stop if the ELF object name does not seem to be the runtime linker
|
||||
*/
|
||||
static Bool search_runtime_resolve(obj_node* obj)
|
||||
{
|
||||
#if defined(VGP_x86_linux)
|
||||
static UChar code[] = {
|
||||
/* 0*/ 0x50, 0x51, 0x52, 0x8b, 0x54, 0x24, 0x10, 0x8b,
|
||||
/* 8*/ 0x44, 0x24, 0x0c, 0xe8, 0x70, 0x01, 0x00, 0x00,
|
||||
/*16*/ 0x5a, 0x59, 0x87, 0x04, 0x24, 0xc2, 0x08, 0x00 };
|
||||
/* Check ranges [0-11] and [16-23] ([12-15] is an absolute address) */
|
||||
static struct pattern pat = {
|
||||
"x86-def", 24, {{ 0,12 }, { 16,8 }, { 24,0}} };
|
||||
|
||||
/* Pattern for glibc-2.8 on OpenSuse11.0 */
|
||||
static UChar code_28[] = {
|
||||
/* 0*/ 0x50, 0x51, 0x52, 0x8b, 0x54, 0x24, 0x10, 0x8b,
|
||||
/* 8*/ 0x44, 0x24, 0x0c, 0xe8, 0x70, 0x01, 0x00, 0x00,
|
||||
/*16*/ 0x5a, 0x8b, 0x0c, 0x24, 0x89, 0x04, 0x24, 0x8b,
|
||||
/*24*/ 0x44, 0x24, 0x04, 0xc2, 0x0c, 0x00 };
|
||||
static struct pattern pat_28 = {
|
||||
"x86-glibc2.8", 30, {{ 0,12 }, { 16,14 }, { 30,0}} };
|
||||
|
||||
if (VG_(strncmp)(obj->name, "/lib/ld", 7) != 0) return False;
|
||||
if (check_code(obj, code, &pat)) return True;
|
||||
if (check_code(obj, code_28, &pat_28)) return True;
|
||||
return False;
|
||||
#endif
|
||||
|
||||
#if defined(VGP_ppc32_linux)
|
||||
static UChar code[] = {
|
||||
/* 0*/ 0x94, 0x21, 0xff, 0xc0, 0x90, 0x01, 0x00, 0x0c,
|
||||
/* 8*/ 0x90, 0x61, 0x00, 0x10, 0x90, 0x81, 0x00, 0x14,
|
||||
/*16*/ 0x7d, 0x83, 0x63, 0x78, 0x90, 0xa1, 0x00, 0x18,
|
||||
/*24*/ 0x7d, 0x64, 0x5b, 0x78, 0x90, 0xc1, 0x00, 0x1c,
|
||||
/*32*/ 0x7c, 0x08, 0x02, 0xa6, 0x90, 0xe1, 0x00, 0x20,
|
||||
/*40*/ 0x90, 0x01, 0x00, 0x30, 0x91, 0x01, 0x00, 0x24,
|
||||
/*48*/ 0x7c, 0x00, 0x00, 0x26, 0x91, 0x21, 0x00, 0x28,
|
||||
/*56*/ 0x91, 0x41, 0x00, 0x2c, 0x90, 0x01, 0x00, 0x08,
|
||||
/*64*/ 0x48, 0x00, 0x02, 0x91, 0x7c, 0x69, 0x03, 0xa6, /* at 64: bl aff0 <fixup> */
|
||||
/*72*/ 0x80, 0x01, 0x00, 0x30, 0x81, 0x41, 0x00, 0x2c,
|
||||
/*80*/ 0x81, 0x21, 0x00, 0x28, 0x7c, 0x08, 0x03, 0xa6,
|
||||
/*88*/ 0x81, 0x01, 0x00, 0x24, 0x80, 0x01, 0x00, 0x08,
|
||||
/*96*/ 0x80, 0xe1, 0x00, 0x20, 0x80, 0xc1, 0x00, 0x1c,
|
||||
/*104*/0x7c, 0x0f, 0xf1, 0x20, 0x80, 0xa1, 0x00, 0x18,
|
||||
/*112*/0x80, 0x81, 0x00, 0x14, 0x80, 0x61, 0x00, 0x10,
|
||||
/*120*/0x80, 0x01, 0x00, 0x0c, 0x38, 0x21, 0x00, 0x40,
|
||||
/*128*/0x4e, 0x80, 0x04, 0x20 };
|
||||
static struct pattern pat = {
|
||||
"ppc32-def", 132, {{ 0,65 }, { 68,64 }, { 132,0 }} };
|
||||
|
||||
if (VG_(strncmp)(obj->name, "/lib/ld", 7) != 0) return False;
|
||||
return check_code(obj, code, &pat);
|
||||
#endif
|
||||
|
||||
#if defined(VGP_amd64_linux)
|
||||
static UChar code[] = {
|
||||
/* 0*/ 0x48, 0x83, 0xec, 0x38, 0x48, 0x89, 0x04, 0x24,
|
||||
/* 8*/ 0x48, 0x89, 0x4c, 0x24, 0x08, 0x48, 0x89, 0x54, 0x24, 0x10,
|
||||
/*18*/ 0x48, 0x89, 0x74, 0x24, 0x18, 0x48, 0x89, 0x7c, 0x24, 0x20,
|
||||
/*28*/ 0x4c, 0x89, 0x44, 0x24, 0x28, 0x4c, 0x89, 0x4c, 0x24, 0x30,
|
||||
/*38*/ 0x48, 0x8b, 0x74, 0x24, 0x40, 0x49, 0x89, 0xf3,
|
||||
/*46*/ 0x4c, 0x01, 0xde, 0x4c, 0x01, 0xde, 0x48, 0xc1, 0xe6, 0x03,
|
||||
/*56*/ 0x48, 0x8b, 0x7c, 0x24, 0x38, 0xe8, 0xee, 0x01, 0x00, 0x00,
|
||||
/*66*/ 0x49, 0x89, 0xc3, 0x4c, 0x8b, 0x4c, 0x24, 0x30,
|
||||
/*74*/ 0x4c, 0x8b, 0x44, 0x24, 0x28, 0x48, 0x8b, 0x7c, 0x24, 0x20,
|
||||
/*84*/ 0x48, 0x8b, 0x74, 0x24, 0x18, 0x48, 0x8b, 0x54, 0x24, 0x10,
|
||||
/*94*/ 0x48, 0x8b, 0x4c, 0x24, 0x08, 0x48, 0x8b, 0x04, 0x24,
|
||||
/*103*/0x48, 0x83, 0xc4, 0x48, 0x41, 0xff, 0xe3 };
|
||||
static struct pattern pat = {
|
||||
"amd64-def", 110, {{ 0,62 }, { 66,44 }, { 110,0 }} };
|
||||
|
||||
if ((VG_(strncmp)(obj->name, "/lib/ld", 7) != 0) &&
|
||||
(VG_(strncmp)(obj->name, "/lib64/ld", 9) != 0)) return False;
|
||||
return check_code(obj, code, &pat);
|
||||
#endif
|
||||
|
||||
/* For other platforms, no patterns known */
|
||||
return False;
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Object/File/Function hash entry operations ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
/* Object hash table, fixed */
|
||||
static obj_node* obj_table[N_OBJ_ENTRIES];
|
||||
|
||||
void CLG_(init_obj_table)()
|
||||
{
|
||||
Int i;
|
||||
for (i = 0; i < N_OBJ_ENTRIES; i++)
|
||||
obj_table[i] = 0;
|
||||
}
|
||||
|
||||
#define HASH_CONSTANT 256
|
||||
|
||||
static UInt str_hash(const HChar *s, UInt table_size)
|
||||
{
|
||||
int hash_value = 0;
|
||||
for ( ; *s; s++)
|
||||
hash_value = (HASH_CONSTANT * hash_value + *s) % table_size;
|
||||
return hash_value;
|
||||
}
|
||||
|
||||
|
||||
static const HChar* anonymous_obj = "???";
|
||||
|
||||
static __inline__
|
||||
obj_node* new_obj_node(DebugInfo* di, obj_node* next)
|
||||
{
|
||||
Int i;
|
||||
obj_node* obj;
|
||||
|
||||
obj = (obj_node*) CLG_MALLOC("cl.fn.non.1", sizeof(obj_node));
|
||||
obj->name = di ? VG_(strdup)( "cl.fn.non.2",
|
||||
VG_(DebugInfo_get_filename)(di) )
|
||||
: anonymous_obj;
|
||||
for (i = 0; i < N_FILE_ENTRIES; i++) {
|
||||
obj->files[i] = NULL;
|
||||
}
|
||||
CLG_(stat).distinct_objs ++;
|
||||
obj->number = CLG_(stat).distinct_objs;
|
||||
/* JRS 2008 Feb 19: maybe rename .start/.size/.offset to
|
||||
.text_avma/.text_size/.test_bias to make it clearer what these
|
||||
fields really mean */
|
||||
obj->start = di ? VG_(DebugInfo_get_text_avma)(di) : 0;
|
||||
obj->size = di ? VG_(DebugInfo_get_text_size)(di) : 0;
|
||||
obj->offset = di ? VG_(DebugInfo_get_text_bias)(di) : 0;
|
||||
obj->next = next;
|
||||
|
||||
// not only used for debug output (see static.c)
|
||||
obj->last_slash_pos = 0;
|
||||
i = 0;
|
||||
while(obj->name[i]) {
|
||||
if (obj->name[i]=='/') obj->last_slash_pos = i+1;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (runtime_resolve_addr == 0) search_runtime_resolve(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj_node* CLG_(get_obj_node)(DebugInfo* di)
|
||||
{
|
||||
obj_node* curr_obj_node;
|
||||
UInt objname_hash;
|
||||
const HChar* obj_name;
|
||||
|
||||
obj_name = di ? VG_(DebugInfo_get_filename)(di) : anonymous_obj;
|
||||
|
||||
/* lookup in obj hash */
|
||||
objname_hash = str_hash(obj_name, N_OBJ_ENTRIES);
|
||||
curr_obj_node = obj_table[objname_hash];
|
||||
while (NULL != curr_obj_node &&
|
||||
VG_(strcmp)(obj_name, curr_obj_node->name) != 0) {
|
||||
curr_obj_node = curr_obj_node->next;
|
||||
}
|
||||
if (NULL == curr_obj_node) {
|
||||
obj_table[objname_hash] = curr_obj_node =
|
||||
new_obj_node(di, obj_table[objname_hash]);
|
||||
}
|
||||
|
||||
return curr_obj_node;
|
||||
}
|
||||
|
||||
|
||||
static __inline__
|
||||
file_node* new_file_node(const HChar *filename,
|
||||
obj_node* obj, file_node* next)
|
||||
{
|
||||
Int i;
|
||||
file_node* file = (file_node*) CLG_MALLOC("cl.fn.nfn.1",
|
||||
sizeof(file_node));
|
||||
file->name = VG_(strdup)("cl.fn.nfn.2", filename);
|
||||
for (i = 0; i < N_FN_ENTRIES; i++) {
|
||||
file->fns[i] = NULL;
|
||||
}
|
||||
CLG_(stat).distinct_files++;
|
||||
file->number = CLG_(stat).distinct_files;
|
||||
file->obj = obj;
|
||||
file->next = next;
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
file_node* CLG_(get_file_node)(obj_node* curr_obj_node,
|
||||
const HChar *dir, const HChar *file)
|
||||
{
|
||||
file_node* curr_file_node;
|
||||
UInt filename_hash;
|
||||
|
||||
/* Build up an absolute pathname, if there is a directory available */
|
||||
HChar filename[VG_(strlen)(dir) + 1 + VG_(strlen)(file) + 1];
|
||||
VG_(strcpy)(filename, dir);
|
||||
if (filename[0] != '\0') {
|
||||
VG_(strcat)(filename, "/");
|
||||
}
|
||||
VG_(strcat)(filename, file);
|
||||
|
||||
/* lookup in file hash */
|
||||
filename_hash = str_hash(filename, N_FILE_ENTRIES);
|
||||
curr_file_node = curr_obj_node->files[filename_hash];
|
||||
while (NULL != curr_file_node &&
|
||||
VG_(strcmp)(filename, curr_file_node->name) != 0) {
|
||||
curr_file_node = curr_file_node->next;
|
||||
}
|
||||
if (NULL == curr_file_node) {
|
||||
curr_obj_node->files[filename_hash] = curr_file_node =
|
||||
new_file_node(filename, curr_obj_node,
|
||||
curr_obj_node->files[filename_hash]);
|
||||
}
|
||||
|
||||
return curr_file_node;
|
||||
}
|
||||
|
||||
/* forward decl. */
|
||||
static void resize_fn_array(void);
|
||||
|
||||
static __inline__
|
||||
fn_node* new_fn_node(const HChar *fnname,
|
||||
file_node* file, fn_node* next)
|
||||
{
|
||||
fn_node* fn = (fn_node*) CLG_MALLOC("cl.fn.nfnnd.1",
|
||||
sizeof(fn_node));
|
||||
fn->name = VG_(strdup)("cl.fn.nfnnd.2", fnname);
|
||||
|
||||
CLG_(stat).distinct_fns++;
|
||||
fn->number = CLG_(stat).distinct_fns;
|
||||
fn->last_cxt = 0;
|
||||
fn->pure_cxt = 0;
|
||||
fn->file = file;
|
||||
fn->next = next;
|
||||
|
||||
fn->dump_before = False;
|
||||
fn->dump_after = False;
|
||||
fn->zero_before = False;
|
||||
fn->toggle_collect = False;
|
||||
fn->skip = False;
|
||||
fn->pop_on_jump = CLG_(clo).pop_on_jump;
|
||||
fn->is_malloc = False;
|
||||
fn->is_realloc = False;
|
||||
fn->is_free = False;
|
||||
|
||||
fn->group = 0;
|
||||
fn->separate_callers = CLG_(clo).separate_callers;
|
||||
fn->separate_recursions = CLG_(clo).separate_recursions;
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
fn->verbosity = -1;
|
||||
#endif
|
||||
|
||||
if (CLG_(stat).distinct_fns >= current_fn_active.size)
|
||||
resize_fn_array();
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
||||
/* Get a function node in hash2 with known file node.
|
||||
* hash nodes are created if needed
|
||||
*/
|
||||
static
|
||||
fn_node* get_fn_node_infile(file_node* curr_file_node,
|
||||
const HChar *fnname)
|
||||
{
|
||||
fn_node* curr_fn_node;
|
||||
UInt fnname_hash;
|
||||
|
||||
CLG_ASSERT(curr_file_node != 0);
|
||||
|
||||
/* lookup in function hash */
|
||||
fnname_hash = str_hash(fnname, N_FN_ENTRIES);
|
||||
curr_fn_node = curr_file_node->fns[fnname_hash];
|
||||
while (NULL != curr_fn_node &&
|
||||
VG_(strcmp)(fnname, curr_fn_node->name) != 0) {
|
||||
curr_fn_node = curr_fn_node->next;
|
||||
}
|
||||
if (NULL == curr_fn_node) {
|
||||
curr_file_node->fns[fnname_hash] = curr_fn_node =
|
||||
new_fn_node(fnname, curr_file_node,
|
||||
curr_file_node->fns[fnname_hash]);
|
||||
}
|
||||
|
||||
return curr_fn_node;
|
||||
}
|
||||
|
||||
|
||||
/* Get a function node in a Segment.
|
||||
* Hash nodes are created if needed.
|
||||
*/
|
||||
static __inline__
|
||||
fn_node* get_fn_node_inseg(DebugInfo* di,
|
||||
const HChar *dirname,
|
||||
const HChar *filename,
|
||||
const HChar *fnname)
|
||||
{
|
||||
obj_node *obj = CLG_(get_obj_node)(di);
|
||||
file_node *file = CLG_(get_file_node)(obj, dirname, filename);
|
||||
fn_node *fn = get_fn_node_infile(file, fnname);
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
||||
Bool CLG_(get_debug_info)(Addr instr_addr,
|
||||
const HChar **dir,
|
||||
const HChar **file,
|
||||
const HChar **fn_name, UInt* line_num,
|
||||
DebugInfo** pDebugInfo)
|
||||
{
|
||||
Bool found_file_line, found_fn, result = True;
|
||||
UInt line;
|
||||
|
||||
CLG_DEBUG(6, " + get_debug_info(%#lx)\n", instr_addr);
|
||||
|
||||
if (pDebugInfo) {
|
||||
*pDebugInfo = VG_(find_DebugInfo)(instr_addr);
|
||||
|
||||
// for generated code in anonymous space, pSegInfo is 0
|
||||
}
|
||||
|
||||
found_file_line = VG_(get_filename_linenum)(instr_addr,
|
||||
file,
|
||||
dir,
|
||||
&line);
|
||||
found_fn = VG_(get_fnname)(instr_addr, fn_name);
|
||||
|
||||
if (!found_file_line && !found_fn) {
|
||||
CLG_(stat).no_debug_BBs++;
|
||||
*file = "???";
|
||||
*fn_name = "???";
|
||||
if (line_num) *line_num=0;
|
||||
result = False;
|
||||
|
||||
} else if ( found_file_line && found_fn) {
|
||||
CLG_(stat).full_debug_BBs++;
|
||||
if (line_num) *line_num=line;
|
||||
|
||||
} else if ( found_file_line && !found_fn) {
|
||||
CLG_(stat).file_line_debug_BBs++;
|
||||
*fn_name = "???";
|
||||
if (line_num) *line_num=line;
|
||||
|
||||
} else /*(!found_file_line && found_fn)*/ {
|
||||
CLG_(stat).fn_name_debug_BBs++;
|
||||
*file = "???";
|
||||
if (line_num) *line_num=0;
|
||||
}
|
||||
|
||||
CLG_DEBUG(6, " - get_debug_info(%#lx): seg '%s', fn %s\n",
|
||||
instr_addr,
|
||||
!pDebugInfo ? "-" :
|
||||
(*pDebugInfo) ? VG_(DebugInfo_get_filename)(*pDebugInfo) :
|
||||
"(None)",
|
||||
*fn_name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* for _libc_freeres_wrapper => _exit renaming */
|
||||
static BB* exit_bb = 0;
|
||||
|
||||
|
||||
/*
|
||||
* Attach function struct to a BB from debug info.
|
||||
*/
|
||||
fn_node* CLG_(get_fn_node)(BB* bb)
|
||||
{
|
||||
const HChar *fnname, *filename, *dirname;
|
||||
DebugInfo* di;
|
||||
UInt line_num;
|
||||
fn_node* fn;
|
||||
|
||||
/* fn from debug info is idempotent for a BB */
|
||||
if (bb->fn) return bb->fn;
|
||||
|
||||
CLG_DEBUG(3,"+ get_fn_node(BB %#lx)\n", bb_addr(bb));
|
||||
|
||||
/* get function/file name, line number and object of
|
||||
* the BB according to debug information
|
||||
*/
|
||||
CLG_(get_debug_info)(bb_addr(bb),
|
||||
&dirname, &filename, &fnname, &line_num, &di);
|
||||
|
||||
if (0 == VG_(strcmp)(fnname, "???")) {
|
||||
int p;
|
||||
static HChar buf[32]; // for sure large enough
|
||||
/* Use address as found in library */
|
||||
if (sizeof(Addr) == 4)
|
||||
p = VG_(sprintf)(buf, "%#08lx", (UWord)bb->offset);
|
||||
else
|
||||
// 64bit address
|
||||
p = VG_(sprintf)(buf, "%#016lx", (UWord)bb->offset);
|
||||
|
||||
VG_(sprintf)(buf + p, "%s",
|
||||
(bb->sect_kind == Vg_SectData) ? " [Data]" :
|
||||
(bb->sect_kind == Vg_SectBSS) ? " [BSS]" :
|
||||
(bb->sect_kind == Vg_SectGOT) ? " [GOT]" :
|
||||
(bb->sect_kind == Vg_SectPLT) ? " [PLT]" : "");
|
||||
fnname = buf;
|
||||
}
|
||||
else {
|
||||
if (VG_(get_fnname_if_entry)(bb_addr(bb), &fnname))
|
||||
bb->is_entry = 1;
|
||||
}
|
||||
|
||||
/* HACK for correct _exit:
|
||||
* _exit is redirected to VG_(__libc_freeres_wrapper) by valgrind,
|
||||
* so we rename it back again :-)
|
||||
*/
|
||||
if (0 == VG_(strcmp)(fnname, "vgPlain___libc_freeres_wrapper")
|
||||
&& exit_bb) {
|
||||
CLG_(get_debug_info)(bb_addr(exit_bb),
|
||||
&dirname, &filename, &fnname, &line_num, &di);
|
||||
|
||||
CLG_DEBUG(1, "__libc_freeres_wrapper renamed to _exit\n");
|
||||
}
|
||||
if (0 == VG_(strcmp)(fnname, "_exit") && !exit_bb)
|
||||
exit_bb = bb;
|
||||
|
||||
if (runtime_resolve_addr &&
|
||||
(bb_addr(bb) >= runtime_resolve_addr) &&
|
||||
(bb_addr(bb) < runtime_resolve_addr + runtime_resolve_length)) {
|
||||
/* BB in runtime_resolve found by code check; use this name */
|
||||
fnname = "_dl_runtime_resolve";
|
||||
}
|
||||
|
||||
/* get fn_node struct for this function */
|
||||
fn = get_fn_node_inseg( di, dirname, filename, fnname);
|
||||
|
||||
/* if this is the 1st time the function is seen,
|
||||
* some attributes are set */
|
||||
if (fn->pure_cxt == 0) {
|
||||
|
||||
/* Every function gets a "pure" context, i.e. a context with stack
|
||||
* depth 1 only with this function. This is for compression of mangled
|
||||
* names
|
||||
*/
|
||||
fn_node* pure[2];
|
||||
pure[0] = 0;
|
||||
pure[1] = fn;
|
||||
fn->pure_cxt = CLG_(get_cxt)(pure+1);
|
||||
|
||||
if (bb->sect_kind == Vg_SectPLT)
|
||||
fn->skip = CLG_(clo).skip_plt;
|
||||
|
||||
if (VG_(strcmp)(fn->name, "_dl_runtime_resolve")==0) {
|
||||
fn->pop_on_jump = True;
|
||||
|
||||
if (VG_(clo_verbosity) > 1)
|
||||
VG_(message)(Vg_DebugMsg, "Symbol match: found runtime_resolve:"
|
||||
" %s +%#lx=%#lx\n",
|
||||
bb->obj->name + bb->obj->last_slash_pos,
|
||||
(UWord)bb->offset, bb_addr(bb));
|
||||
}
|
||||
|
||||
fn->is_malloc = (VG_(strcmp)(fn->name, "malloc")==0);
|
||||
fn->is_realloc = (VG_(strcmp)(fn->name, "realloc")==0);
|
||||
fn->is_free = (VG_(strcmp)(fn->name, "free")==0);
|
||||
|
||||
/* apply config options from function name patterns
|
||||
* given on command line */
|
||||
CLG_(update_fn_config)(fn);
|
||||
}
|
||||
|
||||
|
||||
bb->fn = fn;
|
||||
bb->line = line_num;
|
||||
|
||||
if (dirname[0]) {
|
||||
CLG_DEBUG(3,"- get_fn_node(BB %#lx): %s (in %s:%u)\n",
|
||||
bb_addr(bb), fnname, filename, line_num);
|
||||
} else
|
||||
CLG_DEBUG(3,"- get_fn_node(BB %#lx): %s (in %s/%s:%u)\n",
|
||||
bb_addr(bb), fnname, dirname, filename, line_num);
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Active function array operations ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
/* The active function array is a thread-specific array
|
||||
* of UInts, mapping function numbers to the active count of
|
||||
* functions.
|
||||
* The active count is the number of times a function appears
|
||||
* in the current call stack, and is used when costs for recursion
|
||||
* levels should be separated.
|
||||
*/
|
||||
|
||||
UInt* CLG_(get_fn_entry)(Int n)
|
||||
{
|
||||
CLG_ASSERT(n < current_fn_active.size);
|
||||
return current_fn_active.array + n;
|
||||
}
|
||||
|
||||
void CLG_(init_fn_array)(fn_array* a)
|
||||
{
|
||||
Int i;
|
||||
|
||||
CLG_ASSERT(a != 0);
|
||||
|
||||
a->size = N_INITIAL_FN_ARRAY_SIZE;
|
||||
if (a->size <= CLG_(stat).distinct_fns)
|
||||
a->size = CLG_(stat).distinct_fns+1;
|
||||
|
||||
a->array = (UInt*) CLG_MALLOC("cl.fn.gfe.1",
|
||||
a->size * sizeof(UInt));
|
||||
for(i=0;i<a->size;i++)
|
||||
a->array[i] = 0;
|
||||
}
|
||||
|
||||
void CLG_(copy_current_fn_array)(fn_array* dst)
|
||||
{
|
||||
CLG_ASSERT(dst != 0);
|
||||
|
||||
dst->size = current_fn_active.size;
|
||||
dst->array = current_fn_active.array;
|
||||
}
|
||||
|
||||
fn_array* CLG_(get_current_fn_array)()
|
||||
{
|
||||
return ¤t_fn_active;
|
||||
}
|
||||
|
||||
void CLG_(set_current_fn_array)(fn_array* a)
|
||||
{
|
||||
CLG_ASSERT(a != 0);
|
||||
|
||||
current_fn_active.size = a->size;
|
||||
current_fn_active.array = a->array;
|
||||
if (current_fn_active.size <= CLG_(stat).distinct_fns)
|
||||
resize_fn_array();
|
||||
}
|
||||
|
||||
/* ensure that active_array is big enough:
|
||||
* <distinct_fns> is the highest index, so <fn_active_array_size>
|
||||
* has to be bigger than that.
|
||||
*/
|
||||
static void resize_fn_array(void)
|
||||
{
|
||||
UInt* new_array;
|
||||
Int i;
|
||||
|
||||
UInt newsize = current_fn_active.size;
|
||||
while (newsize <= CLG_(stat).distinct_fns) newsize *=2;
|
||||
|
||||
CLG_DEBUG(0, "Resize fn_active_array: %u => %u\n",
|
||||
current_fn_active.size, newsize);
|
||||
|
||||
new_array = (UInt*) CLG_MALLOC("cl.fn.rfa.1", newsize * sizeof(UInt));
|
||||
for(i=0;i<current_fn_active.size;i++)
|
||||
new_array[i] = current_fn_active.array[i];
|
||||
while(i<newsize)
|
||||
new_array[i++] = 0;
|
||||
|
||||
VG_(free)(current_fn_active.array);
|
||||
current_fn_active.size = newsize;
|
||||
current_fn_active.array = new_array;
|
||||
CLG_(stat).fn_array_resizes++;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,858 @@
|
||||
/*--------------------------------------------------------------------*/
|
||||
/*--- Callgrind data structures, functions. global.h ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
This file is part of Valgrind, a dynamic binary instrumentation
|
||||
framework.
|
||||
|
||||
Copyright (C) 2004-2015 Josef Weidendorfer
|
||||
josef.weidendorfer@gmx.de
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The GNU General Public License is contained in the file COPYING.
|
||||
*/
|
||||
|
||||
#ifndef CLG_GLOBAL
|
||||
#define CLG_GLOBAL
|
||||
|
||||
#include "pub_tool_basics.h"
|
||||
#include "pub_tool_vki.h"
|
||||
#include "pub_tool_debuginfo.h"
|
||||
#include "pub_tool_libcbase.h"
|
||||
#include "pub_tool_libcassert.h"
|
||||
#include "pub_tool_libcfile.h"
|
||||
#include "pub_tool_libcprint.h"
|
||||
#include "pub_tool_libcproc.h"
|
||||
#include "pub_tool_machine.h"
|
||||
#include "pub_tool_mallocfree.h"
|
||||
#include "pub_tool_options.h"
|
||||
#include "pub_tool_tooliface.h"
|
||||
#include "pub_tool_xarray.h"
|
||||
#include "pub_tool_clientstate.h"
|
||||
#include "pub_tool_machine.h" // VG_(fnptr_to_fnentry)
|
||||
|
||||
#include "events.h" // defines CLG_ macro
|
||||
#include "costs.h"
|
||||
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Callgrind compile options --- */
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
/* Enable debug output */
|
||||
#define CLG_ENABLE_DEBUG 1
|
||||
|
||||
/* Enable experimental features? */
|
||||
#define CLG_EXPERIMENTAL 0
|
||||
|
||||
/* Syscall Timing in microseconds?
|
||||
* (define to 0 if you get compile errors) */
|
||||
#define CLG_MICROSYSTIME 0
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Command line options ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
#define DEFAULT_OUTFORMAT "callgrind.out.%p"
|
||||
|
||||
typedef struct _CommandLineOptions CommandLineOptions;
|
||||
struct _CommandLineOptions {
|
||||
|
||||
/* Dump format options */
|
||||
const HChar* out_format; /* Format string for callgrind output file name */
|
||||
Bool combine_dumps; /* Dump trace parts into same file? */
|
||||
Bool compress_strings;
|
||||
Bool compress_events;
|
||||
Bool compress_pos;
|
||||
Bool mangle_names;
|
||||
Bool compress_mangled;
|
||||
Bool dump_line;
|
||||
Bool dump_instr;
|
||||
Bool dump_bb;
|
||||
Bool dump_bbs; /* Dump basic block information? */
|
||||
|
||||
/* Dump generation options */
|
||||
ULong dump_every_bb; /* Dump every xxx BBs. */
|
||||
|
||||
/* Collection options */
|
||||
Bool separate_threads; /* Separate threads in dump? */
|
||||
Int separate_callers; /* Separate dependent on how many callers? */
|
||||
Int separate_recursions; /* Max level of recursions to separate */
|
||||
Bool skip_plt; /* Skip functions in PLT section? */
|
||||
Bool skip_direct_recursion; /* Increment direct recursions the level? */
|
||||
|
||||
Bool collect_atstart; /* Start in collecting state ? */
|
||||
Bool collect_jumps; /* Collect (cond.) jumps in functions ? */
|
||||
|
||||
Bool collect_alloc; /* Collect size of allocated memory */
|
||||
Bool collect_systime; /* Collect time for system calls */
|
||||
|
||||
Bool collect_bus; /* Collect global bus events */
|
||||
|
||||
/* Instrument options */
|
||||
Bool instrument_atstart; /* Instrument at start? */
|
||||
Bool simulate_cache; /* Call into cache simulator ? */
|
||||
Bool simulate_branch; /* Call into branch prediction simulator ? */
|
||||
|
||||
/* Call graph generation */
|
||||
Bool pop_on_jump; /* Handle a jump between functions as ret+call */
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
Int verbose;
|
||||
ULong verbose_start;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Constants ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
/* Minimum cache line size allowed */
|
||||
#define MIN_LINE_SIZE 16
|
||||
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Statistics ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
typedef struct _Statistics Statistics;
|
||||
struct _Statistics {
|
||||
ULong call_counter;
|
||||
ULong jcnd_counter;
|
||||
ULong jump_counter;
|
||||
ULong rec_call_counter;
|
||||
ULong ret_counter;
|
||||
ULong bb_executions;
|
||||
|
||||
Int context_counter;
|
||||
Int bb_retranslations;
|
||||
|
||||
Int distinct_objs;
|
||||
Int distinct_files;
|
||||
Int distinct_fns;
|
||||
Int distinct_contexts;
|
||||
Int distinct_bbs;
|
||||
Int distinct_jccs;
|
||||
Int distinct_bbccs;
|
||||
Int distinct_instrs;
|
||||
Int distinct_skips;
|
||||
|
||||
Int bb_hash_resizes;
|
||||
Int bbcc_hash_resizes;
|
||||
Int jcc_hash_resizes;
|
||||
Int cxt_hash_resizes;
|
||||
Int fn_array_resizes;
|
||||
Int call_stack_resizes;
|
||||
Int fn_stack_resizes;
|
||||
|
||||
Int full_debug_BBs;
|
||||
Int file_line_debug_BBs;
|
||||
Int fn_name_debug_BBs;
|
||||
Int no_debug_BBs;
|
||||
Int bbcc_lru_misses;
|
||||
Int jcc_lru_misses;
|
||||
Int cxt_lru_misses;
|
||||
Int bbcc_clones;
|
||||
};
|
||||
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Structure declarations ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
typedef struct _Context Context;
|
||||
typedef struct _CC CC;
|
||||
typedef struct _BB BB;
|
||||
typedef struct _BBCC BBCC;
|
||||
typedef struct _jCC jCC;
|
||||
typedef struct _fCC fCC;
|
||||
typedef struct _fn_node fn_node;
|
||||
typedef struct _file_node file_node;
|
||||
typedef struct _obj_node obj_node;
|
||||
typedef struct _fn_config fn_config;
|
||||
typedef struct _call_entry call_entry;
|
||||
typedef struct _thread_info thread_info;
|
||||
|
||||
/* Costs of event sets. Aliases to arrays of 64-bit values */
|
||||
typedef ULong* SimCost; /* All events the simulator can produce */
|
||||
typedef ULong* UserCost;
|
||||
typedef ULong* FullCost; /* Simulator + User */
|
||||
|
||||
|
||||
/* The types of control flow changes that can happen between
|
||||
* execution of two BBs in a thread.
|
||||
*/
|
||||
typedef enum {
|
||||
jk_None = 0, /* no explicit change by a guest instruction */
|
||||
jk_Jump, /* regular jump */
|
||||
jk_Call,
|
||||
jk_Return,
|
||||
jk_CondJump /* conditional jump taken (only used as jCC type) */
|
||||
} ClgJumpKind;
|
||||
|
||||
|
||||
/* JmpCall cost center
|
||||
* for subroutine call (from->bb->jmp_addr => to->bb->addr)
|
||||
*
|
||||
* Each BB has at most one CALL instruction. The list of JCC from
|
||||
* this call is a pointer to the list head (stored in BBCC), and
|
||||
* <next_from> in the JCC struct.
|
||||
*
|
||||
* For fast lookup, JCCs are reachable with a hash table, keyed by
|
||||
* the (from_bbcc,to) pair. <next_hash> is used for the JCC chain
|
||||
* of one hash table entry.
|
||||
*
|
||||
* Cost <sum> holds event counts for already returned executions.
|
||||
* <last> are the event counters at last enter of the subroutine.
|
||||
* <sum> is updated on returning from the subroutine by
|
||||
* adding the diff of <last> and current event counters to <sum>.
|
||||
*
|
||||
* After updating, <last> is set to current event counters. Thus,
|
||||
* events are not counted twice for recursive calls (TODO: True?)
|
||||
*/
|
||||
|
||||
struct _jCC {
|
||||
ClgJumpKind jmpkind; /* jk_Call, jk_Jump, jk_CondJump */
|
||||
jCC* next_hash; /* for hash entry chain */
|
||||
jCC* next_from; /* next JCC from a BBCC */
|
||||
BBCC *from, *to; /* call arc from/to this BBCC */
|
||||
UInt jmp; /* jump no. in source */
|
||||
|
||||
ULong call_counter; /* no wraparound with 64 bit */
|
||||
|
||||
FullCost cost; /* simulator + user counters */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Info for one instruction of a basic block.
|
||||
*/
|
||||
typedef struct _InstrInfo InstrInfo;
|
||||
struct _InstrInfo {
|
||||
UInt instr_offset;
|
||||
UInt instr_size;
|
||||
UInt cost_offset;
|
||||
EventSet* eventset;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Info for a side exit in a BB
|
||||
*/
|
||||
typedef struct _CJmpInfo CJmpInfo;
|
||||
struct _CJmpInfo {
|
||||
UInt instr; /* instruction index for BB.instr array */
|
||||
ClgJumpKind jmpkind; /* jump kind when leaving BB at this side exit */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An instrumented basic block (BB).
|
||||
*
|
||||
* BBs are put into a resizable hash to allow for fast detection if a
|
||||
* BB is to be retranslated but cost info is already available.
|
||||
* The key for a BB is a (object, offset) tupel making it independent
|
||||
* from possibly multiple mappings of the same ELF object.
|
||||
*
|
||||
* At the beginning of each instrumented BB,
|
||||
* a call to setup_bbcc(), specifying a pointer to the
|
||||
* according BB structure, is added.
|
||||
*
|
||||
* As cost of a BB has to be distinguished depending on the context,
|
||||
* multiple cost centers for one BB (struct BBCC) exist and the according
|
||||
* BBCC is set by setup_bbcc.
|
||||
*/
|
||||
struct _BB {
|
||||
obj_node* obj; /* ELF object of BB */
|
||||
PtrdiffT offset; /* offset of BB in ELF object file */
|
||||
BB* next; /* chaining for a hash entry */
|
||||
|
||||
VgSectKind sect_kind; /* section of this BB, e.g. PLT */
|
||||
UInt instr_count;
|
||||
|
||||
/* filled by CLG_(get_fn_node) if debug info is available */
|
||||
fn_node* fn; /* debug info for this BB */
|
||||
UInt line;
|
||||
Bool is_entry; /* True if this BB is a function entry */
|
||||
|
||||
BBCC* bbcc_list; /* BBCCs for same BB (see next_bbcc in BBCC) */
|
||||
BBCC* last_bbcc; /* Temporary: Cached for faster access (LRU) */
|
||||
|
||||
/* filled by CLG_(instrument) if not seen before */
|
||||
UInt cjmp_count; /* number of side exits */
|
||||
CJmpInfo* jmp; /* array of info for condition jumps,
|
||||
* allocated directly after this struct */
|
||||
Bool cjmp_inverted; /* is last side exit actually fall through? */
|
||||
|
||||
UInt instr_len;
|
||||
UInt cost_count;
|
||||
InstrInfo instr[0]; /* info on instruction sizes and costs */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Function context
|
||||
*
|
||||
* Basic blocks are always executed in the scope of a context.
|
||||
* A function context is a list of function nodes representing
|
||||
* the call chain to the current context: I.e. fn[0] is the
|
||||
* function we are currently in, fn[1] has called fn[0], and so on.
|
||||
* Recursion levels are used for fn[0].
|
||||
*
|
||||
* To get a unique number for a full execution context, use
|
||||
* rec_index = min(<fn->rec_separation>,<active>) - 1;
|
||||
* unique_no = <number> + rec_index
|
||||
*
|
||||
* For each Context, recursion index and BB, there can be a BBCC.
|
||||
*/
|
||||
struct _Context {
|
||||
UInt size; // number of function dependencies
|
||||
UInt base_number; // for context compression & dump array
|
||||
Context* next; // entry chaining for hash
|
||||
UWord hash; // for faster lookup...
|
||||
fn_node* fn[0];
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Cost info for a side exits from a BB
|
||||
*/
|
||||
typedef struct _JmpData JmpData;
|
||||
struct _JmpData {
|
||||
ULong ecounter; /* number of times the BB was left at this exit */
|
||||
jCC* jcc_list; /* JCCs used for this exit */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Basic Block Cost Center
|
||||
*
|
||||
* On demand, multiple BBCCs will be created for the same BB
|
||||
* dependend on command line options and:
|
||||
* - current function (it's possible that a BB is executed in the
|
||||
* context of different functions, e.g. in manual assembler/PLT)
|
||||
* - current thread ID
|
||||
* - position where current function is called from
|
||||
* - recursion level of current function
|
||||
*
|
||||
* The cost centres for the instructions of a basic block are
|
||||
* stored in a contiguous array.
|
||||
* They are distinguishable by their tag field.
|
||||
*/
|
||||
struct _BBCC {
|
||||
BB* bb; /* BB for this cost center */
|
||||
|
||||
Context* cxt; /* execution context of this BBCC */
|
||||
ThreadId tid; /* only for assertion check purpose */
|
||||
UInt rec_index; /* Recursion index in rec->bbcc for this bbcc */
|
||||
BBCC** rec_array; /* Variable sized array of pointers to
|
||||
* recursion BBCCs. Shared. */
|
||||
ULong ret_counter; /* how often returned from jccs of this bbcc;
|
||||
* used to check if a dump for this BBCC is needed */
|
||||
|
||||
BBCC* next_bbcc; /* Chain of BBCCs for same BB */
|
||||
BBCC* lru_next_bbcc; /* BBCC executed next the last time */
|
||||
|
||||
jCC* lru_from_jcc; /* Temporary: Cached for faster access (LRU) */
|
||||
jCC* lru_to_jcc; /* Temporary: Cached for faster access (LRU) */
|
||||
FullCost skipped; /* cost for skipped functions called from
|
||||
* jmp_addr. Allocated lazy */
|
||||
|
||||
BBCC* next; /* entry chain in hash */
|
||||
ULong* cost; /* start of 64bit costs for this BBCC */
|
||||
ULong ecounter_sum; /* execution counter for first instruction of BB */
|
||||
JmpData jmp[0];
|
||||
};
|
||||
|
||||
|
||||
/* the <number> of fn_node, file_node and obj_node are for compressed dumping
|
||||
* and a index into the dump boolean table and fn_info_table
|
||||
*/
|
||||
|
||||
struct _fn_node {
|
||||
HChar* name;
|
||||
UInt number;
|
||||
Context* last_cxt; /* LRU info */
|
||||
Context* pure_cxt; /* the context with only the function itself */
|
||||
file_node* file; /* reverse mapping for 2nd hash */
|
||||
fn_node* next;
|
||||
|
||||
Bool dump_before :1;
|
||||
Bool dump_after :1;
|
||||
Bool zero_before :1;
|
||||
Bool toggle_collect :1;
|
||||
Bool skip :1;
|
||||
Bool pop_on_jump : 1;
|
||||
|
||||
Bool is_malloc :1;
|
||||
Bool is_realloc :1;
|
||||
Bool is_free :1;
|
||||
|
||||
Int group;
|
||||
Int separate_callers;
|
||||
Int separate_recursions;
|
||||
#if CLG_ENABLE_DEBUG
|
||||
Int verbosity; /* Stores old verbosity level while in function */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Quite arbitrary fixed hash sizes */
|
||||
|
||||
#define N_OBJ_ENTRIES 47
|
||||
#define N_FILE_ENTRIES 53
|
||||
#define N_FN_ENTRIES 87
|
||||
|
||||
struct _file_node {
|
||||
HChar* name;
|
||||
fn_node* fns[N_FN_ENTRIES];
|
||||
UInt number;
|
||||
obj_node* obj;
|
||||
file_node* next;
|
||||
};
|
||||
|
||||
/* If an object is dlopened multiple times, we hope that <name> is unique;
|
||||
* <start> and <offset> can change with each dlopen, and <start> is
|
||||
* zero when object is unmapped (possible at dump time).
|
||||
*/
|
||||
struct _obj_node {
|
||||
const HChar* name;
|
||||
UInt last_slash_pos;
|
||||
|
||||
Addr start; /* Start address of text segment mapping */
|
||||
SizeT size; /* Length of mapping */
|
||||
PtrdiffT offset; /* Offset between symbol address and file offset */
|
||||
|
||||
file_node* files[N_FILE_ENTRIES];
|
||||
UInt number;
|
||||
obj_node* next;
|
||||
};
|
||||
|
||||
/* an entry in the callstack
|
||||
*
|
||||
* <nonskipped> is 0 if the function called is not skipped (usual case).
|
||||
* Otherwise, it is the last non-skipped BBCC. This one gets all
|
||||
* the calls to non-skipped functions and all costs in skipped
|
||||
* instructions.
|
||||
*/
|
||||
struct _call_entry {
|
||||
jCC* jcc; /* jCC for this call */
|
||||
FullCost enter_cost; /* cost event counters at entering frame */
|
||||
Addr sp; /* stack pointer directly after call */
|
||||
Addr ret_addr; /* address to which to return to
|
||||
* is 0 on a simulated call */
|
||||
BBCC* nonskipped; /* see above */
|
||||
Context* cxt; /* context before call */
|
||||
Int fn_sp; /* function stack index before call */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Execution state of main thread or a running signal handler in
|
||||
* a thread while interrupted by another signal handler.
|
||||
* As there's no scheduling among running signal handlers of one thread,
|
||||
* we only need a subset of a full thread state:
|
||||
* - event counter
|
||||
* - collect state
|
||||
* - last BB, last jump kind, last nonskipped BB
|
||||
* - callstack pointer for sanity checking and correct unwinding
|
||||
* after exit
|
||||
*/
|
||||
typedef struct _exec_state exec_state;
|
||||
struct _exec_state {
|
||||
|
||||
/* the signum of the handler, 0 for main thread context
|
||||
*/
|
||||
Int sig;
|
||||
|
||||
/* the old call stack pointer at entering the signal handler */
|
||||
Int orig_sp;
|
||||
|
||||
FullCost cost;
|
||||
Bool collect;
|
||||
Context* cxt;
|
||||
|
||||
/* number of conditional jumps passed in last BB */
|
||||
Int jmps_passed;
|
||||
BBCC* bbcc; /* last BB executed */
|
||||
BBCC* nonskipped;
|
||||
|
||||
Int call_stack_bottom; /* Index into fn_stack */
|
||||
};
|
||||
|
||||
/* Global state structures */
|
||||
typedef struct _bb_hash bb_hash;
|
||||
struct _bb_hash {
|
||||
UInt size, entries;
|
||||
BB** table;
|
||||
};
|
||||
|
||||
typedef struct _cxt_hash cxt_hash;
|
||||
struct _cxt_hash {
|
||||
UInt size, entries;
|
||||
Context** table;
|
||||
};
|
||||
|
||||
/* Thread specific state structures, i.e. parts of a thread state.
|
||||
* There are variables for the current state of each part,
|
||||
* on which a thread state is copied at thread switch.
|
||||
*/
|
||||
typedef struct _bbcc_hash bbcc_hash;
|
||||
struct _bbcc_hash {
|
||||
UInt size, entries;
|
||||
BBCC** table;
|
||||
};
|
||||
|
||||
typedef struct _jcc_hash jcc_hash;
|
||||
struct _jcc_hash {
|
||||
UInt size, entries;
|
||||
jCC** table;
|
||||
jCC* spontaneous;
|
||||
};
|
||||
|
||||
typedef struct _fn_array fn_array;
|
||||
struct _fn_array {
|
||||
UInt size;
|
||||
UInt* array;
|
||||
};
|
||||
|
||||
typedef struct _call_stack call_stack;
|
||||
struct _call_stack {
|
||||
UInt size;
|
||||
Int sp;
|
||||
call_entry* entry;
|
||||
};
|
||||
|
||||
typedef struct _fn_stack fn_stack;
|
||||
struct _fn_stack {
|
||||
UInt size;
|
||||
fn_node **bottom, **top;
|
||||
};
|
||||
|
||||
/* The maximum number of simultaneous running signal handlers per thread.
|
||||
* This is the number of execution states storable in a thread.
|
||||
*/
|
||||
#define MAX_SIGHANDLERS 10
|
||||
|
||||
typedef struct _exec_stack exec_stack;
|
||||
struct _exec_stack {
|
||||
Int sp; /* > 0 if a handler is running */
|
||||
exec_state* entry[MAX_SIGHANDLERS];
|
||||
};
|
||||
|
||||
/* Thread State
|
||||
*
|
||||
* This structure stores thread specific info while a thread is *not*
|
||||
* running. See function switch_thread() for save/restore on thread switch.
|
||||
*
|
||||
* If --separate-threads=no, BBCCs and JCCs can be shared by all threads, i.e.
|
||||
* only structures of thread 1 are used.
|
||||
* This involves variables fn_info_table, bbcc_table and jcc_table.
|
||||
*/
|
||||
struct _thread_info {
|
||||
|
||||
/* state */
|
||||
fn_stack fns; /* function stack */
|
||||
call_stack calls; /* context call arc stack */
|
||||
exec_stack states; /* execution states interrupted by signals */
|
||||
|
||||
/* dump statistics */
|
||||
FullCost lastdump_cost; /* Cost at last dump */
|
||||
FullCost sighandler_cost;
|
||||
|
||||
/* thread specific data structure containers */
|
||||
fn_array fn_active;
|
||||
jcc_hash jccs;
|
||||
bbcc_hash bbccs;
|
||||
};
|
||||
|
||||
/* Structs used for dumping */
|
||||
|
||||
/* Address position inside of a BBCC:
|
||||
* This includes
|
||||
* - the address offset from the BB start address
|
||||
* - file/line from debug info for that address (can change inside a BB)
|
||||
*/
|
||||
typedef struct _AddrPos AddrPos;
|
||||
struct _AddrPos {
|
||||
Addr addr;
|
||||
Addr bb_addr;
|
||||
file_node* file;
|
||||
UInt line;
|
||||
};
|
||||
|
||||
/* a simulator cost entity that can be written out in one line */
|
||||
typedef struct _AddrCost AddrCost;
|
||||
struct _AddrCost {
|
||||
AddrPos p;
|
||||
SimCost cost;
|
||||
};
|
||||
|
||||
/* A function in an execution context */
|
||||
typedef struct _FnPos FnPos;
|
||||
struct _FnPos {
|
||||
file_node* file;
|
||||
fn_node* fn;
|
||||
obj_node* obj;
|
||||
Context* cxt;
|
||||
int rec_index;
|
||||
UInt line;
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Cache simulator interface ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
struct cachesim_if
|
||||
{
|
||||
void (*print_opts)(void);
|
||||
Bool (*parse_opt)(const HChar* arg);
|
||||
void (*post_clo_init)(void);
|
||||
void (*clear)(void);
|
||||
void (*dump_desc)(VgFile *fp);
|
||||
void (*printstat)(Int,Int,Int);
|
||||
void (*add_icost)(SimCost, BBCC*, InstrInfo*, ULong);
|
||||
void (*finish)(void);
|
||||
|
||||
void (*log_1I0D)(InstrInfo*) VG_REGPARM(1);
|
||||
void (*log_2I0D)(InstrInfo*, InstrInfo*) VG_REGPARM(2);
|
||||
void (*log_3I0D)(InstrInfo*, InstrInfo*, InstrInfo*) VG_REGPARM(3);
|
||||
|
||||
void (*log_1I1Dr)(InstrInfo*, Addr, Word) VG_REGPARM(3);
|
||||
void (*log_1I1Dw)(InstrInfo*, Addr, Word) VG_REGPARM(3);
|
||||
|
||||
void (*log_0I1Dr)(InstrInfo*, Addr, Word) VG_REGPARM(3);
|
||||
void (*log_0I1Dw)(InstrInfo*, Addr, Word) VG_REGPARM(3);
|
||||
|
||||
// function names of helpers (for debugging generated code)
|
||||
const HChar *log_1I0D_name, *log_2I0D_name, *log_3I0D_name;
|
||||
const HChar *log_1I1Dr_name, *log_1I1Dw_name;
|
||||
const HChar *log_0I1Dr_name, *log_0I1Dw_name;
|
||||
};
|
||||
|
||||
// Event groups
|
||||
#define EG_USE 0
|
||||
#define EG_IR 1
|
||||
#define EG_DR 2
|
||||
#define EG_DW 3
|
||||
#define EG_BC 4
|
||||
#define EG_BI 5
|
||||
#define EG_BUS 6
|
||||
#define EG_ALLOC 7
|
||||
#define EG_SYS 8
|
||||
|
||||
struct event_sets {
|
||||
EventSet *base, *full;
|
||||
};
|
||||
|
||||
#define fullOffset(group) (CLG_(sets).full->offset[group])
|
||||
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Functions ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
/* from clo.c */
|
||||
|
||||
void CLG_(set_clo_defaults)(void);
|
||||
void CLG_(update_fn_config)(fn_node*);
|
||||
Bool CLG_(process_cmd_line_option)(const HChar*);
|
||||
void CLG_(print_usage)(void);
|
||||
void CLG_(print_debug_usage)(void);
|
||||
|
||||
/* from sim.c */
|
||||
void CLG_(init_eventsets)(void);
|
||||
|
||||
/* from main.c */
|
||||
Bool CLG_(get_debug_info)(Addr, const HChar **dirname,
|
||||
const HChar **filename,
|
||||
const HChar **fn_name, UInt*, DebugInfo**);
|
||||
void CLG_(collectBlockInfo)(IRSB* bbIn, UInt*, UInt*, Bool*);
|
||||
void CLG_(set_instrument_state)(const HChar*,Bool);
|
||||
void CLG_(dump_profile)(const HChar* trigger,Bool only_current_thread);
|
||||
void CLG_(zero_all_cost)(Bool only_current_thread);
|
||||
Int CLG_(get_dump_counter)(void);
|
||||
void CLG_(fini)(Int exitcode);
|
||||
|
||||
/* from bb.c */
|
||||
void CLG_(init_bb_hash)(void);
|
||||
bb_hash* CLG_(get_bb_hash)(void);
|
||||
BB* CLG_(get_bb)(Addr addr, IRSB* bb_in, Bool *seen_before);
|
||||
void CLG_(delete_bb)(Addr addr);
|
||||
|
||||
static __inline__ Addr bb_addr(BB* bb)
|
||||
{ return bb->offset + bb->obj->offset; }
|
||||
static __inline__ Addr bb_jmpaddr(BB* bb)
|
||||
{ UInt off = (bb->instr_count > 0) ? bb->instr[bb->instr_count-1].instr_offset : 0;
|
||||
return off + bb->offset + bb->obj->offset; }
|
||||
|
||||
/* from fn.c */
|
||||
void CLG_(init_fn_array)(fn_array*);
|
||||
void CLG_(copy_current_fn_array)(fn_array* dst);
|
||||
fn_array* CLG_(get_current_fn_array)(void);
|
||||
void CLG_(set_current_fn_array)(fn_array*);
|
||||
UInt* CLG_(get_fn_entry)(Int n);
|
||||
|
||||
void CLG_(init_obj_table)(void);
|
||||
obj_node* CLG_(get_obj_node)(DebugInfo* si);
|
||||
file_node* CLG_(get_file_node)(obj_node*, const HChar *dirname,
|
||||
const HChar* filename);
|
||||
fn_node* CLG_(get_fn_node)(BB* bb);
|
||||
|
||||
/* from bbcc.c */
|
||||
void CLG_(init_bbcc_hash)(bbcc_hash* bbccs);
|
||||
void CLG_(copy_current_bbcc_hash)(bbcc_hash* dst);
|
||||
bbcc_hash* CLG_(get_current_bbcc_hash)(void);
|
||||
void CLG_(set_current_bbcc_hash)(bbcc_hash*);
|
||||
void CLG_(forall_bbccs)(void (*func)(BBCC*));
|
||||
void CLG_(zero_bbcc)(BBCC* bbcc);
|
||||
BBCC* CLG_(get_bbcc)(BB* bb);
|
||||
BBCC* CLG_(clone_bbcc)(BBCC* orig, Context* cxt, Int rec_index);
|
||||
void CLG_(setup_bbcc)(BB* bb) VG_REGPARM(1);
|
||||
|
||||
|
||||
/* from jumps.c */
|
||||
void CLG_(init_jcc_hash)(jcc_hash*);
|
||||
void CLG_(copy_current_jcc_hash)(jcc_hash* dst);
|
||||
void CLG_(set_current_jcc_hash)(jcc_hash*);
|
||||
jCC* CLG_(get_jcc)(BBCC* from, UInt, BBCC* to);
|
||||
|
||||
/* from callstack.c */
|
||||
void CLG_(init_call_stack)(call_stack*);
|
||||
void CLG_(copy_current_call_stack)(call_stack* dst);
|
||||
void CLG_(set_current_call_stack)(call_stack*);
|
||||
call_entry* CLG_(get_call_entry)(Int n);
|
||||
|
||||
void CLG_(push_call_stack)(BBCC* from, UInt jmp, BBCC* to, Addr sp, Bool skip);
|
||||
void CLG_(pop_call_stack)(void);
|
||||
Int CLG_(unwind_call_stack)(Addr sp, Int);
|
||||
|
||||
/* from context.c */
|
||||
void CLG_(init_fn_stack)(fn_stack*);
|
||||
void CLG_(copy_current_fn_stack)(fn_stack*);
|
||||
void CLG_(set_current_fn_stack)(fn_stack*);
|
||||
|
||||
void CLG_(init_cxt_table)(void);
|
||||
Context* CLG_(get_cxt)(fn_node** fn);
|
||||
void CLG_(push_cxt)(fn_node* fn);
|
||||
|
||||
/* from threads.c */
|
||||
void CLG_(init_threads)(void);
|
||||
thread_info** CLG_(get_threads)(void);
|
||||
thread_info* CLG_(get_current_thread)(void);
|
||||
void CLG_(switch_thread)(ThreadId tid);
|
||||
void CLG_(forall_threads)(void (*func)(thread_info*));
|
||||
void CLG_(run_thread)(ThreadId tid);
|
||||
|
||||
void CLG_(init_exec_state)(exec_state* es);
|
||||
void CLG_(init_exec_stack)(exec_stack*);
|
||||
void CLG_(copy_current_exec_stack)(exec_stack*);
|
||||
void CLG_(set_current_exec_stack)(exec_stack*);
|
||||
void CLG_(pre_signal)(ThreadId tid, Int sigNum, Bool alt_stack);
|
||||
void CLG_(post_signal)(ThreadId tid, Int sigNum);
|
||||
void CLG_(run_post_signal_on_call_stack_bottom)(void);
|
||||
|
||||
/* from dump.c */
|
||||
void CLG_(init_dumps)(void);
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Exported global variables ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
extern CommandLineOptions CLG_(clo);
|
||||
extern Statistics CLG_(stat);
|
||||
extern EventMapping* CLG_(dumpmap);
|
||||
|
||||
/* Function active counter array, indexed by function number */
|
||||
extern UInt* CLG_(fn_active_array);
|
||||
extern Bool CLG_(instrument_state);
|
||||
/* min of L1 and LL cache line sizes */
|
||||
extern Int CLG_(min_line_size);
|
||||
extern call_stack CLG_(current_call_stack);
|
||||
extern fn_stack CLG_(current_fn_stack);
|
||||
extern exec_state CLG_(current_state);
|
||||
extern ThreadId CLG_(current_tid);
|
||||
extern FullCost CLG_(total_cost);
|
||||
extern struct cachesim_if CLG_(cachesim);
|
||||
extern struct event_sets CLG_(sets);
|
||||
|
||||
// set by setup_bbcc at start of every BB, and needed by log_* helpers
|
||||
extern Addr CLG_(bb_base);
|
||||
extern ULong* CLG_(cost_base);
|
||||
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Debug output ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
#if CLG_ENABLE_DEBUG
|
||||
|
||||
#define CLG_DEBUGIF(x) \
|
||||
if (UNLIKELY( (CLG_(clo).verbose >x) && \
|
||||
(CLG_(stat).bb_executions >= CLG_(clo).verbose_start)))
|
||||
|
||||
#define CLG_DEBUG(x,format,args...) \
|
||||
CLG_DEBUGIF(x) { \
|
||||
CLG_(print_bbno)(); \
|
||||
VG_(printf)(format,##args); \
|
||||
}
|
||||
|
||||
#define CLG_ASSERT(cond) \
|
||||
if (UNLIKELY(!(cond))) { \
|
||||
CLG_(print_context)(); \
|
||||
CLG_(print_bbno)(); \
|
||||
tl_assert(cond); \
|
||||
}
|
||||
|
||||
#else
|
||||
#define CLG_DEBUGIF(x) if (0)
|
||||
#define CLG_DEBUG(x...) {}
|
||||
#define CLG_ASSERT(cond) tl_assert(cond);
|
||||
#endif
|
||||
|
||||
/* from debug.c */
|
||||
void CLG_(print_bbno)(void);
|
||||
void CLG_(print_context)(void);
|
||||
void CLG_(print_jcc)(int s, jCC* jcc);
|
||||
void CLG_(print_bbcc)(int s, BBCC* bbcc);
|
||||
void CLG_(print_bbcc_fn)(BBCC* bbcc);
|
||||
void CLG_(print_execstate)(int s, exec_state* es);
|
||||
void CLG_(print_eventset)(int s, EventSet* es);
|
||||
void CLG_(print_cost)(int s, EventSet*, ULong* cost);
|
||||
void CLG_(print_bb)(int s, BB* bb);
|
||||
void CLG_(print_bbcc_cost)(int s, BBCC*);
|
||||
void CLG_(print_cxt)(int s, Context* cxt, int rec_index);
|
||||
void CLG_(print_short_jcc)(jCC* jcc);
|
||||
void CLG_(print_stackentry)(int s, int sp);
|
||||
void CLG_(print_addr)(Addr addr);
|
||||
void CLG_(print_addr_ln)(Addr addr);
|
||||
|
||||
void* CLG_(malloc)(const HChar* cc, UWord s, const HChar* f);
|
||||
void* CLG_(free)(void* p, const HChar* f);
|
||||
#if 0
|
||||
#define CLG_MALLOC(_cc,x) CLG_(malloc)((_cc),x,__FUNCTION__)
|
||||
#define CLG_FREE(p) CLG_(free)(p,__FUNCTION__)
|
||||
#else
|
||||
#define CLG_MALLOC(_cc,x) VG_(malloc)((_cc),x)
|
||||
#define CLG_FREE(p) VG_(free)(p)
|
||||
#endif
|
||||
|
||||
#endif /* CLG_GLOBAL */
|
||||
@@ -0,0 +1,233 @@
|
||||
/*--------------------------------------------------------------------*/
|
||||
/*--- Callgrind ---*/
|
||||
/*--- ct_jumps.c ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
This file is part of Callgrind, a Valgrind tool for call tracing.
|
||||
|
||||
Copyright (C) 2002-2015, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The GNU General Public License is contained in the file COPYING.
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Jump Cost Center (JCC) operations, including Calls ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
#define N_JCC_INITIAL_ENTRIES 4437
|
||||
|
||||
static jcc_hash current_jccs;
|
||||
|
||||
void CLG_(init_jcc_hash)(jcc_hash* jccs)
|
||||
{
|
||||
Int i;
|
||||
|
||||
CLG_ASSERT(jccs != 0);
|
||||
|
||||
jccs->size = N_JCC_INITIAL_ENTRIES;
|
||||
jccs->entries = 0;
|
||||
jccs->table = (jCC**) CLG_MALLOC("cl.jumps.ijh.1",
|
||||
jccs->size * sizeof(jCC*));
|
||||
jccs->spontaneous = 0;
|
||||
|
||||
for (i = 0; i < jccs->size; i++)
|
||||
jccs->table[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
void CLG_(copy_current_jcc_hash)(jcc_hash* dst)
|
||||
{
|
||||
CLG_ASSERT(dst != 0);
|
||||
|
||||
dst->size = current_jccs.size;
|
||||
dst->entries = current_jccs.entries;
|
||||
dst->table = current_jccs.table;
|
||||
dst->spontaneous = current_jccs.spontaneous;
|
||||
}
|
||||
|
||||
void CLG_(set_current_jcc_hash)(jcc_hash* h)
|
||||
{
|
||||
CLG_ASSERT(h != 0);
|
||||
|
||||
current_jccs.size = h->size;
|
||||
current_jccs.entries = h->entries;
|
||||
current_jccs.table = h->table;
|
||||
current_jccs.spontaneous = h->spontaneous;
|
||||
}
|
||||
|
||||
__inline__
|
||||
static UInt jcc_hash_idx(BBCC* from, UInt jmp, BBCC* to, UInt size)
|
||||
{
|
||||
return (UInt) ( (UWord)from + 7* (UWord)to + 13*jmp) % size;
|
||||
}
|
||||
|
||||
/* double size of jcc table */
|
||||
static void resize_jcc_table(void)
|
||||
{
|
||||
Int i, new_size, conflicts1 = 0, conflicts2 = 0;
|
||||
jCC** new_table;
|
||||
UInt new_idx;
|
||||
jCC *curr_jcc, *next_jcc;
|
||||
|
||||
new_size = 2* current_jccs.size +3;
|
||||
new_table = (jCC**) CLG_MALLOC("cl.jumps.rjt.1",
|
||||
new_size * sizeof(jCC*));
|
||||
|
||||
for (i = 0; i < new_size; i++)
|
||||
new_table[i] = NULL;
|
||||
|
||||
for (i = 0; i < current_jccs.size; i++) {
|
||||
if (current_jccs.table[i] == NULL) continue;
|
||||
|
||||
curr_jcc = current_jccs.table[i];
|
||||
while (NULL != curr_jcc) {
|
||||
next_jcc = curr_jcc->next_hash;
|
||||
|
||||
new_idx = jcc_hash_idx(curr_jcc->from, curr_jcc->jmp,
|
||||
curr_jcc->to, new_size);
|
||||
|
||||
curr_jcc->next_hash = new_table[new_idx];
|
||||
new_table[new_idx] = curr_jcc;
|
||||
if (curr_jcc->next_hash) {
|
||||
conflicts1++;
|
||||
if (curr_jcc->next_hash->next_hash)
|
||||
conflicts2++;
|
||||
}
|
||||
|
||||
curr_jcc = next_jcc;
|
||||
}
|
||||
}
|
||||
|
||||
VG_(free)(current_jccs.table);
|
||||
|
||||
|
||||
CLG_DEBUG(0, "Resize JCC Hash: %u => %d (entries %u, conflicts %d/%d)\n",
|
||||
current_jccs.size, new_size,
|
||||
current_jccs.entries, conflicts1, conflicts2);
|
||||
|
||||
current_jccs.size = new_size;
|
||||
current_jccs.table = new_table;
|
||||
CLG_(stat).jcc_hash_resizes++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* new jCC structure: a call was done to a BB of a BBCC
|
||||
* for a spontaneous call, from is 0 (i.e. caller unknown)
|
||||
*/
|
||||
static jCC* new_jcc(BBCC* from, UInt jmp, BBCC* to)
|
||||
{
|
||||
jCC* jcc;
|
||||
UInt new_idx;
|
||||
|
||||
/* check fill degree of jcc hash table and resize if needed (>80%) */
|
||||
current_jccs.entries++;
|
||||
if (10 * current_jccs.entries / current_jccs.size > 8)
|
||||
resize_jcc_table();
|
||||
|
||||
jcc = (jCC*) CLG_MALLOC("cl.jumps.nj.1", sizeof(jCC));
|
||||
|
||||
jcc->from = from;
|
||||
jcc->jmp = jmp;
|
||||
jcc->to = to;
|
||||
jcc->jmpkind = jk_Call;
|
||||
jcc->call_counter = 0;
|
||||
jcc->cost = 0;
|
||||
|
||||
/* insert into JCC chain of calling BBCC.
|
||||
* This list is only used at dumping time */
|
||||
|
||||
if (from) {
|
||||
/* Prohibit corruption by array overrun */
|
||||
CLG_ASSERT((0 <= jmp) && (jmp <= from->bb->cjmp_count));
|
||||
jcc->next_from = from->jmp[jmp].jcc_list;
|
||||
from->jmp[jmp].jcc_list = jcc;
|
||||
}
|
||||
else {
|
||||
jcc->next_from = current_jccs.spontaneous;
|
||||
current_jccs.spontaneous = jcc;
|
||||
}
|
||||
|
||||
/* insert into JCC hash table */
|
||||
new_idx = jcc_hash_idx(from, jmp, to, current_jccs.size);
|
||||
jcc->next_hash = current_jccs.table[new_idx];
|
||||
current_jccs.table[new_idx] = jcc;
|
||||
|
||||
CLG_(stat).distinct_jccs++;
|
||||
|
||||
CLG_DEBUGIF(3) {
|
||||
VG_(printf)(" new_jcc (now %d): %p\n",
|
||||
CLG_(stat).distinct_jccs, jcc);
|
||||
}
|
||||
|
||||
return jcc;
|
||||
}
|
||||
|
||||
|
||||
/* get the jCC for a call arc (BBCC->BBCC) */
|
||||
jCC* CLG_(get_jcc)(BBCC* from, UInt jmp, BBCC* to)
|
||||
{
|
||||
jCC* jcc;
|
||||
UInt idx;
|
||||
|
||||
CLG_DEBUG(5, "+ get_jcc(bbcc %p/%u => bbcc %p)\n",
|
||||
from, jmp, to);
|
||||
|
||||
/* first check last recently used JCC */
|
||||
jcc = to->lru_to_jcc;
|
||||
if (jcc && (jcc->from == from) && (jcc->jmp == jmp)) {
|
||||
CLG_ASSERT(to == jcc->to);
|
||||
CLG_DEBUG(5,"- get_jcc: [LRU to] jcc %p\n", jcc);
|
||||
return jcc;
|
||||
}
|
||||
|
||||
jcc = from->lru_from_jcc;
|
||||
if (jcc && (jcc->to == to) && (jcc->jmp == jmp)) {
|
||||
CLG_ASSERT(from == jcc->from);
|
||||
CLG_DEBUG(5, "- get_jcc: [LRU from] jcc %p\n", jcc);
|
||||
return jcc;
|
||||
}
|
||||
|
||||
CLG_(stat).jcc_lru_misses++;
|
||||
|
||||
idx = jcc_hash_idx(from, jmp, to, current_jccs.size);
|
||||
jcc = current_jccs.table[idx];
|
||||
|
||||
while(jcc) {
|
||||
if ((jcc->from == from) &&
|
||||
(jcc->jmp == jmp) &&
|
||||
(jcc->to == to)) break;
|
||||
jcc = jcc->next_hash;
|
||||
}
|
||||
|
||||
if (!jcc)
|
||||
jcc = new_jcc(from, jmp, to);
|
||||
|
||||
/* set LRU */
|
||||
from->lru_from_jcc = jcc;
|
||||
to->lru_to_jcc = jcc;
|
||||
|
||||
CLG_DEBUG(5, "- get_jcc(bbcc %p => bbcc %p)\n",
|
||||
from, to);
|
||||
|
||||
return jcc;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
@@ -0,0 +1 @@
|
||||
# dummy
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.tool-tests.am
|
||||
|
||||
SUBDIRS = .
|
||||
DIST_SUBDIRS = .
|
||||
|
||||
dist_noinst_SCRIPTS = filter_stderr
|
||||
|
||||
EXTRA_DIST = \
|
||||
clreq.vgtest clreq.stderr.exp \
|
||||
simwork1.vgtest simwork1.stdout.exp simwork1.stderr.exp \
|
||||
simwork2.vgtest simwork2.stdout.exp simwork2.stderr.exp \
|
||||
simwork3.vgtest simwork3.stdout.exp simwork3.stderr.exp \
|
||||
simwork-both.vgtest simwork-both.stdout.exp simwork-both.stderr.exp \
|
||||
simwork-branch.vgtest simwork-branch.stdout.exp simwork-branch.stderr.exp \
|
||||
simwork-cache.vgtest simwork-cache.stdout.exp simwork-cache.stderr.exp \
|
||||
notpower2.vgtest notpower2.stderr.exp \
|
||||
notpower2-wb.vgtest notpower2-wb.stderr.exp \
|
||||
notpower2-hwpref.vgtest notpower2-hwpref.stderr.exp \
|
||||
notpower2-use.vgtest notpower2-use.stderr.exp \
|
||||
threads.vgtest threads.stderr.exp \
|
||||
threads-use.vgtest threads-use.stderr.exp
|
||||
|
||||
check_PROGRAMS = clreq simwork threads
|
||||
|
||||
AM_CFLAGS += $(AM_FLAG_M3264_PRI)
|
||||
AM_CXXFLAGS += $(AM_FLAG_M3264_PRI)
|
||||
|
||||
threads_LDADD = -lpthread
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,67 @@
|
||||
|
||||
// Similar to Cachegrind, check if instrumentation works in the presence
|
||||
// of a client request. Uses a Callgrind request to check this.
|
||||
|
||||
#include "../callgrind.h"
|
||||
|
||||
int some_work(int sum)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0;i<10;i++) sum += i; /* some dummy work */
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
int some_callgrind_clreqs()
|
||||
{
|
||||
int sum = some_work(0);
|
||||
|
||||
CALLGRIND_ZERO_STATS;
|
||||
|
||||
sum += some_work(sum);
|
||||
|
||||
CALLGRIND_DUMP_STATS;
|
||||
|
||||
sum += some_work(sum);
|
||||
|
||||
CALLGRIND_DUMP_STATS_AT("Please dump here");
|
||||
|
||||
return some_work(sum);
|
||||
}
|
||||
|
||||
int more_callgrind_clreqs()
|
||||
{
|
||||
int sum = some_callgrind_clreqs();
|
||||
|
||||
CALLGRIND_TOGGLE_COLLECT;
|
||||
|
||||
sum += some_callgrind_clreqs();
|
||||
|
||||
CALLGRIND_TOGGLE_COLLECT;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
more_callgrind_clreqs();
|
||||
|
||||
CALLGRIND_STOP_INSTRUMENTATION;
|
||||
|
||||
more_callgrind_clreqs();
|
||||
|
||||
CALLGRIND_START_INSTRUMENTATION;
|
||||
|
||||
more_callgrind_clreqs();
|
||||
|
||||
CALLGRIND_STOP_INSTRUMENTATION;
|
||||
|
||||
more_callgrind_clreqs();
|
||||
|
||||
CALLGRIND_START_INSTRUMENTATION;
|
||||
|
||||
return RUNNING_ON_VALGRIND;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
Events : Ir
|
||||
Collected :
|
||||
|
||||
I refs:
|
||||
@@ -0,0 +1,3 @@
|
||||
prog: clreq
|
||||
vgopts:
|
||||
cleanup: rm callgrind.out.*
|
||||
@@ -0,0 +1,33 @@
|
||||
#! /bin/sh
|
||||
|
||||
dir=`dirname $0`
|
||||
|
||||
$dir/../../tests/filter_stderr_basic |
|
||||
|
||||
# Remove "Callgrind, ..." line and the following copyright line.
|
||||
sed "/^Callgrind, a call-graph generating cache profiler/ , /./ d" |
|
||||
|
||||
# Remove pointer to callgrind_control
|
||||
sed "/^For interactive control,.*$/d" |
|
||||
|
||||
# Remove numbers from "Collected" line
|
||||
sed "s/^\(Collected *:\)[ 0-9]*$/\1/" |
|
||||
|
||||
# Remove numbers from I/D/LL "refs:" lines
|
||||
perl -p -e 's/((I|D|LL) *refs:)[ 0-9,()+rdw]*$/\1/' |
|
||||
|
||||
# Remove numbers from I1/D1/LL/LLi/LLd "misses:" and "miss rates:" lines
|
||||
perl -p -e 's/((I1|D1|LL|LLi|LLd) *(misses|miss rate):)[ 0-9,()+rdw%\.]*$/\1/' |
|
||||
|
||||
# Remove numbers from "Branches:", "Mispredicts:, and "Mispred rate:" lines
|
||||
perl -p -e 's/((Branches|Mispredicts|Mispred rate):)[ 0-9,()+condi%\.]*$/\1/' |
|
||||
|
||||
# Remove CPUID warnings lines for P4s and other machines
|
||||
sed "/warning: Pentium 4 with 12 KB micro-op instruction trace cache/d" |
|
||||
sed "/Simulating a 16 KB I-cache with 32 B lines/d" |
|
||||
sed "/warning: L3 cache found, using its data for the LL simulation./d" |
|
||||
sed "/warning: L4 cache found, using its data for the LL simulation./d" |
|
||||
sed "/Warning: Cannot auto-detect cache config, using defaults./d" |
|
||||
sed "/Run with -v to see./d" |
|
||||
sed "/warning: specified LL cache: line_size .*$/d" |
|
||||
sed "/warning: simulated LL cache: line_size .*$/d"
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
Events : Ir Dr Dw I1mr D1mr D1mw ILmr DLmr DLmw
|
||||
Collected :
|
||||
|
||||
I refs:
|
||||
I1 misses:
|
||||
LLi misses:
|
||||
I1 miss rate:
|
||||
LLi miss rate:
|
||||
|
||||
D refs:
|
||||
D1 misses:
|
||||
LLd misses:
|
||||
D1 miss rate:
|
||||
LLd miss rate:
|
||||
|
||||
LL refs:
|
||||
LL misses:
|
||||
LL miss rate:
|
||||
@@ -0,0 +1,3 @@
|
||||
prog: ../../tests/true
|
||||
vgopts: --I1=32768,8,64 --D1=24576,6,64 --LL=3145728,12,64 --simulate-hwpref=yes
|
||||
cleanup: rm callgrind.out.*
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
Events : Ir Dr Dw I1mr D1mr D1mw ILmr DLmr DLmw AcCost1 SpLoss1 AcCost2 SpLoss2
|
||||
Collected :
|
||||
|
||||
I refs:
|
||||
I1 misses:
|
||||
LLi misses:
|
||||
I1 miss rate:
|
||||
LLi miss rate:
|
||||
|
||||
D refs:
|
||||
D1 misses:
|
||||
LLd misses:
|
||||
D1 miss rate:
|
||||
LLd miss rate:
|
||||
|
||||
LL refs:
|
||||
LL misses:
|
||||
LL miss rate:
|
||||
@@ -0,0 +1,3 @@
|
||||
prog: ../../tests/true
|
||||
vgopts: --I1=32768,8,64 --D1=24576,6,64 --LL=3145728,12,64 --cacheuse=yes
|
||||
cleanup: rm callgrind.out.*
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
Events : Ir Dr Dw I1mr D1mr D1mw ILmr DLmr DLmw ILdmr DLdmr DLdmw
|
||||
Collected :
|
||||
|
||||
I refs:
|
||||
I1 misses:
|
||||
LLi misses:
|
||||
I1 miss rate:
|
||||
LLi miss rate:
|
||||
|
||||
D refs:
|
||||
D1 misses:
|
||||
LLd misses:
|
||||
D1 miss rate:
|
||||
LLd miss rate:
|
||||
|
||||
LL refs:
|
||||
LL misses:
|
||||
LL miss rate:
|
||||
@@ -0,0 +1,3 @@
|
||||
prog: ../../tests/true
|
||||
vgopts: --I1=32768,8,64 --D1=24576,6,64 --LL=3145728,12,64 --simulate-wb=yes
|
||||
cleanup: rm callgrind.out.*
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
Events : Ir Dr Dw I1mr D1mr D1mw ILmr DLmr DLmw
|
||||
Collected :
|
||||
|
||||
I refs:
|
||||
I1 misses:
|
||||
LLi misses:
|
||||
I1 miss rate:
|
||||
LLi miss rate:
|
||||
|
||||
D refs:
|
||||
D1 misses:
|
||||
LLd misses:
|
||||
D1 miss rate:
|
||||
LLd miss rate:
|
||||
|
||||
LL refs:
|
||||
LL misses:
|
||||
LL miss rate:
|
||||
@@ -0,0 +1,3 @@
|
||||
prog: ../../tests/true
|
||||
vgopts: --I1=32768,8,64 --D1=24576,6,64 --LL=3145728,12,64
|
||||
cleanup: rm callgrind.out.*
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
|
||||
Events : Ir Dr Dw I1mr D1mr D1mw ILmr DLmr DLmw Bc Bcm Bi Bim
|
||||
Collected :
|
||||
|
||||
I refs:
|
||||
I1 misses:
|
||||
LLi misses:
|
||||
I1 miss rate:
|
||||
LLi miss rate:
|
||||
|
||||
D refs:
|
||||
D1 misses:
|
||||
LLd misses:
|
||||
D1 miss rate:
|
||||
LLd miss rate:
|
||||
|
||||
LL refs:
|
||||
LL misses:
|
||||
LL miss rate:
|
||||
|
||||
Branches:
|
||||
Mispredicts:
|
||||
Mispred rate:
|
||||
@@ -0,0 +1 @@
|
||||
Sum: 1000000
|
||||
@@ -0,0 +1,3 @@
|
||||
prog: simwork
|
||||
vgopts: --cache-sim=yes --branch-sim=yes
|
||||
cleanup: rm callgrind.out.*
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
Events : Ir Bc Bcm Bi Bim
|
||||
Collected :
|
||||
|
||||
I refs:
|
||||
|
||||
Branches:
|
||||
Mispredicts:
|
||||
Mispred rate:
|
||||
@@ -0,0 +1 @@
|
||||
Sum: 1000000
|
||||
@@ -0,0 +1,3 @@
|
||||
prog: simwork
|
||||
vgopts: --branch-sim=yes
|
||||
cleanup: rm callgrind.out.*
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
Events : Ir Dr Dw I1mr D1mr D1mw ILmr DLmr DLmw
|
||||
Collected :
|
||||
|
||||
I refs:
|
||||
I1 misses:
|
||||
LLi misses:
|
||||
I1 miss rate:
|
||||
LLi miss rate:
|
||||
|
||||
D refs:
|
||||
D1 misses:
|
||||
LLd misses:
|
||||
D1 miss rate:
|
||||
LLd miss rate:
|
||||
|
||||
LL refs:
|
||||
LL misses:
|
||||
LL miss rate:
|
||||
@@ -0,0 +1 @@
|
||||
Sum: 1000000
|
||||
@@ -0,0 +1,3 @@
|
||||
prog: simwork
|
||||
vgopts: --cache-sim=yes
|
||||
cleanup: rm callgrind.out.*
|
||||
@@ -0,0 +1,66 @@
|
||||
// Some work exercising the cache simulator
|
||||
// with a simple call graph
|
||||
|
||||
#include "../callgrind.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define SIZE 100000
|
||||
|
||||
double *a, *b, *c;
|
||||
|
||||
void init()
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i< SIZE; i++) a[i] = b[i] = 1.0;
|
||||
}
|
||||
|
||||
void do_add()
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i< SIZE; i++) {
|
||||
a[i] += 1.0;
|
||||
c[i] = a[i] + b[i];
|
||||
}
|
||||
}
|
||||
|
||||
double do_sum()
|
||||
{
|
||||
int i;
|
||||
double sum=0.0;
|
||||
|
||||
do_add();
|
||||
for(i = 0; i< SIZE; i++) sum += c[i];
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
double do_some_work(int iter)
|
||||
{
|
||||
double sum=0.0;
|
||||
|
||||
if (iter > 0) sum += do_some_work(iter-1);
|
||||
do_add();
|
||||
sum += do_sum();
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
double res;
|
||||
|
||||
a = (double*) malloc(SIZE * sizeof(double));
|
||||
b = (double*) malloc(SIZE * sizeof(double));
|
||||
c = (double*) malloc(SIZE * sizeof(double));
|
||||
|
||||
CALLGRIND_ZERO_STATS;
|
||||
init();
|
||||
res = do_some_work(1);
|
||||
CALLGRIND_DUMP_STATS;
|
||||
|
||||
printf("Sum: %.0f\n", res);
|
||||
return RUNNING_ON_VALGRIND;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
Events : Ir Dr Dw I1mr D1mr D1mw ILmr DLmr DLmw
|
||||
Collected :
|
||||
|
||||
I refs:
|
||||
I1 misses:
|
||||
LLi misses:
|
||||
I1 miss rate:
|
||||
LLi miss rate:
|
||||
|
||||
D refs:
|
||||
D1 misses:
|
||||
LLd misses:
|
||||
D1 miss rate:
|
||||
LLd miss rate:
|
||||
|
||||
LL refs:
|
||||
LL misses:
|
||||
LL miss rate:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user