Use unordered_map in memdbg for performance
[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_OOM_EXTRA)) return
41
42 #ifdef HAVE_W32_SYSTEM
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 #ifdef HAVE_W32_SYSTEM
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_debug ("%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_debug ("%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 #endif
98   return false;
99 }
100
101 void
102 _memdbg_addRef (void *obj, const char *nameSuggestion)
103 {
104   DBGGUARD;
105
106   if (!obj)
107     {
108       return;
109     }
110
111   gpgrt_lock_lock (&memdbg_log);
112
113   auto it = olObjs.find (obj);
114
115   if (it == olObjs.end())
116     {
117       it = olObjs.insert (std::make_pair (obj, 0)).first;
118     }
119   if (register_name (obj, nameSuggestion) && it->second)
120     {
121       log_error ("%s:%s Name change without null ref on %p!",
122                  SRCNAME, __func__, obj);
123     }
124   it->second++;
125
126   gpgrt_lock_unlock (&memdbg_log);
127 }
128
129 void
130 memdbg_released (void *obj)
131 {
132   DBGGUARD;
133
134   if (!obj)
135     {
136       return;
137     }
138
139   gpgrt_lock_lock (&memdbg_log);
140
141   auto it = olObjs.find (obj);
142
143   if (it == olObjs.end())
144     {
145       log_error ("%s:%s Released %p without query if!!",
146                  SRCNAME, __func__, obj);
147       gpgrt_lock_unlock (&memdbg_log);
148       return;
149     }
150
151   it->second--;
152
153   if (it->second < 0)
154     {
155       log_error ("%s:%s Released %p below zero",
156                  SRCNAME, __func__, obj);
157     }
158   gpgrt_lock_unlock (&memdbg_log);
159 }
160
161 void
162 memdbg_ctor (const char *objName)
163 {
164   DBGGUARD;
165
166   if (!objName)
167     {
168       TRACEPOINT;
169       return;
170     }
171
172   gpgrt_lock_lock (&memdbg_log);
173
174   const std::string nameStr (objName);
175
176   auto it = cppObjs.find (nameStr);
177
178   if (it == cppObjs.end())
179     {
180       it = cppObjs.insert (std::make_pair (nameStr, 0)).first;
181     }
182   it->second++;
183
184   gpgrt_lock_unlock (&memdbg_log);
185 }
186
187 void
188 memdbg_dtor (const char *objName)
189 {
190   DBGGUARD;
191
192   if (!objName)
193     {
194       TRACEPOINT;
195       return;
196     }
197
198   const std::string nameStr (objName);
199   auto it = cppObjs.find (nameStr);
200
201   if (it == cppObjs.end())
202     {
203       log_error ("%s:%s Dtor of %s before ctor",
204                  SRCNAME, __func__, nameStr.c_str());
205       gpgrt_lock_unlock (&memdbg_log);
206       return;
207     }
208
209   it->second--;
210
211   if (it->second < 0)
212     {
213       log_error ("%s:%s Dtor of %s more often then ctor",
214                  SRCNAME, __func__, nameStr.c_str());
215     }
216   gpgrt_lock_unlock (&memdbg_log);
217 }
218
219
220 void
221 _memdbg_alloc (void *ptr, const char *srcname, const char *func, int line)
222 {
223   DBGGUARD;
224
225   if (!ptr)
226     {
227       TRACEPOINT;
228       return;
229     }
230
231   gpgrt_lock_lock (&memdbg_log);
232
233   const std::string identifier = std::string (srcname) + std::string (":") +
234                                   std::string (func) + std::string (":") +
235                                   std::to_string (line);
236
237   auto it = allocs.find (ptr);
238
239   if (it != allocs.end())
240     {
241       TRACEPOINT;
242       gpgrt_lock_unlock (&memdbg_log);
243       return;
244     }
245   allocs.insert (std::make_pair (ptr, identifier));
246
247   gpgrt_lock_unlock (&memdbg_log);
248 }
249
250
251 int
252 memdbg_free (void *ptr)
253 {
254   DBGGUARD false;
255
256   if (!ptr)
257     {
258       TRACEPOINT;
259       return false;
260     }
261
262   gpgrt_lock_lock (&memdbg_log);
263
264   auto it = allocs.find (ptr);
265
266   if (it == allocs.end())
267     {
268       log_error ("%s:%s Free unregistered: %p",
269                  SRCNAME, __func__, ptr);
270       gpgrt_lock_unlock (&memdbg_log);
271       return false;
272     }
273
274   allocs.erase (it);
275
276   gpgrt_lock_unlock (&memdbg_log);
277   return true;
278 }
279
280 void
281 memdbg_dump ()
282 {
283   DBGGUARD;
284   gpgrt_lock_lock (&memdbg_log);
285   log_debug(""
286 "------------------------------MEMORY DUMP----------------------------------");
287
288   log_debug("-- C++ Objects --");
289   for (const auto &pair: cppObjs)
290     {
291       log_debug("%s\t: %i", pair.first.c_str(), pair.second);
292     }
293   log_debug("-- C++ End --");
294   log_debug("-- OL Objects --");
295   for (const auto &pair: olObjs)
296     {
297       if (!pair.second)
298         {
299           continue;
300         }
301       const auto it = olNames.find (pair.first);
302       if (it == olNames.end())
303         {
304           log_debug("%p\t: %i", pair.first, pair.second);
305         }
306       else
307         {
308           log_debug("%p:%s\t: %i", pair.first,
309                     it->second.c_str (), pair.second);
310         }
311     }
312   log_debug("-- OL End --");
313   log_debug("-- Allocated Addresses --");
314   for (const auto &pair: allocs)
315     {
316       log_debug ("%s: %p", pair.second.c_str(), pair.first);
317     }
318   log_debug("-- Allocated Addresses End --");
319
320   log_debug(""
321 "------------------------------MEMORY END ----------------------------------");
322   gpgrt_lock_unlock (&memdbg_log);
323 }