comparison src/lib/ioloop.c @ 22512:bcc3a15c18a6

lib: When logging I/O or timeout leak, log also raw backtrace This can be useful when trying to figure out where the io_loop_destroy() was called from.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Tue, 08 Aug 2017 20:07:18 +0300
parents ef07cc84cbb3
children cb108f786fb4
comparison
equal deleted inserted replaced
22511:f0694e6eda8d 22512:bcc3a15c18a6
1 /* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ 1 /* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
2 2
3 #include "lib.h" 3 #include "lib.h"
4 #include "array.h" 4 #include "array.h"
5 #include "backtrace-string.h"
5 #include "llist.h" 6 #include "llist.h"
6 #include "time-util.h" 7 #include "time-util.h"
7 #include "istream-private.h" 8 #include "istream-private.h"
8 #include "ioloop-private.h" 9 #include "ioloop-private.h"
9 10
701 void io_loop_destroy(struct ioloop **_ioloop) 702 void io_loop_destroy(struct ioloop **_ioloop)
702 { 703 {
703 struct ioloop *ioloop = *_ioloop; 704 struct ioloop *ioloop = *_ioloop;
704 struct timeout *const *to_idx; 705 struct timeout *const *to_idx;
705 struct priorityq_item *item; 706 struct priorityq_item *item;
707 bool leaks = FALSE;
706 708
707 *_ioloop = NULL; 709 *_ioloop = NULL;
708 710
709 /* ->prev won't work unless loops are destroyed in create order */ 711 /* ->prev won't work unless loops are destroyed in create order */
710 i_assert(ioloop == current_ioloop); 712 i_assert(ioloop == current_ioloop);
720 i_warning("I/O leak: %p (%s:%u, fd %d)", 722 i_warning("I/O leak: %p (%s:%u, fd %d)",
721 (void *)io->io.callback, 723 (void *)io->io.callback,
722 io->io.source_filename, 724 io->io.source_filename,
723 io->io.source_linenum, io->fd); 725 io->io.source_linenum, io->fd);
724 io_remove(&_io); 726 io_remove(&_io);
727 leaks = TRUE;
725 } 728 }
726 i_assert(ioloop->io_pending_count == 0); 729 i_assert(ioloop->io_pending_count == 0);
727 730
728 array_foreach(&ioloop->timeouts_new, to_idx) { 731 array_foreach(&ioloop->timeouts_new, to_idx) {
729 struct timeout *to = *to_idx; 732 struct timeout *to = *to_idx;
730 733
731 i_warning("Timeout leak: %p (%s:%u)", (void *)to->callback, 734 i_warning("Timeout leak: %p (%s:%u)", (void *)to->callback,
732 to->source_filename, 735 to->source_filename,
733 to->source_linenum); 736 to->source_linenum);
734 timeout_free(to); 737 timeout_free(to);
738 leaks = TRUE;
735 } 739 }
736 array_free(&ioloop->timeouts_new); 740 array_free(&ioloop->timeouts_new);
737 741
738 while ((item = priorityq_pop(ioloop->timeouts)) != NULL) { 742 while ((item = priorityq_pop(ioloop->timeouts)) != NULL) {
739 struct timeout *to = (struct timeout *)item; 743 struct timeout *to = (struct timeout *)item;
740 744
741 i_warning("Timeout leak: %p (%s:%u)", (void *)to->callback, 745 i_warning("Timeout leak: %p (%s:%u)", (void *)to->callback,
742 to->source_filename, 746 to->source_filename,
743 to->source_linenum); 747 to->source_linenum);
744 timeout_free(to); 748 timeout_free(to);
749 leaks = TRUE;
745 } 750 }
746 priorityq_deinit(&ioloop->timeouts); 751 priorityq_deinit(&ioloop->timeouts);
747 752
748 while (ioloop->wait_timers != NULL) { 753 while (ioloop->wait_timers != NULL) {
749 struct io_wait_timer *timer = ioloop->wait_timers; 754 struct io_wait_timer *timer = ioloop->wait_timers;
750 755
751 i_warning("IO wait timer leak: %s:%u", 756 i_warning("IO wait timer leak: %s:%u",
752 timer->source_filename, 757 timer->source_filename,
753 timer->source_linenum); 758 timer->source_linenum);
754 io_wait_timer_remove(&timer); 759 io_wait_timer_remove(&timer);
760 leaks = TRUE;
761 }
762
763 if (leaks) {
764 const char *backtrace;
765 if (backtrace_get(&backtrace) == 0)
766 i_warning("Raw backtrace for leaks: %s", backtrace);
755 } 767 }
756 768
757 if (ioloop->handler_context != NULL) 769 if (ioloop->handler_context != NULL)
758 io_loop_handler_deinit(ioloop); 770 io_loop_handler_deinit(ioloop);
759 771