[CTRACER]: Lots of improvements

1. We now use relayfs
2. ostra-cg is shipped and used in the Makefile
3. README.ctracer has all the details

I followed README.ctracer and the result, in my workstation, was this one:
http://oops.ghostprotocols.net:81/acme/dwarves/callgraphs/acme_eating_his_dog_food/6.html

I've started on the 6th page as it is more interesting, having tcp and netlink
callchains, don't be disappointed by some pages having just one level of
nesting, that is just the lack of containers and aliases (to follow tcp_sock,
skb->sk, etc) from the OSTRA days, but it's getting there! :-)

Ah, it is collecting all the base types in the specified struct by doing a
struct "view", i.e. trimming the struct to have just members that are
"reducible" to basic types (int, long, char, signed and unsigned), then
reorganizing it with the code introduced in pahole --reorganize to get it to
the best layout possible, reducing the size of the entry probe points trace
entries.

Updated rpms are available at the usual place:

http://oops.ghostprotocols.net:81/acme/dwarves/rpm/

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Arnaldo Carvalho de Melo 2007-02-02 15:15:27 -02:00
parent d37f41df58
commit ce9d7e11fe
11 changed files with 1339 additions and 65 deletions

View File

@ -63,4 +63,8 @@ INSTALL(TARGETS codiff ctracer dtagnames pahole pdwtags
pfunct pglobal prefcnt RUNTIME DESTINATION /usr/bin)
INSTALL(TARGETS dwarves LIBRARY DESTINATION ${LIB_INSTALL_DIR})
INSTALL(FILES dwarves.h DESTINATION /usr/include)
INSTALL(FILES lib/Makefile lib/ctracer_jprobe.c DESTINATION ${LIB_INSTALL_DIR}/ctracer)
INSTALL(FILES ostra/ostra-cg DESTINATION /usr/bin)
INSTALL(FILES ostra/python/ostra.py DESTINATION ${LIB_INSTALL_DIR}/ctracer/python)
INSTALL(FILES lib/Makefile lib/ctracer_jprobe.c
lib/ctracer_relay.c lib/ctracer_relay.h
DESTINATION ${LIB_INSTALL_DIR}/ctracer)

View File

@ -17,3 +17,7 @@ README.ctracer
rpm/SPECS/dwarves.spec
lib/Makefile
lib/ctracer_jprobe.c
lib/ctracer_relay.c
lib/ctracer_relay.h
ostra/ostra-cg
ostra/python/ostra.py

View File

