Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753868AbbFXOSZ (ORCPT ); Wed, 24 Jun 2015 10:18:25 -0400 Received: from smtprelay0116.hostedemail.com ([216.40.44.116]:57600 "EHLO smtprelay.hostedemail.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752723AbbFXORu (ORCPT ); Wed, 24 Jun 2015 10:17:50 -0400 X-Session-Marker: 726F737465647440676F6F646D69732E6F7267 X-Spam-Summary: 2,0,0,,d41d8cd98f00b204,rostedt@goodmis.org,:::::,RULES_HIT:327:355:379:541:599:960:966:973:988:989:1260:1277:1311:1313:1314:1345:1359:1434:1437:1515:1516:1518:1593:1594:1605:1730:1747:1777:1792:2196:2198:2199:2200:2393:2559:2562:2693:2892:3138:3139:3140:3141:3142:3622:3865:3866:3867:3868:3870:3872:3873:3874:4250:4321:4362:4385:4605:5007:6261:6299:7774:7875:7903:7904:8531:8603:9010:10004:10848:10967:11026:11232:11233:11914:12043:12050:12289:12291:12294:12296:12438:12517:12519:12555:12663:12683:12740:13138:13231:21080:21094,0,RBL:none,CacheIP:none,Bayesian:0.5,0.5,0.5,Netcheck:none,DomainCache:0,MSF:not bulk,SPF:fn,MSBL:0,DNSBL:none,Custom_rules:0:0:0 X-HE-Tag: mist60_440e6bf655e4e X-Filterd-Recvd-Size: 37045 Date: Wed, 24 Jun 2015 10:17:45 -0400 From: Steven Rostedt To: Josef Bacik Cc: , Subject: Re: [PATCH] trace-cmd: add a kernel memory leak detector Message-ID: <20150624101745.20669cec@gandalf.local.home> In-Reply-To: <1435100799-32723-1-git-send-email-jbacik@fb.com> References: <1435100799-32723-1-git-send-email-jbacik@fb.com> X-Mailer: Claws Mail 3.11.1 (GTK+ 2.24.25; x86_64-pc-linux-gnu) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 35326 Lines: 1157 On Tue, 23 Jun 2015 16:06:39 -0700 Josef Bacik wrote: > I needed to track down a very slow memory leak so I adapted the same approach > trace-cmd profile uses to track kernel memory allocations. You run this with > > trace-cmd kmemleak > > and then you can kill -SIGUSR2 to get current status updates, or > just stop the process when you are ready. It will tell you how much was lost > and the size of the objects that were allocated, along with the tracebacks and > the counts of the allocators. Thanks, Thanks! I'll take a look at this today. I'm hoping to release 2.6 soon. Can you write up another patch that adds a man page for this? Documentation/trace-cmd-kmemleak.1.txt -- Steve > > Signed-off-by: Josef Bacik > --- > Makefile | 2 +- > trace-cmd.c | 3 +- > trace-cmd.h | 6 + > trace-hash.h | 5 + > trace-input.c | 23 +++ > trace-kmemleak.c | 552 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > trace-local.h | 20 +- > trace-profile.c | 10 +- > trace-read.c | 13 +- > trace-record.c | 71 +++++-- > trace-stream.c | 12 +- > trace-usage.c | 6 + > 12 files changed, 680 insertions(+), 43 deletions(-) > create mode 100644 trace-kmemleak.c > > diff --git a/Makefile b/Makefile > index 402f711..1e3626e 100644 > --- a/Makefile > +++ b/Makefile > @@ -308,7 +308,7 @@ TRACE_GUI_OBJS = trace-filter.o trace-compat.o trace-filter-hash.o trace-dialog. > trace-xml.o > TRACE_CMD_OBJS = trace-cmd.o trace-record.o trace-read.o trace-split.o trace-listen.o \ > trace-stack.o trace-hist.o trace-mem.o trace-snapshot.o trace-stat.o \ > - trace-hash.o trace-profile.o trace-stream.o > + trace-hash.o trace-profile.o trace-stream.o trace-kmemleak.o > TRACE_VIEW_OBJS = trace-view.o trace-view-store.o > TRACE_GRAPH_OBJS = trace-graph.o trace-plot.o trace-plot-cpu.o trace-plot-task.o > TRACE_VIEW_MAIN_OBJS = trace-view-main.o $(TRACE_VIEW_OBJS) $(TRACE_GUI_OBJS) > diff --git a/trace-cmd.c b/trace-cmd.c > index 4c5b564..fc30c3c 100644 > --- a/trace-cmd.c > +++ b/trace-cmd.c > @@ -483,7 +483,8 @@ int main (int argc, char **argv) > strcmp(argv[1], "stream") == 0 || > strcmp(argv[1], "profile") == 0 || > strcmp(argv[1], "restart") == 0 || > - strcmp(argv[1], "reset") == 0) { > + strcmp(argv[1], "reset") == 0 || > + strcmp(argv[1], "kmemleak") == 0) { > trace_record(argc, argv); > exit(0); > > diff --git a/trace-cmd.h b/trace-cmd.h > index 7bce2a5..50e2b79 100644 > --- a/trace-cmd.h > +++ b/trace-cmd.h > @@ -103,6 +103,9 @@ struct tracecmd_ftrace { > int long_size; > }; > > +typedef void (*trace_show_data_func)(struct tracecmd_input *handle, > + struct pevent_record *record); > + > struct tracecmd_input *tracecmd_alloc(const char *file); > struct tracecmd_input *tracecmd_alloc_fd(int fd); > struct tracecmd_input *tracecmd_open(const char *file); > @@ -184,6 +187,9 @@ tracecmd_get_cursor(struct tracecmd_input *handle, int cpu); > int tracecmd_ftrace_overrides(struct tracecmd_input *handle, struct tracecmd_ftrace *finfo); > struct pevent *tracecmd_get_pevent(struct tracecmd_input *handle); > bool tracecmd_get_use_trace_clock(struct tracecmd_input *handle); > +trace_show_data_func tracecmd_get_show_data_func(struct tracecmd_input *handle); > +void tracecmd_set_show_data_func(struct tracecmd_input *handle, > + trace_show_data_func func); > > char *tracecmd_get_tracing_file(const char *name); > void tracecmd_put_tracing_file(char *name); > diff --git a/trace-hash.h b/trace-hash.h > index 2529f4d..2636a9c 100644 > --- a/trace-hash.h > +++ b/trace-hash.h > @@ -44,9 +44,14 @@ static inline void trace_hash_del(struct trace_hash_item *item) > { > struct trace_hash_item *prev = item->prev; > > + if (!prev) > + return; > + > prev->next = item->next; > if (item->next) > item->next->prev = prev; > + item->next = NULL; > + item->prev = NULL; > } > > #define trace_hash_for_each_bucket(bucket, hash) \ > diff --git a/trace-input.c b/trace-input.c > index 4120189..bb8076d 100644 > --- a/trace-input.c > +++ b/trace-input.c > @@ -37,6 +37,7 @@ > #include > > #include "trace-cmd-local.h" > +#include "trace-local.h" > #include "kbuffer.h" > #include "list.h" > > @@ -108,6 +109,9 @@ struct tracecmd_input { > size_t ftrace_files_start; > size_t event_files_start; > size_t total_file_size; > + > + /* For custom profilers. */ > + trace_show_data_func show_data_func; > }; > > __thread struct tracecmd_input *tracecmd_curr_thread_handle; > @@ -2938,3 +2942,22 @@ bool tracecmd_get_use_trace_clock(struct tracecmd_input *handle) > { > return handle->use_trace_clock; > } > + > +/** > + * tracecmd_get_show_data_func - return the show data func > + * @handle: input handle for the trace.dat file > + */ > +trace_show_data_func tracecmd_get_show_data_func(struct tracecmd_input *handle) > +{ > + return handle->show_data_func; > +} > + > +/** > + * tracecmd_set_show_data_func - set the show data func > + * @handle: input handle for the trace.dat file > + */ > +void tracecmd_set_show_data_func(struct tracecmd_input *handle, > + trace_show_data_func func) > +{ > + handle->show_data_func = func; > +} > diff --git a/trace-kmemleak.c b/trace-kmemleak.c > new file mode 100644 > index 0000000..2e288fe > --- /dev/null > +++ b/trace-kmemleak.c > @@ -0,0 +1,552 @@ > +#define _LARGEFILE64_SOURCE > +#include > +#include > +#include > +#include > +#include > + > +#include "trace-local.h" > +#include "trace-hash.h" > +#include "list.h" > + > +#define memory_from_item(item) container_of(item, struct memory, hash) > +#define memory_from_phash(item) container_of(item, struct memory, phash) > +#define leak_from_item(item) container_of(item, struct memory_leak, hash) > +#define edata_from_item(item) container_of(item, struct event_data, hash) > +#define stack_from_item(item) container_of(item, struct stack_trace, hash) > + > +struct kmemleak_handle { > + struct trace_hash event_hash; > + struct trace_hash alloc_hash; > + struct trace_hash pid_hash; > + struct tracecmd_input *handle; > + struct pevent *pevent; > + struct format_field *common_pid; > + struct kmemleak_handle *next; > +}; > + > +struct memory { > + struct trace_hash_item hash; > + struct trace_hash_item phash; > + unsigned long pid; > + unsigned long ptr; > + size_t alloc_size; > + unsigned long stack_size; > + char *caller; > +}; > + > +struct stack_trace { > + struct trace_hash_item hash; > + char *caller; > + unsigned long size; > + int count; > +}; > + > +struct memory_leak { > + struct trace_hash_item hash; > + size_t total_lost; > + struct trace_hash stack_hash; > +}; > + > +struct event_data; > +typedef void (*event_handler)(struct kmemleak_handle *h, > + struct pevent_record *record, > + struct event_data *edata); > + > +struct event_data { > + struct trace_hash_item hash; > + int id; > + struct format_field *ptr_field; > + struct format_field *data_field; > + event_handler handler; > +}; > + > +static struct kmemleak_handle *handles = NULL; > +static struct kmemleak_handle *last_handle = NULL; > + > +static int match_pid(struct trace_hash_item *item, void *data) > +{ > + struct memory *mem = memory_from_phash(item); > + unsigned long pid = (unsigned long)data; > + > + return mem->pid == pid; > +} > + > +static void handle_kmalloc(struct kmemleak_handle *h, > + struct pevent_record *record, > + struct event_data *edata) > +{ > + struct memory *mem; > + unsigned long long ptr, size, pid; > + int ret; > + > + mem = malloc_or_die(sizeof(*mem)); > + memset(mem, 0, sizeof(*mem)); > + ret = pevent_read_number_field(edata->ptr_field, record->data, &ptr); > + ret |= pevent_read_number_field(edata->data_field, record->data, > + &size); > + ret |= pevent_read_number_field(h->common_pid, record->data, &pid); > + if (ret) > + die("missing important filed in kmalloc"); > + > + mem->ptr = ptr; > + mem->pid = pid; > + mem->alloc_size = size; > + mem->hash.key = trace_hash(mem->ptr); > + trace_hash_add(&h->alloc_hash, &mem->hash); > + > + mem->phash.key = trace_hash(pid); > + trace_hash_add(&h->pid_hash, &mem->phash); > +} > + > +static void handle_stacktrace(struct kmemleak_handle *h, > + struct pevent_record *record, > + struct event_data *edata) > +{ > + struct memory *mem; > + struct trace_hash_item *item; > + void *caller; > + unsigned long long size, pid; > + int ret; > + > + ret = pevent_read_number_field(h->common_pid, record->data, &pid); > + if (ret) > + die("missing pid field"); > + item = trace_hash_find(&h->pid_hash, trace_hash(pid), match_pid, > + (unsigned long *)pid); > + if (!item) > + return; > + mem = memory_from_phash(item); > + trace_hash_del(item); > + > + size = record->size - edata->data_field->offset; > + caller = record->data + edata->data_field->offset; > + > + mem->stack_size = size; > + mem->caller = malloc_or_die(size); > + memset(mem->caller, 0, size); > + memcpy(mem->caller, caller, size); > +} > + > +static int match_memory(struct trace_hash_item *item, void *data) > +{ > + struct memory *mem = memory_from_item(item); > + unsigned long ptr = (unsigned long)data; > + > + return mem->ptr == ptr; > +} > + > +static void handle_kfree(struct kmemleak_handle *h, > + struct pevent_record *record, > + struct event_data *edata) > +{ > + struct memory *mem; > + struct trace_hash_item *item; > + unsigned long long ptr; > + unsigned long long key; > + int ret; > + > + ret = pevent_read_number_field(edata->ptr_field, > + record->data, &ptr); > + if (ret) > + die("missing important field in kfree/kmemcache_free"); > + > + key = trace_hash(ptr); > + item = trace_hash_find(&h->alloc_hash, key, match_memory, > + (unsigned long *)ptr); > + if (!item) > + return; > + mem = memory_from_item(item); > + trace_hash_del(item); > + trace_hash_del(&mem->phash); > + if (mem->caller) > + free(mem->caller); > + free(mem); > +} > + > +static void handle_missed_events(struct kmemleak_handle *h) > +{ > + struct trace_hash_item **bucket; > + struct trace_hash_item *item; > + struct memory *mem; > + > + trace_hash_for_each_bucket(bucket, &h->alloc_hash) { > + trace_hash_while_item(item, bucket) { > + mem = memory_from_item(item); > + trace_hash_del(item); > + trace_hash_del(&mem->phash); > + if (mem->caller) > + free(mem->caller); > + free(mem); > + } > + } > + > + trace_hash_for_each_bucket(bucket, &h->pid_hash) { > + trace_hash_while_item(item, bucket) { > + mem = memory_from_phash(item); > + printf("found something on the pid hash\n"); > + trace_hash_del(item); > + if (mem->caller) > + free(mem->caller); > + free(mem); > + } > + } > + fprintf(stderr, "Missed events, results won't be accurate\n"); > +} > + > +static void usr1_handler(int signum) > +{ > + trace_kmemleak_output(); > +} > + > +void trace_kmemleak_global_init(void) > +{ > + struct sigaction new_action; > + > + new_action.sa_handler = usr1_handler; > + sigemptyset(&new_action.sa_mask); > + new_action.sa_flags = 0; > + > + sigaction(SIGUSR2, &new_action, NULL); > +} > + > +static int compare_stacks(const void *a, const void *b) > +{ > + struct stack_trace * const *A = a; > + struct stack_trace * const *B = b; > + > + if ((*A)->count < (*B)->count) > + return 1; > + else if ((*A)->count > (*B)->count) > + return -1; > + return 0; > +} > + > +static unsigned long long > +stack_value(struct stack_trace *stack, int longsize, int level) > +{ > + void *ptr; > + > + ptr = &stack->caller[longsize * level]; > + return longsize == 8 ? *(u64 *)ptr : *(unsigned *)ptr; > +} > + > +static void output_stack(struct kmemleak_handle *h, struct stack_trace *stack) > +{ > + const char *func; > + unsigned long long val; > + int longsize = pevent_get_long_size(h->pevent); > + int cnt = stack->size / longsize, i; > + > + printf("\tStack count: %d\n", stack->count); > + for (i = 0; i < cnt; i++) { > + val = stack_value(stack, longsize, i); > + func = pevent_find_function(h->pevent, val); > + if (func) > + printf("\t\t%s (0x%llx)\n", func, val); > + else > + printf("\t\t0x%llx\n", val); > + } > +} > + > +static void output_stacks(struct kmemleak_handle *h, struct memory_leak *leak) > +{ > + struct trace_hash_item **bucket; > + struct trace_hash_item *item; > + struct stack_trace **stacks; > + int nr_stacks = 0, i; > + > + trace_hash_for_each_bucket(bucket, &leak->stack_hash) { > + trace_hash_for_each_item(item, bucket) > + nr_stacks++; > + } > + > + stacks = malloc_or_die(sizeof(*stacks) * nr_stacks); > + > + nr_stacks = 0; > + trace_hash_for_each_bucket(bucket, &leak->stack_hash) { > + trace_hash_while_item(item, bucket) { > + stacks[nr_stacks++] = stack_from_item(item); > + trace_hash_del(item); > + } > + } > + > + qsort(stacks, nr_stacks, sizeof(*stacks), compare_stacks); > + > + for (i = 0; i < nr_stacks; i++) { > + output_stack(h, stacks[i]); > + free(stacks[i]->caller); > + free(stacks[i]); > + } > + free(stacks); > +} > + > +static void output_leak(struct kmemleak_handle *h, struct memory_leak *leak) > +{ > + printf("Leaked %llu bytes of size %llu\n", > + (unsigned long long)leak->total_lost, > + (unsigned long long)leak->hash.key); > + output_stacks(h, leak); > +} > + > +struct stack_match { > + void *caller; > + unsigned long size; > +}; > + > +static int match_stack(struct trace_hash_item *item, void *data) > +{ > + struct stack_trace *stack = stack_from_item(item); > + struct stack_match *match = data; > + > + if (match->size != stack->size) > + return 0; > + > + return memcmp(stack->caller, match->caller, stack->size) == 0; > +} > + > +static void add_stack(struct memory_leak *leak, void *caller, > + unsigned long size) > +{ > + struct trace_hash_item *item; > + struct stack_match match; > + struct stack_trace *stack; > + unsigned long long key; > + int i; > + > + match.caller = caller; > + match.size = size; > + > + if (size < sizeof(int)) > + return; > + > + for (key = 0, i = 0; i <= size - sizeof(int); i += sizeof(int)) > + key += trace_hash(*(int *)(caller + i)); > + > + item = trace_hash_find(&leak->stack_hash, key, match_stack, &match); > + if (!item) { > + stack = malloc_or_die(sizeof(*stack)); > + memset(stack, 0, sizeof(*stack)); > + stack->hash.key = key; > + stack->caller = malloc_or_die(size); > + memcpy(stack->caller, caller, size); > + stack->size = size; > + stack->count = 1; > + trace_hash_add(&leak->stack_hash, &stack->hash); > + } else { > + stack = stack_from_item(item); > + stack->count++; > + } > +} > + > +static int compare_leaks(const void *a, const void *b) > +{ > + struct memory_leak * const *A = a; > + struct memory_leak * const *B = b; > + > + if ((*A)->total_lost < (*B)->total_lost) > + return 1; > + else if ((*A)->total_lost > (*B)->total_lost) > + return -1; > + return 0; > +} > + > +static void output_handle(struct kmemleak_handle *h, int *total_leaks) > +{ > + struct memory_leak **leaks; > + struct trace_hash_item **bucket; > + struct trace_hash_item *item; > + struct trace_hash leak_hash; > + int nr_leaks = 0, i; > + > + /* God I hope we don't have more than 64 leak buckets */ > + if (trace_hash_init(&leak_hash, 64)) > + die("Couldn't allocate leak hash table"); > + > + trace_hash_for_each_bucket(bucket, &h->alloc_hash) { > + trace_hash_for_each_item(item, bucket) { > + struct trace_hash_item *tmp; > + struct memory *mem; > + struct memory_leak *leak; > + > + mem = memory_from_item(item); > + tmp = trace_hash_find(&leak_hash, mem->alloc_size, > + NULL, NULL); > + if (tmp) { > + leak = leak_from_item(tmp); > + leak->total_lost += mem->alloc_size; > + } else { > + leak = malloc_or_die(sizeof(*leak)); > + memset(leak, 0, sizeof(*leak)); > + leak->hash.key = mem->alloc_size; > + leak->total_lost = mem->alloc_size; > + trace_hash_init(&leak->stack_hash, 1024); > + trace_hash_add(&leak_hash, &leak->hash); > + nr_leaks++; > + } > + add_stack(leak, mem->caller, mem->stack_size); > + } > + } > + > + if (!nr_leaks) > + return; > + > + leaks = malloc_or_die(sizeof(*leaks) * nr_leaks); > + *total_leaks += nr_leaks; > + nr_leaks = 0; > + > + trace_hash_for_each_bucket(bucket, &leak_hash) { > + trace_hash_while_item(item, bucket) { > + leaks[nr_leaks++] = leak_from_item(item); > + trace_hash_del(item); > + } > + } > + > + qsort(leaks, nr_leaks, sizeof(*leaks), compare_leaks); > + > + for (i = 0; i < nr_leaks; i++) { > + output_leak(h, leaks[i]); > + trace_hash_free(&leaks[i]->stack_hash); > + free(leaks[i]); > + } > + > + free(leaks); > + trace_hash_free(&leak_hash); > +} > + > +static int match_event(struct trace_hash_item *item, void *data) > +{ > + struct event_data *edata = edata_from_item(item); > + int id = (int)(unsigned long)data; > + > + return edata->id == id; > +} > + > +void trace_kmemleak_record(struct tracecmd_input *handle, > + struct pevent_record *record) > +{ > + struct kmemleak_handle *h; > + struct pevent *pevent; > + struct event_data *edata; > + struct trace_hash_item *item; > + unsigned long id; > + > + if (last_handle && last_handle->handle == handle) > + h = last_handle; > + else { > + for (h = handles; h; h = h->next) { > + if (h->handle == handle) > + break; > + } > + if (!h) > + die("Handle not found?"); > + last_handle = h; > + } > + > + pevent = h->pevent; > + > + if (record->missed_events) > + handle_missed_events(h); > + > + id = pevent_data_type(pevent, record); > + item = trace_hash_find(&h->event_hash, trace_hash(id), match_event, > + (void *)id); > + if (!item) > + return; > + edata = edata_from_item(item); > + edata->handler(h, record, edata); > +} > + > +static void setup_fields(struct kmemleak_handle *h) > +{ > + struct event_format *event; > + struct event_data *edata; > + struct pevent *pevent = h->pevent; > + > + edata = malloc_or_die(sizeof(*edata)); > + memset(edata, 0, sizeof(*edata)); > + event = pevent_find_event_by_name(pevent, "kmem", "kmalloc"); > + if (!event) > + die("Can't find kmem:kmalloc event"); > + h->common_pid = pevent_find_common_field(event, "common_pid"); > + edata->id = event->id; > + edata->hash.key = trace_hash(edata->id); > + edata->ptr_field = pevent_find_field(event, "ptr"); > + edata->data_field = pevent_find_field(event, "bytes_alloc"); > + edata->handler = handle_kmalloc; > + if (!edata->ptr_field || !edata->data_field) > + die("Missing key fields"); > + trace_hash_add(&h->event_hash, &edata->hash); > + > + edata = malloc_or_die(sizeof(*edata)); > + memset(edata, 0, sizeof(*edata)); > + event = pevent_find_event_by_name(pevent, "kmem", "kfree"); > + if (!event) > + die("Can't find kmem:kfree event"); > + edata->id = event->id; > + edata->hash.key = trace_hash(edata->id); > + edata->ptr_field = pevent_find_field(event, "ptr"); > + edata->handler = handle_kfree; > + if (!edata->ptr_field) > + die("Missing key kfree fields"); > + trace_hash_add(&h->event_hash, &edata->hash); > + > + edata = malloc_or_die(sizeof(*edata)); > + memset(edata, 0, sizeof(*edata)); > + event = pevent_find_event_by_name(pevent, "kmem", "kmem_cache_free"); > + if (!event) > + die("Can't find kmem:kmem_cache_free event"); > + edata->id = event->id; > + edata->hash.key = trace_hash(edata->id); > + edata->ptr_field = pevent_find_field(event, "ptr"); > + edata->handler = handle_kfree; > + if (!edata->ptr_field) > + die("Missing key kmem_cache_free field"); > + trace_hash_add(&h->event_hash, &edata->hash); > + > + edata = malloc_or_die(sizeof(*edata)); > + memset(edata, 0, sizeof(*edata)); > + event = pevent_find_event_by_name(pevent, "ftrace", "kernel_stack"); > + if (!event) > + die("Can't find ftrace:kernel_stack event"); > + edata->id = event->id; > + edata->hash.key = trace_hash(edata->id); > + edata->data_field = pevent_find_field(event, "caller"); > + edata->handler = handle_stacktrace; > + if (!edata->data_field) > + die("Missing caller in stack trace"); > + trace_hash_add(&h->event_hash, &edata->hash); > +} > + > +void trace_init_kmemleak(struct tracecmd_input *handle, struct hook_list *hook, > + int global) > +{ > + struct pevent *pevent = tracecmd_get_pevent(handle); > + struct kmemleak_handle *h; > + > + tracecmd_set_show_data_func(handle, trace_kmemleak_record); > + h = malloc_or_die(sizeof(*h)); > + memset(h, 0, sizeof(*h)); > + h->next = handles; > + handles = h; > + > + trace_hash_init(&h->alloc_hash, 1024); > + trace_hash_init(&h->pid_hash, 1024); > + trace_hash_init(&h->event_hash, 16); > + h->handle = handle; > + h->pevent = pevent; > + > + setup_fields(h); > +} > + > +void trace_kmemleak_output(void) > +{ > + struct kmemleak_handle *h; > + int leaks = 0; > + > + printf("Printing kmemleak summary\n"); > + for (h = handles; h; h = h->next) > + output_handle(h, &leaks); > + if (!leaks) > + printf("Hooray no leakage!\n"); > +} > diff --git a/trace-local.h b/trace-local.h > index d9a4fac..89ad06f 100644 > --- a/trace-local.h > +++ b/trace-local.h > @@ -47,6 +47,9 @@ struct pid_record_data { > struct pevent_record *record; > }; > > +typedef void (*handle_init_func)(struct tracecmd_input *handle, > + struct hook_list *hook, int global); > + > void show_file(const char *name); > > struct tracecmd_input *read_trace_header(const char *file); > @@ -76,21 +79,23 @@ void trace_stat(int argc, char **argv); > > struct hook_list; > > -int trace_profile_record(struct tracecmd_input *handle, > - struct pevent_record *record, int cpu); > void trace_init_profile(struct tracecmd_input *handle, struct hook_list *hooks, > int global); > int trace_profile(void); > void trace_profile_set_merge_like_comms(void); > > +void trace_init_kmemleak(struct tracecmd_input *handle, struct hook_list *hook, > + int global); > +void trace_kmemleak_global_init(void); > +void trace_kmemleak_output(void); > + > struct tracecmd_input * > trace_stream_init(struct buffer_instance *instance, int cpu, int fd, int cpus, > - int profile, struct hook_list *hooks, int global); > -int trace_stream_read(struct pid_record_data *pids, int nr_pids, struct timeval *tv, > - int profile); > + struct hook_list *hooks, handle_init_func handle_init, > + int global); > +int trace_stream_read(struct pid_record_data *pids, int nr_pids, struct timeval *tv); > > -void trace_show_data(struct tracecmd_input *handle, struct pevent_record *record, > - int profile); > +void trace_show_data(struct tracecmd_input *handle, struct pevent_record *record); > > /* --- event interation --- */ > > @@ -167,6 +172,7 @@ struct buffer_instance { > int keep; > int buffer_size; > int profile; > + int kmemleak; > }; > > extern struct buffer_instance top_instance; > diff --git a/trace-profile.c b/trace-profile.c > index 2356701..aa54f0d 100644 > --- a/trace-profile.c > +++ b/trace-profile.c > @@ -698,8 +698,8 @@ find_event_data(struct handle_data *h, int id) > return NULL; > } > > -int trace_profile_record(struct tracecmd_input *handle, > - struct pevent_record *record, int cpu) > +static void trace_profile_record(struct tracecmd_input *handle, > + struct pevent_record *record) > { > static struct handle_data *last_handle; > struct pevent_record *stack_record; > @@ -708,6 +708,7 @@ int trace_profile_record(struct tracecmd_input *handle, > struct handle_data *h; > struct pevent *pevent; > unsigned long long pid; > + int cpu = record->cpu; > int id; > > if (last_handle && last_handle->handle == handle) > @@ -732,7 +733,7 @@ int trace_profile_record(struct tracecmd_input *handle, > event_data = find_event_data(h, id); > > if (!event_data) > - return -1; > + return; > > > /* Get this current PID */ > @@ -751,8 +752,6 @@ int trace_profile_record(struct tracecmd_input *handle, > free_record(stack_record); > task->last_stack = NULL; > } > - > - return 0; > } > > static struct event_data * > @@ -1225,6 +1224,7 @@ void trace_init_profile(struct tracecmd_input *handle, struct hook_list *hook, > int ret; > int i; > > + tracecmd_set_show_data_func(handle, trace_profile_record); > h = malloc_or_die(sizeof(*h)); > memset(h, 0, sizeof(*h)); > h->next = handles; > diff --git a/trace-read.c b/trace-read.c > index f4dffd6..103bc0c 100644 > --- a/trace-read.c > +++ b/trace-read.c > @@ -737,22 +737,21 @@ static void finish_wakeup(void) > trace_hash_free(&wakeup_hash); > } > > -void trace_show_data(struct tracecmd_input *handle, struct pevent_record *record, > - int profile) > +void trace_show_data(struct tracecmd_input *handle, struct pevent_record *record) > { > + trace_show_data_func func = tracecmd_get_show_data_func(handle); > struct pevent *pevent; > struct trace_seq s; > int cpu = record->cpu; > bool use_trace_clock; > > - pevent = tracecmd_get_pevent(handle); > - > test_save(record, cpu); > > - if (profile) { > - trace_profile_record(handle, record, cpu); > + if (func) { > + func(handle, record); > return; > } > + pevent = tracecmd_get_pevent(handle); > > trace_seq_init(&s); > if (record->missed_events > 0) > @@ -1109,7 +1108,7 @@ static void read_data_info(struct list_head *handle_list, enum output_type otype > } > if (last_record) { > print_handle_file(last_handle); > - trace_show_data(last_handle->handle, last_record, profile); > + trace_show_data(last_handle->handle, last_record); > free_handle_record(last_handle); > } > } while (last_record); > diff --git a/trace-record.c b/trace-record.c > index 102bfe1..cd36585 100644 > --- a/trace-record.c > +++ b/trace-record.c > @@ -67,9 +67,10 @@ enum trace_type { > TRACE_TYPE_START = (1 << 1), > TRACE_TYPE_STREAM = (1 << 2), > TRACE_TYPE_EXTRACT = (1 << 3), > - TRACE_TYPE_PROFILE = (1 << 4) | TRACE_TYPE_STREAM, > }; > > +static handle_init_func handle_init = NULL; > + > static int rt_prio; > > static int use_tcp; > @@ -489,7 +490,6 @@ static void delete_thread_data(void) > static void stop_threads(enum trace_type type) > { > struct timeval tv = { 0, 0 }; > - int profile = (type & TRACE_TYPE_PROFILE) == TRACE_TYPE_PROFILE; > int ret; > int i; > > @@ -506,7 +506,7 @@ static void stop_threads(enum trace_type type) > /* Flush out the pipes */ > if (type & TRACE_TYPE_STREAM) { > do { > - ret = trace_stream_read(pids, recorder_threads, &tv, profile); > + ret = trace_stream_read(pids, recorder_threads, &tv); > } while (ret > 0); > } > > @@ -839,7 +839,6 @@ static pid_t trace_waitpid(enum trace_type type, pid_t pid, int *status, int opt > { > struct timeval tv = { 1, 0 }; > int ret; > - int profile = (type & TRACE_TYPE_PROFILE) == TRACE_TYPE_PROFILE; > > if (type & TRACE_TYPE_STREAM) > options |= WNOHANG; > @@ -850,7 +849,7 @@ static pid_t trace_waitpid(enum trace_type type, pid_t pid, int *status, int opt > return ret; > > if (type & TRACE_TYPE_STREAM) > - trace_stream_read(pids, recorder_threads, &tv, profile); > + trace_stream_read(pids, recorder_threads, &tv); > } while (1); > } > #ifndef NO_PTRACE > @@ -1008,12 +1007,11 @@ static inline void ptrace_attach(int pid) { } > static void trace_or_sleep(enum trace_type type) > { > struct timeval tv = { 1 , 0 }; > - int profile = (type & TRACE_TYPE_PROFILE) == TRACE_TYPE_PROFILE; > > if (do_ptrace && filter_pid >= 0) > ptrace_wait(type, filter_pid); > else if (type & TRACE_TYPE_STREAM) > - trace_stream_read(pids, recorder_threads, &tv, profile); > + trace_stream_read(pids, recorder_threads, &tv); > else > sleep(10); > } > @@ -2494,7 +2492,6 @@ static void finish_network(void) > > static void start_threads(enum trace_type type, int global) > { > - int profile = (type & TRACE_TYPE_PROFILE) == TRACE_TYPE_PROFILE; > struct buffer_instance *instance; > int *brass = NULL; > int i = 0; > @@ -2518,7 +2515,7 @@ static void start_threads(enum trace_type type, int global) > die("pipe"); > pids[i].stream = trace_stream_init(instance, x, > brass[0], cpu_count, > - profile, hooks, > + hooks, handle_init, > global); > if (!pids[i].stream) > die("Creating stream for %d", i); > @@ -3334,7 +3331,8 @@ static void check_function_plugin(void) > > static int __check_doing_something(struct buffer_instance *instance) > { > - return instance->profile || instance->plugin || instance->events; > + return instance->kmemleak || instance->profile || instance->plugin || > + instance->events; > } > > static void check_doing_something(void) > @@ -3654,6 +3652,26 @@ static void enable_profile(struct buffer_instance *instance) > profile_add_event(instance, events[i], 0); > } > > +static void enable_kmemleak(struct buffer_instance *instance) > +{ > + int stacktrace = 0, i; > + char *events[] = { > + "kmem:kfree", > + "kmem:kmem_cache_free", > + NULL, > + }; > + > + if (test_stacktrace_trigger(instance)) > + stacktrace = 1; > + else > + save_option("stacktrace"); > + > + profile_add_event(instance, "kmem:kmalloc", stacktrace); > + > + for (i = 0; events[i]; i++) > + profile_add_event(instance, events[i], 0); > +} > + > static struct event_list * > create_hook_event(struct buffer_instance *instance, > const char *system, const char *event) > @@ -3703,6 +3721,7 @@ static void add_hook(struct buffer_instance *instance, const char *arg) > } > > enum { > + OPT_kmemleak = 249, > OPT_bycomm = 250, > OPT_stderr = 251, > OPT_profile = 252, > @@ -3738,7 +3757,7 @@ void trace_record (int argc, char **argv) > int neg_event = 0; > int date = 0; > int manual = 0; > - > + int kmemleak = 0; > int c; > > init_instance(instance); > @@ -3754,8 +3773,11 @@ void trace_record (int argc, char **argv) > else if ((stream = strcmp(argv[1], "stream") == 0)) > ; /* do nothing */ > else if ((profile = strcmp(argv[1], "profile") == 0)) { > + handle_init = trace_init_profile; > + events = 1; > + } else if ((kmemleak = strcmp(argv[1], "kmemleak") == 0)) { > + handle_init = trace_init_kmemleak; > events = 1; > - > } else if (strcmp(argv[1], "stop") == 0) { > int topt = 0; > for (;;) { > @@ -3873,6 +3895,7 @@ void trace_record (int argc, char **argv) > {"profile", no_argument, NULL, OPT_profile}, > {"stderr", no_argument, NULL, OPT_stderr}, > {"by-comm", no_argument, NULL, OPT_bycomm}, > + {"kmemleak", no_argument, NULL, OPT_kmemleak}, > {"help", no_argument, NULL, '?'}, > {NULL, 0, NULL, 0} > }; > @@ -4002,7 +4025,7 @@ void trace_record (int argc, char **argv) > die("only one output file allowed"); > output = optarg; > > - if (profile) { > + if (profile || kmemleak) { > int fd; > > /* pipe the output to this file instead of stdout */ > @@ -4076,6 +4099,8 @@ void trace_record (int argc, char **argv) > add_instance(instance); > if (profile) > instance->profile = 1; > + if (kmemleak) > + instance->kmemleak = 1; > break; > case 'k': > keep = 1; > @@ -4093,6 +4118,7 @@ void trace_record (int argc, char **argv) > recorder_flags |= TRACECMD_RECORD_NOSPLICE; > break; > case OPT_profile: > + handle_init = trace_init_profile; > instance->profile = 1; > events = 1; > break; > @@ -4107,6 +4133,11 @@ void trace_record (int argc, char **argv) > case OPT_bycomm: > trace_profile_set_merge_like_comms(); > break; > + case OPT_kmemleak: > + handle_init = trace_init_kmemleak; > + events = 1; > + instance->kmemleak = 1; > + break; > default: > usage(argv); > } > @@ -4131,6 +4162,8 @@ void trace_record (int argc, char **argv) > */ > if (profile && !buffer_instances) > top_instance.profile = 1; > + if (kmemleak && !buffer_instances) > + top_instance.kmemleak = 1; > > /* > * If top_instance doesn't have any plugins or events, then > @@ -4154,6 +4187,10 @@ void trace_record (int argc, char **argv) > > if (!manual && instance->profile) > enable_profile(instance); > + if (!manual && instance->kmemleak) { > + trace_kmemleak_global_init(); > + enable_kmemleak(instance); > + } > > instance->tracing_on_init_val = read_tracing_on(instance); > /* Some instances may not be created yet */ > @@ -4203,8 +4240,9 @@ void trace_record (int argc, char **argv) > else if (extract) > type = TRACE_TYPE_EXTRACT; > else if (profile) > - /* PROFILE includes the STREAM bit */ > - type = TRACE_TYPE_PROFILE; > + type = TRACE_TYPE_STREAM; > + else if (kmemleak) > + type = TRACE_TYPE_STREAM; > else > type = TRACE_TYPE_START; > > @@ -4295,6 +4333,7 @@ void trace_record (int argc, char **argv) > > if (profile) > trace_profile(); > - > + if (kmemleak) > + trace_kmemleak_output(); > exit(0); > } > diff --git a/trace-stream.c b/trace-stream.c > index 9ebe65b..b103fda 100644 > --- a/trace-stream.c > +++ b/trace-stream.c > @@ -35,7 +35,8 @@ > */ > struct tracecmd_input * > trace_stream_init(struct buffer_instance *instance, int cpu, int fd, int cpus, > - int profile, struct hook_list *hooks, int global) > + struct hook_list *hooks, handle_init_func handle_init, > + int global) > { > struct tracecmd_input *trace_input; > struct tracecmd_output *trace_output; > @@ -75,8 +76,8 @@ trace_stream_init(struct buffer_instance *instance, int cpu, int fd, int cpus, > if (tracecmd_read_headers(trace_input) < 0) > goto fail_free_input; > > - if (profile) > - trace_init_profile(trace_input, hooks, global); > + if (handle_init) > + handle_init(trace_input, hooks, global); > > make_pipe: > /* Do not block on this pipe */ > @@ -98,8 +99,7 @@ trace_stream_init(struct buffer_instance *instance, int cpu, int fd, int cpus, > return NULL; > } > > -int trace_stream_read(struct pid_record_data *pids, int nr_pids, struct timeval *tv, > - int profile) > +int trace_stream_read(struct pid_record_data *pids, int nr_pids, struct timeval *tv) > { > struct pevent_record *record; > struct pid_record_data *pid; > @@ -127,7 +127,7 @@ int trace_stream_read(struct pid_record_data *pids, int nr_pids, struct timeval > last_pid = pid; > } > if (last_pid) { > - trace_show_data(last_pid->instance->handle, last_pid->record, profile); > + trace_show_data(last_pid->instance->handle, last_pid->record); > free_record(last_pid->record); > last_pid->record = NULL; > return 1; > diff --git a/trace-usage.c b/trace-usage.c > index bdd5727..27235e5 100644 > --- a/trace-usage.c > +++ b/trace-usage.c > @@ -169,6 +169,12 @@ static struct usage_help usage_help[] = { > " -H Allows users to hook two events together for timings\n" > }, > { > + "kmemleak", > + "Streaming kmemleak detector", > + " %s kmemleak\n" > + " Uses same options as record\n" > + }, > + { > "hist", > "show a historgram of the trace.dat information", > " %s hist [-i file][-P] [file]" -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/