diff options
author | Robert Mustacchi <rm@joyent.com> | 2015-07-27 00:35:52 +0000 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2015-07-28 19:05:39 +0000 |
commit | 14c3d85bba96b10c225341f4c7f4af93c314b508 (patch) | |
tree | 8ef3a2513e220291581564d4db16bd5b4fbf127e /usr/src/lib/libctf/common/ctf_merge.c | |
parent | 9211d9b4c9ccc64292132e8e87c92ad6084b29a8 (diff) | |
download | illumos-joyent-14c3d85bba96b10c225341f4c7f4af93c314b508.tar.gz |
OS-4548 CTF Everywhere: Phase 1
OS-4549 ctfconvert should be implemented in terms of libctf
OS-4550 ctfconvert could convert multiple compilation units
OS-4553 want multi-threaded ctfmerge
OS-4552 Want general workq
OS-4551 Want general mergeq
OS-4554 ctfdiff doesn't properly handle unknown options
OS-4555 ctfdiff's symbols could be more consistently prefixed
OS-4048 new ctfmerge uses tmpfile after freeing it
OS-4556 ctfdump should drive on when incomplete files exist
OS-4557 ctf_add_encoded assigns() incorrect byte size to types
OS-4558 ctf_add_{struct,union,enum} can reuse forwards
OS-4559 ctf_add_{struct,union,enum} occasionally forget to dirty the ctf_file_t
OS-4560 ctf_add_member could better handle bitfields
OS-4561 ctf_type_size() reports wrong size for forwards
OS-4563 diffing CTF typedefs needs to walk multiple definitions
OS-4564 build scripts shouldn't hardcode CTF paths
OS-4565 ctf_fdcreate could be more flexible
OS-4566 Want libctf ctf_kind_name() function
OS-4567 Want libctf function to set struct/union size
OS-4568 Want ctfmerge altexec
Diffstat (limited to 'usr/src/lib/libctf/common/ctf_merge.c')
-rw-r--r-- | usr/src/lib/libctf/common/ctf_merge.c | 492 |
1 files changed, 405 insertions, 87 deletions
diff --git a/usr/src/lib/libctf/common/ctf_merge.c b/usr/src/lib/libctf/common/ctf_merge.c index 70676bb0fd..9611c50acc 100644 --- a/usr/src/lib/libctf/common/ctf_merge.c +++ b/usr/src/lib/libctf/common/ctf_merge.c @@ -31,16 +31,15 @@ * we should take care to do the merge in the same way every time. */ -#include <ctf_impl.h> -#include <libctf.h> +#include <libctf_impl.h> #include <sys/debug.h> #include <sys/list.h> #include <stddef.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> - -#include <stdio.h> +#include <mergeq.h> +#include <errno.h> typedef struct ctf_merge_tinfo { uint16_t cmt_map; /* Map to the type in out */ @@ -56,6 +55,7 @@ typedef struct ctf_merge_types { ctf_file_t *cm_out; /* Output CTF file */ ctf_file_t *cm_src; /* Input CTF file */ ctf_merge_tinfo_t *cm_tmap; /* Type state information */ + boolean_t cm_dedup; /* Are we doing a dedup? */ } ctf_merge_types_t; typedef struct ctf_merge_objmap { @@ -80,11 +80,13 @@ typedef struct ctf_merge_input { ctf_file_t *cmi_input; list_t cmi_omap; list_t cmi_fmap; + boolean_t cmi_created; } ctf_merge_input_t; struct ctf_merge_handle { - ctf_file_t *cmh_output; /* Final output */ list_t cmh_inputs; /* Input list */ + uint_t cmh_ninputs; /* Number of inputs */ + uint_t cmh_nthreads; /* Number of threads to use */ ctf_file_t *cmh_unique; /* ctf to uniquify against */ boolean_t cmh_msyms; /* Should we merge symbols/funcs? */ int cmh_ofd; /* FD for output file */ @@ -95,6 +97,22 @@ struct ctf_merge_handle { static int ctf_merge_add_type(ctf_merge_types_t *, ctf_id_t); +static ctf_id_t +ctf_merge_gettype(ctf_merge_types_t *cmp, ctf_id_t id) +{ + if (cmp->cm_dedup == B_FALSE) { + VERIFY(cmp->cm_tmap[id].cmt_map != 0); + return (cmp->cm_tmap[id].cmt_map); + } + + while (cmp->cm_tmap[id].cmt_missing == B_FALSE) { + VERIFY(cmp->cm_tmap[id].cmt_map != 0); + id = cmp->cm_tmap[id].cmt_map; + } + VERIFY(cmp->cm_tmap[id].cmt_map != 0); + return (cmp->cm_tmap[id].cmt_map); +} + static void ctf_merge_diffcb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp, ctf_id_t oid, void *arg) @@ -108,6 +126,8 @@ ctf_merge_diffcb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp, VERIFY(cmt[oid].cmt_map == 0); cmt[oid].cmt_map = iid; cmt[oid].cmt_forward = B_TRUE; + ctf_dprintf("merge diff forward mapped %d->%d\n", oid, + iid); return; } @@ -119,9 +139,11 @@ ctf_merge_diffcb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp, if (cmt[oid].cmt_map != 0) return; cmt[oid].cmt_map = iid; + ctf_dprintf("merge diff mapped %d->%d\n", oid, iid); } else if (ifp == cmp->cm_src) { VERIFY(cmt[iid].cmt_map == 0); cmt[iid].cmt_missing = B_TRUE; + ctf_dprintf("merge diff said %d is missing\n", iid); } } @@ -176,7 +198,7 @@ ctf_merge_add_array(ctf_merge_types_t *cmp, ctf_id_t id) return (ret); ASSERT(cmp->cm_tmap[ar.ctr_contents].cmt_map != 0); } - ar.ctr_contents = cmp->cm_tmap[ar.ctr_contents].cmt_map; + ar.ctr_contents = ctf_merge_gettype(cmp, ar.ctr_contents); if (cmp->cm_tmap[ar.ctr_index].cmt_map == 0) { ret = ctf_merge_add_type(cmp, ar.ctr_index); @@ -184,7 +206,7 @@ ctf_merge_add_array(ctf_merge_types_t *cmp, ctf_id_t id) return (ret); ASSERT(cmp->cm_tmap[ar.ctr_index].cmt_map != 0); } - ar.ctr_index = cmp->cm_tmap[ar.ctr_index].cmt_map; + ar.ctr_index = ctf_merge_gettype(cmp, ar.ctr_index); ret = ctf_add_array(cmp->cm_out, flags, &ar); if (ret == CTF_ERR) @@ -221,7 +243,7 @@ ctf_merge_add_reftype(ctf_merge_types_t *cmp, ctf_id_t id) return (ret); ASSERT(cmp->cm_tmap[reftype].cmt_map != 0); } - reftype = cmp->cm_tmap[reftype].cmt_map; + reftype = ctf_merge_gettype(cmp, reftype); ret = ctf_add_reftype(cmp->cm_out, flags, name, reftype, ctf_type_kind(cmp->cm_src, id)); @@ -258,7 +280,7 @@ ctf_merge_add_typedef(ctf_merge_types_t *cmp, ctf_id_t id) return (ret); ASSERT(cmp->cm_tmap[reftype].cmt_map != 0); } - reftype = cmp->cm_tmap[reftype].cmt_map; + reftype = ctf_merge_gettype(cmp, reftype); ret = ctf_add_typedef(cmp->cm_out, flags, name, reftype); if (ret == CTF_ERR) @@ -346,7 +368,7 @@ ctf_merge_add_func(ctf_merge_types_t *cmp, ctf_id_t id) return (ret); ASSERT(cmp->cm_tmap[ctc.ctc_return].cmt_map != 0); } - ctc.ctc_return = cmp->cm_tmap[ctc.ctc_return].cmt_map; + ctc.ctc_return = ctf_merge_gettype(cmp, ctc.ctc_return); for (i = 0; i < ctc.ctc_argc; i++) { if (cmp->cm_tmap[argv[i]].cmt_map == 0) { @@ -355,7 +377,7 @@ ctf_merge_add_func(ctf_merge_types_t *cmp, ctf_id_t id) return (ret); ASSERT(cmp->cm_tmap[argv[i]].cmt_map != 0); } - argv[i] = cmp->cm_tmap[argv[i]].cmt_map; + argv[i] = ctf_merge_gettype(cmp, argv[i]); } ret = ctf_add_funcptr(cmp->cm_out, flags, &ctc, argv); @@ -411,6 +433,7 @@ ctf_merge_add_member(const char *name, ctf_id_t type, ulong_t offset, void *arg) VERIFY(cms->cms_cm->cm_tmap[type].cmt_map != 0); type = cms->cms_cm->cm_tmap[type].cmt_map; + ctf_dprintf("Trying to add member %s to %d\n", name, cms->cms_id); return (ctf_add_member(cms->cms_cm->cm_out, cms->cms_id, name, type, offset) == CTF_ERR); } @@ -421,7 +444,7 @@ ctf_merge_add_member(const char *name, ctf_id_t type, ulong_t offset, void *arg) * mark all structures and unions as needing to be fixed up. */ static int -ctf_merge_add_su(ctf_merge_types_t *cmp, ctf_id_t id, boolean_t forward) +ctf_merge_add_sou(ctf_merge_types_t *cmp, ctf_id_t id, boolean_t forward) { int flags, kind; const ctf_type_t *tp; @@ -451,6 +474,8 @@ ctf_merge_add_su(ctf_merge_types_t *cmp, ctf_id_t id, boolean_t forward) if (forward == B_FALSE) { VERIFY(cmp->cm_tmap[id].cmt_map == 0); cmp->cm_tmap[id].cmt_map = suid; + ctf_dprintf("added sou \"%s\" as (%d) %d->%d\n", name, kind, id, + suid); } else { VERIFY(cmp->cm_tmap[id].cmt_map == suid); } @@ -501,7 +526,7 @@ ctf_merge_add_type(ctf_merge_types_t *cmp, ctf_id_t id) break; case CTF_K_STRUCT: case CTF_K_UNION: - ret = ctf_merge_add_su(cmp, id, B_FALSE); + ret = ctf_merge_add_sou(cmp, id, B_FALSE); break; case CTF_K_UNKNOWN: /* @@ -517,22 +542,29 @@ ctf_merge_add_type(ctf_merge_types_t *cmp, ctf_id_t id) } static int -ctf_merge_fixup_su(ctf_merge_types_t *cmp, ctf_id_t id) +ctf_merge_fixup_sou(ctf_merge_types_t *cmp, ctf_id_t id) { ctf_dtdef_t *dtd; ctf_merge_su_t cms; ctf_id_t mapid; + ssize_t size; mapid = cmp->cm_tmap[id].cmt_map; VERIFY(mapid != 0); dtd = ctf_dtd_lookup(cmp->cm_out, mapid); VERIFY(dtd != NULL); + ctf_dprintf("Trying to fix up sou %d\n", id); cms.cms_cm = cmp; cms.cms_id = mapid; if (ctf_member_iter(cmp->cm_src, id, ctf_merge_add_member, &cms) != 0) return (CTF_ERR); + if ((size = ctf_type_size(cmp->cm_src, id)) == CTF_ERR) + return (CTF_ERR); + if (ctf_set_size(cmp->cm_out, mapid, size) == CTF_ERR) + return (CTF_ERR); + return (0); } @@ -545,7 +577,7 @@ ctf_merge_fixup_type(ctf_merge_types_t *cmp, ctf_id_t id) switch (kind) { case CTF_K_STRUCT: case CTF_K_UNION: - ret = ctf_merge_fixup_su(cmp, id); + ret = ctf_merge_fixup_sou(cmp, id); break; default: VERIFY(0); @@ -555,6 +587,41 @@ ctf_merge_fixup_type(ctf_merge_types_t *cmp, ctf_id_t id) return (ret); } +/* + * Now that we've successfully merged everything, we're going to clean + * up the merge type table. Traditionally if we had just two different + * files that we were working between, the types would be fully + * resolved. However, because we were comparing with ourself every step + * of the way and not our reduced self, we need to go through and update + * every mapped entry to what it now points to in the deduped file. + */ +static void +ctf_merge_fixup_dedup_map(ctf_merge_types_t *cmp) +{ + int i; + + for (i = 1; i < cmp->cm_src->ctf_typemax + 1; i++) { + ctf_id_t tid; + + /* + * Missing types always have their id updated to exactly what it + * should be. + */ + if (cmp->cm_tmap[i].cmt_missing == B_TRUE) { + VERIFY(cmp->cm_tmap[i].cmt_map != 0); + continue; + } + + tid = i; + while (cmp->cm_tmap[tid].cmt_missing == B_FALSE) { + VERIFY(cmp->cm_tmap[tid].cmt_map != 0); + tid = cmp->cm_tmap[tid].cmt_map; + } + VERIFY(cmp->cm_tmap[tid].cmt_map != 0); + cmp->cm_tmap[i].cmt_map = cmp->cm_tmap[tid].cmt_map; + } +} + /* * We're going to do three passes over the containers. @@ -571,19 +638,23 @@ ctf_merge_fixup_type(ctf_merge_types_t *cmp, ctf_id_t id) * * Importantly, we *must* call ctf_update between the second and third pass, * otherwise several of the libctf functions will not properly find the data in - * the container. + * the container. If we're doing a dedup we also fix up the type mapping. */ static int ctf_merge_common(ctf_merge_types_t *cmp) { int ret, i; + ctf_phase_dump(cmp->cm_src, "merge-common-src"); + ctf_phase_dump(cmp->cm_out, "merge-common-dest"); + /* Pass 1 */ for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) { if (cmp->cm_tmap[i].cmt_forward == B_TRUE) { - ret = ctf_merge_add_su(cmp, i, B_TRUE); - if (ret != 0) + ret = ctf_merge_add_sou(cmp, i, B_TRUE); + if (ret != 0) { return (ret); + } } } @@ -591,8 +662,10 @@ ctf_merge_common(ctf_merge_types_t *cmp) for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) { if (cmp->cm_tmap[i].cmt_missing == B_TRUE) { ret = ctf_merge_add_type(cmp, i); - if (ret != 0) + if (ret != 0) { + ctf_dprintf("Failed to merge type %d\n", i); return (ret); + } } } @@ -600,6 +673,11 @@ ctf_merge_common(ctf_merge_types_t *cmp) if (ret != 0) return (ret); + if (cmp->cm_dedup == B_TRUE) { + ctf_merge_fixup_dedup_map(cmp); + } + + ctf_dprintf("Beginning merge pass 3\n"); /* Pass 3 */ for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) { if (cmp->cm_tmap[i].cmt_fixup == B_TRUE) { @@ -609,6 +687,10 @@ ctf_merge_common(ctf_merge_types_t *cmp) } } + if (cmp->cm_dedup == B_TRUE) { + ctf_merge_fixup_dedup_map(cmp); + } + return (0); } @@ -666,18 +748,24 @@ ctf_merge_types_fini(ctf_merge_types_t *cmp) } /* - * Merge types from targ into dest. + * Merge the types contained inside of two input files. The second input file is + * always going to be the destination. We're guaranteed that it's always + * writeable. */ static int -ctf_merge(ctf_file_t **outp, ctf_merge_input_t *cmi) +ctf_merge_types(void *arg, void *arg2, void **outp, void *unsued) { int ret; ctf_merge_types_t cm; ctf_diff_t *cdp; ctf_merge_objmap_t *cmo; ctf_merge_funcmap_t *cmf; - ctf_file_t *out = *outp; - ctf_file_t *source = cmi->cmi_input; + ctf_merge_input_t *scmi = arg; + ctf_merge_input_t *dcmi = arg2; + ctf_file_t *out = dcmi->cmi_input; + ctf_file_t *source = scmi->cmi_input; + + ctf_dprintf("merging %p->%p\n", source, out); if (!(out->ctf_flags & LCTF_RDWR)) return (ctf_set_errno(out, ECTF_RDONLY)); @@ -690,6 +778,7 @@ ctf_merge(ctf_file_t **outp, ctf_merge_input_t *cmi) cm.cm_out = out; cm.cm_src = source; + cm.cm_dedup = B_FALSE; ret = ctf_merge_types_init(&cm); if (ret != 0) { ctf_diff_fini(cdp); @@ -700,24 +789,27 @@ ctf_merge(ctf_file_t **outp, ctf_merge_input_t *cmi) if (ret != 0) goto cleanup; ret = ctf_merge_common(&cm); - if (ret == 0) + ctf_dprintf("merge common returned with %d\n", ret); + if (ret == 0) { ret = ctf_update(out); - else + ctf_dprintf("update returned with %d\n", ret); + } else { goto cleanup; + } /* * Now we need to fix up the object and function maps. */ - for (cmo = list_head(&cmi->cmi_omap); cmo != NULL; - cmo = list_next(&cmi->cmi_omap, cmo)) { + for (cmo = list_head(&scmi->cmi_omap); cmo != NULL; + cmo = list_next(&scmi->cmi_omap, cmo)) { if (cmo->cmo_tid == 0) continue; VERIFY(cm.cm_tmap[cmo->cmo_tid].cmt_map != 0); cmo->cmo_tid = cm.cm_tmap[cmo->cmo_tid].cmt_map; } - for (cmf = list_head(&cmi->cmi_fmap); cmf != NULL; - cmf = list_next(&cmi->cmi_fmap, cmf)) { + for (cmf = list_head(&scmi->cmi_fmap); cmf != NULL; + cmf = list_next(&scmi->cmi_fmap, cmf)) { int i; VERIFY(cm.cm_tmap[cmf->cmf_rtid].cmt_map != 0); @@ -728,28 +820,36 @@ ctf_merge(ctf_file_t **outp, ctf_merge_input_t *cmi) } } + /* + * Now that we've fixed things up, we need to give our function and + * object maps to the destination, such that it can continue to update + * them going forward. + */ + list_move_tail(&dcmi->cmi_fmap, &scmi->cmi_fmap); + list_move_tail(&dcmi->cmi_omap, &scmi->cmi_omap); + cleanup: + if (ret == 0) + *outp = dcmi; ctf_merge_types_fini(&cm); ctf_diff_fini(cdp); - return (ret); + if (ret != 0) + return (ctf_errno(out)); + return (0); } static int -ctf_uniquify_types(ctf_merge_t *cmh, ctf_file_t **outp) +ctf_uniquify_types(ctf_merge_t *cmh, ctf_file_t *src, ctf_file_t **outp) { int err, ret; ctf_file_t *out; ctf_merge_types_t cm; ctf_diff_t *cdp; ctf_merge_input_t *cmi; - ctf_file_t *src = cmh->cmh_output; ctf_file_t *parent = cmh->cmh_unique; *outp = NULL; - if (cmh->cmh_ofd == -1) - out = ctf_create(&err); - else - out = ctf_fdcreate(cmh->cmh_ofd, &err); + out = ctf_fdcreate(cmh->cmh_ofd, &err); if (out == NULL) return (ctf_set_errno(src, err)); @@ -773,6 +873,7 @@ ctf_uniquify_types(ctf_merge_t *cmh, ctf_file_t **outp) cm.cm_out = parent; cm.cm_src = src; + cm.cm_dedup = B_FALSE; ret = ctf_merge_types_init(&cm); if (ret != 0) { ctf_close(out); @@ -841,6 +942,9 @@ ctf_merge_fini_input(ctf_merge_input_t *cmi) ctf_free(cmf, sizeof (ctf_merge_funcmap_t) + sizeof (ctf_id_t) * cmf->cmf_argc); + if (cmi->cmi_created == B_TRUE && cmi->cmi_input != NULL) + ctf_close(cmi->cmi_input); + ctf_free(cmi, sizeof (ctf_merge_input_t)); } @@ -850,9 +954,6 @@ ctf_merge_fini(ctf_merge_t *cmh) size_t len; ctf_merge_input_t *cmi; - if (cmh->cmh_output != NULL) - ctf_close(cmh->cmh_output); - if (cmh->cmh_label != NULL) { len = strlen(cmh->cmh_label) + 1; ctf_free(cmh->cmh_label, len); @@ -891,22 +992,18 @@ ctf_merge_init(int fd, int *errp) } if (fd == -1) { - out->cmh_output = ctf_create(errp); out->cmh_msyms = B_FALSE; } else { - out->cmh_output = ctf_fdcreate(fd, errp); out->cmh_msyms = B_TRUE; } - if (out->cmh_output == NULL) { - ctf_free(out, sizeof (ctf_merge_t)); - return (NULL); - } - list_create(&out->cmh_inputs, sizeof (ctf_merge_input_t), offsetof(ctf_merge_input_t, cmi_node)); + out->cmh_ninputs = 0; + out->cmh_nthreads = 1; out->cmh_unique = NULL; out->cmh_ofd = fd; + out->cmh_flags = 0; out->cmh_label = NULL; out->cmh_pname = NULL; @@ -918,9 +1015,6 @@ ctf_merge_label(ctf_merge_t *cmh, const char *label) { char *dup; - if (cmh->cmh_output == NULL) - return (EINVAL); - if (label == NULL) return (EINVAL); @@ -983,14 +1077,17 @@ ctf_merge_add_objs(const char *name, ctf_id_t id, ulong_t idx, void *arg) return (0); } +/* + * Whenever we create an entry to merge, we then go and add a second empty + * ctf_file_t which we use for the purposes of our merging. It's not the best, + * but it's the best that we've got at the moment. + */ int ctf_merge_add(ctf_merge_t *cmh, ctf_file_t *input) { int ret; ctf_merge_input_t *cmi; - - if (cmh->cmh_output == NULL) - return (EINVAL); + ctf_file_t *empty; if (input->ctf_flags & LCTF_CHILD) return (ECTF_MCHILD); @@ -999,6 +1096,7 @@ ctf_merge_add(ctf_merge_t *cmh, ctf_file_t *input) if (cmi == NULL) return (ENOMEM); + cmi->cmi_created = B_FALSE; cmi->cmi_input = input; list_create(&cmi->cmi_fmap, sizeof (ctf_merge_funcmap_t), offsetof(ctf_merge_funcmap_t, cmf_node)); @@ -1020,6 +1118,30 @@ ctf_merge_add(ctf_merge_t *cmh, ctf_file_t *input) } list_insert_tail(&cmh->cmh_inputs, cmi); + cmh->cmh_ninputs++; + + /* And now the empty one to merge into this */ + cmi = ctf_alloc(sizeof (ctf_merge_input_t)); + if (cmi == NULL) + return (ENOMEM); + list_create(&cmi->cmi_fmap, sizeof (ctf_merge_funcmap_t), + offsetof(ctf_merge_funcmap_t, cmf_node)); + list_create(&cmi->cmi_omap, sizeof (ctf_merge_funcmap_t), + offsetof(ctf_merge_objmap_t, cmo_node)); + + empty = ctf_fdcreate(cmh->cmh_ofd, &ret); + if (empty == NULL) + return (ret); + cmi->cmi_input = empty; + cmi->cmi_created = B_TRUE; + + if (ctf_setmodel(empty, ctf_getmodel(input)) == CTF_ERR) { + return (ctf_errno(empty)); + } + + list_insert_tail(&cmh->cmh_inputs, cmi); + cmh->cmh_ninputs++; + ctf_dprintf("added containers %p and %p\n", input, empty); return (0); } @@ -1028,8 +1150,6 @@ ctf_merge_uniquify(ctf_merge_t *cmh, ctf_file_t *u, const char *pname) { char *dup; - if (cmh->cmh_output == NULL) - return (EINVAL); if (u->ctf_flags & LCTF_CHILD) return (ECTF_MCHILD); if (pname == NULL) @@ -1093,8 +1213,12 @@ found: if (cmo != NULL) { if (cmo->cmo_tid == 0) continue; - if ((err = ctf_add_object(fp, i, cmo->cmo_tid)) != 0) + if ((err = ctf_add_object(fp, i, cmo->cmo_tid)) != 0) { + ctf_dprintf("Failed to add symbol %s->%d: %s\n", + name, cmo->cmo_tid, + ctf_errmsg(ctf_errno(fp))); return (err); + } } } @@ -1161,12 +1285,14 @@ found: } int -ctf_merge_merge(ctf_merge_t *cmh, ctf_file_t **out) +ctf_merge_merge(ctf_merge_t *cmh, ctf_file_t **outp) { - int err; + int err, merr; ctf_merge_input_t *cmi; - boolean_t mset = B_FALSE; ctf_id_t ltype; + mergeq_t *mqp; + ctf_merge_input_t *final; + ctf_file_t *out; if (cmh->cmh_label != NULL && cmh->cmh_unique != NULL) { const char *label = ctf_label_topmost(cmh->cmh_unique); @@ -1176,56 +1302,248 @@ ctf_merge_merge(ctf_merge_t *cmh, ctf_file_t **out) return (ECTF_LCONFLICT); } + if (mergeq_init(&mqp, cmh->cmh_nthreads) == -1) { + return (errno); + } + /* * We should consider doing a divide and conquer and parallel merge * here. If we did, we'd want to use some number of threads to perform * this operation. */ + VERIFY(cmh->cmh_ninputs % 2 == 0); for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL; cmi = list_next(&cmh->cmh_inputs, cmi)) { - if (mset == B_FALSE) { - if (ctf_setmodel(cmh->cmh_output, - ctf_getmodel(cmi->cmi_input)) != 0) { - return (ctf_errno(cmh->cmh_output)); - } - mset = B_TRUE; + if (mergeq_add(mqp, cmi) == -1) { + err = errno; + mergeq_fini(mqp); } - err = ctf_merge(&cmh->cmh_output, cmi); - if (err != 0) - return (ctf_errno(cmh->cmh_output)); } + err = mergeq_merge(mqp, ctf_merge_types, NULL, (void **)&final, &merr); + mergeq_fini(mqp); + + if (err == MERGEQ_ERROR) { + return (errno); + } else if (err == MERGEQ_UERROR) { + return (merr); + } + + /* + * Disassociate the generated ctf_file_t from the original input. That + * way when the input gets cleaned up, we don't accidentally kill the + * final reference to the ctf_file_t. If it gets uniquified then we'll + * kill it. + */ + VERIFY(final->cmi_input != NULL); + out = final->cmi_input; + final->cmi_input = NULL; + + ctf_dprintf("preparing to uniquify against: %p\n", cmh->cmh_unique); if (cmh->cmh_unique != NULL) { - err = ctf_uniquify_types(cmh, out); - if (err != 0) - return (ctf_errno(cmh->cmh_output)); - ctf_close(cmh->cmh_output); - } else { - *out = cmh->cmh_output; + ctf_file_t *u; + err = ctf_uniquify_types(cmh, out, &u); + if (err != 0) { + err = ctf_errno(out); + ctf_close(out); + return (err); + } + ctf_close(out); + out = u; } - ltype = (*out)->ctf_typemax; - if (((*out)->ctf_flags & LCTF_CHILD) && ltype != 0) + ltype = out->ctf_typemax; + if ((out->ctf_flags & LCTF_CHILD) && ltype != 0) ltype += 0x8000; - if (ctf_add_label(*out, cmh->cmh_label, ltype, 0) != 0) { - return (ctf_errno(*out)); + ctf_dprintf("trying to add the label\n"); + if (cmh->cmh_label != NULL && + ctf_add_label(out, cmh->cmh_label, ltype, 0) != 0) { + ctf_close(out); + return (ctf_errno(out)); } + ctf_dprintf("merging symbols and the like\n"); if (cmh->cmh_msyms == B_TRUE) { - err = ctf_merge_symbols(cmh, *out); - if (err != 0) - return (ctf_errno(*out)); + err = ctf_merge_symbols(cmh, out); + if (err != 0) { + ctf_close(out); + return (ctf_errno(out)); + } + + err = ctf_merge_functions(cmh, out); + if (err != 0) { + ctf_close(out); + return (ctf_errno(out)); + } + } + + err = ctf_update(out); + if (err != 0) { + ctf_close(out); + return (ctf_errno(out)); + } + + *outp = out; + return (0); +} + +/* + * When we get told that something is unique, eg. same is B_FALSE, then that + * tells us that we need to add it to the output. If same is B_TRUE, then we'll + * want to record it in the mapping table so that we know how to redirect types + * to the extant ones. + */ +static void +ctf_dedup_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp, + ctf_id_t oid, void *arg) +{ + ctf_merge_types_t *cmp = arg; + ctf_merge_tinfo_t *cmt = cmp->cm_tmap; - err = ctf_merge_functions(cmh, *out); - if (err != 0) - return (ctf_errno(*out)); + if (same == B_TRUE) { + /* + * The output id here may itself map to something else. + * Therefore, we need to basically walk a chain and see what it + * points to until it itself points to a base type, eg. -1. + * Otherwise we'll dedup to something which no longer exists. + */ + while (cmt[oid].cmt_missing == B_FALSE) + oid = cmt[oid].cmt_map; + cmt[iid].cmt_map = oid; + ctf_dprintf("%d->%d \n", iid, oid); + } else { + VERIFY(cmt[iid].cmt_map == 0); + cmt[iid].cmt_missing = B_TRUE; + ctf_dprintf("%d is missing\n", iid); } +} - err = ctf_update(*out); - if (err != 0) - return (ctf_errno(*out)); +/* + * Dedup a CTF container. + * + * DWARF and other encoding formats that we use to create CTF data may create + * multiple copies of a given type. However, after doing a conversion, and + * before doing a merge, we'd prefer, if possible, to have every input container + * to be unique. + * + * Doing a deduplication is like a normal merge. However, when we diff the types + * in the container, rather than doing a normal diff, we instead want to diff + * against any already processed types. eg, for a given type i in a container, + * we want to diff it from 0 to i - 1. + */ +int +ctf_merge_dedup(ctf_merge_t *cmp, ctf_file_t **outp) +{ + int ret; + ctf_diff_t *cdp = NULL; + ctf_merge_input_t *cmi, *cmc; + ctf_file_t *ifp, *ofp; + ctf_merge_objmap_t *cmo; + ctf_merge_funcmap_t *cmf; + ctf_merge_types_t cm; - cmh->cmh_output = NULL; + if (cmp == NULL || outp == NULL) + return (EINVAL); + + ctf_dprintf("encountered %d inputs\n", cmp->cmh_ninputs); + if (cmp->cmh_ninputs != 2) + return (EINVAL); + + ctf_dprintf("passed argument sanity check\n"); + + cmi = list_head(&cmp->cmh_inputs); + VERIFY(cmi != NULL); + cmc = list_next(&cmp->cmh_inputs, cmi); + VERIFY(cmc != NULL); + ifp = cmi->cmi_input; + ofp = cmc->cmi_input; + VERIFY(ifp != NULL); + VERIFY(ofp != NULL); + cm.cm_src = ifp; + cm.cm_out = ofp; + cm.cm_dedup = B_TRUE; + + if ((ret = ctf_merge_types_init(&cm)) != 0) { + return (ret); + } + if ((ret = ctf_diff_init(ifp, ifp, &cdp)) != 0) + goto err; + + ctf_dprintf("Successfully initialized dedup\n"); + if ((ret = ctf_diff_self(cdp, ctf_dedup_cb, &cm)) != 0) + goto err; + + ctf_dprintf("Successfully diffed types\n"); + ret = ctf_merge_common(&cm); + ctf_dprintf("deduping types result: %d\n", ret); + if (ret == 0) + ret = ctf_update(cm.cm_out); + if (ret != 0) + goto err; + + ctf_dprintf("Successfully deduped types\n"); + ctf_phase_dump(cm.cm_out, "dedup-pre-syms"); + + + /* + * Now we need to fix up the object and function maps. + */ + for (cmo = list_head(&cmi->cmi_omap); cmo != NULL; + cmo = list_next(&cmi->cmi_omap, cmo)) { + if (cmo->cmo_tid == 0) + continue; + ctf_dprintf("mapped %s %d->%d\n", cmo->cmo_name, + cmo->cmo_tid, cm.cm_tmap[cmo->cmo_tid].cmt_map); + cmo->cmo_tid = cm.cm_tmap[cmo->cmo_tid].cmt_map; + } + + for (cmf = list_head(&cmi->cmi_fmap); cmf != NULL; + cmf = list_next(&cmi->cmi_fmap, cmf)) { + int i; + + VERIFY(cm.cm_tmap[cmf->cmf_rtid].cmt_map != 0); + cmf->cmf_rtid = cm.cm_tmap[cmf->cmf_rtid].cmt_map; + for (i = 0; i < cmf->cmf_argc; i++) { + VERIFY(cm.cm_tmap[cmf->cmf_args[i]].cmt_map != 0); + cmf->cmf_args[i] = cm.cm_tmap[cmf->cmf_args[i]].cmt_map; + } + } + + if (cmp->cmh_msyms == B_TRUE) { + ret = ctf_merge_symbols(cmp, cm.cm_out); + if (ret != 0) { + ret = ctf_errno(cm.cm_out); + ctf_dprintf("failed to dedup symbols: %s\n", + ctf_errmsg(ret)); + goto err; + } + + ret = ctf_merge_functions(cmp, cm.cm_out); + if (ret != 0) { + ret = ctf_errno(cm.cm_out); + ctf_dprintf("failed to dedup functions: %s\n", + ctf_errmsg(ret)); + goto err; + } + } + + ret = ctf_update(cm.cm_out); + if (ret == 0) { + cmc->cmi_input = NULL; + *outp = cm.cm_out; + } +err: + ctf_merge_types_fini(&cm); + ctf_diff_fini(cdp); + return (ret); +} + +int +ctf_merge_set_nthreads(ctf_merge_t *cmp, const uint_t nthrs) +{ + if (nthrs == 0) + return (EINVAL); + cmp->cmh_nthreads = nthrs; return (0); } |