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