////////////////////////////////////////////////////////////////////////
// class decls
-class PerfConsumer;
+// Unwind statistics for a Dwfl and associated process.
+struct UnwindDwflStats {
+ Dwfl *dwfl;
+ char *comm;
+ int max_frames; /* for diagnostic purposes */
+ int total_samples; /* for diagnostic purposes */
+ int lost_samples; /* for diagnostic purposes */
+ Dwfl_Unwound_Source last_unwound; /* track CFI source, for diagnostic purposes */
+ Dwfl_Unwound_Source worst_unwound; /* track CFI source, for diagnostic purposes */
+};
+
+// Unwind statistics for a single module identified by build-id.
+struct UnwindModuleStats {
+ /* TODO: For gmon.out: histogram. */
+ /* TODO: For gmon.out: callgraph arcs. */
+};
+
+struct UnwindStatsTable
+{
+ unordered_map<pid_t, UnwindDwflStats> dwfl_tab;
+ unordered_map<string, UnwindModuleStats> buildid_tab;
+ UnwindStatsTable () {}
+ ~UnwindStatsTable () {}
+
+ UnwindDwflStats *pid_find(pid_t pid);
+ UnwindDwflStats *pid_find_or_create(pid_t pid);
+ const char *pid_find_comm(pid_t pid);
+ Dwfl *pid_find_dwfl(pid_t pid);
+ void pid_store_dwfl(pid_t pid, Dwfl *dwfl);
+
+ /* TODO buildid functions :: buildid_find{,_or_create} */
+};
+
+class PerfConsumer;
// A PerfReader creates perf_events file descriptors, monitors the
// mmap'd ring buffers for events, and dispatches decoded forms to a
{
const perf_event_header *event;
uint32_t pid, tid;
- vector<pair<string,Dwarf_Addr>> buildid_reladdrs;
+ vector<pair<string,Dwarf_Addr>> buildid_reladdrs; /* TODO: Populate. */
vector<Dwarf_Addr> addrs;
int elfclass;
Dwarf_Addr base; /* for diagnostic purposes */
};
-struct DwflEntry;
class UnwindSampleConsumer;
UnwindSampleConsumer *consumer;
UnwindSample last_us;
Dwflst_Process_Tracker *tracker;
- unordered_map<pid_t, DwflEntry> dwfltab;
+ UnwindStatsTable *stats;
- DwflEntry *dwfltab_find_or_create(pid_t pid);
- const char *pid_find_comm(pid_t pid);
- Dwfl *pid_find_dwfl(pid_t pid);
- void pid_store_dwfl(pid_t pid, Dwfl *dwfl);
int find_procfile(Dwfl *dwfl, pid_t *pid, Elf **elf, int *elf_fd);
Dwfl *find_dwfl(pid_t pid, const uint64_t *regs, uint32_t nregs,
Elf **elf, bool *cached);
public:
- PerfConsumerUnwinder(UnwindSampleConsumer* usc): consumer(usc) {
+ PerfConsumerUnwinder(UnwindSampleConsumer* usc, UnwindStatsTable *ust)
+ : consumer(usc), stats(ust) {
this->tracker = dwflst_tracker_begin (&dwfl_cfi_callbacks);
}
- PerfConsumerUnwinder(UnwindSampleConsumer* usc, PerfReader *reader): consumer(usc) {
+ PerfConsumerUnwinder(UnwindSampleConsumer* usc, UnwindStatsTable *ust, PerfReader *reader)
+ : consumer(usc), stats(ust) {
this->reader = reader;
this->tracker = dwflst_tracker_begin (&dwfl_cfi_callbacks);
}
// a received stream of UnwindSamples.
class UnwindStatsConsumer: public UnwindSampleConsumer
{
- unordered_map<int,unsigned> event_unwind_counts;
- unordered_map<string,unsigned> event_buildid_hits;
+ UnwindStatsTable *stats;
+
+ /* TODO subsumed by stats? */
+ unordered_map<int,unsigned> event_unwind_counts; /* TODO by pid? */
+ unordered_map<string,unsigned> event_buildid_hits; /* by buildid */
public:
- UnwindStatsConsumer() {}
+ UnwindStatsConsumer(UnwindStatsTable *usc) : stats(usc) {}
~UnwindStatsConsumer();
void process(const UnwindSample* sample);
};
// them by buildid, for eventual writing out into gmon.out format files.
class GprofUnwindSampleConsumer: public UnwindSampleConsumer
{
+ UnwindStatsTable *stats;
public:
- GprofUnwindSampleConsumer() {}
+ GprofUnwindSampleConsumer(UnwindStatsTable *usc) : stats(usc) {}
~GprofUnwindSampleConsumer(); // write out all the gmon.$BUILDID.out files
void process(const UnwindSample* sample); // accumulate hits / callgraph edges (need maxdepth=1 only)
};
// Create the perf processing pipeline as per command line options
PerfReader *pr = nullptr;
- UnwindStatsConsumer *usc = nullptr;
+ UnwindStatsTable *tab = nullptr;
+ UnwindSampleConsumer *usc = nullptr;
PerfConsumerUnwinder *pcu = nullptr;
StatsPerfConsumer *spc = nullptr;
if (gmon)
{
- usc = new UnwindStatsConsumer();
- pcu = new PerfConsumerUnwinder(usc);
+ tab = new UnwindStatsTable();
+ usc = new GprofUnwindSampleConsumer(tab); /* TODO also reduce maxdepth */
+ pcu = new PerfConsumerUnwinder(usc, tab);
pr = new PerfReader(&attr, pcu, pid);
-#if 0
- GprofUnwindSampleConsumer usc;
- PerfReader pr(&attr, &pcu, pid);
- PerfConsumerUnwinder pcu(&usc, pr);
-#endif
}
else
{
+ tab = new UnwindStatsTable();
+ usc = new UnwindStatsConsumer(tab);
+ pcu = new PerfConsumerUnwinder (usc, tab);
+ pr = new PerfReader(&attr, pcu, pid);
+#if 0
spc = new StatsPerfConsumer();
pr = new PerfReader(&attr, spc, pid);
+#endif
}
signal(SIGINT, sigint_handler);
this->event_type_counts[ehdr->type] ++;
}
-////////////////////////////////////////////////////////////////////////
-// real perf consumer: unwind helpers
+//////////////////////////////////////////////////////////////////////
+// unwind stats table for PerfConsumerUnwinder + downstream consumers
-struct DwflEntry {
- Dwfl *dwfl;
- char *comm;
- int max_frames; /* for diagnostic purposes */
- int total_samples; /* for diagnostic purposes */
- int lost_samples; /* for diagnostic purposes */
- Dwfl_Unwound_Source last_unwound; /* track CFI source, for diagnostic purposes */
- Dwfl_Unwound_Source worst_unwound; /* track CFI source, for diagnostic purposes */
-};
+UnwindDwflStats *UnwindStatsTable::pid_find (pid_t pid)
+{
+ if (this->dwfl_tab.count(pid) == 0)
+ this->dwfl_tab.emplace(pid, UnwindDwflStats());
+ return &this->dwfl_tab[pid];
+}
-DwflEntry *PerfConsumerUnwinder::dwfltab_find_or_create (pid_t pid)
+UnwindDwflStats *UnwindStatsTable::pid_find_or_create (pid_t pid)
{
- if (this->dwfltab.count(pid) == 0)
- this->dwfltab.emplace(pid, DwflEntry());
- return &this->dwfltab[pid];
+ if (this->dwfl_tab.count(pid) == 0)
+ this->dwfl_tab.emplace(pid, UnwindDwflStats());
+ return &this->dwfl_tab[pid];
}
static const char *unknown_comm = "<unknown>";
-const char *PerfConsumerUnwinder::pid_find_comm (pid_t pid)
+const char *UnwindStatsTable::pid_find_comm (pid_t pid)
{
- DwflEntry *entry = this->dwfltab_find_or_create(pid);
+ UnwindDwflStats *entry = this->pid_find_or_create(pid);
if (entry == NULL)
return unknown_comm;
if (entry->comm != NULL)
return entry->comm;
}
-Dwfl *PerfConsumerUnwinder::pid_find_dwfl (pid_t pid)
+Dwfl *UnwindStatsTable::pid_find_dwfl (pid_t pid)
{
- if (this->dwfltab.count(pid) == 0)
+ if (this->dwfl_tab.count(pid) == 0)
return NULL;
- return this->dwfltab[pid].dwfl;
+ return this->dwfl_tab[pid].dwfl;
}
-void PerfConsumerUnwinder::pid_store_dwfl (pid_t pid, Dwfl *dwfl)
+void UnwindStatsTable::pid_store_dwfl (pid_t pid, Dwfl *dwfl)
{
- DwflEntry *entry = this->dwfltab_find_or_create(pid);
+ UnwindDwflStats *entry = this->pid_find_or_create(pid);
if (entry == NULL)
return;
entry->dwfl = dwfl;
return;
}
+////////////////////////////////////////////////////////////////////////
+// real perf consumer: unwind helpers
+
/* TODO: Including extern "C" libdwflP.h in a C++ program is a no-go. Add dwfl_process() &c as an API? */
struct _DwflHack
{
this->last_us.base = this->last_us.sp;
if (!*cached)
- this->pid_store_dwfl (pid, dwfl);
+ this->stats->pid_store_dwfl (pid, dwfl);
*out_elf = elf;
return dwfl;
}
}
#endif
- DwflEntry *dwfl_ent = this->dwfltab_find_or_create(this->last_us.pid);
+ UnwindDwflStats *dwfl_ent = this->stats->pid_find_or_create(this->last_us.pid);
if (dwfl_ent != NULL)
{
Dwfl_Unwound_Source unwound_source = dwfl_frame_unwound_source(state);
{
const char *comm = NULL;
if (show_summary)
- comm = this->pid_find_comm(pid);
+ comm = this->stats->pid_find_comm(pid);
if (show_frames)
cout << endl; /* extra newline for padding */
Elf *elf = NULL;
bool cached = false;
Dwfl *dwfl = this->find_dwfl (pid, regs, nregs, &elf, &cached);
- DwflEntry *dwfl_ent = NULL;
+ UnwindDwflStats *dwfl_ent = NULL;
if (dwfl == NULL)
{
if (show_summary)
{
if (dwfl_ent == NULL)
- dwfl_ent = this->dwfltab_find_or_create(pid);
+ dwfl_ent = this->stats->pid_find_or_create(pid);
dwfl_ent->total_samples++;
dwfl_ent->lost_samples++;
}
{
/* For final diagnostics. */
if (dwfl_ent == NULL)
- dwfl_ent = this->dwfltab_find_or_create(pid);
+ dwfl_ent = this->stats->pid_find_or_create(pid);
if (this->last_us.addrs.size() > (unsigned long)dwfl_ent->max_frames)
dwfl_ent->max_frames = this->last_us.addrs.size();
dwfl_ent->total_samples++;
uint8_t build_id_size, const uint8_t *build_id,
const char *filename)
{
- Dwfl *dwfl = this->pid_find_dwfl(pid);
+ Dwfl *dwfl = this->stats->pid_find_dwfl(pid);
if (dwfl != NULL)
{
dwfl_report_begin_add(dwfl);
////////////////////////////////////////////////////////////////////////
-// unwind data consumers // gprof
+// unwind data consumers // basic statistics
UnwindStatsConsumer::~UnwindStatsConsumer()
{
- /* TODO (REVIEW.3): Add the code to print the final statistics table neatly. */
- cout << "pid / unwind-hit counts:" << endl;
- for (const auto& kv : this->event_unwind_counts)
- cout << "pid " << setbase(10) << kv.first << " count " << kv.second << setbase(16) << endl;
-
- cout << "buildid / unwind-hit counts:" << endl;
- for (const auto& kv : this->event_buildid_hits)
- cout << "buildid " << kv.first << " count " << kv.second << endl;
+ /* TODO: Perhaps move this to a this->stats->show_summary() method, also invokable from GmonUnwindSampleConsumer? */
+ if (show_summary)
+ {
+#define PERCENT(x,tot) ((x+tot == 0)?0.0:((double)x)/((double)tot)*100.0)
+ int total_samples = 0;
+ int total_lost_samples = 0;
+ cout << endl << "=== pid / sample counts ===" << endl;
+ for (auto& p : this->stats->dwfl_tab)
+ {
+ pid_t pid = p.first;
+ UnwindDwflStats& d = p.second;
+ fprintf (stdout, N_("%d %s -- max %d frames, received %d samples, lost %d samples (%.1f%%) (last %s, worst %s)\n"),
+ pid, d.comm, d.max_frames,
+ d.total_samples, d.lost_samples,
+ PERCENT(d.lost_samples, d.total_samples),
+ dwfl_unwound_source_str(d.last_unwound),
+ dwfl_unwound_source_str(d.worst_unwound));
+ total_samples += d.total_samples;
+ total_lost_samples += d.lost_samples;
+ }
+ fprintf(stdout, "===\n");
+ fprintf(stdout, N_("TOTAL -- received %d samples, lost %d samples, loaded %ld processes\n"),
+ total_samples, total_lost_samples,
+ this->stats->dwfl_tab.size() /* TODO: If implementing eviction, need to maintain a separate count of evicted pids. */);
+ cout << endl;
+
+ /* TODO: Implement in terms of the build-id table. */
+ cout << "=== buildid / unwind-hit counts ===" << endl;
+ for (const auto& kv : this->event_buildid_hits)
+ cout << "buildid " << kv.first << " -- received " << kv.second << " samples" << endl;
+ }
}
void UnwindStatsConsumer::process(const UnwindSample* sample)
this->event_buildid_hits[p.first] ++;
}
+////////////////////////////////////////////////////////////////////////
+// unwind data consumers // gprof
+
+GprofUnwindSampleConsumer::~GprofUnwindSampleConsumer()
+{
+ /* TODO write gmon.out files */
+}
+
+void GprofUnwindSampleConsumer::process(const UnwindSample *sample)
+{
+ /* TODO: Record pc in histogram */
+ /* TODO: Record callgraph arc in histogram */
+}