1/* Copyright 2016 Google Inc.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License. */
14
15/* Routines for debugging. */
16
17#include "nsync_cpp.h"
18#include "platform.h"
19#include "compiler.h"
20#include "cputype.h"
21#include "nsync.h"
22#include "dll.h"
23#include "sem.h"
24#include "wait_internal.h"
25#include "common.h"
26#include "atomic.h"
27
28NSYNC_CPP_START_
29
30/* ---------- */
31
32/* An emit_buf represents a buffer into which debug information can
33 be written. */
34struct emit_buf {
35 char *start; /* start of buffer */
36 int len; /* pength of buffer */
37 int pos; /* position of next character to bve written */
38 int overflow; /* non-zero iff buffer overflow has occurred */
39};
40
41/* Initialize *b to point to start[0, .., len-1], and return b.
42 of to an internal static buffer if buf==NULL. */
43static struct emit_buf *emit_init (struct emit_buf *b, char *start, int len) {
44 b->start = start;
45 b->len = len;
46 b->pos = 0;
47 b->overflow = 0;
48 return (b);
49}
50
51
52/* Write character c to buffer *b. */
53static void emit_c (struct emit_buf *b, int c) {
54 if (b->pos < b->len) {
55 b->start[b->pos++] = c;
56 } else if (!b->overflow) {
57 static const char suffix[] = "...";
58 const char *s = &suffix[sizeof (suffix)]; /* past nul */
59 char *p = &b->start[b->len]; /* past end */
60 while (s > suffix && p > b->start) {
61 *--p = *--s;
62 }
63 b->overflow = 1;
64 }
65}
66
67/* A printf-like function that writes to an emit_buf.
68 It understands only the format specifiers %s (const char *), and %i
69 (uintptr_t in hex), with no modifiers. */
70static void emit_print (struct emit_buf *b, const char *fmt, ...) {
71 va_list ap;
72 va_start (ap, fmt);
73 while (*fmt != 0) {
74 int c = *fmt++;
75 if (c != '%') {
76 emit_c (b, c);
77 } else {
78 c = *fmt++;
79 if (c == 's') {
80 const char *s = va_arg (ap, const char *);
81 while (*s != 0) {
82 emit_c (b, *s++);
83 }
84 } else if (c == 'i') {
85 uintptr_t n = va_arg (ap, uintptr_t);
86 int i;
87 for (i = 0; (n >> i) >= 0x10; i += 4) {
88 }
89 for (; i >= 0; i -= 4) {
90 emit_c (b, "0123456789abcdef"[(n >> i) & 0xf]);
91 }
92 } else {
93 ASSERT (0);
94 }
95 }
96 }
97 va_end (ap);
98}
99
100/* Map a bit in a uint32_t to a human-readable name. */
101struct bit_name {
102 uint32_t mask;
103 const char *name;
104};
105
106/* names for bits in a mu word */
107static const struct bit_name mu_bit[] = {
108 { MU_WLOCK, "wlock" },
109 { MU_SPINLOCK, "spin" },
110 { MU_WAITING, "wait" },
111 { MU_DESIG_WAKER, "desig" },
112 { MU_CONDITION, "cond" },
113 { MU_WRITER_WAITING, "writer" },
114 { MU_LONG_WAIT, "long" },
115 { MU_ALL_FALSE, "false" },
116 { 0, "" } /* sentinel */
117};
118
119/* names for bits in a cv word */
120static const struct bit_name cv_bit[] = {
121 { CV_SPINLOCK, "spin" },
122 { CV_NON_EMPTY, "wait" },
123 { 0, "" } /* sentinel */
124};
125
126/* names for bits in a waiter flags word */
127static const struct bit_name waiter_flags_bit[] = {
128 { WAITER_RESERVED, "rsrvd" },
129 { WAITER_IN_USE, "in_use" },
130 { 0, "" } /* sentinel */
131};
132
133/* Emit the names of bits in word to buffer *b using names[] */
134static void emit_word (struct emit_buf *b, const struct bit_name *name, uint32_t word) {
135 int i;
136 for (i = 0; name[i].mask != 0; i++) {
137 if ((word & name[i].mask) != 0) {
138 emit_print (b, " %s", name[i].name);
139 }
140 }
141}
142
143/* Emit the waiter queue *q to *b. */
144static void emit_waiters (struct emit_buf *b, nsync_dll_list_ list) {
145 nsync_dll_element_ *p = nsync_dll_first_ (list);
146 nsync_dll_element_ *next;
147 if (p != NULL) {
148 emit_print (b, "\nwaiters =\n");
149 }
150 for (; p != NULL && !b->overflow; p = next) {
151 struct nsync_waiter_s *nw = DLL_NSYNC_WAITER (p);
152 waiter *w = DLL_WAITER (p);
153 next = NULL;
154 emit_print (b, " %i", (uintptr_t) w);
155 if (w->tag != WAITER_TAG) {
156 emit_print (b, "bad WAITER_TAG %i",
157 (uintptr_t) w->tag);
158 } else {
159 next = nsync_dll_next_ (list, p);
160 if (nw->tag != NSYNC_WAITER_TAG) {
161 emit_print (b, " bad WAITER_TAG %i",
162 (uintptr_t) nw->tag);
163 } else {
164 emit_print (b, " embedded=%i waiting=%i",
165 (uintptr_t) (w->flags & NSYNC_WAITER_FLAG_MUCV),
166 (uintptr_t) ATM_LOAD (&nw->waiting));
167 }
168 emit_word (b, waiter_flags_bit, w->flags);
169 emit_print (b, " %s removes=%i cond=(%i %i %i)",
170 w->l_type == nsync_writer_type_? "writer" :
171 w->l_type == nsync_reader_type_? "reader" :
172 "??????",
173 (uintptr_t) ATM_LOAD (&w->remove_count),
174 (uintptr_t) w->cond.f,
175 (uintptr_t) w->cond.v,
176 (uintptr_t) w->cond.eq);
177 if (w->same_condition.next != &w->same_condition) {
178 emit_print (b, " same_as %i",
179 (uintptr_t) DLL_WAITER_SAMECOND (
180 w->same_condition.next));
181 }
182 }
183 emit_c (b, '\n');
184 }
185}
186
187/* Emit to *b the state of *mu, and return a pointer to *b's buffer.
188
189 If blocking!=0, print_waiters!=0, and *mu's waiter list is non-empty, the
190 call will block until it can acquire the spinlock.
191 If print_waiters!=0, the waiter list is printed.
192 The spinlock is released before return if it was acquired.
193 blocking==0 && print_waiters!=0 is unsafe and is intended for use within
194 interactive debuggers. */
195static char *emit_mu_state (struct emit_buf *b, nsync_mu *mu,
196 int blocking, int print_waiters) {
197 uintptr_t word;
198 uintptr_t readers;
199 int acquired = 0;
200 IGNORE_RACES_START ();
201 word = ATM_LOAD (&mu->word);
202 if ((word & MU_WAITING) != 0 && print_waiters && /* can benefit from lock */
203 (blocking || (word & MU_SPINLOCK) == 0)) { /* willing, or no need to wait */
204 word = nsync_spin_test_and_set_ (&mu->word, MU_SPINLOCK, MU_SPINLOCK, 0);
205 acquired = 1;
206 }
207 readers = word / MU_RLOCK;
208 emit_print (b, "mu 0x%i -> 0x%i = {", (uintptr_t) mu, word);
209 emit_word (b, mu_bit, word);
210 if (readers != 0) {
211 emit_print (b, " readers=0x%i", readers);
212 }
213 emit_print (b, " }");
214 if (print_waiters) {
215 emit_waiters (b, mu->waiters);
216 }
217 if (acquired) {
218 ATM_STORE_REL (&mu->word, word); /* release store */
219 }
220 emit_c (b, 0);
221 IGNORE_RACES_END ();
222 return (b->start);
223}
224
225/* Emit to *b the state of *cv, and return a pointer to *b's buffer.
226
227 If blocking!=0, print_waiters!=0, and *cv's waiter list is non-empty, the
228 call will block until it can acquire the spinlock.
229 If print_waiters!=0, the waiter list is printed.
230 The spinlock is released before return if it was acquired.
231 blocking==0 && print_waiters!=0 is unsafe and is intended for use within
232 interactive debuggers. */
233static char *emit_cv_state (struct emit_buf *b, nsync_cv *cv,
234 int blocking, int print_waiters) {
235 uintptr_t word;
236 int acquired = 0;
237 IGNORE_RACES_START ();
238 word = ATM_LOAD (&cv->word);
239 if ((word & CV_NON_EMPTY) != 0 && print_waiters && /* can benefit from lock */
240 (blocking || (word & CV_SPINLOCK) == 0)) { /* willing, or no need to wait */
241 word = nsync_spin_test_and_set_ (&cv->word, CV_SPINLOCK, CV_SPINLOCK, 0);
242 acquired = 1;
243 }
244 emit_print (b, "cv 0x%i -> 0x%i = {", (uintptr_t) cv, word);
245 emit_word (b, cv_bit, word);
246 emit_print (b, " }");
247 if (print_waiters) {
248 emit_waiters (b, cv->waiters);
249 }
250 if (acquired) {
251 ATM_STORE_REL (&cv->word, word); /* release store */
252 }
253 emit_c (b, 0);
254 IGNORE_RACES_END ();
255 return (b->start);
256}
257
258char *nsync_mu_debug_state (nsync_mu *mu, char *buf, int n) {
259 struct emit_buf b;
260 return (emit_mu_state (emit_init (&b, buf, n), mu, 0, 0));
261}
262
263char *nsync_cv_debug_state (nsync_cv *cv, char *buf, int n) {
264 struct emit_buf b;
265 return (emit_cv_state (emit_init (&b, buf, n), cv, 0, 0));
266}
267
268char *nsync_mu_debug_state_and_waiters (nsync_mu *mu, char *buf, int n) {
269 struct emit_buf b;
270 return (emit_mu_state (emit_init (&b, buf, n), mu, 1, 1));
271}
272
273char *nsync_cv_debug_state_and_waiters (nsync_cv *cv, char *buf, int n) {
274 struct emit_buf b;
275 return (emit_cv_state (emit_init (&b, buf, n), cv, 1, 1));
276}
277
278static char nsync_debug_buf[1024];
279
280char *nsync_mu_debugger (nsync_mu *mu) {
281 struct emit_buf b;
282 return (emit_mu_state (emit_init (&b, nsync_debug_buf,
283 (int) sizeof (nsync_debug_buf)),
284 mu, 0, 1));
285}
286char *nsync_cv_debugger (nsync_cv *cv) {
287 struct emit_buf b;
288 return (emit_cv_state (emit_init (&b, nsync_debug_buf,
289 (int) sizeof (nsync_debug_buf)),
290 cv, 0, 1));
291}
292
293NSYNC_CPP_END_
294