mem.cpp.skip 9.7 KB
Newer Older
羽飞's avatar
羽飞 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
// __CR__
// Copyright (c) 2021 LongdaFeng All Rights Reserved
//
// This software contains the intellectual property of LongdaFeng
// or is licensed to LongdaFeng from third parties.  Use of this
// software and the intellectual property contained therein is
// expressly limited to the terms and conditions of the License Agreement
// under which it is provided by or on behalf of LongdaFeng.
// __CR__
//
// Created by Longda on 2010
//

#ifdef MEM_DEBUG

#include <cstddef>
#include <stdlib.h>
#include <string.h>

#include "lang/mutex.h"
#include "mm/lmem.h"

#define MEM_ID_HASH(p) (((unsigned long)(p) >> 8) % MEM_HASHTABLE_SIZE)

pthread_mutex_t CLMemTrace::mMutex = PTHREAD_MUTEX_INITIALIZER;
u64_t CLMemTrace::mUsedSize = 0;
MemID *CLMemTrace::mMemIDs[MEM_HASHTABLE_SIZE] = {0};
bool CLMemTrace::mVerbose = false;
;

31 32
void *CLMemTrace::malloc(size_t size, const char *file, const int line, bool retry) throw(std::bad_alloc)
{
羽飞's avatar
羽飞 已提交
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

  size_t allocSize = size + sizeof(MemID);
  void *usedPointer = NULL;

  do {
    MemID *ptr = (MemID *)::malloc(allocSize);
    if (ptr) {
      // successfully alloc memory

      // set the MemID
      strncpy(ptr->mFile, file, MemID::MEM_FILENAME_LEN - 1);
      ptr->mFile[MemID::MEM_FILENAME_LEN - 1] = '\0';
      ptr->mLine = line;
      ptr->mSize = size;

      usedPointer = (char *)ptr + sizeof(MemID);
      u64_t hashIndex = (u64_t)MEM_ID_HASH(usedPointer);

      MUTEX_LOCK(&mMutex);

      ptr->mNext = mMemIDs[hashIndex];
      mMemIDs[hashIndex] = ptr;

      mUsedSize += size;

      MUTEX_UNLOCK(&mMutex);

      if (mVerbose) {
        LOG_INFO("%s:%d alloc %llu memory %p", file, line, size, usedPointer);
      }
      return usedPointer;
    }

    if (retry == false) {
      throw std::bad_alloc();
      break;
    }

    std::new_handler allocHandler = getNewHandler();

    if (allocHandler) {
      (*allocHandler)();
    } else {
      throw std::bad_alloc();
      break;
    }
  } while (retry);

  return NULL;
}

