This source file includes following definitions.
- IntLog2
 
- Random
 
- LLA_SkiplistLevels
 
- LLA_SkiplistSearch
 
- LLA_SkiplistInsert
 
- LLA_SkiplistDelete
 
- arena_
 
- ArenaLock
 
- Leave
 
- Magic
 
- ArenaInit
 
- NewArena
 
- DeleteArena
 
- RoundUp
 
- Next
 
- Coalesce
 
- AddToFreelist
 
- Free
 
- DoAllocWithArena
 
- AllocWithArena
 
- DefaultArena
 
#include "base/low_level_alloc.h"
#include "base/dynamic_annotations.h"
#include "base/spinlock.h"
#include "base/logging.h"
#include "malloc_hook-inl.h"
#include <gperftools/malloc_hook.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif
#include <new>                   
#ifndef MAP_ANONYMOUS
# define MAP_ANONYMOUS MAP_ANON
#endif
static const int kMaxLevel = 30;
namespace low_level_alloc_internal {
  
  struct AllocList {
    struct Header {
      intptr_t size;  
                      
      intptr_t magic; 
      LowLevelAlloc::Arena *arena; 
      void *dummy_for_alignment;   
    } header;
    
    
    int levels;           
    AllocList *next[kMaxLevel];   
                                  
                                  
                                  
  };
}
using low_level_alloc_internal::AllocList;
static int IntLog2(size_t size, size_t base) {
  int result = 0;
  for (size_t i = size; i > base; i >>= 1) { 
    result++;
  }
  
  
  
  return result;
}
static int Random() {
  static int32 r = 1;         
  ANNOTATE_BENIGN_RACE(&r, "benign race, not critical.");
  int result = 1;
  while ((((r = r*1103515245 + 12345) >> 30) & 1) == 0) {
    result++;
  }
  return result;
}
static int LLA_SkiplistLevels(size_t size, size_t base, bool random) {
  
  
  
  int max_fit = (size-OFFSETOF_MEMBER(AllocList, next)) / sizeof (AllocList *);
  int level = IntLog2(size, base) + (random? Random() : 1);
  if (level > max_fit)     level = max_fit;
  if (level > kMaxLevel-1) level = kMaxLevel - 1;
  RAW_CHECK(level >= 1, "block not big enough for even one level");
  return level;
}
static AllocList *LLA_SkiplistSearch(AllocList *head,
                                     AllocList *e, AllocList **prev) {
  AllocList *p = head;
  for (int level = head->levels - 1; level >= 0; level--) {
    for (AllocList *n; (n = p->next[level]) != 0 && n < e; p = n) {
    }
    prev[level] = p;
  }
  return (head->levels == 0) ?  0 : prev[0]->next[0];
}
static void LLA_SkiplistInsert(AllocList *head, AllocList *e,
                               AllocList **prev) {
  LLA_SkiplistSearch(head, e, prev);
  for (; head->levels < e->levels; head->levels++) { 
    prev[head->levels] = head;                       
  }
  for (int i = 0; i != e->levels; i++) { 
    e->next[i] = prev[i]->next[i];
    prev[i]->next[i] = e;
  }
}
static void LLA_SkiplistDelete(AllocList *head, AllocList *e,
                               AllocList **prev) {
  AllocList *found = LLA_SkiplistSearch(head, e, prev);
  RAW_CHECK(e == found, "element not in freelist");
  for (int i = 0; i != e->levels && prev[i]->next[i] == e; i++) {
    prev[i]->next[i] = e->next[i];
  }
  while (head->levels > 0 && head->next[head->levels - 1] == 0) {
    head->levels--;   
  }
}
struct LowLevelAlloc::Arena {
  Arena() : mu(SpinLock::LINKER_INITIALIZED) {} 
  explicit Arena(int) : pagesize(0) {}  
                                        
  SpinLock mu;            
                          
  AllocList freelist;     
  int32 allocation_count; 
  int32 flags;            
  size_t pagesize;        
  size_t roundup;         
                          
