2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <stdlib.h>
2N/A#include <assert.h>
2N/A#include <errno.h>
2N/A#include <string.h>
2N/A#include <libgen.h>
2N/A
2N/A#include <dt_impl.h>
2N/A#include <dt_pid.h>
2N/A
2N/A#define OP(x) ((x) >> 30)
2N/A#define OP2(x) (((x) >> 22) & 0x07)
2N/A#define COND(x) (((x) >> 25) & 0x0f)
2N/A#define A(x) (((x) >> 29) & 0x01)
2N/A
2N/A#define OP_BRANCH 0
2N/A
2N/A#define OP2_BPcc 0x1
2N/A#define OP2_Bicc 0x2
2N/A#define OP2_BPr 0x3
2N/A#define OP2_FBPfcc 0x5
2N/A#define OP2_FBfcc 0x6
2N/A
2N/A/*ARGSUSED*/
2N/Aint
2N/Adt_pid_create_entry_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
2N/A fasttrap_probe_spec_t *ftp, const GElf_Sym *symp)
2N/A{
2N/A ftp->ftps_type = DTFTP_ENTRY;
2N/A ftp->ftps_pc = (uintptr_t)symp->st_value;
2N/A ftp->ftps_size = (size_t)symp->st_size;
2N/A ftp->ftps_noffs = 1;
2N/A ftp->ftps_offs[0] = 0;
2N/A
2N/A if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
2N/A dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
2N/A strerror(errno));
2N/A return (dt_set_errno(dtp, errno));
2N/A }
2N/A
2N/A return (1);
2N/A}
2N/A
2N/Aint
2N/Adt_pid_create_return_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
2N/A fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret)
2N/A{
2N/A
2N/A uint32_t *text;
2N/A int i;
2N/A int srdepth = 0;
2N/A
2N/A if ((text = malloc(symp->st_size + 4)) == NULL) {
2N/A dt_dprintf("mr sparkle: malloc() failed\n");
2N/A return (DT_PROC_ERR);
2N/A }
2N/A
2N/A if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) {
2N/A dt_dprintf("mr sparkle: Pread() failed\n");
2N/A free(text);
2N/A return (DT_PROC_ERR);
2N/A }
2N/A
2N/A /*
2N/A * Leave a dummy instruction in the last slot to simplify edge
2N/A * conditions.
2N/A */
2N/A text[symp->st_size / 4] = 0;
2N/A
2N/A ftp->ftps_type = DTFTP_RETURN;
2N/A ftp->ftps_pc = symp->st_value;
2N/A ftp->ftps_size = symp->st_size;
2N/A ftp->ftps_noffs = 0;
2N/A
2N/A for (i = 0; i < symp->st_size / 4; i++) {
2N/A /*
2N/A * If we encounter an existing tracepoint, query the
2N/A * kernel to find out the instruction that was
2N/A * replaced at this spot.
2N/A */
2N/A while (text[i] == FASTTRAP_INSTR) {
2N/A fasttrap_instr_query_t instr;
2N/A
2N/A instr.ftiq_pid = Pstatus(P)->pr_pid;
2N/A instr.ftiq_pc = symp->st_value + i * 4;
2N/A
2N/A if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_GETINSTR,
2N/A &instr) != 0) {
2N/A
2N/A if (errno == ESRCH || errno == ENOENT) {
2N/A if (Pread(P, &text[i], 4,
2N/A instr.ftiq_pc) != 4) {
2N/A dt_dprintf("mr sparkle: "
2N/A "Pread() failed\n");
2N/A free(text);
2N/A return (DT_PROC_ERR);
2N/A }
2N/A continue;
2N/A }
2N/A
2N/A free(text);
2N/A dt_dprintf("mr sparkle: getinstr query "
2N/A "failed: %s\n", strerror(errno));
2N/A return (DT_PROC_ERR);
2N/A }
2N/A
2N/A text[i] = instr.ftiq_instr;
2N/A break;
2N/A }
2N/A
2N/A /* save */
2N/A if ((text[i] & 0xc1f80000) == 0x81e00000) {
2N/A srdepth++;
2N/A continue;
2N/A }
2N/A
2N/A /* restore */
2N/A if ((text[i] & 0xc1f80000) == 0x81e80000) {
2N/A srdepth--;
2N/A continue;
2N/A }
2N/A
2N/A if (srdepth > 0) {
2N/A /* ret */
2N/A if (text[i] == 0x81c7e008)
2N/A goto is_ret;
2N/A
2N/A /* return */
2N/A if (text[i] == 0x81cfe008)
2N/A goto is_ret;
2N/A
2N/A /* call or jmpl w/ restore in the slot */
2N/A if (((text[i] & 0xc0000000) == 0x40000000 ||
2N/A (text[i] & 0xc1f80000) == 0x81c00000) &&
2N/A (text[i + 1] & 0xc1f80000) == 0x81e80000)
2N/A goto is_ret;
2N/A
2N/A /* call to one of the stret routines */
2N/A if ((text[i] & 0xc0000000) == 0x40000000) {
2N/A int32_t disp = text[i] << 2;
2N/A uint64_t dest = ftp->ftps_pc + i * 4 + disp;
2N/A
2N/A dt_dprintf("dest = %llx\n", (u_longlong_t)dest);
2N/A
2N/A if (dest == stret[0] || dest == stret[1] ||
2N/A dest == stret[2] || dest == stret[3])
2N/A goto is_ret;
2N/A }
2N/A } else {
2N/A /* external call */
2N/A if ((text[i] & 0xc0000000) == 0x40000000) {
2N/A int32_t dst = text[i] << 2;
2N/A
2N/A dst += i * 4;
2N/A
2N/A if ((uintptr_t)dst >= (uintptr_t)symp->st_size)
2N/A goto is_ret;
2N/A }
2N/A
2N/A /* jmpl into %g0 -- this includes the retl pseudo op */
2N/A if ((text[i] & 0xfff80000) == 0x81c00000)
2N/A goto is_ret;
2N/A
2N/A /* external branch -- possible return site */
2N/A if (OP(text[i]) == OP_BRANCH) {
2N/A int32_t dst;
2N/A int baa;
2N/A
2N/A switch (OP2(text[i])) {
2N/A case OP2_BPcc:
2N/A dst = text[i] & 0x7ffff;
2N/A dst <<= 13;
2N/A dst >>= 11;
2N/A
2N/A baa = COND(text[i]) == 8 && A(text[i]);
2N/A break;
2N/A case OP2_Bicc:
2N/A dst = text[i] & 0x3fffff;
2N/A dst <<= 10;
2N/A dst >>= 8;
2N/A
2N/A baa = COND(text[i]) == 8 && A(text[i]);
2N/A break;
2N/A case OP2_BPr:
2N/A dst = (((text[i]) >> 6) & 0xc000) |
2N/A ((text[i]) & 0x3fff);
2N/A dst <<= 16;
2N/A dst >>= 14;
2N/A
2N/A baa = 0;
2N/A break;
2N/A case OP2_FBPfcc:
2N/A dst = text[i] & 0x7ffff;
2N/A dst <<= 13;
2N/A dst >>= 11;
2N/A
2N/A baa = COND(text[i]) == 8 && A(text[i]);
2N/A break;
2N/A case OP2_FBfcc:
2N/A dst = text[i] & 0x3fffff;
2N/A dst <<= 10;
2N/A dst >>= 8;
2N/A
2N/A baa = COND(text[i]) == 8 && A(text[i]);
2N/A break;
2N/A default:
2N/A continue;
2N/A }
2N/A
2N/A dst += i * 4;
2N/A
2N/A /*
2N/A * Interpret branches outside of the function's
2N/A * bounds as potential return sites. If the
2N/A * branch is a ba,a don't skip the instruction
2N/A * in the delay slot.
2N/A */
2N/A if ((uintptr_t)dst >=
2N/A (uintptr_t)symp->st_size) {
2N/A if (baa)
2N/A goto is_ret_baa;
2N/A else
2N/A goto is_ret;
2N/A }
2N/A }
2N/A }
2N/A
2N/A continue;
2N/Ais_ret:
2N/A i++;
2N/Ais_ret_baa:
2N/A dt_dprintf("return at offset %x\n", i * 4);
2N/A ftp->ftps_offs[ftp->ftps_noffs++] = i * 4;
2N/A }
2N/A
2N/A free(text);
2N/A if (ftp->ftps_noffs > 0) {
2N/A if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
2N/A dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
2N/A strerror(errno));
2N/A return (dt_set_errno(dtp, errno));
2N/A }
2N/A }
2N/A
2N/A
2N/A return (ftp->ftps_noffs);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Aint
2N/Adt_pid_create_offset_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
2N/A fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, ulong_t off)
2N/A{
2N/A if (off & 0x3)
2N/A return (DT_PROC_ALIGN);
2N/A
2N/A ftp->ftps_type = DTFTP_OFFSETS;
2N/A ftp->ftps_pc = (uintptr_t)symp->st_value;
2N/A ftp->ftps_size = (size_t)symp->st_size;
2N/A ftp->ftps_noffs = 1;
2N/A ftp->ftps_offs[0] = off;
2N/A
2N/A if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
2N/A dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
2N/A strerror(errno));
2N/A return (dt_set_errno(dtp, errno));
2N/A }
2N/A
2N/A return (1);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Aint
2N/Adt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp,
2N/A fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, const char *pattern)
2N/A{
2N/A ulong_t i;
2N/A
2N/A ftp->ftps_type = DTFTP_OFFSETS;
2N/A ftp->ftps_pc = (uintptr_t)symp->st_value;
2N/A ftp->ftps_size = (size_t)symp->st_size;
2N/A ftp->ftps_noffs = 0;
2N/A
2N/A /*
2N/A * If we're matching against everything, just iterate through each
2N/A * instruction in the function, otherwise look for matching offset
2N/A * names by constructing the string and comparing it against the
2N/A * pattern.
2N/A */
2N/A if (strcmp("*", pattern) == 0) {
2N/A for (i = 0; i < symp->st_size; i += 4) {
2N/A ftp->ftps_offs[ftp->ftps_noffs++] = i;
2N/A }
2N/A } else {
2N/A char name[sizeof (i) * 2 + 1];
2N/A
2N/A for (i = 0; i < symp->st_size; i += 4) {
2N/A (void) sprintf(name, "%lx", i);
2N/A if (gmatch(name, pattern))
2N/A ftp->ftps_offs[ftp->ftps_noffs++] = i;
2N/A }
2N/A }
2N/A
2N/A if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
2N/A dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
2N/A strerror(errno));
2N/A return (dt_set_errno(dtp, errno));
2N/A }
2N/A
2N/A return (ftp->ftps_noffs);
2N/A}