Merge branch 'development'
[NeonServV5.git] / src / mutexDebug.c
1 /* mutexDebug.c - NeonServ v5.6
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17 #include "main.h"
18 #include "mutexDebug.h"
19
20 #ifdef ENABLE_MUTEX_DEBUG
21
22 struct MutexLockEvent {
23     int locked;
24     const char *file;
25     unsigned int line;
26     struct MutexLockEvent *next;
27 };
28
29 struct MutexLock {
30     int thread;
31     int count;
32     struct MutexLockEvent *first_event, *last_event;
33     struct MutexLock *prev, *next;
34 };
35
36 struct MutexNode {
37     pthread_mutex_t *mutex;
38     struct MutexLock *first_lock, *last_lock;
39     struct MutexNode *next;
40 };
41
42 static pthread_mutex_t synchronized;
43 static struct MutexNode *mutex_nodes = NULL;
44
45 static struct MutexNode *getMutexNode(pthread_mutex_t *mutex, int create);
46 static void lockMutex(struct MutexNode *node, const char *file, unsigned int line);
47 static void unlockMutex(struct MutexNode *node, const char *file, unsigned int line);
48 static void mutex_replay(struct MutexNode *node);
49
50 void xmutex(int lock, pthread_mutex_t *mutex, const char *file, unsigned int line) {
51     pthread_mutex_lock(&synchronized);
52     struct MutexNode *node = getMutexNode(mutex, 1);
53     if(lock)
54         lockMutex(node, file, line);
55     else
56         unlockMutex(node, file, line);
57     pthread_mutex_unlock(&synchronized);
58 }
59
60 void initMutexDebug() {
61     THREAD_MUTEX_INIT(synchronized);
62 }
63
64 static struct MutexNode *getMutexNode(pthread_mutex_t *mutex, int create) {
65     struct MutexNode *node;
66     for(node = mutex_nodes; node; node = node->next) {
67         if(node->mutex == mutex)
68             return node;
69     }
70     if(!create)
71         return NULL;
72     node = malloc(sizeof(*node));
73     node->first_lock = NULL;
74     node->last_lock = NULL;
75     node->mutex = mutex;
76     node->next = mutex_nodes;
77     mutex_nodes = node;
78     return node;
79 }
80
81 static void lockMutex(struct MutexNode *node, const char *file, unsigned int line) {
82     struct MutexLock *lock;
83     int thread = getCurrentThreadID();
84     for(lock = node->first_lock; lock; lock = lock->next) {
85         if(lock->thread == thread)
86             break;
87     }
88     if(!lock) {
89         lock = malloc(sizeof(*lock));
90         lock->thread = thread;
91         lock->count = 0;
92         lock->first_event = NULL;
93         lock->last_event = NULL;
94         lock->prev = node->last_lock;
95         lock->next = NULL;
96         node->last_lock = lock;
97         if(!node->first_lock)
98             node->first_lock = lock;
99     }
100     lock->count++;
101     //add event
102     struct MutexLockEvent *event = malloc(sizeof(*event));
103     event->locked = 1;
104     event->file = file;
105     event->line = line;
106     event->next = NULL;
107     if(lock->last_event) {
108         lock->last_event->next = event;
109         lock->last_event = event;
110     } else {
111         lock->first_event = event;
112         lock->last_event = event;
113     }
114 }
115
116 static void unlockMutex(struct MutexNode *node, const char *file, unsigned int line) {
117     struct MutexLock *lock;
118     int thread = getCurrentThreadID();
119     for(lock = node->first_lock; lock; lock = lock->next) {
120         if(lock->thread == thread)
121             break;
122     }
123     if(!lock)
124         return;
125     lock->count--;
126     if(lock->count <= 0) {
127         //remove lock
128         if(lock->prev)
129             lock->prev->next = lock->next;
130         else
131             node->first_lock = lock->next;
132         if(lock->next)
133             lock->next->prev = lock->prev;
134         else
135             node->last_lock = lock->prev;
136         //recursive free all events
137         struct MutexLockEvent *event, *next_event;
138         for(event = lock->first_event; event; event = next_event) {
139             next_event = event->next;
140             free(event);
141         }
142         free(lock);
143     } else {
144         //add event
145         struct MutexLockEvent *event = malloc(sizeof(*event));
146         event->locked = 0;
147         event->file = file;
148         event->line = line;
149         event->next = NULL;
150         if(lock->last_event) {
151             lock->last_event->next = event;
152             lock->last_event = event;
153         } else {
154             lock->first_event = event;
155             lock->last_event = event;
156         }
157     }
158 }
159
160 void mutex_debug(pthread_mutex_t *mutex) {
161     //replay mutex events to stdout
162     struct MutexNode *node;
163     if(mutex) {
164         node = getMutexNode(mutex, 0);
165         if(!node) {
166             printf("[MUTEX_DEBUG] unknown mutex!\n");
167             return;
168         }
169         mutex_replay(node);
170     } else {
171         for(node = mutex_nodes; node; node = node->next) {
172             mutex_replay(node);
173         }
174     }
175     printf("[MUTEX_DEBUG] end of mutex replay.\n");
176 }
177
178 static void mutex_replay(struct MutexNode *node) {
179     printf("[MUTEX_DEBUG] mutex replay:\n");
180     struct MutexLock *lock;
181     struct MutexLockEvent *event;
182     for(lock = node->first_lock; lock; lock = lock->next) {
183         printf("[MUTEX_DEBUG]  THREAD %d (%d locks):\n", lock->thread, lock->count);
184         for(event = lock->first_event; event; event = event->next) {
185             printf("[MUTEX_DEBUG]   %s in %s:%d\n", (event->locked ? "lock  " : "unlock"), event->file, event->line);
186         }
187     }
188 }
189
190 #endif