[xbuild] Add new reserved properties $(MSBuildThisFile*).
[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 #include <unistd.h>
15
16 #ifdef HAVE_EXECINFO_H
17 #include <execinfo.h>
18 #endif
19
20 #include <mono/io-layer/io-layer.h>
21
22 #include "lock-tracer.h"
23
24 /*
25  * This is a very simple lock trace implementation. It can be used to verify that the runtime is
26  * correctly following all locking rules.
27  * 
28  * To log more kind of locks just do the following:
29  *      - add an entry into the RuntimeLocks enum
30  *  - change EnterCriticalSection(mutex) to mono_locks_acquire (mutex, LockName)
31  *  - change LeaveCriticalSection to mono_locks_release (mutex, LockName)
32  *  - change the decoder to understand the new lock kind.
33  *
34  * TODO:
35  *      - Use unbuffered IO without fsync
36  *  - Switch to a binary log format
37  *  - Enable tracing of more runtime locks
38  *  - Add lock check assertions (must_not_hold_any_lock_but, must_hold_lock, etc)
39  *   This should be used to verify methods that expect that a given lock is held at entrypoint, for example.
40  * 
41  * To use the trace, define LOCK_TRACER in lock-trace.h and when running mono define MONO_ENABLE_LOCK_TRACER.
42  * This will produce a locks.ZZZ where ZZZ is the pid of the mono process.
43  * Use the decoder to verify the result.
44  */
45
46 #ifdef LOCK_TRACER
47
48 static FILE *trace_file;
49 static CRITICAL_SECTION tracer_lock;
50
51 typedef enum {
52         RECORD_MUST_NOT_HOLD_ANY,
53         RECORD_MUST_NOT_HOLD_ONE,
54         RECORD_MUST_HOLD_ONE,
55         RECORD_LOCK_ACQUIRED,
56         RECORD_LOCK_RELEASED
57 } RecordType;
58
59 void
60 mono_locks_tracer_init (void)
61 {
62         char *name;
63         InitializeCriticalSection (&tracer_lock);
64         if (!getenv ("MONO_ENABLE_LOCK_TRACER"))
65                 return;
66         name = g_strdup_printf ("locks.%d", getpid ());
67         trace_file = fopen (name, "w+");
68         g_free (name);
69 }
70
71
72 #ifdef HAVE_EXECINFO_H
73
74 static int
75 mono_backtrace (gpointer array[], int traces)
76 {
77         return backtrace (array, traces);
78 }
79
80 #else
81
82 static int
83 mono_backtrace (gpointer array[], int traces)
84 {
85         return 0;
86 }
87
88 #endif
89
90 static void
91 add_record (RecordType record_kind, RuntimeLocks kind, gpointer lock)
92 {
93         gpointer frames[10];
94         char *msg;
95         if (!trace_file)
96                 return;
97
98         memset (frames, 0, sizeof (gpointer));
99         mono_backtrace (frames, 6);
100
101         /*We only dump 5 frames, which should be more than enough to most analysis.*/
102         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]);
103         fwrite (msg, strlen (msg), 1, trace_file);
104         fflush (trace_file);
105         g_free (msg);
106 }
107
108 void
109 mono_locks_lock_acquired (RuntimeLocks kind, gpointer lock)
110 {
111         add_record (RECORD_LOCK_ACQUIRED, kind, lock);
112 }
113
114 void
115 mono_locks_lock_released (RuntimeLocks kind, gpointer lock)
116 {
117         add_record (RECORD_LOCK_RELEASED, kind, lock);
118 }
119
120 #endif