Enable building tests for windows
[gpgol.git] / src / memdbg.cpp
1 /* @file memdbg.cpp
2  * @brief Memory debugging helpers
3  *
4  * Copyright (C) 2018 Intevation GmbH
5  *
6  * This file is part of GpgOL.
7  *
8  * GpgOL is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * GpgOL is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "config.h"
23
24 #include "memdbg.h"
25
26 #include "common_indep.h"
27
28 #include <gpg-error.h>
29
30 #include <unordered_map>
31 #include <string>
32
33 std::unordered_map <std::string, int> cppObjs;
34 std::unordered_map <void *, int> olObjs;
35 std::unordered_map <void *, std::string> olNames;
36 std::unordered_map <void *, std::string> allocs;
37
38 GPGRT_LOCK_DEFINE (memdbg_log);
39
40 #define DBGGUARD if (!(opt.enable_debug & DBG_MEMORY)) return
41
42 #ifndef BUILD_TESTS
43 # include "oomhelp.h"
44 #endif
45
46 /* Returns true on a name change */
47 static bool
48 register_name (void *obj, const char *nameSuggestion)
49 {
50 #ifndef BUILD_TESTS
51
52   char *name = get_object_name ((LPUNKNOWN)obj);
53   bool suggestionUsed = false;
54
55   if (!name && nameSuggestion)
56     {
57       name = xstrdup (nameSuggestion);
58       suggestionUsed = true;
59     }
60   if (!name)
61     {
62       auto it = olNames.find (obj);
63       if (it != olNames.end())
64         {
65           if (it->second != "unknown")
66             {
67               log_memory ("%s:%s Ptr %p name change from %s to unknown",
68                           SRCNAME, __func__, obj, it->second.c_str());
69               it->second = "unknown";
70               return true;
71             }
72         }
73       return false;
74     }
75
76   std::string sName = name;
77   xfree (name);
78
79   auto it = olNames.find (obj);
80   if (it != olNames.end())
81     {
82       if (it->second != sName)
83         {
84           log_memory ("%s:%s Ptr %p name change from %s to %s",
85                      SRCNAME, __func__, obj, it->second.c_str(),
86                      sName.c_str());
87           it->second = sName;
88           return !suggestionUsed;
89         }
90     }
91   else
92     {
93       olNames.insert (std::make_pair (obj, sName));
94     }
95 #else
96   (void) obj;
97   (void) nameSuggestion;
98 #endif
99   return false;
100 }
101
102 void
103 _memdbg_addRef (void *obj, const char *nameSuggestion)
104 {
105   DBGGUARD;
106
107   if (!obj)
108     {
109       return;
110     }
111
112   gpgrt_lock_lock (&memdbg_log);
113
114   auto it = olObjs.find (obj);
115
116   if (it == olObjs.end())
117     {
118       it = olObjs.insert (std::make_pair (obj, 0)).first;
119     }
120   if (register_name (obj, nameSuggestion) && it->second)
121     {
122       log_error ("%s:%s Name change without null ref on %p!",
123                  SRCNAME, __func__, obj);
124     }
125   it->second++;
126
127   gpgrt_lock_unlock (&memdbg_log);
128 }
129
130 void
131 memdbg_released (void *obj)
132 {
133   DBGGUARD;
134
135   if (!obj)
136     {
137       return;
138     }
139
140   gpgrt_lock_lock (&memdbg_log);
141
142   auto it = olObjs.find (obj);
143
144   if (it == olObjs.end())
145     {
146       log_error ("%s:%s Released %p without query if!!",
147                  SRCNAME, __func__, obj);
148       gpgrt_lock_unlock (&memdbg_log);
149       return;
150     }
151
152   it->second--;
153
154   if (it->second < 0)
155     {
156       log_error ("%s:%s Released %p below zero",
157                  SRCNAME, __func__, obj);
158     }
159   gpgrt_lock_unlock (&memdbg_log);
160 }
161
162 void
163 memdbg_ctor (const char *objName)
164 {
165   DBGGUARD;
166
167   if (!objName)
168     {
169       TRACEPOINT;
170       return;
171     }
172
173   gpgrt_lock_lock (&memdbg_log);
174
175   const std::string nameStr (objName);
176
177   auto it = cppObjs.find (nameStr);
178
179   if (it == cppObjs.end())
180     {
181       it = cppObjs.insert (std::make_pair (nameStr, 0)).first;
182     }
183   it->second++;
184
185   gpgrt_lock_unlock (&memdbg_log);
186 }
187
188 void
189 memdbg_dtor (const char *objName)
190 {
191   DBGGUARD;
192
193   if (!objName)
194     {
195       TRACEPOINT;
196       return;
197     }
198
199   const std::string nameStr (objName);
200   auto it = cppObjs.find (nameStr);
201
202   if (it == cppObjs.end())
203     {
204       log_error ("%s:%s Dtor of %s before ctor",
205                  SRCNAME, __func__, nameStr.c_str());
206       gpgrt_lock_unlock (&memdbg_log);
207       return;
208     }
209
210   it->second--;
211
212   if (it->second < 0)
213     {
214       log_error ("%s:%s Dtor of %s more often then ctor",
215                  SRCNAME, __func__, nameStr.c_str());
216     }
217   gpgrt_lock_unlock (&memdbg_log);
218 }
219
220
221 void
222 _memdbg_alloc (void *ptr, const char *srcname, const char *func, int line)
223 {
224   DBGGUARD;
225
226   if (!ptr)
227     {
228       TRACEPOINT;
229       return;
230     }
231
232   gpgrt_lock_lock (&memdbg_log);
233
234   const std::string identifier = std::string (srcname) + std::string (":") +
235                                   std::string (func) + std::string (":") +
236                                   std::to_string (line);
237
238   auto it = allocs.find (ptr);
239
240   if (it != allocs.end())
241     {
242       TRACEPOINT;
243       gpgrt_lock_unlock (&memdbg_log);
244       return;
245     }
246   allocs.insert (std::make_pair (ptr, identifier));
247
248   gpgrt_lock_unlock (&memdbg_log);
249 }
250
251
252 int
253 memdbg_free (void *ptr)
254 {
255   DBGGUARD false;
256
257   if (!ptr)
258     {
259       TRACEPOINT;
260       return false;
261     }
262
263   gpgrt_lock_lock (&memdbg_log);
264
265   auto it = allocs.find (ptr);
266
267   if (it == allocs.end())
268     {
269       log_error ("%s:%s Free unregistered: %p",
270                  SRCNAME, __func__, ptr);
271       gpgrt_lock_unlock (&memdbg_log);
272       return false;
273     }
274
275   allocs.erase (it);
276
277   gpgrt_lock_unlock (&memdbg_log);
278   return true;
279 }
280
281 void
282 memdbg_dump ()
283 {
284   DBGGUARD;
285   gpgrt_lock_lock (&memdbg_log);
286   log_memory (""
287 "------------------------------MEMORY DUMP----------------------------------");
288
289   log_memory("-- C++ Objects --");
290   for (const auto &pair: cppObjs)
291     {
292       log_memory("%s\t: %i", pair.first.c_str(), pair.second);
293     }
294   log_memory("-- C++ End --");
295   log_memory("-- OL Objects --");
296   for (const auto &pair: olObjs)
297     {
298       if (!pair.second)
299         {
300           continue;
301         }
302       const auto it = olNames.find (pair.first);
303       if (it == olNames.end())
304         {
305           log_memory("%p\t: %i", pair.first, pair.second);
306         }
307       else
308         {
309           log_memory("%p:%s\t: %i", pair.first,
310                     it->second.c_str (), pair.second);
311         }
312     }
313   log_memory("-- OL End --");
314   log_memory("-- Allocated Addresses --");
315   for (const auto &pair: allocs)
316     {
317       log_memory ("%s: %p", pair.second.c_str(), pair.first);
318     }
319   log_memory("-- Allocated Addresses End --");
320
321   log_memory(""
322 "------------------------------MEMORY END ----------------------------------");
323   gpgrt_lock_unlock (&memdbg_log);
324 }