1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <err.h>
18#include <inttypes.h>
19#include <stdatomic.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <sys/mman.h>
24#include <unistd.h>
25
26#include "err.h"
27#include "Pointers.h"
28
29Pointers::Pointers(size_t max_allocs) {
30  size_t pagesize = getpagesize();
31  // Create a mmap that contains a 4:1 ratio of allocations to entries.
32  // Align to a page.
33  pointers_size_ = (max_allocs * 4 * sizeof(pointer_data) + pagesize - 1) & ~(pagesize - 1);
34  max_pointers_ = pointers_size_ / sizeof(pointer_data);
35  void* memory = mmap(nullptr, pointers_size_, PROT_READ | PROT_WRITE,
36                      MAP_ANON | MAP_PRIVATE, -1, 0);
37  if (memory == MAP_FAILED) {
38    err(1, "Unable to allocate data for pointer hash: %zu total_allocs\n", max_allocs);
39  }
40  // Make sure that all of the PSS for this is counted right away.
41  memset(memory, 0, pointers_size_);
42  pointers_ = reinterpret_cast<pointer_data*>(memory);
43}
44
45Pointers::~Pointers() {
46  if (pointers_ != nullptr) {
47    munmap(pointers_, pointers_size_);
48    pointers_ = nullptr;
49  }
50}
51
52void Pointers::Add(uintptr_t key_pointer, void* pointer) {
53  pointer_data* data = FindEmpty(key_pointer);
54  if (data == nullptr) {
55    err(1, "No empty entry found for 0x%" PRIxPTR "\n", key_pointer);
56  }
57  atomic_store(&data->key_pointer, key_pointer);
58  data->pointer = pointer;
59}
60
61void* Pointers::Remove(uintptr_t key_pointer) {
62  if (key_pointer == 0) {
63    err(1, "Illegal zero value passed to Remove\n");
64  }
65
66  pointer_data* data = Find(key_pointer);
67  if (data == nullptr) {
68    err(1, "No pointer value found for 0x%" PRIxPTR "\n", key_pointer);
69  }
70
71  void* pointer = data->pointer;
72  atomic_store(&data->key_pointer, uintptr_t(0));
73
74  return pointer;
75}
76
77pointer_data* Pointers::Find(uintptr_t key_pointer) {
78  size_t index = GetHash(key_pointer);
79  for (size_t entries = max_pointers_; entries != 0; entries--) {
80    if (atomic_load(&pointers_[index].key_pointer) == key_pointer) {
81      return pointers_ + index;
82    }
83    if (++index == max_pointers_) {
84      index = 0;
85    }
86  }
87  return nullptr;
88}
89
90pointer_data* Pointers::FindEmpty(uintptr_t key_pointer) {
91  size_t index = GetHash(key_pointer);
92  for (size_t entries = 0; entries < max_pointers_; entries++) {
93    uintptr_t empty = 0;
94    if (atomic_compare_exchange_strong(&pointers_[index].key_pointer, &empty,
95        uintptr_t(1))) {
96      return pointers_ + index;
97    }
98    if (++index == max_pointers_) {
99      index = 0;
100    }
101  }
102  return nullptr;
103}
104
105size_t Pointers::GetHash(uintptr_t key_pointer) {
106  return key_pointer % max_pointers_;
107}
108
109void Pointers::FreeAll() {
110  for (size_t i = 0; i < max_pointers_; i++) {
111    if (atomic_load(&pointers_[i].key_pointer) != 0) {
112      free(pointers_[i].pointer);
113    }
114  }
115}
116