threadstorage.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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. #include "asterisk/strings.h"
  35. #include "asterisk/utils.h"
  36. #include "asterisk/threadstorage.h"
  37. #include "asterisk/linkedlists.h"
  38. #include "asterisk/cli.h"
  39. struct tls_object {
  40. void *key;
  41. size_t size;
  42. const char *file;
  43. const char *function;
  44. unsigned int line;
  45. pthread_t thread;
  46. AST_LIST_ENTRY(tls_object) entry;
  47. };
  48. static AST_LIST_HEAD_NOLOCK_STATIC(tls_objects, tls_object);
  49. /* Allow direct use of pthread_mutex_t and friends */
  50. #undef pthread_mutex_t
  51. #undef pthread_mutex_lock
  52. #undef pthread_mutex_unlock
  53. #undef pthread_mutex_init
  54. #undef pthread_mutex_destroy
  55. /*!
  56. * \brief lock for the tls_objects list
  57. *
  58. * \note We can not use an ast_mutex_t for this. The reason is that this
  59. * lock is used within the context of thread-local data destructors,
  60. * and the ast_mutex_* API uses thread-local data. Allocating more
  61. * thread-local data at that point just causes a memory leak.
  62. */
  63. static pthread_mutex_t threadstoragelock;
  64. void __ast_threadstorage_object_add(void *key, size_t len, const char *file, const char *function, unsigned int line)
  65. {
  66. struct tls_object *to;
  67. if (!(to = ast_calloc(1, sizeof(*to))))
  68. return;
  69. to->key = key;
  70. to->size = len;
  71. to->file = file;
  72. to->function = function;
  73. to->line = line;
  74. to->thread = pthread_self();
  75. pthread_mutex_lock(&threadstoragelock);
  76. AST_LIST_INSERT_TAIL(&tls_objects, to, entry);
  77. pthread_mutex_unlock(&threadstoragelock);
  78. }
  79. void __ast_threadstorage_object_remove(void *key)
  80. {
  81. struct tls_object *to;
  82. pthread_mutex_lock(&threadstoragelock);
  83. AST_LIST_TRAVERSE_SAFE_BEGIN(&tls_objects, to, entry) {
  84. if (to->key == key) {
  85. AST_LIST_REMOVE_CURRENT(entry);
  86. break;
  87. }
  88. }
  89. AST_LIST_TRAVERSE_SAFE_END;
  90. pthread_mutex_unlock(&threadstoragelock);
  91. if (to)
  92. ast_free(to);
  93. }
  94. void __ast_threadstorage_object_replace(void *key_old, void *key_new, size_t len)
  95. {
  96. struct tls_object *to;
  97. pthread_mutex_lock(&threadstoragelock);
  98. AST_LIST_TRAVERSE_SAFE_BEGIN(&tls_objects, to, entry) {
  99. if (to->key == key_old) {
  100. to->key = key_new;
  101. to->size = len;
  102. break;
  103. }
  104. }
  105. AST_LIST_TRAVERSE_SAFE_END;
  106. pthread_mutex_unlock(&threadstoragelock);
  107. }
  108. static char *handle_cli_threadstorage_show_allocations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  109. {
  110. const char *fn = NULL;
  111. size_t len = 0;
  112. unsigned int count = 0;
  113. struct tls_object *to;
  114. switch (cmd) {
  115. case CLI_INIT:
  116. e->command = "threadstorage show allocations";
  117. e->usage =
  118. "Usage: threadstorage show allocations [<file>]\n"
  119. " Dumps a list of all thread-specific memory allocations,\n"
  120. " optionally limited to those from a specific file\n";
  121. return NULL;
  122. case CLI_GENERATE:
  123. return NULL;
  124. }
  125. if (a->argc > 4)
  126. return CLI_SHOWUSAGE;
  127. if (a->argc > 3)
  128. fn = a->argv[3];
  129. pthread_mutex_lock(&threadstoragelock);
  130. AST_LIST_TRAVERSE(&tls_objects, to, entry) {
  131. if (fn && strcasecmp(to->file, fn))
  132. continue;
  133. ast_cli(a->fd, "%10d bytes allocated in %20s at line %5d of %25s (thread %p)\n",
  134. (int) to->size, to->function, to->line, to->file, (void *) to->thread);
  135. len += to->size;
  136. count++;
  137. }
  138. pthread_mutex_unlock(&threadstoragelock);
  139. ast_cli(a->fd, "%10d bytes allocated in %d allocation%s\n", (int) len, count, count > 1 ? "s" : "");
  140. return CLI_SUCCESS;
  141. }
  142. static char *handle_cli_threadstorage_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  143. {
  144. const char *fn = NULL;
  145. size_t len = 0;
  146. unsigned int count = 0;
  147. struct tls_object *to;
  148. struct file {
  149. const char *name;
  150. size_t len;
  151. unsigned int count;
  152. AST_LIST_ENTRY(file) entry;
  153. } *file;
  154. AST_LIST_HEAD_NOLOCK_STATIC(file_summary, file);
  155. switch (cmd) {
  156. case CLI_INIT:
  157. e->command = "threadstorage show summary";
  158. e->usage =
  159. "Usage: threadstorage show summary [<file>]\n"
  160. " Summarizes thread-specific memory allocations by file, or optionally\n"
  161. " by function, if a file is specified\n";
  162. return NULL;
  163. case CLI_GENERATE:
  164. return NULL;
  165. }
  166. if (a->argc > 4)
  167. return CLI_SHOWUSAGE;
  168. if (a->argc > 3)
  169. fn = a->argv[3];
  170. pthread_mutex_lock(&threadstoragelock);
  171. AST_LIST_TRAVERSE(&tls_objects, to, entry) {
  172. if (fn && strcasecmp(to->file, fn))
  173. continue;
  174. AST_LIST_TRAVERSE(&file_summary, file, entry) {
  175. if ((!fn && (file->name == to->file)) || (fn && (file->name == to->function)))
  176. break;
  177. }
  178. if (!file) {
  179. file = ast_alloca(sizeof(*file));
  180. memset(file, 0, sizeof(*file));
  181. file->name = fn ? to->function : to->file;
  182. AST_LIST_INSERT_TAIL(&file_summary, file, entry);
  183. }
  184. file->len += to->size;
  185. file->count++;
  186. }
  187. pthread_mutex_unlock(&threadstoragelock);
  188. AST_LIST_TRAVERSE(&file_summary, file, entry) {
  189. len += file->len;
  190. count += file->count;
  191. if (fn) {
  192. ast_cli(a->fd, "%10d bytes in %d allocation%ss in function %s\n",
  193. (int) file->len, file->count, file->count > 1 ? "s" : "", file->name);
  194. } else {
  195. ast_cli(a->fd, "%10d bytes in %d allocation%s in file %s\n",
  196. (int) file->len, file->count, file->count > 1 ? "s" : "", file->name);
  197. }
  198. }
  199. ast_cli(a->fd, "%10d bytes allocated in %d allocation%s\n", (int) len, count, count > 1 ? "s" : "");
  200. return CLI_SUCCESS;
  201. }
  202. static struct ast_cli_entry cli[] = {
  203. AST_CLI_DEFINE(handle_cli_threadstorage_show_allocations, "Display outstanding thread local storage allocations"),
  204. AST_CLI_DEFINE(handle_cli_threadstorage_show_summary, "Summarize outstanding memory allocations")
  205. };
  206. static void threadstorage_shutdown(void)
  207. {
  208. ast_cli_unregister_multiple(cli, ARRAY_LEN(cli));
  209. }
  210. void threadstorage_init(void)
  211. {
  212. pthread_mutex_init(&threadstoragelock, NULL);
  213. ast_cli_register_multiple(cli, ARRAY_LEN(cli));
  214. ast_register_cleanup(threadstorage_shutdown);
  215. }
  216. #endif /* !defined(DEBUG_THREADLOCALS) */