1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2011 by Delphix. All rights reserved.
  25  */
  26 
  27 #include <unistd.h>
  28 #include <strings.h>
  29 #include <stdlib.h>
  30 #include <errno.h>
  31 #include <assert.h>
  32 #include <ctype.h>
  33 #include <alloca.h>
  34 
  35 #include <dt_impl.h>
  36 #include <dt_program.h>
  37 #include <dt_printf.h>
  38 #include <dt_provider.h>
  39 
  40 dtrace_prog_t *
  41 dt_program_create(dtrace_hdl_t *dtp)
  42 {
  43         dtrace_prog_t *pgp = dt_zalloc(dtp, sizeof (dtrace_prog_t));
  44 
  45         if (pgp != NULL) {
  46                 dt_list_append(&dtp->dt_programs, pgp);
  47         } else {
  48                 (void) dt_set_errno(dtp, EDT_NOMEM);
  49                 return (NULL);
  50         }
  51 
  52         /*
  53          * By default, programs start with DOF version 1 so that output files
  54          * containing DOF are backward compatible. If a program requires new
  55          * DOF features, the version is increased as needed.
  56          */
  57         pgp->dp_dofversion = DOF_VERSION_1;
  58 
  59         return (pgp);
  60 }
  61 
  62 void
  63 dt_program_destroy(dtrace_hdl_t *dtp, dtrace_prog_t *pgp)
  64 {
  65         dt_stmt_t *stp, *next;
  66         uint_t i;
  67 
  68         for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
  69                 next = dt_list_next(stp);
  70                 dtrace_stmt_destroy(dtp, stp->ds_desc);
  71                 dt_free(dtp, stp);
  72         }
  73 
  74         for (i = 0; i < pgp->dp_xrefslen; i++)
  75                 dt_free(dtp, pgp->dp_xrefs[i]);
  76 
  77         dt_free(dtp, pgp->dp_xrefs);
  78         dt_list_delete(&dtp->dt_programs, pgp);
  79         dt_free(dtp, pgp);
  80 }
  81 
  82 /*ARGSUSED*/
  83 void
  84 dtrace_program_info(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
  85     dtrace_proginfo_t *pip)
  86 {
  87         dt_stmt_t *stp;
  88         dtrace_actdesc_t *ap;
  89         dtrace_ecbdesc_t *last = NULL;
  90 
  91         if (pip == NULL)
  92                 return;
  93 
  94         bzero(pip, sizeof (dtrace_proginfo_t));
  95 
  96         if (dt_list_next(&pgp->dp_stmts) != NULL) {
  97                 pip->dpi_descattr = _dtrace_maxattr;
  98                 pip->dpi_stmtattr = _dtrace_maxattr;
  99         } else {
 100                 pip->dpi_descattr = _dtrace_defattr;
 101                 pip->dpi_stmtattr = _dtrace_defattr;
 102         }
 103 
 104         for (stp = dt_list_next(&pgp->dp_stmts); stp; stp = dt_list_next(stp)) {
 105                 dtrace_ecbdesc_t *edp = stp->ds_desc->dtsd_ecbdesc;
 106 
 107                 if (edp == last)
 108                         continue;
 109                 last = edp;
 110 
 111                 pip->dpi_descattr =
 112                     dt_attr_min(stp->ds_desc->dtsd_descattr, pip->dpi_descattr);
 113 
 114                 pip->dpi_stmtattr =
 115                     dt_attr_min(stp->ds_desc->dtsd_stmtattr, pip->dpi_stmtattr);
 116 
 117                 /*
 118                  * If there aren't any actions, account for the fact that
 119                  * recording the epid will generate a record.
 120                  */
 121                 if (edp->dted_action == NULL)
 122                         pip->dpi_recgens++;
 123 
 124                 for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
 125                         if (ap->dtad_kind == DTRACEACT_SPECULATE) {
 126                                 pip->dpi_speculations++;
 127                                 continue;
 128                         }
 129 
 130                         if (DTRACEACT_ISAGG(ap->dtad_kind)) {
 131                                 pip->dpi_recgens -= ap->dtad_arg;
 132                                 pip->dpi_aggregates++;
 133                                 continue;
 134                         }
 135 
 136                         if (DTRACEACT_ISDESTRUCTIVE(ap->dtad_kind))
 137                                 continue;
 138 
 139                         if (ap->dtad_kind == DTRACEACT_DIFEXPR &&
 140                             ap->dtad_difo->dtdo_rtype.dtdt_kind ==
 141                             DIF_TYPE_CTF &&
 142                             ap->dtad_difo->dtdo_rtype.dtdt_size == 0)
 143                                 continue;
 144 
 145                         pip->dpi_recgens++;
 146                 }
 147         }
 148 }
 149 
 150 int
 151 dtrace_program_exec(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
 152     dtrace_proginfo_t *pip)
 153 {
 154         void *dof;
 155         int n, err;
 156 
 157         dtrace_program_info(dtp, pgp, pip);
 158 
 159         if ((dof = dtrace_dof_create(dtp, pgp, DTRACE_D_STRIP)) == NULL)
 160                 return (-1);
 161 
 162         n = dt_ioctl(dtp, DTRACEIOC_ENABLE, dof);
 163         dtrace_dof_destroy(dtp, dof);
 164 
 165         if (n == -1) {
 166                 switch (errno) {
 167                 case EINVAL:
 168                         err = EDT_DIFINVAL;
 169                         break;
 170                 case EFAULT:
 171                         err = EDT_DIFFAULT;
 172                         break;
 173                 case E2BIG:
 174                         err = EDT_DIFSIZE;
 175                         break;
 176                 case EBUSY:
 177                         err = EDT_ENABLING_ERR;
 178                         break;
 179                 default:
 180                         err = errno;
 181                 }
 182 
 183                 return (dt_set_errno(dtp, err));
 184         }
 185 
 186         if (pip != NULL)
 187                 pip->dpi_matches += n;
 188 
 189         return (0);
 190 }
 191 
 192 static void
 193 dt_ecbdesc_hold(dtrace_ecbdesc_t *edp)
 194 {
 195         edp->dted_refcnt++;
 196 }
 197 
 198 void
 199 dt_ecbdesc_release(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
 200 {
 201         if (--edp->dted_refcnt > 0)
 202                 return;
 203 
 204         dt_difo_free(dtp, edp->dted_pred.dtpdd_difo);
 205         assert(edp->dted_action == NULL);
 206         dt_free(dtp, edp);
 207 }
 208 
 209 dtrace_ecbdesc_t *
 210 dt_ecbdesc_create(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
 211 {
 212         dtrace_ecbdesc_t *edp;
 213 
 214         if ((edp = dt_zalloc(dtp, sizeof (dtrace_ecbdesc_t))) == NULL) {
 215                 (void) dt_set_errno(dtp, EDT_NOMEM);
 216                 return (NULL);
 217         }
 218 
 219         edp->dted_probe = *pdp;
 220         dt_ecbdesc_hold(edp);
 221         return (edp);
 222 }
 223 
 224 dtrace_stmtdesc_t *
 225 dtrace_stmt_create(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
 226 {
 227         dtrace_stmtdesc_t *sdp;
 228 
 229         if ((sdp = dt_zalloc(dtp, sizeof (dtrace_stmtdesc_t))) == NULL)
 230                 return (NULL);
 231 
 232         dt_ecbdesc_hold(edp);
 233         sdp->dtsd_ecbdesc = edp;
 234         sdp->dtsd_descattr = _dtrace_defattr;
 235         sdp->dtsd_stmtattr = _dtrace_defattr;
 236 
 237         return (sdp);
 238 }
 239 
 240 dtrace_actdesc_t *
 241 dtrace_stmt_action(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
 242 {
 243         dtrace_actdesc_t *new;
 244         dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
 245 
 246         if ((new = dt_alloc(dtp, sizeof (dtrace_actdesc_t))) == NULL)
 247                 return (NULL);
 248 
 249         if (sdp->dtsd_action_last != NULL) {
 250                 assert(sdp->dtsd_action != NULL);
 251                 assert(sdp->dtsd_action_last->dtad_next == NULL);
 252                 sdp->dtsd_action_last->dtad_next = new;
 253         } else {
 254                 dtrace_actdesc_t *ap = edp->dted_action;
 255 
 256                 assert(sdp->dtsd_action == NULL);
 257                 sdp->dtsd_action = new;
 258 
 259                 while (ap != NULL && ap->dtad_next != NULL)
 260                         ap = ap->dtad_next;
 261 
 262                 if (ap == NULL)
 263                         edp->dted_action = new;
 264                 else
 265                         ap->dtad_next = new;
 266         }
 267 
 268         sdp->dtsd_action_last = new;
 269         bzero(new, sizeof (dtrace_actdesc_t));
 270         new->dtad_uarg = (uintptr_t)sdp;
 271 
 272         return (new);
 273 }
 274 
 275 int
 276 dtrace_stmt_add(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *sdp)
 277 {
 278         dt_stmt_t *stp = dt_alloc(dtp, sizeof (dt_stmt_t));
 279 
 280         if (stp == NULL)
 281                 return (-1); /* errno is set for us */
 282 
 283         dt_list_append(&pgp->dp_stmts, stp);
 284         stp->ds_desc = sdp;
 285 
 286         return (0);
 287 }
 288 
 289 int
 290 dtrace_stmt_iter(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
 291     dtrace_stmt_f *func, void *data)
 292 {
 293         dt_stmt_t *stp, *next;
 294         int status = 0;
 295 
 296         for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
 297                 next = dt_list_next(stp);
 298                 if ((status = func(dtp, pgp, stp->ds_desc, data)) != 0)
 299                         break;
 300         }
 301 
 302         return (status);
 303 }
 304 
 305 void
 306 dtrace_stmt_destroy(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
 307 {
 308         dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
 309 
 310         /*
 311          * We need to remove any actions that we have on this ECB, and
 312          * remove our hold on the ECB itself.
 313          */
 314         if (sdp->dtsd_action != NULL) {
 315                 dtrace_actdesc_t *last = sdp->dtsd_action_last;
 316                 dtrace_actdesc_t *ap, *next;
 317 
 318                 assert(last != NULL);
 319 
 320                 for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
 321                         if (ap == sdp->dtsd_action)
 322                                 break;
 323 
 324                         if (ap->dtad_next == sdp->dtsd_action)
 325                                 break;
 326                 }
 327 
 328                 assert(ap != NULL);
 329 
 330                 if (ap == edp->dted_action)
 331                         edp->dted_action = last->dtad_next;
 332                 else
 333                         ap->dtad_next = last->dtad_next;
 334 
 335                 /*
 336                  * We have now removed our action list from its ECB; we can
 337                  * safely destroy the list.
 338                  */
 339                 last->dtad_next = NULL;
 340 
 341                 for (ap = sdp->dtsd_action; ap != NULL; ap = next) {
 342                         assert(ap->dtad_uarg == (uintptr_t)sdp);
 343                         dt_difo_free(dtp, ap->dtad_difo);
 344                         next = ap->dtad_next;
 345                         dt_free(dtp, ap);
 346                 }
 347         }
 348 
 349         if (sdp->dtsd_fmtdata != NULL)
 350                 dt_printf_destroy(sdp->dtsd_fmtdata);
 351         dt_free(dtp, sdp->dtsd_strdata);
 352 
 353         dt_ecbdesc_release(dtp, sdp->dtsd_ecbdesc);
 354         dt_free(dtp, sdp);
 355 }
 356 
 357 typedef struct dt_header_info {
 358         dtrace_hdl_t *dthi_dtp; /* consumer handle */
 359         FILE *dthi_out;         /* output file */
 360         char *dthi_pmname;      /* provider macro name */
 361         char *dthi_pfname;      /* provider function name */
 362         int dthi_empty;         /* should we generate empty macros */
 363 } dt_header_info_t;
 364 
 365 static void
 366 dt_header_fmt_macro(char *buf, const char *str)
 367 {
 368         for (;;) {
 369                 if (islower(*str)) {
 370                         *buf++ = *str++ + 'A' - 'a';
 371                 } else if (*str == '-') {
 372                         *buf++ = '_';
 373                         str++;
 374                 } else if (*str == '.') {
 375                         *buf++ = '_';
 376                         str++;
 377                 } else if ((*buf++ = *str++) == '\0') {
 378                         break;
 379                 }
 380         }
 381 }
 382 
 383 static void
 384 dt_header_fmt_func(char *buf, const char *str)
 385 {
 386         for (;;) {
 387                 if (*str == '-') {
 388                         *buf++ = '_';
 389                         *buf++ = '_';
 390                         str++;
 391                 } else if ((*buf++ = *str++) == '\0') {
 392                         break;
 393                 }
 394         }
 395 }
 396 
 397 /*ARGSUSED*/
 398 static int
 399 dt_header_decl(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
 400 {
 401         dt_header_info_t *infop = data;
 402         dtrace_hdl_t *dtp = infop->dthi_dtp;
 403         dt_probe_t *prp = idp->di_data;
 404         dt_node_t *dnp;
 405         char buf[DT_TYPE_NAMELEN];
 406         char *fname;
 407         const char *p;
 408         int i;
 409 
 410         p = prp->pr_name;
 411         for (i = 0; (p = strchr(p, '-')) != NULL; i++)
 412                 p++;
 413 
 414         fname = alloca(strlen(prp->pr_name) + 1 + i);
 415         dt_header_fmt_func(fname, prp->pr_name);
 416 
 417         if (fprintf(infop->dthi_out, "extern void __dtrace_%s___%s(",
 418             infop->dthi_pfname, fname) < 0)
 419                 return (dt_set_errno(dtp, errno));
 420 
 421         for (dnp = prp->pr_nargs, i = 0; dnp != NULL; dnp = dnp->dn_list, i++) {
 422                 if (fprintf(infop->dthi_out, "%s",
 423                     ctf_type_name(dnp->dn_ctfp, dnp->dn_type,
 424                     buf, sizeof (buf))) < 0)
 425                         return (dt_set_errno(dtp, errno));
 426 
 427                 if (i + 1 != prp->pr_nargc &&
 428                     fprintf(infop->dthi_out, ", ") < 0)
 429                         return (dt_set_errno(dtp, errno));
 430         }
 431 
 432         if (i == 0 && fprintf(infop->dthi_out, "void") < 0)
 433                 return (dt_set_errno(dtp, errno));
 434 
 435         if (fprintf(infop->dthi_out, ");\n") < 0)
 436                 return (dt_set_errno(dtp, errno));
 437 
 438         if (fprintf(infop->dthi_out,
 439             "#ifndef\t__sparc\n"
 440             "extern int __dtraceenabled_%s___%s(void);\n"
 441             "#else\n"
 442             "extern int __dtraceenabled_%s___%s(long);\n"
 443             "#endif\n",
 444             infop->dthi_pfname, fname, infop->dthi_pfname, fname) < 0)
 445                 return (dt_set_errno(dtp, errno));
 446 
 447         return (0);
 448 }
 449 
 450 /*ARGSUSED*/
 451 static int
 452 dt_header_probe(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
 453 {
 454         dt_header_info_t *infop = data;
 455         dtrace_hdl_t *dtp = infop->dthi_dtp;
 456         dt_probe_t *prp = idp->di_data;
 457         char *mname, *fname;
 458         const char *p;
 459         int i;
 460 
 461         p = prp->pr_name;
 462         for (i = 0; (p = strchr(p, '-')) != NULL; i++)
 463                 p++;
 464 
 465         mname = alloca(strlen(prp->pr_name) + 1);
 466         dt_header_fmt_macro(mname, prp->pr_name);
 467 
 468         fname = alloca(strlen(prp->pr_name) + 1 + i);
 469         dt_header_fmt_func(fname, prp->pr_name);
 470 
 471         if (fprintf(infop->dthi_out, "#define\t%s_%s(",
 472             infop->dthi_pmname, mname) < 0)
 473                 return (dt_set_errno(dtp, errno));
 474 
 475         for (i = 0; i < prp->pr_nargc; i++) {
 476                 if (fprintf(infop->dthi_out, "arg%d", i) < 0)
 477                         return (dt_set_errno(dtp, errno));
 478 
 479                 if (i + 1 != prp->pr_nargc &&
 480                     fprintf(infop->dthi_out, ", ") < 0)
 481                         return (dt_set_errno(dtp, errno));
 482         }
 483 
 484         if (!infop->dthi_empty) {
 485                 if (fprintf(infop->dthi_out, ") \\\n\t") < 0)
 486                         return (dt_set_errno(dtp, errno));
 487 
 488                 if (fprintf(infop->dthi_out, "__dtrace_%s___%s(",
 489                     infop->dthi_pfname, fname) < 0)
 490                         return (dt_set_errno(dtp, errno));
 491 
 492                 for (i = 0; i < prp->pr_nargc; i++) {
 493                         if (fprintf(infop->dthi_out, "arg%d", i) < 0)
 494                                 return (dt_set_errno(dtp, errno));
 495 
 496                         if (i + 1 != prp->pr_nargc &&
 497                             fprintf(infop->dthi_out, ", ") < 0)
 498                                 return (dt_set_errno(dtp, errno));
 499                 }
 500         }
 501 
 502         if (fprintf(infop->dthi_out, ")\n") < 0)
 503                 return (dt_set_errno(dtp, errno));
 504 
 505         if (!infop->dthi_empty) {
 506                 if (fprintf(infop->dthi_out,
 507                     "#ifndef\t__sparc\n"
 508                     "#define\t%s_%s_ENABLED() \\\n"
 509                     "\t__dtraceenabled_%s___%s()\n"
 510                     "#else\n"
 511                     "#define\t%s_%s_ENABLED() \\\n"
 512                     "\t__dtraceenabled_%s___%s(0)\n"
 513                     "#endif\n",
 514                     infop->dthi_pmname, mname,
 515                     infop->dthi_pfname, fname,
 516                     infop->dthi_pmname, mname,
 517                     infop->dthi_pfname, fname) < 0)
 518                         return (dt_set_errno(dtp, errno));
 519 
 520         } else {
 521                 if (fprintf(infop->dthi_out, "#define\t%s_%s_ENABLED() (0)\n",
 522                     infop->dthi_pmname, mname) < 0)
 523                         return (dt_set_errno(dtp, errno));
 524         }
 525 
 526         return (0);
 527 }
 528 
 529 static int
 530 dt_header_provider(dtrace_hdl_t *dtp, dt_provider_t *pvp, FILE *out)
 531 {
 532         dt_header_info_t info;
 533         const char *p;
 534         int i;
 535 
 536         if (pvp->pv_flags & DT_PROVIDER_IMPL)
 537                 return (0);
 538 
 539         /*
 540          * Count the instances of the '-' character since we'll need to double
 541          * those up.
 542          */
 543         p = pvp->pv_desc.dtvd_name;
 544         for (i = 0; (p = strchr(p, '-')) != NULL; i++)
 545                 p++;
 546 
 547         info.dthi_dtp = dtp;
 548         info.dthi_out = out;
 549         info.dthi_empty = 0;
 550 
 551         info.dthi_pmname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1);
 552         dt_header_fmt_macro(info.dthi_pmname, pvp->pv_desc.dtvd_name);
 553 
 554         info.dthi_pfname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1 + i);
 555         dt_header_fmt_func(info.dthi_pfname, pvp->pv_desc.dtvd_name);
 556 
 557         if (fprintf(out, "#if _DTRACE_VERSION\n\n") < 0)
 558                 return (dt_set_errno(dtp, errno));
 559 
 560         if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
 561                 return (-1); /* dt_errno is set for us */
 562         if (fprintf(out, "\n\n") < 0)
 563                 return (dt_set_errno(dtp, errno));
 564         if (dt_idhash_iter(pvp->pv_probes, dt_header_decl, &info) != 0)
 565                 return (-1); /* dt_errno is set for us */
 566 
 567         if (fprintf(out, "\n#else\n\n") < 0)
 568                 return (dt_set_errno(dtp, errno));
 569 
 570         info.dthi_empty = 1;
 571 
 572         if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
 573                 return (-1); /* dt_errno is set for us */
 574 
 575         if (fprintf(out, "\n#endif\n\n") < 0)
 576                 return (dt_set_errno(dtp, errno));
 577 
 578         return (0);
 579 }
 580 
 581 int
 582 dtrace_program_header(dtrace_hdl_t *dtp, FILE *out, const char *fname)
 583 {
 584         dt_provider_t *pvp;
 585         char *mfname, *p;
 586 
 587         if (fname != NULL) {
 588                 if ((p = strrchr(fname, '/')) != NULL)
 589                         fname = p + 1;
 590 
 591                 mfname = alloca(strlen(fname) + 1);
 592                 dt_header_fmt_macro(mfname, fname);
 593                 if (fprintf(out, "#ifndef\t_%s\n#define\t_%s\n\n",
 594                     mfname, mfname) < 0)
 595                         return (dt_set_errno(dtp, errno));
 596         }
 597 
 598         if (fprintf(out, "#include <unistd.h>\n\n") < 0)
 599                 return (-1);
 600 
 601         if (fprintf(out, "#ifdef\t__cplusplus\nextern \"C\" {\n#endif\n\n") < 0)
 602                 return (-1);
 603 
 604         for (pvp = dt_list_next(&dtp->dt_provlist);
 605             pvp != NULL; pvp = dt_list_next(pvp)) {
 606                 if (dt_header_provider(dtp, pvp, out) != 0)
 607                         return (-1); /* dt_errno is set for us */
 608         }
 609 
 610         if (fprintf(out, "\n#ifdef\t__cplusplus\n}\n#endif\n") < 0)
 611                 return (dt_set_errno(dtp, errno));
 612 
 613         if (fname != NULL && fprintf(out, "\n#endif\t/* _%s */\n", mfname) < 0)
 614                 return (dt_set_errno(dtp, errno));
 615 
 616         return (0);
 617 }