84 85
void *CLMemTrace::realloc(void *pointer, size_t size, const char *file, const int line)
{
羽飞's avatar
羽飞 已提交
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
  if (pointer == NULL && size == 0) {
    return NULL;
  } else if (pointer == NULL && size != 0) {
    try {
      return malloc(size, file, line);
    } catch (std::bad_alloc &e) {
      LOG_WARN("NO memory to alloc for %llu", size);
      return NULL;
    }
  } else if (pointer && size == 0) {
    free(pointer);
    return NULL;
  }

  // the left case, ptr && size
  MemID *pMemID = NULL;
  MemID *pLast = NULL;

  MemID *pFreeMemID = NULL;
  MemID *pNewMemID = NULL;

  MemID oldMemID;

  bool foundOld = false;

  // use big lock
  MUTEX_LOCK(&mMutex);
  u64_t hashIndex = MEM_ID_HASH(pointer);
  pMemID = mMemIDs[hashIndex];
  while (pMemID) {
    if ((char *)pMemID + sizeof(MemID) != pointer) {
      // not found
      pLast = pMemID;
      pMemID = pMemID->mNext;
      continue;
    }

    // find
    foundOld = true;

    // backup old MemID firstly
    memcpy(&oldMemID, pMemID, sizeof(MemID));

    u64_t allocSize = size + sizeof(MemID);

    pNewMemID = (MemID *)::realloc(pMemID, allocSize);
    if (pNewMemID == NULL) {
      // case 1:no memory to alloc, free the old one
      if (pLast == NULL) {
        mMemIDs[hashIndex] = pMemID->mNext;
      } else {
        pLast->mNext = pMemID->mNext;
      }

      pFreeMemID = pMemID;
      mUsedSize -= oldMemID.mSize;
      break;
    }

    // set the new pNewMemID
    strncpy(pNewMemID->mFile, file, MemID::MEM_FILENAME_LEN - 1);
    pNewMemID->mFile[MemID::MEM_FILENAME_LEN - 1] = '\0';
    pNewMemID->mLine = line;
    pNewMemID->mSize = size;

    mUsedSize -= oldMemID.mSize;
    mUsedSize += size;

    if (pNewMemID == pMemID) {
      // case 2: just extension the old memory
      pFreeMemID = NULL;
      break;

    } else {
      // case 3: the old memory can't meet the requirement, alloc new
      /**
       * Firstly, remove the old one from table
       * don't add new before remove the old one
       */
      if (pLast == NULL) {
        mMemIDs[hashIndex] = pMemID->mNext;
      } else {
        pLast->mNext = pMemID->mNext;
      }
      pFreeMemID = pMemID;

      /**
       * Secondly, add the new one to table
       */
175
      u64_t newHashIndex = (u64_t)MEM_ID_HASH((char *)pNewMemID + sizeof(MemID));
羽飞's avatar
羽飞 已提交
176 177 178 179 180 181 182 183

      pNewMemID->mNext = mMemIDs[newHashIndex];
      mMemIDs[newHashIndex] = pNewMemID;

      /**
       * Third, do memory copy
       * to simplify the old logic, copy memory here
       */
184
      memcpy((char *)pNewMemID + sizeof(MemID), (char *)pFreeMemID + sizeof(MemID), pFreeMemID->mSize);
羽飞's avatar
羽飞 已提交
185 186 187 188 189 190 191
      break;
    }
  }

  MUTEX_UNLOCK(&mMutex);

  if (foundOld == false) {
192
    LOG_WARN("Something is wrong, the old pointer %p isn't found, so alloc new one", pointer);
羽飞's avatar
羽飞 已提交
193 194 195 196 197 198 199 200 201
    try {
      return malloc(size, file, line, false);
    } catch (std::bad_alloc &e) {
      LOG_WARN("NO memory to alloc for %llu", size);
      return NULL;
    };
  }

  if (mVerbose) {
202
    LOG_INFO("Delete %p, file:%s, line:%u, size:%llu", pointer, oldMemID.mFile, oldMemID.mLine, oldMemID.mSize);
羽飞's avatar
羽飞 已提交
203 204 205 206 207 208 209 210 211 212
  }

  if (pFreeMemID) {
    ::free(pFreeMemID);
  }

  if (pNewMemID) {

    if (mVerbose) {
      LOG_INFO("Alloc %p, file:%s, line:%u, size:%llu",
213 214 215 216
          (char *)pNewMemID + sizeof(MemID),
          pNewMemID->mFile,
          pNewMemID->mLine,
          pNewMemID->mSize);
羽飞's avatar
羽飞 已提交
217 218 219 220 221 222 223
    }
    return pNewMemID;
  }

  return NULL;
}

224 225
void CLMemTrace::free(void *pointer)
{
羽飞's avatar
羽飞 已提交
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
  if (pointer == NULL) {
    LOG_WARN("Free one empty pointer");
    return;
  }

  u64_t hashIndex = MEM_ID_HASH(pointer);

  MemID *pMemID = NULL;
  MemID *pLast = NULL;

  // use big lock
  MUTEX_LOCK(&mMutex);

  pMemID = mMemIDs[hashIndex];
  while (pMemID) {
    if ((char *)pMemID + sizeof(MemID) == pointer) {
      // find
      if (pLast == NULL) {
        mMemIDs[hashIndex] = pMemID->mNext;
      } else {
        pLast->mNext = pMemID->mNext;
      }

      mUsedSize -= pMemID->mSize;
      break;
    } else {
      pLast = pMemID;
      pMemID = pMemID->mNext;
    }
  }
  MUTEX_UNLOCK(&mMutex);

  if (pMemID) {

    if (mVerbose) {
261
      LOG_INFO("Delete %p, file:%s, line:%u, size:%llu", pointer, pMemID->mFile, pMemID->mLine, pMemID->mSize);
羽飞's avatar
羽飞 已提交
262 263 264 265 266 267 268 269 270 271 272
    }
    ::free(pMemID);
    return;
  } else {
    // not found
    LOG_ERROR("Double free for pointer :%p", pointer);
  }

  return;
}

273 274
std::new_handler CLMemTrace::getNewHandler()
{
羽飞's avatar
羽飞 已提交
275 276 277 278 279 280 281 282 283 284 285
  std::new_handler newHandler = NULL;
  MUTEX_LOCK(&mMutex);

  newHandler = std::set_new_handler(0);
  std::set_new_handler(newHandler);

  MUTEX_UNLOCK(&mMutex);

  return newHandler;
}

