From: Serhei Makarov Date: Tue, 3 Feb 2026 20:42:44 +0000 (-0500) Subject: src/stackprof.cxx snap: basic json metadata, per-file cf fche's suggestion X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a3fed78f1cbfd1f5fb890b1e6e578ed19d53c4f8;p=thirdparty%2Felfutils.git src/stackprof.cxx snap: basic json metadata, per-file cf fche's suggestion --- diff --git a/src/Makefile.am b/src/Makefile.am index e12c09f0..13a8289d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -116,8 +116,9 @@ elfclassify_LDADD = $(libelf) $(libdw) $(libeu) $(argp_LDADD) srcfiles_SOURCES = srcfiles.cxx srcfiles_LDADD = $(libdw) $(libelf) $(libeu) $(argp_LDADD) $(libarchive_LIBS) $(libdebuginfod) stackprof_SOURCES = stackprof.cxx +stackprof_CPPFLAGS = $(jsonc_CXXFLAGS) stackprof_CXXFLAGS = -std=c++11 -Wall -stackprof_LDADD = $(libebl) $(libdw) $(libelf) $(libeu) $(argp_LDADD) $(libpfm_LIBS) +stackprof_LDADD = $(libebl) $(libdw) $(libelf) $(libeu) $(argp_LDADD) $(libpfm_LIBS) $(jsonc_LIBS) installcheck-binPROGRAMS: $(bin_PROGRAMS) bad=0; pid=$$$$; list="$(bin_PROGRAMS)"; for p in $$list; do \ diff --git a/src/stackprof.cxx b/src/stackprof.cxx index a0d7b59a..98c266f2 100644 --- a/src/stackprof.cxx +++ b/src/stackprof.cxx @@ -61,6 +61,8 @@ #include #endif +#include + #include #include #include @@ -134,33 +136,16 @@ struct hash_arc { // Unwind statistics for a single module identified by build-id. struct UnwindModuleStats { - Dwfl_Module *mod; map histogram; /* sorted by pc */ unordered_map, uint32_t, hash_arc> callgraph; - UnwindModuleStats() : mod(NULL) {} - void record_pc(Dwfl_Module *mod2, Dwarf_Addr pc) { - if (mod == NULL) mod = mod2; -#if 0 - /* TODO(REVIEW.1): 'duplicate modules for buildid' should not be an - issue (libdwflst caching is done on the underlying Elf - structs), but it might be worth tracking to see if the paths - are ever different. */ - if (mod != mod2) - cerr << "XXX duplicate modules for buildid" << endl; -#endif + void record_pc(Dwarf_Addr pc) { if (histogram.count(pc) == 0) histogram[pc] = 0; histogram[pc]++; } - void record_callgraph_arc(Dwfl_Module *mod2, Dwarf_Addr from, Dwarf_Addr to) { - if (mod == NULL) mod = mod2; -#if 0 - /* TODO(REVIEW.1): ditto. */ - if (mod != mod2) - cerr << "XXX duplicate modules for buildid" << endl; -#endif - pair arc(from, to); + void record_callgraph_arc(Dwarf_Addr from, Dwarf_Addr to) { + std::pair arc(from, to); if (callgraph.count(arc) == 0) callgraph[arc] = 0; callgraph[arc]++; @@ -406,7 +391,8 @@ struct gmon_hist_hdr; class GprofUnwindSampleConsumer: public UnwindSampleConsumer { UnwindStatsTable *stats; - unordered_map buildid_to_path; + unordered_map buildid_to_mainfile; + unordered_map buildid_to_debugfile; public: GprofUnwindSampleConsumer(UnwindStatsTable *usc) : stats(usc) {} @@ -1626,15 +1612,49 @@ void GprofUnwindSampleConsumer::record_gmon_out(const string& buildid, UnwindMod { string filename = output_dir + "/" + "gmon." + buildid + ".out"; string exe_symlink_path = output_dir + "/" + "gmon." + buildid + ".exe"; - - string target_path = buildid_to_path[buildid]; + string json_path = output_dir + "/" + "gmon." + buildid + ".json"; + + string target_path = buildid_to_mainfile[buildid]; if (symlink(target_path.c_str(), exe_symlink_path.c_str()) == -1) { // Handle error, e.g., print errno or throw exception cerr << "symlink failed: " << strerror(errno) << endl; - return; + //return; /* TODO: We may want to re-create the symlink on repeated runs. */ } - // plop buildid_to_path bits into per-gmon-out json files + // TODO(REVIEW.4): plop buildid_to_{mainfile,debugfile} bits into per-gmon-out json files + json_object *metadata = json_object_new_object(); + if (!metadata) { + json_fail: + cerr << "json allocation failed: " << strerror(errno) << endl; + return; + } + json_object *buildid_js = json_object_new_string(buildid.c_str()); + if (NULL == buildid_js) goto json_fail; + json_object_object_add(metadata, "buildid", buildid_js); + const char *mainfile = NULL; + if (buildid_to_mainfile.count(buildid) != 0) + mainfile = buildid_to_mainfile[buildid].c_str(); + if (mainfile != NULL) + { + json_object *mainfile_js = json_object_new_string(mainfile); + if (NULL == mainfile_js) goto json_fail; + json_object_object_add(metadata, "mainfile", mainfile_js); + } + const char *debugfile = NULL; + if (buildid_to_debugfile.count(buildid) != 0) + debugfile = buildid_to_debugfile[buildid].c_str(); + if (debugfile != NULL) + { + json_object *debugfile_js = json_object_new_string(debugfile); + if (NULL == debugfile_js) goto json_fail; + json_object_object_add(metadata, "debugfile", debugfile_js); + } + const char *metadata_str = json_object_to_json_string(metadata); + if (!metadata_str) goto json_fail; + ofstream of_js (json_path); + cerr << metadata_str; + of_js << metadata_str; + of_js.close(); ofstream of (filename, ios::binary); if (!of) @@ -1750,11 +1770,12 @@ GprofUnwindSampleConsumer::~GprofUnwindSampleConsumer() debuginfod-find can be used as a mostly-functional fallback (for packaged rather than locally built executables) if the results are moved to another system. */ - const char *mainfile; - const char *debugfile; - const char *modname = dwfl_module_info (m.mod, NULL, NULL, NULL, NULL, - NULL, &mainfile, &debugfile); - (void) modname; + const char *mainfile = NULL; + if (buildid_to_mainfile.count(buildid) != 0) + mainfile = buildid_to_mainfile[buildid].c_str(); + const char *debugfile = NULL; + if (buildid_to_debugfile.count(buildid) != 0) + debugfile = buildid_to_debugfile[buildid].c_str(); if (show_summary) fprintf (stdout, N_("buildid %s (%s%s%s) -- received %ld distinct pcs, %ld callgraph arcs\n"), /* TODO also count samples / estimated histogram size? */ buildid.c_str(), @@ -1814,7 +1835,11 @@ void GprofUnwindSampleConsumer::process(const UnwindSample *sample) const char *debugfile; dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL, &mainfile, &debugfile); - if (mainfile && !buildid_to_path.count(buildid)) buildid_to_path[buildid] = mainfile; + if (mainfile && !buildid_to_mainfile.count(buildid)) + buildid_to_mainfile[buildid] = mainfile; + if (debugfile && !buildid_to_debugfile.count(buildid)) + buildid_to_debugfile[buildid] = debugfile; + /* TODO(REVIEW.6): Also monitor for collisions. */ UnwindModuleStats *buildid_ent = this->stats->buildid_find_or_create(buildid, mod); @@ -1826,12 +1851,12 @@ void GprofUnwindSampleConsumer::process(const UnwindSample *sample) if (i >= 0) name = dwfl_module_relocation_info (mod, i, NULL); #endif - buildid_ent->record_pc(mod, pc); + buildid_ent->record_pc(pc); if (mod == mod2) // intra-module call { int j = dwfl_module_relocate_address (mod, &pc2); // map pc2 also (void) j; - buildid_ent->record_callgraph_arc(mod, pc2, pc); + buildid_ent->record_callgraph_arc(pc2, pc); } }