2009-12-15 15:58:56

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 1/3] perf symbols: Make symbol_conf global

From: Arnaldo Carvalho de Melo <[email protected]>

This simplifies a lot of functions, less stuff to be done by tool
writers.

Cc: Frédéric Weisbecker <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Paul Mackerras <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/builtin-annotate.c | 14 +++++-------
tools/perf/builtin-buildid-list.c | 5 ++-
tools/perf/builtin-diff.c | 8 ++----
tools/perf/builtin-kmem.c | 5 +--
tools/perf/builtin-record.c | 4 +-
tools/perf/builtin-report.c | 7 +----
tools/perf/builtin-sched.c | 5 +--
tools/perf/builtin-timechart.c | 5 +--
tools/perf/builtin-top.c | 6 +---
tools/perf/builtin-trace.c | 4 +-
tools/perf/util/session.c | 5 +--
tools/perf/util/session.h | 5 +---
tools/perf/util/symbol.c | 38 ++++++++++++++----------------------
tools/perf/util/symbol.h | 9 +++----
14 files changed, 48 insertions(+), 72 deletions(-)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 2e2855a..e656e25 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -51,11 +51,6 @@ struct sym_priv {
struct sym_ext *ext;
};

-static struct symbol_conf symbol_conf = {
- .priv_size = sizeof(struct sym_priv),
- .try_vmlinux_path = true,
-};
-
static const char *sym_hist_filter;

