hwpmc: hwpmc: record page size to fix analysis in some case

This fixes a bug where a binary linked using max-page-size=0x200000
can result in a bogus relocation offset when running on a system
with a smaller page size.  This causes samples  to fall outside
the image mapping or be translated to the wrong address (resulting
in symbol resolution, or incorrect symbol resolution).  We noticed
this at Netflix because we run a patchset enabling 16k pages on
amd64 and have been compiling userspace with a 2MB page size.
Since we started doing this profiling userspace binaries has been
wonky.

Reviewed by: ali_mashtizadeh.com
Differential Revision: https://reviews.freebsd.org/D59771
Sponsored by: Netflix
This commit is contained in:
Andrew Gallatin committed 2026-09-23 14:35:44 -04:00
1 parent ac9c07a3e9
commit 2449fa9c4d
9 files changed
+52 -12

No files matched your search

+1 -1
View File
@@ -338,7 +338,7 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
case PMCLOG_TYPE_MAP_IN:
PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_map_in);
PMCLOG_READ32(le,ev->pl_u.pl_mi.pl_pid);
PMCLOG_SKIP32(le);
PMCLOG_READ32(le,ev->pl_u.pl_mi.pl_u);
PMCLOG_READADDR(le,ev->pl_u.pl_mi.pl_start);
PMCLOG_READSTRING(le, ev->pl_u.pl_mi.pl_pathname, pathlen);
break;
+7
View File
@@ -69,6 +69,13 @@ struct pmclog_ev_initialize {
struct pmclog_ev_map_in {
pid_t pl_pid;
union {
uint32_t pl_u;
struct {
uint8_t pl_pageshift;
uint8_t pl_pad[3];
};
};
uintfptr_t pl_start;
char pl_pathname[PATH_MAX];
};
+1
View File
@@ -132,6 +132,7 @@ struct pmcstat_args {
int pa_mergepmc; /* merge PMC with same name */
double pa_duration; /* time duration */
uint32_t pa_tid;
uint8_t pa_pageshift; /* page shift recorded in log */
int pa_argc;
char **pa_argv;
STAILQ_HEAD(, pmcstat_ev) pa_events;
+17 -2
View File
@@ -312,7 +312,7 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image,
const char *path, *elfbase;
char *p, *endp;
bool first_exec_segment;
uintfptr_t minva, maxva;
uintfptr_t minva, maxva, pagesize;
Elf *e;
Elf_Scn *scn;
GElf_Ehdr eh;
@@ -330,6 +330,11 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image,
image->pi_dynlinkerpath = NULL;
image->pi_vaddr = 0;
if (args->pa_pageshift != 0)
pagesize = 1ULL << args->pa_pageshift;
else
pagesize = getpagesize();
path = pmcstat_string_unintern(image->pi_execpath);
assert(path != NULL);
@@ -421,7 +426,17 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image,
case PT_LOAD:
if ((ph.p_flags & PF_X) != 0 &&
first_exec_segment) {
image->pi_vaddr = ph.p_vaddr & (-ph.p_align);
if (image->pi_iskernelmodule) {
image->pi_vaddr = ph.p_vaddr &
(-ph.p_align);
} else {
/*
* User MAP_IN records depend
* on page size.
*/
image->pi_vaddr = rounddown2(
ph.p_vaddr, pagesize);
}
first_exec_segment = false;
}
break;
+1
View File
@@ -235,6 +235,7 @@ pmcstat_analyze_log(struct pmcstat_args *args,
PMCSTAT_ALLOCATE);
assert(pp != NULL);
args->pa_pageshift = ev.pl_u.pl_mi.pl_pageshift;
image_path = pmcstat_string_intern(ev.pl_u.pl_mi.
pl_pathname);
+5 -1
View File
@@ -947,6 +947,7 @@ void
pmclog_process_map_in(struct pmc_owner *po, pid_t pid, uintfptr_t start,
const char *path)
{
struct pmclog_map_in *mi;
int pathlen, recordlen;
KASSERT(path != NULL, ("[pmclog,%d] map-in, null path", __LINE__));
@@ -956,8 +957,11 @@ pmclog_process_map_in(struct pmc_owner *po, pid_t pid, uintfptr_t start,
pathlen;
PMCLOG_RESERVE(po, PMCLOG_TYPE_MAP_IN, recordlen);
mi = (struct pmclog_map_in *)ph;
PMCLOG_EMIT32(pid);
PMCLOG_EMIT32(0);
mi->pl_u = 0;
mi->pl_pageshift = PAGE_SHIFT;
PMCLOG_EMIT32(mi->pl_u);
PMCLOG_EMITADDR(start);
PMCLOG_EMITSTRING(path,pathlen);
PMCLOG_DESPATCH_SYNC(po);
+7 -1
View File
@@ -159,7 +159,13 @@ struct pmclog_initialize {
struct pmclog_map_in {
PMCLOG_ENTRY_HEADER
uint32_t pl_pid;
uint32_t pl_pad;
union {
uint32_t pl_u;
struct {
uint8_t pl_pageshift;
uint8_t pl_pad[3];
};
};
uintfptr_t pl_start; /* 8 byte aligned */
char pl_pathname[PATH_MAX];
} __packed;
+11 -6
View File
@@ -89,8 +89,8 @@ syminfo::to_string(bool show_line)
return ss.str();
}
pmcview::pmcview() : tscfreq(0), pmcid(), pmcinfo(), procs(), tidtopid(),
images(), sysroot(""), filter()
pmcview::pmcview() : tscfreq(0), pageshift(0), pmcid(), pmcinfo(), procs(),
tidtopid(), images(), sysroot(""), filter()
{
char *root;
@@ -436,7 +436,7 @@ pmcview::process(struct pmclog_ev_threadexit &p)
* if the dwarf symbols are available.
*/
image
pmcview::loadimage(const std::string &path)
pmcview::loadimage(const std::string &path, bool iskernel)
{
std::string fullpath;
image im;
@@ -519,7 +519,12 @@ pmcview::loadimage(const std::string &path)
if (ph.p_type == PT_LOAD) {
if ((ph.p_flags & PF_X) != 0 && !foundexec) {
im.vaddr = ph.p_vaddr & ~(ph.p_align - 1);
uint64_t alignment;
alignment = iskernel ? ph.p_align :
(pageshift != 0 ? 1ULL << pageshift :
(uint64_t)getpagesize());
im.vaddr = rounddown2(ph.p_vaddr, alignment);
foundexec = true;
}
}
@@ -727,8 +732,9 @@ pmcview::process(struct pmclog_ev_map_in &p)
{
// Kernel map-in events should be mapped to pid 0
pid_t pid = (p.pl_pid == -1) ? 0 : p.pl_pid;
pageshift = p.pl_pageshift;
image im = loadimage(p.pl_pathname);
image im = loadimage(p.pl_pathname, pid == 0);
mapimage(pid, im, p.pl_start);
}
@@ -958,4 +964,3 @@ pmcview::printvm(pid_t pid)
i.second.highpc, i.second.image.c_str());
}
}
+2 -1
View File
@@ -389,6 +389,7 @@ protected:
void printvm(pid_t pid);
// Fields available to views
uint64_t tscfreq;
uint8_t pageshift;
std::unordered_map<uint32_t, uint32_t> pmcid;
std::unordered_map<uint32_t, struct pmcinfo> pmcinfo;
std::unordered_map<pid_t, struct procinfo> procs;
@@ -404,7 +405,7 @@ protected:
std::vector<struct pmcinfox> extpmcinfo;
std::map<uint32_t, struct cpuidleaf> cpuid; // x86 Only
private:
image loadimage(const std::string &path);
image loadimage(const std::string &path, bool iskernel = false);
void mapimage(pid_t pid, const image &im, uint64_t linkaddr);
void loadsymboltable(image *im, Elf *e, Elf_Scn *scn, GElf_Shdr *sh);
void loadsymbols(image *im);