update .sln too.
[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 EnterCriticalSection(mutex) to mono_locks_acquire (mutex, LockName)
34  *  - change LeaveCriticalSection to mono_locks_release (mutex, LockName)
35  *  - change the decoder to understand the new lock kind.
36  *
37  * TODO:
38  *      - Use unbuffered IO without fsync
39  *  - Switch to a binary log format
40  *  - Enable tracing of more runtime locks
41  *  - Add lock check assertions (must_not_hold_any_lock_but, must_hold_lock, etc)
42  *   This should be used to verify methods that expect that a given lock is held at entrypoint, for example.
43  * 
44  * To use the trace, define LOCK_TRACER in lock-trace.h and when running mono define MONO_ENABLE_LOCK_TRACER.
45  * This will produce a locks.ZZZ where ZZZ is the pid of the mono process.
46  * Use the decoder to verify the result.
47  */
48
49 #ifdef LOCK_TRACER
50
51 static FILE *trace_file;
52 static CRITICAL_SECTION tracer_lock;
53
54 typedef enum {
55         RECORD_MUST_NOT_HOLD_ANY,
56         RECORD_MUST_NOT_HOLD_ONE,
57         RECORD_MUST_HOLD_ONE,
58         RECORD_LOCK_ACQUIRED,
59         RECORD_LOCK_RELEASED
60 } RecordType;
61
62 void
63 mono_locks_tracer_init (void)
64 {
65         char *name;
66         InitializeCriticalSection (&tracer_lock);
67         if (!g_getenv ("MONO_ENABLE_LOCK_TRACER"))
68                 return;
69         name = g_strdup_printf ("locks.%d", getpid ());
70         trace_file = fopen (name, "w+");
71         g_free (name);
72 }
73
74
75 #ifdef HAVE_EXECINFO_H
76
77 static int
78 mono_backtrace (gpointer array[], int traces)
79 {
80         return backtrace (array, traces);
81 }
82
83 #else
84
85 static int
86 mono_backtrace (gpointer array[], int traces)
87 {
88         return 0;
89 }
90
91 #endif
92
93 static void
94 add_record (RecordType record_kind, RuntimeLocks kind, gpointer lock)
95 {
96         gpointer frames[10];
97         char *msg;
98         if (!trace_file)
99                 return;
100
101         memset (frames, 0, sizeof (gpointer));
102         mono_backtrace (frames, 6);
103
104         /*We only dump 5 frames, which should be more than enough to most analysis.*/
105         msg = g_strdup_printf ("%x,%d,%d,%p,%p,%p,%p,%p,%p\n", (guint32)GetCurrentThreadId (), record_kind, kind, lock, frames [1], frames [2], frames [3], frames [4], frames [5]);
106         fwrite (msg, strlen (msg), 1, trace_file);
107         fflush (trace_file);
108         g_free (msg);
109 }
110
111 void
112 mono_locks_lock_acquired (RuntimeLocks kind, gpointer lock)
113 {
114         add_record (RECORD_LOCK_ACQUIRED, kind, lock);
115 }
116
117 void
118 mono_locks_lock_released (RuntimeLocks kind, gpointer lock)
119 {
120         add_record (RECORD_LOCK_RELEASED, kind, lock);
121 }
122
123 #endif