static int symbol_filter(struct map *map __used, struct symbol *sym)
@@ -464,10 +459,10 @@ static struct perf_event_ops event_ops = {

static int __cmd_annotate(void)
{
- struct perf_session *session = perf_session__new(input_name, O_RDONLY,
- force, &symbol_conf);
int ret;
+ struct perf_session *session;

+ session = perf_session__new(input_name, O_RDONLY, force);
if (session == NULL)
return -ENOMEM;

@@ -523,7 +518,10 @@ static const struct option options[] = {

int cmd_annotate(int argc, const char **argv, const char *prefix __used)
{
- if (symbol__init(&symbol_conf) < 0)
+ symbol_conf.priv_size = sizeof(struct sym_priv);
+ symbol_conf.try_vmlinux_path = true;
+
+ if (symbol__init() < 0)
return -1;

argc = parse_options(argc, argv, options, annotate_usage, 0);
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c
index 7c36e4b..e693e67 100644
--- a/tools/perf/builtin-buildid-list.c
+++ b/tools/perf/builtin-buildid-list.c
@@ -54,8 +54,9 @@ static int perf_file_section__process_buildids(struct perf_file_section *self,
static int __cmd_buildid_list(void)
{
int err = -1;
- struct perf_session *session = perf_session__new(input_name, O_RDONLY,
- force, NULL);
+ struct perf_session *session;
+
+ session = perf_session__new(input_name, O_RDONLY, force);
if (session == NULL)
return -1;

diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 0d52801..67328d1 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -21,8 +21,6 @@ static char const *input_old = "perf.data.old",
static int force;
static bool show_percent;

-struct symbol_conf symbol_conf;
-
static int perf_session__add_hist_entry(struct perf_session *self,
struct addr_location *al, u64 count)
{
@@ -226,8 +224,8 @@ static int __cmd_diff(void)
int ret, i;
struct perf_session *session[2];

- session[0] = perf_session__new(input_old, O_RDONLY, force, &symbol_conf);
- session[1] = perf_session__new(input_new, O_RDONLY, force, &symbol_conf);
+ session[0] = perf_session__new(input_old, O_RDONLY, force);
+ session[1] = perf_session__new(input_new, O_RDONLY, force);
if (session[0] == NULL || session[1] == NULL)
return -ENOMEM;

@@ -267,7 +265,7 @@ static const struct option options[] = {

int cmd_diff(int argc, const char **argv, const char *prefix __used)
{
- if (symbol__init(&symbol_conf) < 0)
+ if (symbol__init() < 0)
return -1;

setup_sorting(diff_usage, options);
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index dda6086..e078797 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -505,8 +505,7 @@ static void sort_result(void)
static int __cmd_kmem(void)
{
int err;
- struct perf_session *session = perf_session__new(input_name, O_RDONLY,
- 0, NULL);
+ struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0);
if (session == NULL)
return -ENOMEM;

@@ -767,7 +766,7 @@ static int __cmd_record(int argc, const char **argv)

int cmd_kmem(int argc, const char **argv, const char *prefix __used)
{
- symbol__init(0);
+ symbol__init();

argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 66979a5..1da48a8 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -451,7 +451,7 @@ static int __cmd_record(int argc, const char **argv)
exit(-1);
}

- session = perf_session__new(output_name, O_WRONLY, force, NULL);
+ session = perf_session__new(output_name, O_WRONLY, force);
if (session == NULL) {
pr_err("Not enough memory for reading perf file header\n");
return -1;
@@ -632,7 +632,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
{
int counter;

- symbol__init(0);
+ symbol__init();

argc = parse_options(argc, argv, options, record_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 40389c0..c349bdb 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -52,9 +52,6 @@ static int exclude_other = 1;

static char callchain_default_opt[] = "fractal,0.5";

-static struct symbol_conf symbol_conf;
-
-
static size_t
callchain__fprintf_left_margin(FILE *fp, int left_margin)
{
@@ -705,7 +702,7 @@ static int __cmd_report(void)
int ret;
struct perf_session *session;

- session = perf_session__new(input_name, O_RDONLY, force, &symbol_conf);
+ session = perf_session__new(input_name, O_RDONLY, force);
if (session == NULL)
return -ENOMEM;

@@ -864,7 +861,7 @@ static void setup_list(struct strlist **list, const char *list_str,

int cmd_report(int argc, const char **argv, const char *prefix __used)
{
- if (symbol__init(&symbol_conf) < 0)
+ if (symbol__init() < 0)
return -1;

argc = parse_options(argc, argv, options, report_usage, 0);
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index d67f274..80209df 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -1675,8 +1675,7 @@ static struct perf_event_ops event_ops = {
static int read_events(void)
{
int err;
- struct perf_session *session = perf_session__new(input_name, O_RDONLY,
- 0, NULL);
+ struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0);
if (session == NULL)
return -ENOMEM;

@@ -1912,7 +1911,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __used)
if (!strcmp(argv[0], "trace"))
return cmd_trace(argc, argv, prefix);

- symbol__init(0);
+ symbol__init();
if (!strncmp(argv[0], "rec", 3)) {
return __cmd_record(argc, argv);
} else if (!strncmp(argv[0], "lat", 3)) {
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index ffd81e8..9c98b7a 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -1050,8 +1050,7 @@ static struct perf_event_ops event_ops = {

static int __cmd_timechart(void)
{
- struct perf_session *session = perf_session__new(input_name, O_RDONLY,
- 0, NULL);
+ struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0);
int ret;

if (session == NULL)
@@ -1138,7 +1137,7 @@ static const struct option options[] = {

int cmd_timechart(int argc, const char **argv, const char *prefix __used)
{
- symbol__init(0);
+ symbol__init();

argc = parse_options(argc, argv, options, timechart_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 296e809..cd89b6d 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -80,7 +80,6 @@ static int dump_symtab = 0;
static bool hide_kernel_symbols = false;
static bool hide_user_symbols = false;
static struct winsize winsize;
-static struct symbol_conf symbol_conf;

/*
* Source
@@ -1162,8 +1161,7 @@ static int __cmd_top(void)
* FIXME: perf_session__new should allow passing a O_MMAP, so that all this
* mmap reading, etc is encapsulated in it. Use O_WRONLY for now.
*/
- struct perf_session *session = perf_session__new(NULL, O_WRONLY, false,
- &symbol_conf);
+ struct perf_session *session = perf_session__new(NULL, O_WRONLY, false);
if (session == NULL)
return -ENOMEM;

@@ -1284,7 +1282,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
(nr_counters + 1) * sizeof(unsigned long));
if (symbol_conf.vmlinux_name == NULL)
symbol_conf.try_vmlinux_path = true;
- if (symbol__init(&symbol_conf) < 0)
+ if (symbol__init() < 0)
return -1;

if (delay_secs < 1)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 7e744f7..07ad25c 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -579,7 +579,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
exit(-1);
}

- symbol__init(0);
+ symbol__init();

setup_scripting();

@@ -588,7 +588,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)

setup_pager();

- session = perf_session__new(input_name, O_RDONLY, 0, NULL);
+ session = perf_session__new(input_name, O_RDONLY, 0);
if (session == NULL)
return -ENOMEM;

diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index ecd54be..bceaa09 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -49,8 +49,7 @@ out_close:
return -1;
}

-struct perf_session *perf_session__new(const char *filename, int mode,
- bool force, struct symbol_conf *conf)
+struct perf_session *perf_session__new(const char *filename, int mode, bool force)
{
size_t len = filename ? strlen(filename) + 1 : 0;
struct perf_session *self = zalloc(sizeof(*self) + len);
@@ -69,7 +68,7 @@ struct perf_session *perf_session__new(const char *filename, int mode,
self->cwdlen = 0;
map_groups__init(&self->kmaps);

- if (perf_session__create_kernel_maps(self, conf) < 0)
+ if (perf_session__create_kernel_maps(self) < 0)
goto out_delete;

if (mode == O_RDONLY && perf_session__open(self, force) < 0)
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index bdfc4b8..faf18a8 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -10,7 +10,6 @@
struct ip_callchain;
struct thread;
struct symbol;
-struct symbol_conf;

struct perf_session {
struct perf_header header;
@@ -26,7 +25,6 @@ struct perf_session {
int fd;
int cwdlen;
char *cwd;
- bool use_modules;
bool use_callchain;
char filename[0];
};
@@ -48,8 +46,7 @@ struct perf_event_ops {
bool full_paths;
};

-struct perf_session *perf_session__new(const char *filename, int mode,
- bool force, struct symbol_conf *conf);
+struct perf_session *perf_session__new(const char *filename, int mode, bool force);
void perf_session__delete(struct perf_session *self);

int perf_session__process_events(struct perf_session *self,
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 185b9ee..17ce012 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -33,11 +33,10 @@ static void dsos__add(struct list_head *head, struct dso *dso);
static struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
static int dso__load_kernel_sym(struct dso *self, struct map *map,
struct perf_session *session, symbol_filter_t filter);
-unsigned int symbol__priv_size;
static int vmlinux_path__nr_entries;
static char **vmlinux_path;

-static struct symbol_conf symbol_conf__defaults = {
+struct symbol_conf symbol_conf = {
.use_modules = true,
.try_vmlinux_path = true,
};
@@ -130,13 +129,13 @@ static void map_groups__fixup_end(struct map_groups *self)
static struct symbol *symbol__new(u64 start, u64 len, const char *name)
{
size_t namelen = strlen(name) + 1;
- struct symbol *self = zalloc(symbol__priv_size +
+ struct symbol *self = zalloc(symbol_conf.priv_size +
sizeof(*self) + namelen);
if (self == NULL)
return NULL;

- if (symbol__priv_size)
- self = ((void *)self) + symbol__priv_size;
+ if (symbol_conf.priv_size)
+ self = ((void *)self) + symbol_conf.priv_size;

self->start = start;
self->end = len ? start + len - 1 : start;
@@ -150,7 +149,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name)

static void symbol__delete(struct symbol *self)
{
- free(((void *)self) - symbol__priv_size);
+ free(((void *)self) - symbol_conf.priv_size);
}

static size_t symbol__fprintf(struct symbol *self, FILE *fp)
@@ -471,7 +470,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,

module = strchr(pos->name, '\t');
if (module) {
- if (!session->use_modules)
+ if (!symbol_conf.use_modules)
goto discard_symbol;

*module++ = '\0';
@@ -1740,34 +1739,27 @@ out_fail:
return -1;
}

-int symbol__init(struct symbol_conf *conf)
+int symbol__init(void)
{
- const struct symbol_conf *pconf = conf ?: &symbol_conf__defaults;
-
elf_version(EV_CURRENT);
- symbol__priv_size = pconf->priv_size;
- if (pconf->sort_by_name)
- symbol__priv_size += (sizeof(struct symbol_name_rb_node) -
- sizeof(struct symbol));
+ if (symbol_conf.sort_by_name)
+ symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) -
+ sizeof(struct symbol));

- if (pconf->try_vmlinux_path && vmlinux_path__init() < 0)
+ if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0)
return -1;

return 0;
}

-int perf_session__create_kernel_maps(struct perf_session *self,
- struct symbol_conf *conf)
+int perf_session__create_kernel_maps(struct perf_session *self)
{
- const struct symbol_conf *pconf = conf ?: &symbol_conf__defaults;
-
if (map_groups__create_kernel_maps(&self->kmaps,
- pconf->vmlinux_name) < 0)
+ symbol_conf.vmlinux_name) < 0)
return -1;

- self->use_modules = pconf->use_modules;
-
- if (pconf->use_modules && perf_session__create_module_maps(self) < 0)
+ if (symbol_conf.use_modules &&
+ perf_session__create_module_maps(self) < 0)
pr_debug("Failed to load list of modules for session %s, "
"continuing...\n", self->filename);
/*
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 941ef33..7662947 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -57,11 +57,11 @@ struct symbol_conf {
const char *vmlinux_name;
};

-extern unsigned int symbol__priv_size;
+extern struct symbol_conf symbol_conf;

static inline void *symbol__priv(struct symbol *self)
{
- return ((void *)self) - symbol__priv_size;
+ return ((void *)self) - symbol_conf.priv_size;
}

struct addr_location {
@@ -119,9 +119,8 @@ int sysfs__read_build_id(const char *filename, void *bf, size_t size);
bool dsos__read_build_ids(void);
int build_id__sprintf(u8 *self, int len, char *bf);

-int symbol__init(struct symbol_conf *conf);
-int perf_session__create_kernel_maps(struct perf_session *self,
- struct symbol_conf *conf);
+int symbol__init(void);
+int perf_session__create_kernel_maps(struct perf_session *self);

extern struct list_head dsos__user, dsos__kernel;
extern struct dso *vdso;
--
1.6.2.5


2009-12-15 15:59:55

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 2/3] perf symbols: Adopt the strlists for dso, comm,

From: Arnaldo Carvalho de Melo <[email protected]>

Will be used in perf diff too.

Cc: Frédéric Weisbecker <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Paul Mackerras <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/builtin-annotate.c | 4 +-
tools/perf/builtin-diff.c | 9 ++---
tools/perf/builtin-kmem.c | 4 +-
tools/perf/builtin-record.c | 6 ++--
tools/perf/builtin-report.c | 73 +++++++++++++++++----------------------
tools/perf/builtin-timechart.c | 4 +-
tools/perf/builtin-trace.c | 4 +-
tools/perf/util/symbol.c | 33 ++++++++++++++++++
tools/perf/util/symbol.h | 9 +++++
9 files changed, 89 insertions(+), 57 deletions(-)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index e656e25..645d580 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -518,14 +518,14 @@ static const struct option options[] = {

int cmd_annotate(int argc, const char **argv, const char *prefix __used)
{
+ argc = parse_options(argc, argv, options, annotate_usage, 0);
+
symbol_conf.priv_size = sizeof(struct sym_priv);
symbol_conf.try_vmlinux_path = true;

if (symbol__init() < 0)
return -1;

- argc = parse_options(argc, argv, options, annotate_usage, 0);
-
setup_sorting(annotate_usage, options);

if (argc) {
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 67328d1..4fde606 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -265,11 +265,6 @@ static const struct option options[] = {

int cmd_diff(int argc, const char **argv, const char *prefix __used)
{
- if (symbol__init() < 0)
- return -1;
-
- setup_sorting(diff_usage, options);
-
argc = parse_options(argc, argv, options, diff_usage, 0);
if (argc) {
if (argc > 2)
@@ -281,6 +276,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix __used)
input_new = argv[0];
}

+ if (symbol__init() < 0)
+ return -1;
+
+ setup_sorting(diff_usage, options);
setup_pager();
return __cmd_diff();
}
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index e078797..fc21ad7 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -766,13 +766,13 @@ static int __cmd_record(int argc, const char **argv)

int cmd_kmem(int argc, const char **argv, const char *prefix __used)
{
- symbol__init();
-
argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);

if (!argc)
usage_with_options(kmem_usage, kmem_options);

+ symbol__init();
+
if (!strncmp(argv[0], "rec", 3)) {
return __cmd_record(argc, argv);
} else if (!strcmp(argv[0], "stat")) {
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 1da48a8..65301c5 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -632,13 +632,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
{
int counter;

- symbol__init();
-
argc = parse_options(argc, argv, options, record_usage,
- PARSE_OPT_STOP_AT_NON_OPTION);
+ PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc && target_pid == -1 && !system_wide)
usage_with_options(record_usage, options);

+ symbol__init();
+
if (!nr_counters) {
nr_counters = 1;
attrs[0].type = PERF_TYPE_HARDWARE;
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index c349bdb..03afac3 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -33,10 +33,6 @@

static char const *input_name = "perf.data";

-static char *dso_list_str, *comm_list_str, *sym_list_str,
- *col_width_list_str;
-static struct strlist *dso_list, *comm_list, *sym_list;
-
static int force;
static bool use_callchain;

@@ -365,8 +361,9 @@ static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self,

static void dso__calc_col_width(struct dso *self)
{
- if (!col_width_list_str && !field_sep &&
- (!dso_list || strlist__has_entry(dso_list, self->name))) {
+ if (!symbol_conf.col_width_list_str && !field_sep &&
+ (!symbol_conf.dso_list ||
+ strlist__has_entry(symbol_conf.dso_list, self->name))) {
unsigned int slen = strlen(self->name);
if (slen > dsos__col_width)
dsos__col_width = slen;
@@ -379,8 +376,9 @@ static void thread__comm_adjust(struct thread *self)
{
char *comm = self->comm;

- if (!col_width_list_str && !field_sep &&
- (!comm_list || strlist__has_entry(comm_list, comm))) {
+ if (!symbol_conf.col_width_list_str && !field_sep &&
+ (!symbol_conf.comm_list ||
+ strlist__has_entry(symbol_conf.comm_list, comm))) {
unsigned int slen = strlen(comm);

if (slen > comms__col_width) {
@@ -442,7 +440,7 @@ static size_t perf_session__fprintf_hist_entries(struct perf_session *self,
struct rb_node *nd;
size_t ret = 0;
unsigned int width;
- char *col_width = col_width_list_str;
+ char *col_width = symbol_conf.col_width_list_str;
int raw_printing_style;

raw_printing_style = !strcmp(pretty_printing_style, "raw");
@@ -468,7 +466,7 @@ static size_t perf_session__fprintf_hist_entries(struct perf_session *self,
}
width = strlen(se->header);
if (se->width) {
- if (col_width_list_str) {
+ if (symbol_conf.col_width_list_str) {
if (col_width) {
*se->width = atoi(col_width);
col_width = strchr(col_width, ',');
@@ -587,7 +585,8 @@ static int process_sample_event(event_t *event, struct perf_session *session)

dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);

- if (comm_list && !strlist__has_entry(comm_list, thread->comm))
+ if (symbol_conf.comm_list &&
+ !strlist__has_entry(symbol_conf.comm_list, thread->comm))
return 0;

cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
@@ -601,14 +600,15 @@ static int process_sample_event(event_t *event, struct perf_session *session)
if (al.map && !sort_dso.elide && !al.map->dso->slen_calculated)
dso__calc_col_width(al.map->dso);

- if (dso_list &&
+ if (symbol_conf.dso_list &&
(!al.map || !al.map->dso ||
- !(strlist__has_entry(dso_list, al.map->dso->short_name) ||
+ !(strlist__has_entry(symbol_conf.dso_list, al.map->dso->short_name) ||
(al.map->dso->short_name != al.map->dso->long_name &&
- strlist__has_entry(dso_list, al.map->dso->long_name)))))
+ strlist__has_entry(symbol_conf.dso_list, al.map->dso->long_name)))))
return 0;

- if (sym_list && al.sym && !strlist__has_entry(sym_list, al.sym->name))
+ if (symbol_conf.sym_list && al.sym &&
+ !strlist__has_entry(symbol_conf.sym_list, al.sym->name))
return 0;

if (perf_session__add_hist_entry(session, &al, data.callchain, data.period)) {
@@ -825,13 +825,13 @@ static const struct option options[] = {
OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent",
"Display callchains using output_type and min percent threshold. "
"Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt),
- OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]",
+ OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
"only consider symbols in these dsos"),
- OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]",
+ OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
"only consider symbols in these comms"),
- OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]",
+ OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
"only consider these symbols"),
- OPT_STRING('w', "column-widths", &col_width_list_str,
+ OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
"width[,width...]",
"don't try to adjust column width, use these fixed values"),
OPT_STRING('t', "field-separator", &field_sep, "separator",
@@ -840,32 +840,25 @@ static const struct option options[] = {
OPT_END()
};

-static void setup_list(struct strlist **list, const char *list_str,
- struct sort_entry *se, const char *list_name,
- FILE *fp)
+static void sort_entry__setup_elide(struct sort_entry *self,
+ struct strlist *list,
+ const char *list_name, FILE *fp)
{
- if (list_str) {
- *list = strlist__new(true, list_str);
- if (!*list) {
- fprintf(stderr, "problems parsing %s list\n",
- list_name);
- exit(129);
- }
- if (strlist__nr_entries(*list) == 1) {
- fprintf(fp, "# %s: %s\n", list_name,
- strlist__entry(*list, 0)->s);
- se->elide = true;
- }
+ if (list && strlist__nr_entries(list) == 1) {
+ fprintf(fp, "# %s: %s\n", list_name, strlist__entry(list, 0)->s);
+ self->elide = true;
}
}

int cmd_report(int argc, const char **argv, const char *prefix __used)
{
+ argc = parse_options(argc, argv, options, report_usage, 0);
+
+ setup_pager();
+
if (symbol__init() < 0)
return -1;

- argc = parse_options(argc, argv, options, report_usage, 0);
-
setup_sorting(report_usage, options);

if (parent_pattern != default_parent_pattern) {
@@ -880,11 +873,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
if (argc)
usage_with_options(report_usage, options);

- setup_pager();
-
- setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout);
- setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout);
- setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout);
+ sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
+ sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
+ sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);

if (field_sep && *field_sep == '.') {
fputs("'.' is the only non valid --field-separator argument\n",
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 9c98b7a..a589a43 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -1137,11 +1137,11 @@ static const struct option options[] = {

int cmd_timechart(int argc, const char **argv, const char *prefix __used)
{
- symbol__init();
-
argc = parse_options(argc, argv, options, timechart_usage,
PARSE_OPT_STOP_AT_NON_OPTION);

+ symbol__init();
+
if (argc && !strncmp(argv[0], "rec", 3))
return __cmd_record(argc, argv);
else if (argc)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 07ad25c..e2285e2 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -579,13 +579,13 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
exit(-1);
}

- symbol__init();
-
setup_scripting();

argc = parse_options(argc, argv, options, annotate_usage,
PARSE_OPT_STOP_AT_NON_OPTION);

+ if (symbol__init() < 0)
+ return -1;
setup_pager();

session = perf_session__new(input_name, O_RDONLY, 0);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 17ce012..164286a 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1,6 +1,7 @@
#include "util.h"
#include "../perf.h"
#include "session.h"
+#include "sort.h"
#include "string.h"
#include "symbol.h"
#include "thread.h"
@@ -1739,6 +1740,20 @@ out_fail:
return -1;
}

+static int setup_list(struct strlist **list, const char *list_str,
+ const char *list_name)
+{
+ if (list_str == NULL)
+ return 0;
+
+ *list = strlist__new(true, list_str);
+ if (!*list) {
+ pr_err("problems parsing %s list\n", list_name);
+ return -1;
+ }
+ return 0;
+}
+
int symbol__init(void)
{
elf_version(EV_CURRENT);
@@ -1749,7 +1764,25 @@ int symbol__init(void)
if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0)
return -1;

+ if (setup_list(&symbol_conf.dso_list,
+ symbol_conf.dso_list_str, "dso") < 0)
+ return -1;
+
+ if (setup_list(&symbol_conf.comm_list,
+ symbol_conf.comm_list_str, "comm") < 0)
+ goto out_free_dso_list;
+
+ if (setup_list(&symbol_conf.sym_list,
+ symbol_conf.sym_list_str, "symbol") < 0)
+ goto out_free_comm_list;
+
return 0;
+
+out_free_dso_list:
+ strlist__delete(symbol_conf.dso_list);
+out_free_comm_list:
+ strlist__delete(symbol_conf.comm_list);
+ return -1;
}

int perf_session__create_kernel_maps(struct perf_session *self)
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 7662947..d61f350 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -49,12 +49,21 @@ struct symbol {
char name[0];
};

+struct strlist;
+
struct symbol_conf {
unsigned short priv_size;
bool try_vmlinux_path,
use_modules,
sort_by_name;
const char *vmlinux_name;
+ char *dso_list_str,
+ *comm_list_str,
+ *sym_list_str,
+ *col_width_list_str;
+ struct strlist *dso_list,
+ *comm_list,
+ *sym_list;
};

extern struct symbol_conf symbol_conf;
--
1.6.2.5

2009-12-15 15:58:55

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 3/3] perf symbols: Move symbol filtering to event__preprocess_sample

From: Arnaldo Carvalho de Melo <[email protected]>

So that --dsos, --comm, --symbols can bem used in more tools, like in
perf diff:

$ perf record -f find / > /dev/null
$ perf record -f find / > /dev/null
$ perf diff --dsos /lib64/libc-2.10.1.so | head -5
1 +22392124 /lib64/libc-2.10.1.so _IO_vfprintf_internal
2 +6410655 /lib64/libc-2.10.1.so __GI_memmove
3 +1 +9192692 /lib64/libc-2.10.1.so _int_malloc
4 -1 -15158605 /lib64/libc-2.10.1.so _int_free
5 +45669 /lib64/libc-2.10.1.so _IO_new_file_xsputn
$

Cc: Frédéric Weisbecker <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Paul Mackerras <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/builtin-annotate.c | 2 +-
tools/perf/builtin-diff.c | 19 ++++++++-
tools/perf/builtin-report.c | 86 ++++++++---------------------------------
tools/perf/builtin-top.c | 2 +-
tools/perf/util/event.c | 42 ++++++++++++++++++++
tools/perf/util/symbol.c | 5 ++
tools/perf/util/symbol.h | 4 +-
7 files changed, 84 insertions(+), 76 deletions(-)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 645d580..593ff25 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -141,7 +141,7 @@ static int process_sample_event(event_t *event, struct perf_session *session)
return -1;
}

- if (perf_session__add_hist_entry(session, &al, 1)) {
+ if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) {
fprintf(stderr, "problem incrementing symbol count, "
"skipping event\n");
return -1;
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 4fde606..ff91e9c 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -50,6 +50,9 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi
return -1;
}

+ if (al.filtered)
+ return 0;
+
event__parse_sample(event, session->sample_type, &data);

if (al.sym && perf_session__add_hist_entry(session, &al, data.period)) {
@@ -182,10 +185,14 @@ blank: memset(displacement, ' ', sizeof(displacement));
printed = fprintf(fp, "%4lu %5.5s ", pos, displacement);

if (show_percent) {
- double old_percent = (old_count * 100) / pair_session->events_stats.total,
- new_percent = (self->count * 100) / session->events_stats.total;
- double diff = old_percent - new_percent;
+ double old_percent = 0, new_percent = 0, diff;
+
+ if (pair_session->events_stats.total > 0)
+ old_percent = (old_count * 100) / pair_session->events_stats.total;
+ if (session->events_stats.total > 0)
+ new_percent = (self->count * 100) / session->events_stats.total;

+ diff = old_percent - new_percent;
if (verbose)
printed += fprintf(fp, " %3.2f%% %3.2f%%", old_percent, new_percent);

@@ -260,6 +267,12 @@ static const struct option options[] = {
"Don't shorten the pathnames taking into account the cwd"),
OPT_BOOLEAN('P', "full-paths", &event_ops.full_paths,
"Don't shorten the pathnames taking into account the cwd"),
+ OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
+ "only consider symbols in these dsos"),
+ OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
+ "only consider symbols in these comms"),
+ OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
+ "only consider these symbols"),
OPT_END()
};

diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 03afac3..9c59534 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -316,14 +316,14 @@ static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self,

if (total_samples)
ret = percent_color_fprintf(fp,
- field_sep ? "%.2f" : " %6.2f%%",
+ symbol_conf.field_sep ? "%.2f" : " %6.2f%%",
(self->count * 100.0) / total_samples);
else
- ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count);
+ ret = fprintf(fp, symbol_conf.field_sep ? "%lld" : "%12lld ", self->count);

if (show_nr_samples) {
- if (field_sep)
- fprintf(fp, "%c%lld", *field_sep, self->count);
+ if (symbol_conf.field_sep)
+ fprintf(fp, "%c%lld", *symbol_conf.field_sep, self->count);
else
fprintf(fp, "%11lld", self->count);
}
@@ -332,7 +332,7 @@ static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self,
if (se->elide)
continue;

- fprintf(fp, "%s", field_sep ?: " ");
+ fprintf(fp, "%s", symbol_conf.field_sep ?: " ");
ret += se->print(fp, self, se->width ? *se->width : 0);
}

@@ -355,28 +355,11 @@ static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self,
return ret;
}

-/*
- *
- */
-
-static void dso__calc_col_width(struct dso *self)
-{
- if (!symbol_conf.col_width_list_str && !field_sep &&
- (!symbol_conf.dso_list ||
- strlist__has_entry(symbol_conf.dso_list, self->name))) {
- unsigned int slen = strlen(self->name);
- if (slen > dsos__col_width)
- dsos__col_width = slen;
- }
-
- self->slen_calculated = 1;
-}
-
static void thread__comm_adjust(struct thread *self)
{
char *comm = self->comm;

- if (!symbol_conf.col_width_list_str && !field_sep &&
+ if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
(!symbol_conf.comm_list ||
strlist__has_entry(symbol_conf.comm_list, comm))) {
unsigned int slen = strlen(comm);
@@ -452,16 +435,16 @@ static size_t perf_session__fprintf_hist_entries(struct perf_session *self,

fprintf(fp, "# Overhead");
if (show_nr_samples) {
- if (field_sep)
- fprintf(fp, "%cSamples", *field_sep);
+ if (symbol_conf.field_sep)
+ fprintf(fp, "%cSamples", *symbol_conf.field_sep);
else
fputs(" Samples ", fp);
}
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
continue;
- if (field_sep) {
- fprintf(fp, "%c%s", *field_sep, se->header);
+ if (symbol_conf.field_sep) {
+ fprintf(fp, "%c%s", *symbol_conf.field_sep, se->header);
continue;
}
width = strlen(se->header);
@@ -480,7 +463,7 @@ static size_t perf_session__fprintf_hist_entries(struct perf_session *self,
}
fprintf(fp, "\n");

- if (field_sep)
+ if (symbol_conf.field_sep)
goto print_entries;

fprintf(fp, "# ........");
@@ -542,13 +525,8 @@ static int validate_chain(struct ip_callchain *chain, event_t *event)

static int process_sample_event(event_t *event, struct perf_session *session)
{
- struct sample_data data;
- int cpumode;
+ struct sample_data data = { .period = 1, };
struct addr_location al;
- struct thread *thread;
-
- memset(&data, 0, sizeof(data));
- data.period = 1;

event__parse_sample(event, session->sample_type, &data);

@@ -576,39 +554,13 @@ static int process_sample_event(event_t *event, struct perf_session *session)
}
}

- thread = perf_session__findnew(session, data.pid);
- if (thread == NULL) {
- pr_debug("problem processing %d event, skipping it.\n",
+ if (event__preprocess_sample(event, session, &al, NULL) < 0) {
+ fprintf(stderr, "problem processing %d event, skipping it.\n",
event->header.type);
return -1;
}

- dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
-
- if (symbol_conf.comm_list &&
- !strlist__has_entry(symbol_conf.comm_list, thread->comm))
- return 0;
-
- cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
-
- thread__find_addr_location(thread, session, cpumode,
- MAP__FUNCTION, data.ip, &al, NULL);
- /*
- * We have to do this here as we may have a dso with no symbol hit that
- * has a name longer than the ones with symbols sampled.
- */
- if (al.map && !sort_dso.elide && !al.map->dso->slen_calculated)
- dso__calc_col_width(al.map->dso);
-
- if (symbol_conf.dso_list &&
- (!al.map || !al.map->dso ||
- !(strlist__has_entry(symbol_conf.dso_list, al.map->dso->short_name) ||
- (al.map->dso->short_name != al.map->dso->long_name &&
- strlist__has_entry(symbol_conf.dso_list, al.map->dso->long_name)))))
- return 0;
-
- if (symbol_conf.sym_list && al.sym &&
- !strlist__has_entry(symbol_conf.sym_list, al.sym->name))
+ if (al.filtered)
return 0;

if (perf_session__add_hist_entry(session, &al, data.callchain, data.period)) {
@@ -834,7 +786,7 @@ static const struct option options[] = {
OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
"width[,width...]",
"don't try to adjust column width, use these fixed values"),
- OPT_STRING('t', "field-separator", &field_sep, "separator",
+ OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
"separator for columns, no spaces will be added between "
"columns '.' is reserved."),
OPT_END()
@@ -877,11 +829,5 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);

- if (field_sep && *field_sep == '.') {
- fputs("'.' is the only non valid --field-separator argument\n",
- stderr);
- exit(129);
- }
-
return __cmd_report();
}
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index cd89b6d..ddc584b 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -948,7 +948,7 @@ static void event__process_sample(const event_t *self,
}

if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 ||
- al.sym == NULL)
+ al.sym == NULL || al.filtered)
return;

syme = symbol__priv(al.sym);
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 375fb6d..bf491fd 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -2,7 +2,9 @@
#include "event.h"
#include "debug.h"
#include "session.h"
+#include "sort.h"
#include "string.h"
+#include "strlist.h"
#include "thread.h"

static pid_t event__synthesize_comm(pid_t pid, int full,
@@ -299,6 +301,19 @@ try_again:
}
}

+static void dso__calc_col_width(struct dso *self)
+{
+ if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
+ (!symbol_conf.dso_list ||
+ strlist__has_entry(symbol_conf.dso_list, self->name))) {
+ unsigned int slen = strlen(self->name);
+ if (slen > dsos__col_width)
+ dsos__col_width = slen;
+ }
+
+ self->slen_calculated = 1;
+}
+
int event__preprocess_sample(const event_t *self, struct perf_session *session,
struct addr_location *al, symbol_filter_t filter)
{
@@ -308,6 +323,10 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
if (thread == NULL)
return -1;

+ if (symbol_conf.comm_list &&
+ !strlist__has_entry(symbol_conf.comm_list, thread->comm))
+ goto out_filtered;
+
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);

thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION,
@@ -315,6 +334,29 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
dump_printf(" ...... dso: %s\n",
al->map ? al->map->dso->long_name :
al->level == 'H' ? "[hypervisor]" : "<not found>");
+ /*
+ * We have to do this here as we may have a dso with no symbol hit that
+ * has a name longer than the ones with symbols sampled.
+ */
+ if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated)
+ dso__calc_col_width(al->map->dso);
+
+ if (symbol_conf.dso_list &&
+ (!al->map || !al->map->dso ||
+ !(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) ||
+ (al->map->dso->short_name != al->map->dso->long_name &&
+ strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name)))))
+ goto out_filtered;
+
+ if (symbol_conf.sym_list && al->sym &&
+ !strlist__has_entry(symbol_conf.sym_list, al->sym->name))
+ goto out_filtered;
+
+ al->filtered = false;
+ return 0;
+
+out_filtered:
+ al->filtered = true;
return 0;
}

diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 164286a..7707897 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1764,6 +1764,11 @@ int symbol__init(void)
if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0)
return -1;

+ if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') {
+ pr_err("'.' is the only non valid --field-separator argument\n");
+ return -1;
+ }
+
if (setup_list(&symbol_conf.dso_list,
symbol_conf.dso_list_str, "dso") < 0)
return -1;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index d61f350..6015152 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -56,7 +56,8 @@ struct symbol_conf {
bool try_vmlinux_path,
use_modules,
sort_by_name;
- const char *vmlinux_name;
+ const char *vmlinux_name,
+ *field_sep;
char *dso_list_str,
*comm_list_str,
*sym_list_str,
@@ -79,6 +80,7 @@ struct addr_location {
struct symbol *sym;
u64 addr;
char level;
+ bool filtered;
};

struct dso {
--
1.6.2.5

2009-12-15 16:11:53

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 1/3] perf symbols: Make symbol_conf global

Em Tue, Dec 15, 2009 at 01:58:35PM -0200, Arnaldo Carvalho de Melo escreveu:
> From: Arnaldo Carvalho de Melo <[email protected]>
>
> This simplifies a lot of functions, less stuff to be done by tool
> writers.

Ingo, this clashes with the batch Masami-san just submitted, please
defer applying my series so that I can rework it to take into account
the changes that Masami-san contributed.

Thanks!

- Arnaldo

2009-12-15 17:15:39

by Masami Hiramatsu

[permalink] [raw]
Subject: Re: [PATCH 1/3] perf symbols: Make symbol_conf global

Arnaldo Carvalho de Melo wrote:
> Em Tue, Dec 15, 2009 at 01:58:35PM -0200, Arnaldo Carvalho de Melo escreveu:
>> From: Arnaldo Carvalho de Melo <[email protected]>
>>
>> This simplifies a lot of functions, less stuff to be done by tool
>> writers.
>
> Ingo, this clashes with the batch Masami-san just submitted, please
> defer applying my series so that I can rework it to take into account
> the changes that Masami-san contributed.

Ah, thank you for working on that!
Feel free to update builtin-probe.c too :-) it might be use
symbol.c in different way from others. (for getting vmlinux path)

Thanks,

--
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: [email protected]