[PATCH] Add support for callgraph profiling to operf
Unlike legacy oprofile (using opcontrol), the --calllgraph
option of operf is boolean. The perf_events kernel subsystem
records the whole callstack, so there's no way to use the
depth value used in legacy oprofile.
This patch is already pushed into the perf-events branch, but review
comments are welcome.
Signed-off-by: Maynard Johnson <maynardj@...>
---
doc/operf.1.in | 9 +-
libperf_events/operf_counter.cpp | 12 ++-
libperf_events/operf_counter.h | 8 +-
libperf_events/operf_event.h | 5 +
libperf_events/operf_mangling.cpp | 4 +-
libperf_events/operf_sfile.cpp | 56 +++++++++-
libperf_events/operf_sfile.h | 4 +
libperf_events/operf_utils.cpp | 237 +++++++++++++++++++++---------------
libperf_events/operf_utils.h | 2 +-
libutil++/cverb.cpp | 1 +
libutil++/cverb.h | 4 +-
pe_profiling/operf.cpp | 40 +++++--
12 files changed, 256 insertions(+), 126 deletions(-)
diff --git a/doc/operf.1.in b/doc/operf.1.in
index 3c4ab24..c279323 100644
--- a/doc/operf.1.in
+++ b/doc/operf.1.in
@@ -73,12 +73,13 @@ storing sample data files in locations accessible by regular users.
.br
.TP
.BI "--verbose / -V [level]"
-This increases the verbosity of the output. Level could be: debug,
-perf_events, misc, all.
+A comma-separated list of debugging control values, used to increase the verbosity of the output.
+Valid values are: debug, perf_events, misc, sfile, arcs, or the special value, 'all'.
.br
.TP
-.BI "--callgraph / -g [depth]"
-This option enables the callgraph to be saved during profiling.
+.BI "--callgraph / -g"
+This option enables the callgraph to be saved during profiling. NOTE: The
+full callchain is recorded, so there is no depth limit.
.br
.TP
.BI "--reset / -r"
diff --git a/libperf_events/operf_counter.cpp b/libperf_events/operf_counter.cpp
index 9eb1a49..8d4cc85 100644
--- a/libperf_events/operf_counter.cpp
+++ b/libperf_events/operf_counter.cpp
@@ -53,10 +53,13 @@ static const char *__op_magic = "OPFILE";
} // end anonymous namespace
-operf_counter::operf_counter(operf_event_t evt, bool enable_on_exec) {
+operf_counter::operf_counter(operf_event_t evt, bool enable_on_exec, bool do_cg)
+{
memset(&attr, 0, sizeof(attr));
attr.size = sizeof(attr);
attr.sample_type = OP_BASIC_SAMPLE_FORMAT;
+ if (do_cg)
+ attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
attr.type = PERF_TYPE_RAW;
attr.config = evt.evt_code;
attr.sample_period = evt.count;
@@ -127,7 +130,7 @@ operf_record::~operf_record()
}
operf_record::operf_record(string outfile, bool sys_wide, pid_t the_pid, bool pid_running,
- vector<operf_event_t> & events, vmlinux_info_t vi)
+ vector<operf_event_t> & events, vmlinux_info_t vi, bool do_cg)
{
int flags = O_CREAT|O_RDWR|O_TRUNC;
struct sigaction sa;
@@ -138,6 +141,7 @@ operf_record::operf_record(string outfile, bool sys_wide, pid_t the_pid, bool pi
pid = the_pid;
pid_started = pid_running;
system_wide = sys_wide;
+ callgraph = do_cg;
total_bytes_recorded = 0;
poll_count = 0;
evts = events;
@@ -335,7 +339,9 @@ void operf_record::setup()
perfCounters.push_back(tmp_pcvec);
for (unsigned event = 0; event < evts.size(); event++) {
evts[event].counter = event;
- perfCounters[cpu].push_back(operf_counter(evts[event], (!pid_started && !system_wide)));
+ perfCounters[cpu].push_back(operf_counter(evts[event],
+ (!pid_started && !system_wide),
+ callgraph));
if ((rc = perfCounters[cpu][event].perf_event_open(pid, real_cpu, event, this)) < 0) {
err_msg = "Internal Error. Perf event setup failed.";
goto error;
diff --git a/libperf_events/operf_counter.h b/libperf_events/operf_counter.h
index 20c5577..3012ccd 100644
--- a/libperf_events/operf_counter.h
+++ b/libperf_events/operf_counter.h
@@ -53,7 +53,7 @@ op_perf_event_open(struct perf_event_attr * attr,
class operf_counter {
public:
- operf_counter(operf_event_t evt, bool enable_on_exec);
+ operf_counter(operf_event_t evt, bool enable_on_exec, bool callgraph);
~operf_counter();
int perf_event_open(pid_t ppid, int cpu, unsigned counter, operf_record * pr);
const struct perf_event_attr * the_attr(void) const { return &attr; }
@@ -76,7 +76,8 @@ public:
* and pid_running=true if profiling an already active process; otherwise false.
*/
operf_record(std::string outfile, bool sys_wide, pid_t the_pid, bool pid_running,
- std::vector<operf_event_t> & evts, OP_perf_utils::vmlinux_info_t vi);
+ std::vector<operf_event_t> & evts, OP_perf_utils::vmlinux_info_t vi,
+ bool callgraph);
~operf_record();
void recordPerfData(void);
int out_fd(void) const { return outputFile; }
@@ -97,6 +98,7 @@ private:
pid_t pid;
bool pid_started;
bool system_wide;
+ bool callgraph;
std::vector< std::vector<operf_counter> > perfCounters;
int total_bytes_recorded;
int poll_count;
@@ -117,7 +119,7 @@ public:
int convertPerfData(void);
bool is_valid(void) {return valid; }
int get_eventnum_by_perf_event_id(u64 id) const;
- inline const operf_event * get_event_by_counter(u32 counter) { return &evts[counter]; }
+ inline const operf_event_t * get_event_by_counter(u32 counter) { return &evts[counter]; }
private:
std::string inputFname;
diff --git a/libperf_events/operf_event.h b/libperf_events/operf_event.h
index 6e9cd7f..755b5ab 100644
--- a/libperf_events/operf_event.h
+++ b/libperf_events/operf_event.h
@@ -90,6 +90,10 @@ struct mmap_data {
u64 prev;
};
+struct ip_callchain {
+ u64 nr;
+ u64 ips[0];
+};
struct sample_data {
u64 ip;
u32 pid, tid;
@@ -101,6 +105,7 @@ struct sample_data {
u64 period;
u32 raw_size;
void *raw_data;
+ struct ip_callchain * callchain;
};
struct mmap_info {
diff --git a/libperf_events/operf_mangling.cpp b/libperf_events/operf_mangling.cpp
index 1109f67..9780aab 100644
--- a/libperf_events/operf_mangling.cpp
+++ b/libperf_events/operf_mangling.cpp
@@ -95,7 +95,7 @@ mangle_filename(struct operf_sfile * last, struct operf_sfile const * sf, int co
}
*/
else {
- values.cg_image_name = sf->image_name;
+ values.cg_image_name = last->image_name;
}
}
@@ -117,7 +117,7 @@ static void fill_header(struct opd_header * header, unsigned long counter,
int is_kernel, int cg_to_is_kernel,
int spu_samples, uint64_t embed_offset, time_t mtime)
{
- const struct operf_event * event = operfRead.get_event_by_counter(counter);
+ const operf_event_t * event = operfRead.get_event_by_counter(counter);
memset(header, '\0', sizeof(struct opd_header));
header->version = OPD_VERSION;
diff --git a/libperf_events/operf_sfile.cpp b/libperf_events/operf_sfile.cpp
index cbd2394..0f36d05 100644
--- a/libperf_events/operf_sfile.cpp
+++ b/libperf_events/operf_sfile.cpp
@@ -250,7 +250,6 @@ void operf_sfile_dup(struct operf_sfile * to, struct operf_sfile * from)
list_init(&to->lru);
}
-
static odb_t * get_file(struct operf_transient const * trans, int is_cg)
{
struct operf_sfile * sf = trans->current;
@@ -341,6 +340,61 @@ static void verbose_sample(struct operf_transient const * trans, vma_t pc)
printf("\n");
}
+static void
+verbose_arc(struct operf_transient const * trans, vma_t from, vma_t to)
+{
+ printf("Arc ");
+ verbose_print_sample(trans->current, from, trans->event);
+ printf(" -> 0x%llx", to);
+ printf("\n");
+}
+
+void operf_sfile_log_arc(struct operf_transient const * trans)
+{
+ int err;
+ vma_t from = trans->pc;
+ vma_t to = trans->last_pc;
+ uint64_t key;
+ odb_t * file;
+
+ file = get_file(trans, 1);
+
+ /* absolute value -> offset */
+ if (trans->current->kernel)
+ from -= trans->current->kernel->start;
+
+ if (trans->last->kernel)
+ to -= trans->last->kernel->start;
+
+ /* TODO handle anon
+ if (trans->current->anon)
+ from -= trans->current->anon->start;
+
+ if (trans->last->anon)
+ to -= trans->last->anon->start;
+ */
+
+ if (cverb << varcs)
+ verbose_arc(trans, from, to);
+
+ /* TODO: handle sstats
+ if (!file) {
+ opd_stats[OPD_LOST_SAMPLEFILE]++;
+ return;
+ }
+ */
+
+ /* Possible narrowings to 32-bit value only. */
+ key = to & (0xffffffff);
+ key |= ((uint64_t)from) << 32;
+
+ err = odb_update_node(file, key);
+ if (err) {
+ fprintf(stderr, "%s: %s\n", __FUNCTION__, strerror(err));
+ abort();
+ }
+
+}
void operf_sfile_log_sample(struct operf_transient const * trans)
{
diff --git a/libperf_events/operf_sfile.h b/libperf_events/operf_sfile.h
index 2a38d2f..5042a70 100644
--- a/libperf_events/operf_sfile.h
+++ b/libperf_events/operf_sfile.h
@@ -100,6 +100,7 @@ struct operf_transient {
u32 tgid;
vma_t start_addr;
vma_t end_addr;
+ bool cg;
// TODO: handle extended
//void * ext;
};
@@ -142,6 +143,9 @@ void operf_sfile_log_sample(struct operf_transient const * trans);
void operf_sfile_log_sample_count(struct operf_transient const * trans,
unsigned long int count);
+/** Log a callgraph arc. */
+void operf_sfile_log_arc(struct operf_transient const * trans);
+
/** initialise hashes */
void operf_sfile_init(void);
diff --git a/libperf_events/operf_utils.cpp b/libperf_events/operf_utils.cpp
index b485220..e0bb523 100644
--- a/libperf_events/operf_utils.cpp
+++ b/libperf_events/operf_utils.cpp
@@ -255,11 +255,137 @@ static void __handle_mmap_event(event_t * event)
}
}
+static struct operf_transient * __get_operf_trans(struct sample_data * data)
+{
+ operf_process_info * proc;
+ const struct operf_mmap * op_mmap = NULL;
+ struct operf_transient * retval = NULL;
+
+ if (trans.tgid == data->pid) {
+ proc = trans.cur_procinfo;
+ } else {
+ // Find operf_process info for data.tgid.
+ std::map<pid_t, operf_process_info *>::const_iterator it = process_map.find(data->pid);
+ if (it != process_map.end() && (it->second->appname_valid())) {
+ proc = it->second;
+ trans.cur_procinfo = proc;
+ } else {
+ // TODO
+ /* This can happen for the following reasons:
+ * - We get a sample before getting a COMM or MMAP
+ * event for the process being profiled
+ * - The COMM event has been processed, but since that
+ * only gives 16 chars of the app name, we don't
+ * have a valid app name yet
+ * - The kernel incorrectly records a sample for a
+ * process other than the one we requested (not
+ * likely -- this would be a kernel bug if it did)
+ *
+ * Need to look into caching these discarded samples and trying to
+ * process them after we have a valid app name recorded. This could
+ * cause a lot of thrashing about. But at the very least,
+ * we need to log the lost sample.
+ */
+ cverb << vmisc << "Discarding sample -- process info unavailable" << endl;
+ goto out;
+ }
+ }
+
+ // Now find mmapping that contains the data.ip address.
+ // Use that mmapping to set fields in trans.
+ if (trans.in_kernel) {
+ if (data->ip >= kernel_mmap->start_addr &&
+ data->ip < kernel_mmap->end_addr) {
+ op_mmap = kernel_mmap;
+ } else {
+ map<u64, struct operf_mmap *>::iterator it;
+ it = kernel_modules.begin();
+ while (it != kernel_modules.end()) {
+ if (data->ip >= it->second->start_addr &&
+ data->ip < it->second->end_addr) {
+ op_mmap = it->second;
+ break;
+ }
+ it++;
+ }
+ } if (!op_mmap) {
+ if ((kernel_mmap->start_addr == 0ULL) &&
+ (kernel_mmap->end_addr == 0ULL))
+ op_mmap = kernel_mmap;
+ }
+ if (!op_mmap)
+ cverb << vperf << "Discarding sample: no mapping found for kernel sample addr "
+ << hex << data->ip << endl;
+ } else {
+ op_mmap = proc->find_mapping_for_sample(data->ip);
+ }
+ if (op_mmap) {
+ cverb << vperf << "Found mmap for sample" << endl;
+ trans.image_name = op_mmap->filename;
+ trans.app_filename = proc->get_app_name().c_str();
+ trans.start_addr = op_mmap->start_addr;
+ trans.end_addr = op_mmap->end_addr;
+ trans.tgid = data->pid;
+ trans.tid = data->tid;
+ if (trans.in_kernel)
+ trans.pc = data->ip;
+ else
+ trans.pc = data->ip - trans.start_addr;
+
+ trans.sample_id = data->id;
+ retval = &trans;
+ } else {
+ // TODO: log lost sample due to no mapping
+ /* Ditto the comment above -- i.e., we need to look into caching these
+ * discarded samples and trying to process them later.
+ */
+ cverb << vmisc << "Discarding sample where no mapping was found" << endl;
+ retval = NULL;
+ }
+out:
+ return retval;
+}
+
+static void __handle_callchain(u64 * array, struct sample_data * data)
+{
+ data->callchain = (struct ip_callchain *) array;
+ if (data->callchain->nr) {
+ cverb << vperf << "Processing callchain" << endl;
+ for (int i = 0; i < data->callchain->nr; i++) {
+ data->ip = data->callchain->ips[i];
+ if (data->ip >= PERF_CONTEXT_MAX) {
+ switch (data->ip) {
+ case PERF_CONTEXT_HV:
+ // hypervisor samples currently unsupported
+ // TODO: log lost callgraph arc
+ break;
+ case PERF_CONTEXT_KERNEL:
+ trans.in_kernel = 1;
+ break;
+ case PERF_CONTEXT_USER:
+ trans.in_kernel = 0;
+ break;
+ default:
+ break;
+ }
+ continue;
+ }
+ if (data->ip && __get_operf_trans(data)) {
+ if ((trans.current = operf_sfile_find(&trans))) {
+ operf_sfile_log_arc(&trans);
+ update_trans_last(&trans);
+ }
+ } else {
+ // TODO: log lost callgraph arc, but only for non-zero data->ip
+ }
+ }
+ }
+}
+
static void __handle_sample_event(event_t * event)
{
struct sample_data data;
bool early_match = false;
- operf_process_info * proc;
const u64 type = OP_BASIC_SAMPLE_FORMAT;
const struct operf_mmap * op_mmap = NULL;
@@ -320,7 +446,6 @@ static void __handle_sample_event(event_t * event)
goto out;
}
- // TODO: handle callchain
cverb << vperf << "(IP, " << event->header.misc << "): " << dec << data.pid << "/"
<< data.tid << ": " << hex << (unsigned long long)data.ip
<< endl << "\tdata ID: " << data.id << "; data stream ID: " << data.stream_id << endl;
@@ -332,11 +457,13 @@ static void __handle_sample_event(event_t * event)
<< " is invalid. Skipping sample." << endl;
goto out;
}
- // Do the common case first. For the "no_vmlinux" case, start_addr and end_addr will be
- // zero, so need to make sure we detect that.
+ /* Check for the common case first -- i.e., where the current sample is from
+ * the same context as the previous sample. For the "no-vmlinux" case, start_addr
+ * and end_addr will be zero, so need to make sure we detect that.
+ */
if (trans.in_kernel) {
if (trans.image_name && trans.tgid == data.pid) {
- // For the no_vmlinux case . . .
+ // For the no-vmlinux case . . .
if ((trans.start_addr == 0ULL) && (trans.end_addr == 0ULL)) {
trans.pc = data.ip;
early_match = true;
@@ -353,110 +480,22 @@ static void __handle_sample_event(event_t * event)
early_match = true;
}
}
- if (early_match) {
- operf_sfile_find(&trans);
+ if (early_match || __get_operf_trans(&data)) {
+ trans.current = operf_sfile_find(&trans);
/*
* trans.current may be NULL if a kernel sample falls through
* the cracks, or if it's a sample from an anon region we couldn't find
*/
- if (trans.current)
+ if (trans.current) {
/* log the sample or arc */
operf_sfile_log_sample(&trans);
- update_trans_last(&trans);
- goto out;
- }
-
- if (trans.tgid == data.pid) {
- proc = trans.cur_procinfo;
- } else {
- // Find operf_process info for data.tgid.
- std::map<pid_t, operf_process_info *>::const_iterator it = process_map.find(data.pid);
- if (it != process_map.end() && (it->second->appname_valid())) {
- proc = it->second;
- trans.cur_procinfo = proc;
- } else {
- // TODO
- /* This can happen for the following reasons:
- * - We get a sample before getting a COMM or MMAP
- * event for the process being profiled
- * - The COMM event has been processed, but since that
- * only gives 16 chars of the app name, we don't
- * have a valid app name yet
- * - The kernel incorrectly records a sample for a
- * process other than the one we requested (not
- * likely -- this would be a kernel bug if it did)
- *
- * Need to look into caching these discarded samples and trying to
- * process them after we have a valid app name recorded. This could
- * cause a lot of thrashing about. But at the very least,
- * we need to log the lost sample.
- */
- cverb << vmisc << "Discarding sample -- process info unavailable" << endl;
- goto out;
+ update_trans_last(&trans);
+ if (operf_options::callgraph)
+ __handle_callchain(array, &data);
}
}
- // Now find mmapping that contains the data.ip address.
- // Use that mmapping to set fields in trans.
- if (trans.in_kernel) {
- if (data.ip >= kernel_mmap->start_addr &&
- data.ip < kernel_mmap->end_addr) {
- op_mmap = kernel_mmap;
- } else {
- map<u64, struct operf_mmap *>::iterator it;
- it = kernel_modules.begin();
- while (it != kernel_modules.end()) {
- if (data.ip >= it->second->start_addr &&
- data.ip < it->second->end_addr) {
- op_mmap = it->second;
- break;
- }
- it++;
- }
- } if (!op_mmap) {
- if ((kernel_mmap->start_addr == 0ULL) &&
- (kernel_mmap->end_addr == 0ULL))
- op_mmap = kernel_mmap;
- }
- if (!op_mmap)
- cverb << vperf << "Discarding sample: no mapping found for kernel sample addr "
- << hex << data.ip << endl;
- } else {
- op_mmap = proc->find_mapping_for_sample(data.ip);
- }
- if (op_mmap) {
- cverb << vperf << "Found mmap for sample" << endl;
- trans.image_name = op_mmap->filename;
- trans.app_filename = proc->get_app_name().c_str();
- trans.start_addr = op_mmap->start_addr;
- trans.end_addr = op_mmap->end_addr;
- trans.tgid = data.pid;
- trans.tid = data.tid;
- if (trans.in_kernel)
- trans.pc = data.ip;
- else
- trans.pc = data.ip - trans.start_addr;
-
- trans.sample_id = data.id;
- trans.current = operf_sfile_find(&trans);
- /*
- * trans.current may be NULL if a kernel sample falls through
- * the cracks, or if it's a sample from an anon region we couldn't find
- */
- if (trans.current)
- /* log the sample or arc */
- operf_sfile_log_sample(&trans);
-
- update_trans_last(&trans);
- } else {
- // TODO: log lost sample due to no mapping
- /* Ditto the comment above -- i.e., we need to look into caching these
- * discarded samples and trying to process them later.
- */
- cverb << vmisc << "Discarding sample where no mapping was found" << endl;
- return;
- }
out:
return;
}
diff --git a/libperf_events/operf_utils.h b/libperf_events/operf_utils.h
index 4e02907..dea26ec 100644
--- a/libperf_events/operf_utils.h
+++ b/libperf_events/operf_utils.h
@@ -28,7 +28,7 @@ namespace operf_options {
extern bool system_wide;
extern bool reset;
extern int pid;
-extern int callgraph_depth;
+extern bool callgraph;
extern int mmap_pages_mult;
extern std::string session_dir;
extern bool separate_cpu;
diff --git a/libutil++/cverb.cpp b/libutil++/cverb.cpp
index 150aa55..9dfb89a 100644
--- a/libutil++/cverb.cpp
+++ b/libutil++/cverb.cpp
@@ -26,6 +26,7 @@ verbose vlevel1("level1");
verbose vdebug("debug");
verbose vstats("stats");
verbose vsfile("sfile");
+verbose varcs("arcs");
verbose vxml("xml");
namespace {
diff --git a/libutil++/cverb.h b/libutil++/cverb.h
index 905c528..a14c62e 100644
--- a/libutil++/cverb.h
+++ b/libutil++/cverb.h
@@ -76,8 +76,10 @@ private:
extern verbose vlevel1; /**< named "level1" */
extern verbose vdebug; /**< named "debug" */
extern verbose vstats; /**< named "stats" */
+extern verbose vxml; /**< named "xml" */
// all sample filename manipulation.
extern verbose vsfile; /**< named "sfile" */
-extern verbose vxml; /**< named "xml" */
+extern verbose varcs; /**< named "arcs" */
+
#endif /* !CVERB_H */
diff --git a/pe_profiling/operf.cpp b/pe_profiling/operf.cpp
index e395673..add4984 100644
--- a/pe_profiling/operf.cpp
+++ b/pe_profiling/operf.cpp
@@ -61,6 +61,8 @@ char op_samples_current_dir[PATH_MAX];
#define DEFAULT_OPERF_OUTFILE "operf.data"
+#define CALLGRAPH_MIN_COUNT_SCALE 15
+
static char full_pathname[PATH_MAX];
static char * app_name_SAVE = NULL;
static char * app_args = NULL;
@@ -80,7 +82,7 @@ namespace operf_options {
bool system_wide;
bool reset;
int pid;
-int callgraph_depth;
+bool callgraph;
int mmap_pages_mult;
string session_dir;
string vmlinux;
@@ -93,13 +95,13 @@ vector<string> verbose_string;
popt::option options_array[] = {
popt::option(verbose_string, "verbose", 'V',
- "verbose output", "debug,perf_events,misc,all"),
+ "verbose output", "debug,perf_events,misc,sfile,arcs,all"),
popt::option(operf_options::session_dir, "session-dir", 'd',
"session path to hold sample data", "path"),
popt::option(operf_options::vmlinux, "vmlinux", 'k',
"pathname for vmlinux file to use for symbol resolution and debuginfo", "path"),
- popt::option(operf_options::callgraph_depth, "callgraph", 'g',
- "callgraph depth", "depth"),
+ popt::option(operf_options::callgraph, "callgraph", 'g',
+ "enable callgraph recording"),
popt::option(operf_options::system_wide, "system-wide", 's',
"profile entire system"),
popt::option(operf_options::reset, "reset", 'r',
@@ -253,7 +255,8 @@ int start_profiling_app(void)
vi.start = kernel_start;
vi.end = kernel_end;
operf_record operfRecord(outputfile, operf_options::system_wide, app_PID,
- (operf_options::pid == app_PID), events, vi);
+ (operf_options::pid == app_PID), events, vi,
+ operf_options::callgraph);
if (operfRecord.get_valid() == false) {
/* If valid is false, it means that one of the "known" errors has
* occurred:
@@ -645,6 +648,9 @@ static void _process_events_list(void)
string full_cmd = cmd;
string event_spec = operf_options::evts[i];
full_cmd += event_spec;
+ if (operf_options::callgraph) {
+ full_cmd += " --callgraph=1";
+ }
fp = popen(full_cmd.c_str(), "r");
if (fp == NULL) {
cerr << "Unable to execute ophelp to get info for event "
@@ -652,8 +658,11 @@ static void _process_events_list(void)
exit(EXIT_FAILURE);
}
if (fgetc(fp) == EOF) {
- cerr << "Unable to find info for event "
+ cerr << "Error retrieving info for event "
<< event_spec << endl;
+ if (operf_options::callgraph)
+ cerr << "Note: When doing callgraph profiling, the sample count must be"
+ << endl << "15 times the minimum count value for the event." << endl;
exit(EXIT_FAILURE);
}
char * event_str = op_xstrndup(event_spec.c_str(), event_spec.length());
@@ -695,6 +704,7 @@ static void get_default_event(void)
struct op_default_event_descr descr;
vector<operf_event_t> tmp_events;
+
op_default_event(cpu_type, &descr);
if (descr.name[0] == '\0') {
cerr << "Unable to find default event" << endl;
@@ -702,7 +712,18 @@ static void get_default_event(void)
}
memset(&dft_evt, 0, sizeof(dft_evt));
- dft_evt.count = descr.count;
+ if (operf_options::callgraph) {
+ struct op_event * _event;
+ op_events(cpu_type);
+ if ((_event = find_event_by_name(descr.name, 0, 0))) {
+ dft_evt.count = _event->min_count * CALLGRAPH_MIN_COUNT_SCALE;
+ } else {
+ cerr << "Error getting event info for " << descr.name << endl;
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ dft_evt.count = descr.count;
+ }
dft_evt.evt_um = descr.um;
strncpy(dft_evt.name, descr.name, OP_MAX_EVT_NAME_LEN - 1);
dft_evt.op_evt_code = _get_event_code(dft_evt.name);
@@ -890,11 +911,6 @@ static void process_args(int argc, char const ** argv)
vector<string> non_options;
popt::parse_options(argc, argv, non_options, true/*non-options IS an app*/);
- if (operf_options::callgraph_depth) {
- cerr << "The --callgraph option is not yet supported." << endl;
- exit(EXIT_FAILURE);
- }
-
if (!non_options.empty()) {
if (operf_options::pid || operf_options::system_wide)
__print_usage_and_exit(NULL);
--
1.6.2.rc2
|