1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2011 Google, Inc
3	Contributed by Paul Pluzhnikov <ppluzhnikov@google.com>
4
5Permission is hereby granted, free of charge, to any person obtaining
6a copy of this software and associated documentation files (the
7"Software"), to deal in the Software without restriction, including
8without limitation the rights to use, copy, modify, merge, publish,
9distribute, sublicense, and/or sell copies of the Software, and to
10permit persons to whom the Software is furnished to do so, subject to
11the following conditions:
12
13The above copyright notice and this permission notice shall be
14included in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
23
24#define UNW_LOCAL_ONLY
25#include <libunwind.h>
26
27#include <unistd.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <dlfcn.h>
31#include <pthread.h>
32
33#define panic(args...)				\
34	{ fprintf (stderr, args); exit (-1); }
35
36int num_mallocs;
37int num_callocs;
38int in_unwind;
39
40void *
41calloc(size_t n, size_t s)
42{
43  static void * (*func)(size_t, size_t);
44
45#ifdef __GLIBC__
46  /* In glibc, dlsym() calls calloc. Calling dlsym(RTLD_NEXT, "calloc") here
47     causes infinite recursion.  Instead, we simply use it by its other
48     name.  */
49  extern void *__libc_calloc(size_t, size_t);
50  if (!func)
51    func = &__libc_calloc;
52#else
53  if(!func)
54    func = dlsym(RTLD_NEXT, "calloc");
55#endif
56
57  if (in_unwind) {
58    num_callocs++;
59    return NULL;
60  } else {
61    return func(n, s);
62  }
63}
64
65void *
66malloc(size_t s)
67{
68  static void * (*func)(size_t);
69
70  if(!func)
71    func = dlsym(RTLD_NEXT, "malloc");
72
73  if (in_unwind) {
74    num_mallocs++;
75    return NULL;
76  } else {
77    return func(s);
78  }
79}
80
81static void
82do_backtrace (void)
83{
84  const int num_levels = 100;
85  void *pc[num_levels];
86
87  in_unwind = 1;
88  unw_backtrace(pc, num_levels);
89  in_unwind = 0;
90}
91
92void
93foo3 (void)
94{
95  do_backtrace ();
96}
97
98void
99foo2 (void)
100{
101  foo3 ();
102}
103
104void
105foo1 (void)
106{
107  foo2 ();
108}
109
110int
111main (void)
112{
113  int i, num_errors;
114
115  /* Create (and leak) 100 TSDs, then call backtrace()
116     and check that it doesn't call malloc()/calloc().  */
117  for (i = 0; i < 100; ++i) {
118    pthread_key_t key;
119    if (pthread_key_create (&key, NULL))
120      panic ("FAILURE: unable to create key %d\n", i);
121  }
122  /* Call backtrace right after thread creation,
123   * where we are sure that we're not inside malloc */
124  do_backtrace();
125  num_mallocs = num_callocs = 0;
126  foo1 ();
127  num_errors = num_mallocs + num_callocs;
128  if (num_errors > 0)
129    {
130      fprintf (stderr,
131	       "FAILURE: detected %d error%s (malloc: %d, calloc: %d)\n",
132	       num_errors, num_errors > 1 ? "s" : "",
133	       num_mallocs, num_callocs);
134      exit (-1);
135    }
136  return 0;
137}
138