/* $OpenBSD: uthread_info_openbsd.c,v 1.15 2007/05/21 16:50:36 kurt Exp $ */ /* * Copyright (c) 1995-1998 John Birrell * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #ifdef _THREAD_SAFE #include #include "pthread_private.h" int _thread_dump_verbose = 0; struct s_thread_info { enum pthread_state state; const char *name; }; /* Static variables: */ static const struct s_thread_info thread_info[] = { {PS_RUNNING , "running"}, {PS_SIGTHREAD , "sigthread"}, {PS_MUTEX_WAIT , "mutex_wait"}, {PS_COND_WAIT , "cond_wait"}, {PS_FDLR_WAIT , "fdlr_wait"}, {PS_FDLW_WAIT , "fdlw_wait"}, {PS_FDR_WAIT , "fdr_wait"}, {PS_FDW_WAIT , "fdw_wait"}, {PS_FILE_WAIT , "file_wait"}, {PS_POLL_WAIT , "poll_wait"}, {PS_SELECT_WAIT , "select_wait"}, {PS_SLEEP_WAIT , "sleep_wait"}, {PS_WAIT_WAIT , "wait_wait"}, {PS_SIGSUSPEND , "sigsuspend"}, {PS_SIGWAIT , "sigwait"}, {PS_SPINBLOCK , "spinblock"}, {PS_JOIN , "join"}, {PS_SUSPENDED , "suspended"}, {PS_DEAD , "dead"}, {PS_DEADLOCK , "deadlock"}, {(enum pthread_state)0, ""}, }; #define writestring(fd, s) _thread_sys_write(fd, s, (sizeof s) - 1) const static char info_lead[] = " -"; /* Determine a filename for display purposes: */ static const char * truncname(const char *name, int maxlen) { int len; if (name == NULL) name = "(null)"; len = (int)strlen(name); if (len > maxlen) return name + len - maxlen; else return name; } static void _thread_dump_entry(pthread_t pthread, int fd, int verbose) { const char *state; char s[512]; char location[30]; unsigned int j; /* Find last known file:line checkpoint: */ if (pthread->fname && pthread->state != PS_RUNNING) snprintf(location, sizeof location, "%s:%d", truncname(pthread->fname, 21), pthread->lineno); else location[0] = '\0'; /* Find the state: */ for (j = 0; j < (sizeof(thread_info) / sizeof(struct s_thread_info)) - 1; j++) if (thread_info[j].state == pthread->state) break; state = thread_info[j].name; /* Output a record for the current thread: */ s[0] = 0; snprintf(s, sizeof(s), " %8p%c%-11s %2d %c%c%c%c%c%c%c%c%c%c %04x %-8.8s %s\n", (void *)pthread, (pthread == _thread_run) ? '*' : ' ', state, pthread->active_priority, (pthread->flags & PTHREAD_FLAGS_PRIVATE) ? 'p' : '-', (pthread->flags & PTHREAD_EXITING) ? 'E' : (pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) ? 'c' : '-', (pthread->flags & PTHREAD_FLAGS_TRACE) ? 't' : '-', (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) ? 'C' : '-', (pthread->flags & PTHREAD_FLAGS_IN_WORKQ) ? 'R' : '-', (pthread->flags & PTHREAD_FLAGS_IN_WAITQ) ? 'W' : '-', (pthread->flags & PTHREAD_FLAGS_IN_PRIOQ) ? 'P' : '-', (pthread->attr.flags & PTHREAD_DETACHED) ? 'd' : '-', (pthread->attr.flags & PTHREAD_INHERIT_SCHED) ? 'i' : '-', (pthread->attr.flags & PTHREAD_NOFLOAT) ? '-' : 'f', ((unsigned int)pthread->sigmask & 0xffff), (pthread->name == NULL) ? "" : pthread->name, (verbose) ? location : "" ); _thread_sys_write(fd, s, strlen(s)); if (!verbose) return; /* Show the scheduler wake and time slice properties */ snprintf(s, sizeof(s), "%s sched wake ", info_lead); _thread_sys_write(fd, s, strlen(s)); if (pthread->wakeup_time.tv_sec == -1) writestring(fd, "- slice "); else { struct timeval now; struct timespec nows, delta; gettimeofday(&now, NULL); TIMEVAL_TO_TIMESPEC(&now, &nows); timespecsub(&pthread->wakeup_time, &nows, &delta); snprintf(s, sizeof s, "%d.%09ld slice ", delta.tv_sec, delta.tv_nsec); _thread_sys_write(fd, s, strlen(s)); } if (pthread->slice_usec == -1) writestring(fd, "-\n"); else { snprintf(s, sizeof s, "%ld.%06ld\n", pthread->slice_usec / 1000000, pthread->slice_usec % 1000000); _thread_sys_write(fd, s, strlen(s)); } /* Process according to thread state: */ switch (pthread->state) { /* File descriptor read lock wait: */ case PS_FDLR_WAIT: case PS_FDLW_WAIT: case PS_FDR_WAIT: case PS_FDW_WAIT: /* Write the lock details: */ snprintf(s, sizeof(s), "%s fd %d [%s:%d]\n", info_lead, pthread->data.fd.fd, truncname(pthread->data.fd.fname, 32), pthread->data.fd.branch); _thread_sys_write(fd, s, strlen(s)); s[0] = 0; if (_thread_fd_table[pthread->data.fd.fd] && _thread_fd_table[pthread->data.fd.fd]->state != FD_ENTRY_CLOSED) snprintf(s, sizeof(s), "%s owner %pr/%pw\n", info_lead, _thread_fd_table[pthread->data.fd.fd]->r_owner, _thread_fd_table[pthread->data.fd.fd]->w_owner); else snprintf(s, sizeof(s), "%s owner [unknown]\n", info_lead); _thread_sys_write(fd, s, strlen(s)); break; case PS_SIGWAIT: snprintf(s, sizeof(s), "%s sigmask 0x%08lx\n", info_lead, (unsigned long)pthread->sigmask); _thread_sys_write(fd, s, strlen(s)); break; case PS_MUTEX_WAIT: snprintf(s, sizeof(s), "%s mutex %p\n", info_lead, pthread->data.mutex); _thread_sys_write(fd, s, strlen(s)); if (pthread->data.mutex) { snprintf(s, sizeof(s), "%s owner %p\n", info_lead, pthread->data.mutex->m_owner); _thread_sys_write(fd, s, strlen(s)); } break; case PS_COND_WAIT: snprintf(s, sizeof(s), "%s cond %p\n", info_lead, pthread->data.cond); _thread_sys_write(fd, s, strlen(s)); break; #ifdef notyet case PS_JOIN: { struct pthread *t, * volatile *last; pthread_entry_t *e; /* Find the end of the list: */ for (e = &pthread->qe; e->tqe_next != NULL; e = &e->tqe_next->qe) ; last = &e->tqe_next; /* Walk backwards to the head: */ for (e = &pthread->qe; ((_thread_list_t *)e)->tqh_last != last; e = (pthread_entry_t *)e->tqe_prev) ; /* Convert the head address into a thread address: */ t = (pthread_t)((caddr_t)e - offsetof(struct pthread, join_queue)); snprintf(s, sizeof(s), "%s thread %p\n", info_lead, t); _thread_sys_write(fd, s, strlen(s)); } break; #endif case PS_SLEEP_WAIT: { struct timeval tv; struct timespec current_time; struct timespec remaining_time; double remain; gettimeofday(&tv, NULL); TIMEVAL_TO_TIMESPEC(&tv, ¤t_time); timespecsub(&pthread->wakeup_time, ¤t_time, &remaining_time); remain = remaining_time.tv_sec + (double)remaining_time.tv_nsec / 1e9; snprintf(s, sizeof(s), "%s wake in %f sec\n", info_lead, remain); _thread_sys_write(fd, s, strlen(s)); } break; case PS_SELECT_WAIT: case PS_POLL_WAIT: { nfds_t i; for (i = 0; i < pthread->data.poll_data->nfds; i++) snprintf(s, sizeof(s), "%s%d:%s%s", i ? " " : "", pthread->data.poll_data->fds[i].fd, pthread->data.poll_data->fds[i].events & POLLIN ? "r" : "", pthread->data.poll_data->fds[i].events & POLLOUT ? "w" : "" ); snprintf(s, sizeof(s), "\n"); } break; default: /* Nothing to do here. */ break; } } void _thread_dump_info(void) { char s[512]; int fd; int i; pthread_t pthread; pq_list_t * pq_list; int verbose; verbose = _thread_dump_verbose; if (getenv("PTHREAD_DEBUG") != NULL) verbose = 1; /* Open the controlling tty: */ fd = _thread_sys_open(_PATH_TTY, O_WRONLY | O_APPEND); if (fd < 0) return; if (!verbose) { /* Display only a very brief list of threads */ TAILQ_FOREACH(pthread, &_thread_list, tle) if ((pthread->flags & PTHREAD_FLAGS_PRIVATE) == 0) _thread_dump_entry(pthread, fd, 0); return; } /* Display a list of active threads: */ snprintf(s, sizeof s, " %8s%c%-11s %2s %-10s %4s %-8s %s\n", "id", ' ', "state", "pr", "flags", "mask", "name", "location"); _thread_sys_write(fd, s, strlen(s)); writestring(fd, "active:\n"); TAILQ_FOREACH(pthread, &_thread_list, tle) _thread_dump_entry(pthread, fd, 1); writestring(fd, "ready:\n"); TAILQ_FOREACH (pq_list, &_readyq.pq_queue, pl_link) TAILQ_FOREACH(pthread, &pq_list->pl_head, pqe) _thread_dump_entry(pthread, fd, 0); writestring(fd, "waiting:\n"); TAILQ_FOREACH (pthread, &_waitingq, pqe) _thread_dump_entry(pthread, fd, 0); writestring(fd, "workq:\n"); TAILQ_FOREACH (pthread, &_workq, qe) _thread_dump_entry(pthread, fd, 0); writestring(fd, "dead:\n"); TAILQ_FOREACH(pthread, &_dead_list, dle) _thread_dump_entry(pthread, fd, 1); /* Output a header for file descriptors: */ snprintf(s, sizeof(s), "file descriptor table, size %d:\n", _thread_max_fdtsize); _thread_sys_write(fd, s, strlen(s)); snprintf(s, sizeof s, " %3s %8s %4s %21s %8s %4s %21s\n", "fd", "rdowner", "rcnt", "rdcode", "wrowner", "wcnt", "wrcode"); _thread_sys_write(fd, s, strlen(s)); /* Enter a loop to report file descriptor lock usage: */ for (i = 0; i < _thread_max_fdtsize; i++) { /* * Check if memory is allocated for this file * descriptor: */ char rcode[22], wcode[22]; if (_thread_fd_table[i] != NULL && _thread_fd_table[i]->state != FD_ENTRY_CLOSED) { /* Find the reader's last file:line: */ if (_thread_fd_table[i]->r_owner) snprintf(rcode, sizeof rcode, "%s:%d", truncname(_thread_fd_table[i]->r_fname, 16), _thread_fd_table[i]->r_lineno); else rcode[0] = '\0'; /* Find the writer's last file:line: */ if (_thread_fd_table[i]->w_owner) snprintf(wcode, sizeof wcode, "%s:%d", truncname(_thread_fd_table[i]->w_fname, 16), _thread_fd_table[i]->w_lineno); else wcode[0] = '\0'; /* Report the file descriptor lock status: */ snprintf(s, sizeof(s), " %3d %8p %4d %21s %8p %4d %21s\n", i, _thread_fd_table[i]->r_owner, _thread_fd_table[i]->r_lockcount, rcode, _thread_fd_table[i]->w_owner, _thread_fd_table[i]->w_lockcount, wcode ); _thread_sys_write(fd, s, strlen(s)); } } /* Close the dump file: */ _thread_sys_close(fd); return; } /* * Generic "dump some data to /dev/tty in hex format" function * output format is: * 0 22 48 * 0x0123456789abcdef: 00 11 22 33 44 55 66 77 01234567 */ #define DUMP_BUFLEN 84 #define DUMP_HEX_OFF 22 #define DUMP_ASCII_OFF 48 void _thread_dump_data(const void *addr, int len) { int fd = -1; unsigned char data[DUMP_BUFLEN]; const unsigned char hexdigits[] = "0123456789abcdef"; if (getenv("PTHREAD_DEBUG") != NULL) fd = _thread_sys_open(_PATH_TTY, O_WRONLY | O_APPEND); if (fd != -1) { memset(data, ' ', DUMP_BUFLEN); while (len) { const unsigned char *d; unsigned char *h; unsigned char *a; int count; d = addr; h = &data[DUMP_HEX_OFF]; a = &data[DUMP_ASCII_OFF]; if (len > 8) { count = 8; len -= 8; } else { count = len; len = 0; memset(data, ' ', DUMP_BUFLEN); } addr = (char *)addr + 8; snprintf((char *)data, DUMP_BUFLEN, "%18p: ", d); while (count--) { if (isprint(*d)) *a++ = *d; else *a++ = '.'; *h++ = hexdigits[(*d >> 4) & 0xf]; *h++ = hexdigits[*d++ & 0xf]; *h++ = ' '; } *a++ = '\n'; *a = 0; _thread_sys_write(fd, data, (size_t)(a - data)); } writestring(fd, "\n"); _thread_sys_close(fd); } } /* Set the thread name for debug: */ void pthread_set_name_np(pthread_t thread, const char *name) { /* Check if the caller has specified a valid thread: */ if (thread != NULL && thread->magic == PTHREAD_MAGIC) { if (thread->name != NULL) free(thread->name); thread->name = strdup(name); } return; } #endif