[profiler] Implement call context introspection for enter/leave events.
authorAlex Rønne Petersen <alexrp@xamarin.com>
Tue, 25 Jul 2017 22:55:22 +0000 (00:55 +0200)
committerAlex Rønne Petersen <alexrp@xamarin.com>
Tue, 1 Aug 2017 04:15:06 +0000 (06:15 +0200)
commitf9ae98ab88f522219cd6be6fd282ef30adbc5365
treed875c122d99e38f16f005b6a3a1a2aa9d11a85a5
parenta71c1c7196c272486507a96a784e0f0158acac35
[profiler] Implement call context introspection for enter/leave events.

When this feature is enabled for a method, the enter/leave event receives an
additional argument, a so-called 'call context'. This call context contains
enough information about the stack frame of the instrumented method to allow
the enter/leave callback to inspect the 'this' reference, method arguments,
local variables, and the return value (for non-void methods).

This feature enables some interesting scenarios that were not possible with
the regular enter/leave events. For example, a profiler could instrument
well-known methods in the managed thread pool code to get an idea of how an
application is using the thread pool, or it could instrument network-related
methods to gather statistics or even log all network traffic.

This is implemented by storing a MonoProfilerCallContext on the stack, whose
MonoContext field is populated by executing an OP_FILL_PROF_CALL_CTX opcode
which stores the stack pointer, frame pointer, and all callee-saved registers
to it. For the epilogue, a pointer to the return value (for non-void methods)
is also stored in the MonoProfilerCallContext. An address to this context is
then passed to mono_profiler_raise_method_enter/leave. Based on debug info,
all arguments and locals can then be looked up in the instrumented method's
stack frame.

For the interpreter, we just store an InterpFrame pointer on the
MonoProfilerCallContext and look everything up from that. We don't need debug
info in this case.

This feature is currently not supported with LLVM (for regular LLVM mode, it
will fall back to Mono's JIT, while for LLVM-only mode, it's not available).

I also refactored the interpreter code so that enter/leave events are generated
not only when interpreter debugging is enabled. Also, the interpreter will only
call mono_profiler_get_call_instrumentation_flags () once per method now.
Finally, I made the interpreter also generate exception leave events.
30 files changed:
CODEOWNERS
mono/metadata/profiler-events.h
mono/metadata/profiler-private.h
mono/metadata/profiler.c
mono/metadata/profiler.h
mono/mini/Makefile.am.in
mono/mini/cpu-amd64.md
mono/mini/cpu-arm.md
mono/mini/cpu-arm64.md
mono/mini/cpu-x86.md
mono/mini/debug-mini.c
mono/mini/interp/interp-internals.h
mono/mini/interp/interp.c
mono/mini/interp/mintops.def
mono/mini/interp/transform.c
mono/mini/method-to-ir.c
mono/mini/mini-amd64.c
mono/mini/mini-arm.c
mono/mini/mini-arm64.c
mono/mini/mini-ops.h
mono/mini/mini-profiler.c [new file with mode: 0644]
mono/mini/mini-runtime.c
mono/mini/mini-x86.c
mono/mini/mini.c
mono/mini/mini.h
mono/profiler/log.c
msvc/libmono-static.vcxproj
msvc/libmono-static.vcxproj.filters
msvc/mono.def
msvc/monosgen.def