-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Frederic Weisbecker
authored and
Ingo Molnar
committed
Dec 29, 2008
1 parent
36553af
commit d2e2513
Showing
5 changed files
with
272 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
refs/heads/master: f633cef0200bbaec539e2dbb0bc4bed7f022f98b | ||
refs/heads/master: dbd0b4b33074aa6b7832a9d9a5bd985eca5c1aa2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
/* | ||
* Infrastructure for statistic tracing (histogram output). | ||
* | ||
* Copyright (C) 2008 Frederic Weisbecker <fweisbec@gmail.com> | ||
* | ||
* Based on the code from trace_branch.c which is | ||
* Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com> | ||
* | ||
*/ | ||
|
||
|
||
#include <linux/list.h> | ||
#include <linux/seq_file.h> | ||
#include <linux/debugfs.h> | ||
#include "trace.h" | ||
|
||
|
||
/* List of stat entries from a tracer */ | ||
struct trace_stat_list { | ||
struct list_head list; | ||
void *stat; | ||
}; | ||
|
||
static struct trace_stat_list stat_list; | ||
|
||
/* | ||
* This is a copy of the current tracer to avoid racy | ||
* and dangerous output while the current tracer is | ||
* switched. | ||
*/ | ||
static struct tracer current_tracer; | ||
|
||
/* | ||
* Protect both the current tracer and the global | ||
* stat list. | ||
*/ | ||
static DEFINE_MUTEX(stat_list_mutex); | ||
|
||
|
||
static void reset_stat_list(void) | ||
{ | ||
struct trace_stat_list *node; | ||
struct list_head *next; | ||
|
||
if (list_empty(&stat_list.list)) | ||
return; | ||
|
||
node = list_entry(stat_list.list.next, struct trace_stat_list, list); | ||
next = node->list.next; | ||
|
||
while (&node->list != next) { | ||
kfree(node); | ||
node = list_entry(next, struct trace_stat_list, list); | ||
} | ||
kfree(node); | ||
|
||
INIT_LIST_HEAD(&stat_list.list); | ||
} | ||
|
||
void init_tracer_stat(struct tracer *trace) | ||
{ | ||
mutex_lock(&stat_list_mutex); | ||
current_tracer = *trace; | ||
mutex_unlock(&stat_list_mutex); | ||
} | ||
|
||
/* | ||
* For tracers that don't provide a stat_cmp callback. | ||
* This one will force an immediate insertion on tail of | ||
* the list. | ||
*/ | ||
static int dummy_cmp(void *p1, void *p2) | ||
{ | ||
return 1; | ||
} | ||
|
||
/* | ||
* Initialize the stat list at each trace_stat file opening. | ||
* All of these copies and sorting are required on all opening | ||
* since the stats could have changed between two file sessions. | ||
*/ | ||
static int stat_seq_init(void) | ||
{ | ||
struct trace_stat_list *iter_entry, *new_entry; | ||
void *prev_stat; | ||
int ret = 0; | ||
int i; | ||
|
||
mutex_lock(&stat_list_mutex); | ||
reset_stat_list(); | ||
|
||
if (!current_tracer.stat_start || !current_tracer.stat_next || | ||
!current_tracer.stat_show) | ||
goto exit; | ||
|
||
if (!current_tracer.stat_cmp) | ||
current_tracer.stat_cmp = dummy_cmp; | ||
|
||
/* | ||
* The first entry. Actually this is the second, but the first | ||
* one (the stat_list head) is pointless. | ||
*/ | ||
new_entry = kmalloc(sizeof(struct trace_stat_list), GFP_KERNEL); | ||
if (!new_entry) { | ||
ret = -ENOMEM; | ||
goto exit; | ||
} | ||
|
||
INIT_LIST_HEAD(&new_entry->list); | ||
list_add(&new_entry->list, &stat_list.list); | ||
new_entry->stat = current_tracer.stat_start(); | ||
|
||
prev_stat = new_entry->stat; | ||
|
||
/* | ||
* Iterate over the tracer stat entries and store them in a sorted | ||
* list. | ||
*/ | ||
for (i = 1; ; i++) { | ||
new_entry = kmalloc(sizeof(struct trace_stat_list), GFP_KERNEL); | ||
if (!new_entry) { | ||
ret = -ENOMEM; | ||
goto exit_free_list; | ||
} | ||
|
||
INIT_LIST_HEAD(&new_entry->list); | ||
new_entry->stat = current_tracer.stat_next(prev_stat, i); | ||
|
||
/* End of insertion */ | ||
if (!new_entry->stat) | ||
break; | ||
|
||
list_for_each_entry(iter_entry, &stat_list.list, list) { | ||
/* Insertion with a descendent sorting */ | ||
if (current_tracer.stat_cmp(new_entry->stat, | ||
iter_entry->stat) > 0) { | ||
|
||
list_add_tail(&new_entry->list, | ||
&iter_entry->list); | ||
break; | ||
|
||
/* The current smaller value */ | ||
} else if (list_is_last(&iter_entry->list, | ||
&stat_list.list)) { | ||
list_add(&new_entry->list, &iter_entry->list); | ||
break; | ||
} | ||
} | ||
|
||
prev_stat = new_entry->stat; | ||
} | ||
exit: | ||
mutex_unlock(&stat_list_mutex); | ||
return ret; | ||
|
||
exit_free_list: | ||
reset_stat_list(); | ||
mutex_unlock(&stat_list_mutex); | ||
return ret; | ||
} | ||
|
||
|
||
static void *stat_seq_start(struct seq_file *s, loff_t *pos) | ||
{ | ||
struct trace_stat_list *l = (struct trace_stat_list *)s->private; | ||
|
||
/* Prevent from tracer switch or stat_list modification */ | ||
mutex_lock(&stat_list_mutex); | ||
|
||
/* If we are in the beginning of the file, print the headers */ | ||
if (!*pos && current_tracer.stat_headers) | ||
current_tracer.stat_headers(s); | ||
|
||
return seq_list_start(&l->list, *pos); | ||
} | ||
|
||
static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos) | ||
{ | ||
struct trace_stat_list *l = (struct trace_stat_list *)s->private; | ||
|
||
return seq_list_next(p, &l->list, pos); | ||
} | ||
|
||
static void stat_seq_stop(struct seq_file *m, void *p) | ||
{ | ||
mutex_unlock(&stat_list_mutex); | ||
} | ||
|
||
static int stat_seq_show(struct seq_file *s, void *v) | ||
{ | ||
struct trace_stat_list *l = list_entry(v, struct trace_stat_list, list); | ||
return current_tracer.stat_show(s, l->stat); | ||
} | ||
|
||
static const struct seq_operations trace_stat_seq_ops = { | ||
.start = stat_seq_start, | ||
.next = stat_seq_next, | ||
.stop = stat_seq_stop, | ||
.show = stat_seq_show | ||
}; | ||
|
||
static int tracing_stat_open(struct inode *inode, struct file *file) | ||
{ | ||
int ret; | ||
|
||
ret = seq_open(file, &trace_stat_seq_ops); | ||
if (!ret) { | ||
struct seq_file *m = file->private_data; | ||
m->private = &stat_list; | ||
ret = stat_seq_init(); | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
|
||
/* | ||
* Avoid consuming memory with our now useless list. | ||
*/ | ||
static int tracing_stat_release(struct inode *i, struct file *f) | ||
{ | ||
mutex_lock(&stat_list_mutex); | ||
reset_stat_list(); | ||
mutex_unlock(&stat_list_mutex); | ||
return 0; | ||
} | ||
|
||
static const struct file_operations tracing_stat_fops = { | ||
.open = tracing_stat_open, | ||
.read = seq_read, | ||
.llseek = seq_lseek, | ||
.release = tracing_stat_release | ||
}; | ||
|
||
static int __init tracing_stat_init(void) | ||
{ | ||
struct dentry *d_tracing; | ||
struct dentry *entry; | ||
|
||
INIT_LIST_HEAD(&stat_list.list); | ||
d_tracing = tracing_init_dentry(); | ||
|
||
entry = debugfs_create_file("trace_stat", 0444, d_tracing, | ||
NULL, | ||
&tracing_stat_fops); | ||
if (!entry) | ||
pr_warning("Could not create debugfs " | ||
"'trace_stat' entry\n"); | ||
return 0; | ||
} | ||
fs_initcall(tracing_stat_init); |