threadstorage.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 1999 - 2005, Digium, Inc.
  5. *
  6. * Kevin P. Fleming <kpfleming@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*! \file
  19. *
  20. * \brief Debugging support for thread-local-storage objects
  21. *
  22. * \author Kevin P. Fleming <kpfleming@digium.com>
  23. */
  24. /*** MODULEINFO
  25. <support_level>core</support_level>
  26. ***/
  27. #include "asterisk.h"
  28. #include "asterisk/_private.h"
  29. #if !defined(DEBUG_THREADLOCALS)
  30. void threadstorage_init(void)
  31. {
  32. }
  33. #else /* !defined(DEBUG_THREADLOCALS) */
  34. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  35. #include "asterisk/strings.h"
  36. #include "asterisk/utils.h"
  37. #include "asterisk/threadstorage.h"
  38. #include "asterisk/linkedlists.h"
  39. #include "asterisk/cli.h"
  40. struct tls_object {
  41. void *key;
  42. size_t size;
  43. const char *file;
  44. const char *function;
  45. unsigned int line;
  46. pthread_t thread;
  47. AST_LIST_ENTRY(tls_object) entry;
  48. };
  49. static AST_LIST_HEAD_NOLOCK_STATIC(tls_objects, tls_object);
  50. /* Allow direct use of pthread_mutex_t and friends */
  51. #undef pthread_mutex_t
  52. #undef pthread_mutex_lock
  53. #undef pthread_mutex_unlock
  54. #undef pthread_mutex_init
  55. #undef pthread_mutex_destroy
  56. /*!
  57. * \brief lock for the tls_objects list
  58. *
  59. * \note We can not use an ast_mutex_t for this. The reason is that this
  60. * lock is used within the context of thread-local data destructors,
  61. * and the ast_mutex_* API uses thread-local data. Allocating more
  62. * thread-local data at that point just causes a memory leak.
  63. */
  64. static pthread_mutex_t threadstoragelock;
  65. void __ast_threadstorage_object_add(void *key, size_t len, const char *file, const char *function, unsigned int line)
  66. {
  67. struct tls_object *to;
  68. if (!(to = ast_calloc(1, sizeof(*to))))
  69. return;
  70. to->key = key;
  71. to->size = len;
  72. to->file = file;
  73. to->function = function;
  74. to->line = line;
  75. to->thread = pthread_self();
  76. pthread_mutex_lock(&threadstoragelock);
  77. AST_LIST_INSERT_TAIL(&tls_objects, to, entry);
  78. pthread_mutex_unlock(&threadstoragelock);
  79. }
  80. void __ast_threadstorage_object_remove(void *key)
  81. {
  82. struct tls_object *to;
  83. pthread_mutex_lock(&threadstoragelock);
  84. AST_LIST_TRAVERSE_SAFE_BEGIN(&tls_objects, to, entry) {
  85. if (to->key == key) {
  86. AST_LIST_REMOVE_CURRENT(entry);
  87. break;
  88. }
  89. }
  90. AST_LIST_TRAVERSE_SAFE_END;
  91. pthread_mutex_unlock(&threadstoragelock);
  92. if (to)
  93. ast_free(to);
  94. }
  95. void __ast_threadstorage_object_replace(void *key_old, void *key_new, size_t len)
  96. {
  97. struct tls_object *to;
  98. pthread_mutex_lock(&threadstoragelock);
  99. AST_LIST_TRAVERSE_SAFE_BEGIN(&tls_objects, to, entry) {
  100. if (to->key == key_old) {
  101. to->key = key_new;
  102. to->size = len;
  103. break;
  104. }
  105. }
  106. AST_LIST_TRAVERSE_SAFE_END;
  107. pthread_mutex_unlock(&threadstoragelock);
  108. }
  109. static char *handle_cli_threadstorage_show_allocations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  110. {
  111. const char *fn = NULL;
  112. size_t len = 0;
  113. unsigned int count = 0;
  114. struct tls_object *to;
  115. switch (cmd) {
  116. case CLI_INIT:
  117. e->command = "threadstorage show allocations";
  118. e->usage =
  119. "Usage: threadstorage show allocations [<file>]\n"
  120. " Dumps a list of all thread-specific memory allocations,\n"
  121. " optionally limited to those from a specific file\n";
  122. return NULL;
  123. case CLI_GENERATE:
  124. return NULL;
  125. }
  126. if (a->argc > 4)
  127. return CLI_SHOWUSAGE;
  128. if (a->argc > 3)
  129. fn = a->argv[3];
  130. pthread_mutex_lock(&threadstoragelock);
  131. AST_LIST_TRAVERSE(&tls_objects, to, entry) {
  132. if (fn && strcasecmp(to->file, fn))
  133. continue;
  134. ast_cli(a->fd, "%10d bytes allocated in %20s at line %5d of %25s (thread %p)\n",
  135. (int) to->size, to->function, to->line, to->file, (void *) to->thread);
  136. len += to->size;
  137. count++;
  138. }
  139. pthread_mutex_unlock(&threadstoragelock);
  140. ast_cli(a->fd, "%10d bytes allocated in %d allocation%s\n", (int) len, count, count > 1 ? "s" : "");
  141. return CLI_SUCCESS;
  142. }
  143. static char *handle_cli_threadstorage_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  144. {
  145. const char *fn = NULL;
  146. size_t len = 0;
  147. unsigned int count = 0;
  148. struct tls_object *to;
  149. struct file {
  150. const char *name;
  151. size_t len;
  152. unsigned int count;
  153. AST_LIST_ENTRY(file) entry;
  154. } *file;
  155. AST_LIST_HEAD_NOLOCK_STATIC(file_summary, file);
  156. switch (cmd) {
  157. case CLI_INIT:
  158. e->command = "threadstorage show summary";
  159. e->usage =
  160. "Usage: threadstorage show summary [<file>]\n"
  161. " Summarizes thread-specific memory allocations by file, or optionally\n"
  162. " by function, if a file is specified\n";
  163. return NULL;
  164. case CLI_GENERATE:
  165. return NULL;
  166. }
  167. if (a->argc > 4)
  168. return CLI_SHOWUSAGE;
  169. if (a->argc > 3)
  170. fn = a->argv[3];
  171. pthread_mutex_lock(&threadstoragelock);
  172. AST_LIST_TRAVERSE(&tls_objects, to, entry) {
  173. if (fn && strcasecmp(to->file, fn))
  174. continue;
  175. AST_LIST_TRAVERSE(&file_summary, file, entry) {
  176. if ((!fn && (file->name == to->file)) || (fn && (file->name == to->function)))
  177. break;
  178. }
  179. if (!file) {
  180. file = ast_alloca(sizeof(*file));
  181. memset(file, 0, sizeof(*file));
  182. file->name = fn ? to->function : to->file;
  183. AST_LIST_INSERT_TAIL(&file_summary, file, entry);
  184. }
  185. file->len += to->size;
  186. file->count++;
  187. }
  188. pthread_mutex_unlock(&threadstoragelock);
  189. AST_LIST_TRAVERSE(&file_summary, file, entry) {
  190. len += file->len;
  191. count += file->count;
  192. if (fn) {
  193. ast_cli(a->fd, "%10d bytes in %d allocation%ss in function %s\n",
  194. (int) file->len, file->count, file->count > 1 ? "s" : "", file->name);
  195. } else {
  196. ast_cli(a->fd, "%10d bytes in %d allocation%s in file %s\n",
  197. (int) file->len, file->count, file->count > 1 ? "s" : "", file->name);
  198. }
  199. }
  200. ast_cli(a->fd, "%10d bytes allocated in %d allocation%s\n", (int) len, count, count > 1 ? "s" : "");
  201. return CLI_SUCCESS;
  202. }
  203. static struct ast_cli_entry cli[] = {
  204. AST_CLI_DEFINE(handle_cli_threadstorage_show_allocations, "Display outstanding thread local storage allocations"),
  205. AST_CLI_DEFINE(handle_cli_threadstorage_show_summary, "Summarize outstanding memory allocations")
  206. };
  207. static void threadstorage_shutdown(void)
  208. {
  209. ast_cli_unregister_multiple(cli, ARRAY_LEN(cli));
  210. }
  211. void threadstorage_init(void)
  212. {
  213. pthread_mutex_init(&threadstoragelock, NULL);
  214. ast_cli_register_multiple(cli, ARRAY_LEN(cli));
  215. ast_register_cleanup(threadstorage_shutdown);
  216. }
  217. #endif /* !defined(DEBUG_THREADLOCALS) */