/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ │vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright 2016 Google Inc. │ │ │ │ Licensed under the Apache License, Version 2.0 (the "License"); │ │ you may not use this file except in compliance with the License. │ │ You may obtain a copy of the License at │ │ │ │ http://www.apache.org/licenses/LICENSE-2.0 │ │ │ │ Unless required by applicable law or agreed to in writing, software │ │ distributed under the License is distributed on an "AS IS" BASIS, │ │ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ │ See the License for the specific language governing permissions and │ │ limitations under the License. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "third_party/nsync/atomic.h" #include "third_party/nsync/common.internal.h" #include "third_party/nsync/dll.h" #include "third_party/nsync/mu_semaphore.h" #include "third_party/nsync/races.internal.h" #include "third_party/nsync/wait_s.internal.h" asm(".ident\t\"\\n\\n\ *NSYNC (Apache 2.0)\\n\ Copyright 2016 Google, Inc.\\n\ https://github.com/google/nsync\""); // clang-format off /* Routines for debugging. */ /* An emit_buf represents a buffer into which debug information can be written. */ struct emit_buf { char *start; /* start of buffer */ int len; /* pength of buffer */ int pos; /* position of next character to bve written */ int overflow; /* non-zero iff buffer overflow has occurred */ }; /* Initialize *b to point to start[0, .., len-1], and return b. of to an internal static buffer if buf==NULL. */ static struct emit_buf *emit_init (struct emit_buf *b, char *start, int len) { b->start = start; b->len = len; b->pos = 0; b->overflow = 0; return (b); } /* Write character c to buffer *b. */ static void emit_c (struct emit_buf *b, int c) { if (b->pos < b->len) { b->start[b->pos++] = c; } else if (!b->overflow) { static const char suffix[] = "..."; const char *s = &suffix[sizeof (suffix)]; /* past nul */ char *p = &b->start[b->len]; /* past end */ while (s > suffix && p > b->start) { *--p = *--s; } b->overflow = 1; } } /* A printf-like function that writes to an emit_buf. It understands only the format specifiers %s (const char *), and %i (uintptr_t in hex), with no modifiers. */ static void emit_print (struct emit_buf *b, const char *fmt, ...) { va_list ap; va_start (ap, fmt); while (*fmt != 0) { int c = *fmt++; if (c != '%') { emit_c (b, c); } else { c = *fmt++; if (c == 's') { const char *s = va_arg (ap, const char *); while (*s != 0) { emit_c (b, *s++); } } else if (c == 'i') { uintptr_t n = va_arg (ap, uintptr_t); int i; for (i = 0; (n >> i) >= 0x10; i += 4) { } for (; i >= 0; i -= 4) { emit_c (b, "0123456789abcdef"[(n >> i) & 0xf]); } } else { ASSERT (0); } } } va_end (ap); } /* Map a bit in a uint32_t to a human-readable name. */ struct bit_name { uint32_t mask; const char *name; }; /* names for bits in a mu word */ static const struct bit_name mu_bit[] = { { MU_WLOCK, "wlock" }, { MU_SPINLOCK, "spin" }, { MU_WAITING, "wait" }, { MU_DESIG_WAKER, "desig" }, { MU_CONDITION, "cond" }, { MU_WRITER_WAITING, "writer" }, { MU_LONG_WAIT, "long" }, { MU_ALL_FALSE, "false" }, { 0, "" } /* sentinel */ }; /* names for bits in a cv word */ static const struct bit_name cv_bit[] = { { CV_SPINLOCK, "spin" }, { CV_NON_EMPTY, "wait" }, { 0, "" } /* sentinel */ }; /* names for bits in a waiter flags word */ static const struct bit_name waiter_flags_bit[] = { { WAITER_RESERVED, "rsrvd" }, { WAITER_IN_USE, "in_use" }, { 0, "" } /* sentinel */ }; /* Emit the names of bits in word to buffer *b using names[] */ static void emit_word (struct emit_buf *b, const struct bit_name *name, uint32_t word) { int i; for (i = 0; name[i].mask != 0; i++) { if ((word & name[i].mask) != 0) { emit_print (b, " %s", name[i].name); } } } /* Emit the waiter queue *q to *b. */ static void emit_waiters (struct emit_buf *b, nsync_dll_list_ list) { nsync_dll_element_ *p = nsync_dll_first_ (list); nsync_dll_element_ *next; if (p != NULL) { emit_print (b, "\nwaiters =\n"); } for (; p != NULL && !b->overflow; p = next) { struct nsync_waiter_s *nw = DLL_NSYNC_WAITER (p); waiter *w = DLL_WAITER (p); next = NULL; emit_print (b, " %i", (uintptr_t) w); if (w->tag != WAITER_TAG) { emit_print (b, "bad WAITER_TAG %i", (uintptr_t) w->tag); } else { next = nsync_dll_next_ (list, p); if (nw->tag != NSYNC_WAITER_TAG) { emit_print (b, " bad WAITER_TAG %i", (uintptr_t) nw->tag); } else { emit_print (b, " embedded=%i waiting=%i", (uintptr_t) (w->flags & NSYNC_WAITER_FLAG_MUCV), (uintptr_t) ATM_LOAD (&nw->waiting)); } emit_word (b, waiter_flags_bit, w->flags); emit_print (b, " %s removes=%i cond=(%i %i %i)", w->l_type == nsync_writer_type_? "writer" : w->l_type == nsync_reader_type_? "reader" : "??????", (uintptr_t) ATM_LOAD (&w->remove_count), (uintptr_t) w->cond.f, (uintptr_t) w->cond.v, (uintptr_t) w->cond.eq); if (w->same_condition.next != &w->same_condition) { emit_print (b, " same_as %i", (uintptr_t) DLL_WAITER_SAMECOND ( w->same_condition.next)); } } emit_c (b, '\n'); } } /* Emit to *b the state of *mu, and return a pointer to *b's buffer. If blocking!=0, print_waiters!=0, and *mu's waiter list is non-empty, the call will block until it can acquire the spinlock. If print_waiters!=0, the waiter list is printed. The spinlock is released before return if it was acquired. blocking==0 && print_waiters!=0 is unsafe and is intended for use within interactive debuggers. */ static char *emit_mu_state (struct emit_buf *b, nsync_mu *mu, int blocking, int print_waiters) { uintptr_t word; uintptr_t readers; int acquired = 0; IGNORE_RACES_START (); word = ATM_LOAD (&mu->word); if ((word & MU_WAITING) != 0 && print_waiters && /* can benefit from lock */ (blocking || (word & MU_SPINLOCK) == 0)) { /* willing, or no need to wait */ word = nsync_spin_test_and_set_ (&mu->word, MU_SPINLOCK, MU_SPINLOCK, 0); acquired = 1; } readers = word / MU_RLOCK; emit_print (b, "mu 0x%i -> 0x%i = {", (uintptr_t) mu, word); emit_word (b, mu_bit, word); if (readers != 0) { emit_print (b, " readers=0x%i", readers); } emit_print (b, " }"); if (print_waiters) { emit_waiters (b, mu->waiters); } if (acquired) { ATM_STORE_REL (&mu->word, word); /* release store */ } emit_c (b, 0); IGNORE_RACES_END (); return (b->start); } /* Emit to *b the state of *cv, and return a pointer to *b's buffer. If blocking!=0, print_waiters!=0, and *cv's waiter list is non-empty, the call will block until it can acquire the spinlock. If print_waiters!=0, the waiter list is printed. The spinlock is released before return if it was acquired. blocking==0 && print_waiters!=0 is unsafe and is intended for use within interactive debuggers. */ static char *emit_cv_state (struct emit_buf *b, nsync_cv *cv, int blocking, int print_waiters) { uintptr_t word; int acquired = 0; IGNORE_RACES_START (); word = ATM_LOAD (&cv->word); if ((word & CV_NON_EMPTY) != 0 && print_waiters && /* can benefit from lock */ (blocking || (word & CV_SPINLOCK) == 0)) { /* willing, or no need to wait */ word = nsync_spin_test_and_set_ (&cv->word, CV_SPINLOCK, CV_SPINLOCK, 0); acquired = 1; } emit_print (b, "cv 0x%i -> 0x%i = {", (uintptr_t) cv, word); emit_word (b, cv_bit, word); emit_print (b, " }"); if (print_waiters) { emit_waiters (b, cv->waiters); } if (acquired) { ATM_STORE_REL (&cv->word, word); /* release store */ } emit_c (b, 0); IGNORE_RACES_END (); return (b->start); } char *nsync_mu_debug_state (nsync_mu *mu, char *buf, int n) { struct emit_buf b; return (emit_mu_state (emit_init (&b, buf, n), mu, 0, 0)); } char *nsync_cv_debug_state (nsync_cv *cv, char *buf, int n) { struct emit_buf b; return (emit_cv_state (emit_init (&b, buf, n), cv, 0, 0)); } char *nsync_mu_debug_state_and_waiters (nsync_mu *mu, char *buf, int n) { struct emit_buf b; return (emit_mu_state (emit_init (&b, buf, n), mu, 1, 1)); } char *nsync_cv_debug_state_and_waiters (nsync_cv *cv, char *buf, int n) { struct emit_buf b; return (emit_cv_state (emit_init (&b, buf, n), cv, 1, 1)); } static char nsync_debug_buf[1024]; char *nsync_mu_debugger (nsync_mu *mu) { struct emit_buf b; return (emit_mu_state (emit_init (&b, nsync_debug_buf, (int) sizeof (nsync_debug_buf)), mu, 0, 1)); } char *nsync_cv_debugger (nsync_cv *cv) { struct emit_buf b; return (emit_cv_state (emit_init (&b, nsync_debug_buf, (int) sizeof (nsync_debug_buf)), cv, 0, 1)); }