diff --git a/framework/dwarf_call_site.cpp b/framework/dwarf_call_site.cpp index 44f01fc..dfd9750 100644 --- a/framework/dwarf_call_site.cpp +++ b/framework/dwarf_call_site.cpp @@ -9,6 +9,8 @@ #include "dwarf_call_site.h" #include "dwarf_utils.h" +#include "hash_multimap.h" +#include "dwarf_utils.h" // ----------------------------------------------------------------------------------- // pst_call_site_param @@ -100,8 +102,7 @@ bool site_handle_dwarf(pst_call_site* site, Dwarf_Die* child) Dwarf_Attribute* attr; do { - switch(dwarf_tag(child)) - { + switch(dwarf_tag(child)) { case DW_TAG_GNU_call_site_parameter: { Dwarf_Addr pc; unw_get_reg(site->ctx->curr_frame, UNW_REG_IP, &pc); @@ -144,7 +145,8 @@ bool site_handle_dwarf(pst_call_site* site, Dwarf_Die* child) return true; } -void pst_call_site_init(pst_call_site* site, pst_context* c, uint64_t tgt, const char* orn) { +void pst_call_site_init(pst_call_site* site, pst_context* c, uint64_t tgt, const char* orn) +{ // methods @@ -186,3 +188,165 @@ void pst_call_site_fini(pst_call_site* site) } } + +// ----------------------------------------------------------------------------------- +// pst_call_site_storage +// ----------------------------------------------------------------------------------- + +// DW_AT_low_pc should point to the offset from process base address which is actually PC of current function, usually. +// further handle DW_AT_abstract_origin attribute of DW_TAG_GNU_call_site DIE to determine what DIE is referenced by it. +// probably by invoke by: +// Dwarf_Die *scopes; +// int n = dwarf_getscopes_die (funcdie, &scopes); // where 'n' is the number of scopes +// if (n <= 0) -> FAILURE +// see handle_function() in elfutils/tests/funcscopes.c -> handle_function() -> print_vars() +// DW_TAG_GNU_call_site_parameter is defined under child DIE of DW_TAG_GNU_call_site and defines value of subroutine before calling it +// relates to DW_OP_GNU_entry_value() handling in callee function to determine the value of an argument/variable of the callee +// get DIE of return type +bool storage_handle_dwarf(pst_call_site_storage* storage, Dwarf_Die* result) +{ + Dwarf_Die origin; + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + + pst_log(SEVERITY_DEBUG, "***** DW_TAG_GNU_call_site contents:"); + // reference to DIE which represents callee's parameter if compiler knows where it is at compile time + const char* oname = NULL; + if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) { + oname = dwarf_diename(&origin); + pst_log(SEVERITY_DEBUG, "\tDW_AT_abstract_origin: '%s'", oname); + } + + // The call site may have a DW_AT_call_site_target attribute which is a DWARF expression. For indirect calls or jumps where it is unknown at + // compile time which subprogram will be called the expression computes the address of the subprogram that will be called. + uint64_t target = 0; + if(dwarf_hasattr (result, DW_AT_GNU_call_site_target)) { + attr = dwarf_attr(result, DW_AT_GNU_call_site_target, &attr_mem); + if(attr) { + pst_dwarf_expr expr; + pst_dwarf_expr_init(&expr); + if(handle_location(storage->ctx, &attr_mem, expr, pc, this)) { + target = expr.value; + pst_log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_target: %#lX", target); + } + pst_dwarf_expr_fini(&expr); + } + } + + if(target == 0 && oname == NULL) { + pst_log(SEVERITY_ERROR, "Cannot determine both call-site target and origin"); + return false; + } + + Dwarf_Die child; + if(dwarf_child (result, &child) == 0) { + pst_call_site* st = storage->add_call_site(storage, target, oname); + if(!st->handle_dwarf(st, &child)) { + storage->del_call_site(storage, st); + return false; + } + } + + return true; +} + +pst_call_site* storage_call_site_by_origin(pst_call_site_storage* storage, const char* origin) +{ + pst_call_site* ret = NULL; + hash_node* node = hash_find(&storage->cs_to_origin, origin, strlen(origin)); + if(node) { + ret = hash_entry(node, pst_call_site, node); + } + + return ret; +} + +pst_call_site* storage_call_site_by_target(pst_call_site_storage* storage, uint64_t target) +{ + pst_call_site* ret = NULL; + hash_node* node = hash_find(&storage->cs_to_target, (char*)&target, sizeof(target)); + if(node) { + ret = hash_entry(node, pst_call_site, node); + } + + return ret; +} + +pst_call_site* storage_add_call_site(pst_call_site_storage* storage, uint64_t target, const char* origin) +{ + pst_new(pst_call_site, st, storage->ctx, target, origin); + list_add_bottom(&storage->call_sites, &st->node); + + if(target) { + hash_add(&storage->cs_to_target, &st->tgt_node, &target, sizeof(target)); + } else if(origin) { + hash_add(&storage->cs_to_origin, &st->org_node, (char*)origin, strlen(origin)); + } + + return st; +} + +void storage_del_call_site(pst_call_site_storage* storage, pst_call_site* st) +{ + hash_node* node = NULL; + list_del(&st->node); + + if(st->target) { + node = hash_find(&storage->cs_to_target, &st->target, sizeof(st->target)); + } else if(st->origin) { + node = hash_find(&storage->cs_to_origin, st->origin, strlen(st->origin)); + } + + if(node) { + hash_del(node); + } + + pst_free(st); +} +pst_call_site* storage_next_call_site(pst_call_site_storage* storage, pst_call_site* st) +{ + struct list_node* n = (st == NULL) ? list_first(&storage->call_sites) : list_next(&st->node); + + pst_call_site* ret = NULL; + if(n) { + ret = list_entry(n, pst_call_site, node); + } + + return ret; +} + +pst_call_site* storage_find_call_site(pst_call_site_storage* storage, pst_function* callee) +{ + uint64_t start_pc = storage->ctx->base_addr + callee->lowpc; + pst_call_site* cs = storage_call_site_by_target(storage, start_pc); + if(!cs) { + cs = storage_call_site_by_origin(storage, callee->name.c_str()); + } + + return cs; +} + +void pst_call_site_storage_init(pst_call_site_storage* storage, pst_context* ctx) +{ + storage->ctx = ctx; + list_head_init(&storage->call_sites); + hash_head_init(&storage->cs_to_target); + hash_head_init(&storage->cs_to_origin); + storage->allocated = false; +} + +pst_call_site_storage* pst_call_site_storage_new(pst_context* ctx) +{ + pst_call_site_storage* ns = pst_alloc(pst_call_site_storage); + if(ns) { + pst_call_site_storage_init(ns, ctx); + ns->allocated = true; + } + + return ns; +} + +void pst_call_site_storage_fini(pst_call_site_storage* storage) +{ + +} diff --git a/framework/dwarf_call_site.h b/framework/dwarf_call_site.h index 64fb943..616529b 100644 --- a/framework/dwarf_call_site.h +++ b/framework/dwarf_call_site.h @@ -14,6 +14,7 @@ #include "list_head.h" #include "context.h" #include "dwarf_expression.h" +#include "hash_multimap.h" // ----------------------------------------------------------------------------------- @@ -37,11 +38,14 @@ void pst_call_site_param_init(pst_call_site_param* param); pst_call_site_param* pst_call_site_param_new(); void pst_call_site_param_fini(pst_call_site_param* param); + // ----------------------------------------------------------------------------------- // DW_TAG_call_site // ----------------------------------------------------------------------------------- typedef struct pst_call_site { list_node node; // uplink. !!! must be first !!! + hash_node tgt_node; + hash_node org_node; // methods pst_call_site_param* (*add_param) (pst_call_site* site); @@ -58,6 +62,30 @@ typedef struct pst_call_site { pst_context* ctx; // execution context bool allocated; // whether this object was allocated or not } pst_call_site; +void pst_call_site_init(pst_call_site* site, pst_context* c, uint64_t tgt, const char* orn); +pst_call_site* pst_call_site_new(pst_context* c, uint64_t tgt, const char* orn); +void pst_call_site_fini(pst_call_site* site); +// ----------------------------------------------------------------------------------- +// storage for all of function's call sites +// ----------------------------------------------------------------------------------- +typedef struct pst_call_site_storage { + // methods + bool (*handle_dwarf) (pst_call_site_storage* storage, Dwarf_Die* result); + pst_call_site* (*add_call_site) (pst_call_site_storage* storage, uint64_t target, const char* origin); + void (*del_call_site) (pst_call_site_storage* storage, pst_call_site* site); + pst_call_site* (*find_call_site) (pst_call_site_storage* storage, pst_function* callee); + pst_call_site* (*next_call_site) (pst_call_site_storage* storage, pst_call_site* st); + + // fields + pst_context* ctx; + list_head call_sites; // Call-Site definitions + hash_head cs_to_target; // map pointer to caller to call-site + hash_head cs_to_origin; // map caller name to call-site + bool allocated; // whether this object was allocated or not +} pst_call_site_storage; + +void pst_call_site_storage_init(pst_call_site_storage* storage, pst_context* ctx); +pst_call_site_storage* pst_call_site_storage_new(pst_context* ctx); #endif /* FRAMEWORK_DWARF_CALL_SITE_H_ */ diff --git a/framework/dwarf_function.cpp b/framework/dwarf_function.cpp index dbbf984..4b1f1c1 100644 --- a/framework/dwarf_function.cpp +++ b/framework/dwarf_function.cpp @@ -5,6 +5,8 @@ * Author: nnosov */ +#include + #include "dwarf_function.h" @@ -53,66 +55,6 @@ pst_parameter* __pst_function::next_param(pst_parameter* p) return next; } -pst_call_site* __pst_function::add_call_site(uint64_t target, const char* origin) -{ - pst_call_site* st = new pst_call_site(ctx, target, origin); - call_sites.InsertLast(st); - if(target) { - mCallStToTarget.Insert(&target, sizeof(target), st); - } - - if(origin) { - mCallStToOrigin.Insert(origin, strlen(origin), st); - } - - return st; -} -void __pst_function::del_call_site(pst_call_site* st) -{ - call_sites.Remove(st); - if(st->target && mCallStToTarget.Lookup(&st->target, sizeof(st->target))) { - mCallStToTarget.Remove(); - } - - if(st->origin && mCallStToTarget.Lookup(st->origin, strlen(st->origin))) { - mCallStToTarget.Remove(); - } - - delete st; -} -pst_call_site* __pst_function::next_call_site(pst_call_site* st) -{ - pst_call_site* next = NULL; - if(st) { - next = (pst_call_site*)call_sites.First(); - } else { - next = (pst_call_site*)call_sites.Next(st); - } - - return next; -} - -pst_call_site* __pst_function::call_site_by_origin(const char* origin) -{ - return (pst_call_site*)mCallStToOrigin.Lookup(origin, strlen(origin)); -} - -pst_call_site* __pst_function::call_site_by_target(uint64_t target) -{ - return (pst_call_site*)mCallStToTarget.Lookup(&target, sizeof(target)); -} - -pst_call_site* __pst_function::find_call_site(pst_function* callee) -{ - uint64_t start_pc = ctx->base_addr + callee->lowpc; - pst_call_site* cs = (pst_call_site*)mCallStToTarget.Lookup(&start_pc, sizeof(start_pc)); - if(!cs) { - cs = (pst_call_site*)mCallStToOrigin.Lookup(callee->name.c_str(), strlen(callee->name.c_str())); - } - - return cs; -} - bool __pst_function::print_dwarf() { char* at = NULL; @@ -241,61 +183,6 @@ bool __pst_function::handle_lexical_block(Dwarf_Die* result) return true; } -// DW_AT_low_pc should point to the offset from process base address which is actually PC of current function, usually. -// further handle DW_AT_abstract_origin attribute of DW_TAG_GNU_call_site DIE to determine what DIE is referenced by it. -// probably by invoke by: -// Dwarf_Die *scopes; -// int n = dwarf_getscopes_die (funcdie, &scopes); // where 'n' is the number of scopes -// if (n <= 0) -> FAILURE -// see handle_function() in elfutils/tests/funcscopes.c -> handle_function() -> print_vars() -// DW_TAG_GNU_call_site_parameter is defined under child DIE of DW_TAG_GNU_call_site and defines value of subroutine before calling it -// relates to DW_OP_GNU_entry_value() handling in callee function to determine the value of an argument/variable of the callee -// get DIE of return type -bool __pst_function::handle_call_site(Dwarf_Die* result) -{ - Dwarf_Die origin; - Dwarf_Attribute attr_mem; - Dwarf_Attribute* attr; - - pst_log(SEVERITY_DEBUG, "***** DW_TAG_GNU_call_site contents:"); - // reference to DIE which represents callee's parameter if compiler knows where it is at compile time - const char* oname = NULL; - if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) { - oname = dwarf_diename(&origin); - pst_log(SEVERITY_DEBUG, "\tDW_AT_abstract_origin: '%s'", oname); - } - - // The call site may have a DW_AT_call_site_target attribute which is a DWARF expression. For indirect calls or jumps where it is unknown at - // compile time which subprogram will be called the expression computes the address of the subprogram that will be called. - uint64_t target = 0; - if(dwarf_hasattr (result, DW_AT_GNU_call_site_target)) { - attr = dwarf_attr(result, DW_AT_GNU_call_site_target, &attr_mem); - if(attr) { - pst_dwarf_expr expr(ctx->alloc); - if(handle_location(ctx, &attr_mem, expr, pc, this)) { - target = expr.value; - pst_log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_target: %#lX", target); - } - } - } - - if(target == 0 && oname == NULL) { - pst_log(SEVERITY_ERROR, "Cannot determine both call-site target and origin"); - return false; - } - - Dwarf_Die child; - if(dwarf_child (result, &child) == 0) { - pst_call_site* st = add_call_site(target, oname); - if(!st->handle_dwarf(&child)) { - del_call_site(st); - return false; - } - } - - return true; -} - bool __pst_function::handle_dwarf(Dwarf_Die* d) { die = d; @@ -537,3 +424,27 @@ bool __pst_function::get_frame() return true; } +void pst_function_init(pst_function* fun, pst_context* _ctx, __pst_function* _parent) +{ + list_node_init(&fun->node); + fun->pc = 0; + fun->line = -1; + fun->die = NULL; + fun->lowpc = 0; + fun->highpc = 0; + memcpy(&fun->cursor, _ctx->curr_frame, sizeof(fun->cursor)); + pst_call_site_storage_init(&fun->call_sites, fun->ctx); + fun->parent = _parent; + fun->sp = 0; + fun->cfa = 0; + fun->frame = NULL; + fun->name = NULL; +} + +void pst_function_fini(pst_function* fun) +{ + clear(); + if(frame) { + free(frame); + } +} diff --git a/framework/dwarf_function.h b/framework/dwarf_function.h index 5e37251..16e3ffc 100644 --- a/framework/dwarf_function.h +++ b/framework/dwarf_function.h @@ -17,67 +17,39 @@ // ----------------------------------------------------------------------------------- // pst_function // ----------------------------------------------------------------------------------- -typedef struct __pst_function : public SC_ListNode { - __pst_function(pst_context* _ctx, __pst_function* _parent) : ctx(_ctx) { - pc = 0; - line = -1; - die = NULL; - lowpc = 0; - highpc = 0; - memcpy(&cursor, _ctx->curr_frame, sizeof(cursor)); - parent = _parent; - sp = 0; - cfa = 0; - frame = NULL; - } - - ~__pst_function() { - clear(); - if(frame) { - free(frame); - } - } +typedef struct __pst_function { + list_node node; // uplink. !!! must be first !!! void clear(); pst_parameter* add_param(); void del_param(pst_parameter* p); pst_parameter* next_param(pst_parameter* p); - pst_call_site* add_call_site(uint64_t target, const char* origin); - void del_call_site(pst_call_site* st); - pst_call_site* next_call_site(pst_call_site* st); - pst_call_site* call_site_by_origin(const char* origin); - pst_call_site* call_site_by_target(uint64_t target); - bool unwind(Dwarf_Addr addr); bool handle_dwarf(Dwarf_Die* d); bool print_dwarf(); bool handle_lexical_block(Dwarf_Die* result); - bool handle_call_site(Dwarf_Die* result); - pst_call_site* find_call_site(__pst_function* callee); bool get_frame(); - Dwarf_Addr lowpc; // offset to start of the function against base address - Dwarf_Addr highpc; // offset to the next address after the end of the function against base address - Dwarf_Die* die; // DWARF DIE containing definition of the function - std::string name; // function's name - SC_ListHead params; // function's parameters + Dwarf_Addr lowpc; // offset to start of the function against base address + Dwarf_Addr highpc; // offset to the next address after the end of the function against base address + Dwarf_Die* die; // DWARF DIE containing definition of the function + std::string name; // function's name + SC_ListHead params; // function's parameters - // call-site - SC_ListHead call_sites; // Call-Site definitions - SC_Dict mCallStToTarget; // map pointer to caller to call-site - SC_Dict mCallStToOrigin; // map caller name to call-site + pst_call_site_storage call_sites; - unw_word_t pc; // address between lowpc & highpc (plus base address offset). actually, currently executed command - unw_word_t sp; // SP register in function's frame - unw_word_t cfa; // CFA (Canonical Frame Adress) of the function - unw_cursor_t cursor; // copy of stack state of the function - int line; // line in code where function is defined - std::string file; // file name (DWARF Compilation Unit) where function is defined - __pst_function* parent; // parent function in call trace (caller) - Dwarf_Frame* frame; // function's stack frame - pst_context* ctx; // context of unwinding + unw_word_t pc; // address between LowPC & HighPC (plus base address offset). actually, currently executed command + unw_word_t sp; // SP register in function's frame + unw_word_t cfa; // CFA (Canonical Frame Address) of the function + unw_cursor_t cursor; // copy of stack state of the function + int line; // line in code where function is defined + std::string file; // file name (DWARF Compilation Unit) where function is defined + __pst_function* parent; // parent function in call trace (caller) + Dwarf_Frame* frame; // function's stack frame + pst_context* ctx; // context of unwinding + bool allocated; // whether this object was allocated or not } pst_function; diff --git a/framework/hash_multimap.c b/framework/hash_multimap.c new file mode 100644 index 0000000..e3e426c --- /dev/null +++ b/framework/hash_multimap.c @@ -0,0 +1,316 @@ +/* ============================================================================= + * CDL (Configuration Definition Language) validator $Revision: 1.4 $ + * (C)2004-2007 Nikolai Nosov. All rights reserved. + * + * File: $RCSfile: hash_multimap.c,v $ + * Purpose: Hash Multimap implementation + * Written by: Nikolai Nosov nnosov@gmail.com + * Last modified: $Date: 2008/06/21 12:15:53 $ by $Author: nnosov $. + * + * For more information please visit + * http://cdl.sourceforge.net + * ===========================================================================*/ + +#include +#include +#include + +#include "hash_multimap.h" + +/* Default hash function +@param key hash key +@param size hash key size in bytes +@return hash value, depends of the hash key +*/ +static unsigned int default_hash_fn(const char *key, int size) +{ + register int i, j; + unsigned int cs = 0; + unsigned int ps = 0; + + if(key) { + for(i = 0, j = 0; i < size; i++, j++) { + ps |= ((unsigned int)key[i]) << (j * 8); + if(j == 3 || j == (size - 1)) { + cs ^= ps; + ps = 0; + j = 0; + } + } + } + + return cs; +} + +static int default_compare_fn(const char *key1, const char *key2, const int size) +{ + return !memcmp(key1, key2, size); +} + +/** Initialize hash node. Must be called before first usage of the node. +@param node pointer to the hash node descriptor +*/ +void hash_node_init(struct hash_node *node) +{ + node->key = NULL; + node->key_size = 0; + list_node_init(&node->node); +} + +/** Cleanup hash node +@param node pointer to the hash node descriptor +*/ +void hash_node_cleanup(struct hash_node *node) +{ + if(node->key) free(node->key); + list_node_init(&node->node); + node->key = NULL; + node->key_size = 0; +} + +/** Initialize hash table +@param map pointer to the hash table descriptor +@param hash_shift size of the hash table in bits. Should be greater than +HASH_MIN_SHIFT and less than HASH_MAX_SHIFT. In case of less than HASH_MIN_SHIFT, +will be followed by HASH_MIN_SHIFT, in case of greater HASH_MAX_SHIFT will be +followed by HASH_MAX_SHIFT +@param hf pointer to the user defined hash function. If NULL, then default hash +function will be used +@param cf pointer to the user defined compare function. if NULL, then the default +compare function will be used +*/ +int hash_head_init(struct hash_head *map, unsigned int hash_shift, _hash_fn hf, _compare_fn cf) +{ + int i; + + if(map) { + if(hash_shift <= HASH_MIN_SHIFT) { + map->hash_shift = HASH_MIN_SHIFT; + } else if(hash_shift >= HASH_MAX_SHIFT) { + map->hash_shift = HASH_MAX_SHIFT; + } else { + map->hash_shift = hash_shift; + } + + map->hash_size = 1UL << map->hash_shift; + map->hash_mask = (map->hash_size - 1); + + map->bucket = (list_head*)malloc(sizeof(list_head) * map->hash_size); + if(map->bucket) { + for(uint16_t i = 0; i < map->hash_size; i++) { + list_head_init(map->bucket + i); + } + + if(hf) { + map->hash_fn = hf; + } else { + map->hash_fn = default_hash_fn; + } + + if(cf) { + map->compare_fn = cf; + } else { + map->compare_fn = default_compare_fn; + } + } else { + return ENOMEM; + } + } else { + return ENODEV; + } + + return 0; +} + +/** Cleanup hash table descriptor + +@param map pointer to the hash table descriptor +*/ +void hash_head_cleanup(struct hash_head *map) +{ + struct list_node *p, *n; + struct hash_node *node; + int i; + + if(map) { + if(map->bucket) { + for(i = 0; i < map->hash_size; i++) { + list_for_each_safe(p, n, map->bucket + i) { + node = list_entry(p, struct hash_node, node); + hash_node_cleanup(node); + } + } + free(map->bucket); + } + } +} + +/** Find first node which key is equal to the key represented + +@param map pointer to the hash table descriptor +@param key pointer to the hash key +@param key_size size of the hash key +@return pointer to the key found, or NULL in case of hash table have not nodes +with the key equals to the key represented +*/ +struct hash_node* hash_find(struct hash_head *map, const void *key, int key_size) +{ + struct list_head *list; + struct list_node *n; + struct hash_node *node; + + list = map->bucket + (map->hash_fn(key, key_size) & map->hash_mask); + list_for_each(n, list) { + node = list_entry(n, struct hash_node, node); + if(key_size == node->key_size) { + if(map->compare_fn(key, node->key, key_size)) { + return node; + } + } + } + + return NULL; +} + +/** Find next node, which key is equal to the key represented + +@param hn1 pointer to the previous node found. +@return pointer to the next node with the same key, or NULL in case of the current +node is the last node with appropriate key +*/ +struct hash_node* hash_find_next(struct hash_head *map, struct hash_node *hn1) +{ + struct list_node *n = &hn1->node; + struct hash_node *hn2; + + + while((n = list_next(n)) != NULL) { + hn2 = list_entry(n, struct hash_node, node); + if(hn1->key_size == hn2->key_size) { + if(map->compare_fn(hn1->key, hn2->key, hn2->key_size)) return hn2; + } + } + + return NULL; +} + +/** Add node to the hash table + +@param map pointer to the hash table +@param node pointer to the node, which will be added to the table +@param key pointer to the hash key +@param key_size size of the hash key + +@return zero in case of success, ENOMEM in case of error +*/ +int hash_add(struct hash_head *map, struct hash_node *node, void *key, int key_size) +{ + node->key = (char*)malloc(key_size); + if(node->key) { + memcpy(node->key, key, key_size); + node->key_size = key_size; + list_add_bottom(map->bucket + (map->hash_fn(key, key_size) & map->hash_mask), &node->node); + + return 0; + } + + return ENOMEM; +} + +/** Remove node from the hash table + +@param node pointer to the node descriptor +*/ +void hash_del(struct hash_node *node) +{ + list_del_init(&node->node); + hash_node_cleanup(node); +} + +/** Initialize iterator for usage. Must be called before first iterator usage. + +@param map pointer to the hash table descriptor +@param iter pointer to the iterator descriptor +*/ +void hash_iterator_init(struct hash_head *map, struct hash_iterator *iter) +{ + iter->map = map; + iter->map_idx = 0; + iter->current = NULL; +} + +/** Move iterator to the first node in the table, and return pointer to the node + +@param iter pointer to the iterator descriptor +@return pointer to the first node in the table, or NULL in case of error +*/ +struct hash_node * hash_node_first(struct hash_iterator *iter) +{ + iter->current = list_first(iter->map->bucket); + iter->map_idx = 0; + + if (iter->current) { + return list_entry(iter->current, struct hash_node, node); + } + + return NULL; +} + +/** Move iterator to the next node in the table and return pointer to the node + +@param iter pointer to the iterator descriptor +@return pointer to the next node in the table, or NULL in case of error +*/ +struct hash_node * hash_node_next(struct hash_iterator *iter) +{ + if(iter->map_idx >= iter->map->hash_size) { + return NULL; + } + + if(iter->current) { + iter->current = list_next(iter->current); + } + + if(iter->current) { + return list_entry(iter->current, struct hash_node, node); + } + + while(iter->map_idx < iter->map->hash_size) { + iter->current = list_first(iter->map->bucket + iter->map_idx); + iter->map_idx++; + if(iter->current) { + return list_entry(iter->current, struct hash_node, node); + } + } + + return NULL; +} + +/** Remove current node, pointed by iterator, and move iterator to the next node + +@param iter pointer to the iterator descriptor +*/ +void hash_node_del(struct hash_iterator *iter) +{ + struct list_node *tmp; + + tmp = iter->current; + hash_node_next(iter); + list_del_init(tmp); +} + +/** Return total count of elements in the hash table + +@param head pointer to the hash table descriptor +*/ +int hash_count(struct hash_head *head) +{ + int i; + int count = 0; + + for(i = 0; i < head->hash_size; i++) { + count += list_count(head->bucket + i); + } + + return count; +} diff --git a/framework/hash_multimap.h b/framework/hash_multimap.h new file mode 100644 index 0000000..1c5443f --- /dev/null +++ b/framework/hash_multimap.h @@ -0,0 +1,109 @@ +#ifndef HASH_MULTIMAP_H +#define HASH_MULTIMAP_H + +/* ============================================================================= + * CDL (Configuration Definition Language) validator $Revision: 1.4 $ + * (C)2004-2007 Nikolai Nosov. All rights reserved. + * + * File: $RCSfile: hash_multimap.c,v $ + * Purpose: Hash Multimap implementation + * Written by: Nikolai Nosov nnosov@gmail.com + * Last modified: $Date: 2008/06/21 12:15:53 $ by $Author: nnosov $. + * + * For more information please visit + * http://cdl.sourceforge.net + * ===========================================================================*/ + +#include + +#include "list_head.h" + +#define HASH_MIN_SHIFT (8) +#define HASH_MAX_SHIFT (15) + +#define hash_entry(ptr, type, member) container_of(ptr, type, member) + +/** Hash multimap node descriptor + +@param key hash key value +@param key_size hash key size in bytes +@param node uplink to hash multimap table +*/ +typedef struct hash_node +{ + char* key; + int key_size; + list_node node; +} hash_node; + +/** Hash function prototype + +@param key hash key value +@param key_size hash key size in bytes +@return hash value +*/ +typedef unsigned int (* _hash_fn)(const char *key, int key_size); + +/** Compare function prototype + * + * @param key1 pointer to the first key for comparison + * @param key2 pointer to the second key for comparison + * @param size size of compared part of the keys in bytes + * + * @return zero if keys equals each other, othervize 1 +*/ +typedef int (*_compare_fn)(const char *key1, const char *key2, int size); + +/** Hash multimap table descriptor + +@param hash_shift size of hash table as power of 2 +@param hash_size size of the hash table +@param hash_mask mask for the hash value +@param bucket pointer to the array of linked lists. in other words, pointer to +hash multimap table +@param count count of the nodes in the hash table +*/ +typedef struct hash_head { + uint8_t hash_shift; + uint16_t hash_size; + uint32_t hash_mask; + list_head* bucket; + + _hash_fn hash_fn; + _compare_fn compare_fn; +} hash_head; + +/** Hash table iterator + +@param map pointer to the hash table descriptor, used for iteration +@param current pointer to the current list node (bucket[map_idx]) +@param map_idx current bucket index +*/ +struct hash_iterator { + hash_head* map; // pointer to the hash map header + list_node* current; // pointer to current list node in the bucket[map_idx] list + int map_idx; // index of current list in the hash map bucket +}; + +void hash_node_init(struct hash_node *node); +void hash_node_cleanup(struct hash_node *node); + +unsigned int default_hash_fn(const char *key, int size); +int default_compare_fn(const char *key1, const char *key2, const int size); + +int hash_head_init(struct hash_head *map, unsigned int hash_shift = 8, _hash_fn hf = default_hash_fn, _compare_fn cf = default_compare_fn); +void hash_head_cleanup(struct hash_head *map); + +struct hash_node* hash_find(struct hash_head *map, const void *key, int key_size); +struct hash_node* hash_find_next(struct hash_head *map, struct hash_node *node); + +int hash_add(struct hash_head *map, struct hash_node *node, void *key, int key_size); +void hash_del(struct hash_node *node); + +void hash_iterator_init(struct hash_head *map, struct hash_iterator *iter); +struct hash_node * hash_node_first(struct hash_iterator *iter); +struct hash_node * hash_node_next(struct hash_iterator *iter); +void hash_node_del(struct hash_iterator *iter); +int hash_count(struct hash_head *head); + +#endif // HASH_MULTIMAP_H