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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27#pragma ident "@(#)dt_program.c 1.12 08/05/05 SMI" 28 29#include <unistd.h> 30#include <strings.h> 31#include <stdlib.h> 32#include <errno.h> 33#include <assert.h> 34#include <ctype.h> 35#include <alloca.h> 36 37#include <dt_impl.h> 38#include <dt_program.h> 39#include <dt_printf.h> 40#include <dt_provider.h> 41#include <dt_ld.h> 42 43dtrace_prog_t * 44dt_program_create(dtrace_hdl_t *dtp) 45{ 46 dtrace_prog_t *pgp = dt_zalloc(dtp, sizeof (dtrace_prog_t)); 47 48 if (pgp != NULL) 49 dt_list_append(&dtp->dt_programs, pgp); 50 else 51 (void) dt_set_errno(dtp, EDT_NOMEM); 52 53 /* 54 * By default, programs start with DOF version 1 so that output files 55 * containing DOF are backward compatible. If a program requires new 56 * DOF features, the version is increased as needed. 57 */ 58 /* 59 * APPLE NOTE: The earliest DOF version Leopard will support is v3. 60 * This includes relative offsets for probes, and is-enabled probes. 61 */ 62 pgp->dp_dofversion = DOF_VERSION_3; 63 64 return (pgp); 65} 66 67void 68dt_program_destroy(dtrace_hdl_t *dtp, dtrace_prog_t *pgp) 69{ 70 dt_stmt_t *stp, *next; 71 uint_t i; 72 73 for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) { 74 next = dt_list_next(stp); 75 dtrace_stmt_destroy(dtp, stp->ds_desc); 76 dt_free(dtp, stp); 77 } 78 79 for (i = 0; i < pgp->dp_xrefslen; i++) 80 dt_free(dtp, pgp->dp_xrefs[i]); 81 82 dt_free(dtp, pgp->dp_xrefs); 83 dt_list_delete(&dtp->dt_programs, pgp); 84 dt_free(dtp, pgp); 85} 86 87/*ARGSUSED*/ 88void 89dtrace_program_info(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, 90 dtrace_proginfo_t *pip) 91{ 92 dt_stmt_t *stp; 93 dtrace_actdesc_t *ap; 94 dtrace_ecbdesc_t *last = NULL; 95 96 if (pip == NULL) 97 return; 98 99 bzero(pip, sizeof (dtrace_proginfo_t)); 100 101 if (dt_list_next(&pgp->dp_stmts) != NULL) { 102 pip->dpi_descattr = _dtrace_maxattr; 103 pip->dpi_stmtattr = _dtrace_maxattr; 104 } else { 105 pip->dpi_descattr = _dtrace_defattr; 106 pip->dpi_stmtattr = _dtrace_defattr; 107 } 108 109 for (stp = dt_list_next(&pgp->dp_stmts); stp; stp = dt_list_next(stp)) { 110 dtrace_ecbdesc_t *edp = stp->ds_desc->dtsd_ecbdesc; 111 112 if (edp == last) 113 continue; 114 last = edp; 115 116 pip->dpi_descattr = 117 dt_attr_min(stp->ds_desc->dtsd_descattr, pip->dpi_descattr); 118 119 pip->dpi_stmtattr = 120 dt_attr_min(stp->ds_desc->dtsd_stmtattr, pip->dpi_stmtattr); 121 122 /* 123 * If there aren't any actions, account for the fact that 124 * recording the epid will generate a record. 125 */ 126 if (edp->dted_action == NULL) 127 pip->dpi_recgens++; 128 129 for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) { 130 if (ap->dtad_kind == DTRACEACT_SPECULATE) { 131 pip->dpi_speculations++; 132 continue; 133 } 134 135 if (DTRACEACT_ISAGG(ap->dtad_kind)) { 136 pip->dpi_recgens -= ap->dtad_arg; 137 pip->dpi_aggregates++; 138 continue; 139 } 140 141 if (DTRACEACT_ISDESTRUCTIVE(ap->dtad_kind)) 142 continue; 143 144 if (ap->dtad_kind == DTRACEACT_DIFEXPR && 145 ap->dtad_difo->dtdo_rtype.dtdt_kind == 146 DIF_TYPE_CTF && 147 ap->dtad_difo->dtdo_rtype.dtdt_size == 0) 148 continue; 149 150 pip->dpi_recgens++; 151 } 152 } 153} 154 155int 156dtrace_program_exec(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, 157 dtrace_proginfo_t *pip) 158{ 159 void *dof; 160 int n, err; 161 162 dtrace_program_info(dtp, pgp, pip); 163 164 if ((dof = dtrace_dof_create(dtp, pgp, DTRACE_D_STRIP)) == NULL) 165 return (-1); 166 167 n = dt_ioctl(dtp, DTRACEIOC_ENABLE, dof); 168 if (n == -1 && (errno & 0xfffff000)) 169 n = (((unsigned int)errno) >> 12); /* Darwin's ioctls only return -1 or zero. Overload errno to mimic Solaris. */ 170 dtrace_dof_destroy(dtp, dof); 171 172 if (n == -1) { 173 switch (errno) { 174 case EINVAL: 175 err = EDT_DIFINVAL; 176 break; 177 case EFAULT: 178 err = EDT_DIFFAULT; 179 break; 180 case E2BIG: 181 err = EDT_DIFSIZE; 182 break; 183 case EBUSY: 184 err = EDT_ENABLING_ERR; 185 break; 186 default: 187 err = errno; 188 } 189 190 return (dt_set_errno(dtp, err)); 191 } 192 193 if (pip != NULL) 194 pip->dpi_matches += n; 195 196 return (0); 197} 198 199static void 200dt_ecbdesc_hold(dtrace_ecbdesc_t *edp) 201{ 202 edp->dted_refcnt++; 203} 204 205void 206dt_ecbdesc_release(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp) 207{ 208 if (--edp->dted_refcnt > 0) 209 return; 210 211 dt_difo_free(dtp, edp->dted_pred.dtpdd_difo); 212 assert(edp->dted_action == NULL); 213 dt_free(dtp, edp); 214} 215 216dtrace_ecbdesc_t * 217dt_ecbdesc_create(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp) 218{ 219 dtrace_ecbdesc_t *edp; 220 221 if ((edp = dt_zalloc(dtp, sizeof (dtrace_ecbdesc_t))) == NULL) { 222 (void) dt_set_errno(dtp, EDT_NOMEM); 223 return (NULL); 224 } 225 226 edp->dted_probe = *pdp; 227 dt_ecbdesc_hold(edp); 228 return (edp); 229} 230 231dtrace_stmtdesc_t * 232dtrace_stmt_create(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp) 233{ 234 dtrace_stmtdesc_t *sdp; 235 236 if ((sdp = dt_zalloc(dtp, sizeof (dtrace_stmtdesc_t))) == NULL) 237 return (NULL); 238 239 dt_ecbdesc_hold(edp); 240 sdp->dtsd_ecbdesc = edp; 241 sdp->dtsd_descattr = _dtrace_defattr; 242 sdp->dtsd_stmtattr = _dtrace_defattr; 243 244 return (sdp); 245} 246 247dtrace_actdesc_t * 248dtrace_stmt_action(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp) 249{ 250 dtrace_actdesc_t *new; 251 dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc; 252 253 if ((new = dt_alloc(dtp, sizeof (dtrace_actdesc_t))) == NULL) 254 return (NULL); 255 256 if (sdp->dtsd_action_last != NULL) { 257 assert(sdp->dtsd_action != NULL); 258 assert(sdp->dtsd_action_last->dtad_next == NULL); 259 sdp->dtsd_action_last->dtad_next = new; 260 } else { 261 dtrace_actdesc_t *ap = edp->dted_action; 262 263 assert(sdp->dtsd_action == NULL); 264 sdp->dtsd_action = new; 265 266 while (ap != NULL && ap->dtad_next != NULL) 267 ap = ap->dtad_next; 268 269 if (ap == NULL) 270 edp->dted_action = new; 271 else 272 ap->dtad_next = new; 273 } 274 275 sdp->dtsd_action_last = new; 276 bzero(new, sizeof (dtrace_actdesc_t)); 277 new->dtad_uarg = (uintptr_t)sdp; 278 279 return (new); 280} 281 282int 283dtrace_stmt_add(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *sdp) 284{ 285 dt_stmt_t *stp = dt_alloc(dtp, sizeof (dt_stmt_t)); 286 287 if (stp == NULL) 288 return (-1); /* errno is set for us */ 289 290 dt_list_append(&pgp->dp_stmts, stp); 291 stp->ds_desc = sdp; 292 293 return (0); 294} 295 296int 297dtrace_stmt_iter(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, 298 dtrace_stmt_f *func, void *data) 299{ 300 dt_stmt_t *stp, *next; 301 int status = 0; 302 303 for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) { 304 next = dt_list_next(stp); 305 if ((status = func(dtp, pgp, stp->ds_desc, data)) != 0) 306 break; 307 } 308 309 return (status); 310} 311 312void 313dtrace_stmt_destroy(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp) 314{ 315 dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc; 316 317 /* 318 * We need to remove any actions that we have on this ECB, and 319 * remove our hold on the ECB itself. 320 */ 321 if (sdp->dtsd_action != NULL) { 322 dtrace_actdesc_t *last = sdp->dtsd_action_last; 323 dtrace_actdesc_t *ap, *next; 324 325 assert(last != NULL); 326 327 for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) { 328 if (ap == sdp->dtsd_action) 329 break; 330 331 if (ap->dtad_next == sdp->dtsd_action) 332 break; 333 } 334 335 assert(ap != NULL); 336 337 if (ap == edp->dted_action) 338 edp->dted_action = last->dtad_next; 339 else 340 ap->dtad_next = last->dtad_next; 341 342 /* 343 * We have now removed our action list from its ECB; we can 344 * safely destroy the list. 345 */ 346 last->dtad_next = NULL; 347 348 for (ap = sdp->dtsd_action; ap != NULL; ap = next) { 349 assert(ap->dtad_uarg == (uintptr_t)sdp); 350 dt_difo_free(dtp, ap->dtad_difo); 351 next = ap->dtad_next; 352 dt_free(dtp, ap); 353 } 354 } 355 356 if (sdp->dtsd_fmtdata != NULL) 357 dt_printf_destroy(sdp->dtsd_fmtdata); 358 359 dt_ecbdesc_release(dtp, sdp->dtsd_ecbdesc); 360 dt_free(dtp, sdp); 361} 362 363typedef struct dt_header_info { 364 dtrace_hdl_t *dthi_dtp; /* consumer handle */ 365 FILE *dthi_out; /* output file */ 366 char *dthi_pmname; /* provider macro name */ 367 char *dthi_pfname; /* provider function name */ 368 int dthi_empty; /* should we generate empty macros */ 369} dt_header_info_t; 370 371static void 372dt_header_fmt_macro(char *buf, const char *str) 373{ 374 for (;;) { 375 if (islower(*str)) { 376 *buf++ = *str++ + 'A' - 'a'; 377 } else if (*str == '-') { 378 *buf++ = '_'; 379 str++; 380 } else if (*str == '.') { 381 *buf++ = '_'; 382 str++; 383 } else if ((*buf++ = *str++) == '\0') { 384 break; 385 } 386 } 387} 388 389static void 390dt_header_fmt_func(char *buf, const char *str) 391{ 392 for (;;) { 393 if (*str == '-') { 394 *buf++ = '_'; 395 *buf++ = '_'; 396 str++; 397 } else if ((*buf++ = *str++) == '\0') { 398 break; 399 } 400 } 401} 402 403static bool dt_is_single_rank_pointer_type(ctf_file_t *file, ctf_id_t type) { 404 if (ctf_type_kind(file, type) == CTF_K_POINTER) { 405 ctf_id_t referenced_type = ctf_type_reference(file, type); 406 if (ctf_type_kind(file, referenced_type) != CTF_K_POINTER && 407 ctf_type_kind(file, referenced_type) != CTF_K_FUNCTION) { 408 return TRUE; 409 } 410 } 411 412 return FALSE; 413} 414 415/*ARGSUSED*/ 416static int 417dt_header_decl(dt_idhash_t *dhp, dt_ident_t *idp, void *data) 418{ 419 dt_header_info_t *infop = data; 420 dtrace_hdl_t *dtp = infop->dthi_dtp; 421 dt_probe_t *prp = idp->di_data; 422 dt_node_t *dnp; 423 char buf[DT_TYPE_NAMELEN]; 424 char *fname; 425 const char *p; 426 int i; 427 428 p = prp->pr_name; 429 for (i = 0; (p = strchr(p, '-')) != NULL; i++) 430 p++; 431 432 fname = alloca(strlen(prp->pr_name) + 1 + i); 433 dt_header_fmt_func(fname, prp->pr_name); 434 435 char* probe; 436 437 if ((probe = dt_ld_encode_probe(infop->dthi_pfname, fname, prp)) == NULL) 438 return (dt_set_errno(dtp, errno)); 439 440 if (fprintf(infop->dthi_out, "extern void %s(", probe) < 0) 441 return (dt_set_errno(dtp, errno)); 442 443 for (dnp = prp->pr_nargs, i = 0; dnp != NULL; dnp = dnp->dn_list, i++) { 444 if (fprintf(infop->dthi_out, "%s%s", 445 dt_is_single_rank_pointer_type(dnp->dn_ctfp, dnp->dn_type) ? "const " : "", 446 ctf_type_name(dnp->dn_ctfp, dnp->dn_type, 447 buf, sizeof (buf))) < 0) 448 return (dt_set_errno(dtp, errno)); 449 450 if (i + 1 != prp->pr_nargc && 451 fprintf(infop->dthi_out, ", ") < 0) 452 return (dt_set_errno(dtp, errno)); 453 } 454 455 if (i == 0 && fprintf(infop->dthi_out, "void") < 0) 456 return (dt_set_errno(dtp, errno)); 457 458 if (fprintf(infop->dthi_out, ");\n") < 0) 459 return (dt_set_errno(dtp, errno)); 460 461 char* isenabled; 462 463 if ((isenabled = dt_ld_encode_isenabled(infop->dthi_pfname, fname)) == NULL) 464 return (dt_set_errno(dtp, errno)); 465 466 if (fprintf(infop->dthi_out, "extern int %s(void);\n", isenabled) < 0) 467 return (dt_set_errno(dtp, errno)); 468 469 free(isenabled); 470 isenabled = NULL; 471 472 return (0); 473} 474 475/*ARGSUSED*/ 476static int 477dt_header_probe(dt_idhash_t *dhp, dt_ident_t *idp, void *data) 478{ 479 dt_header_info_t *infop = data; 480 dtrace_hdl_t *dtp = infop->dthi_dtp; 481 dt_probe_t *prp = idp->di_data; 482 char *mname, *fname; 483 const char *p; 484 int i; 485 486 p = prp->pr_name; 487 for (i = 0; (p = strchr(p, '-')) != NULL; i++) 488 p++; 489 490 mname = alloca(strlen(prp->pr_name) + 1); 491 dt_header_fmt_macro(mname, prp->pr_name); 492 493 fname = alloca(strlen(prp->pr_name) + 1 + i); 494 dt_header_fmt_func(fname, prp->pr_name); 495 496 if (fprintf(infop->dthi_out, "#define\t%s_%s(", 497 infop->dthi_pmname, mname) < 0) 498 return (dt_set_errno(dtp, errno)); 499 500 for (i = 0; i < prp->pr_nargc; i++) { 501 if (fprintf(infop->dthi_out, "arg%d", i) < 0) 502 return (dt_set_errno(dtp, errno)); 503 504 if (i + 1 != prp->pr_nargc && 505 fprintf(infop->dthi_out, ", ") < 0) 506 return (dt_set_errno(dtp, errno)); 507 } 508 509 if (fprintf(infop->dthi_out, ") \\\n") < 0) 510 return (dt_set_errno(dtp, errno)); 511 512 if (fprintf(infop->dthi_out, "do { \\\n\t") < 0) 513 return (dt_set_errno(dtp, errno)); 514 515 if (!infop->dthi_empty) { 516 if (fprintf(infop->dthi_out, "__asm__ volatile(\".reference \" %s_TYPEDEFS); \\\n\t", infop->dthi_pmname) < 0) 517 return (dt_set_errno(dtp, errno)); 518 519 char* probe; 520 521 if ((probe = dt_ld_encode_probe(infop->dthi_pfname, fname, prp)) == NULL) 522 return (dt_set_errno(dtp, errno)); 523 524 if (fprintf(infop->dthi_out, "%s(", probe) < 0) 525 return (dt_set_errno(dtp, errno)); 526 527 free(probe); 528 probe = NULL; 529 530 for (i = 0; i < prp->pr_nargc; i++) { 531 if (fprintf(infop->dthi_out, "arg%d", i) < 0) 532 return (dt_set_errno(dtp, errno)); 533 534 if (i + 1 != prp->pr_nargc && 535 fprintf(infop->dthi_out, ", ") < 0) 536 return (dt_set_errno(dtp, errno)); 537 } 538 if (fprintf(infop->dthi_out, "); \\\n\t") < 0) 539 return (dt_set_errno(dtp, errno)); 540 541 if (!infop->dthi_empty) { 542 if (fprintf(infop->dthi_out, "__asm__ volatile(\".reference \" %s_STABILITY); \\\n", infop->dthi_pmname) < 0) 543 return (dt_set_errno(dtp, errno)); 544 } 545 } 546 547 if (fprintf(infop->dthi_out, "} while (0)\n") < 0) 548 return (dt_set_errno(dtp, errno)); 549 550 if (!infop->dthi_empty) { 551 char* isenabled; 552 553 if (fprintf(infop->dthi_out, "#define\t%s_%s_ENABLED() \\\n", 554 infop->dthi_pmname, mname) < 0) 555 return (dt_set_errno(dtp, errno)); 556 557 if ((isenabled = dt_ld_encode_isenabled(infop->dthi_pfname, fname)) == NULL) 558 return (dt_set_errno(dtp, errno)); 559 560 if (fprintf(infop->dthi_out, "\t({ int _r = %s(); \\\n", isenabled) < 0) 561 return (dt_set_errno(dtp, errno)); 562 563 if (fprintf(infop->dthi_out, "\t\t__asm__ volatile(\"\"); \\\n") < 0) 564 return (dt_set_errno(dtp, errno)); 565 566 if (fprintf(infop->dthi_out, "\t\t_r; })\n") < 0) 567 return (dt_set_errno(dtp, errno)); 568 569 free(isenabled); 570 isenabled = NULL; 571 } else { 572 if (fprintf(infop->dthi_out, "#define\t%s_%s_ENABLED() (0)\n", 573 infop->dthi_pmname, mname) < 0) 574 return (dt_set_errno(dtp, errno)); 575 } 576 577 return (0); 578} 579 580static int 581dt_header_provider(dtrace_hdl_t *dtp, dt_provider_t *pvp, FILE *out) 582{ 583 dt_header_info_t info; 584 const char *p; 585 int i; 586 587 if (pvp->pv_flags & DT_PROVIDER_IMPL) 588 return (0); 589 590 /* 591 * Count the instances of the '-' character since we'll need to double 592 * those up. 593 */ 594 p = pvp->pv_desc.dtvd_name; 595 for (i = 0; (p = strchr(p, '-')) != NULL; i++) 596 p++; 597 598 info.dthi_dtp = dtp; 599 info.dthi_out = out; 600 info.dthi_empty = 0; 601 602 info.dthi_pmname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1); 603 dt_header_fmt_macro(info.dthi_pmname, pvp->pv_desc.dtvd_name); 604 605 info.dthi_pfname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1 + i); 606 dt_header_fmt_func(info.dthi_pfname, pvp->pv_desc.dtvd_name); 607 608 char* stability; 609 610 if ((stability = dt_ld_encode_stability(info.dthi_pfname, pvp)) == NULL) 611 return (dt_set_errno(dtp, errno)); 612 613 if (fprintf(out, "#define %s_STABILITY \"%s\"\n\n", info.dthi_pmname, stability) < 0) 614 return (dt_set_errno(dtp, errno)); 615 616 free(stability); 617 stability = NULL; 618 619 char* typedefs; 620 621 if ((typedefs = dt_ld_encode_typedefs(info.dthi_pfname, pvp)) == NULL) 622 return (dt_set_errno(dtp, errno)); 623 624 if (fprintf(out, "#define %s_TYPEDEFS \"%s\"\n\n", info.dthi_pmname, typedefs) < 0) 625 return (dt_set_errno(dtp, errno)); 626 627 free(typedefs); 628 typedefs = NULL; 629 630 if (fprintf(out, "#if !defined(DTRACE_PROBES_DISABLED) || !DTRACE_PROBES_DISABLED\n\n") < 0) 631 return (dt_set_errno(dtp, errno)); 632 633 if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0) 634 return (-1); /* dt_errno is set for us */ 635 if (fprintf(out, "\n\n") < 0) 636 return (dt_set_errno(dtp, errno)); 637 if (dt_idhash_iter(pvp->pv_probes, dt_header_decl, &info) != 0) 638 return (-1); /* dt_errno is set for us */ 639 640 if (fprintf(out, "\n#else\n\n") < 0) 641 return (dt_set_errno(dtp, errno)); 642 643 info.dthi_empty = 1; 644 645 if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0) 646 return (-1); /* dt_errno is set for us */ 647 648 if (fprintf(out, "\n#endif /* !defined(DTRACE_PROBES_DISABLED) || !DTRACE_PROBES_DISABLED */\n\n") < 0) 649 return (dt_set_errno(dtp, errno)); 650 651 return (0); 652} 653 654int 655dtrace_program_header(dtrace_hdl_t *dtp, FILE *out, const char *fname) 656{ 657 dt_provider_t *pvp; 658 char *mfname, *p; 659 660 if (fname != NULL) { 661 if ((p = strrchr(fname, '/')) != NULL) 662 fname = p + 1; 663 664 mfname = alloca(strlen(fname) + 1); 665 dt_header_fmt_macro(mfname, fname); 666 if (fprintf(out, "#ifndef\t_%s\n#define\t_%s\n\n", 667 mfname, mfname) < 0) 668 return (dt_set_errno(dtp, errno)); 669 } 670 671 if (fprintf(out, "#include <unistd.h>\n\n") < 0) 672 return (-1); 673 674 if (fprintf(out, "#ifdef\t__cplusplus\nextern \"C\" {\n#endif\n\n") < 0) 675 return (-1); 676 677 for (pvp = dt_list_next(&dtp->dt_provlist); 678 pvp != NULL; pvp = dt_list_next(pvp)) { 679 if (dt_header_provider(dtp, pvp, out) != 0) 680 return (-1); /* dt_errno is set for us */ 681 } 682 683 if (fprintf(out, "\n#ifdef\t__cplusplus\n}\n#endif\n") < 0) 684 return (dt_set_errno(dtp, errno)); 685 686 if (fname != NULL && fprintf(out, "\n#endif\t/* _%s */\n", mfname) < 0) 687 return (dt_set_errno(dtp, errno)); 688 689 return (0); 690} 691