| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include <cstdlib> |
| #include <string> |
| #include <vector> |
| |
| extern "C" { |
| #define __STDC_LIMIT_MACROS |
| #define __STDC_CONSTANT_MACROS |
| #define __STDC_FORMAT_MACROS |
| #include <inttypes.h> |
| #include <stdint.h> |
| #include <err.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include <gelf.h> |
| #include <libelf.h> |
| } |
| |
| #include <BPatch.h> |
| #include <BPatch_function.h> |
| #include <BPatch_object.h> |
| #include <BPatch_point.h> |
| |
| #include "dynutil.h" |
| #include "../util.h" |
| |
| |
| using namespace std; |
| |
| struct sdt_point { |
| string provider; |
| string name; |
| vector<pair<int, string> > args; |
| GElf_Addr pc_offset; |
| GElf_Addr sem_offset; |
| }; |
| |
| |
| static vector<pair<int, string> > |
| split_sdt_args(const string& args) |
| { |
| vector<pair<int, string> > 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<sdt_point> |
| 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<sdt_point> 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); |
| } |
| } |
| |
| |
| |
| 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]; |
| } |
| |
| |
| 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<BPatch_point*> 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<BPatch_register> 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<BPatch_snippet *> 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<BPatch_function *> 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<sdt_point>& 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<BPatch_object *> 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_point> 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; |
| } |
| |
| |