db_manage.c revision 48ae5fc270ea3bbb965b4bd07cb1691a5c115642
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#include <fcntl.h> 15#include <sys/mman.h> 16#include <sys/types.h> 17#include <sys/stat.h> 18#include <fcntl.h> 19#include <unistd.h> 20#include <errno.h> 21#include <string.h> 22#include <stdio.h> 23 24#include "odb.h" 25#include "op_string.h" 26#include "op_libiberty.h" 27 28 29static __inline odb_descr_t * odb_to_descr(odb_data_t * data) 30{ 31 return (odb_descr_t *)(((char*)data->base_memory) + data->sizeof_header); 32} 33 34 35static __inline odb_node_t * odb_to_node_base(odb_data_t * data) 36{ 37 return (odb_node_t *)(((char *)data->base_memory) + data->offset_node); 38} 39 40 41static __inline odb_index_t * odb_to_hash_base(odb_data_t * data) 42{ 43 return (odb_index_t *)(((char *)data->base_memory) + 44 data->offset_node + 45 (data->descr->size * sizeof(odb_node_t))); 46} 47 48 49/** 50 * return the number of bytes used by hash table, node table and header. 51 */ 52static unsigned int tables_size(odb_data_t const * data, odb_node_nr_t node_nr) 53{ 54 size_t size; 55 56 size = node_nr * (sizeof(odb_index_t) * BUCKET_FACTOR); 57 size += node_nr * sizeof(odb_node_t); 58 size += data->offset_node; 59 60 return size; 61} 62 63 64odb_index_t odb_hash_add_node(odb_t * odb) 65{ 66 odb_data_t * data = odb->data; 67 68 if (data->descr->current_size >= data->descr->size) { 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 ODB_NODE_NR_INVALID; 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 ODB_NODE_NR_INVALID; 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 } 112#endif 113 114 for (pos = 1; pos < data->descr->current_size; ++pos) { 115 odb_node_t * node = &data->node_base[pos]; 116 size_t index = odb_do_hash(data, node->key); 117 node->next = data->hash_base[index]; 118 data->hash_base[index] = pos; 119 } 120 } 121 122 return (odb_index_t)data->descr->current_size++; 123} 124 125 126void odb_init(odb_t * odb) 127{ 128 odb->data = NULL; 129} 130 131 132/* the default number of page, calculated to fit in 4096 bytes */ 133#define DEFAULT_NODE_NR(offset_node) 128 134#define FILES_HASH_SIZE 512 135 136static struct list_head files_hash[FILES_HASH_SIZE]; 137 138 139static void init_hash() 140{ 141 size_t i; 142 for (i = 0; i < FILES_HASH_SIZE; ++i) 143 list_init(&files_hash[i]); 144} 145 146 147static odb_data_t * 148find_samples_data(size_t hash, char const * filename) 149{ 150 struct list_head * pos; 151 152 /* FIXME: maybe an initial init routine ? */ 153 if (files_hash[0].next == NULL) { 154 init_hash(); 155 return NULL; 156 } 157 158 list_for_each(pos, &files_hash[hash]) { 159 odb_data_t * entry = list_entry(pos, odb_data_t, list); 160 if (strcmp(entry->filename, filename) == 0) 161 return entry; 162 } 163 164 return NULL; 165} 166 167 168int odb_open(odb_t * odb, char const * filename, enum odb_rw rw, 169 size_t sizeof_header) 170{ 171 struct stat stat_buf; 172 odb_node_nr_t nr_node; 173 odb_data_t * data; 174 size_t hash; 175 int err = 0; 176 177 int flags = (rw == ODB_RDWR) ? (O_CREAT | O_RDWR) : O_RDONLY; 178 int mmflags = (rw == ODB_RDWR) ? (PROT_READ | PROT_WRITE) : PROT_READ; 179 180 hash = op_hash_string(filename) % FILES_HASH_SIZE; 181 data = find_samples_data(hash, filename); 182 if (data) { 183 odb->data = data; 184 data->ref_count++; 185 return 0; 186 } 187 188 data = xmalloc(sizeof(odb_data_t)); 189 memset(data, '\0', sizeof(odb_data_t)); 190 list_init(&data->list); 191 data->offset_node = sizeof_header + sizeof(odb_descr_t); 192 data->sizeof_header = sizeof_header; 193 data->ref_count = 1; 194 data->filename = xstrdup(filename); 195 196 data->fd = open(filename, flags, 0644); 197 if (data->fd < 0) { 198 err = errno; 199 goto out; 200 } 201 202 if (fstat(data->fd, &stat_buf)) { 203 err = errno; 204 goto fail; 205 } 206 207 if (stat_buf.st_size == 0) { 208 size_t file_size; 209 210 if (rw == ODB_RDONLY) { 211 err = EIO; 212 goto fail; 213 } 214 215 nr_node = DEFAULT_NODE_NR(data->offset_node); 216 217 file_size = tables_size(data, nr_node); 218 if (ftruncate(data->fd, file_size)) { 219 err = errno; 220 goto fail; 221 } 222 } else { 223 /* Calculate nr node allowing a sanity check later */ 224 nr_node = (stat_buf.st_size - data->offset_node) / 225 ((sizeof(odb_index_t) * BUCKET_FACTOR) + sizeof(odb_node_t)); 226 } 227 228 data->base_memory = mmap(0, tables_size(data, nr_node), mmflags, 229 MAP_SHARED, data->fd, 0); 230 231 if (data->base_memory == MAP_FAILED) { 232 err = errno; 233 goto fail; 234 } 235 236 data->descr = odb_to_descr(data); 237 238 if (stat_buf.st_size == 0) { 239 data->descr->size = nr_node; 240 /* page zero is not used */ 241 data->descr->current_size = 1; 242 } else { 243 /* file already exist, sanity check nr node */ 244 if (nr_node != data->descr->size) { 245 err = EINVAL; 246 goto fail_unmap; 247 } 248 } 249 250 data->hash_base = odb_to_hash_base(data); 251 data->node_base = odb_to_node_base(data); 252 data->hash_mask = (data->descr->size * BUCKET_FACTOR) - 1; 253 254 list_add(&data->list, &files_hash[hash]); 255 odb->data = data; 256out: 257 return err; 258fail_unmap: 259 munmap(data->base_memory, tables_size(data, nr_node)); 260fail: 261 close(data->fd); 262 free(data->filename); 263 free(data); 264 odb->data = NULL; 265 goto out; 266} 267 268 269void odb_close(odb_t * odb) 270{ 271 odb_data_t * data = odb->data; 272 273 if (data) { 274 data->ref_count--; 275 if (data->ref_count == 0) { 276 size_t size = tables_size(data, data->descr->size); 277 list_del(&data->list); 278 munmap(data->base_memory, size); 279 if (data->fd >= 0) 280 close(data->fd); 281 free(data->filename); 282 free(data); 283 odb->data = NULL; 284 } 285 } 286} 287 288 289int odb_open_count(odb_t const * odb) 290{ 291 if (!odb->data) 292 return 0; 293 return odb->data->ref_count; 294} 295 296 297void * odb_get_data(odb_t * odb) 298{ 299 return odb->data->base_memory; 300} 301 302 303void odb_sync(odb_t const * odb) 304{ 305 odb_data_t * data = odb->data; 306 size_t size; 307 308 if (!data) 309 return; 310 311 size = tables_size(data, data->descr->size); 312 msync(data->base_memory, size, MS_ASYNC); 313} 314