// dyninst example program // Copyright (C) 2012 Red Hat Inc. // // This file is part of systemtap, and is free software. You can // redistribute it and/or modify it under the terms of the GNU General // Public License (GPL); either version 2, or (at your option) any // later version. #include #include #include extern "C" { #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include } #include #include #include #include #include "dynutil.h" #include "../util.h" using namespace std; struct sdt_point { string provider; string name; vector > args; GElf_Addr pc_offset; GElf_Addr sem_offset; }; static vector > split_sdt_args(const string& args) { vector > vargs; size_t size_idx = 0; size_t at_idx = args.find('@'); while (at_idx != string::npos) { size_t size_idx2 = string::npos; size_t at_idx2 = args.find('@', at_idx + 1); if (at_idx2 != string::npos) { size_idx2 = args.rfind(' ', at_idx2); if (size_idx2 == string::npos || size_idx2 <= size_idx) size_idx2 = at_idx2 = string::npos; else ++size_idx2; } string size_str = args.substr(size_idx, at_idx - size_idx); int size = atoi(size_str.c_str()); string arg = args.substr(at_idx + 1, size_idx2 - at_idx - 2); vargs.push_back(make_pair(size, arg)); size_idx = size_idx2; at_idx = at_idx2; } return vargs; } static vector find_sdt(const string& file) { int fd = -1; size_t shstrndx; GElf_Addr sdt_base_addr = 0; GElf_Off sdt_base_offset = 0; GElf_Addr sdt_probes_addr = 0; GElf_Off sdt_probes_offset = 0; GElf_Off sdt_probes_virt_offset = 0; Elf* elf = NULL; Elf_Scn* scn = NULL; vector points; fd = open(file.c_str(), O_RDONLY); if (fd < 0) { warn("can't open %s", file.c_str()); goto ret; } elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); if (!elf) { warn("can't read elf %s", file.c_str()); goto ret_fd; } elf_getshdrstrndx (elf, &shstrndx); scn = NULL; while ((scn = elf_nextscn(elf, scn))) { GElf_Shdr shdr; if (gelf_getshdr (scn, &shdr) == NULL) continue; if (shdr.sh_type != SHT_PROGBITS) continue; if (!(shdr.sh_flags & SHF_ALLOC)) continue; const char* sh_name = elf_strptr(elf, shstrndx, shdr.sh_name); if (sh_name && !strcmp(".stapsdt.base", sh_name)) { sdt_base_addr = shdr.sh_addr; sdt_base_offset = shdr.sh_offset; warnx("SDT base addr:%#" PRIx64 " offset:%#" PRIx64 "", sdt_base_addr, sdt_base_offset); } if (sh_name && !strcmp(".probes", sh_name)) { sdt_probes_addr = shdr.sh_addr; sdt_probes_offset = shdr.sh_offset; warnx("SDT probes addr:%#" PRIx64 " offset:%#" PRIx64 "", sdt_probes_addr, sdt_probes_offset); } } // Data is usually loaded in a separate segment from text, with padding // between, so this computes the extra padding to subtract from semaphores. if (sdt_probes_offset) sdt_probes_virt_offset = (sdt_probes_addr - sdt_probes_offset) - (sdt_base_addr - sdt_base_offset); scn = NULL; while ((scn = elf_nextscn(elf, scn))) { GElf_Shdr shdr; if (gelf_getshdr (scn, &shdr) == NULL) continue; if (shdr.sh_type != SHT_NOTE) continue; if (shdr.sh_flags & SHF_ALLOC) continue; Elf_Data *data = elf_getdata (scn, NULL); size_t next; GElf_Nhdr nhdr; size_t name_off; size_t desc_off; for (size_t offset = 0; (next = gelf_getnote (data, offset, &nhdr, &name_off, &desc_off)) > 0; offset = next) { char* cdata = ((char*)data->d_buf); if (strcmp(cdata + name_off, "stapsdt") || nhdr.n_type != 3) continue; union { Elf64_Addr a64[3]; Elf32_Addr a32[3]; } buf; Elf_Data dst = { &buf, ELF_T_ADDR, EV_CURRENT, gelf_fsize (elf, ELF_T_ADDR, 3, EV_CURRENT), 0, 0 }; assert (dst.d_size <= sizeof buf); if (nhdr.n_descsz < dst.d_size + 3) continue; Elf_Data src = { cdata + desc_off, ELF_T_ADDR, EV_CURRENT, dst.d_size, 0, 0 }; if (gelf_xlatetom (elf, &dst, &src, elf_getident (elf, NULL)[EI_DATA]) == NULL) warnx ("gelf_xlatetom: %s", elf_errmsg (-1)); sdt_point p; const char* provider = cdata + desc_off + dst.d_size; p.provider = provider; const char* name = provider + p.provider.size() + 1; p.name = name; const char* args = name + p.name.size() + 1; if (args < cdata + desc_off + nhdr.n_descsz) p.args = split_sdt_args(args); GElf_Addr base_ref; if (gelf_getclass (elf) == ELFCLASS32) { p.pc_offset = buf.a32[0]; base_ref = buf.a32[1]; p.sem_offset = buf.a32[2]; } else { p.pc_offset = buf.a64[0]; base_ref = buf.a64[1]; p.sem_offset = buf.a64[2]; } // Convert to its relative position in the file p.pc_offset += sdt_base_offset - base_ref; if (p.sem_offset) p.sem_offset += sdt_base_offset - base_ref - sdt_probes_virt_offset; stringstream joined_args; for (size_t i = 0; i < p.args.size(); ++i) { if (i > 0) joined_args << ", "; joined_args << p.args[i].first << "@\"" << p.args[i].second << "\""; } warnx("SDT offset:%#" PRIx64 " semaphore:%#" PRIx64 " %s:%s(%s)", p.pc_offset, p.sem_offset, p.provider.c_str(), p.name.c_str(), joined_args.str().c_str()); points.push_back(p); } } elf_end(elf); ret_fd: close(fd); ret: return points; } static void instrument_sdt(BPatch_process* process, BPatch_object* object, sdt_point& p) { BPatch_image* image = process->getImage(); Dyninst::Address address = object->fileOffsetToAddr(p.pc_offset); if (address == BPatch_object::E_OUT_OF_BOUNDS) { warnx("couldn't convert %s:%s at %#" PRIx64 " to an address", p.provider.c_str(), p.name.c_str(), p.pc_offset); return; } vector points; object->findPoints(address, points); if (points.empty()) { warnx("couldn't find %s:%s at %#" PRIx64 " -> %#lx", p.provider.c_str(), p.name.c_str(), p.pc_offset, address); return; } ostringstream format; format << "SDT %s:%s"; BPatch_Vector regs; process->getRegisters(regs); for (size_t i = 0; i < regs.size(); ++i) if (regs[i].name()[2] == 'x') format << " " << regs[i].name() << ":%#lx"; format << "\n"; vector printfArgs; printfArgs.push_back(new BPatch_constExpr(format.str().c_str())); printfArgs.push_back(new BPatch_constExpr(p.provider.c_str())); printfArgs.push_back(new BPatch_constExpr(p.name.c_str())); for (size_t i = 0; i < regs.size(); ++i) if (regs[i].name()[2] == 'x') printfArgs.push_back(new BPatch_registerExpr(regs[i])); vector printfFuncs; image->findFunction("printf", printfFuncs); BPatch_funcCallExpr printfCall(*(printfFuncs[0]), printfArgs); warnx("inserting %s:%s at %#" PRIx64 " -> %#lx [%zu]", p.provider.c_str(), p.name.c_str(), p.pc_offset, address, points.size()); process->insertSnippet(printfCall, points); if (p.sem_offset) { Dyninst::Address sem_address = object->fileOffsetToAddr(p.sem_offset); if (sem_address == BPatch_object::E_OUT_OF_BOUNDS) warnx("couldn't convert %s:%s semaphore %#" PRIx64 " to an address", p.provider.c_str(), p.name.c_str(), p.sem_offset); else { warnx("incrementing semaphore for %s:%s at %#" PRIx64 " -> %#lx", p.provider.c_str(), p.name.c_str(), p.sem_offset, sem_address); BPatch_type *sem_type = image->findType("unsigned short"); BPatch_variableExpr *semaphore = process->createVariable(sem_address, sem_type); BPatch_arithExpr addOne(BPatch_assign, *semaphore, BPatch_arithExpr(BPatch_plus, *semaphore, BPatch_constExpr(1))); process->oneTimeCode(addOne); } } } static void instrument_sdt_points(BPatch_process* process, BPatch_object* object, vector& points) { process->beginInsertionSet(); for (size_t i = 0; i < points.size(); ++i) instrument_sdt(process, object, points[i]); process->finalizeInsertionSet(false); } int main(int argc, const char* argv[]) { if (argc < 2) errx(1, "usage: %s PROGRAM [ARGS..]", argv[0]); warnx("got args:"); for (int i = 1; i < argc; ++i) warnx(" [%i] %s", i - 1, argv[i]); if (!check_dyninst_rt()) return 1; if (!check_dyninst_sebools()) return 1; BPatch patch; warnx("creating the process"); string fullpath = find_executable(argv[1]); BPatch_process *app = patch.processCreate(fullpath.c_str(), &argv[1]); BPatch_image *image = app->getImage(); warnx("scanning the process for sdt with BPatch_object"); vector objects; image->getObjects(objects); for (size_t i = 0; i < objects.size(); ++i) { BPatch_object* obj = objects[i]; string file = obj->pathName(); warnx(" found mapped object %s", file.c_str()); vector sdt_points = find_sdt(file); if (sdt_points.empty()) continue; warnx("inserting sdt instrumentation in %s", file.c_str()); instrument_sdt_points(app, obj, sdt_points); } warnx("**** running the process"); app->continueExecution(); while (!app->isTerminated()) { patch.waitForStatusChange(); } warnx("done!"); return check_dyninst_exit(app) ? EXIT_SUCCESS : EXIT_FAILURE; } /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */