3 * Runtime simple lock tracer
6 * Rodrigo Kumpera (rkumpera@novell.com)
14 #include <sys/types.h>
20 #ifdef HAVE_EXECINFO_H
24 #include <mono/utils/mono-compiler.h>
25 #include <mono/utils/mono-threads.h>
27 #include "lock-tracer.h"
30 * This is a very simple lock trace implementation. It can be used to verify that the runtime is
31 * correctly following all locking rules.
33 * To log more kind of locks just do the following:
34 * - add an entry into the RuntimeLocks enum
35 * - change mono_os_mutex_lock(mutex) to mono_locks_os_acquire (mutex, LockName)
36 * - change mono_os_mutex_unlock(mutex) to mono_locks_os_release (mutex, LockName)
37 * - change mono_coop_mutex_lock(mutex) to mono_locks_coop_acquire (mutex, LockName)
38 * - change mono_coop_mutex_unlock(mutex) to mono_locks_coop_release (mutex, LockName)
39 * - change the decoder to understand the new lock kind.
42 * - Use unbuffered IO without fsync
43 * - Switch to a binary log format
44 * - Enable tracing of more runtime locks
45 * - Add lock check assertions (must_not_hold_any_lock_but, must_hold_lock, etc)
46 * This should be used to verify methods that expect that a given lock is held at entrypoint, for example.
48 * To use the trace, define LOCK_TRACER in lock-trace.h and when running mono define MONO_ENABLE_LOCK_TRACER.
49 * This will produce a locks.ZZZ where ZZZ is the pid of the mono process.
50 * Use the decoder to verify the result.
59 static FILE *trace_file
;
60 static mono_mutex_t tracer_lock
;
61 static size_t base_address
;
64 RECORD_MUST_NOT_HOLD_ANY
,
65 RECORD_MUST_NOT_HOLD_ONE
,
72 mono_locks_tracer_init (void)
77 mono_os_mutex_init_recursive (&tracer_lock
);
79 if (!g_hasenv ("MONO_ENABLE_LOCK_TRACER"))
82 name
= g_strdup_printf ("locks.%d", getpid ());
83 trace_file
= fopen (name
, "w+");
87 res
= dladdr ((void*)&mono_locks_tracer_init
, &info
);
88 /* The 0x1000 offset was found by empirically trying it. */
90 base_address
= (size_t)info
.dli_fbase
- 0x1000;
95 #ifdef HAVE_EXECINFO_H
98 mono_backtrace (gpointer array
[], int traces
)
100 return backtrace (array
, traces
);
106 mono_backtrace (gpointer array
[], int traces
)
114 add_record (RecordType record_kind
, RuntimeLocks kind
, gpointer lock
)
117 const int no_frames
= 6;
118 gpointer frames
[no_frames
];
124 memset (frames
, 0, sizeof (gpointer
) * no_frames
);
125 mono_backtrace (frames
, no_frames
);
126 for (i
= 0; i
< no_frames
; ++i
)
127 frames
[i
] = (gpointer
)((size_t)frames
[i
] - base_address
);
129 /*We only dump 5 frames, which should be more than enough to most analysis.*/
130 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]);
131 fwrite (msg
, strlen (msg
), 1, trace_file
);
137 mono_locks_lock_acquired (RuntimeLocks kind
, gpointer lock
)
139 add_record (RECORD_LOCK_ACQUIRED
, kind
, lock
);
143 mono_locks_lock_released (RuntimeLocks kind
, gpointer lock
)
145 add_record (RECORD_LOCK_RELEASED
, kind
, lock
);
149 MONO_EMPTY_SOURCE_FILE (lock_tracer
);
150 #endif /* LOCK_TRACER */