• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file db_manage.c
3  * Management of a DB file
4  *
5  * @remark Copyright 2002 OProfile authors
6  * @remark Read the file COPYING
7  *
8  * @author Philippe Elie
9  */
10 
11 #define _GNU_SOURCE
12 
13 #include <stdlib.h>
14 #ifdef ANDROID
15 #include <fcntl.h>
16 #else
17 #include <sys/fcntl.h>
18 #endif
19 #include <sys/mman.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <stdio.h>
26 
27 #include "odb.h"
28 #include "op_string.h"
29 #include "op_libiberty.h"
30 
31 
odb_to_descr(odb_data_t * data)32 static __inline odb_descr_t * odb_to_descr(odb_data_t * data)
33 {
34 	return (odb_descr_t *)(((char*)data->base_memory) + data->sizeof_header);
35 }
36 
37 
odb_to_node_base(odb_data_t * data)38 static __inline odb_node_t * odb_to_node_base(odb_data_t * data)
39 {
40 	return (odb_node_t *)(((char *)data->base_memory) + data->offset_node);
41 }
42 
43 
odb_to_hash_base(odb_data_t * data)44 static __inline odb_index_t * odb_to_hash_base(odb_data_t * data)
45 {
46 	return (odb_index_t *)(((char *)data->base_memory) +
47 				data->offset_node +
48 				(data->descr->size * sizeof(odb_node_t)));
49 }
50 
51 
52 /**
53  * return the number of bytes used by hash table, node table and header.
54  */
tables_size(odb_data_t const * data,odb_node_nr_t node_nr)55 static unsigned int tables_size(odb_data_t const * data, odb_node_nr_t node_nr)
56 {
57 	size_t size;
58 
59 	size = node_nr * (sizeof(odb_index_t) * BUCKET_FACTOR);
60 	size += node_nr * sizeof(odb_node_t);
61 	size += data->offset_node;
62 
63 	return size;
64 }
65 
66 
odb_grow_hashtable(odb_data_t * data)67 int odb_grow_hashtable(odb_data_t * data)
68 {
69 	unsigned int old_file_size;
70 	unsigned int new_file_size;
71 	unsigned int pos;
72 	void * new_map;
73 
74 	old_file_size = tables_size(data, data->descr->size);
75 	new_file_size = tables_size(data, data->descr->size * 2);
76 
77 	if (ftruncate(data->fd, new_file_size))
78 		return 1;
79 
80 	new_map = mremap(data->base_memory,
81 			 old_file_size, new_file_size, MREMAP_MAYMOVE);
82 
83 	if (new_map == MAP_FAILED)
84 		return 1;
85 
86 	data->base_memory = new_map;
87 	data->descr = odb_to_descr(data);
88 	data->descr->size *= 2;
89 	data->node_base = odb_to_node_base(data);
90 	data->hash_base = odb_to_hash_base(data);
91 	data->hash_mask = (data->descr->size * BUCKET_FACTOR) - 1;
92 
93 	/* rebuild the hash table, node zero is never used. This works
94 	 * because layout of file is node table then hash table,
95 	 * sizeof(node) > sizeof(bucket) and when we grow table we
96 	 * double size ==> old hash table and new hash table can't
97 	 * overlap so on the new hash table is entirely in the new
98 	 * memory area (the grown part) and we know the new hash
99 	 * hash table is zeroed. That's why we don't need to zero init
100 	 * the new table */
101 	/* OK: the above is not exact
102 	 * if BUCKET_FACTOR < sizeof(bd_node_t) / sizeof(bd_node_nr_t)
103 	 * all things are fine and we don't need to init the hash
104 	 * table because in this case the new hash table is completely
105 	 * inside the new growed part. Avoiding to touch this memory is
106 	 * useful.
107 	 */
108 #if 0
109 	for (pos = 0 ; pos < data->descr->size*BUCKET_FACTOR ; ++pos)
110 		data->hash_base[pos] = 0;
111 #endif
112 
113 	for (pos = 1; pos < data->descr->current_size; ++pos) {
114 		odb_node_t * node = &data->node_base[pos];
115 		size_t index = odb_do_hash(data, node->key);
116 		node->next = data->hash_base[index];
117 		data->hash_base[index] = pos;
118 	}
119 
120 	return 0;
121 }
122 
123 
odb_init(odb_t * odb)124 void odb_init(odb_t * odb)
125 {
126 	odb->data = NULL;
127 }
128 
129 
130 /* the default number of page, calculated to fit in 4096 bytes */
131 #define DEFAULT_NODE_NR(offset_node)	128
132 #define FILES_HASH_SIZE                 512
133 
134 static struct list_head files_hash[FILES_HASH_SIZE];
135 
136 
init_hash()137 static void init_hash()
138 {
139 	size_t i;
140 	for (i = 0; i < FILES_HASH_SIZE; ++i)
141 		list_init(&files_hash[i]);
142 }
143 
144 
145 static odb_data_t *
find_samples_data(size_t hash,char const * filename)146 find_samples_data(size_t hash, char const * filename)
147 {
148 	struct list_head * pos;
149 
150 	/* FIXME: maybe an initial init routine ? */
151 	if (files_hash[0].next == NULL) {
152 		init_hash();
153 		return NULL;
154 	}
155 
156 	list_for_each(pos, &files_hash[hash]) {
157 		odb_data_t * entry = list_entry(pos, odb_data_t, list);
158 		if (strcmp(entry->filename, filename) == 0)
159 			return entry;
160 	}
161 
162 	return NULL;
163 }
164 
165 
odb_open(odb_t * odb,char const * filename,enum odb_rw rw,size_t sizeof_header)166 int odb_open(odb_t * odb, char const * filename, enum odb_rw rw,
167 	     size_t sizeof_header)
168 {
169 	struct stat stat_buf;
170 	odb_node_nr_t nr_node;
171 	odb_data_t * data;
172 	size_t hash;
173 	int err = 0;
174 
175 	int flags = (rw == ODB_RDWR) ? (O_CREAT | O_RDWR) : O_RDONLY;
176 	int mmflags = (rw == ODB_RDWR) ? (PROT_READ | PROT_WRITE) : PROT_READ;
177 
178 	hash = op_hash_string(filename) % FILES_HASH_SIZE;
179 	data = find_samples_data(hash, filename);
180 	if (data) {
181 		odb->data = data;
182 		data->ref_count++;
183 		return 0;
184 	}
185 
186 	data = xmalloc(sizeof(odb_data_t));
187 	memset(data, '\0', sizeof(odb_data_t));
188 	list_init(&data->list);
189 	data->offset_node = sizeof_header + sizeof(odb_descr_t);
190 	data->sizeof_header = sizeof_header;
191 	data->ref_count = 1;
192 	data->filename = xstrdup(filename);
193 
194 	data->fd = open(filename, flags, 0644);
195 	if (data->fd < 0) {
196 		err = errno;
197 		goto out;
198 	}
199 
200 	if (fstat(data->fd, &stat_buf)) {
201 		err = errno;
202 		goto fail;
203 	}
204 
205 	if (stat_buf.st_size == 0) {
206 		size_t file_size;
207 
208 		if (rw == ODB_RDONLY) {
209 			err = EIO;
210 			goto fail;
211 		}
212 
213 		nr_node = DEFAULT_NODE_NR(data->offset_node);
214 
215 		file_size = tables_size(data, nr_node);
216 		if (ftruncate(data->fd, file_size)) {
217 			err = errno;
218 			goto fail;
219 		}
220 	} else {
221 		/* Calculate nr node allowing a sanity check later */
222 		nr_node = (stat_buf.st_size - data->offset_node) /
223 			((sizeof(odb_index_t) * BUCKET_FACTOR) + sizeof(odb_node_t));
224 	}
225 
226 	data->base_memory = mmap(0, tables_size(data, nr_node), mmflags,
227 				MAP_SHARED, data->fd, 0);
228 
229 	if (data->base_memory == MAP_FAILED) {
230 		err = errno;
231 		goto fail;
232 	}
233 
234 	data->descr = odb_to_descr(data);
235 
236 	if (stat_buf.st_size == 0) {
237 		data->descr->size = nr_node;
238 		/* page zero is not used */
239 		data->descr->current_size = 1;
240 	} else {
241 		/* file already exist, sanity check nr node */
242 		if (nr_node != data->descr->size) {
243 			err = EINVAL;
244 			goto fail_unmap;
245 		}
246 	}
247 
248 	data->hash_base = odb_to_hash_base(data);
249 	data->node_base = odb_to_node_base(data);
250 	data->hash_mask = (data->descr->size * BUCKET_FACTOR) - 1;
251 
252 	list_add(&data->list, &files_hash[hash]);
253 	odb->data = data;
254 out:
255 	return err;
256 fail_unmap:
257 	munmap(data->base_memory, tables_size(data, nr_node));
258 fail:
259 	close(data->fd);
260 	free(data->filename);
261 	free(data);
262 	odb->data = NULL;
263 	goto out;
264 }
265 
266 
odb_close(odb_t * odb)267 void odb_close(odb_t * odb)
268 {
269 	odb_data_t * data = odb->data;
270 
271 	if (data) {
272 		data->ref_count--;
273 		if (data->ref_count == 0) {
274 			size_t size = tables_size(data, data->descr->size);
275 			list_del(&data->list);
276 			munmap(data->base_memory, size);
277 			if (data->fd >= 0)
278 				close(data->fd);
279 			free(data->filename);
280 			free(data);
281 			odb->data = NULL;
282 		}
283 	}
284 }
285 
286 
odb_open_count(odb_t const * odb)287 int odb_open_count(odb_t const * odb)
288 {
289 	if (!odb->data)
290 		return 0;
291 	return odb->data->ref_count;
292 }
293 
294 
odb_get_data(odb_t * odb)295 void * odb_get_data(odb_t * odb)
296 {
297 	return odb->data->base_memory;
298 }
299 
300 
odb_sync(odb_t const * odb)301 void odb_sync(odb_t const * odb)
302 {
303 	odb_data_t * data = odb->data;
304 	size_t size;
305 
306 	if (!data)
307 		return;
308 
309 	size = tables_size(data, data->descr->size);
310 	msync(data->base_memory, size, MS_ASYNC);
311 }
312