Commit | Line | Data |
---|---|---|
791151d0 MD |
1 | /* |
2 | * urcu-checker.c | |
3 | * | |
4 | * Userspace RCU library checker | |
5 | * | |
6 | * Copyright (c) 2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
7 | * | |
8 | * This library is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License as published by the Free Software Foundation; either | |
11 | * version 2.1 of the License, or (at your option) any later version. | |
12 | * | |
13 | * This library is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * Lesser General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU Lesser General Public | |
19 | * License along with this library; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
21 | */ | |
22 | ||
8a325ad7 MD |
23 | /* |
24 | * NOTE: build application with -rdynamic -ldl -lurcu-common. | |
25 | */ | |
26 | ||
27 | #define _GNU_SOURCE | |
791151d0 MD |
28 | #include <string.h> |
29 | #include <assert.h> | |
30 | #include <stdlib.h> | |
31 | #include <stdio.h> | |
8a325ad7 MD |
32 | #include <execinfo.h> |
33 | #include <dlfcn.h> | |
34 | #include <sys/types.h> | |
791151d0 MD |
35 | #include <urcu/urcu-checker.h> |
36 | #include <urcu/tls-compat.h> | |
37 | ||
8a325ad7 MD |
38 | #define URCU_DEBUG_STACK_LEN 10 |
39 | #define DEFAULT_PRINT_BACKTRACE_LEN 5 | |
40 | #define BACKTRACE_LEN 16 | |
41 | ||
42 | #ifdef __linux__ | |
43 | #include <syscall.h> | |
44 | #endif | |
45 | ||
46 | #if defined(_syscall0) | |
47 | _syscall0(pid_t, gettid) | |
48 | #elif defined(__NR_gettid) | |
49 | #include <unistd.h> | |
50 | static inline pid_t gettid(void) | |
51 | { | |
52 | return syscall(__NR_gettid); | |
53 | } | |
54 | #else | |
55 | #include <sys/types.h> | |
56 | #include <unistd.h> | |
57 | ||
58 | /* Fall-back on getpid for tid if not available. */ | |
59 | static inline pid_t gettid(void) | |
60 | { | |
61 | return getpid(); | |
62 | } | |
63 | #endif | |
64 | ||
65 | #define err_printf(fmt, args...) \ | |
66 | fprintf(stderr, "[urcu-checker %ld/%ld] " fmt, \ | |
67 | (long) getpid(), (long) gettid(), ## args) | |
791151d0 MD |
68 | |
69 | struct urcu_debug_entry { | |
70 | void *ip; | |
71 | int depth; | |
72 | }; | |
73 | ||
74 | struct urcu_debug_stack { | |
75 | struct urcu_debug_entry stack[URCU_DEBUG_STACK_LEN]; | |
76 | int stackend; | |
77 | }; | |
78 | ||
8a325ad7 MD |
79 | struct backtrace { |
80 | void *ptrs[BACKTRACE_LEN]; | |
81 | char **symbols; | |
82 | }; | |
83 | ||
791151d0 MD |
84 | static DEFINE_URCU_TLS(struct urcu_debug_stack, rcu_debug_stack); |
85 | ||
8a325ad7 MD |
86 | static volatile int print_backtrace_len = DEFAULT_PRINT_BACKTRACE_LEN; |
87 | ||
88 | /* | |
89 | * Allocates a string, or NULL. | |
90 | */ | |
91 | static | |
92 | char *get_symbol(const void *caller) | |
93 | { | |
94 | Dl_info info; | |
95 | char *caller_symbol; | |
96 | ||
97 | if (caller && dladdr(caller, &info) && info.dli_sname) { | |
98 | caller_symbol = strdup(info.dli_sname); | |
99 | } else { | |
100 | caller_symbol = NULL; | |
101 | } | |
102 | return caller_symbol; | |
103 | } | |
104 | ||
105 | static inline __attribute__((always_inline)) | |
106 | void save_backtrace(struct backtrace *bt) | |
107 | { | |
108 | memset(bt, 0, sizeof(*bt)); | |
109 | (void) backtrace(bt->ptrs, BACKTRACE_LEN); | |
110 | bt->symbols = backtrace_symbols(bt->ptrs, BACKTRACE_LEN); | |
111 | } | |
112 | ||
113 | static | |
114 | void free_backtrace(struct backtrace *bt) | |
115 | { | |
116 | free(bt->symbols); | |
117 | } | |
118 | ||
119 | static | |
120 | void print_bt(struct backtrace *bt) | |
121 | { | |
122 | int j; | |
123 | unsigned int empty = 1; | |
124 | ||
125 | for (j = 0; j < BACKTRACE_LEN; j++) { | |
126 | if (bt->ptrs[j]) { | |
127 | empty = 0; | |
128 | break; | |
129 | } | |
130 | } | |
131 | if (empty) | |
132 | return; | |
133 | ||
134 | err_printf("[backtrace]\n"); | |
135 | for (j = 0; j < BACKTRACE_LEN && j < print_backtrace_len; j++) { | |
136 | if (!bt->ptrs[j]) | |
137 | continue; | |
138 | if (bt->symbols) | |
139 | err_printf(" %p <%s>\n", bt->ptrs[j], bt->symbols[j]); | |
140 | else | |
141 | err_printf(" %p\n", bt->ptrs[j]); | |
142 | } | |
143 | } | |
144 | ||
791151d0 MD |
145 | void rcu_read_lock_debug(void) |
146 | { | |
147 | struct urcu_debug_stack *r = &URCU_TLS(rcu_debug_stack); | |
148 | ||
149 | r->stack[r->stackend++].ip = __builtin_return_address(0); | |
150 | } | |
151 | ||
152 | void rcu_read_unlock_debug(void) | |
153 | { | |
154 | struct urcu_debug_stack *r = &URCU_TLS(rcu_debug_stack); | |
155 | ||
156 | assert(r->stackend != 0); | |
157 | r->stack[--r->stackend].ip = NULL; | |
158 | } | |
159 | ||
160 | void rcu_read_ongoing_check_debug(const char *func) | |
161 | { | |
162 | struct urcu_debug_stack *r = &URCU_TLS(rcu_debug_stack); | |
163 | ||
164 | if (r->stackend == 0) { | |
8a325ad7 | 165 | struct backtrace bt; |
7f0b004d | 166 | char *func; |
8a325ad7 | 167 | |
7f0b004d | 168 | func = get_symbol(__builtin_return_address(0)); |
8a325ad7 | 169 | err_printf("rcu_dereference() used outside of critical section at %p <%s>\n", |
7f0b004d | 170 | __builtin_return_address(0), func); |
8a325ad7 MD |
171 | save_backtrace(&bt); |
172 | print_bt(&bt); | |
173 | free_backtrace(&bt); | |
7f0b004d | 174 | free(func); |
791151d0 MD |
175 | } |
176 | } |