  size_t min_size;        
                          
};
static struct LowLevelAlloc::Arena default_arena;
static struct LowLevelAlloc::Arena unhooked_arena;
static struct LowLevelAlloc::Arena unhooked_async_sig_safe_arena;
static const intptr_t kMagicAllocated = 0x4c833e95;
static const intptr_t kMagicUnallocated = ~kMagicAllocated;
namespace {
  class SCOPED_LOCKABLE ArenaLock {
   public:
    explicit ArenaLock(LowLevelAlloc::Arena *arena)
        EXCLUSIVE_LOCK_FUNCTION(arena->mu)
        : left_(false), mask_valid_(false), arena_(arena) {
      if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
      
      
      
#if 0
        sigset_t all;
        sigfillset(&all);
        this->mask_valid_ =
            (pthread_sigmask(SIG_BLOCK, &all, &this->mask_) == 0);
#else
        RAW_CHECK(false, "We do not yet support async-signal-safe arena.");
#endif
      }
      this->arena_->mu.Lock();
    }
    ~ArenaLock() { RAW_CHECK(this->left_, "haven't left Arena region"); }
    void Leave()  {
      this->arena_->mu.Unlock();
#if 0
      if (this->mask_valid_) {
        pthread_sigmask(SIG_SETMASK, &this->mask_, 0);
      }
#endif
      this->left_ = true;
    }
   private:
    bool left_;       
    bool mask_valid_;
#if 0
    sigset_t mask_;   
#endif
    LowLevelAlloc::Arena *arena_;
    DISALLOW_COPY_AND_ASSIGN(ArenaLock);
  };
} 
inline static intptr_t Magic(intptr_t magic, AllocList::Header *ptr) {
  return magic ^ reinterpret_cast<intptr_t>(ptr);
}
static void ArenaInit(LowLevelAlloc::Arena *arena) {
  if (arena->pagesize == 0) {
    arena->pagesize = getpagesize();
    
    arena->roundup = 16;
    while (arena->roundup < sizeof (arena->freelist.header)) {
      arena->roundup += arena->roundup;
    }
    
    
    arena->min_size = 2 * arena->roundup;
    arena->freelist.header.size = 0;
    arena->freelist.header.magic =
        Magic(kMagicUnallocated, &arena->freelist.header);
    arena->freelist.header.arena = arena;
    arena->freelist.levels = 0;
    memset(arena->freelist.next, 0, sizeof (arena->freelist.next));
    arena->allocation_count = 0;
    if (arena == &default_arena) {
      
      
      arena->flags = LowLevelAlloc::kCallMallocHook;
    } else if (arena == &unhooked_async_sig_safe_arena) {
      arena->flags = LowLevelAlloc::kAsyncSignalSafe;
    } else {
      arena->flags = 0;   
                          
    }
  }
}
LowLevelAlloc::Arena *LowLevelAlloc::NewArena(int32 flags,
                                              Arena *meta_data_arena) {
  RAW_CHECK(meta_data_arena != 0, "must pass a valid arena");
  if (meta_data_arena == &default_arena) {
    if ((flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
      meta_data_arena = &unhooked_async_sig_safe_arena;
    } else if ((flags & LowLevelAlloc::kCallMallocHook) == 0) {
      meta_data_arena = &unhooked_arena;
    }
  }
  
  Arena *result =
    new (AllocWithArena(sizeof (*result), meta_data_arena)) Arena(0);
  ArenaInit(result);
  result->flags = flags;
  return result;
}
bool LowLevelAlloc::DeleteArena(Arena *arena) {
  RAW_CHECK(arena != 0 && arena != &default_arena && arena != &unhooked_arena,
            "may not delete default arena");
  ArenaLock section(arena);
  bool empty = (arena->allocation_count == 0);
  section.Leave();
  if (empty) {
    while (arena->freelist.next[0] != 0) {
      AllocList *region = arena->freelist.next[0];
      size_t size = region->header.size;
      arena->freelist.next[0] = region->next[0];
      RAW_CHECK(region->header.magic ==
                Magic(kMagicUnallocated, ®ion->header),
                "bad magic number in DeleteArena()");
      RAW_CHECK(region->header.arena == arena,
                "bad arena pointer in DeleteArena()");
      RAW_CHECK(size % arena->pagesize == 0,
                "empty arena has non-page-aligned block size");
      RAW_CHECK(reinterpret_cast<intptr_t>(region) % arena->pagesize == 0,
                "empty arena has non-page-aligned block");
      int munmap_result;
      if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) == 0) {
        munmap_result = munmap(region, size);
      } else {
        munmap_result = MallocHook::UnhookedMUnmap(region, size);
      }
      RAW_CHECK(munmap_result == 0,
                "LowLevelAlloc::DeleteArena:  munmap failed address");
    }
    Free(arena);
  }
  return empty;
}
static intptr_t RoundUp(intptr_t addr, intptr_t align) {
  return (addr + align - 1) & ~(align - 1);
}
static AllocList *Next(int i, AllocList *prev, LowLevelAlloc::Arena *arena) {
  RAW_CHECK(i < prev->levels, "too few levels in Next()");
  AllocList *next = prev->next[i];
  if (next != 0) {
    RAW_CHECK(next->header.magic == Magic(kMagicUnallocated, &next->header),
              "bad magic number in Next()");
    RAW_CHECK(next->header.arena == arena,
              "bad arena pointer in Next()");
    if (prev != &arena->freelist) {
      RAW_CHECK(prev < next, "unordered freelist");
      RAW_CHECK(reinterpret_cast<char *>(prev) + prev->header.size <
                reinterpret_cast<char *>(next), "malformed freelist");
    }
  }
  return next;
}
static void Coalesce(AllocList *a) {
  AllocList *n = a->next[0];
  if (n != 0 && reinterpret_cast<char *>(a) + a->header.size ==
                    reinterpret_cast<char *>(n)) {
    LowLevelAlloc::Arena *arena = a->header.arena;
    a->header.size += n->header.size;
    n->header.magic = 0;
    n->header.arena = 0;
    AllocList *prev[kMaxLevel];
    LLA_SkiplistDelete(&arena->freelist, n, prev);
    LLA_SkiplistDelete(&arena->freelist, a, prev);
    a->levels = LLA_SkiplistLevels(a->header.size, arena->min_size, true);
    LLA_SkiplistInsert(&arena->freelist, a, prev);
  }
}
static void AddToFreelist(void *v, LowLevelAlloc::Arena *arena) {
  AllocList *f = reinterpret_cast<AllocList *>(
                        reinterpret_cast<char *>(v) - sizeof (f->header));
  RAW_CHECK(f->header.magic == Magic(kMagicAllocated, &f->header),
            "bad magic number in AddToFreelist()");
  RAW_CHECK(f->header.arena == arena,
            "bad arena pointer in AddToFreelist()");
  f->levels = LLA_SkiplistLevels(f->header.size, arena->min_size, true);
  AllocList *prev[kMaxLevel];
  LLA_SkiplistInsert(&arena->freelist, f, prev);
  f->header.magic = Magic(kMagicUnallocated, &f->header);
  Coalesce(f);                  
  Coalesce(prev[0]);            
}
void LowLevelAlloc::Free(void *v) {
  if (v != 0) {
    AllocList *f = reinterpret_cast<AllocList *>(
                        reinterpret_cast<char *>(v) - sizeof (f->header));
    RAW_CHECK(f->header.magic == Magic(kMagicAllocated, &f->header),
              "bad magic number in Free()");
    LowLevelAlloc::Arena *arena = f->header.arena;
    if ((arena->flags & kCallMallocHook) != 0) {
      MallocHook::InvokeDeleteHook(v);
    }
    ArenaLock section(arena);
    AddToFreelist(v, arena);
    RAW_CHECK(arena->allocation_count > 0, "nothing in arena to free");
    arena->allocation_count--;
    section.Leave();
  }
}
static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) {
  void *result = 0;
  if (request != 0) {
    AllocList *s;       
    ArenaLock section(arena);
    ArenaInit(arena);
    
    size_t req_rnd = RoundUp(request + sizeof (s->header), arena->roundup);
    for (;;) {      
      
      int i = LLA_SkiplistLevels(req_rnd, arena->min_size, false) - 1;
      if (i < arena->freelist.levels) {   
        AllocList *before = &arena->freelist;  
        while ((s = Next(i, before, arena)) != 0 && s->header.size < req_rnd) {
          before = s;
        }
        if (s != 0) {       
          break;
        }
      }
      
      
      arena->mu.Unlock();
      
      
      size_t new_pages_size = RoundUp(req_rnd, arena->pagesize * 16);
      void *new_pages;
      if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
        new_pages = MallocHook::UnhookedMMap(0, new_pages_size,
            PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
      } else {
        new_pages = mmap(0, new_pages_size,
            PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
      }
      RAW_CHECK(new_pages != MAP_FAILED, "mmap error");
      arena->mu.Lock();
      s = reinterpret_cast<AllocList *>(new_pages);
      s->header.size = new_pages_size;
      
      s->header.magic = Magic(kMagicAllocated, &s->header);
      s->header.arena = arena;
      AddToFreelist(&s->levels, arena);  
    }
    AllocList *prev[kMaxLevel];
    LLA_SkiplistDelete(&arena->freelist, s, prev);    
    
    if (req_rnd + arena->min_size <= s->header.size) {  
      AllocList *n = reinterpret_cast<AllocList *>
                        (req_rnd + reinterpret_cast<char *>(s));
      n->header.size = s->header.size - req_rnd;
      n->header.magic = Magic(kMagicAllocated, &n->header);
      n->header.arena = arena;
      s->header.size = req_rnd;
      AddToFreelist(&n->levels, arena);
    }
    s->header.magic = Magic(kMagicAllocated, &s->header);
    RAW_CHECK(s->header.arena == arena, "");
    arena->allocation_count++;
    section.Leave();
    result = &s->levels;
  }
  ANNOTATE_NEW_MEMORY(result, request);
  return result;
}
void *LowLevelAlloc::Alloc(size_t request) {
  void *result = DoAllocWithArena(request, &default_arena);
  if ((default_arena.flags & kCallMallocHook) != 0) {
    
    
    MallocHook::InvokeNewHook(result, request);
  }
  return result;
}
void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) {
  RAW_CHECK(arena != 0, "must pass a valid arena");
  void *result = DoAllocWithArena(request, arena);
  if ((arena->flags & kCallMallocHook) != 0) {
    
    
    MallocHook::InvokeNewHook(result, request);
  }
  return result;
}
LowLevelAlloc::Arena *LowLevelAlloc::DefaultArena() {
  return &default_arena;
}