MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
memory.cpp
1 /*
2  Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
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; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; see the file COPYING. If not, write to the
15  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
16  MA 02110-1301 USA.
17 */
18 
19 // memory.cpp
20 #include "../../include/lock.hpp" // locking
21 #include <new> // std::bad_alloc
22 #include <cstdlib> // malloc
23 #include <cstring> // memset
24 #include <fstream> // ofstream
25 #include <sstream> // stringstream
26 #include <cassert> // assert
27 #include <iomanip> // setiosflags
28 
29 /*********************************************************************
30 
31 To use MemoryTracker merely add this file to your project
32 No need to instantiate anything
33 
34 If your app is multi threaded define MULTI_THREADED
35 
36 *********************************************************************/
37 
38 
39 // locals
40 namespace {
41 
42 class MemoryTracker {
43  std::ofstream log_;
44 public:
45  MemoryTracker();
46  ~MemoryTracker();
47 private:
48  MemoryTracker(const MemoryTracker&); // hide copy
49  MemoryTracker& operator=(const MemoryTracker&); // and assign
50 
51  void LogStats();
52 };
53 
54 
55 struct alloc_node {
56  alloc_node* left_;
57  alloc_node* right_;
58 
59  alloc_node() : left_(0), right_(0) {}
60 };
61 
62 
63 alloc_node* Root = 0;
64 
65 size_t Allocs = 0;
66 size_t DeAllocs = 0;
67 size_t Bytes = 0;
68 
69 
70 struct size_tracker {
71  size_t size_;
72  size_t count_;
73 };
74 
75 size_tracker sizes[] =
76 {
77  {0,0},
78  {2,0},
79  {4,0},
80  {8,0},
81  {16,0},
82  {32,0},
83  {64,0},
84  {128,0},
85  {256,0},
86  {512,0},
87  {1024,0},
88  {2048,0},
89  {4096,0},
90  {8192,0},
91 };
92 
93 const size_t size_elements(sizeof(sizes) / sizeof(size_tracker));
94 
95 bool Tracking(false);
96 
97 using yaSSL::Mutex;
98 typedef Mutex::Lock Lock;
99 
100 Mutex mutex;
101 
102 MemoryTracker theTracker;
103 
104 
105 bool lookup(alloc_node*& find, void* key, alloc_node*& prev)
106 {
107  bool found(false);
108 
109  while (find) {
110  if (find == key) {
111  found = true;
112  break;
113  }
114  prev = find;
115  if (key < find)
116  find = find->left_;
117  else
118  find = find->right_;
119  }
120  return found;
121 }
122 
123 
124 // iterative insert
125 void insert(alloc_node* entry)
126 {
127  if (!Root) {
128  Root = entry;
129  return;
130  }
131 
132  alloc_node* tmp = Root;
133  alloc_node* prev = 0;
134 
135  if (lookup(tmp, entry, prev))
136  assert(0); // duplicate
137 
138  if (entry < prev)
139  prev->left_ = entry;
140  else
141  prev->right_ = entry;
142 }
143 
144 
145 alloc_node* predecessorSwap(alloc_node* del)
146 {
147  alloc_node* pred = del->left_;
148  alloc_node* predPrev = del;
149 
150  while (pred->right_) {
151  predPrev = pred;
152  pred = pred->right_;
153  }
154  if (predPrev == del)
155  predPrev->left_ = pred->left_;
156  else
157  predPrev->right_ = pred->left_;
158 
159  pred->left_ = del->left_;
160  pred->right_ = del->right_;
161 
162  return pred;
163 }
164 
165 
166 // iterative remove
167 void remove(void* ptr)
168 {
169  alloc_node* del = Root;
170  alloc_node* prev = 0;
171  alloc_node* replace = 0;
172 
173  if ( lookup(del, ptr, prev) == false)
174  assert(0); // oops, not there
175 
176  if (del->left_ && del->right_) // two children
177  replace = predecessorSwap(del);
178  else if (!del->left_ && !del->right_) // no children
179  replace = 0;
180  else // one child
181  replace = (del->left_) ? del->left_ : del->right_;
182 
183  if (del == Root)
184  Root = replace;
185  else if (prev->left_ == del)
186  prev->left_ = replace;
187  else
188  prev->right_ = replace;
189 }
190 
191 
192 typedef void (*fp)(alloc_node*, void*);
193 
194 void applyInOrder(alloc_node* root, fp f, void* arg)
195 {
196  if (root == 0)
197  return;
198 
199  applyInOrder(root->left_, f, arg);
200  f(root, arg);
201  applyInOrder(root->right_, f, arg);
202 }
203 
204 
205 void show(alloc_node* ptr, void* arg)
206 {
207  std::ofstream* log = static_cast<std::ofstream*>(arg);
208  *log << ptr << '\n';
209 }
210 
211 
212 MemoryTracker::MemoryTracker() : log_("memory.log")
213 {
214 #ifdef __GNUC__
215  // Force pool allocator to cleanup at exit
216  setenv("GLIBCPP_FORCE_NEW", "1", 0);
217 #endif
218 
219 #ifdef _MSC_VER
220  // msvc6 needs to create Facility for ostream before main starts, otherwise
221  // if another ostream is created and destroyed in main scope, log stats
222  // will access a dead Facility reference (std::numput)
223  int msvcFac = 6;
224  log_ << "MSVC " << msvcFac << "workaround" << std::endl;
225 #endif
226 
227 
228  Tracking = true;
229 }
230 
231 
232 MemoryTracker::~MemoryTracker()
233 {
234  // stop tracking before log (which will alloc on output)
235  Tracking = false;
236  LogStats();
237 
238  //assert(Allocs == DeAllocs);
239  //assert(Root == 0);
240 }
241 
242 
243 void MemoryTracker::LogStats()
244 {
245  log_ << "Number of Allocs: " << Allocs << '\n';
246  log_ << "Number of DeAllocs: " << DeAllocs << '\n';
247  log_ << "Number of bytes used: " << Bytes << '\n';
248 
249  log_ << "Alloc size table:\n";
250  log_ << " Bytes " << '\t' << " Times\n";
251 
252  for (size_t i = 0; i < size_elements; ++i) {
253  log_ << " " << sizes[i].size_ << " " << '\t';
254  log_ << std::setiosflags(std::ios::right) << std::setw(8);
255  log_ << sizes[i].count_ << '\n';
256  }
257 
258  if (Allocs != DeAllocs) {
259  log_<< "Showing new'd allocs with no deletes" << '\n';
260  applyInOrder(Root, show, &log_);
261  }
262  log_.flush();
263 }
264 
265 
266 // return power of 2 up to size_tracker elements
267 size_t powerOf2(size_t sz)
268 {
269  size_t shifts = 0;
270 
271  if (sz)
272  sz -= 1;
273  else
274  return 0;
275 
276  while (sz) {
277  sz >>= 1;
278  ++shifts;
279  }
280 
281  return shifts < size_elements ? shifts : size_elements;
282 }
283 
284 
285 } // namespace local
286 
287 
288 void* operator new(size_t sz)
289 {
290  // put alloc node in front of requested memory
291  void* ptr = malloc(sz + sizeof(alloc_node));
292  if (ptr) {
293  if (Tracking) {
294  Lock l(mutex);
295  ++Allocs;
296  Bytes += sz;
297  ++sizes[powerOf2(sz)].count_;
298  insert(new (ptr) alloc_node);
299  }
300  return static_cast<char*>(ptr) + sizeof(alloc_node);
301  }
302  else
303  assert(0);
304 }
305 
306 
307 void operator delete(void* ptr)
308 {
309  if (ptr) {
310  ptr = static_cast<char*>(ptr) - sizeof(alloc_node); // correct offset
311  if (Tracking) {
312  Lock l(mutex);
313  ++DeAllocs;
314  remove(ptr);
315  }
316  free(ptr);
317  }
318 }
319 
320 
321 void* operator new[](size_t sz)
322 {
323  return ::operator new(sz);
324 }
325 
326 
327 void operator delete[](void* ptr)
328 {
329  ::operator delete(ptr);
330 }
331 
332 
333 extern "C" {
334 
335 void* XMALLOC(size_t sz, void* head)
336 {
337  return ::operator new(sz);
338 }
339 
340 void* XREALLOC(void* ptr, size_t sz, void* heap)
341 {
342  void* ret = ::operator new(sz);
343 
344  if (ret && ptr)
345  memcpy(ret, ptr, sz);
346 
347  if (ret)
348  ::operator delete(ptr);
349  return ret;
350 }
351 
352 
353 void XFREE(void* ptr, void* heap)
354 {
355  ::operator delete(ptr);
356 }
357 
358 } // extern "C"
359