@ -19,8 +19,7 @@ Basic instructions to use ctracer:
mkdir foo
cd foo
ln -s /usr/lib/ctracer/ctracer_jprobe.c .
ln -s /usr/lib/ctracer/Makefile .
ln -s /usr/lib/ctracer/* .
make CLASS=sock # to trace struct sock methods, this one is safe, try others
# and tell me your horror (or success :-) ) story.
@ -30,25 +29,23 @@ make CLASS=sock # to trace struct sock methods, this one is safe, try others
insmod ctracer.ko
Then do a dmesg (this one was done in an Xorg session so tons of PF_LOCAL
routines printk'ed):
-> unix_peer_get: s=f6a8db80
<- unix_peer_get
<- unix_copy_addr
-> unix_peer_get: s=f6a8db80
<- unix_peer_get
-> unix_peer_get: s=f6a8db80
<- unix_peer_get
-> unix_peer_get: s=f6a8db80
<- unix_peer_get
-> unix_peer_get: s=f6a8db80
dmesg will show how many probes were successfully installed
Try keeping 'tail -f /var/log/messages' on an xterm and doing some more exciting
network activity (or if tracing other struct, relevant activity for it).
5. Do some related activity (ssh, in the above example should do)
When tired of the printk activity, just do:
6. Make sure debugfs is mounted
rmmod ctracer
[root@filo ~]# mount -t debugfs none_debugfs /sys/kernel/debug/
7. Get the log:
cat /sys/kernel/debug/ctracer0 > /tmp/ctracer.log
8. Generate the callgraph!
make callgraph
9. rmmod ctracer
Change the shipped Makefile accordingly to build a module for qemu or another test
machine.

359
ctracer.c
View File

@ -31,6 +31,22 @@ static struct cus *methods_cus;
*/
static struct cus *kprobes_cus;
/**
* Mini class, the subset of the traced class that is collected at the probes
*/
static struct class *mini_class;
/*
* Directory where to generate source files
*/
static const char *src_dir = ".";
/*
* Where to print the ctracer_methods.c file
*/
static FILE *fp_methods;
/*
* List of definitions and forward declarations already emitted for
* methods_cus and kprobes_cus, to avoid duplication.
@ -129,19 +145,229 @@ static int cu_find_methods_iterator(struct cu *cu, void *cookie)
return cu__for_each_tag(cu, find_methods_iterator, target, function__filter);
}
static void class__remove_member(struct class *self, const struct cu *cu,
struct class_member *member)
{
const size_t size = class_member__size(member, cu);
/*
* Is this the first member?
*/
if (member->tag.node.prev == &self->type.members) {
self->type.size -= size;
class__subtract_offsets_from(self, cu, member, size);
} else {
struct class_member *from_prev =
list_entry(member->tag.node.prev,
struct class_member, tag.node);
if (member->hole + size >= cu->addr_size) {
self->type.size -= size + member->hole;
class__subtract_offsets_from(self, cu, member,
size + member->hole);
} else
from_prev->hole += size + member->hole;
}
if (member->hole != 0)
self->nr_holes--;
list_del(&member->tag.node);
class_member__delete(member);
}
static size_t class__find_biggest_member_name(const struct class *self)
{
struct class_member *pos;
size_t biggest_name_len = 0;
list_for_each_entry(pos, &self->type.members, tag.node) {
const size_t len = strlen(pos->name);
if (len > biggest_name_len)
biggest_name_len = len;
}
return biggest_name_len;
}
static void class__emit_class_state_collector(const struct class *self,
const struct class *clone)
{
struct class_member *pos;
size_t len = class__find_biggest_member_name(clone);
fprintf(fp_methods,
"void ctracer__class_state(const void *from, void *to)\n"
"{\n"
"\tconst struct %s *obj = from;\n"
"\tstruct %s *mini_obj = to;\n\n",
class__name(self), class__name(clone));
list_for_each_entry(pos, &clone->type.members, tag.node) {
fprintf(fp_methods, "\tmini_obj->%-*s = obj->%s;\n",
len, pos->name, pos->name);
}
fputs("}\n\n", fp_methods);
}
static struct class *class__clone_base_types(const struct tag *tag_self,
const struct cu *cu,
const char *new_class_name)
{
struct class *self = tag__class(tag_self);
struct class_member *pos, *next;
struct class *clone = class__clone(self, new_class_name);
if (clone == NULL)
return NULL;
class__find_holes(clone, cu);
list_for_each_entry_safe(pos, next, &clone->type.members, tag.node) {
struct tag *member_type = cu__find_tag_by_id(cu, pos->tag.type);
if (member_type->tag != DW_TAG_base_type)
class__remove_member(clone, cu, pos);
}
class__reorganize(clone, cu, 0, NULL);
return clone;
}
/**
* Converter to the legacy ostra tables, will be much improved in the future.
*/
static void emit_struct_member_table_entry(FILE *fp,
int field, const char *name,
int traced, const char *hooks)
{
fprintf(fp, "%u:%s:", field, name);
if (traced)
fprintf(fp, "yes:%%object->%s:u:%s:none\n", name, hooks);
else
fprintf(fp, "no:None:None:%s:dev_null\n", hooks);
}
/**
* Generates a converter to the ostra lebacy tables format, needef by
* ostra-cg to preprocess the raw data collected from the debugfs/relay
* channel.
*/
static int class__emit_ostra_converter(const struct tag *tag_self,
const struct cu *cu)
{
const struct class *self = tag__class(tag_self);
struct class_member *pos;
struct type *type = &mini_class->type;
int field = 0, first = 1;
char filename[128];
char parm_list[1024];
char *p = parm_list;
size_t n;
size_t plen = sizeof(parm_list);
FILE *fp_fields, *fp_converter;
snprintf(filename, sizeof(filename), "%s/%s.fields",
src_dir, class__name(self));
fp_fields = fopen(filename, "w");
if (fp_fields == NULL) {
fprintf(stderr, "ctracer: couldn't create %s\n", filename);
exit(EXIT_FAILURE);
}
snprintf(filename, sizeof(filename), "%s/ctracer2ostra.c", src_dir);
fp_converter = fopen(filename, "w");
if (fp_converter == NULL) {
fprintf(stderr, "ctracer: couldn't create %s\n", filename);
exit(EXIT_FAILURE);
}
fputs("#include <stdio.h>\n"
"#include <string.h>\n"
"#include \"ctracer_relay.h\"\n\n", fp_converter);
class__print(class__tag(mini_class), cu, NULL, NULL, 0, fp_converter);
emit_struct_member_table_entry(fp_fields, field++, "action", 0,
"entry,exit");
emit_struct_member_table_entry(fp_fields, field++, "function_id", 0,
"entry,exit");
emit_struct_member_table_entry(fp_fields, field++, "object", 1,
"entry,exit");
fputs("\n"
"int main(void)\n"
"{\n"
"\twhile (1) {\n"
"\t\tstruct trace_entry hdr;\n"
"\t\tstruct ctracer__mini_sock obj;\n"
"\n"
"\t\tif (read(0, &hdr, sizeof(hdr)) != sizeof(hdr))\n"
"\t\t\tbreak;\n"
"\n"
"\t\tfprintf(stdout, \"%u.%06u %c:%llu:%p\",\n"
"\t\t\thdr.sec, hdr.usec,\n"
"\t\t\thdr.probe_type ? 'o' : 'i',\n"
"\t\t\thdr.function_id,\n"
"\t\t\thdr.object);\n"
"\n"
"\t\tif (hdr.probe_type) {\n"
"\t\t\tfputc('\\n', stdout);\n"
"\t\t\tcontinue;\n"
"\t\t}\n"
"\n"
"\t\tif (read(0, &obj, sizeof(obj)) != sizeof(obj))\n"
"\t\t\tbreak;\n"
"\t\tfprintf(stdout,\n"
"\t\t\t\":",
fp_converter);
list_for_each_entry(pos, &type->members, tag.node) {
if (first)
first = 0;
else {
fputc(':', fp_converter);
n = snprintf(p, plen, ",\n\t\t\t ");
plen -= n; p += n;
}
fprintf(fp_converter, "%%u");
n = snprintf(p, plen, "obj.%s", pos->name);
plen -= n; p += n;
emit_struct_member_table_entry(fp_fields, field++,
pos->name, 1, "entry");
}
fprintf(fp_converter,
"\\n\",\n\t\t\t %s);\n"
"\t}\n"
"\treturn 0;\n"
"}\n", parm_list);
fclose(fp_fields);
fclose(fp_converter);
return 0;
}
static int class__emit_subset(const struct tag *tag_self, const struct cu *cu)
{
struct class *self = tag__class(tag_self);
int err = -1;
char mini_class_name[128];
snprintf(mini_class_name, sizeof(mini_class_name), "ctracer__mini_%s",
class__name(self));
mini_class = class__clone_base_types(tag_self, cu, mini_class_name);
if (mini_class == NULL)
goto out;
class__print(class__tag(mini_class), cu, NULL, NULL, 0, fp_methods);
fputc('\n', fp_methods);
class__emit_class_state_collector(self, mini_class);
err = 0;
out:
return err;
}
/*
* Emit the kprobes routine for one of the selected "methods", later we'll
* put this into the 'kprobes' table, in cu_emit_kprobes_table_iterator.
*
* This marks the function entry, function__emit_kretprobes will emit the
* probe for the function exit.
*
* For now it just printks the function name and the pointer, upcoming patches
* will use relayfs, just like blktrace does, using the struct definition to
* collect the specified subset of the struct members, just like OSTRA did,
* see an example of post processing at:
*
* http://oops.ghostprotocols.net:81/dccp/ostra/delay_100ms_loss20percent_packet_size_256/
*/
static int function__emit_kprobes(struct function *self, const struct cu *cu,
const struct tag *target)
@ -153,8 +379,9 @@ static int function__emit_kprobes(struct function *self, const struct cu *cu,
snprintf(jprobe_name, sizeof(jprobe_name), "jprobe_entry__%s", name);
ftype__snprintf(&self->proto, cu, bf, sizeof(bf), jprobe_name, 0, 0, 0);
printf("static %s\n"
"{\n", bf);
fprintf(fp_methods,
"static %s\n"
"{\n", bf);
list_for_each_entry(pos, &self->proto.parms, tag.node) {
struct tag *type = cu__find_tag_by_id(cu, pos->tag.type);
@ -166,18 +393,21 @@ static int function__emit_kprobes(struct function *self, const struct cu *cu,
if (type == NULL || type->id != target->id)
continue;
printf("\tprintk(\"-> %s: %s=%%p\\n\", %s);\n",
name, pos->name, pos->name);
fprintf(fp_methods,
"\tctracer__method_entry(%#llx, %s, %u);\n",
self->proto.tag.id, pos->name,
class__size(mini_class));
break;
}
printf("\n\tjprobe_return();\n"
"\t/* NOTREACHED */%s\n}\n\n",
self->proto.tag.type != 0 ? "\n\treturn 0;" : "");
fprintf(fp_methods, "\tjprobe_return();\n"
"\t/* NOTREACHED */%s\n}\n\n",
self->proto.tag.type != 0 ? "\n\treturn 0;" : "");
printf("static struct jprobe jprobe__%s = {\n"
"\t.kp = { .symbol_name = \"%s\", },\n"
"\t.entry = (kprobe_opcode_t *)jprobe_entry__%s,\n"
"};\n\n", name, name, name);
fprintf(fp_methods, "static struct jprobe jprobe__%s = {\n"
"\t.kp = { .symbol_name = \"%s\", },\n"
"\t.entry = (kprobe_opcode_t *)jprobe_entry__%s,\n"
"};\n\n", name, name, name);
return 0;
}
@ -195,7 +425,7 @@ static int cu_emit_kprobes_iterator(struct cu *cu, void *cookie)
continue;
pos->priv = (void *)1; /* Mark as visited, for the table iterator */
cus__emit_ftype_definitions(methods_cus, cu,
&pos->proto, stdout);
&pos->proto, fp_methods);
function__emit_kprobes(pos, cu, target);
}
@ -213,8 +443,11 @@ static int cu_emit_kprobes_table_iterator(struct cu *cu, void *cookie __unused)
struct function *pos;
list_for_each_entry(pos, &cu->tool_list, tool_node)
if (pos->priv != NULL)
printf("\t&jprobe__%s,\n", function__name(pos, cu));
if (pos->priv != NULL) {
const char *name = function__name(pos, cu);
fprintf(fp_methods, "\t&jprobe__%s,\n", name);
fprintf(cookie, "%llu:%s\n", pos->proto.tag.id, name);
}
return 0;
}
@ -237,16 +470,18 @@ static void function__emit_kretprobes(struct function *self,
{
const char *name = function__name(self, cu);
printf("static int kretprobe_handler__%s(struct kretprobe_instance *ri, "
"struct pt_regs *regs)\n"
"{\n"
"\tprintk(\"<- %s\\n\");\n"
"\treturn 0;\n"
"}\n\n", name, name);
printf("static struct kretprobe kretprobe__%s = {\n"
"\t.kp = { .symbol_name = \"%s\", },\n"
"\t.handler = (kretprobe_handler_t)kretprobe_handler__%s,\n"
"};\n\n", name, name, name);
fprintf(fp_methods,
"static int kretprobe_handler__%s(struct kretprobe_instance *ri, "
"struct pt_regs *regs)\n"
"{\n"
"\tctracer__method_exit(%#llx);\n"
"\treturn 0;\n"
"}\n\n", name, self->proto.tag.id);
fprintf(fp_methods,
"static struct kretprobe kretprobe__%s = {\n"
"\t.kp = { .symbol_name = \"%s\", },\n"
"\t.handler = (kretprobe_handler_t)kretprobe_handler__%s,\n"
"};\n\n", name, name, name);
}
/*
@ -281,7 +516,8 @@ static int cu_emit_kretprobes_table_iterator(struct cu *cu,
list_for_each_entry(pos, &cu->tool_list, tool_node)
if (pos->priv != NULL)
printf("\t&kretprobe__%s,\n", function__name(pos, cu));
fprintf(fp_methods, "\t&kretprobe__%s,\n",
function__name(pos, cu));
return 0;
}
@ -301,9 +537,10 @@ static void emit_function_defs(const char *fn)
if (f != NULL) {
cus__emit_ftype_definitions(kprobes_cus, cu,
&tag__function(f)->proto, stdout);
tag__print(f, cu, NULL, NULL, 0, stdout);
puts(";\n");
&tag__function(f)->proto,
fp_methods);
tag__print(f, cu, NULL, NULL, 0, fp_methods);
fputs(";\n", fp_methods);
}
}
@ -316,8 +553,8 @@ static void emit_struct_defs(const char *name)
struct cu *cu;
struct tag *c = cus__find_struct_by_name(kprobes_cus, &cu, name);
if (c != NULL) {
cus__emit_type_definitions(kprobes_cus, cu, c, stdout);
type__emit(c, cu, NULL, NULL, stdout);
cus__emit_type_definitions(kprobes_cus, cu, c, fp_methods);
type__emit(c, cu, NULL, NULL, fp_methods);
}
}
@ -329,7 +566,7 @@ static void emit_class_fwd_decl(const char *name)
struct cu *cu;
struct tag *c = cus__find_struct_by_name(kprobes_cus, &cu, name);
if (c != NULL)
cus__emit_fwd_decl(kprobes_cus, tag__type(c), stdout);
cus__emit_fwd_decl(kprobes_cus, tag__type(c), fp_methods);
}
/*
@ -339,6 +576,8 @@ static void emit_class_fwd_decl(const char *name)
*/
static void emit_module_preamble(void)
{
fputs("#include \"ctracer_relay.h\"\n", fp_methods);
emit_struct_defs("jprobe");
emit_struct_defs("kretprobe");
@ -351,6 +590,7 @@ static void emit_module_preamble(void)
static struct option long_options[] = {
{ "dir", required_argument, NULL, 'D' },
{ "src_dir", required_argument, NULL, 'd' },
{ "glob", required_argument, NULL, 'g' },
{ "kprobes", required_argument, NULL, 'k' },
{ "recursive", no_argument, NULL, 'r' },
@ -363,6 +603,8 @@ static void usage(void)
fprintf(stdout,
"usage: ctracer [options] <filename> <class_name>\n"
" where: \n"
" -d, --src_dir generate source files in this "
"directory\n"
" -D, --dir load files in this directory\n"
" -g, --glob file mask to load\n"
" -k, --kprobes kprobes object file\n"
@ -378,10 +620,14 @@ int main(int argc, char *argv[])
char *class_name = NULL;
struct tag *class;
struct cu *cu;
char functions_filename[PATH_MAX];
char methods_filename[PATH_MAX];
FILE *fp;
while ((option = getopt_long(argc, argv, "D:g:k:rh",
while ((option = getopt_long(argc, argv, "d:D:g:k:rh",
long_options, &option_index)) >= 0)
switch (option) {
case 'd': src_dir = optarg; break;
case 'D': dirname = optarg; break;
case 'g': glob = optarg; break;
case 'k': kprobes_filename = optarg; break;
@ -479,23 +725,48 @@ out_dwarf_err:
return EXIT_FAILURE;
}
snprintf(functions_filename, sizeof(functions_filename),
"%s/%s.functions", src_dir, class__name(tag__class(class)));
fp = fopen(functions_filename, "w");
if (fp == NULL) {
fprintf(stderr, "ctracer: couldn't create %s\n",
functions_filename);
exit(EXIT_FAILURE);
}
snprintf(methods_filename, sizeof(methods_filename),
"%s/ctracer_methods.c", src_dir);
fp_methods = fopen(methods_filename, "w");
if (fp_methods == NULL) {
fprintf(stderr, "ctracer: couldn't create %s\n",
methods_filename);
exit(EXIT_FAILURE);
}
emit_module_preamble();
cus__emit_type_definitions(methods_cus, cu, class, fp_methods);
type__emit(class, cu, NULL, NULL, fp_methods);
class__emit_subset(class, cu);
class__emit_ostra_converter(class, cu);
cus__for_each_cu(methods_cus, cu_find_methods_iterator,
class_name, NULL);
cus__for_each_cu(methods_cus, cu_emit_kprobes_iterator,
class_name, NULL);
cus__for_each_cu(methods_cus, cu_emit_kretprobes_iterator,
NULL, NULL);
puts("struct jprobe *ctracer__jprobes[] = {");
fputs("struct jprobe *ctracer__jprobes[] = {", fp_methods);
cus__for_each_cu(methods_cus, cu_emit_kprobes_table_iterator,
NULL, NULL);
fp, NULL);
/* Emit the sentinel */
puts("\t(void *)0,\n};\n");
puts("struct kretprobe *ctracer__kretprobes[] = {");
fputs("\t(void *)0,\n};\n", fp_methods);
fclose(fp);
fputs("struct kretprobe *ctracer__kretprobes[] = {", fp_methods);
cus__for_each_cu(methods_cus, cu_emit_kretprobes_table_iterator,
NULL, NULL);
/* Emit the sentinel */
puts("\t(void *)0,\n};\n");
fputs("\t(void *)0,\n};\n", fp_methods);
return EXIT_SUCCESS;
}

34
lib/Makefile Normal file
View File

@ -0,0 +1,34 @@
obj-m := ctracer.o
ctracer-y := ctracer_methods.o ctracer_jprobe.o ctracer_relay.o
# Files generated that shall be removed upon make clean
clean-files := ctracer_methods.c
CLASS=sock
#KDIR := /home/acme/git/OUTPUT/qemu/linux-2.6/
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
rm -rf .*.mod.c .*o.cmd *.mod.c *.ko *.o \
ctracer_methods.c Module.symvers .tmp_versions/ \
$(CLASS).{fields,functions} ctracer2ostra*
$(src)/ctracer2ostra: ctracer_methods.c
$(CC) $@.c -o $@
LOG=/tmp/ctracer.log
callgraph: ctracer2ostra
./ctracer2ostra < $(LOG) > $(LOG).ostra ; \
rm -rf $(CLASS).callgraph ; \
PYTHONPATH=python/ ostra-cg $(CLASS) $(LOG).ostra
$(obj)/ctracer_methods.o: ctracer_methods.c
# ctracer /home/acme/git/OUTPUT/qemu/linux-2.6/vmlinux --src_dir $(src) $(CLASS)
$(src)/ctracer_methods.c:
ctracer /usr/lib/debug/lib/modules/$(shell uname -r)/vmlinux --src_dir $(src) $(CLASS)

View File

@ -4,6 +4,7 @@
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/stddef.h>
#include "ctracer_relay.h"
extern struct jprobe *ctracer__jprobes[];
extern struct kretprobe *ctracer__kretprobes[];
@ -12,6 +13,9 @@ static int __init ctracer__jprobe_init(void)
{
int i = 0, nj = 0, nr = 0;
if (ctracer__relay_init() != 0)
return -1;
while (ctracer__jprobes[i] != NULL) {
int err = register_jprobe(ctracer__jprobes[i]);
if (err != 0)
@ -44,6 +48,8 @@ static void __exit ctracer__jprobe_exit(void)
{
int i = 0;
ctracer__relay_exit();
while (ctracer__jprobes[i] != NULL) {
if (ctracer__jprobes[i]->kp.nmissed != 0)
pr_info("ctracer: entry: missed %lu %s\n",

124
lib/ctracer_relay.c Normal file
View File

@ -0,0 +1,124 @@
#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/percpu.h>
#include <linux/relay.h>
#include <linux/sched.h>
#include <linux/string.h>
static struct rchan *ctracer__rchan;
static int ctracer__subbuf_start_callback(struct rchan_buf *buf, void *subbuf,
void *prev_subbuf,
size_t prev_padding)
{
static int warned;
if (!relay_buf_full(buf))
return 1;
if (!warned) {
warned = 1;
printk("relay_buf_full!\n");
}
return 0;
}
static struct dentry *ctracer__create_buf_file_callback(const char *filename,
struct dentry *parent,
int mode,
struct rchan_buf *buf,
int *is_global)
{
return debugfs_create_file(filename, mode, parent, buf,
&relay_file_operations);
}
static int ctracer__remove_buf_file_callback(struct dentry *dentry)
{
debugfs_remove(dentry);
return 0;
}
static struct rchan_callbacks ctracer__relay_callbacks = {
.subbuf_start = ctracer__subbuf_start_callback,
.create_buf_file = ctracer__create_buf_file_callback,
.remove_buf_file = ctracer__remove_buf_file_callback,
};
struct trace_entry {
unsigned int sec;
unsigned int usec:31;
unsigned int probe_type:1; /* Entry or exit */
const void *object;
unsigned long long function_id;
};
extern void ctracer__class_state(const void *from, void *to);
void ctracer__method_entry(const unsigned long long function_id,
const void *object, const int state_len)
{
struct timeval now;
do_gettimeofday(&now);
{
unsigned long flags;
void *t;
local_irq_save(flags);
t = relay_reserve(ctracer__rchan,
sizeof(struct trace_entry) + state_len);
if (t != NULL) {
struct trace_entry *entry = t;
entry->sec = now.tv_sec;
entry->usec = now.tv_usec;
entry->probe_type = 0;
entry->object = object;
entry->function_id = function_id;
ctracer__class_state(object, t + sizeof(*entry));
}
local_irq_restore(flags);
}
}
void ctracer__method_exit(unsigned long long function_id)
{
struct timeval now;
do_gettimeofday(&now);
{
unsigned long flags;
void *t;
local_irq_save(flags);
t = relay_reserve(ctracer__rchan, sizeof(struct trace_entry));
if (t != NULL) {
struct trace_entry *entry = t;
entry->sec = now.tv_sec;
entry->usec = now.tv_usec;
entry->probe_type = 1;
entry->object = NULL; /* need to find a way to get this */
entry->function_id = function_id;
}
local_irq_restore(flags);
}
}
int ctracer__relay_init(void)
{
ctracer__rchan = relay_open("ctracer", NULL, 256 * 1024, 64,
&ctracer__relay_callbacks);
if (ctracer__rchan == NULL) {
pr_info("ctracer: couldn't create the relay\n");
return -1;
}
return 0;
}
void ctracer__relay_exit(void)
{
relay_close(ctracer__rchan);
}

19
lib/ctracer_relay.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef _CTRACER_RELAY_H_
#define _CTRACER_RELAY_H_ 1
struct trace_entry {
unsigned int sec;
unsigned int usec:31;
unsigned int probe_type:1; /* Entry or exit */
const void *object;
unsigned long long function_id;
};
void ctracer__method_entry(const unsigned long long function,
const void *object, const int state_len);
void ctracer__method_exit(unsigned long long function);
int ctracer__relay_init(void);
void ctracer__relay_exit(void);
#endif

410
ostra/ostra-cg Executable file
View File

@ -0,0 +1,410 @@
#!/usr/bin/python
# ostra-cg - generate callgraphs from encoded trace
#
# Arnaldo Carvalho de Melo <acme@redhat.com>
# <acme@ghostprotocols.net>
#
# Copyright (C) 2005, 2006, 2007 Arnaldo Carvalho de Melo
# Licensed under the Open Software License version 1.1
import sys, datetime, os, ostra
class_def = None
ident = 0
verbose = False
valid_html = False
print_exits = True
print_exit_details = False
print_function_times = True
gen_html = True
html_file_seq = 0
nr_lines_per_page = 256
output_file = None
callgraph = None
print_nr_exit_points = False
first_table_row = True
plot_min_samples = 4
plot = False
tab_space = 10
my_object = None
# @import url(file:///home/acme/git/ostra/ostra.css);
html_style_import='''
<style type="text/css">
@import url(http://vger.kernel.org/~acme/ostra.css);
</style>
'''
def emit_html_page_sequence_links(page):
global output_file
output_file.write("<div class=\"page_links\">")
if page != 1:
if page == 2:
prev = "index"
else:
prev = str(page - 2)
output_file.write("<a href=\"index.html\">Index</a> | ")
output_file.write("<a href=\"%s.html\">Previous</a> | " % prev)
output_file.write("<a href=\"%d.html\">Next</a> | " % page)
output_file.write("<a href=\"changes.html\">Where fields changed</a> | ")
output_file.write("<a href=\"methods/index.html\">Methods statistics</a> | ")
output_file.write("<a href=\"last.html\">Last</a>\n")
output_file.write("</div>")
def close_callgraph_file():
if gen_html:
output_file.write("</td></tr></table>\n")
emit_html_page_sequence_links(html_file_seq)
if valid_html:
output_file.write('''
<p>
<a href="http://validator.w3.org/check?uri=referer"><img border="0"
src="http://www.w3.org/Icons/valid-html401"
alt="Valid HTML 4.01!" height="31" width="88"></a>
</p>
''')
output_file.write("</body>\n</html>\n")
output_file.close()
def new_callgraph_file(traced_class):
global html_file_seq, output_file, first_table_row
if not gen_html:
if output_file == None:
output_file = file("%s.txt" % callgraph, "w")
return
first_table_row = True
if html_file_seq == 0:
os.mkdir(callgraph)
if output_file != None:
output_file.close()
filename = "index"
help = '''
<h3>Tracing struct %s methods (functions with a struct %s * argument)</h3>
<h3>Click on the timestamps to see the object state</h3>
<h3>Click on the functions to go to its definition in LXR (http://lxr.linux.no/)</h3>
<h3 style=\"color:red;\">Red timestamps means the state changed</h3>
''' % (traced_class, traced_class)
else:
close_callgraph_file()
filename = str(html_file_seq)
help = " "
output_file = file("%s/%s.html" % (callgraph, filename), "w")
output_file.write('''
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>OSTRA Callgraph: %s, file %d</title>
%s
</head>
<body>
''' % (callgraph, html_file_seq, html_style_import))
html_file_seq += 1
emit_html_page_sequence_links(html_file_seq)
output_file.write("\n%s\n<table class=\"listing\" cellspacing=\"0\" border=\"0\">\n" % help)
def trim_tstamp(tstamp):
return str(tstamp).strip().lstrip('0').lstrip(':').lstrip('0').lstrip(':').lstrip('0').lstrip('.').lstrip('0')
def object_state():
output = "<table class=\"state\" cellspacing=\"0\">"
state_changed = False
for field in class_def.fields.values():
if not field.cg:
continue
value_changed_or_not_zero = False
value = field.value
if field.changed():
state_changed = True
last_value = field.last_value
if field.table and last_value and field.table.has_key(int(last_value)):
last_value = field.table[int(last_value)]
transition = "%s -> " % last_value
color = " class=\"odd\""
value_changed_or_not_zero = True
else:
field_changed = False
transition = ""
color = ""
if value != "0" and value != None:
value_changed_or_not_zero = True
if value_changed_or_not_zero:
if field.table and value and field.table.has_key(int(value)):
value = field.table[int(value)]
output = output.strip() + "<tr%s><td>%s</td><td class=\"right\">%s%s</td></tr>" % \
(color, field, transition, value)
output += "</table>"
return (output, state_changed)
total_lines = 0
def tstamp_str():
global total_lines, first_table_row
total_lines += 1
if gen_html:
state, changed = object_state()
if changed:
anchor = "%d.%d" % (class_def.tstamp.seconds, class_def.tstamp.microseconds)
anchor_color = " class=\"red\""
else:
anchor = ""
anchor_color = ""
if total_lines % 2 == 1:
row_color = "odd"
else:
row_color = "evn"
if first_table_row:
close_last_tr = ""
first_table_row = False
else:
close_last_tr = "</td></tr>\n"
return "%s<tr class=\"%s\"><td class=\"state\"><a name=\"%s\"%s>%04d.%06d<span>%s</span></a></td>" % \
(close_last_tr, row_color, anchor, anchor_color,
class_def.tstamp.seconds, class_def.tstamp.microseconds, state)
else:
return "%06d.%06d" % (class_def.tstamp.seconds, class_def.tstamp.microseconds)
def indent_str(indent, text):
if gen_html:
method = class_def.current_method()
time_so_far = method.total_time.seconds * 10000 + method.total_time.microseconds
tooltip = "%s: calls=%d, total time=%dus" % (method.name, method.calls, time_so_far)
if class_def.fields["action"].value[0] == 'o':
if class_def.fields.has_key("exit_point"):
tooltip += ", exit point=%d" % (int(class_def.fields["exit_point"].value) + 1)
else:
text = "<a class=\"lxr\" href=\"http://lxr.linux.no/ident?i=%s\">%s</a>" % (method.name, text)
return "<td title=\"%s\">%s%s" % (tooltip, "&nbsp;" * tab_space * indent, text)
else:
return "%s%s" % ("\t" * ident, text)
def function_time_str(time):
if gen_html:
if class_def.current_method().print_return_value:
ret_value = "%s" % class_def.fields["return_value"].value
else:
ret_value = "0"
if ret_value == "0":
ret_value = ""
else:
ret_value=" title=\"returned %s\"" % ret_value
return "</td><td%s class=\"time\">%sus" % (ret_value, time)
else:
return " %sus\n" % time
previous_was_entry = False
nr_lines = 0
def process_record():
global ident, previous_was_entry, nr_lines
if gen_html:
nr_lines += 1
if nr_lines > nr_lines_per_page:
if ident == 0 or nr_lines > nr_lines_per_page * 5:
new_callgraph_file(traced_class)
nr_lines = 0
method = class_def.current_method()
if class_def.fields["action"].value[0] == 'i':
output = "%s()" % method.name
if print_exits and previous_was_entry:
if gen_html:
last_open = " {</td><td>&nbsp;"
else:
last_open = " {\n"
else:
last_open = ""
output_file.write("%s%s %s" % (last_open, tstamp_str(), indent_str(ident, output.strip())))
if not print_exits:
output_file.write("\n")
ident += 1
method.calls += 1
method.last_tstamp = class_def.tstamp
previous_was_entry = True
else:
if not method.last_tstamp:
method.last_tstamp = class_def.tstamp
tstamp_delta = class_def.tstamp - method.last_tstamp
if tstamp_delta < datetime.timedelta():
tstamp_delta = datetime.timedelta()
method.total_time += tstamp_delta
if ident > 0:
ident -= 1
if print_exits:
if print_exit_details:
exit_point = int(class_def.fields["exit_point"].value) + 1
if class_def.last_method.name != method.name:
output_file.write("%s %s" % (tstamp_str(), indent_str(ident, "}")))
if print_exit_details:
output_file.write(" EXIT #%d (%s)" % (exit_point, method.name))
else:
if print_exit_details:
output_file.write("EXIT #%d" % exit_point)
function_time = trim_tstamp(tstamp_delta)
if len(function_time) == 0:
function_time = "0"
if print_exits:
if print_function_times:
output_file.write(function_time_str(function_time))
else:
output_file.write("\n")
if print_nr_exit_points:
if method.exits.has_key(exit_point):
method.exits[exit_point] += 1
else:
method.exits[exit_point] = 1
previous_was_entry = False
return html_file_seq - 1
def print_where_fields_changed():
f = file("%s/changes.html" % callgraph, "w")
f.write('''
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>OSTRA Callgraph: %s, Where the Fields Changed</title>
%s
</head>
<body>
<h3>Click on the values to go to where it was changed</h3>
<h3>Click on the field names to see a plotting of its value over time</h3>
''' % (callgraph, html_style_import))
output_file.write("<div class=\"page_links\">")
f.write("<a href=\"index.html\">Index</a>\n")
f.write("<a href=\"%d.html\">Last</a>\n" % (html_file_seq - 1))
f.write("<table border=\"1\">")
max_samples = 50
for key in class_def.fields.keys():
fields = class_def.fields[key]
changes = fields.changes
changes_str=""
link_pre=""
link_pos=""
if len(changes) == 0:
changes_str="Unchanged</td></tr>\n"
elif plot and len(changes) >= plot_min_samples and fields.plot_fmt != "dev_null":
link_pre="<a href=\"%s.png\">" % key
link_pos="</a>"
f.write("<tr><td valign=\"top\">%s%s%s</td><td>%s" % (link_pre, key, link_pos, changes_str))
if len(changes) == 0:
continue
f.write("<table border=\"0\">\n")
nr_samples = 0
for change in changes:
nr_samples += 1
if nr_samples <= max_samples:
if change.seq == 0:
filename="index"
else:
filename = str(change.seq)
f.write("<tr><td><a href=%s.html#%d.%d>%s</td></tr>" % \
(filename, change.tstamp.seconds, change.tstamp.microseconds, change.value))
if nr_samples > max_samples:
f.write("<tr><td>Only %d samples out of %d were printed</td></tr>" % (max_samples, nr_samples))
f.write("</table>\n</td></tr>\n")
f.write("</table>")
output_file.write("</div>")
f.write("</body>\n</html>\n")
f.close()
os.symlink("changes.html", "%s/%d.html" % (callgraph, html_file_seq))
os.symlink("%d.html" % (html_file_seq - 1), "%s/last.html" % callgraph)
def method_stats(class_def, callgraph):
os.mkdir("%s/methods" % callgraph)
f = file("%s/methods/index.html" % callgraph, "w")
f.write('''
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>OSTRA Callgraph: %s, Methods Statistics</title>
%s
</head>
<body>
<h3>Click on the methods names to see a plotting of the times for each call</h3>
''' % (callgraph, html_style_import))
if plot:
class_def.plot_methods(callgraph)
f.write("<table border=\"1\">")
for method in class_def.methods.values():
changes_str=""
link_pre=""
link_pos=""
if len(method.times) < 4:
changes_str="Less than 4 calls</td></tr>\n"
else:
if plot:
link_pre="<a href=\"%s.png\">" % method.name
link_pos="</a>"
changes_str="%d calls</td></tr>\n" % len(method.times)
f.write("<tr><td valign=\"top\">%s%s%s</td><td>%s" % \
(link_pre, method.name, link_pos, changes_str))
f.write("</table>")
f.write("</body>\n</html>\n")
f.close()
if __name__ == '__main__':
if len(sys.argv) not in [ 3, 4 ]:
print "usage: ostra-cg <traced_class> <encoded_trace> [object]"
sys.exit(1)
gen_html = True
traced_class = sys.argv[1]
callgraph = "%s.callgraph" % traced_class
encoded_trace = sys.argv[2]
if len(sys.argv) == 4:
my_object = sys.argv[3]
if my_object == "none":
my_object = None
plot = True
class_def = ostra.class_definition(class_def_file = "%s.fields" % traced_class,
class_methods_file = "%s.functions" % traced_class)
new_callgraph_file(traced_class)
class_def.parse_file(encoded_trace, verbose = verbose,
process_record = process_record,
my_object = my_object)
if gen_html:
print_where_fields_changed()
close_callgraph_file()
method_stats(class_def, callgraph)
if plot:
ostra.plot(class_def, callgraph)

392
ostra/python/ostra.py Executable file
View File

@ -0,0 +1,392 @@
#!/usr/bin/python
from datetime import timedelta
class trace_points:
def __init__(self, hooks):
self.entry = "entry" in hooks
self.exit = "exit" in hooks
def __repr__(self):
return str(self.__dict__.values())
__str__ = __repr__
class change_point:
def __init__(self, tstamp, value, seq):
self.tstamp = tstamp
self.value = value
self.seq = seq
class class_field:
def __init__(self, line, class_def_file):
field, self.name, cgtraced, self.grab_expr, \
self.collector_fmt, hooks, self.plot_fmt = line.strip().split(':')
self.field = int(field)
self.cg = cgtraced == "yes"
self.hooks = trace_points(hooks.split(','))
self.value = None
self.last_value = None
self.changes = []
self._load_text_table(class_def_file)
def _text_table_tokenizer(self, line):
tokens = line.split(":")
return int(tokens[0]), tokens[1][:-1]
def _load_text_table(self, class_def_file):
try:
f = file("%s.%s.table" % (class_def_file, self.name))
except:
self.table = {}
return
self.table = dict([self._text_table_tokenizer(line) for line in f.readlines()])
f.close()
def set_last_value(self, tstamp, seq):
if self.value != None:
if self.cg and self.changed():
self.changes.append(change_point(tstamp, self.value, seq))
self.last_value = self.value
def changed(self):
return self.value != None and self.value != self.last_value
def __repr__(self):
return self.name
__str__ = __repr__
class class_method:
def __init__(self, line):
fields = line.strip().split(':')
self.function_id = fields[0]
self.name = fields[1]
self.print_return_value = fields[-1]
self.function_id = int(self.function_id)
self.print_return_value = self.print_return_value == "yes"
self.calls = 0
self.total_time = timedelta()
self.last_tstamp = None
self.times = []
self.exits = {}
def begin(self, tstamp):
self.calls += 1
self.last_tstamp = tstamp
def end(self, tstamp):
tstamp_delta = tstamp - self.last_tstamp
if tstamp_delta < timedelta():
tstamp_delta = timedelta()
self.total_time += tstamp_delta
self.times.append(tstamp_delta.seconds * 1000000 + tstamp_delta.microseconds)
def plot(self, directory, entries, samples, nr_samples, verbose = False):
from matplotlib import use as muse
muse('Agg')
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.ticker import FuncFormatter, FixedFormatter, LinearLocator
from matplotlib.mlab import std as std_deviation
from matplotlib.mlab import mean
from time import asctime
yfont = { 'fontname' : 'Bitstream Vera Sans',
'color' : 'r',
'fontsize' : 8 }
xfont = { 'fontname' : 'Bitstream Vera Sans',
'color' : 'b',
'fontsize' : 8 }
titlefont = { 'fontname' : 'Bitstream Vera Sans',
'color' : 'g',
'fontweight' : 'bold',
'fontsize' : 10 }
inches = 0.00666667
width = 950 * inches
height = 680 * inches
fig = Figure(figsize = (width, height))
canvas = FigureCanvas(fig)
ax = fig.add_subplot(111)
ax.grid(False)
xtickfontsize = 5
ytickfontsize = 5
plot_type = 'b-'
field_mean = mean(samples)
yaxis_plot_fmt = FuncFormatter(pylab_formatter_ms)
ax.plot(entries, samples, "b-")
ax.set_xlabel("samples", xfont)
ax.set_ylabel("time", yfont)
for label in ax.get_xticklabels():
label.set(fontsize = xtickfontsize)
for label in ax.get_yticklabels():
label.set(fontsize = ytickfontsize)
ax.yaxis.set_major_formatter(yaxis_plot_fmt)
ax.set_title("%d %s samples (%s)" % (nr_samples, self.name, asctime()), titlefont)
canvas.print_figure("%s/methods/%s.png" % (directory, self.name))
del fig, canvas, ax
class class_definition:
def __init__(self, class_def_file = None, class_methods_file = None):
self.fields = {}
self.methods = {}
self.tstamp = None
self.last_tstamp = None
self.last_method = None
self.epoch = None
if class_def_file:
f = file(class_def_file)
for line in f.readlines():
field = class_field(line, class_def_file)
self.fields[field.name] = field
f.close()
if class_methods_file:
f = file(class_methods_file)
self.methods = dict([self._method_tokenizer(line) for line in f.readlines()])
f.close()
def _method_tokenizer(self, line):
method = class_method(line)
return method.function_id, method
def set_last_values(self, seq = 0):
self.last_method = self.current_method()
for field in self.fields.values():
field.set_last_value(self.tstamp, seq)
self.last_tstamp = self.tstamp
def parse_record(self, line):
tstamp, record = line[:-1].split(' ', 1)
line_fields = record.split(':')
sec, usec = tstamp.split('.', 1)
self.tstamp = timedelta(seconds = int(sec), microseconds = int(usec))
if self.epoch == None:
self.epoch = self.tstamp
self.tstamp -= self.epoch
action = line_fields[0][0]
nr_fields = len(line_fields)
for field in self.fields.values():
if field.field >= nr_fields or \
(action == 'i' and not field.hooks.entry) or \
(action == 'o' and not field.hooks.exit):
field.value = None
continue
field.value = line_fields[field.field]
def parse_file(self, filename, process_record = None, verbose = False,
my_object = None):
f = file(filename)
current_object = None
object_stack = []
if verbose:
nr_lines = 0
while True:
line = f.readline()
if not line:
break
if verbose:
nr_lines += 1
print "\r%d" % nr_lines,
self.parse_record(line)
method = self.current_method()
# print method.name
if my_object:
if self.fields["action"].value[0] == 'i':
current_object = self.fields["object"].value
object_stack.append(current_object)
else:
current_object = object_stack.pop()
if current_object != my_object:
continue
if self.fields["action"].value[0] == 'i':
method.begin(self.tstamp)
else:
method.end(self.tstamp)
seq = 0
if process_record:
seq = process_record()
self.set_last_values(seq)
f.close()
if verbose:
print
def current_method(self):
return self.methods[int(self.fields["function_id"].value)]
def plot_methods(self, callgraph, verbose = False):
for current_method in self.methods.values():
nr_samples = len(current_method.times)
if nr_samples < 4:
continue
if verbose:
print "plot_methods: plotting %s method (%d samples)" % \
(current_method.name, nr_samples)
entries = [float("%d.0" % entry) for entry in range(nr_samples)]
samples = current_method.times
current_method.plot(callgraph, entries, samples,
nr_samples, verbose)
def pylab_formatter_kbps(x):
mb = 1024 * 1024
if x > mb:
return "%d,%d Mbps" % (x / mb, x % mb)
else:
return "%d,%d Kbps" % (x / 1024, x % 1024)
def pylab_formatter_ms(x, pos = 0):
ms = x / 1000
us = x % 1000
s = "%d" % ms
if us > 0:
s += ".%03d" % us
s = s.rstrip('0')
s += "ms"
return s
def pylab_formatter(x, pos = 0):
if current_plot_fmt == "kbps":
return pylab_formatter_kbps(x)
elif current_plot_fmt == "ms":
return pylab_formatter_ms(x)
else:
return "%s" % str(int(x))
def plot_field(name, directory, tstamps, samples, nr_samples, plot_fmt = None,
table = None, verbose = False):
global current_plot_fmt
from matplotlib import use as muse
muse('Agg')
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.ticker import FuncFormatter, FixedFormatter, LinearLocator
from matplotlib.mlab import std as std_deviation
from matplotlib.mlab import mean
from time import asctime
yfont = { 'fontname' : 'Bitstream Vera Sans',
'color' : 'r',
'fontsize' : 8 }
xfont = { 'fontname' : 'Bitstream Vera Sans',
'color' : 'b',
'fontsize' : 8 }
titlefont = { 'fontname' : 'Bitstream Vera Sans',
'color' : 'g',
'fontweight' : 'bold',
'fontsize' : 10 }
inches = 0.00666667
width = 950 * inches
height = 680 * inches
fig = Figure(figsize = (width, height))
canvas = FigureCanvas(fig)
ax = fig.add_subplot(111)
ax.grid(False)
xtickfontsize = 5
ytickfontsize = 5
current_plot_fmt = plot_fmt
field_mean = None
plot_type = 'b-'
if current_plot_fmt == "filter_dev":
std = std_deviation(samples) * 2
if verbose:
print "filter_dev(%s) std=%d" % (name, std)
for i in range(nr_samples):
if samples[i] > std:
if verbose:
print "%s: filtering out %d" % (name, samples[i])
samples[i] = 0
field_mean = mean(samples)
yaxis_plot_fmt = FuncFormatter(pylab_formatter)
elif current_plot_fmt == "table":
ax.grid(True)
plot_type = 'bo-'
max_value = max(samples)
without_zero = 1
if table.has_key(0):
without_zero = 0
max_value += 1
ax.yaxis.set_major_locator(LinearLocator(max_value))
tstamps = range(nr_samples)
seq = [ " " ] * max_value
for key in table.keys():
if key in samples:
seq[key - without_zero] = "%s(%d)" % (table[key], key)
ytickfontsize = 4
yaxis_plot_fmt = FixedFormatter(seq)
else:
field_mean = mean(samples)
yaxis_plot_fmt = FuncFormatter(pylab_formatter)
ax.plot(tstamps, samples, plot_type)
ax.set_xlabel("time", xfont)
yname = name
if field_mean:
yname += " (mean=%s)" % pylab_formatter(field_mean)
ax.set_ylabel(yname, yfont)
for label in ax.get_xticklabels():
label.set(fontsize = xtickfontsize)
for label in ax.get_yticklabels():
label.set(fontsize = ytickfontsize)
ax.yaxis.set_major_formatter(yaxis_plot_fmt)
ax.set_title("%d %s samples (%s)" % (nr_samples, name, asctime()), titlefont)
canvas.print_figure("%s/%s.png" % (directory, name))
del fig, canvas, ax
def plot(class_def, callgraph, verbose = False):
for current_field in class_def.fields.values():
nr_samples = len(current_field.changes)
if nr_samples < 4:
continue
if verbose:
print "ostra-plot: plotting %s field (%d samples)" % (current_field.name, nr_samples)
tstamps = [float("%d.%06d" % (entry.tstamp.seconds, entry.tstamp.microseconds)) \
for entry in current_field.changes]
try:
samples = [int(entry.value) for entry in current_field.changes]
except:
continue
plot_field(current_field.name, callgraph, tstamps, samples,
nr_samples, current_field.plot_fmt,
current_field.table, verbose)
if __name__ == '__main__':
import sys
c = class_definition(sys.argv[1], sys.argv[2])
for field in c.fields.values():
print "%s: %s" % (field, field.table)
for method in c.methods.values():
print "%d: %s" % (method.function_id, method.name)

View File

@ -3,7 +3,7 @@
Name: dwarves
Version: 0
Release: 13
Release: 14
License: GPL
Summary: Dwarf Tools
Group: Base
@ -72,9 +72,14 @@ rm -rf %{buildroot}
%{_bindir}/pfunct
%{_bindir}/pglobal
%{_bindir}/prefcnt
%{_bindir}/ostra-cg
%dir %{_libdir}/ctracer
%{_libdir}/ctracer/Makefile
%{_libdir}/ctracer/ctracer_jprobe.c
%{_libdir}/ctracer/ctracer_relay.c
%{_libdir}/ctracer/ctracer_relay.h
%dir %{_libdir}/ctracer/python
%{_libdir}/ctracer/python/ostra.py*
%files -n %{libname}%{libver}
%defattr(0644,root,root,0755)
@ -87,7 +92,15 @@ rm -rf %{buildroot}
%changelog
* Fri Feb 2 2007 Arnaldo Carvalho de Melo <acme@redhat.com>
- 4ab3403e3b72a18fbd5fe15630e76605264ba18c
- d37f41df58c375412badf827e24dfc346cea2ff2
- ostra-cg
- relay/debugfs
- mini-structs
- ctracer2ostra
- All this in the Makefile
* Fri Feb 2 2007 Arnaldo Carvalho de Melo <acme@redhat.com>
- b7cad1782d683571ffb2601b429ab151bddad5d7
- pglobal, by Davi Arnaut
- pahole --show_reorg_steps
- Reorganize bitfields in pahole --reorganize