Merge pull request #314 from ceph/wip-4228
[ceph.git] / src / common / lockdep.cc
1
2 #include "lockdep.h"
3
4 int g_lockdep = 0;
5
6 #include "include/types.h"
7 #include "Clock.h"
8 #include "BackTrace.h"
9
10 #include <ext/hash_map>
11
12 #include "config.h"
13
14 #undef dout
15 #define  dout(l)    if (l<=g_conf.debug_lockdep) *_dout << g_clock.now() << " " << pthread_self() << " lockdep: "
16
17
18 pthread_mutex_t lockdep_mutex = PTHREAD_MUTEX_INITIALIZER;
19
20
21 #define MAX_LOCKS  100   // increase me as needed
22
23 hash_map<const char *, int> lock_ids;
24 map<int, const char *> lock_names;
25
26 int last_id = 0;
27
28 hash_map<pthread_t, map<int,BackTrace*> > held;
29 BackTrace *follows[MAX_LOCKS][MAX_LOCKS];       // follows[a][b] means b taken after a 
30
31
32 #define BACKTRACE_SKIP 3
33
34
35 int lockdep_register(const char *name)
36 {
37   int id;
38
39   pthread_mutex_lock(&lockdep_mutex);
40
41   if (last_id == 0)
42     for (int i=0; i<MAX_LOCKS; i++)
43       for (int j=0; j<MAX_LOCKS; j++)
44         follows[i][j] = NULL;
45   
46   hash_map<const char *, int>::iterator p = lock_ids.find(name);
47   if (p == lock_ids.end()) {
48     assert(last_id < MAX_LOCKS);
49     id = last_id++;
50     lock_ids[name] = id;
51     lock_names[id] = name;
52     dout(10) << "registered '" << name << "' as " << id << std::endl;
53   } else {
54     id = p->second;
55     dout(20) << "had '" << name << "' as " << id << std::endl;
56   }
57
58   pthread_mutex_unlock(&lockdep_mutex);
59
60   return id;
61 }
62
63
64 // does a follow b?
65 static bool does_follow(int a, int b)
66 {
67   if (follows[a][b]) {
68     *_dout << std::endl;
69     dout(0) << "------------------------------------" << std::endl;
70     dout(0) << "existing dependency " << lock_names[a] << " (" << a << ") -> " << lock_names[b] << " (" << b << ") at: " << std::endl;
71     follows[a][b]->print(*_dout);
72     *_dout << std::endl;
73     return true;
74   }
75
76   for (int i=0; i<MAX_LOCKS; i++) {
77     if (follows[a][i] &&
78         does_follow(i, b)) {
79       dout(0) << "existing intermediate dependency " << lock_names[a] << " (" << a << ") -> " << lock_names[i] << " (" << i << ") at:" << std::endl;
80       follows[a][i]->print(*_dout);
81       *_dout << std::endl;
82       return true;
83     }
84   }
85
86   return false;
87 }
88
89 int lockdep_will_lock(const char *name, int id)
90 {
91   pthread_t p = pthread_self();
92   if (id < 0) id = lockdep_register(name);
93
94   pthread_mutex_lock(&lockdep_mutex);
95   dout(20) << "_will_lock " << name << " (" << id << ")" << std::endl;
96
97   // check dependency graph
98   map<int, BackTrace *> &m = held[p];
99   for (map<int, BackTrace *>::iterator p = m.begin();
100        p != m.end();
101        p++) {
102     if (p->first == id) {
103       *_dout << std::endl;
104       dout(0) << "recursive lock of " << name << " (" << id << ")" << std::endl;
105       BackTrace *bt = new BackTrace(BACKTRACE_SKIP);
106       bt->print(*_dout);
107       if (g_lockdep >= 2) {
108         *_dout << std::endl;
109         dout(0) << "previously locked at" << std::endl;
110         p->second->print(*_dout);
111       }
112       *_dout << std::endl;
113       assert(0);
114     }
115     else if (!follows[p->first][id]) {
116       // new dependency 
117       
118       // did we just create a cycle?
119       BackTrace *bt = new BackTrace(BACKTRACE_SKIP);
120       if (does_follow(id, p->first)) {
121         dout(0) << "new dependency " << lock_names[p->first] << " (" << p->first << ") -> " << name << " (" << id << ")"
122                 << " creates a cycle at"
123                 << std::endl;
124         bt->print(*_dout);
125         *_dout << std::endl;
126
127         dout(0) << "btw, i am holding these locks:" << std::endl;
128         for (map<int, BackTrace *>::iterator q = m.begin();
129              q != m.end();
130              q++) {
131           dout(0) << "  " << lock_names[q->first] << " (" << q->first << ")" << std::endl;
132           if (g_lockdep >= 2) {
133             q->second->print(*_dout);
134             *_dout << std::endl;
135           }
136         }
137
138         *_dout << std::endl;
139         *_dout << std::endl;
140
141         // don't add this dependency, or we'll get aMutex. cycle in the graph, and
142         // does_follow() won't terminate.
143
144         assert(0);  // actually, we should just die here.
145       } else {
146         follows[p->first][id] = bt;
147         dout(10) << lock_names[p->first] << " -> " << name << " at" << std::endl;
148         //bt->print(*_dout);
149       }
150     }
151   }
152
153   pthread_mutex_unlock(&lockdep_mutex);
154   return id;
155 }
156
157 int lockdep_locked(const char *name, int id)
158 {
159   pthread_t p = pthread_self();
160
161   if (id < 0) id = lockdep_register(name);
162
163   pthread_mutex_lock(&lockdep_mutex);
164   dout(20) << "_locked " << name << std::endl;
165   if (g_lockdep >= 2)
166     held[p][id] = new BackTrace(BACKTRACE_SKIP);
167   else
168     held[p][id] = 0;
169   pthread_mutex_unlock(&lockdep_mutex);
170   return id;
171 }
172
173 int lockdep_unlocked(const char *name, int id)
174 {
175   pthread_t p = pthread_self();
176   
177   if (id < 0) id = lockdep_register(name);
178
179   pthread_mutex_lock(&lockdep_mutex);
180   dout(20) << "_unlocked " << name << std::endl;
181
182   // don't assert.. lockdep may be enabled at any point in time
183   //assert(held.count(p));
184   //assert(held[p].count(id));
185
186   delete held[p][id];
187   held[p].erase(id);
188   pthread_mutex_unlock(&lockdep_mutex);
189   return id;
190 }
191
192