[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Libunwind-devel] [PATCH V2] Configurable cache size
From: |
Bert Wesarg |
Subject: |
Re: [Libunwind-devel] [PATCH V2] Configurable cache size |
Date: |
Fri, 2 Dec 2016 07:48:03 +0100 |
On Fri, Dec 2, 2016 at 12:20 AM, Dave Watson <address@hidden> wrote:
> This is a re-spin of Milian's last patch:
> http://lists.nongnu.org/archive/html/libunwind-devel/2014-10/msg00006.html
>
> Changes:
>
> * Use item size and round up to nearest power of 2.
>
> * Initial cache still exists in BSS. Without this, it means we would fail
> backtrace when out of memory. The test-mem test fails without this
>
> * Drop the threading changes. Unfortunately, in glibc, neither __thread
> (tls_get_addr) nor pthread_setspecific are async-signal-safe. We've
> actually had issues with the Gtrace code because of this, and in some
> cases have to fallback to unw_step. It could be made to only work
> in PER_THREAD caching case as a non-default, but it still seems
> like it's against the spirit of async-signal-safe code.
>
> Alternativly to threading, a simple rw-lock would help, or even better,
> a lock-free or RCU style cache.
> ---
> include/dwarf.h | 21 ++++++++++-----
> include/libunwind-common.h.in | 2 ++
> src/Makefile.am | 6 +++--
> src/dwarf/Gparser.c | 62
> ++++++++++++++++++++++++++++++++-----------
> src/mi/Gset_cache_size.c | 58 ++++++++++++++++++++++++++++++++++++++++
> src/mi/Lset_cache_size.c | 5 ++++
> tests/check-namespace.sh.in | 2 ++
> tests/test-flush-cache.c | 1 +
I miss a manual page entry
> 8 files changed, 133 insertions(+), 24 deletions(-)
> create mode 100644 src/mi/Gset_cache_size.c
> create mode 100644 src/mi/Lset_cache_size.c
>
> diff --git a/include/dwarf.h b/include/dwarf.h
> index 633868b..615c8a9 100644
> --- a/include/dwarf.h
> +++ b/include/dwarf.h
> @@ -325,11 +325,11 @@ typedef struct dwarf_cursor
> }
> dwarf_cursor_t;
>
> -#define DWARF_LOG_UNW_CACHE_SIZE 7
> -#define DWARF_UNW_CACHE_SIZE (1 << DWARF_LOG_UNW_CACHE_SIZE)
> +#define DWARF_DEFAULT_LOG_UNW_CACHE_SIZE 7
> +#define DWARF_DEFAULT_UNW_CACHE_SIZE (1 <<
> DWARF_DEFAULT_LOG_UNW_CACHE_SIZE)
>
> -#define DWARF_LOG_UNW_HASH_SIZE (DWARF_LOG_UNW_CACHE_SIZE + 1)
> -#define DWARF_UNW_HASH_SIZE (1 << DWARF_LOG_UNW_HASH_SIZE)
> +#define DWARF_DEFAULT_LOG_UNW_HASH_SIZE (DWARF_DEFAULT_LOG_UNW_CACHE_SIZE +
> 1)
> +#define DWARF_DEFAULT_UNW_HASH_SIZE (1 <<
> DWARF_DEFAULT_LOG_UNW_HASH_SIZE)
>
> typedef unsigned char unw_hash_index_t;
>
> @@ -339,13 +339,20 @@ struct dwarf_rs_cache
> unsigned short lru_head; /* index of lead-recently used rs */
> unsigned short lru_tail; /* index of most-recently used rs */
>
> + unsigned short log_size;
> + unsigned short prev_log_size;
> +
> /* hash table that maps instruction pointer to rs index: */
> - unsigned short hash[DWARF_UNW_HASH_SIZE];
> + unsigned short *hash;
>
> uint32_t generation; /* generation number */
>
> /* rs cache: */
> - dwarf_reg_state_t buckets[DWARF_UNW_CACHE_SIZE];
> + dwarf_reg_state_t *buckets;
> +
> + /* default memory, loaded in BSS segment */
> + unsigned short default_hash[DWARF_DEFAULT_UNW_HASH_SIZE];
> + dwarf_reg_state_t default_buckets[DWARF_DEFAULT_UNW_CACHE_SIZE];
> };
>
> /* A list of descriptors for loaded .debug_frame sections. */
> @@ -394,6 +401,7 @@ struct dwarf_callback_data
> #define dwarf_make_proc_info UNW_OBJ (dwarf_make_proc_info)
> #define dwarf_read_encoded_pointer UNW_OBJ (dwarf_read_encoded_pointer)
> #define dwarf_step UNW_OBJ (dwarf_step)
> +#define dwarf_flush_rs_cache UNW_OBJ (dwarf_flush_rs_cache)
>
> extern int dwarf_init (void);
> #ifndef UNW_REMOTE_ONLY
> @@ -438,5 +446,6 @@ extern int dwarf_read_encoded_pointer (unw_addr_space_t
> as,
> const unw_proc_info_t *pi,
> unw_word_t *valp, void *arg);
> extern int dwarf_step (struct dwarf_cursor *c);
> +extern int dwarf_flush_rs_cache (struct dwarf_rs_cache *cache);
>
> #endif /* dwarf_h */
> diff --git a/include/libunwind-common.h.in b/include/libunwind-common.h.in
> index fa753ba..97a799a 100644
> --- a/include/libunwind-common.h.in
> +++ b/include/libunwind-common.h.in
> @@ -225,6 +225,7 @@ unw_save_loc_t;
> #define unw_handle_signal_frame UNW_OBJ(handle_signal_frame)
> #define unw_get_proc_name UNW_OBJ(get_proc_name)
> #define unw_set_caching_policy UNW_OBJ(set_caching_policy)
> +#define unw_set_cache_size UNW_OBJ(set_cache_size)
> #define unw_regname UNW_ARCH_OBJ(regname)
> #define unw_flush_cache UNW_ARCH_OBJ(flush_cache)
> #define unw_strerror UNW_ARCH_OBJ(strerror)
> @@ -234,6 +235,7 @@ extern void unw_destroy_addr_space (unw_addr_space_t);
> extern unw_accessors_t *unw_get_accessors (unw_addr_space_t);
> extern void unw_flush_cache (unw_addr_space_t, unw_word_t, unw_word_t);
> extern int unw_set_caching_policy (unw_addr_space_t, unw_caching_policy_t);
> +extern int unw_set_cache_size (unw_addr_space_t, size_t);
> extern const char *unw_regname (unw_regnum_t);
>
> extern int unw_init_local (unw_cursor_t *, unw_context_t *);
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 5d87475..bcdb01f 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -109,7 +109,8 @@ libunwind_la_SOURCES_generic =
> \
> mi/Gput_dynamic_unwind_info.c mi/Gdestroy_addr_space.c \
> mi/Gget_reg.c mi/Gset_reg.c \
> mi/Gget_fpreg.c mi/Gset_fpreg.c \
> - mi/Gset_caching_policy.c
> + mi/Gset_caching_policy.c \
> + mi/Gset_cache_size.c
>
> if SUPPORT_CXX_EXCEPTIONS
> libunwind_la_SOURCES_local_unwind = \
> @@ -137,7 +138,8 @@ libunwind_la_SOURCES_local_nounwind =
> \
> mi/Lput_dynamic_unwind_info.c mi/Ldestroy_addr_space.c \
> mi/Lget_reg.c mi/Lset_reg.c \
> mi/Lget_fpreg.c mi/Lset_fpreg.c \
> - mi/Lset_caching_policy.c
> + mi/Lset_caching_policy.c \
> + mi/Lset_cache_size.c
>
> libunwind_la_SOURCES_local = \
> $(libunwind_la_SOURCES_local_nounwind) \
> diff --git a/src/dwarf/Gparser.c b/src/dwarf/Gparser.c
> index 3a47255..a34301d 100644
> --- a/src/dwarf/Gparser.c
> +++ b/src/dwarf/Gparser.c
> @@ -23,13 +23,17 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
> IN AN ACTION
> OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
> WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
>
> -#include <stddef.h>
> #include "dwarf_i.h"
> #include "libunwind_i.h"
> +#include <stddef.h>
> +#include <limits.h>
>
> #define alloc_reg_state() (mempool_alloc (&dwarf_reg_state_pool))
> #define free_reg_state(rs) (mempool_free (&dwarf_reg_state_pool, rs))
>
> +#define DWARF_UNW_CACHE_SIZE(log_size) (1 << log_size)
> +#define DWARF_UNW_HASH_SIZE(log_size) (1 << (log_size + 1))
> +
> static inline int
> read_regnum (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr,
> unw_word_t *valp, void *arg)
> @@ -508,15 +512,37 @@ parse_fde (struct dwarf_cursor *c, unw_word_t ip,
> dwarf_state_record_t *sr)
> return 0;
> }
>
> -static inline void
> -flush_rs_cache (struct dwarf_rs_cache *cache)
> +HIDDEN int
> +dwarf_flush_rs_cache (struct dwarf_rs_cache *cache)
> {
> int i;
>
> - cache->lru_head = DWARF_UNW_CACHE_SIZE - 1;
> + if (cache->log_size == DWARF_DEFAULT_LOG_UNW_CACHE_SIZE
> + || !cache->hash) {
> + cache->hash = cache->default_hash;
> + cache->buckets = cache->default_buckets;
> + cache->log_size = DWARF_DEFAULT_LOG_UNW_CACHE_SIZE;
> + } else {
> + if (cache->hash && cache->hash != cache->default_hash)
> + munmap(cache->hash, DWARF_UNW_HASH_SIZE(cache->prev_log_size));
> + if (cache->buckets && cache->buckets != cache->default_buckets)
> + munmap(cache->buckets, DWARF_UNW_CACHE_SIZE(cache->prev_log_size));
> + GET_MEMORY(cache->hash, DWARF_UNW_HASH_SIZE(cache->log_size)
> + * sizeof (cache->hash[0]));
> + GET_MEMORY(cache->buckets, DWARF_UNW_CACHE_SIZE(cache->log_size)
> + * sizeof (cache->buckets[0]));
> + if (!cache->hash || !cache->buckets)
> + {
> + Debug (5, "Unable to allocate cache memory");
> + return -1;
> + }
> + cache->prev_log_size = cache->log_size;
> + }
> +
> + cache->lru_head = DWARF_UNW_CACHE_SIZE(cache->log_size) - 1;
> cache->lru_tail = 0;
>
> - for (i = 0; i < DWARF_UNW_CACHE_SIZE; ++i)
> + for (i = 0; i < DWARF_UNW_CACHE_SIZE(cache->log_size); ++i)
> {
> if (i > 0)
> cache->buckets[i].lru_chain = (i - 1);
> @@ -524,8 +550,10 @@ flush_rs_cache (struct dwarf_rs_cache *cache)
> cache->buckets[i].ip = 0;
> cache->buckets[i].valid = 0;
> }
> - for (i = 0; i<DWARF_UNW_HASH_SIZE; ++i)
> + for (i = 0; i< DWARF_UNW_HASH_SIZE(cache->log_size); ++i)
> cache->hash[i] = -1;
> +
> + return 0;
> }
>
> static inline struct dwarf_rs_cache *
> @@ -543,9 +571,11 @@ get_rs_cache (unw_addr_space_t as, intrmask_t
> *saved_maskp)
> lock_acquire (&cache->lock, *saved_maskp);
> }
>
> - if (atomic_read (&as->cache_generation) != atomic_read
> (&cache->generation))
> + if ((atomic_read (&as->cache_generation) != atomic_read
> (&cache->generation))
> + || !cache->hash)
> {
> - flush_rs_cache (cache);
> + if (dwarf_flush_rs_cache (cache) < 0)
> + return NULL;
> cache->generation = as->cache_generation;
> }
>
> @@ -564,12 +594,12 @@ put_rs_cache (unw_addr_space_t as, struct
> dwarf_rs_cache *cache,
> }
>
> static inline unw_hash_index_t CONST_ATTR
> -hash (unw_word_t ip)
> +hash (unw_word_t ip, unsigned short log_size)
> {
> /* based on (sqrt(5)/2-1)*2^64 */
> # define magic ((unw_word_t) 0x9e3779b97f4a7c16ULL)
>
> - return ip * magic >> ((sizeof(unw_word_t) * 8) - DWARF_LOG_UNW_HASH_SIZE);
> + return ip * magic >> ((sizeof(unw_word_t) * 8) - (log_size + 1));
> }
>
> static inline long
> @@ -592,8 +622,8 @@ rs_lookup (struct dwarf_rs_cache *cache, struct
> dwarf_cursor *c)
> if (cache_match (rs, ip))
> return rs;
>
> - index = cache->hash[hash (ip)];
> - if (index >= DWARF_UNW_CACHE_SIZE)
> + index = cache->hash[hash (ip, cache->log_size)];
> + if (index >= DWARF_UNW_CACHE_SIZE(cache->log_size))
> return NULL;
>
> rs = cache->buckets + index;
> @@ -606,7 +636,7 @@ rs_lookup (struct dwarf_rs_cache *cache, struct
> dwarf_cursor *c)
> (rs - cache->buckets);
> return rs;
> }
> - if (rs->coll_chain >= DWARF_UNW_HASH_SIZE)
> + if (rs->coll_chain >= DWARF_UNW_HASH_SIZE(cache->log_size))
> return NULL;
> rs = cache->buckets + rs->coll_chain;
> }
> @@ -630,7 +660,7 @@ rs_new (struct dwarf_rs_cache *cache, struct dwarf_cursor
> * c)
> /* remove the old rs from the hash table (if it's there): */
> if (rs->ip)
> {
> - index = hash (rs->ip);
> + index = hash (rs->ip, cache->log_size);
> tmp = cache->buckets + cache->hash[index];
> prev = NULL;
> while (1)
> @@ -645,7 +675,7 @@ rs_new (struct dwarf_rs_cache *cache, struct dwarf_cursor
> * c)
> }
> else
> prev = tmp;
> - if (tmp->coll_chain >= DWARF_UNW_CACHE_SIZE)
> + if (tmp->coll_chain >= DWARF_UNW_CACHE_SIZE(cache->log_size))
> /* old rs wasn't in the hash-table */
> break;
This change of indentation looks unintended, but may also be of my mailviewer.
Best,
Bert
> tmp = cache->buckets + tmp->coll_chain;
> @@ -653,7 +683,7 @@ rs_new (struct dwarf_rs_cache *cache, struct dwarf_cursor
> * c)
> }
>
> /* enter new rs in the hash table */
> - index = hash (c->ip);
> + index = hash (c->ip, cache->log_size);
> rs->coll_chain = cache->hash[index];
> cache->hash[index] = rs - cache->buckets;
>
> diff --git a/src/mi/Gset_cache_size.c b/src/mi/Gset_cache_size.c
> new file mode 100644
> index 0000000..895cd49
> --- /dev/null
> +++ b/src/mi/Gset_cache_size.c
> @@ -0,0 +1,58 @@
> +/* libunwind - a platform-independent unwind library
> + Copyright (C) 2014
> + Contributed by Milian Wolff <address@hidden>
> +
> +This file is part of libunwind.
> +
> +Permission is hereby granted, free of charge, to any person obtaining
> +a copy of this software and associated documentation files (the
> +"Software"), to deal in the Software without restriction, including
> +without limitation the rights to use, copy, modify, merge, publish,
> +distribute, sublicense, and/or sell copies of the Software, and to
> +permit persons to whom the Software is furnished to do so, subject to
> +the following conditions:
> +
> +The above copyright notice and this permission notice shall be
> +included in all copies or substantial portions of the Software.
> +
> +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
> +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
> +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
> +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
> +
> +#include "libunwind_i.h"
> +
> +PROTECTED int
> +unw_set_cache_size (unw_addr_space_t as, size_t size)
> +{
> + if (!tdep_init_done)
> + tdep_init ();
> +
> + /* Round up to next power of two, slowly but portably */
> + size_t power = 1;
> + unsigned short log_size = 0;
> + while(power < size) {
> + power *= 2;
> + log_size++;
> + /* Largest size currently supported by rs_cache */
> + if (log_size >= 15)
> + break;
> + }
> +
> + if (log_size == as->global_cache.log_size)
> + return 0; /* no change */
> +
> + as->global_cache.log_size = log_size;
> +
> + /* Ensure caches are empty (and initialized). */
> + unw_flush_cache (as, 0, 0);
> +#ifdef __ia64__
> + return 0;
> +#else
> + /* Synchronously purge cache, to ensure memory is allocated */
> + dwarf_flush_rs_cache(&as->global_cache);
missing return. please note, that if you use the return value from
dwarf_flush_rs_cache, it will for the caller look like he gets
-UNW_EUNSPEC in case of an error, as -1 will be returned there. Though
-UNW_ENOMEM would be more appropriate. Thus it may be better to
convert the result:
if (0 > dwarf_flush_rs_cache(&as->global_cache))
return -UNW_ENOMEM;
return 0;
> +#endif
> +}
> diff --git a/src/mi/Lset_cache_size.c b/src/mi/Lset_cache_size.c
> new file mode 100644
> index 0000000..670f64d
> --- /dev/null
> +++ b/src/mi/Lset_cache_size.c
> @@ -0,0 +1,5 @@
> +#define UNW_LOCAL_ONLY
> +#include <libunwind.h>
> +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
> +#include "Gset_cache_size.c"
> +#endif
> diff --git a/tests/check-namespace.sh.in b/tests/check-namespace.sh.in
> index 1ef74ab..1ae8121 100644
> --- a/tests/check-namespace.sh.in
> +++ b/tests/check-namespace.sh.in
> @@ -103,6 +103,7 @@ check_local_unw_abi () {
> match _UL${plat}_local_addr_space
> match _UL${plat}_resume
> match _UL${plat}_set_caching_policy
> + match _UL${plat}_set_cache_size
> match _UL${plat}_set_reg
> match _UL${plat}_set_fpreg
> match _UL${plat}_step
> @@ -199,6 +200,7 @@ check_generic_unw_abi () {
> match _U${plat}_regname
> match _U${plat}_resume
> match _U${plat}_set_caching_policy
> + match _U${plat}_set_cache_size
> match _U${plat}_set_fpreg
> match _U${plat}_set_reg
> match _U${plat}_step
> diff --git a/tests/test-flush-cache.c b/tests/test-flush-cache.c
> index 592162c..5a24fee 100644
> --- a/tests/test-flush-cache.c
> +++ b/tests/test-flush-cache.c
> @@ -46,6 +46,7 @@ f257 (void)
> for (i = 0; i < n; ++i)
> printf ("[%d] ip=%p\n", i, buffer[i]);
>
> + unw_set_cache_size (unw_local_addr_space, 1023);
> unw_flush_cache (unw_local_addr_space, 0, 0);
>
> if (verbose)
> --
> 2.8.0-rc2
>
>
> _______________________________________________
> Libunwind-devel mailing list
> address@hidden
> https://lists.nongnu.org/mailman/listinfo/libunwind-devel