19e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura/* libunwind - a platform-independent unwind library
23b9fd99cb78383e0ce8cd1a31e3b824a30ef965eLassi Tuura   Copyright (C) 2010, 2011 by FERMI NATIONAL ACCELERATOR LABORATORY
39e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
49e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi TuuraThis file is part of libunwind.
59e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
69e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi TuuraPermission is hereby granted, free of charge, to any person obtaining
79e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuuraa copy of this software and associated documentation files (the
89e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura"Software"), to deal in the Software without restriction, including
99e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuurawithout limitation the rights to use, copy, modify, merge, publish,
109e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuuradistribute, sublicense, and/or sell copies of the Software, and to
119e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuurapermit persons to whom the Software is furnished to do so, subject to
129e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuurathe following conditions:
139e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
149e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi TuuraThe above copyright notice and this permission notice shall be
159e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuuraincluded in all copies or substantial portions of the Software.
169e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
179e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi TuuraTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
189e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi TuuraEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
199e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi TuuraMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
209e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi TuuraNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
219e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi TuuraLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
229e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi TuuraOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
239e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi TuuraWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
249e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
259e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura#include "unwind_i.h"
269e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura#include "ucontext_i.h"
279e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura#include <signal.h>
280a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma#include <limits.h>
299e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura#pragma weak pthread_once
3144a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura#pragma weak pthread_key_create
3244a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura#pragma weak pthread_getspecific
3344a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura#pragma weak pthread_setspecific
3444a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
35d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura/* Initial hash table size. Table expands by 2 bits (times four). */
36d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura#define HASH_MIN_BITS 14
379e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3844a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuuratypedef struct
3944a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura{
40d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  unw_tdep_frame_t *frames;
41d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  size_t log_size;
4244a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  size_t used;
430a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma  size_t dtor_count;  /* Counts how many times our destructor has already
440a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma			 been called. */
4544a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura} unw_trace_cache_t;
4644a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
4744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuurastatic const unw_tdep_frame_t empty_frame = { 0, UNW_X86_64_FRAME_OTHER, -1, -1, 0, -1, -1 };
48aebba1f8a7dee9b9ae3e70128ad48de69ca90b15Tommi Rantalastatic define_lock (trace_init_lock);
4944a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuurastatic pthread_once_t trace_cache_once = PTHREAD_ONCE_INIT;
5008077a4962c4e606598f9f0e54b515b3c882be10Arun Sharmastatic sig_atomic_t trace_cache_once_happen;
5144a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuurastatic pthread_key_t trace_cache_key;
5244a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuurastatic struct mempool trace_cache_pool;
530a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharmastatic __thread  unw_trace_cache_t *tls_cache;
540a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharmastatic __thread  int tls_cache_destroyed;
5544a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
5644a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura/* Free memory for a thread's trace cache. */
5744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuurastatic void
5844a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuuratrace_cache_free (void *arg)
599e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura{
6044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  unw_trace_cache_t *cache = arg;
610a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma  if (++cache->dtor_count < PTHREAD_DESTRUCTOR_ITERATIONS)
620a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma  {
630a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma    /* Not yet our turn to get destroyed. Re-install ourselves into the key. */
640a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma    pthread_setspecific(trace_cache_key, cache);
650a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma    Debug(5, "delayed freeing cache %p (%zx to go)\n", cache,
660a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma	  PTHREAD_DESTRUCTOR_ITERATIONS - cache->dtor_count);
670a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma    return;
680a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma  }
6952ca68c770e9128940748bab72a5b103057166a8Arun Sharma  tls_cache_destroyed = 1;
7052ca68c770e9128940748bab72a5b103057166a8Arun Sharma  tls_cache = NULL;
71d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  munmap (cache->frames, (1u << cache->log_size) * sizeof(unw_tdep_frame_t));
7244a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  mempool_free (&trace_cache_pool, cache);
7344a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  Debug(5, "freed cache %p\n", cache);
7444a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura}
7544a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
7644a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura/* Initialise frame tracing for threaded use. */
7744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuurastatic void
7844a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuuratrace_cache_init_once (void)
7944a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura{
8044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  pthread_key_create (&trace_cache_key, &trace_cache_free);
81d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  mempool_init (&trace_cache_pool, sizeof (unw_trace_cache_t), 0);
8208077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma  trace_cache_once_happen = 1;
8344a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura}
8444a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
8544a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuurastatic unw_tdep_frame_t *
86d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuuratrace_cache_buckets (size_t n)
8744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura{
88d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  unw_tdep_frame_t *frames;
8944a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  size_t i;
9044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
91d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  GET_MEMORY(frames, n * sizeof (unw_tdep_frame_t));
92890e23eb9d3ffd9be2a025189a21794b5ed0e0ffTommi Rantala  if (likely(frames != NULL))
93d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura    for (i = 0; i < n; ++i)
9444a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura      frames[i] = empty_frame;
9544a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
9644a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  return frames;
9744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura}
9844a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
9944a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura/* Allocate and initialise hash table for frame cache lookups.
10044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura   Returns the cache initialised with (1u << HASH_LOW_BITS) hash
10144a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura   buckets, or NULL if there was a memory allocation problem. */
10244a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuurastatic unw_trace_cache_t *
10344a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuuratrace_cache_create (void)
10444a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura{
10544a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  unw_trace_cache_t *cache;
10644a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
1070a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma  if (tls_cache_destroyed)
1080a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma  {
1090a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma    /* The current thread is in the process of exiting. Don't recreate
1100a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma       cache, as we wouldn't have another chance to free it. */
1110a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma    Debug(5, "refusing to reallocate cache: "
1120a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma	     "thread-locals are being deallocated\n");
1130a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma    return NULL;
1140a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma  }
1150a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma
11644a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  if (! (cache = mempool_alloc(&trace_cache_pool)))
11744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  {
11844a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    Debug(5, "failed to allocate cache\n");
1190a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma    return NULL;
12044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  }
1219e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
122d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  if (! (cache->frames = trace_cache_buckets(1u << HASH_MIN_BITS)))
12344a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  {
12444a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    Debug(5, "failed to allocate buckets\n");
12544a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    mempool_free(&trace_cache_pool, cache);
1260a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma    return NULL;
12744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  }
1289e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
129d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  cache->log_size = HASH_MIN_BITS;
13044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  cache->used = 0;
1310a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma  cache->dtor_count = 0;
1320a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma  tls_cache_destroyed = 0;  /* Paranoia: should already be 0. */
13344a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  Debug(5, "allocated cache %p\n", cache);
13444a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  return cache;
1359e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura}
1369e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
13744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura/* Expand the hash table in the frame cache if possible. This always
13844a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura   quadruples the hash size, and clears all previous frame entries. */
13944a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuurastatic int
14044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuuratrace_cache_expand (unw_trace_cache_t *cache)
1419e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura{
142d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  size_t old_size = (1u << cache->log_size);
143d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  size_t new_log_size = cache->log_size + 2;
144d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  unw_tdep_frame_t *new_frames = trace_cache_buckets (1u << new_log_size);
145d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura
146d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  if (unlikely(! new_frames))
14744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  {
148d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura    Debug(5, "failed to expand cache to 2^%lu buckets\n", new_log_size);
14944a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    return -UNW_ENOMEM;
15044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  }
15144a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
152d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  Debug(5, "expanded cache from 2^%lu to 2^%lu buckets\n", cache->log_size, new_log_size);
153d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  munmap(cache->frames, old_size * sizeof(unw_tdep_frame_t));
154d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  cache->frames = new_frames;
155d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  cache->log_size = new_log_size;
156f1ea02be58c2127cc655ef3fd6a385a1bd22cdcdLassi Tuura  cache->used = 0;
1579e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  return 0;
1589e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura}
1599e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
16008077a4962c4e606598f9f0e54b515b3c882be10Arun Sharmastatic unw_trace_cache_t *
16108077a4962c4e606598f9f0e54b515b3c882be10Arun Sharmatrace_cache_get_unthreaded (void)
16208077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma{
16308077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma  unw_trace_cache_t *cache;
16408077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma  intrmask_t saved_mask;
165890e23eb9d3ffd9be2a025189a21794b5ed0e0ffTommi Rantala  static unw_trace_cache_t *global_cache = NULL;
16608077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma  lock_acquire (&trace_init_lock, saved_mask);
16708077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma  if (! global_cache)
16808077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma  {
16908077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma    mempool_init (&trace_cache_pool, sizeof (unw_trace_cache_t), 0);
17008077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma    global_cache = trace_cache_create ();
17108077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma  }
17208077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma  cache = global_cache;
17308077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma  lock_release (&trace_init_lock, saved_mask);
17408077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma  Debug(5, "using cache %p\n", cache);
17508077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma  return cache;
17608077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma}
17708077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma
17844a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura/* Get the frame cache for the current thread. Create it if there is none. */
17944a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuurastatic unw_trace_cache_t *
18044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuuratrace_cache_get (void)
18144a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura{
18244a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  unw_trace_cache_t *cache;
183890e23eb9d3ffd9be2a025189a21794b5ed0e0ffTommi Rantala  if (likely (pthread_once != NULL))
18444a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  {
18544a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    pthread_once(&trace_cache_once, &trace_cache_init_once);
18608077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma    if (!trace_cache_once_happen)
18708077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma    {
18808077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma      return trace_cache_get_unthreaded();
18908077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma    }
19015f182828d88ea51d419d94b3daabb7a69181c78Arun Sharma    if (! (cache = tls_cache))
19144a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    {
19244a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura      cache = trace_cache_create();
19344a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura      pthread_setspecific(trace_cache_key, cache);
19415f182828d88ea51d419d94b3daabb7a69181c78Arun Sharma      tls_cache = cache;
19544a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    }
19644a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    Debug(5, "using cache %p\n", cache);
19744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    return cache;
19844a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  }
19944a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  else
20044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  {
20108077a4962c4e606598f9f0e54b515b3c882be10Arun Sharma    return trace_cache_get_unthreaded();
20244a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  }
20344a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura}
20444a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
2059e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura/* Initialise frame properties for address cache slot F at address
2069e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   RIP using current CFA, RBP and RSP values.  Modifies CURSOR to
2079e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   that location, performs one unw_step(), and fills F with what
2089e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   was discovered about the location.  Returns F.
2099e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
2109e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   FIXME: This probably should tell DWARF handling to never evaluate
2119e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   or use registers other than RBP, RSP and RIP in case there is
2129e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   highly unusual unwind info which uses these creatively. */
2139e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuurastatic unw_tdep_frame_t *
2149e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuuratrace_init_addr (unw_tdep_frame_t *f,
2159e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura		 unw_cursor_t *cursor,
2169e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura		 unw_word_t cfa,
2179e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura		 unw_word_t rip,
2189e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura		 unw_word_t rbp,
2199e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura		 unw_word_t rsp)
2209e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura{
2219e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  struct cursor *c = (struct cursor *) cursor;
2229e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  struct dwarf_cursor *d = &c->dwarf;
2239e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  int ret = -UNW_EINVAL;
2249e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
2259e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  /* Initialise frame properties: unknown, not last. */
2269e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  f->virtual_address = rip;
2279e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  f->frame_type = UNW_X86_64_FRAME_OTHER;
2289e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  f->last_frame = 0;
2299e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  f->cfa_reg_rsp = -1;
2309e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  f->cfa_reg_offset = 0;
2319e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  f->rbp_cfa_offset = -1;
2329e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  f->rsp_cfa_offset = -1;
2339e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
2349e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  /* Reinitialise cursor to this instruction - but undo next/prev RIP
2359e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     adjustment because unw_step will redo it - and force RIP, RBP
2369e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     RSP into register locations (=~ ucontext we keep), then set
2379e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     their desired values. Then perform the step. */
2389e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  d->ip = rip + d->use_prev_instr;
2399e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  d->cfa = cfa;
2409e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  d->loc[UNW_X86_64_RIP] = DWARF_REG_LOC (d, UNW_X86_64_RIP);
2419e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  d->loc[UNW_X86_64_RBP] = DWARF_REG_LOC (d, UNW_X86_64_RBP);
2429e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  d->loc[UNW_X86_64_RSP] = DWARF_REG_LOC (d, UNW_X86_64_RSP);
2439e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  c->frame_info = *f;
2449e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
245ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura  if (likely(dwarf_put (d, d->loc[UNW_X86_64_RIP], rip) >= 0)
246ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura      && likely(dwarf_put (d, d->loc[UNW_X86_64_RBP], rbp) >= 0)
247ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura      && likely(dwarf_put (d, d->loc[UNW_X86_64_RSP], rsp) >= 0)
248ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura      && likely((ret = unw_step (cursor)) >= 0))
2499e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    *f = c->frame_info;
2509e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
2519e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  /* If unw_step() stopped voluntarily, remember that, even if it
2529e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     otherwise could not determine anything useful.  This avoids
2539e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     failing trace if we hit frames without unwind info, which is
2549e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     common for the outermost frame (CRT stuff) on many systems.
2559e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     This avoids failing trace in very common circumstances; failing
2569e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     to unw_step() loop wouldn't produce any better result. */
2579e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  if (ret == 0)
2589e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    f->last_frame = -1;
2599e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
2609e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  Debug (3, "frame va %lx type %d last %d cfa %s+%d rbp @ cfa%+d rsp @ cfa%+d\n",
2619e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura	 f->virtual_address, f->frame_type, f->last_frame,
2629e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura	 f->cfa_reg_rsp ? "rsp" : "rbp", f->cfa_reg_offset,
2639e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura	 f->rbp_cfa_offset, f->rsp_cfa_offset);
2649e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
2659e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  return f;
2669e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura}
2679e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
2689e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura/* Look up and if necessary fill in frame attributes for address RIP
2699e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   in CACHE using current CFA, RBP and RSP values.  Uses CURSOR to
2709e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   perform any unwind steps necessary to fill the cache.  Returns the
2719e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   frame cache slot which describes RIP. */
2729e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuurastatic unw_tdep_frame_t *
2739e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuuratrace_lookup (unw_cursor_t *cursor,
27444a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura	      unw_trace_cache_t *cache,
2759e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura	      unw_word_t cfa,
2769e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura	      unw_word_t rip,
2779e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura	      unw_word_t rbp,
2789e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura	      unw_word_t rsp)
2799e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura{
2809e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  /* First look up for previously cached information using cache as
2819e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     linear probing hash table with probe step of 1.  Majority of
2829e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     lookups should be completed within few steps, but it is very
2839e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     important the hash table does not fill up, or performance falls
2849e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     off the cliff. */
285d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  uint64_t i, addr;
286d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura  uint64_t cache_size = 1u << cache->log_size;
28744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  uint64_t slot = ((rip * 0x9e3779b97f4a7c16) >> 43) & (cache_size-1);
28844a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  unw_tdep_frame_t *frame;
2899e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
29044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  for (i = 0; i < 16; ++i)
2919e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  {
292d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura    frame = &cache->frames[slot];
29344a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    addr = frame->virtual_address;
2949e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
2959e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    /* Return if we found the address. */
296ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura    if (likely(addr == rip))
2979e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    {
2989e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      Debug (4, "found address after %ld steps\n", i);
29944a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura      return frame;
3009e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    }
3019e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3029e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    /* If slot is empty, reuse it. */
303ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura    if (likely(! addr))
3049e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      break;
3059e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3069e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    /* Linear probe to next slot candidate, step = 1. */
30744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    if (++slot >= cache_size)
3089e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      slot -= cache_size;
3099e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  }
3109e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
31144a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  /* If we collided after 16 steps, or if the hash is more than half
31244a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura     full, force the hash to expand. Fill the selected slot, whether
31344a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura     it's free or collides. Note that hash expansion drops previous
31444a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura     contents; further lookups will refill the hash. */
31544a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  Debug (4, "updating slot %lu after %ld steps, replacing 0x%lx\n", slot, i, addr);
316ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura  if (unlikely(addr || cache->used >= cache_size / 2))
31744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  {
318ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura    if (unlikely(trace_cache_expand (cache) < 0))
3190a26727ea2b3b9afd8d019a91777f350d06dd8dcArun Sharma      return NULL;
32044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
321d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura    cache_size = 1u << cache->log_size;
32244a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    slot = ((rip * 0x9e3779b97f4a7c16) >> 43) & (cache_size-1);
323d2525ec9369525c4d089eeffc84b417fd9c9aea7Lassi Tuura    frame = &cache->frames[slot];
32444a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    addr = frame->virtual_address;
32544a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  }
32644a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
32744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  if (! addr)
32844a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    ++cache->used;
32944a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
33044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  return trace_init_addr (frame, cursor, cfa, rip, rbp, rsp);
3319e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura}
3329e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3339e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura/* Fast stack backtrace for x86-64.
3349e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3355f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura   This is used by backtrace() implementation to accelerate frequent
3365f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura   queries for current stack, without any desire to unwind. It fills
3375f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura   BUFFER with the call tree from CURSOR upwards for at most SIZE
3385f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura   stack levels. The first frame, backtrace itself, is omitted. When
3395f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura   called, SIZE should give the maximum number of entries that can be
3405f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura   stored into BUFFER. Uses an internal thread-specific cache to
3415f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura   accelerate queries.
3429e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3439e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   The caller should fall back to a unw_step() loop if this function
3449e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   fails by returning -UNW_ESTOPUNWIND, meaning the routine hit a
3459e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   stack frame that is too complex to be traced in the fast path.
3469e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3479e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   This function is tuned for clients which only need to walk the
3489e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   stack to get the call tree as fast as possible but without any
3499e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   other details, for example profilers sampling the stack thousands
3509e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   to millions of times per second.  The routine handles the most
3519e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   common x86-64 ABI stack layouts: CFA is RBP or RSP plus/minus
3529e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   constant offset, return address is at CFA-8, and RBP and RSP are
3539e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   either unchanged or saved on stack at constant offset from the CFA;
3549e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   the signal return frame; and frames without unwind info provided
3559e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   they are at the outermost (final) frame or can conservatively be
3569e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   assumed to be frame-pointer based.
3579e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3585f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura   Any other stack layout will cause the routine to give up. There
3599e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   are only a handful of relatively rarely used functions which do
3609e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   not have a stack in the standard form: vfork, longjmp, setcontext
3619e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   and _dl_runtime_profile on common linux systems for example.
3629e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3639e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   On success BUFFER and *SIZE reflect the trace progress up to *SIZE
3649e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   stack levels or the outermost frame, which ever is less.  It may
3659e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   stop short of outermost frame if unw_step() loop would also do so,
3669e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   e.g. if there is no more unwind information; this is not reported
3679e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   as an error.
3689e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3699e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   The function returns a negative value for errors, -UNW_ESTOPUNWIND
3709e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   if tracing stopped because of an unusual frame unwind info.  The
3719e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   BUFFER and *SIZE reflect tracing progress up to the error frame.
3729e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3739e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura   Callers of this function would normally look like this:
3749e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3759e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     unw_cursor_t     cur;
3765f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura     unw_context_t    ctx;
3779e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     void             addrs[128];
3789e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     int              depth = 128;
3799e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     int              ret;
3809e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
3819e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     unw_getcontext(&ctx);
3829e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     unw_init_local(&cur, &ctx);
38344a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura     if ((ret = unw_tdep_trace(&cur, addrs, &depth)) < 0)
3849e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     {
3859e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura       depth = 0;
3865f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura       unw_getcontext(&ctx);
3875f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura       unw_init_local(&cur, &ctx);
3885f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura       while ((ret = unw_step(&cur)) > 0 && depth < 128)
3899e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura       {
3909e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura         unw_word_t ip;
3919e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura         unw_get_reg(&cur, UNW_REG_IP, &ip);
3929e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura         addresses[depth++] = (void *) ip;
3939e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura       }
3949e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     }
3959e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura*/
3965f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi TuuraHIDDEN int
3975f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuuratdep_trace (unw_cursor_t *cursor, void **buffer, int *size)
3989e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura{
3999e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  struct cursor *c = (struct cursor *) cursor;
4009e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  struct dwarf_cursor *d = &c->dwarf;
40144a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  unw_trace_cache_t *cache;
4029e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  unw_word_t rbp, rsp, rip, cfa;
4039e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  int maxdepth = 0;
4049e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  int depth = 0;
4059e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  int ret;
4069e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4079e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  /* Check input parametres. */
408ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura  if (unlikely(! cursor || ! buffer || ! size || (maxdepth = *size) <= 0))
4099e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    return -UNW_EINVAL;
4109e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4119e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  Debug (1, "begin ip 0x%lx cfa 0x%lx\n", d->ip, d->cfa);
4129e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4139e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  /* Tell core dwarf routines to call back to us. */
4149e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  d->stash_frames = 1;
4159e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4165c2cade264010c9855c4ea5effc5b4789739e7caLassi Tuura  /* Determine initial register values. These are direct access safe
4175c2cade264010c9855c4ea5effc5b4789739e7caLassi Tuura     because we know they come from the initial machine context. */
4189e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  rip = d->ip;
4199e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  rsp = cfa = d->cfa;
4205c2cade264010c9855c4ea5effc5b4789739e7caLassi Tuura  ACCESS_MEM_FAST(ret, 0, d, DWARF_GET_LOC(d->loc[UNW_X86_64_RBP]), rbp);
4215c2cade264010c9855c4ea5effc5b4789739e7caLassi Tuura  assert(ret == 0);
4229e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
42344a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  /* Get frame cache. */
424ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura  if (unlikely(! (cache = trace_cache_get())))
42544a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  {
42644a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    Debug (1, "returning %d, cannot get trace cache\n", -UNW_ENOMEM);
42744a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    *size = 0;
42844a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    d->stash_frames = 0;
42944a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura    return -UNW_ENOMEM;
43044a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  }
43144a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura
4329e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  /* Trace the stack upwards, starting from current RIP.  Adjust
4339e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     the RIP address for previous/next instruction as the main
4349e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     unwinding logic would also do.  We undo this before calling
4359e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura     back into unw_step(). */
4369e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  while (depth < maxdepth)
4379e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  {
4389e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    rip -= d->use_prev_instr;
4399e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    Debug (2, "depth %d cfa 0x%lx rip 0x%lx rsp 0x%lx rbp 0x%lx\n",
4409e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura	   depth, cfa, rip, rsp, rbp);
4419e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4429e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    /* See if we have this address cached.  If not, evaluate enough of
4439e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura       the dwarf unwind information to fill the cache line data, or to
4449e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura       decide this frame cannot be handled in fast trace mode.  We
4459e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura       cache negative results too to prevent unnecessary dwarf parsing
4469e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura       for common failures. */
4479e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    unw_tdep_frame_t *f = trace_lookup (cursor, cache, cfa, rip, rbp, rsp);
4489e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4499e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    /* If we don't have information for this frame, give up. */
450ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura    if (unlikely(! f))
4519e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    {
4529e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      ret = -UNW_ENOINFO;
4539e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      break;
4549e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    }
4559e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4569e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    Debug (3, "frame va %lx type %d last %d cfa %s+%d rbp @ cfa%+d rsp @ cfa%+d\n",
4579e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura           f->virtual_address, f->frame_type, f->last_frame,
4589e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura           f->cfa_reg_rsp ? "rsp" : "rbp", f->cfa_reg_offset,
4599e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura           f->rbp_cfa_offset, f->rsp_cfa_offset);
4609e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4619e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    assert (f->virtual_address == rip);
4629e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4639e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    /* Stop if this was the last frame.  In particular don't evaluate
4649e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura       new register values as it may not be safe - we don't normally
4659e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura       run with full validation on, and do not want to - and there's
4669e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura       enough bad unwind info floating around that we need to trust
4679e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura       what unw_step() previously said, in potentially bogus frames. */
4689e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    if (f->last_frame)
4699e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      break;
4709e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4719e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    /* Evaluate CFA and registers for the next frame. */
4729e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    switch (f->frame_type)
4739e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    {
4749e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    case UNW_X86_64_FRAME_GUESSED:
4759e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      /* Fall thru to standard processing after forcing validation. */
4769e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      c->validate = 1;
4779e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4789e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    case UNW_X86_64_FRAME_STANDARD:
4799e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      /* Advance standard traceable frame. */
4809e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      cfa = (f->cfa_reg_rsp ? rsp : rbp) + f->cfa_reg_offset;
481ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura      ACCESS_MEM_FAST(ret, c->validate, d, cfa - 8, rip);
482ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura      if (likely(ret >= 0) && likely(f->rbp_cfa_offset != -1))
483ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura	ACCESS_MEM_FAST(ret, c->validate, d, cfa + f->rbp_cfa_offset, rbp);
4849e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4859e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      /* Don't bother reading RSP from DWARF, CFA becomes new RSP. */
4869e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      rsp = cfa;
4879e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4889e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      /* Next frame needs to back up for unwind info lookup. */
4899e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      d->use_prev_instr = 1;
4909e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      break;
4919e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
4929e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    case UNW_X86_64_FRAME_SIGRETURN:
4931010880548589685a27b8f63ef54a3ea78e052fcArun Sharma      cfa = cfa + f->cfa_reg_offset; /* cfa now points to ucontext_t.  */
4941010880548589685a27b8f63ef54a3ea78e052fcArun Sharma
4951010880548589685a27b8f63ef54a3ea78e052fcArun Sharma      ACCESS_MEM_FAST(ret, c->validate, d, cfa + UC_MCONTEXT_GREGS_RIP, rip);
496ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura      if (likely(ret >= 0))
4971010880548589685a27b8f63ef54a3ea78e052fcArun Sharma        ACCESS_MEM_FAST(ret, c->validate, d, cfa + UC_MCONTEXT_GREGS_RBP, rbp);
498ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura      if (likely(ret >= 0))
4991010880548589685a27b8f63ef54a3ea78e052fcArun Sharma        ACCESS_MEM_FAST(ret, c->validate, d, cfa + UC_MCONTEXT_GREGS_RSP, rsp);
5009e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
5019e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      /* Resume stack at signal restoration point. The stack is not
5029e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura         necessarily continuous here, especially with sigaltstack(). */
5039e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      cfa = rsp;
5049e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
5059e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      /* Next frame should not back up. */
5069e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      d->use_prev_instr = 0;
5079e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      break;
5089e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
5099e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    default:
5109e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      /* We cannot trace through this frame, give up and tell the
5119e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura	 caller we had to stop.  Data collected so far may still be
5129e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura	 useful to the caller, so let it know how far we got.  */
5139e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      ret = -UNW_ESTOPUNWIND;
5149e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      break;
5159e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    }
5169e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
5179e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura    Debug (4, "new cfa 0x%lx rip 0x%lx rsp 0x%lx rbp 0x%lx\n",
5189e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura	   cfa, rip, rsp, rbp);
5199e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
520ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura    /* If we failed or ended up somewhere bogus, stop. */
521ae5c1f2adf4da04235d87d024d4d942c01b2b447Lassi Tuura    if (unlikely(ret < 0 || rip < 0x4000))
5229e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura      break;
5235f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura
5245f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura    /* Record this address in stack trace. We skipped the first address. */
5255f38f35d5d6c78aafa6da20845d9ceff74af00f8Lassi Tuura    buffer[depth++] = (void *) (rip - d->use_prev_instr);
5269e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  }
5279e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura
5289e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura#if UNW_DEBUG
52944a14d1364abff3dc7ec49f35b7220542c4cf2b1Lassi Tuura  Debug (1, "returning %d, depth %d\n", ret, depth);
5309e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura#endif
5319e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  *size = depth;
5329e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura  return ret;
5339e98f15e9aee12e67cd5956d06ccb559f6a06213Lassi Tuura}
534