aboutsummaryrefslogtreecommitdiff
path: root/gprofng/src/HeapMap.cc
blob: 70fddd4382ddac3dec26f72bce295d49ba55127a (plain)
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
31
32
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
84
85
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
/* Copyright (C) 2021-2024 Free Software Foundation, Inc.
   Contributed by Oracle.

   This file is part of GNU Binutils.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

#include "config.h"
#include "util.h"
#include "HeapMap.h"

#define HEAPCHUNKSZ     1024 // number of HeapObj's in a chunk
#define HEAPCHAINS      9192 // number of address-based chains
#define hash(x)         (((x) >> 6) % HEAPCHAINS)

typedef struct HeapObj
{
  uint64_t addr;
  uint64_t size;
  long val;
  HeapObj *next;
} HeapObj;

typedef struct HeapChunk
{
  void *addr;
  HeapChunk *next;
} HeapChunk;

HeapMap::HeapMap ()
{
  chunks = NULL;
  empty = NULL;
  chain = new HeapObj*[HEAPCHAINS];
  for (int i = 0; i < HEAPCHAINS; i++)
    chain[i] = NULL;

  mmaps = new HeapObj;
  mmaps->addr = (uint64_t) 0;
  mmaps->size = (uint64_t) 0;
  mmaps->val = -1;
  mmaps->next = NULL;
}

HeapMap::~HeapMap ()
{
  // free up all the chunks
  HeapChunk *c = chunks;
  while (c != NULL)
    {
      HeapChunk *next = c->next;
      delete c;
      c = next;
    }
  delete[] chain;
  delete mmaps;
}

void
HeapMap::allocate (uint64_t addr, long val)
{
  // get a HeapObj, and set it up for the allocated block
  HeapObj *incoming = getHeapObj ();
  incoming->addr = addr;
  incoming->val = val;
  incoming->next = NULL;

  // determine which chain the block belongs on
  int ichain = (int) hash (addr);

  // if this is the first, just set it up and return
  if (chain[ichain] == NULL)
    {
      chain[ichain] = incoming;
      return;
    }
  // chain is non-empty -- find the slot for this one
  //  chain is maintained in reverse address order
  HeapObj *prev = NULL;
  HeapObj *next = chain[ichain];

  for (;;)
    {
      if ((next == NULL) || (next->addr < incoming->addr))
	{
	  // we've found the spot
	  incoming->next = next;
	  if (prev == NULL)
	    chain[ichain] = incoming;
	  else
	    prev->next = incoming;
	  return;
	}
      if (next->addr == incoming->addr)
	{
	  // error -- two blocks with same address active
	  //   ignore the new one
	  releaseHeapObj (incoming);
	  return;
	}
      // not yet, keep looking
      prev = next;
      next = next->next;
    }
}

long
HeapMap::deallocate (uint64_t addr)
{
  int ichain = (int) hash (addr);
  HeapObj *cur = chain[ichain];
  HeapObj *prev = NULL;
  while (cur != NULL)
    {
      if (cur->addr == addr)
	{
	  // we've found the block
	  long val = cur->val;

	  // delete the block from the chain
	  if (prev == NULL)
	    chain[ichain] = cur->next;
	  else
	    prev->next = cur->next;
	  releaseHeapObj (cur);
	  return val;
	}
      prev = cur;
      cur = cur->next;
    }

  // block not found
  return 0;
}

void
HeapMap::allocateChunk ()
{
  // allocate the memory
  HeapChunk *c = new HeapChunk;
  HeapObj *newc = new HeapObj[HEAPCHUNKSZ];

  // set up the chunk, and queue it for destructor's use
  c->addr = (void *) newc;
  c->next = chunks;
  chunks = c;

  // Initialize the HeapObj's in the chunk to one chain
  // last entry is left NULL, terminating the chain
  for (int i = 0; i < (HEAPCHUNKSZ - 1); i++)
    newc[i].next = newc + i + 1;
  newc[HEAPCHUNKSZ - 1].next = NULL;

  // put that chain on the empty queue
  empty = newc;
}

HeapObj *
HeapMap::getHeapObj ()
{
  if (empty == NULL)
    allocateChunk ();
  HeapObj *ret = empty;
  empty = ret->next;
  return ret;
}

void
HeapMap::releaseHeapObj (HeapObj *obj)
{
  obj->next = empty;
  empty = obj;
}

UnmapChunk*
HeapMap::mmap (uint64_t addr, int64_t size, long val)
{
  HeapObj *incoming = getHeapObj ();
  incoming->addr = addr;
  incoming->size = size;
  incoming->val = val;
  incoming->next = NULL;
  UnmapChunk* list = process (incoming, addr, size);
  return list;
}

UnmapChunk*
HeapMap::munmap (uint64_t addr, int64_t size)
{
  UnmapChunk* list = process (NULL, addr, size);
  return list;
}

UnmapChunk*
HeapMap::process (HeapObj *obj, uint64_t addr, int64_t size)
{
  // Some graphics are used to clarify the alignment of mmap regions
  // obj, shown as consecutive pages: "NNNNNN"
  // cur, shown as consecutive pages: "CCCCCC"

  // Find the first overlap, start of N is before end of C.  Examples:
  //   CCCCC
  //     NNNNN
  // or
  //   CCCCC
  //    NNN
  // or
  //   CCCCC
  //  NNNNN
  // or
  //   CCCCC
  //  NNNNNNN
  HeapObj *prev = mmaps;
  HeapObj *cur = prev->next;
  while (cur)
    {
      if (addr < cur->addr + cur->size)
	break;
      prev = cur;
      cur = prev->next;
    }

  // None found
  if (cur == NULL)
    {
      prev->next = obj;
      return NULL;
    }

  UnmapChunk* list = NULL;
  if (addr > cur->addr)
    {
      if (cur->addr + cur->size <= addr + size)
	{
	  // Process overlap on the left (low side) of new allocation
	  // New range: N-start to C-end (gets freed below)
	  prev = cur;
	  cur = getHeapObj ();
	  cur->addr = addr;
	  cur->size = prev->addr + prev->size - addr;
	  cur->val = prev->val;
	  cur->next = prev->next;

	  // Truncate range: C-start to N-start
	  prev->size = addr - prev->addr;
	}
      else
	{
	  // Process new allocation that fits completely within old allocation
	  // New range: N-start to N-end (freed below)
	  int64_t c_end = cur->addr + cur->size;
	  prev = cur;
	  cur = getHeapObj ();
	  cur->addr = addr;
	  cur->size = size;
	  cur->val = prev->val;
	  cur->next = prev->next;

	  // Truncate range: C-start to N-start
	  prev->size = addr - prev->addr;

	  // New range: N-end to C-end.
	  HeapObj *next = getHeapObj ();
	  next->addr = addr + size;
	  next->size = c_end - next->addr;
	  next->val = cur->val;
	  next->next = cur->next;
	  cur->next = next;
	}
    }

  // Process all full overlaps.
  // Copy details of cur to UnmapChunk list, remove cur from mmaps
  while (cur && cur->addr + cur->size <= addr + size)
    {

      UnmapChunk* uc = new UnmapChunk;
      uc->val = cur->val;
      uc->size = cur->size;
      uc->next = list;
      list = uc;

      HeapObj *t = cur;
      cur = cur->next;
      releaseHeapObj (t);
    }

  if (cur && cur->addr < addr + size)
    {
      // Process the last overlap (right side of new allocation)
      // Copy details of cur to UnmapChunk list
      UnmapChunk* uc = new UnmapChunk;
      uc->val = cur->val;
      uc->size = addr + size - cur->addr;
      uc->next = list;
      list = uc;

      // Truncate left side of cur
      cur->size -= uc->size;
      cur->addr = addr + size;
    }

  // Insert new allocation
  if (obj)
    {
      prev->next = obj;
      obj->next = cur;
    }
  else
    prev->next = cur;
  return list;
}