2 * lock-tracer.c: Runtime simple lock tracer
5 * Rodrigo Kumpera (rkumpera@novell.com)
13 #include <sys/types.h>
19 #ifdef HAVE_EXECINFO_H
23 #include <mono/io-layer/io-layer.h>
25 #include "lock-tracer.h"
28 * This is a very simple lock trace implementation. It can be used to verify that the runtime is
29 * correctly following all locking rules.
31 * To log more kind of locks just do the following:
32 * - add an entry into the RuntimeLocks enum
33 * - change mono_os_mutex_lock(mutex) to mono_locks_os_acquire (mutex, LockName)
34 * - change mono_os_mutex_unlock(mutex) to mono_locks_os_release (mutex, LockName)
35 * - change mono_coop_mutex_lock(mutex) to mono_locks_coop_acquire (mutex, LockName)
36 * - change mono_coop_mutex_unlock(mutex) to mono_locks_coop_release (mutex, LockName)
37 * - change the decoder to understand the new lock kind.
40 * - Use unbuffered IO without fsync
41 * - Switch to a binary log format
42 * - Enable tracing of more runtime locks
43 * - Add lock check assertions (must_not_hold_any_lock_but, must_hold_lock, etc)
44 * This should be used to verify methods that expect that a given lock is held at entrypoint, for example.
46 * To use the trace, define LOCK_TRACER in lock-trace.h and when running mono define MONO_ENABLE_LOCK_TRACER.
47 * This will produce a locks.ZZZ where ZZZ is the pid of the mono process.
48 * Use the decoder to verify the result.
57 static FILE *trace_file;
58 static mono_mutex_t tracer_lock;
59 static size_t base_address;
62 RECORD_MUST_NOT_HOLD_ANY,
63 RECORD_MUST_NOT_HOLD_ONE,
70 mono_locks_tracer_init (void)
75 mono_os_mutex_init_recursive (&tracer_lock);
76 if (!g_getenv ("MONO_ENABLE_LOCK_TRACER"))
78 name = g_strdup_printf ("locks.%d", getpid ());
79 trace_file = fopen (name, "w+");
83 res = dladdr ((void*)&mono_locks_tracer_init, &info);
84 /* The 0x1000 offset was found by empirically trying it. */
86 base_address = (size_t)info.dli_fbase - 0x1000;
91 #ifdef HAVE_EXECINFO_H
94 mono_backtrace (gpointer array[], int traces)
96 return backtrace (array, traces);
102 mono_backtrace (gpointer array[], int traces)
110 add_record (RecordType record_kind, RuntimeLocks kind, gpointer lock)
113 const int no_frames = 6;
114 gpointer frames[no_frames];
120 memset (frames, 0, sizeof (gpointer) * no_frames);
121 mono_backtrace (frames, no_frames);
122 for (i = 0; i < no_frames; ++i)
123 frames [i] = (gpointer)((size_t)frames[i] - base_address);
125 /*We only dump 5 frames, which should be more than enough to most analysis.*/
126 msg = g_strdup_printf ("%x,%d,%d,%p,%p,%p,%p,%p,%p\n", (guint32)mono_native_thread_id_get (), record_kind, kind, lock, frames [1], frames [2], frames [3], frames [4], frames [5]);
127 fwrite (msg, strlen (msg), 1, trace_file);
133 mono_locks_lock_acquired (RuntimeLocks kind, gpointer lock)
135 add_record (RECORD_LOCK_ACQUIRED, kind, lock);
139 mono_locks_lock_released (RuntimeLocks kind, gpointer lock)
141 add_record (RECORD_LOCK_RELEASED, kind, lock);
146 // Quiet Visual Studio linker warning, LNK4221, in cases when this source file intentional ends up empty.
147 void __mono_win32_lock_tracer_quiet_lnk4221(void) {}
149 #endif /* LOCK_TRACER */