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
|
#include "onewayalloc.h"
// https://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html
#define OWA_NATURAL_ALIGNMENT (sizeof(uintptr_t) * 2)
typedef struct owa_page {
size_t stats_pages;
size_t stats_pages_size;
size_t stats_mallocs_made;
size_t stats_mallocs_size;
size_t size; // the total size of the page
size_t offset; // the first free byte of the page
struct owa_page *next; // the next page on the list
struct owa_page *last; // the last page on the list - we currently allocate on this
} OWA_PAGE;
// allocations need to be aligned to CPU register width
// https://en.wikipedia.org/wiki/Data_structure_alignment
static inline size_t natural_alignment(size_t size) {
if(unlikely(size % OWA_NATURAL_ALIGNMENT))
size = size + OWA_NATURAL_ALIGNMENT - (size % OWA_NATURAL_ALIGNMENT);
return size;
}
// Create an OWA
// Once it is created, the called may call the onewayalloc_mallocz()
// any number of times, for any amount of memory.
static OWA_PAGE *onewayalloc_create_internal(OWA_PAGE *head, size_t size_hint) {
static size_t OWA_NATURAL_PAGE_SIZE = 0;
if(unlikely(!OWA_NATURAL_PAGE_SIZE)) {
long int page_size = sysconf(_SC_PAGE_SIZE);
if (unlikely(page_size == -1))
OWA_NATURAL_PAGE_SIZE = 4096;
else
OWA_NATURAL_PAGE_SIZE = page_size;
}
// our default page size
size_t size = OWA_NATURAL_PAGE_SIZE;
// make sure the new page will fit both the requested size
// and the OWA_PAGE structure at its beginning
size_hint += natural_alignment(sizeof(OWA_PAGE));
// prefer the user size if it is bigger than our size
if(size_hint > size) size = size_hint;
// try to allocate half of the total we have allocated already
if(likely(head)) {
size_t optimal_size = head->stats_pages_size / 2;
if(optimal_size > size) size = optimal_size;
}
// Make sure our allocations are always a multiple of the hardware page size
if(size % OWA_NATURAL_PAGE_SIZE) size = size + OWA_NATURAL_PAGE_SIZE - (size % OWA_NATURAL_PAGE_SIZE);
// OWA_PAGE *page = (OWA_PAGE *)netdata_mmap(NULL, size, MAP_ANONYMOUS|MAP_PRIVATE, 0);
// if(unlikely(!page)) fatal("Cannot allocate onewayalloc buffer of size %zu", size);
OWA_PAGE *page = (OWA_PAGE *)mallocz(size);
page->size = size;
page->offset = natural_alignment(sizeof(OWA_PAGE));
page->next = page->last = NULL;
if(unlikely(!head)) {
// this is the first time we are called
head = page;
head->stats_pages = 0;
head->stats_pages_size = 0;
head->stats_mallocs_made = 0;
head->stats_mallocs_size = 0;
}
else {
// link this page into our existing linked list
head->last->next = page;
}
head->last = page;
head->stats_pages++;
head->stats_pages_size += size;
return page;
}
ONEWAYALLOC *onewayalloc_create(size_t size_hint) {
return (ONEWAYALLOC *)onewayalloc_create_internal(NULL, size_hint);
}
void *onewayalloc_mallocz(ONEWAYALLOC *owa, size_t size) {
OWA_PAGE *head = (OWA_PAGE *)owa;
OWA_PAGE *page = head->last;
// update stats
head->stats_mallocs_made++;
head->stats_mallocs_size += size;
// make sure the size is aligned
size = natural_alignment(size);
if(unlikely(page->size - page->offset < size)) {
// we don't have enough space to fit the data
// let's get another page
page = onewayalloc_create_internal(head, (size > page->size)?size:page->size);
}
char *mem = (char *)page;
mem = &mem[page->offset];
page->offset += size;
return (void *)mem;
}
void *onewayalloc_callocz(ONEWAYALLOC *owa, size_t nmemb, size_t size) {
size_t total = nmemb * size;
void *mem = onewayalloc_mallocz(owa, total);
memset(mem, 0, total);
return mem;
}
char *onewayalloc_strdupz(ONEWAYALLOC *owa, const char *s) {
size_t size = strlen(s) + 1;
char *d = onewayalloc_mallocz((OWA_PAGE *)owa, size);
memcpy(d, s, size);
return d;
}
void *onewayalloc_memdupz(ONEWAYALLOC *owa, const void *src, size_t size) {
void *mem = onewayalloc_mallocz((OWA_PAGE *)owa, size);
// memcpy() is way faster than strcpy() since it does not check for '\0'
memcpy(mem, src, size);
return mem;
}
void onewayalloc_freez(ONEWAYALLOC *owa __maybe_unused, const void *ptr __maybe_unused) {
#ifdef NETDATA_INTERNAL_CHECKS
// allow the caller to call us for a mallocz() allocation
// so try to find it in our memory and if it is not there
// log an error
if (unlikely(!ptr))
return;
OWA_PAGE *head = (OWA_PAGE *)owa;
OWA_PAGE *page;
uintptr_t seeking = (uintptr_t)ptr;
for(page = head; page ;page = page->next) {
uintptr_t start = (uintptr_t)page;
uintptr_t end = start + page->size;
if(seeking >= start && seeking <= end) {
// found it - it is ours
// just return to let the caller think we actually did something
return;
}
}
// not found - it is not ours
// let's free it with the system allocator
error("ONEWAYALLOC: request to free address 0x%p that is not allocated by this OWA", ptr);
#endif
return;
}
void *onewayalloc_doublesize(ONEWAYALLOC *owa, const void *src, size_t oldsize) {
size_t newsize = oldsize * 2;
void *dst = onewayalloc_mallocz(owa, newsize);
memcpy(dst, src, oldsize);
onewayalloc_freez(owa, src);
return dst;
}
void onewayalloc_destroy(ONEWAYALLOC *owa) {
if(!owa) return;
OWA_PAGE *head = (OWA_PAGE *)owa;
//info("OWA: %zu allocations of %zu total bytes, in %zu pages of %zu total bytes",
// head->stats_mallocs_made, head->stats_mallocs_size,
// head->stats_pages, head->stats_pages_size);
OWA_PAGE *page = head;
while(page) {
OWA_PAGE *p = page;
page = page->next;
// munmap(p, p->size);
freez(p);
}
}
|