00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "asterisk.h"
00027
00028 #if defined(DEBUG_THREADLOCALS)
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 49553 $")
00031
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035
00036 #include "asterisk/logger.h"
00037 #include "asterisk/strings.h"
00038 #include "asterisk/utils.h"
00039 #include "asterisk/threadstorage.h"
00040 #include "asterisk/linkedlists.h"
00041 #include "asterisk/cli.h"
00042
00043 struct tls_object {
00044 void *key;
00045 size_t size;
00046 const char *file;
00047 const char *function;
00048 unsigned int line;
00049 pthread_t thread;
00050 AST_LIST_ENTRY(tls_object) entry;
00051 };
00052
00053 static AST_LIST_HEAD_STATIC(tls_objects, tls_object);
00054
00055 void __ast_threadstorage_object_add(void *key, size_t len, const char *file, const char *function, unsigned int line)
00056 {
00057 struct tls_object *to;
00058
00059 if (!(to = ast_calloc(sizeof(*to), 1)))
00060 return;
00061
00062 to->key = key;
00063 to->size = len;
00064 to->file = file;
00065 to->function = function;
00066 to->line = line;
00067 to->thread = pthread_self();
00068
00069 AST_LIST_LOCK(&tls_objects);
00070 AST_LIST_INSERT_TAIL(&tls_objects, to, entry);
00071 AST_LIST_UNLOCK(&tls_objects);
00072 }
00073
00074 void __ast_threadstorage_object_remove(void *key)
00075 {
00076 struct tls_object *to;
00077
00078 AST_LIST_LOCK(&tls_objects);
00079 AST_LIST_TRAVERSE_SAFE_BEGIN(&tls_objects, to, entry) {
00080 if (to->key == key) {
00081 AST_LIST_REMOVE_CURRENT(&tls_objects, entry);
00082 break;
00083 }
00084 }
00085 AST_LIST_TRAVERSE_SAFE_END;
00086 AST_LIST_UNLOCK(&tls_objects);
00087 if (to)
00088 free(to);
00089 }
00090
00091 void __ast_threadstorage_object_replace(void *key_old, void *key_new, size_t len)
00092 {
00093 struct tls_object *to;
00094
00095 AST_LIST_LOCK(&tls_objects);
00096 AST_LIST_TRAVERSE_SAFE_BEGIN(&tls_objects, to, entry) {
00097 if (to->key == key_old) {
00098 to->key = key_new;
00099 to->size = len;
00100 break;
00101 }
00102 }
00103 AST_LIST_TRAVERSE_SAFE_END;
00104 AST_LIST_UNLOCK(&tls_objects);
00105 }
00106
00107 static int handle_show_allocations(int fd, int argc, char *argv[])
00108 {
00109 char *fn = NULL;
00110 size_t len = 0;
00111 unsigned int count = 0;
00112 struct tls_object *to;
00113
00114 if (argc > 3)
00115 fn = argv[3];
00116
00117 AST_LIST_LOCK(&tls_objects);
00118
00119 AST_LIST_TRAVERSE(&tls_objects, to, entry) {
00120 if (fn && strcasecmp(to->file, fn))
00121 continue;
00122
00123 ast_cli(fd, "%10d bytes allocated in %20s at line %5d of %25s (thread %p)\n",
00124 (int) to->size, to->function, to->line, to->file, (void *) to->thread);
00125 len += to->size;
00126 count++;
00127 }
00128
00129 AST_LIST_UNLOCK(&tls_objects);
00130
00131 ast_cli(fd, "%10d bytes allocated in %d allocation%s\n", (int) len, count, count > 1 ? "s" : "");
00132
00133 return RESULT_SUCCESS;
00134 }
00135
00136 static int handle_show_summary(int fd, int argc, char *argv[])
00137 {
00138 char *fn = NULL;
00139 size_t len = 0;
00140 unsigned int count = 0;
00141 struct tls_object *to;
00142 struct file {
00143 const char *name;
00144 size_t len;
00145 unsigned int count;
00146 AST_LIST_ENTRY(file) entry;
00147 } *file;
00148 AST_LIST_HEAD_NOLOCK_STATIC(file_summary, file);
00149
00150 if (argc > 3)
00151 fn = argv[3];
00152
00153 AST_LIST_LOCK(&tls_objects);
00154
00155 AST_LIST_TRAVERSE(&tls_objects, to, entry) {
00156 if (fn && strcasecmp(to->file, fn))
00157 continue;
00158
00159 AST_LIST_TRAVERSE(&file_summary, file, entry) {
00160 if ((!fn && (file->name == to->file)) || (fn && (file->name == to->function)))
00161 break;
00162 }
00163
00164 if (!file) {
00165 file = alloca(sizeof(*file));
00166 memset(file, 0, sizeof(*file));
00167 file->name = fn ? to->function : to->file;
00168 AST_LIST_INSERT_TAIL(&file_summary, file, entry);
00169 }
00170
00171 file->len += to->size;
00172 file->count++;
00173 }
00174
00175 AST_LIST_UNLOCK(&tls_objects);
00176
00177 AST_LIST_TRAVERSE(&file_summary, file, entry) {
00178 len += file->len;
00179 count += file->count;
00180 if (fn) {
00181 ast_cli(fd, "%10d bytes in %d allocation%ss in function %s\n",
00182 (int) file->len, file->count, file->count > 1 ? "s" : "", file->name);
00183 } else {
00184 ast_cli(fd, "%10d bytes in %d allocation%s in file %s\n",
00185 (int) file->len, file->count, file->count > 1 ? "s" : "", file->name);
00186 }
00187 }
00188
00189 ast_cli(fd, "%10d bytes allocated in %d allocation%s\n", (int) len, count, count > 1 ? "s" : "");
00190
00191 return RESULT_SUCCESS;
00192 }
00193
00194 static struct ast_cli_entry cli[] = {
00195 {
00196 .cmda = { "threadstorage", "show", "allocations", NULL },
00197 .handler = handle_show_allocations,
00198 .summary = "Display outstanding thread local storage allocations",
00199 .usage =
00200 "Usage: threadstorage show allocations [<file>]\n"
00201 " Dumps a list of all thread-specific memory allocations,\n"
00202 "optionally limited to those from a specific file\n",
00203 },
00204 {
00205 .cmda = { "threadstorage", "show", "summary", NULL },
00206 .handler = handle_show_summary,
00207 .summary = "Summarize outstanding memory allocations",
00208 .usage =
00209 "Usage: threadstorage show summary [<file>]\n"
00210 " Summarizes thread-specific memory allocations by file, or optionally\n"
00211 "by function, if a file is specified\n",
00212 },
00213 };
00214
00215 void threadstorage_init(void)
00216 {
00217 ast_cli_register_multiple(cli, sizeof(cli) / sizeof(cli[0]));
00218 }
00219
00220 #else
00221
00222 void threadstorage_init(void)
00223 {
00224 }
00225
00226 #endif
00227