From 0693f6fc7590923ba0c837cfb5f65c9ad02c0f04 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 15 Jun 2017 14:24:51 +0000 Subject: [PATCH 1/5] lib: un-static trash buffer for pipe poker data races Signed-off-by: Quentin Young --- lib/thread.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/thread.c b/lib/thread.c index a1a9e7c359..335e7fc044 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -676,7 +676,7 @@ fd_poll (struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize, num = poll (pfds, count + 1, timeout); - static unsigned char trash[64]; + unsigned char trash[64]; if (num > 0 && pfds[count].revents != 0 && num--) while (read (m->io_pipe[0], &trash, sizeof (trash)) > 0); From e0bebc7c223abf239b5ccda1c2de42a2b0410455 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 15 Jun 2017 16:05:19 +0000 Subject: [PATCH 2/5] lib: mt-safe tracebacks can't be using them statics anymore sonny Signed-off-by: Quentin Young --- lib/log.c | 19 ++++++++++++------- lib/thread.c | 29 +++++++++++++++++------------ lib/thread.h | 2 +- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/lib/log.c b/lib/log.c index a8b221fd64..1c61d72168 100644 --- a/lib/log.c +++ b/lib/log.c @@ -509,16 +509,18 @@ zlog_signal(int signo, const char *action ); s = buf; - if (!thread_current) + struct thread *tc; + tc = pthread_getspecific (thread_current); + if (!tc) s = str_append (LOC, "no thread information available\n"); else { s = str_append (LOC, "in thread "); - s = str_append (LOC, thread_current->funcname); + s = str_append (LOC, tc->funcname); s = str_append (LOC, " scheduled from "); - s = str_append (LOC, thread_current->schedfrom); + s = str_append (LOC, tc->schedfrom); s = str_append (LOC, ":"); - s = num_append (LOC, thread_current->schedfrom_line); + s = num_append (LOC, tc->schedfrom_line); s = str_append (LOC, "\n"); } @@ -700,10 +702,13 @@ ZLOG_FUNC(zlog_debug, LOG_DEBUG) void zlog_thread_info (int log_level) { - if (thread_current) + struct thread *tc; + tc = pthread_getspecific (thread_current); + + if (tc) zlog(log_level, "Current thread function %s, scheduled from " - "file %s, line %u", thread_current->funcname, - thread_current->schedfrom, thread_current->schedfrom_line); + "file %s, line %u", tc->funcname, + tc->schedfrom, tc->schedfrom_line); else zlog(log_level, "Current thread not known/applicable"); } diff --git a/lib/thread.c b/lib/thread.c index 335e7fc044..feeffd31e8 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -47,8 +47,10 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats") write (m->io_pipe[1], &wakebyte, 1); \ } while (0); +pthread_once_t init_once = PTHREAD_ONCE_INIT; static pthread_mutex_t cpu_record_mtx = PTHREAD_MUTEX_INITIALIZER; static struct hash *cpu_record = NULL; +pthread_key_t thread_current; static unsigned long timeval_elapsed (struct timeval a, struct timeval b) @@ -334,6 +336,17 @@ cancelreq_del (void *cr) XFREE (MTYPE_TMP, cr); } +/* initializer, only ever called once */ +static void initializer () +{ + if (cpu_record == NULL) + cpu_record = hash_create ((unsigned int (*) (void *))cpu_record_hash_key, + (int (*) (const void *, const void *)) + cpu_record_hash_cmp); + + pthread_key_create (&thread_current, NULL); +} + /* Allocate new thread master. */ struct thread_master * thread_master_create (void) @@ -343,14 +356,7 @@ thread_master_create (void) getrlimit(RLIMIT_NOFILE, &limit); - pthread_mutex_lock (&cpu_record_mtx); - { - if (cpu_record == NULL) - cpu_record = hash_create ((unsigned int (*) (void *))cpu_record_hash_key, - (int (*) (const void *, const void *)) - cpu_record_hash_cmp); - } - pthread_mutex_unlock (&cpu_record_mtx); + pthread_once (&init_once, &initializer); rv = XCALLOC (MTYPE_THREAD_MASTER, sizeof (struct thread_master)); if (rv == NULL) @@ -1096,6 +1102,7 @@ thread_cancel (struct thread *thread) listnode_add (thread->master->cancel_req, cr); do_thread_cancel (thread->master); } +done: pthread_mutex_unlock (&thread->master->mtx); } @@ -1449,8 +1456,6 @@ thread_getrusage (RUSAGE_T *r) getrusage(RUSAGE_SELF, &(r->cpu)); } -struct thread *thread_current = NULL; - /* We check thread consumed time. If the system has getrusage, we'll use that to get in-depth stats on the performance of the thread in addition to wall clock time stats from gettimeofday. */ @@ -1463,9 +1468,9 @@ thread_call (struct thread *thread) GETRUSAGE (&before); thread->real = before.real; - thread_current = thread; + pthread_setspecific (thread_current, thread); (*thread->func) (thread); - thread_current = NULL; + pthread_setspecific (thread_current, NULL); GETRUSAGE (&after); diff --git a/lib/thread.h b/lib/thread.h index e48068b174..1760a930f9 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -220,6 +220,6 @@ extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before, unsigned long *cpu_time_elapsed); /* only for use in logging functions! */ -extern struct thread *thread_current; +extern pthread_key_t thread_current; #endif /* _ZEBRA_THREAD_H */ From 62f44022399236ec619a6ebedd3bd6e11765fafd Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 15 Jun 2017 19:10:57 +0000 Subject: [PATCH 3/5] lib: make `show thread...` commands mt-aware This patch fixes up show thread commands so that they know about and operate on all extant thread_masters, since we can now have multiple running in any given application. This change also eliminates a heap use after free that appears when using a single cpu_record shared among multiple threads. Since struct thread's have pointers to bits of memory that are freed when the global statistics hash table is freed, later accesses are invalid. By moving the stats hash to be unique to each thread_master this problem is sidestepped. Signed-off-by: Quentin Young --- lib/thread.c | 328 +++++++++++++++++++++++++-------------------------- lib/thread.h | 1 + 2 files changed, 164 insertions(+), 165 deletions(-) diff --git a/lib/thread.c b/lib/thread.c index feeffd31e8..d3eb293137 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -47,18 +47,15 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats") write (m->io_pipe[1], &wakebyte, 1); \ } while (0); +/* control variable for initializer */ pthread_once_t init_once = PTHREAD_ONCE_INIT; -static pthread_mutex_t cpu_record_mtx = PTHREAD_MUTEX_INITIALIZER; -static struct hash *cpu_record = NULL; pthread_key_t thread_current; -static unsigned long -timeval_elapsed (struct timeval a, struct timeval b) -{ - return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) - + (a.tv_usec - b.tv_usec)); -} +pthread_mutex_t masters_mtx = PTHREAD_MUTEX_INITIALIZER; +static struct list *masters; + +/* CLI start ---------------------------------------------------------------- */ static unsigned int cpu_record_hash_key (struct cpu_thread_history *a) { @@ -108,12 +105,12 @@ vty_out_cpu_thread_history(struct vty* vty, } static void -cpu_record_hash_print(struct hash_backet *bucket, - void *args[]) +cpu_record_hash_print(struct hash_backet *bucket, void *args[]) { struct cpu_thread_history *totals = args[0]; struct vty *vty = args[1]; thread_type *filter = args[2]; + struct cpu_thread_history *a = bucket->data; if ( !(a->types & *filter) ) @@ -134,29 +131,116 @@ cpu_record_print(struct vty *vty, thread_type filter) { struct cpu_thread_history tmp; void *args[3] = {&tmp, vty, &filter}; + struct thread_master *m; + struct listnode *ln; + int n = 0; memset(&tmp, 0, sizeof tmp); tmp.funcname = "TOTAL"; tmp.types = filter; - vty_outln (vty, "%21s %18s %18s", - "", "CPU (user+system):", "Real (wall-clock):"); - vty_out(vty, "Active Runtime(ms) Invoked Avg uSec Max uSecs"); - vty_out(vty, " Avg uSec Max uSecs"); - vty_outln (vty, " Type Thread"); - - pthread_mutex_lock (&cpu_record_mtx); + pthread_mutex_lock (&masters_mtx); { - hash_iterate(cpu_record, - (void(*)(struct hash_backet*,void*))cpu_record_hash_print, - args); + for (ALL_LIST_ELEMENTS_RO (masters, ln, m)) { + + vty_out (vty, VTYNL); + vty_outln(vty, "Showing statistics for pthread %d", n++); + vty_outln(vty, "-----------------------------------------------"); + vty_outln(vty, "%21s %18s %18s", + "", "CPU (user+system):", "Real (wall-clock):"); + vty_out(vty, "Active Runtime(ms) Invoked Avg uSec Max uSecs"); + vty_out(vty, " Avg uSec Max uSecs"); + vty_outln(vty, " Type Thread"); + + hash_iterate(m->cpu_record, + (void (*)(struct hash_backet *, void *)) + cpu_record_hash_print, + args); + vty_out(vty, VTYNL); + } } - pthread_mutex_unlock (&cpu_record_mtx); + pthread_mutex_unlock (&masters_mtx); if (tmp.total_calls > 0) vty_out_cpu_thread_history(vty, &tmp); } +static void +cpu_record_hash_clear (struct hash_backet *bucket, void *args[]) +{ + thread_type *filter = args[0]; + struct hash *cpu_record = args[1]; + + struct cpu_thread_history *a = bucket->data; + + if ( !(a->types & *filter) ) + return; + + hash_release (cpu_record, bucket->data); +} + +static void +cpu_record_clear (thread_type filter) +{ + thread_type *tmp = &filter; + struct thread_master *m; + struct listnode *ln; + + pthread_mutex_lock (&masters_mtx); + { + for (ALL_LIST_ELEMENTS_RO (masters, ln, m)) { + pthread_mutex_lock (&m->mtx); + { + void *args[2] = { tmp, m->cpu_record }; + hash_iterate (m->cpu_record, + (void (*) (struct hash_backet*,void*)) + cpu_record_hash_clear, + args); + } + pthread_mutex_unlock (&m->mtx); + } + } + pthread_mutex_unlock (&masters_mtx); +} + +static thread_type +parse_filter (const char *filterstr) +{ + int i = 0; + int filter = 0; + + while (filterstr[i] != '\0') + { + switch (filterstr[i]) + { + case 'r': + case 'R': + filter |= (1 << THREAD_READ); + break; + case 'w': + case 'W': + filter |= (1 << THREAD_WRITE); + break; + case 't': + case 'T': + filter |= (1 << THREAD_TIMER); + break; + case 'e': + case 'E': + filter |= (1 << THREAD_EVENT); + break; + case 'x': + case 'X': + filter |= (1 << THREAD_EXECUTE); + break; + default: + break; + } + ++i; + } + return filter; +} + DEFUN (show_thread_cpu, show_thread_cpu_cmd, "show thread cpu [FILTER]", @@ -165,138 +249,41 @@ DEFUN (show_thread_cpu, "Thread CPU usage\n" "Display filter (rwtexb)\n") { - int idx_filter = 3; - int i = 0; thread_type filter = (thread_type) -1U; + int idx = 0; - if (argc > 3) - { - filter = 0; - while (argv[idx_filter]->arg[i] != '\0') - { - switch ( argv[idx_filter]->arg[i] ) - { - case 'r': - case 'R': - filter |= (1 << THREAD_READ); - break; - case 'w': - case 'W': - filter |= (1 << THREAD_WRITE); - break; - case 't': - case 'T': - filter |= (1 << THREAD_TIMER); - break; - case 'e': - case 'E': - filter |= (1 << THREAD_EVENT); - break; - case 'x': - case 'X': - filter |= (1 << THREAD_EXECUTE); - break; - default: - break; - } - ++i; - } - if (filter == 0) - { - vty_outln (vty, "Invalid filter \"%s\" specified," - " must contain at least one of 'RWTEXB'", - argv[idx_filter]->arg); - return CMD_WARNING; - } + if (argv_find (argv, argc, "FILTER", &idx)) { + filter = parse_filter (argv[idx]->arg); + if (!filter) { + vty_outln(vty, "Invalid filter \"%s\" specified; must contain at least" + "one of 'RWTEXB'%s", argv[idx]->arg); + return CMD_WARNING; } + } cpu_record_print(vty, filter); return CMD_SUCCESS; } -static void -cpu_record_hash_clear (struct hash_backet *bucket, - void *args) -{ - thread_type *filter = args; - struct cpu_thread_history *a = bucket->data; - - if ( !(a->types & *filter) ) - return; - - pthread_mutex_lock (&cpu_record_mtx); - { - hash_release (cpu_record, bucket->data); - } - pthread_mutex_unlock (&cpu_record_mtx); -} - -static void -cpu_record_clear (thread_type filter) -{ - thread_type *tmp = &filter; - - pthread_mutex_lock (&cpu_record_mtx); - { - hash_iterate (cpu_record, - (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear, - tmp); - } - pthread_mutex_unlock (&cpu_record_mtx); -} - DEFUN (clear_thread_cpu, clear_thread_cpu_cmd, "clear thread cpu [FILTER]", - "Clear stored data\n" + "Clear stored data in all pthreads\n" "Thread information\n" "Thread CPU usage\n" "Display filter (rwtexb)\n") { - int idx_filter = 3; - int i = 0; thread_type filter = (thread_type) -1U; + int idx = 0; - if (argc > 3) - { - filter = 0; - while (argv[idx_filter]->arg[i] != '\0') - { - switch ( argv[idx_filter]->arg[i] ) - { - case 'r': - case 'R': - filter |= (1 << THREAD_READ); - break; - case 'w': - case 'W': - filter |= (1 << THREAD_WRITE); - break; - case 't': - case 'T': - filter |= (1 << THREAD_TIMER); - break; - case 'e': - case 'E': - filter |= (1 << THREAD_EVENT); - break; - case 'x': - case 'X': - filter |= (1 << THREAD_EXECUTE); - break; - default: - break; - } - ++i; - } - if (filter == 0) - { - vty_outln (vty, "Invalid filter \"%s\" specified," - " must contain at least one of 'RWTEXB'", - argv[idx_filter]->arg); - return CMD_WARNING; - } + if (argv_find (argv, argc, "FILTER", &idx)) { + filter = parse_filter (argv[idx]->arg); + if (!filter) { + vty_outln(vty, "Invalid filter \"%s\" specified; must contain at least" + "one of 'RWTEXB'%s", argv[idx]->arg); + return CMD_WARNING; } + } cpu_record_clear (filter); return CMD_SUCCESS; @@ -308,6 +295,8 @@ thread_cmd_init (void) install_element (VIEW_NODE, &show_thread_cpu_cmd); install_element (ENABLE_NODE, &clear_thread_cpu_cmd); } +/* CLI end ------------------------------------------------------------------ */ + static int thread_timer_cmp(void *a, void *b) @@ -339,10 +328,8 @@ cancelreq_del (void *cr) /* initializer, only ever called once */ static void initializer () { - if (cpu_record == NULL) - cpu_record = hash_create ((unsigned int (*) (void *))cpu_record_hash_key, - (int (*) (const void *, const void *)) - cpu_record_hash_cmp); + if (!masters) + masters = list_new(); pthread_key_create (&thread_current, NULL); } @@ -354,17 +341,18 @@ thread_master_create (void) struct thread_master *rv; struct rlimit limit; - getrlimit(RLIMIT_NOFILE, &limit); - pthread_once (&init_once, &initializer); rv = XCALLOC (MTYPE_THREAD_MASTER, sizeof (struct thread_master)); if (rv == NULL) return NULL; + /* Initialize master mutex */ pthread_mutex_init (&rv->mtx, NULL); pthread_cond_init (&rv->cancel_cond, NULL); + /* Initialize I/O task data structures */ + getrlimit(RLIMIT_NOFILE, &limit); rv->fd_limit = (int)limit.rlim_cur; rv->read = XCALLOC (MTYPE_THREAD, sizeof (struct thread *) * rv->fd_limit); if (rv->read == NULL) @@ -372,7 +360,6 @@ thread_master_create (void) XFREE (MTYPE_THREAD_MASTER, rv); return NULL; } - rv->write = XCALLOC (MTYPE_THREAD, sizeof (struct thread *) * rv->fd_limit); if (rv->write == NULL) { @@ -381,20 +368,32 @@ thread_master_create (void) return NULL; } + rv->cpu_record = hash_create ((unsigned int (*) (void *))cpu_record_hash_key, + (int (*) (const void *, const void *)) + cpu_record_hash_cmp); + + /* Initialize the timer queues */ rv->timer = pqueue_create(); rv->timer->cmp = thread_timer_cmp; rv->timer->update = thread_timer_update; + + /* Initialize thread_fetch() settings */ rv->spin = true; rv->handle_signals = true; + + /* Set pthread owner, should be updated by actual owner */ rv->owner = pthread_self(); rv->cancel_req = list_new (); rv->cancel_req->del = cancelreq_del; rv->canceled = true; + + /* Initialize pipe poker */ pipe (rv->io_pipe); set_nonblocking (rv->io_pipe[0]); set_nonblocking (rv->io_pipe[1]); + /* Initialize data structures for poll() */ rv->handler.pfdsize = rv->fd_limit; rv->handler.pfdcount = 0; rv->handler.pfds = XCALLOC (MTYPE_THREAD_MASTER, @@ -402,6 +401,13 @@ thread_master_create (void) rv->handler.copy = XCALLOC (MTYPE_THREAD_MASTER, sizeof (struct pollfd) * rv->handler.pfdsize); + /* add to list */ + pthread_mutex_lock (&masters_mtx); + { + listnode_add (masters, rv); + } + pthread_mutex_unlock (&masters_mtx); + return rv; } @@ -551,20 +557,13 @@ thread_master_free (struct thread_master *m) close (m->io_pipe[1]); list_delete (m->cancel_req); + hash_clean (m->cpu_record, cpu_record_hash_free); + hash_free (m->cpu_record); + m->cpu_record = NULL; + XFREE (MTYPE_THREAD_MASTER, m->handler.pfds); XFREE (MTYPE_THREAD_MASTER, m->handler.copy); XFREE (MTYPE_THREAD_MASTER, m); - - pthread_mutex_lock (&cpu_record_mtx); - { - if (cpu_record) - { - hash_clean (cpu_record, cpu_record_hash_free); - hash_free (cpu_record); - cpu_record = NULL; - } - } - pthread_mutex_unlock (&cpu_record_mtx); } /* Return remain time in second. */ @@ -636,12 +635,8 @@ thread_get (struct thread_master *m, u_char type, { tmp.func = func; tmp.funcname = funcname; - pthread_mutex_lock (&cpu_record_mtx); - { - thread->hist = hash_get (cpu_record, &tmp, - (void * (*) (void *))cpu_record_hash_alloc); - } - pthread_mutex_unlock (&cpu_record_mtx); + thread->hist = hash_get (m->cpu_record, &tmp, + (void * (*) (void *))cpu_record_hash_alloc); } thread->hist->total_active++; thread->func = func; @@ -1408,6 +1403,13 @@ thread_fetch (struct thread_master *m, struct thread *fetch) return fetch; } +static unsigned long +timeval_elapsed (struct timeval a, struct timeval b) +{ + return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) + + (a.tv_usec - b.tv_usec)); +} + unsigned long thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime) { @@ -1523,12 +1525,8 @@ funcname_thread_execute (struct thread_master *m, tmp.func = dummy.func = func; tmp.funcname = dummy.funcname = funcname; - pthread_mutex_lock (&cpu_record_mtx); - { - dummy.hist = hash_get (cpu_record, &tmp, - (void * (*) (void *))cpu_record_hash_alloc); - } - pthread_mutex_unlock (&cpu_record_mtx); + dummy.hist = hash_get (m->cpu_record, &tmp, + (void * (*) (void *))cpu_record_hash_alloc); dummy.schedfrom = schedfrom; dummy.schedfrom_line = fromln; diff --git a/lib/thread.h b/lib/thread.h index 1760a930f9..12c85fc14b 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -80,6 +80,7 @@ struct thread_master struct list *cancel_req; bool canceled; pthread_cond_t cancel_cond; + struct hash *cpu_record; int io_pipe[2]; int fd_limit; struct fd_handler handler; From 972a411ccc1c15ffe52862477653ab877d0afcd6 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 15 Jun 2017 20:17:44 +0000 Subject: [PATCH 4/5] lib: more `show thread cpu` Add support for naming pthreads. Also, note that we don't have any records yet if that's the case. Signed-off-by: Quentin Young --- ldpd/lde.c | 2 +- ldpd/ldpe.c | 2 +- lib/frr_pthread.c | 2 +- lib/grammar_sandbox_main.c | 2 +- lib/libfrr.c | 2 +- lib/thread.c | 38 ++++++++++++++++++++++-------- lib/thread.h | 4 +++- ospfclient/ospfclient.c | 2 +- tests/bgpd/test_aspath.c | 2 +- tests/bgpd/test_capability.c | 2 +- tests/bgpd/test_mp_attr.c | 2 +- tests/bgpd/test_mpath.c | 2 +- tests/helpers/c/main.c | 2 +- tests/lib/cli/common_cli.c | 2 +- tests/lib/test_segv.c | 2 +- tests/lib/test_sig.c | 2 +- tests/lib/test_timer_correctness.c | 2 +- tests/lib/test_timer_performance.c | 2 +- tests/test_lblmgr.c | 2 +- zebra/client_main.c | 2 +- 20 files changed, 49 insertions(+), 29 deletions(-) diff --git a/ldpd/lde.c b/ldpd/lde.c index 0ef46dab3d..602dc8805e 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -131,7 +131,7 @@ lde(void) ldpd_process = PROC_LDE_ENGINE; log_procname = log_procnames[PROC_LDE_ENGINE]; - master = thread_master_create(); + master = thread_master_create(NULL); /* setup signal handler */ signal_init(master, array_size(lde_signals), lde_signals); diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index ba153dfde2..b2f9fdce55 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -109,7 +109,7 @@ ldpe(void) ldpd_process = PROC_LDP_ENGINE; log_procname = log_procnames[ldpd_process]; - master = thread_master_create(); + master = thread_master_create(NULL); /* setup signal handler */ signal_init(master, array_size(ldpe_signals), ldpe_signals); diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index 17bc82f5da..614c722be1 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -86,7 +86,7 @@ struct frr_pthread *frr_pthread_new(const char *name, unsigned int id, XCALLOC(MTYPE_FRR_PTHREAD, sizeof(struct frr_pthread)); fpt->id = id; - fpt->master = thread_master_create(); + fpt->master = thread_master_create(name); fpt->start_routine = start_routine; fpt->stop_routine = stop_routine; fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name); diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index 3bf0e268cf..02aefd603d 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -38,7 +38,7 @@ int main(int argc, char **argv) { struct thread thread; - master = thread_master_create (); + master = thread_master_create(NULL); openzlog ("grammar_sandbox", "NONE", 0, LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); diff --git a/lib/libfrr.c b/lib/libfrr.c index 132f7d4d2c..8d816437cb 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -366,7 +366,7 @@ struct thread_master *frr_init(void) zprivs_init(di->privs); - master = thread_master_create(); + master = thread_master_create(NULL); signal_init(master, di->n_signals, di->signals); if (di->flags & FRR_LIMITED_CLI) diff --git a/lib/thread.c b/lib/thread.c index d3eb293137..5a7888379b 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -133,7 +133,6 @@ cpu_record_print(struct vty *vty, thread_type filter) void *args[3] = {&tmp, vty, &filter}; struct thread_master *m; struct listnode *ln; - int n = 0; memset(&tmp, 0, sizeof tmp); tmp.funcname = "TOTAL"; @@ -142,25 +141,41 @@ cpu_record_print(struct vty *vty, thread_type filter) pthread_mutex_lock (&masters_mtx); { for (ALL_LIST_ELEMENTS_RO (masters, ln, m)) { + const char *name = m->name ? m->name : "main"; + + char underline[strlen(name) + 1]; + memset (underline, '-', sizeof (underline)); + underline[sizeof(underline)] = '\0'; vty_out (vty, VTYNL); - vty_outln(vty, "Showing statistics for pthread %d", n++); - vty_outln(vty, "-----------------------------------------------"); - vty_outln(vty, "%21s %18s %18s", - "", "CPU (user+system):", "Real (wall-clock):"); + vty_outln(vty, "Showing statistics for pthread %s", name); + vty_outln(vty, "-------------------------------%s", underline); + vty_outln(vty, "%21s %18s %18s", "", "CPU (user+system):", "Real (wall-clock):"); vty_out(vty, "Active Runtime(ms) Invoked Avg uSec Max uSecs"); vty_out(vty, " Avg uSec Max uSecs"); vty_outln(vty, " Type Thread"); - hash_iterate(m->cpu_record, - (void (*)(struct hash_backet *, void *)) - cpu_record_hash_print, - args); + if (m->cpu_record->count) + hash_iterate(m->cpu_record, + (void (*)(struct hash_backet *, void *)) + cpu_record_hash_print, + args); + else + vty_outln(vty, "No data to display yet."); + vty_out(vty, VTYNL); } } pthread_mutex_unlock (&masters_mtx); + vty_out(vty, VTYNL); + vty_outln(vty, "Total thread statistics"); + vty_outln(vty, "-------------------------"); + vty_outln(vty, "%21s %18s %18s", "", "CPU (user+system):", "Real (wall-clock):"); + vty_out(vty, "Active Runtime(ms) Invoked Avg uSec Max uSecs"); + vty_out(vty, " Avg uSec Max uSecs"); + vty_outln(vty, " Type Thread"); + if (tmp.total_calls > 0) vty_out_cpu_thread_history(vty, &tmp); } @@ -336,7 +351,7 @@ static void initializer () /* Allocate new thread master. */ struct thread_master * -thread_master_create (void) +thread_master_create (const char *name) { struct thread_master *rv; struct rlimit limit; @@ -351,6 +366,9 @@ thread_master_create (void) pthread_mutex_init (&rv->mtx, NULL); pthread_cond_init (&rv->cancel_cond, NULL); + /* Set name */ + rv->name = name ? XSTRDUP (MTYPE_THREAD_MASTER, name) : NULL; + /* Initialize I/O task data structures */ getrlimit(RLIMIT_NOFILE, &limit); rv->fd_limit = (int)limit.rlim_cur; diff --git a/lib/thread.h b/lib/thread.h index 12c85fc14b..ad923c413f 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -71,6 +71,8 @@ struct cancel_req { /* Master of the theads. */ struct thread_master { + char *name; + struct thread **read; struct thread **write; struct pqueue *timer; @@ -178,7 +180,7 @@ struct cpu_thread_history #define thread_execute(m,f,a,v) funcname_thread_execute(m,f,a,v,#f,__FILE__,__LINE__) /* Prototypes. */ -extern struct thread_master *thread_master_create (void); +extern struct thread_master *thread_master_create (const char *); extern void thread_master_free (struct thread_master *); extern void thread_master_free_unused(struct thread_master *); diff --git a/ospfclient/ospfclient.c b/ospfclient/ospfclient.c index affcbc9d6a..63fcafb736 100644 --- a/ospfclient/ospfclient.c +++ b/ospfclient/ospfclient.c @@ -326,7 +326,7 @@ main (int argc, char *argv[]) /* Initialization */ zprivs_init (&ospfd_privs); - master = thread_master_create (); + master = thread_master_create(NULL); /* Open connection to OSPF daemon */ oclient = ospf_apiclient_connect (args[1], ASYNCPORT); diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c index 2d83fe6828..461fb829d4 100644 --- a/tests/bgpd/test_aspath.c +++ b/tests/bgpd/test_aspath.c @@ -1331,7 +1331,7 @@ main (void) { int i = 0; qobj_init (); - bgp_master_init (thread_master_create ()); + bgp_master_init (thread_master_create(NULL)); master = bm->master; bgp_option_set (BGP_OPT_NO_LISTEN); bgp_attr_init (); diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c index 1e3a5be4e3..be0ca37b1f 100644 --- a/tests/bgpd/test_capability.c +++ b/tests/bgpd/test_capability.c @@ -648,7 +648,7 @@ main (void) term_bgp_debug_as4 = -1UL; qobj_init (); - master = thread_master_create (); + master = thread_master_create(NULL); bgp_master_init (master); vrf_init (NULL, NULL, NULL, NULL); bgp_option_set (BGP_OPT_NO_LISTEN); diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index e323748e97..f6f0fb70a5 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -748,7 +748,7 @@ main (void) term_bgp_debug_as4 = -1UL; qobj_init (); - master = thread_master_create (); + master = thread_master_create(NULL); bgp_master_init (master); vrf_init (NULL, NULL, NULL, NULL); bgp_option_set (BGP_OPT_NO_LISTEN); diff --git a/tests/bgpd/test_mpath.c b/tests/bgpd/test_mpath.c index affebbafea..3309a93227 100644 --- a/tests/bgpd/test_mpath.c +++ b/tests/bgpd/test_mpath.c @@ -376,7 +376,7 @@ static int global_test_init (void) { qobj_init (); - master = thread_master_create (); + master = thread_master_create(NULL); zclient = zclient_new(master); bgp_master_init (master); vrf_init (NULL, NULL, NULL, NULL); diff --git a/tests/helpers/c/main.c b/tests/helpers/c/main.c index b0e80fb674..f842b03727 100644 --- a/tests/helpers/c/main.c +++ b/tests/helpers/c/main.c @@ -116,7 +116,7 @@ main (int argc, char **argv) progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); /* master init. */ - master = thread_master_create (); + master = thread_master_create(NULL); while (1) { diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index 27b28b126f..728ae8cb04 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -67,7 +67,7 @@ main (int argc, char **argv) umask (0027); /* master init. */ - master = thread_master_create (); + master = thread_master_create(NULL); openzlog("common-cli", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); diff --git a/tests/lib/test_segv.c b/tests/lib/test_segv.c index c43431622d..14384f4327 100644 --- a/tests/lib/test_segv.c +++ b/tests/lib/test_segv.c @@ -45,7 +45,7 @@ threadfunc (struct thread *thread) int main (void) { - master = thread_master_create (); + master = thread_master_create(NULL); signal_init (master, array_size(sigs), sigs); openzlog("testsegv", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); diff --git a/tests/lib/test_sig.c b/tests/lib/test_sig.c index a04c9f4206..1ffc6692f1 100644 --- a/tests/lib/test_sig.c +++ b/tests/lib/test_sig.c @@ -61,7 +61,7 @@ struct thread t; int main (void) { - master = thread_master_create (); + master = thread_master_create(NULL); signal_init (master, array_size(sigs), sigs); openzlog("testsig", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); diff --git a/tests/lib/test_timer_correctness.c b/tests/lib/test_timer_correctness.c index 10461be1ef..8fbe5bcbd7 100644 --- a/tests/lib/test_timer_correctness.c +++ b/tests/lib/test_timer_correctness.c @@ -115,7 +115,7 @@ int main(int argc, char **argv) struct thread t; struct timeval **alarms; - master = thread_master_create(); + master = thread_master_create(NULL); log_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1; log_buf_pos = 0; diff --git a/tests/lib/test_timer_performance.c b/tests/lib/test_timer_performance.c index b67af19aea..2bd02b5b01 100644 --- a/tests/lib/test_timer_performance.c +++ b/tests/lib/test_timer_performance.c @@ -49,7 +49,7 @@ int main(int argc, char **argv) struct timeval tv_start, tv_lap, tv_stop; unsigned long t_schedule, t_remove; - master = thread_master_create(); + master = thread_master_create(NULL); prng = prng_new(0); timers = calloc(SCHEDULE_TIMERS, sizeof(*timers)); diff --git a/tests/test_lblmgr.c b/tests/test_lblmgr.c index d830094bad..a659447e72 100644 --- a/tests/test_lblmgr.c +++ b/tests/test_lblmgr.c @@ -140,7 +140,7 @@ int main (int argc, char *argv[]) printf ("Sequence to be tested: %s\n", sequence); - master = thread_master_create(); + master = thread_master_create(NULL); init_zclient (master, ZSERV_PATH); zebra_send_label_manager_connect (); diff --git a/zebra/client_main.c b/zebra/client_main.c index 0e77ea4aec..2903b8425d 100644 --- a/zebra/client_main.c +++ b/zebra/client_main.c @@ -200,7 +200,7 @@ main (int argc, char **argv) if (argc == 1) usage_exit (); - master = thread_master_create(); + master = thread_master_create(NULL); /* Establish connection to zebra. */ zclient = zclient_new(master); zclient->enable = 1; From 26eb0e4ff5031e791b4fd23d397dc15e1beab8d1 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 29 Jun 2017 17:49:45 +0000 Subject: [PATCH 5/5] lib: fix merge error Signed-off-by: Quentin Young --- lib/thread.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/thread.c b/lib/thread.c index 5a7888379b..bc52d1b134 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -1115,7 +1115,6 @@ thread_cancel (struct thread *thread) listnode_add (thread->master->cancel_req, cr); do_thread_cancel (thread->master); } -done: pthread_mutex_unlock (&thread->master->mtx); }