286 287
void CLMemTrace::output()
{
羽飞's avatar
羽飞 已提交
288 289 290 291 292 293 294 295 296 297 298 299
  for (int i = 0; i < MEM_HASHTABLE_SIZE; ++i) {
    // Don't lock outside of the loop
    // 1. avoid output too long to alloc/free memory
    // 2. if LOG_INFO alloc memory, it will leading to dead loop
    MUTEX_LOCK(&mMutex);
    MemID *ptr = mMemIDs[i];
    if (ptr == NULL) {
      MUTEX_UNLOCK(&mMutex);
      continue;
    }
    while (ptr) {
      // if LOG_INFO alloc memory, it will easy leading to dead lock
300 301
      LOG_INFO(
          "Exist %p, file:%s, line:%u, size:%llu", (char *)ptr + sizeof(MemID), ptr->mFile, ptr->mLine, ptr->mSize);
羽飞's avatar
羽飞 已提交
302 303 304 305 306 307 308
      ptr = ptr->mNext;
    }

    MUTEX_UNLOCK(&mMutex);
  }
}

309 310
void *operator new(std::size_t size, const char *file, int line)
{
羽飞's avatar
羽飞 已提交
311 312 313
  return CLMemTrace::malloc(size, file, line, true);
}

314 315
void *operator new[](std::size_t size, const char *file, int line)
{
羽飞's avatar
羽飞 已提交
316 317 318
  return operator new(size, file, line);
}

319 320
void *operator new(std::size_t size) throw(std::bad_alloc)
{
羽飞's avatar
羽飞 已提交
321 322 323
  return operator new(size, "<Unknown>", 0);
}

324 325
void *operator new[](std::size_t size) throw(std::bad_alloc)
{
羽飞's avatar
羽飞 已提交
326 327 328
  return operator new(size);
}

329 330
void *operator new(std::size_t size, const std::nothrow_t &) throw()
{
羽飞's avatar
羽飞 已提交
331 332 333 334 335 336 337 338 339 340
  void *pointer = NULL;
  try {
    pointer = operator new(size);
  } catch (std::bad_alloc &e) {
    LOG_WARN("Failed to alloc memory");
    return NULL;
  }
  return pointer;
}

341 342
void *operator new[](std::size_t size, const std::nothrow_t &) throw()
{
羽飞's avatar
羽飞 已提交
343 344 345 346 347 348 349 350 351 352
  void *pointer = NULL;
  try {
    pointer = operator[] new(size);
  } catch (std::bad_alloc &e) {
    LOG_WARN("Failed to alloc memory");
    return NULL;
  }
  return pointer;
}

353 354 355 356
void operator delete(void *pointer)
{
  CLMemTrace::free(pointer);
}
羽飞's avatar
羽飞 已提交
357

358 359 360 361
void operator delete[](void *pointer)
{
  operator delete(pointer);
}
羽飞's avatar
羽飞 已提交
362 363 364 365 366 367 368

// Some older compilers like Borland C++ Compiler 5.5.1 and Digital Mars
// Compiler 8.29 do not support placement delete operators.
// NO_PLACEMENT_DELETE needs to be defined when using such compilers.
// Also note that in that case memory leakage will occur if an exception
// is thrown in the initialization (constructor) of a dynamically
// created object.
369 370
void operator delete(void *pointer, const char *file, int line)
{
羽飞's avatar
羽飞 已提交
371 372 373
  operator delete(pointer);
}

374 375
void operator delete[](void *pointer, const char *file, int line)
{
羽飞's avatar
羽飞 已提交
376 377 378
  operator delete(pointer, file, line);
}

379 380
void operator delete(void *pointer, const std::nothrow_t &)
{
羽飞's avatar
羽飞 已提交
381 382 383
  operator delete(pointer, "<Unknown>", 0);
}

384 385
void operator delete[](void *pointer, const std::nothrow_t &)
{
羽飞's avatar
羽飞 已提交
386 387 388
  operator delete(pointer, std::nothrow);
}

389 390
void *Lcalloc(size_t nmemb, size_t size, const char *file, const int line)
{
羽飞's avatar
羽飞 已提交
391 392 393 394 395 396 397 398 399 400 401 402
  try {
    void *point = CLMemTrace::malloc(size * nmemb, file, line, false);
    if (point) {
      memset(point, 0, size * nmemb);
    }
  } catch (std::bad_alloc &e) {
    LOG_WARN("Failed to alloc memory");
    return NULL;
  }
  return pointer;
}

403 404
void *Lmalloc(size_t size, const char *file, const int line)
{
羽飞's avatar
羽飞 已提交
405 406 407 408 409 410 411 412 413
  try {
    void *point = CLMemTrace::malloc(size, file, line, false);
  } catch (std::bad_alloc &e) {
    LOG_WARN("Failed to alloc memory");
    return NULL;
  }
  return pointer;
}

414 415 416 417 418 419
void Lfree(void *ptr)
{
  CLMemTrace::free(pointer);
}
void *Lrealloc(void *ptr, size_t size, const char *file, const int line)
{
羽飞's avatar
羽飞 已提交
420 421 422 423 424
  // simplify the logic
  return CLMemTrace::realloc(ptr, size, file, line);
}

#endif /* MEM_DEBUG */