551f8a751e8e1d58edac3c6186346d64fd46100c
[mono.git] / mono / metadata / lock-tracer.c
1 /*
2  * lock-tracer.c: Runtime simple lock tracer
3  *
4  * Authors:
5  *      Rodrigo Kumpera (rkumpera@novell.com)
6  * 
7  */
8
9 #include <config.h>
10 #include <stdio.h>
11 #include <string.h>
12
13 #include <sys/types.h>
14
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18
19 #ifdef HAVE_EXECINFO_H
20 #include <execinfo.h>
21 #endif
22
23 #include <mono/io-layer/io-layer.h>
24
25 #include "lock-tracer.h"
26
27 /*
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.
30  * 
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.
38  *
39  * TODO:
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.
45  * 
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.
49  */
50
51 #ifdef LOCK_TRACER
52
53 #ifdef TARGET_OSX
54 #include <dlfcn.h>
55 #endif
56
57 static FILE *trace_file;
58 static mono_mutex_t tracer_lock;
59 static size_t base_address;
60
61 typedef enum {
62         RECORD_MUST_NOT_HOLD_ANY,
63         RECORD_MUST_NOT_HOLD_ONE,
64         RECORD_MUST_HOLD_ONE,
65         RECORD_LOCK_ACQUIRED,
66         RECORD_LOCK_RELEASED
67 } RecordType;
68
69 void
70 mono_locks_tracer_init (void)
71 {
72         Dl_info info;
73         int res;
74         char *name;
75         mono_os_mutex_init_recursive (&tracer_lock);
76         if (!g_getenv ("MONO_ENABLE_LOCK_TRACER"))
77                 return;
78         name = g_strdup_printf ("locks.%d", getpid ());
79         trace_file = fopen (name, "w+");
80         g_free (name);
81
82 #ifdef TARGET_OSX
83         res = dladdr ((void*)&mono_locks_tracer_init, &info);
84         /* The 0x1000 offset was found by empirically trying it. */
85         if (res)
86                 base_address = (size_t)info.dli_fbase - 0x1000;
87 #endif
88 }
89
90
91 #ifdef HAVE_EXECINFO_H
92
93 static int
94 mono_backtrace (gpointer array[], int traces)
95 {
96         return backtrace (array, traces);
97 }
98
99 #else
100
101 static int
102 mono_backtrace (gpointer array[], int traces)
103 {
104         return 0;
105 }
106
107 #endif
108
109 static void
110 add_record (RecordType record_kind, RuntimeLocks kind, gpointer lock)
111 {
112         int i = 0;
113         const int no_frames = 6;
114         gpointer frames[no_frames];
115
116         char *msg;
117         if (!trace_file)
118                 return;
119
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);
124
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);
128         fflush (trace_file);
129         g_free (msg);
130 }
131
132 void
133 mono_locks_lock_acquired (RuntimeLocks kind, gpointer lock)
134 {
135         add_record (RECORD_LOCK_ACQUIRED, kind, lock);
136 }
137
138 void
139 mono_locks_lock_released (RuntimeLocks kind, gpointer lock)
140 {
141         add_record (RECORD_LOCK_RELEASED, kind, lock);
142 }
143 #else
144
145 #ifdef _MSC_VER
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) {}
148 #endif
149 #endif /* LOCK_TRACER */