/* should match BPF_USDT_MAX_ARG_CNT in usdt.bpf.h */ #define USDT_MAX_ARG_CNT 12
/* should match struct __bpf_usdt_spec from usdt.bpf.h */ struct usdt_spec { struct usdt_arg_spec args[USDT_MAX_ARG_CNT];
__u64 usdt_cookie; short arg_cnt;
};
struct usdt_note { constchar *provider; constchar *name; /* USDT args specification string, e.g.: *"-4@%esi-4@-24(%rbp)-4@%ecx2@%ax8@%rdx"
*/ constchar *args; long loc_addr; long base_addr; long sema_addr;
};
struct usdt_target { long abs_ip; long rel_ip; long sema_off; struct usdt_spec spec; constchar *spec_str;
};
specs_map = bpf_object__find_map_by_name(obj, "__bpf_usdt_specs");
ip_to_spec_id_map = bpf_object__find_map_by_name(obj, "__bpf_usdt_ip_to_spec_id"); if (!specs_map || !ip_to_spec_id_map) {
pr_warn("usdt: failed to find USDT support BPF maps, did you forget to include bpf/usdt.bpf.h?\n"); return ERR_PTR(-ESRCH);
}
man = calloc(1, sizeof(*man)); if (!man) return ERR_PTR(-ENOMEM);
void usdt_manager_free(struct usdt_manager *man)
{ if (IS_ERR_OR_NULL(man)) return;
free(man->free_spec_ids);
free(man);
}
staticint sanity_check_usdt_elf(Elf *elf, constchar *path)
{
GElf_Ehdr ehdr; int endianness;
if (elf_kind(elf) != ELF_K_ELF) {
pr_warn("usdt: unrecognized ELF kind %d for '%s'\n", elf_kind(elf), path); return -EBADF;
}
switch (gelf_getclass(elf)) { case ELFCLASS64: if (sizeof(void *) != 8) {
pr_warn("usdt: attaching to 64-bit ELF binary '%s' is not supported\n", path); return -EBADF;
} break; case ELFCLASS32: if (sizeof(void *) != 4) {
pr_warn("usdt: attaching to 32-bit ELF binary '%s' is not supported\n", path); return -EBADF;
} break; default:
pr_warn("usdt: unsupported ELF class for '%s'\n", path); return -EBADF;
}
if (!gelf_getehdr(elf, &ehdr)) return -EINVAL;
if (ehdr.e_type != ET_EXEC && ehdr.e_type != ET_DYN) {
pr_warn("usdt: unsupported type of ELF binary '%s' (%d), only ET_EXEC and ET_DYN are supported\n",
path, ehdr.e_type); return -EBADF;
}
/* Handle containerized binaries only accessible from */proc/<pid>/root/<path>.Theywillbereportedasjust/<path>in */proc/<pid>/maps.
*/ if (sscanf(lib_path, "/proc/%d/root%s", &tmp_pid, path) == 2 && pid == tmp_pid) goto proceed;
if (!realpath(lib_path, path)) {
pr_warn("usdt: failed to get absolute path of '%s' (err %s), using path as is...\n",
lib_path, errstr(-errno));
libbpf_strlcpy(path, lib_path, sizeof(path));
}
proceed:
sprintf(line, "/proc/%d/maps", pid);
f = fopen(line, "re"); if (!f) {
err = -errno;
pr_warn("usdt: failed to open '%s' to get base addr of '%s': %s\n",
line, lib_path, errstr(err)); return err;
}
/* We need to handle lines with no path at the end: * *7f5c6f5d1000-7f5c6f5d3000rw-p001c700008:0421238613/usr/lib64/libc-2.17.so *7f5c6f5d3000-7f5c6f5d8000rw-p0000000000:000 *7f5c6f5d8000-7f5c6f5d9000r-xp00000000103:01362990598/data/users/andriin/linux/tools/bpf/usdt/libhello_usdt.so
*/ while (fscanf(f, "%zx-%zx %s %zx %*s %*d%[^\n]\n",
&seg_start, &seg_end, mode, &seg_off, line) == 5) { void *tmp;
/* to handle no path case (see above) we need to capture line *withoutskippinganywhitespaces.Soweneedtostrip *leadingwhitespacesmanuallyhere
*/
i = 0; while (isblank(line[i]))
i++; if (strcmp(line + i, path) != 0) continue;
staticstruct elf_seg *find_elf_seg(struct elf_seg *segs, size_t seg_cnt, long virtaddr)
{ struct elf_seg *seg; int i;
/* for ELF binaries (both executables and shared libraries), we are *givenvirtualaddress(absoluteforexecutables,relativefor *libraries)whichshouldmatchaddressrangeof[seg_start,seg_end)
*/ for (i = 0, seg = segs; i < seg_cnt; i++, seg++) { if (seg->start <= virtaddr && virtaddr < seg->end) return seg;
} return NULL;
}
staticstruct elf_seg *find_vma_seg(struct elf_seg *segs, size_t seg_cnt, long offset)
{ struct elf_seg *seg; int i;
/* for VMA segments from /proc/<pid>/maps file, provided "address" is *actuallyafileoffset,soshouldbefallwithinlogical *offset-basedrangeof[offset_start,offset_end)
*/ for (i = 0, seg = segs; i < seg_cnt; i++, seg++) { if (seg->offset <= offset && offset < seg->offset + (seg->end - seg->start)) return seg;
} return NULL;
}
err = parse_elf_segs(elf, path, &segs, &seg_cnt); if (err) {
pr_warn("usdt: failed to process ELF program segments for '%s': %s\n",
path, errstr(err)); goto err_out;
}
/* .stapsdt.base ELF section is optional, but is used for prelink *offsetcompensation(seeabigcommentfurtherbelow)
*/ if (find_elf_sec_by_name(elf, USDT_BASE_SEC, &base_shdr, &base_scn) == 0)
base_addr = base_shdr.sh_addr;
data = elf_getdata(notes_scn, 0);
off = 0; while ((off = gelf_getnote(data, off, &nhdr, &name_off, &desc_off)) > 0) { long usdt_abs_ip, usdt_rel_ip, usdt_sema_off = 0; struct usdt_note note; struct elf_seg *seg = NULL; void *tmp;
/* We need to compensate "prelink effect". See [0] for details, *relevantpartsquotedhere: * *EachSDTprobealsoexpandsintoanon-allocatedELFnote.Youcan *findthisbylookingatSHT_NOTEsectionsanddecodingtheformat; *seebelowfordetails.Becausethenoteisnon-allocated,itmeans *thereisnoruntimecost,andalsopreservedinbothstrippedfiles *and.debugfiles. * *However,thismeansthatprelinkwon'tadjustthenote'scontents *foraddressoffsets.Instead,thisisdoneviathe.stapsdt.base *section.Thisisaspecialsectionthatisaddedtothetext.We *willonlyeverhaveoneofthesesectionsinafinallinkandit *willonlyeverbeonebytelong.Nothingaboutthissectionitself *matters,wejustuseitasamarkertodetectprelinkaddress *adjustments. * *Eachprobenoterecordsthelink-timeaddressofthe.stapsdt.base *sectionalongsidetheprobePCaddress.Thedecodercomparesthe *baseaddressstoredinthenotewiththe.stapsdt.basesection's *sh_addr.Initiallythesearethesame,butthesectionheaderwill *beadjustedbyprelink.Sothedecoderappliesthedifferenceto *theprobePCaddresstogetthecorrectprelinkedPCaddress;the *sameadjustmentisappliedtothesemaphoreaddress,ifany. * *[0]https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation
*/
usdt_abs_ip = note.loc_addr; if (base_addr && note.base_addr)
usdt_abs_ip += base_addr - note.base_addr;
/* When attaching uprobes (which is what USDTs basically are) *kernelexpectsfileoffsettobespecified,notarelative *virtualaddress,soweneedtotranslatevirtualaddressto *fileoffset,forbothET_EXECandET_DYNbinaries.
*/
seg = find_elf_seg(segs, seg_cnt, usdt_abs_ip); if (!seg) {
err = -ESRCH;
pr_warn("usdt: failed to find ELF program segment for '%s:%s' in '%s' at IP 0x%lx\n",
usdt_provider, usdt_name, path, usdt_abs_ip); goto err_out;
} if (!seg->is_exec) {
err = -ESRCH;
pr_warn("usdt: matched ELF binary '%s' segment [0x%lx, 0x%lx) for '%s:%s' at IP 0x%lx is not executable\n",
path, seg->start, seg->end, usdt_provider, usdt_name,
usdt_abs_ip); goto err_out;
} /* translate from virtual address to file offset */
usdt_rel_ip = usdt_abs_ip - seg->start + seg->offset;
if (ehdr.e_type == ET_DYN && !man->has_bpf_cookie) { /* If we don't have BPF cookie support but need to *attachtoasharedlibrary,we'llneedtoknowand *recordabsoluteaddressesofattachpointsdueto *theneedtolookupUSDTspecbyabsoluteIPof *triggereduprobe.Doingthisresolutionisonly *possiblewhenwehaveaspecificPIDoftheprocess *that'susingspecifiedsharedlibrary.BPFcookie *removestheabsoluteaddresslimitationaswedon't *needtodothislookup(wejustuseBPFcookieas *anindexofUSDTspec),sofornewerkernelswith *BPFcookiesupportlibbpfsupportsUSDTattachment *tosharedlibrarieswithnoPIDfilter.
*/ if (pid < 0) {
pr_warn("usdt: attaching to shared libraries without specific PID is not supported on current kernel\n");
err = -ENOTSUP; goto err_out;
}
/* vma_segs are lazily initialized only if necessary */ if (vma_seg_cnt == 0) {
err = parse_vma_segs(pid, path, &vma_segs, &vma_seg_cnt); if (err) {
pr_warn("usdt: failed to get memory segments in PID %d for shared library '%s': %s\n",
pid, path, errstr(err)); goto err_out;
}
}
seg = find_vma_seg(vma_segs, vma_seg_cnt, usdt_rel_ip); if (!seg) {
err = -ESRCH;
pr_warn("usdt: failed to find shared lib memory segment for '%s:%s' in '%s' at relative IP 0x%lx\n",
usdt_provider, usdt_name, path, usdt_rel_ip); goto err_out;
}
pr_debug("usdt: probe for '%s:%s' in %s '%s': addr 0x%lx base 0x%lx (resolved abs_ip 0x%lx rel_ip 0x%lx) args '%s' in segment [0x%lx, 0x%lx) at offset 0x%lx\n",
usdt_provider, usdt_name, ehdr.e_type == ET_EXEC ? "exec" : "lib ", path,
note.loc_addr, note.base_addr, usdt_abs_ip, usdt_rel_ip, note.args,
seg ? seg->start : 0, seg ? seg->end : 0, seg ? seg->offset : 0);
/* Adjust semaphore address to be a file offset */ if (note.sema_addr) { if (!man->has_sema_refcnt) {
pr_warn("usdt: kernel doesn't support USDT semaphore refcounting for '%s:%s' in '%s'\n",
usdt_provider, usdt_name, path);
err = -ENOTSUP; goto err_out;
}
seg = find_elf_seg(segs, seg_cnt, note.sema_addr); if (!seg) {
err = -ESRCH;
pr_warn("usdt: failed to find ELF loadable segment with semaphore of '%s:%s' in '%s' at 0x%lx\n",
usdt_provider, usdt_name, path, note.sema_addr); goto err_out;
} if (seg->is_exec) {
err = -ESRCH;
pr_warn("usdt: matched ELF binary '%s' segment [0x%lx, 0x%lx] for semaphore of '%s:%s' at 0x%lx is executable\n",
path, seg->start, seg->end, usdt_provider, usdt_name,
note.sema_addr); goto err_out;
}
/* When having multi_link, uprobe_cnt is 0 */ for (i = 0; i < usdt_link->uprobe_cnt; i++) { /* detach underlying uprobe link */
bpf_link__destroy(usdt_link->uprobes[i].link); /* there is no need to update specs map because it will be *unconditionallyoverwrittenonsubsequentUSDTattaches, *butifBPFcookiesarenotusedweneedtoremoveentry *fromip_to_spec_idmap,otherwisewe'llrunintofalse *conflictingIPerrors
*/ if (!man->has_bpf_cookie) { /* not much we can do about errors here */
(void)bpf_map_delete_elem(bpf_map__fd(man->ip_to_spec_id_map),
&usdt_link->uprobes[i].abs_ip);
}
}
/* try to return the list of previously used spec IDs to usdt_manager *forfuturereuseforsubsequentUSDTattaches
*/ if (!man->free_spec_ids) { /* if there were no free spec IDs yet, just transfer our IDs */
man->free_spec_ids = usdt_link->spec_ids;
man->free_spec_cnt = usdt_link->spec_cnt;
usdt_link->spec_ids = NULL;
} else { /* otherwise concat IDs */
size_t new_cnt = man->free_spec_cnt + usdt_link->spec_cnt; int *new_free_ids;
staticint allocate_spec_id(struct usdt_manager *man, struct hashmap *specs_hash, struct bpf_link_usdt *link, struct usdt_target *target, int *spec_id, bool *is_new)
{ long tmp; void *new_ids; int err;
/* check if we already allocated spec ID for this spec string */ if (hashmap__find(specs_hash, target->spec_str, &tmp)) {
*spec_id = tmp;
*is_new = false; return0;
}
/* otherwise it's a new ID that needs to be set up in specs map and *returnedbacktousdt_managerwhenUSDTlinkisdetached
*/
new_ids = libbpf_reallocarray(link->spec_ids, link->spec_cnt + 1, sizeof(*link->spec_ids)); if (!new_ids) return -ENOMEM;
link->spec_ids = new_ids;
/* get next free spec ID, giving preference to free list, if not empty */ if (man->free_spec_cnt) {
*spec_id = man->free_spec_ids[man->free_spec_cnt - 1];
/* cache spec ID for current spec string for future lookups */
err = hashmap__add(specs_hash, target->spec_str, *spec_id); if (err) return err;
man->free_spec_cnt--;
} else { /* don't allocate spec ID bigger than what fits in specs map */ if (man->next_free_spec_id >= bpf_map__max_entries(man->specs_map)) return -E2BIG;
*spec_id = man->next_free_spec_id;
/* cache spec ID for current spec string for future lookups */
err = hashmap__add(specs_hash, target->spec_str, *spec_id); if (err) return err;
man->next_free_spec_id++;
}
/* remember new spec ID in the link for later return back to free list on detach */
link->spec_ids[link->spec_cnt] = *spec_id;
link->spec_cnt++;
*is_new = true; return0;
}
for (i = 0; i < target_cnt; i++) { struct usdt_target *target = &targets[i]; struct bpf_link *uprobe_link; bool is_new; int spec_id;
/* Spec ID can be either reused or newly allocated. If it is *newlyallocated,we'llneedtofilloutspecmap,otherwise *entirespecshouldbevalidandcanbejustusedbyanew *uprobe.WereusespecwhenUSDTargspecisidentical.We *alsoneversharespecsbetweentwodifferentUSDT *attachments("links"),soallthereusedspecsalready *shareUSDTcookievalueimplicitly.
*/
err = allocate_spec_id(man, specs_hash, link, target, &spec_id, &is_new); if (err) goto err_out;
if (is_new && bpf_map_update_elem(spec_map_fd, &spec_id, &target->spec, BPF_ANY)) {
err = -errno;
pr_warn("usdt: failed to set USDT spec #%d for '%s:%s' in '%s': %s\n",
spec_id, usdt_provider, usdt_name, path, errstr(err)); goto err_out;
} if (!man->has_bpf_cookie &&
bpf_map_update_elem(ip_map_fd, &target->abs_ip, &spec_id, BPF_NOEXIST)) {
err = -errno; if (err == -EEXIST) {
pr_warn("usdt: IP collision detected for spec #%d for '%s:%s' in '%s'\n",
spec_id, usdt_provider, usdt_name, path);
} else {
pr_warn("usdt: failed to map IP 0x%lx to spec #%d for '%s:%s' in '%s': %s\n",
target->abs_ip, spec_id, usdt_provider, usdt_name,
path, errstr(err));
} goto err_out;
}
if (man->has_uprobe_multi) {
offsets[i] = target->rel_ip;
ref_ctr_offsets[i] = target->sema_off;
cookies[i] = spec_id;
} else {
opts.ref_ctr_offset = target->sema_off;
opts.bpf_cookie = man->has_bpf_cookie ? spec_id : 0;
uprobe_link = bpf_program__attach_uprobe_opts(prog, pid, path,
target->rel_ip, &opts);
err = libbpf_get_error(uprobe_link); if (err) {
pr_warn("usdt: failed to attach uprobe #%d for '%s:%s' in '%s': %s\n",
i, usdt_provider, usdt_name, path, errstr(err)); goto err_out;
}
/* sanity check USDT note name and type first */ if (strncmp(data + name_off, USDT_NOTE_NAME, nhdr->n_namesz) != 0) return -EINVAL; if (nhdr->n_type != USDT_NOTE_TYPE) return -EINVAL;
/* sanity check USDT note contents ("description" in ELF terminology) */
len = nhdr->n_descsz;
data = data + desc_off;
/* +3 is the very minimum required to store three empty strings */ if (len < sizeof(addrs) + 3) return -EINVAL;
/* get location, base, and semaphore addrs */
memcpy(&addrs, data, sizeof(addrs));
name = (constchar *)memchr(provider, '\0', data + len - provider); if (!name) /* non-zero-terminated provider */ return -EINVAL;
name++; if (name >= data + len || *name == '\0') /* missing or empty name */ return -EINVAL;
args = memchr(name, '\0', data + len - name); if (!args) /* non-zero-terminated name */ return -EINVAL;
++args; if (args >= data + len) /* missing arguments spec */ return -EINVAL;
s = note->args; while (s[0]) { if (spec->arg_cnt >= USDT_MAX_ARG_CNT) {
pr_warn("usdt: too many USDT arguments (> %d) for '%s:%s' with args spec '%s'\n",
USDT_MAX_ARG_CNT, note->provider, note->name, note->args); return -E2BIG;
}
arg = &spec->args[spec->arg_cnt];
len = parse_usdt_arg(s, spec->arg_cnt, arg, &arg_sz); if (len < 0) return len;
staticint parse_usdt_arg(constchar *arg_str, int arg_num, struct usdt_arg_spec *arg, int*arg_sz)
{
pr_warn("usdt: libbpf doesn't support USDTs on current architecture\n"); return -ENOTSUP;
}
#endif
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.67Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-30)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.