db_manage.c

Go to the documentation of this file.
00001 
00011 #define _GNU_SOURCE
00012 
00013 #include <stdlib.h>
00014 #include <sys/fcntl.h>
00015 #include <sys/mman.h>
00016 #include <sys/types.h>
00017 #include <sys/stat.h>
00018 #include <unistd.h>
00019 #include <errno.h>
00020 #include <string.h>
00021 #include <stdio.h>
00022 
00023 #include "odb.h"
00024 #include "op_string.h"
00025 #include "op_libiberty.h"
00026 
00027  
00028 static __inline odb_descr_t * odb_to_descr(odb_data_t * data)
00029 {
00030     return (odb_descr_t *)(((char*)data->base_memory) + data->sizeof_header);
00031 }
00032 
00033  
00034 static __inline odb_node_t * odb_to_node_base(odb_data_t * data)
00035 {
00036     return (odb_node_t *)(((char *)data->base_memory) + data->offset_node);
00037 }
00038 
00039  
00040 static __inline odb_index_t * odb_to_hash_base(odb_data_t * data)
00041 {
00042     return (odb_index_t *)(((char *)data->base_memory) + 
00043                 data->offset_node +
00044                 (data->descr->size * sizeof(odb_node_t)));
00045 }
00046 
00047  
00051 static unsigned int tables_size(odb_data_t const * data, odb_node_nr_t node_nr)
00052 {
00053     size_t size;
00054 
00055     size = node_nr * (sizeof(odb_index_t) * BUCKET_FACTOR);
00056     size += node_nr * sizeof(odb_node_t);
00057     size += data->offset_node;
00058 
00059     return size;
00060 }
00061 
00062 
00063 int odb_grow_hashtable(odb_data_t * data)
00064 {
00065     unsigned int old_file_size;
00066     unsigned int new_file_size;
00067     unsigned int pos;
00068     void * new_map;
00069 
00070     old_file_size = tables_size(data, data->descr->size);
00071     new_file_size = tables_size(data, data->descr->size * 2);
00072 
00073     if (ftruncate(data->fd, new_file_size))
00074         return 1;
00075 
00076     new_map = mremap(data->base_memory,
00077              old_file_size, new_file_size, MREMAP_MAYMOVE);
00078 
00079     if (new_map == MAP_FAILED)
00080         return 1;
00081 
00082     data->base_memory = new_map;
00083     data->descr = odb_to_descr(data);
00084     data->descr->size *= 2;
00085     data->node_base = odb_to_node_base(data);
00086     data->hash_base = odb_to_hash_base(data);
00087     data->hash_mask = (data->descr->size * BUCKET_FACTOR) - 1;
00088 
00089     /* rebuild the hash table, node zero is never used. This works
00090      * because layout of file is node table then hash table,
00091      * sizeof(node) > sizeof(bucket) and when we grow table we
00092      * double size ==> old hash table and new hash table can't
00093      * overlap so on the new hash table is entirely in the new
00094      * memory area (the grown part) and we know the new hash
00095      * hash table is zeroed. That's why we don't need to zero init
00096      * the new table */
00097     /* OK: the above is not exact
00098      * if BUCKET_FACTOR < sizeof(bd_node_t) / sizeof(bd_node_nr_t)
00099      * all things are fine and we don't need to init the hash
00100      * table because in this case the new hash table is completely
00101      * inside the new growed part. Avoiding to touch this memory is
00102      * useful.
00103      */
00104 #if 0
00105     for (pos = 0 ; pos < data->descr->size*BUCKET_FACTOR ; ++pos)
00106         data->hash_base[pos] = 0;
00107 #endif
00108 
00109     for (pos = 1; pos < data->descr->current_size; ++pos) {
00110         odb_node_t * node = &data->node_base[pos];
00111         size_t index = odb_do_hash(data, node->key);
00112         node->next = data->hash_base[index];
00113         data->hash_base[index] = pos;
00114     }
00115 
00116     return 0;
00117 }
00118 
00119 
00120 void odb_init(odb_t * odb)
00121 {
00122     odb->data = NULL;
00123 }
00124 
00125 
00126 /* the default number of page, calculated to fit in 4096 bytes */
00127 #define DEFAULT_NODE_NR(offset_node)    128
00128 #define FILES_HASH_SIZE                 512
00129 
00130 static struct list_head files_hash[FILES_HASH_SIZE];
00131 
00132 
00133 static void init_hash()
00134 {
00135     size_t i;
00136     for (i = 0; i < FILES_HASH_SIZE; ++i)
00137         list_init(&files_hash[i]);
00138 }
00139 
00140 
00141 static odb_data_t *
00142 find_samples_data(size_t hash, char const * filename)
00143 {
00144     struct list_head * pos;
00145 
00146     /* FIXME: maybe an initial init routine ? */
00147     if (files_hash[0].next == NULL) {
00148         init_hash();
00149         return NULL;
00150     }
00151 
00152     list_for_each(pos, &files_hash[hash]) {
00153         odb_data_t * entry = list_entry(pos, odb_data_t, list);
00154         if (strcmp(entry->filename, filename) == 0)
00155             return entry;
00156     }
00157 
00158     return NULL;
00159 }
00160 
00161 
00162 int odb_open(odb_t * odb, char const * filename, enum odb_rw rw,
00163          size_t sizeof_header)
00164 {
00165     struct stat stat_buf;
00166     odb_node_nr_t nr_node;
00167     odb_data_t * data;
00168     size_t hash;
00169     int err = 0;
00170 
00171     int flags = (rw == ODB_RDWR) ? (O_CREAT | O_RDWR) : O_RDONLY;
00172     int mmflags = (rw == ODB_RDWR) ? (PROT_READ | PROT_WRITE) : PROT_READ;
00173 
00174     hash = op_hash_string(filename) % FILES_HASH_SIZE;
00175     data = find_samples_data(hash, filename);
00176     if (data) {
00177         odb->data = data;
00178         data->ref_count++;
00179         return 0;
00180     }
00181 
00182     data = xmalloc(sizeof(odb_data_t));
00183     memset(data, '\0', sizeof(odb_data_t));
00184     list_init(&data->list);
00185     data->offset_node = sizeof_header + sizeof(odb_descr_t);
00186     data->sizeof_header = sizeof_header;
00187     data->ref_count = 1;
00188     data->filename = xstrdup(filename);
00189 
00190     data->fd = open(filename, flags, 0644);
00191     if (data->fd < 0) {
00192         err = errno;
00193         goto out;
00194     }
00195 
00196     if (fstat(data->fd, &stat_buf)) {
00197         err = errno;
00198         goto fail;
00199     }
00200 
00201     if (stat_buf.st_size == 0) {
00202         size_t file_size;
00203 
00204         if (rw == ODB_RDONLY) {
00205             err = EIO;
00206             goto fail;
00207         }
00208 
00209         nr_node = DEFAULT_NODE_NR(data->offset_node);
00210 
00211         file_size = tables_size(data, nr_node);
00212         if (ftruncate(data->fd, file_size)) {
00213             err = errno;
00214             goto fail;
00215         }
00216     } else {
00217         /* Calculate nr node allowing a sanity check later */
00218         nr_node = (stat_buf.st_size - data->offset_node) /
00219             ((sizeof(odb_index_t) * BUCKET_FACTOR) + sizeof(odb_node_t));
00220     }
00221 
00222     data->base_memory = mmap(0, tables_size(data, nr_node), mmflags,
00223                 MAP_SHARED, data->fd, 0);
00224 
00225     if (data->base_memory == MAP_FAILED) {
00226         err = errno;
00227         goto fail;
00228     }
00229 
00230     data->descr = odb_to_descr(data);
00231 
00232     if (stat_buf.st_size == 0) {
00233         data->descr->size = nr_node;
00234         /* page zero is not used */
00235         data->descr->current_size = 1;
00236     } else {
00237         /* file already exist, sanity check nr node */
00238         if (nr_node != data->descr->size) {
00239             err = EINVAL;
00240             goto fail_unmap;
00241         }
00242     }
00243 
00244     data->hash_base = odb_to_hash_base(data);
00245     data->node_base = odb_to_node_base(data);
00246     data->hash_mask = (data->descr->size * BUCKET_FACTOR) - 1;
00247 
00248     list_add(&data->list, &files_hash[hash]);
00249     odb->data = data;
00250 out:
00251     return err;
00252 fail_unmap:
00253     munmap(data->base_memory, tables_size(data, nr_node));
00254 fail:
00255     close(data->fd);
00256     free(data->filename);
00257     free(data);
00258     odb->data = NULL;
00259     goto out;
00260 }
00261 
00262 
00263 void odb_close(odb_t * odb)
00264 {
00265     odb_data_t * data = odb->data;
00266 
00267     if (data) {
00268         data->ref_count--;
00269         if (data->ref_count == 0) {
00270             size_t size = tables_size(data, data->descr->size);
00271             list_del(&data->list);
00272             munmap(data->base_memory, size);
00273             if (data->fd >= 0)
00274                 close(data->fd);
00275             free(data->filename);
00276             free(data);
00277             odb->data = NULL;
00278         }
00279     }
00280 }
00281 
00282 
00283 int odb_open_count(odb_t const * odb)
00284 {
00285     if (!odb->data)
00286         return 0;
00287     return odb->data->ref_count;
00288 }
00289 
00290 
00291 void * odb_get_data(odb_t * odb)
00292 {
00293     return odb->data->base_memory;
00294 }
00295 
00296 
00297 void odb_sync(odb_t const * odb)
00298 {
00299     odb_data_t * data = odb->data;
00300     size_t size;
00301 
00302     if (!data)
00303         return;
00304 
00305     size = tables_size(data, data->descr->size);
00306     msync(data->base_memory, size, MS_ASYNC);
00307 }

Generated on 8 Nov 2012 for Oprofile by  doxygen 1.6.1