ctfmerge.c revision 225736
162587Sitojun/*
295023Ssuz * CDDL HEADER START
362587Sitojun *
4139826Simp * The contents of this file are subject to the terms of the
553541Sshin * Common Development and Distribution License (the "License").
653541Sshin * You may not use this file except in compliance with the License.
753541Sshin *
853541Sshin * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
953541Sshin * or http://www.opensolaris.org/os/licensing.
1053541Sshin * See the License for the specific language governing permissions
1153541Sshin * and limitations under the License.
1253541Sshin *
1353541Sshin * When distributing Covered Code, include this CDDL HEADER in each
1453541Sshin * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1553541Sshin * If applicable, add the following below this CDDL HEADER, with the
1653541Sshin * fields enclosed by brackets "[]" replaced with your own identifying
1753541Sshin * information: Portions Copyright [yyyy] [name of copyright owner]
1853541Sshin *
1953541Sshin * CDDL HEADER END
2053541Sshin */
2153541Sshin/*
2253541Sshin * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2353541Sshin * Use is subject to license terms.
2453541Sshin */
2553541Sshin
2653541Sshin#pragma ident	"%Z%%M%	%I%	%E% SMI"
2753541Sshin
2853541Sshin/*
2953541Sshin * Given several files containing CTF data, merge and uniquify that data into
3053541Sshin * a single CTF section in an output file.
3153541Sshin *
3253541Sshin * Merges can proceed independently.  As such, we perform the merges in parallel
33139826Simp * using a worker thread model.  A given glob of CTF data (either all of the CTF
3453541Sshin * data from a single input file, or the result of one or more merges) can only
3553541Sshin * be involved in a single merge at any given time, so the process decreases in
3653541Sshin * parallelism, especially towards the end, as more and more files are
3753541Sshin * consolidated, finally resulting in a single merge of two large CTF graphs.
3853541Sshin * Unfortunately, the last merge is also the slowest, as the two graphs being
3953541Sshin * merged are each the product of merges of half of the input files.
4053541Sshin *
4153541Sshin * The algorithm consists of two phases, described in detail below.  The first
4253541Sshin * phase entails the merging of CTF data in groups of eight.  The second phase
4353541Sshin * takes the results of Phase I, and merges them two at a time.  This disparity
4453541Sshin * is due to an observation that the merge time increases at least quadratically
4553541Sshin * with the size of the CTF data being merged.  As such, merges of CTF graphs
4653541Sshin * newly read from input files are much faster than merges of CTF graphs that
4753541Sshin * are themselves the results of prior merges.
4853541Sshin *
4953541Sshin * A further complication is the need to ensure the repeatability of CTF merges.
5053541Sshin * That is, a merge should produce the same output every time, given the same
5153541Sshin * input.  In both phases, this consistency requirement is met by imposing an
5253541Sshin * ordering on the merge process, thus ensuring that a given set of input files
5353541Sshin * are merged in the same order every time.
5453541Sshin *
5553541Sshin *   Phase I
5653541Sshin *
5753541Sshin *   The main thread reads the input files one by one, transforming the CTF
5853541Sshin *   data they contain into tdata structures.  When a given file has been read
5953541Sshin *   and parsed, it is placed on the work queue for retrieval by worker threads.
6053541Sshin *
6153541Sshin *   Central to Phase I is the Work In Progress (wip) array, which is used to
6253541Sshin *   merge batches of files in a predictable order.  Files are read by the main
6353541Sshin *   thread, and are merged into wip array elements in round-robin order.  When
6462587Sitojun *   the number of files merged into a given array slot equals the batch size,
6562587Sitojun *   the merged CTF graph in that array is added to the done slot in order by
6655009Sshin *   array slot.
6755009Sshin *
6853541Sshin *   For example, consider a case where we have five input files, a batch size
6953541Sshin *   of two, a wip array size of two, and two worker threads (T1 and T2).
7078064Sume *
7153541Sshin *    1. The wip array elements are assigned initial batch numbers 0 and 1.
7283366Sjulian *    2. T1 reads an input file from the input queue (wq_queue).  This is the
7353541Sshin *       first input file, so it is placed into wip[0].  The second file is
7453541Sshin *       similarly read and placed into wip[1].  The wip array slots now contain
7553541Sshin *       one file each (wip_nmerged == 1).
7653541Sshin *    3. T1 reads the third input file, which it merges into wip[0].  The
7753541Sshin *       number of files in wip[0] is equal to the batch size.
7853541Sshin *    4. T2 reads the fourth input file, which it merges into wip[1].  wip[1]
7953541Sshin *       is now full too.
8053541Sshin *    5. T2 attempts to place the contents of wip[1] on the done queue
8153541Sshin *       (wq_done_queue), but it can't, since the batch ID for wip[1] is 1.
8253541Sshin *       Batch 0 needs to be on the done queue before batch 1 can be added, so
8353541Sshin *       T2 blocks on wip[1]'s cv.
8453541Sshin *    6. T1 attempts to place the contents of wip[0] on the done queue, and
8553541Sshin *       succeeds, updating wq_lastdonebatch to 0.  It clears wip[0], and sets
8653541Sshin *       its batch ID to 2.  T1 then signals wip[1]'s cv to awaken T2.
8764060Sdarrenr *    7. T2 wakes up, notices that wq_lastdonebatch is 0, which means that
8853541Sshin *       batch 1 can now be added.  It adds wip[1] to the done queue, clears
8953541Sshin *       wip[1], and sets its batch ID to 3.  It signals wip[0]'s cv, and
9053541Sshin *       restarts.
9162587Sitojun *
9253541Sshin *   The above process continues until all input files have been consumed.  At
9353541Sshin *   this point, a pair of barriers are used to allow a single thread to move
9495023Ssuz *   any partial batches from the wip array to the done array in batch ID order.
9562587Sitojun *   When this is complete, wq_done_queue is moved to wq_queue, and Phase II
9653541Sshin *   begins.
9753541Sshin *
9862587Sitojun *	Locking Semantics (Phase I)
9962587Sitojun *
100121161Sume *	The input queue (wq_queue) and the done queue (wq_done_queue) are
10153541Sshin *	protected by separate mutexes - wq_queue_lock and wq_done_queue.  wip
10253541Sshin *	array slots are protected by their own mutexes, which must be grabbed
10353541Sshin *	before releasing the input queue lock.  The wip array lock is dropped
104171167Sgnn *	when the thread restarts the loop.  If the array slot was full, the
105105199Ssam *	array lock will be held while the slot contents are added to the done
106171133Sgnn *	queue.  The done queue lock is used to protect the wip slot cv's.
107105199Ssam *
108171167Sgnn *	The pow number is protected by the queue lock.  The master batch ID
109105199Ssam *	and last completed batch (wq_lastdonebatch) counters are protected *in
11053541Sshin *	Phase I* by the done queue lock.
11153541Sshin *
11262587Sitojun *   Phase II
11353541Sshin *
11462587Sitojun *   When Phase II begins, the queue consists of the merged batches from the
115111888Sjlemon *   first phase.  Assume we have five batches:
11662587Sitojun *
11762587Sitojun *	Q:	a b c d e
11853541Sshin *
11978064Sume *   Using the same batch ID mechanism we used in Phase I, but without the wip
12078064Sume *   array, worker threads remove two entries at a time from the beginning of
12162587Sitojun *   the queue.  These two entries are merged, and are added back to the tail
12262587Sitojun *   of the queue, as follows:
12362587Sitojun *
12453541Sshin *	Q:	a b c d e	# start
12578064Sume *	Q:	c d e ab	# a, b removed, merged, added to end
12678064Sume *	Q:	e ab cd		# c, d removed, merged, added to end
127120386Ssam *	Q:	cd eab		# e, ab removed, merged, added to end
12878064Sume *	Q:	cdeab		# cd, eab removed, merged, added to end
12962587Sitojun *
13053541Sshin *   When one entry remains on the queue, with no merges outstanding, Phase II
13162587Sitojun *   finishes.  We pre-determine the stopping point by pre-calculating the
132121673Sume *   number of nodes that will appear on the list.  In the example above, the
13362587Sitojun *   number (wq_ninqueue) is 9.  When ninqueue is 1, we conclude Phase II by
13462587Sitojun *   signaling the main thread via wq_done_cv.
13562587Sitojun *
13653541Sshin *	Locking Semantics (Phase II)
13753541Sshin *
13853541Sshin *	The queue (wq_queue), ninqueue, and the master batch ID and last
13953541Sshin *	completed batch counters are protected by wq_queue_lock.  The done
14053541Sshin *	queue and corresponding lock are unused in Phase II as is the wip array.
14153541Sshin *
14253541Sshin *   Uniquification
14353541Sshin *
14453541Sshin *   We want the CTF data that goes into a given module to be as small as
14578064Sume *   possible.  For example, we don't want it to contain any type data that may
14678064Sume *   be present in another common module.  As such, after creating the master
14753541Sshin *   tdata_t for a given module, we can, if requested by the user, uniquify it
14878064Sume *   against the tdata_t from another module (genunix in the case of the SunOS
14978064Sume *   kernel).  We perform a merge between the tdata_t for this module and the
15078064Sume *   tdata_t from genunix.  Nodes found in this module that are not present in
15178064Sume *   genunix are added to a third tdata_t - the uniquified tdata_t.
15253541Sshin *
15353541Sshin *   Additive Merges
15453541Sshin *
155136689Sandre *   In some cases, for example if we are issuing a new version of a common
156136689Sandre *   module in a patch, we need to make sure that the CTF data already present
15753541Sshin *   in that module does not change.  Changes to this data would void the CTF
15853541Sshin *   data in any module that uniquified against the common module.  To preserve
159136689Sandre *   the existing data, we can perform what is known as an additive merge.  In
160136689Sandre *   this case, a final uniquification is performed against the CTF data in the
161136689Sandre *   previous version of the module.  The result will be the placement of new
162136689Sandre *   and changed data after the existing data, thus preserving the existing type
16353541Sshin *   ID space.
16453541Sshin *
16553541Sshin *   Saving the result
166136689Sandre *
167136689Sandre *   When the merges are complete, the resulting tdata_t is placed into the
168143675Ssam *   output file, replacing the .SUNW_ctf section (if any) already in that file.
169136689Sandre *
170136689Sandre * The person who changes the merging thread code in this file without updating
171134383Sandre * this comment will not live to see the stock hit five.
172134383Sandre */
173120386Ssam
174120386Ssam#include <stdio.h>
175120386Ssam#include <stdlib.h>
176120386Ssam#include <unistd.h>
177120386Ssam#include <pthread.h>
178134383Sandre#include <assert.h>
17953541Sshin#if defined(sun)
18093818Sjhb#include <synch.h>
181122320Ssam#endif
182121161Sume#include <signal.h>
183121742Sume#include <libgen.h>
18453541Sshin#include <string.h>
18553541Sshin#include <errno.h>
186120648Sume#if defined(sun)
18753541Sshin#include <alloca.h>
18853541Sshin#endif
18953541Sshin#include <sys/param.h>
19053541Sshin#include <sys/types.h>
19153541Sshin#include <sys/mman.h>
19253541Sshin#if defined(sun)
19353541Sshin#include <sys/sysconf.h>
19453541Sshin#endif
19578064Sume
19678064Sume#include "ctf_headers.h"
19778064Sume#include "ctftools.h"
19878064Sume#include "ctfmerge.h"
199121806Sume#include "traverse.h"
20078064Sume#include "memory.h"
20178064Sume#include "fifo.h"
20278064Sume#include "barrier.h"
20378064Sume
20453541Sshin#pragma init(bigheap)
20553541Sshin
20653541Sshin#define	MERGE_PHASE1_BATCH_SIZE		8
20755009Sshin#define	MERGE_PHASE1_MAX_SLOTS		5
20855009Sshin#define	MERGE_INPUT_THROTTLE_LEN	10
20953541Sshin
21053541Sshinconst char *progname;
21153541Sshinstatic char *outfile = NULL;
21253541Sshinstatic char *tmpname = NULL;
21353541Sshinstatic int dynsym;
21453541Sshinint debug_level = DEBUG_LEVEL;
21553541Sshinstatic size_t maxpgsize = 0x400000;
21653541Sshin
21753541Sshin
21853541Sshinvoid
21953541Sshinusage(void)
22053541Sshin{
22153541Sshin	(void) fprintf(stderr,
222121143Ssam	    "Usage: %s [-fgstv] -l label | -L labelenv -o outfile file ...\n"
223121143Ssam	    "       %s [-fgstv] -l label | -L labelenv -o outfile -d uniqfile\n"
224121144Ssam	    "       %*s [-g] [-D uniqlabel] file ...\n"
225122320Ssam	    "       %s [-fgstv] -l label | -L labelenv -o outfile -w withfile "
226171133Sgnn	    "file ...\n"
227171167Sgnn	    "       %s [-g] -c srcfile destfile\n"
22853541Sshin	    "\n"
22953541Sshin	    "  Note: if -L labelenv is specified and labelenv is not set in\n"
23053541Sshin	    "  the environment, a default value is used.\n",
231171133Sgnn	    progname, progname, strlen(progname), " ",
23253541Sshin	    progname, progname);
23353541Sshin}
234171133Sgnn
235171133Sgnn#if defined(sun)
236171133Sgnnstatic void
237171167Sgnnbigheap(void)
238171133Sgnn{
23953541Sshin	size_t big, *size;
240121630Sume	int sizes;
24178064Sume	struct memcntl_mha mha;
24278064Sume
24378064Sume	/*
24478064Sume	 * First, get the available pagesizes.
24595023Ssuz	 */
24653541Sshin	if ((sizes = getpagesizes(NULL, 0)) == -1)
24753541Sshin		return;
24853541Sshin
24953541Sshin	if (sizes == 1 || (size = alloca(sizeof (size_t) * sizes)) == NULL)
25053541Sshin		return;
25153541Sshin
25253541Sshin	if (getpagesizes(size, sizes) == -1)
25378064Sume		return;
25453541Sshin
25553541Sshin	while (size[sizes - 1] > maxpgsize)
256120913Sume		sizes--;
25778064Sume
25853541Sshin	/* set big to the largest allowed page size */
25953541Sshin	big = size[sizes - 1];
26053541Sshin	if (big & (big - 1)) {
26153541Sshin		/*
26253541Sshin		 * The largest page size is not a power of two for some
26378064Sume		 * inexplicable reason; return.
26453541Sshin		 */
26553541Sshin		return;
266151474Ssuz	}
267151474Ssuz
268151474Ssuz	/*
269151474Ssuz	 * Now, align our break to the largest page size.
270151474Ssuz	 */
271151474Ssuz	if (brk((void *)((((uintptr_t)sbrk(0) - 1) & ~(big - 1)) + big)) != 0)
27253541Sshin		return;
27353541Sshin
27453541Sshin	/*
27562587Sitojun	 * set the preferred page size for the heap
27674336Skuriyama	 */
27774336Skuriyama	mha.mha_cmd = MHA_MAPSIZE_BSSBRK;
27874336Skuriyama	mha.mha_flags = 0;
27974336Skuriyama	mha.mha_pagesize = big;
28074336Skuriyama
28174336Skuriyama	(void) memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mha, 0, 0);
28274336Skuriyama}
28374336Skuriyama#endif
284111119Simp
28577003Sumestatic void
286108466Ssamfinalize_phase_one(workqueue_t *wq)
287108825Ssam{
288111119Simp	int startslot, i;
28974336Skuriyama
29074336Skuriyama	/*
29174336Skuriyama	 * wip slots are cleared out only when maxbatchsz td's have been merged
29274336Skuriyama	 * into them.  We're not guaranteed that the number of files we're
29374336Skuriyama	 * merging is a multiple of maxbatchsz, so there will be some partial
29495023Ssuz	 * groups in the wip array.  Move them to the done queue in batch ID
29577003Sume	 * order, starting with the slot containing the next batch that would
296120913Sume	 * have been placed on the done queue, followed by the others.
29777003Sume	 * One thread will be doing this while the others wait at the barrier
29874336Skuriyama	 * back in worker_thread(), so we don't need to worry about pesky things
299108825Ssam	 * like locks.
300108825Ssam	 */
30174336Skuriyama
30274336Skuriyama	for (startslot = -1, i = 0; i < wq->wq_nwipslots; i++) {
30374336Skuriyama		if (wq->wq_wip[i].wip_batchid == wq->wq_lastdonebatch + 1) {
304120913Sume			startslot = i;
30562587Sitojun			break;
30653541Sshin		}
30753541Sshin	}
30853541Sshin
30953541Sshin	assert(startslot != -1);
310120913Sume
31153541Sshin	for (i = startslot; i < startslot + wq->wq_nwipslots; i++) {
31253541Sshin		int slotnum = i % wq->wq_nwipslots;
31353541Sshin		wip_t *wipslot = &wq->wq_wip[slotnum];
31453541Sshin
31553541Sshin		if (wipslot->wip_td != NULL) {
31653541Sshin			debug(2, "clearing slot %d (%d) (saving %d)\n",
31753541Sshin			    slotnum, i, wipslot->wip_nmerged);
31853541Sshin		} else
31953541Sshin			debug(2, "clearing slot %d (%d)\n", slotnum, i);
32053541Sshin
32153541Sshin		if (wipslot->wip_td != NULL) {
32253541Sshin			fifo_add(wq->wq_donequeue, wipslot->wip_td);
32353541Sshin			wq->wq_wip[slotnum].wip_td = NULL;
32453541Sshin		}
32553541Sshin	}
32653541Sshin
32753541Sshin	wq->wq_lastdonebatch = wq->wq_next_batchid++;
32878064Sume
32953541Sshin	debug(2, "phase one done: donequeue has %d items\n",
33053541Sshin	    fifo_len(wq->wq_donequeue));
33153541Sshin}
33278064Sume
33378064Sumestatic void
33478064Sumeinit_phase_two(workqueue_t *wq)
33553541Sshin{
33653541Sshin	int num;
33753541Sshin
33853541Sshin	/*
339126444Sume	 * We're going to continually merge the first two entries on the queue,
340126444Sume	 * placing the result on the end, until there's nothing left to merge.
341126444Sume	 * At that point, everything will have been merged into one.  The
342126444Sume	 * initial value of ninqueue needs to be equal to the total number of
343126444Sume	 * entries that will show up on the queue, both at the start of the
344126444Sume	 * phase and as generated by merges during the phase.
345126444Sume	 */
346126444Sume	wq->wq_ninqueue = num = fifo_len(wq->wq_donequeue);
34778064Sume	while (num != 1) {
34878064Sume		wq->wq_ninqueue += num / 2;
34978064Sume		num = num / 2 + num % 2;
35078064Sume	}
35195023Ssuz
352130416Smlaier	/*
353130416Smlaier	 * Move the done queue to the work queue.  We won't be using the done
354130416Smlaier	 * queue in phase 2.
355130416Smlaier	 */
356130416Smlaier	assert(fifo_len(wq->wq_queue) == 0);
357130416Smlaier	fifo_free(wq->wq_queue, NULL);
35862587Sitojun	wq->wq_queue = wq->wq_donequeue;
35978064Sume}
36078064Sume
36178064Sumestatic void
362120913Sumewip_save_work(workqueue_t *wq, wip_t *slot, int slotnum)
36378064Sume{
36478064Sume	pthread_mutex_lock(&wq->wq_donequeue_lock);
36578064Sume
36678064Sume	while (wq->wq_lastdonebatch + 1 < slot->wip_batchid)
36778064Sume		pthread_cond_wait(&slot->wip_cv, &wq->wq_donequeue_lock);
368120913Sume	assert(wq->wq_lastdonebatch + 1 == slot->wip_batchid);
36962587Sitojun
37078064Sume	fifo_add(wq->wq_donequeue, slot->wip_td);
37178064Sume	wq->wq_lastdonebatch++;
37278064Sume	pthread_cond_signal(&wq->wq_wip[(slotnum + 1) %
37378064Sume	    wq->wq_nwipslots].wip_cv);
37478064Sume
37578064Sume	/* reset the slot for next use */
37662587Sitojun	slot->wip_td = NULL;
37762587Sitojun	slot->wip_batchid = wq->wq_next_batchid++;
37862587Sitojun
37962587Sitojun	pthread_mutex_unlock(&wq->wq_donequeue_lock);
38062587Sitojun}
38162587Sitojun
38262587Sitojunstatic void
38362587Sitojunwip_add_work(wip_t *slot, tdata_t *pow)
38462587Sitojun{
38562587Sitojun	if (slot->wip_td == NULL) {
38662587Sitojun		slot->wip_td = pow;
38762587Sitojun		slot->wip_nmerged = 1;
38862587Sitojun	} else {
38962587Sitojun		debug(2, "%d: merging %p into %p\n", pthread_self(),
39062587Sitojun		    (void *)pow, (void *)slot->wip_td);
39175246Sume
392126444Sume		merge_into_master(pow, slot->wip_td, NULL, 0);
393126508Smlaier		tdata_free(pow);
394126508Smlaier
395126508Smlaier		slot->wip_nmerged++;
396126508Smlaier	}
397126508Smlaier}
398126508Smlaier
399126508Smlaierstatic void
400134383Sandreworker_runphase1(workqueue_t *wq)
401134383Sandre{
402155201Scsjp	wip_t *wipslot;
403134383Sandre	tdata_t *pow;
404134383Sandre	int wipslotnum, pownum;
405135920Smlaier
406126508Smlaier	for (;;) {
407126508Smlaier		pthread_mutex_lock(&wq->wq_queue_lock);
408126508Smlaier
409126508Smlaier		while (fifo_empty(wq->wq_queue)) {
410126508Smlaier			if (wq->wq_nomorefiles == 1) {
411126508Smlaier				pthread_cond_broadcast(&wq->wq_work_avail);
412134383Sandre				pthread_mutex_unlock(&wq->wq_queue_lock);
413126508Smlaier
414154804Sume				/* on to phase 2 ... */
415154804Sume				return;
416154804Sume			}
417154804Sume
418154804Sume			pthread_cond_wait(&wq->wq_work_avail,
419154804Sume			    &wq->wq_queue_lock);
420154804Sume		}
421154804Sume
422154804Sume		/* there's work to be done! */
423154804Sume		pow = fifo_remove(wq->wq_queue);
424154804Sume		pownum = wq->wq_nextpownum++;
425154804Sume		pthread_cond_broadcast(&wq->wq_work_removed);
426154804Sume
427154804Sume		assert(pow != NULL);
428154804Sume
429154804Sume		/* merge it into the right slot */
430154804Sume		wipslotnum = pownum % wq->wq_nwipslots;
431154804Sume		wipslot = &wq->wq_wip[wipslotnum];
432154804Sume
433154804Sume		pthread_mutex_lock(&wipslot->wip_lock);
43453541Sshin
43553541Sshin		pthread_mutex_unlock(&wq->wq_queue_lock);
43653541Sshin
437120913Sume		wip_add_work(wipslot, pow);
43853541Sshin
43953541Sshin		if (wipslot->wip_nmerged == wq->wq_maxbatchsz)
44053541Sshin			wip_save_work(wq, wipslot, wipslotnum);
44153541Sshin
44253541Sshin		pthread_mutex_unlock(&wipslot->wip_lock);
44353541Sshin	}
44453541Sshin}
44553541Sshin
44653541Sshinstatic void
44756723Sshinworker_runphase2(workqueue_t *wq)
44853541Sshin{
44953541Sshin	tdata_t *pow1, *pow2;
45053541Sshin	int batchid;
45153541Sshin
45253541Sshin	for (;;) {
45353541Sshin		pthread_mutex_lock(&wq->wq_queue_lock);
45453541Sshin
45553541Sshin		if (wq->wq_ninqueue == 1) {
45653541Sshin			pthread_cond_broadcast(&wq->wq_work_avail);
45753541Sshin			pthread_mutex_unlock(&wq->wq_queue_lock);
45853541Sshin
45953541Sshin			debug(2, "%d: entering p2 completion barrier\n",
46062587Sitojun			    pthread_self());
461120913Sume			if (barrier_wait(&wq->wq_bar1)) {
46262587Sitojun				pthread_mutex_lock(&wq->wq_queue_lock);
463120913Sume				wq->wq_alldone = 1;
46462587Sitojun				pthread_cond_signal(&wq->wq_alldone_cv);
46562587Sitojun				pthread_mutex_unlock(&wq->wq_queue_lock);
46678064Sume			}
46778064Sume
46853541Sshin			return;
46962587Sitojun		}
47062587Sitojun
47153541Sshin		if (fifo_len(wq->wq_queue) < 2) {
47253541Sshin			pthread_cond_wait(&wq->wq_work_avail,
47353541Sshin			    &wq->wq_queue_lock);
47462587Sitojun			pthread_mutex_unlock(&wq->wq_queue_lock);
47553541Sshin			continue;
47678064Sume		}
47778064Sume
47878064Sume		/* there's work to be done! */
47978064Sume		pow1 = fifo_remove(wq->wq_queue);
48053541Sshin		pow2 = fifo_remove(wq->wq_queue);
481122921Sandre		wq->wq_ninqueue -= 2;
48253541Sshin
48353541Sshin		batchid = wq->wq_next_batchid++;
48453541Sshin
48553541Sshin		pthread_mutex_unlock(&wq->wq_queue_lock);
48653541Sshin
48753541Sshin		debug(2, "%d: merging %p into %p\n", pthread_self(),
48853541Sshin		    (void *)pow1, (void *)pow2);
48953541Sshin		merge_into_master(pow1, pow2, NULL, 0);
49053541Sshin		tdata_free(pow1);
49153541Sshin
49253541Sshin		/*
49353541Sshin		 * merging is complete.  place at the tail of the queue in
49478064Sume		 * proper order.
49578064Sume		 */
49678064Sume		pthread_mutex_lock(&wq->wq_queue_lock);
49778064Sume		while (wq->wq_lastdonebatch + 1 != batchid) {
49878064Sume			pthread_cond_wait(&wq->wq_done_cv,
49978064Sume			    &wq->wq_queue_lock);
50078064Sume		}
50178064Sume
50278064Sume		wq->wq_lastdonebatch = batchid;
50378064Sume
50478064Sume		fifo_add(wq->wq_queue, pow2);
50553541Sshin		debug(2, "%d: added %p to queue, len now %d, ninqueue %d\n",
50653541Sshin		    pthread_self(), (void *)pow2, fifo_len(wq->wq_queue),
50753541Sshin		    wq->wq_ninqueue);
50853541Sshin		pthread_cond_broadcast(&wq->wq_done_cv);
50978064Sume		pthread_cond_signal(&wq->wq_work_avail);
51078064Sume		pthread_mutex_unlock(&wq->wq_queue_lock);
51178064Sume	}
51278064Sume}
51378064Sume
51478064Sume/*
51562587Sitojun * Main loop for worker threads.
51653541Sshin */
51762587Sitojunstatic void
51862587Sitojunworker_thread(workqueue_t *wq)
51962587Sitojun{
52053541Sshin	worker_runphase1(wq);
52162587Sitojun
522120913Sume	debug(2, "%d: entering first barrier\n", pthread_self());
52362587Sitojun
52453541Sshin	if (barrier_wait(&wq->wq_bar1)) {
52553541Sshin
52653541Sshin		debug(2, "%d: doing work in first barrier\n", pthread_self());
52778064Sume
52862587Sitojun		finalize_phase_one(wq);
529121630Sume
53078064Sume		init_phase_two(wq);
53178064Sume
53278064Sume		debug(2, "%d: ninqueue is %d, %d on queue\n", pthread_self(),
53378064Sume		    wq->wq_ninqueue, fifo_len(wq->wq_queue));
53462587Sitojun	}
53562587Sitojun
53662587Sitojun	debug(2, "%d: entering second barrier\n", pthread_self());
53753541Sshin
53862587Sitojun	(void) barrier_wait(&wq->wq_bar2);
53953541Sshin
54053541Sshin	debug(2, "%d: phase 1 complete\n", pthread_self());
54167334Sjoe
54267334Sjoe	worker_runphase2(wq);
54367334Sjoe}
54453541Sshin
54553541Sshin/*
546165118Sbz * Pass a tdata_t tree, built from an input file, off to the work queue for
547165118Sbz * consumption by worker threads.
54862587Sitojun */
54978064Sumestatic int
55078064Sumemerge_ctf_cb(tdata_t *td, char *name, void *arg)
551165118Sbz{
552165118Sbz	workqueue_t *wq = arg;
55362587Sitojun
55462587Sitojun	debug(3, "Adding tdata %p for processing\n", (void *)td);
55553541Sshin
55653541Sshin	pthread_mutex_lock(&wq->wq_queue_lock);
55753541Sshin	while (fifo_len(wq->wq_queue) > wq->wq_ithrottle) {
55853541Sshin		debug(2, "Throttling input (len = %d, throttle = %d)\n",
559120913Sume		    fifo_len(wq->wq_queue), wq->wq_ithrottle);
56053541Sshin		pthread_cond_wait(&wq->wq_work_removed, &wq->wq_queue_lock);
56153541Sshin	}
56253541Sshin
56353541Sshin	fifo_add(wq->wq_queue, td);
56453541Sshin	debug(1, "Thread %d announcing %s\n", pthread_self(), name);
56553541Sshin	pthread_cond_broadcast(&wq->wq_work_avail);
56695023Ssuz	pthread_mutex_unlock(&wq->wq_queue_lock);
56753541Sshin
56853541Sshin	return (1);
56953541Sshin}
57053541Sshin
57153541Sshin/*
57253541Sshin * This program is intended to be invoked from a Makefile, as part of the build.
57353541Sshin * As such, in the event of a failure or user-initiated interrupt (^C), we need
57453541Sshin * to ensure that a subsequent re-make will cause ctfmerge to be executed again.
57553541Sshin * Unfortunately, ctfmerge will usually be invoked directly after (and as part
57653541Sshin * of the same Makefile rule as) a link, and will operate on the linked file
57753541Sshin * in place.  If we merely exit upon receipt of a SIGINT, a subsequent make
57853541Sshin * will notice that the *linked* file is newer than the object files, and thus
57953541Sshin * will not reinvoke ctfmerge.  The only way to ensure that a subsequent make
58053541Sshin * reinvokes ctfmerge, is to remove the file to which we are adding CTF
58153541Sshin * data (confusingly named the output file).  This means that the link will need
58253541Sshin * to happen again, but links are generally fast, and we can't allow the merge
583121630Sume * to be skipped.
58478064Sume *
58578064Sume * Another possibility would be to block SIGINT entirely - to always run to
58678064Sume * completion.  The run time of ctfmerge can, however, be measured in minutes
58778064Sume * in some cases, so this is not a valid option.
58878064Sume */
58978064Sumestatic void
59078064Sumehandle_sig(int sig)
59178064Sume{
59278064Sume	terminate("Caught signal %d - exiting\n", sig);
59378064Sume}
59478064Sume
59578064Sumestatic void
59678064Sumeterminate_cleanup(void)
59778064Sume{
59878064Sume	int dounlink = getenv("CTFMERGE_TERMINATE_NO_UNLINK") ? 0 : 1;
59978064Sume
60078064Sume	if (tmpname != NULL && dounlink)
60178064Sume		unlink(tmpname);
60278064Sume
60378064Sume	if (outfile == NULL)
60453541Sshin		return;
60553541Sshin
60653541Sshin#if !defined(__FreeBSD__)
60753541Sshin	if (dounlink) {
60853541Sshin		fprintf(stderr, "Removing %s\n", outfile);
60953541Sshin		unlink(outfile);
61062587Sitojun	}
61162587Sitojun#endif
61253541Sshin}
61362587Sitojun
61453541Sshinstatic void
61562587Sitojuncopy_ctf_data(char *srcfile, char *destfile, int keep_stabs)
61653541Sshin{
61753541Sshin	tdata_t *srctd;
61862587Sitojun
61953541Sshin	if (read_ctf(&srcfile, 1, NULL, read_ctf_save_cb, &srctd, 1) == 0)
62053541Sshin		terminate("No CTF data found in source file %s\n", srcfile);
62153541Sshin
62253541Sshin	tmpname = mktmpname(destfile, ".ctf");
62395023Ssuz	write_ctf(srctd, destfile, tmpname, CTF_COMPRESS | keep_stabs);
62462587Sitojun	if (rename(tmpname, destfile) != 0) {
62562587Sitojun		terminate("Couldn't rename temp file %s to %s", tmpname,
62662587Sitojun		    destfile);
62762587Sitojun	}
62862587Sitojun	free(tmpname);
62962587Sitojun	tdata_free(srctd);
630120913Sume}
631120913Sume
63262587Sitojunstatic void
63362587Sitojunwq_init(workqueue_t *wq, int nfiles)
63462587Sitojun{
63562587Sitojun	int throttle, nslots, i;
63662587Sitojun
63762587Sitojun	if (getenv("CTFMERGE_MAX_SLOTS"))
63862587Sitojun		nslots = atoi(getenv("CTFMERGE_MAX_SLOTS"));
63962587Sitojun	else
64062587Sitojun		nslots = MERGE_PHASE1_MAX_SLOTS;
64162587Sitojun
64262587Sitojun	if (getenv("CTFMERGE_PHASE1_BATCH_SIZE"))
64362587Sitojun		wq->wq_maxbatchsz = atoi(getenv("CTFMERGE_PHASE1_BATCH_SIZE"));
64462587Sitojun	else
64562587Sitojun		wq->wq_maxbatchsz = MERGE_PHASE1_BATCH_SIZE;
64662587Sitojun
64762587Sitojun	nslots = MIN(nslots, (nfiles + wq->wq_maxbatchsz - 1) /
64862587Sitojun	    wq->wq_maxbatchsz);
64962587Sitojun
65062587Sitojun	wq->wq_wip = xcalloc(sizeof (wip_t) * nslots);
65162587Sitojun	wq->wq_nwipslots = nslots;
65262587Sitojun	wq->wq_nthreads = MIN(sysconf(_SC_NPROCESSORS_ONLN) * 3 / 2, nslots);
65362587Sitojun	wq->wq_thread = xmalloc(sizeof (pthread_t) * wq->wq_nthreads);
65462587Sitojun
655169557Sjinmei	if (getenv("CTFMERGE_INPUT_THROTTLE"))
656169557Sjinmei		throttle = atoi(getenv("CTFMERGE_INPUT_THROTTLE"));
657169557Sjinmei	else
658169557Sjinmei		throttle = MERGE_INPUT_THROTTLE_LEN;
659169557Sjinmei	wq->wq_ithrottle = throttle * wq->wq_nthreads;
66053541Sshin
661169557Sjinmei	debug(1, "Using %d slots, %d threads\n", wq->wq_nwipslots,
662169557Sjinmei	    wq->wq_nthreads);
663169557Sjinmei
664169557Sjinmei	wq->wq_next_batchid = 0;
665169557Sjinmei
666169557Sjinmei	for (i = 0; i < nslots; i++) {
667169557Sjinmei		pthread_mutex_init(&wq->wq_wip[i].wip_lock, NULL);
668169557Sjinmei		wq->wq_wip[i].wip_batchid = wq->wq_next_batchid++;
669169557Sjinmei	}
670169557Sjinmei
671169557Sjinmei	pthread_mutex_init(&wq->wq_queue_lock, NULL);
672169557Sjinmei	wq->wq_queue = fifo_new();
673169557Sjinmei	pthread_cond_init(&wq->wq_work_avail, NULL);
67453541Sshin	pthread_cond_init(&wq->wq_work_removed, NULL);
67553541Sshin	wq->wq_ninqueue = nfiles;
67653541Sshin	wq->wq_nextpownum = 0;
67753541Sshin
67853541Sshin	pthread_mutex_init(&wq->wq_donequeue_lock, NULL);
67953541Sshin	wq->wq_donequeue = fifo_new();
68053541Sshin	wq->wq_lastdonebatch = -1;
68153541Sshin
68253541Sshin	pthread_cond_init(&wq->wq_done_cv, NULL);
68353541Sshin
68453541Sshin	pthread_cond_init(&wq->wq_alldone_cv, NULL);
68553541Sshin	wq->wq_alldone = 0;
68653541Sshin
68753541Sshin	barrier_init(&wq->wq_bar1, wq->wq_nthreads);
68853541Sshin	barrier_init(&wq->wq_bar2, wq->wq_nthreads);
68953541Sshin
69053541Sshin	wq->wq_nomorefiles = 0;
69153541Sshin}
69253541Sshin
69353541Sshinstatic void
69453541Sshinstart_threads(workqueue_t *wq)
69553541Sshin{
69653541Sshin	sigset_t sets;
69753541Sshin	int i;
69853541Sshin
69953541Sshin	sigemptyset(&sets);
70056723Sshin	sigaddset(&sets, SIGINT);
70156723Sshin	sigaddset(&sets, SIGQUIT);
70256723Sshin	sigaddset(&sets, SIGTERM);
70356723Sshin	pthread_sigmask(SIG_BLOCK, &sets, NULL);
70456723Sshin
70556723Sshin	for (i = 0; i < wq->wq_nthreads; i++) {
70656723Sshin		pthread_create(&wq->wq_thread[i], NULL,
70756723Sshin		    (void *(*)(void *))worker_thread, wq);
708166938Sbms	}
709166938Sbms
71056723Sshin#if defined(sun)
71156723Sshin	sigset(SIGINT, handle_sig);
71256723Sshin	sigset(SIGQUIT, handle_sig);
71356723Sshin	sigset(SIGTERM, handle_sig);
71453541Sshin#else
71553541Sshin	signal(SIGINT, handle_sig);
71653541Sshin	signal(SIGQUIT, handle_sig);
71753541Sshin	signal(SIGTERM, handle_sig);
71853541Sshin#endif
719121143Ssam	pthread_sigmask(SIG_UNBLOCK, &sets, NULL);
72053541Sshin}
721121673Sume
72253541Sshinstatic void
72362587Sitojunjoin_threads(workqueue_t *wq)
72462587Sitojun{
72553541Sshin	int i;
72662587Sitojun
72762587Sitojun	for (i = 0; i < wq->wq_nthreads; i++) {
72862587Sitojun		pthread_join(wq->wq_thread[i], NULL);
72962587Sitojun	}
73062587Sitojun}
73162587Sitojun
73262587Sitojunstatic int
73362587Sitojunstrcompare(const void *p1, const void *p2)
73462587Sitojun{
73562587Sitojun	char *s1 = *((char **)p1);
73662587Sitojun	char *s2 = *((char **)p2);
73762587Sitojun
73862587Sitojun	return (strcmp(s1, s2));
73962587Sitojun}
74062587Sitojun
74162587Sitojun/*
74253541Sshin * Core work queue structure; passed to worker threads on thread creation
74353541Sshin * as the main point of coordination.  Allocate as a static structure; we
74453541Sshin * could have put this into a local variable in main, but passing a pointer
74553541Sshin * into your stack to another thread is fragile at best and leads to some
74653541Sshin * hard-to-debug failure modes.
74778064Sume */
74853541Sshinstatic workqueue_t wq;
74953541Sshin
75053541Sshinint
75153541Sshinmain(int argc, char **argv)
75253541Sshin{
75353541Sshin	tdata_t *mstrtd, *savetd;
75453541Sshin	char *uniqfile = NULL, *uniqlabel = NULL;
75553541Sshin	char *withfile = NULL;
75653541Sshin	char *label = NULL;
75753541Sshin	char **ifiles, **tifiles;
75853541Sshin	int verbose = 0, docopy = 0;
75953541Sshin	int write_fuzzy_match = 0;
76053541Sshin	int keep_stabs = 0;
76153541Sshin	int require_ctf = 0;
76253541Sshin	int nifiles, nielems;
76353541Sshin	int c, i, idx, tidx, err;
764171167Sgnn
76578064Sume	progname = basename(argv[0]);
76678064Sume
76778064Sume	if (getenv("CTFMERGE_DEBUG_LEVEL"))
76878064Sume		debug_level = atoi(getenv("CTFMERGE_DEBUG_LEVEL"));
76978064Sume
770171133Sgnn	err = 0;
77178064Sume	while ((c = getopt(argc, argv, ":cd:D:fgl:L:o:tvw:s")) != EOF) {
772171167Sgnn		switch (c) {
77353541Sshin		case 'c':
77453541Sshin			docopy = 1;
77553541Sshin			break;
77653541Sshin		case 'd':
77753541Sshin			/* Uniquify against `uniqfile' */
77853541Sshin			uniqfile = optarg;
77953541Sshin			break;
78053541Sshin		case 'D':
78178064Sume			/* Uniquify against label `uniqlabel' in `uniqfile' */
78278064Sume			uniqlabel = optarg;
78378064Sume			break;
784121673Sume		case 'f':
78578064Sume			write_fuzzy_match = CTF_FUZZY_MATCH;
78678064Sume			break;
78778064Sume		case 'g':
78878064Sume			keep_stabs = CTF_KEEP_STABS;
789121673Sume			break;
79078064Sume		case 'l':
791121673Sume			/* Label merged types with `label' */
792121673Sume			label = optarg;
793121673Sume			break;
794121673Sume		case 'L':
79578064Sume			/* Label merged types with getenv(`label`) */
79678064Sume			if ((label = getenv(optarg)) == NULL)
79778064Sume				label = CTF_DEFAULT_LABEL;
79878064Sume			break;
79978064Sume		case 'o':
80078064Sume			/* Place merged types in CTF section in `outfile' */
801121673Sume			outfile = optarg;
80278064Sume			break;
803121673Sume		case 't':
804121673Sume			/* Insist *all* object files built from C have CTF */
805121673Sume			require_ctf = 1;
80678064Sume			break;
80778064Sume		case 'v':
80878064Sume			/* More debugging information */
80978064Sume			verbose = 1;
81078064Sume			break;
81153541Sshin		case 'w':
81253541Sshin			/* Additive merge with data from `withfile' */
81353541Sshin			withfile = optarg;
81453541Sshin			break;
81553541Sshin		case 's':
81653541Sshin			/* use the dynsym rather than the symtab */
81753541Sshin			dynsym = CTF_USE_DYNSYM;
81853541Sshin			break;
81953541Sshin		default:
82053541Sshin			usage();
82178064Sume			exit(2);
82253541Sshin		}
82353541Sshin	}
82453541Sshin
82553541Sshin	/* Validate arguments */
82653541Sshin	if (docopy) {
82762587Sitojun		if (uniqfile != NULL || uniqlabel != NULL || label != NULL ||
82853541Sshin		    outfile != NULL || withfile != NULL || dynsym != 0)
82953541Sshin			err++;
83053541Sshin
83153541Sshin		if (argc - optind != 2)
83253541Sshin			err++;
83353541Sshin	} else {
83462587Sitojun		if (uniqfile != NULL && withfile != NULL)
83562587Sitojun			err++;
83662587Sitojun
83762587Sitojun		if (uniqlabel != NULL && uniqfile == NULL)
83862587Sitojun			err++;
83962587Sitojun
84062587Sitojun		if (outfile == NULL || label == NULL)
84162587Sitojun			err++;
84262587Sitojun
84362587Sitojun		if (argc - optind == 0)
84462587Sitojun			err++;
84562587Sitojun	}
84662587Sitojun
84762587Sitojun	if (err) {
84862587Sitojun		usage();
84953541Sshin		exit(2);
85053541Sshin	}
85153541Sshin
85253541Sshin	if (getenv("STRIPSTABS_KEEP_STABS") != NULL)
85353541Sshin		keep_stabs = CTF_KEEP_STABS;
85453541Sshin
855120856Sume	if (uniqfile && access(uniqfile, R_OK) != 0) {
85653541Sshin		warning("Uniquification file %s couldn't be opened and "
85753541Sshin		    "will be ignored.\n", uniqfile);
85853541Sshin		uniqfile = NULL;
859120856Sume	}
86053541Sshin	if (withfile && access(withfile, R_OK) != 0) {
86153541Sshin		warning("With file %s couldn't be opened and will be "
86253541Sshin		    "ignored.\n", withfile);
86353541Sshin		withfile = NULL;
86453541Sshin	}
86553541Sshin	if (outfile && access(outfile, R_OK|W_OK) != 0)
86653541Sshin		terminate("Cannot open output file %s for r/w", outfile);
86778064Sume
86878064Sume	/*
86978064Sume	 * This is ugly, but we don't want to have to have a separate tool
87078064Sume	 * (yet) just for copying an ELF section with our specific requirements,
87153541Sshin	 * so we shoe-horn a copier into ctfmerge.
87253541Sshin	 */
87353541Sshin	if (docopy) {
87453541Sshin		copy_ctf_data(argv[optind], argv[optind + 1], keep_stabs);
87553541Sshin
87653541Sshin		exit(0);
87753541Sshin	}
87853541Sshin
87953541Sshin	set_terminate_cleanup(terminate_cleanup);
88053541Sshin
88153541Sshin	/* Sort the input files and strip out duplicates */
88253541Sshin	nifiles = argc - optind;
88353541Sshin	ifiles = xmalloc(sizeof (char *) * nifiles);
88462587Sitojun	tifiles = xmalloc(sizeof (char *) * nifiles);
88578064Sume
88653541Sshin	for (i = 0; i < nifiles; i++)
88753541Sshin		tifiles[i] = argv[optind + i];
88878064Sume	qsort(tifiles, nifiles, sizeof (char *), (int (*)())strcompare);
88978064Sume
89078064Sume	ifiles[0] = tifiles[0];
89178064Sume	for (idx = 0, tidx = 1; tidx < nifiles; tidx++) {
89278064Sume		if (strcmp(ifiles[idx], tifiles[tidx]) != 0)
89378064Sume			ifiles[++idx] = tifiles[tidx];
89478064Sume	}
89578064Sume	nifiles = idx + 1;
89678064Sume
89778064Sume	/* Make sure they all exist */
89878064Sume	if ((nielems = count_files(ifiles, nifiles)) < 0)
899121472Sume		terminate("Some input files were inaccessible\n");
90062587Sitojun
90178064Sume	/* Prepare for the merge */
90278064Sume	wq_init(&wq, nielems);
90378064Sume
90478064Sume	start_threads(&wq);
90578064Sume
90678064Sume	/*
90778064Sume	 * Start the merge
908120913Sume	 *
909120913Sume	 * We're reading everything from each of the object files, so we
910120856Sume	 * don't need to specify labels.
91178064Sume	 */
91278064Sume	if (read_ctf(ifiles, nifiles, NULL, merge_ctf_cb,
91378064Sume	    &wq, require_ctf) == 0) {
91478064Sume		/*
91578064Sume		 * If we're verifying that C files have CTF, it's safe to
91678064Sume		 * assume that in this case, we're building only from assembly
91778064Sume		 * inputs.
91862587Sitojun		 */
91962587Sitojun		if (require_ctf)
92062587Sitojun			exit(0);
92162587Sitojun		terminate("No ctf sections found to merge\n");
92278064Sume	}
92378064Sume
92478064Sume	pthread_mutex_lock(&wq.wq_queue_lock);
925120913Sume	wq.wq_nomorefiles = 1;
926120913Sume	pthread_cond_broadcast(&wq.wq_work_avail);
927120856Sume	pthread_mutex_unlock(&wq.wq_queue_lock);
92878064Sume
92962587Sitojun	pthread_mutex_lock(&wq.wq_queue_lock);
93053541Sshin	while (wq.wq_alldone == 0)
93162587Sitojun		pthread_cond_wait(&wq.wq_alldone_cv, &wq.wq_queue_lock);
93262587Sitojun	pthread_mutex_unlock(&wq.wq_queue_lock);
93378064Sume
93462587Sitojun	join_threads(&wq);
93562587Sitojun
93662587Sitojun	/*
93762587Sitojun	 * All requested files have been merged, with the resulting tree in
93862587Sitojun	 * mstrtd.  savetd is the tree that will be placed into the output file.
939120913Sume	 *
940120913Sume	 * Regardless of whether we're doing a normal uniquification or an
941120856Sume	 * additive merge, we need a type tree that has been uniquified
94262587Sitojun	 * against uniqfile or withfile, as appropriate.
94353541Sshin	 *
94462587Sitojun	 * If we're doing a uniquification, we stuff the resulting tree into
94562587Sitojun	 * outfile.  Otherwise, we add the tree to the tree already in withfile.
94662587Sitojun	 */
94762587Sitojun	assert(fifo_len(wq.wq_queue) == 1);
94862587Sitojun	mstrtd = fifo_remove(wq.wq_queue);
94962587Sitojun
95062587Sitojun	if (verbose || debug_level) {
95162587Sitojun		debug(2, "Statistics for td %p\n", (void *)mstrtd);
95262587Sitojun
95362587Sitojun		iidesc_stats(mstrtd->td_iihash);
95462587Sitojun	}
95562587Sitojun
95662587Sitojun	if (uniqfile != NULL || withfile != NULL) {
95762587Sitojun		char *reffile, *reflabel = NULL;
95862587Sitojun		tdata_t *reftd;
95962587Sitojun
96062587Sitojun		if (uniqfile != NULL) {
96162587Sitojun			reffile = uniqfile;
96262587Sitojun			reflabel = uniqlabel;
963120913Sume		} else
964120913Sume			reffile = withfile;
965120856Sume
96662587Sitojun		if (read_ctf(&reffile, 1, reflabel, read_ctf_save_cb,
96762587Sitojun		    &reftd, require_ctf) == 0) {
96862587Sitojun			terminate("No CTF data found in reference file %s\n",
96962587Sitojun			    reffile);
97062587Sitojun		}
97162587Sitojun
97262587Sitojun		savetd = tdata_new();
97362587Sitojun
97462587Sitojun		if (CTF_TYPE_ISCHILD(reftd->td_nextid))
975120913Sume			terminate("No room for additional types in master\n");
976120913Sume
977120856Sume		savetd->td_nextid = withfile ? reftd->td_nextid :
97862587Sitojun		    CTF_INDEX_TO_TYPE(1, TRUE);
97962587Sitojun		merge_into_master(mstrtd, reftd, savetd, 0);
98062587Sitojun
98162587Sitojun		tdata_label_add(savetd, label, CTF_LABEL_LASTIDX);
98278064Sume
98378064Sume		if (withfile) {
98478064Sume			/*
98578064Sume			 * savetd holds the new data to be added to the withfile
98678064Sume			 */
98778064Sume			tdata_t *withtd = reftd;
98878064Sume
98978064Sume			tdata_merge(withtd, savetd);
990120856Sume
99178064Sume			savetd = withtd;
99278064Sume		} else {
99353541Sshin			char uniqname[MAXPATHLEN];
99453541Sshin			labelent_t *parle;
99553541Sshin
996120856Sume			parle = tdata_label_top(reftd);
99753541Sshin
99853541Sshin			savetd->td_parlabel = xstrdup(parle->le_name);
99953541Sshin
1000120856Sume			strncpy(uniqname, reffile, sizeof (uniqname));
100153541Sshin			uniqname[MAXPATHLEN - 1] = '\0';
100253541Sshin			savetd->td_parname = xstrdup(basename(uniqname));
100353541Sshin		}
100453541Sshin
100553541Sshin	} else {
100653541Sshin		/*
100753541Sshin		 * No post processing.  Write the merged tree as-is into the
100853541Sshin		 * output file.
100953541Sshin		 */
101053541Sshin		tdata_label_free(mstrtd);
101153541Sshin		tdata_label_add(mstrtd, label, CTF_LABEL_LASTIDX);
101253541Sshin
101353541Sshin		savetd = mstrtd;
101453541Sshin	}
101553541Sshin
101653541Sshin	tmpname = mktmpname(outfile, ".ctf");
101778064Sume	write_ctf(savetd, outfile, tmpname,
101878064Sume	    CTF_COMPRESS | write_fuzzy_match | dynsym | keep_stabs);
1019120856Sume	if (rename(tmpname, outfile) != 0)
102078064Sume		terminate("Couldn't rename output temp file %s", tmpname);
102178064Sume	free(tmpname);
1022120856Sume
102378064Sume	return (0);
102478064Sume}
102578064Sume