xref: /freebsd-src/cddl/contrib/opensolaris/tools/ctf/cvt/ctfmerge.c (revision 95ca89cda1a6c4e0ef0b3f765c6563f1db0d23fa)
11673e404SJohn Birrell /*
21673e404SJohn Birrell  * CDDL HEADER START
31673e404SJohn Birrell  *
41673e404SJohn Birrell  * The contents of this file are subject to the terms of the
51673e404SJohn Birrell  * Common Development and Distribution License (the "License").
61673e404SJohn Birrell  * You may not use this file except in compliance with the License.
71673e404SJohn Birrell  *
81673e404SJohn Birrell  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91673e404SJohn Birrell  * or http://www.opensolaris.org/os/licensing.
101673e404SJohn Birrell  * See the License for the specific language governing permissions
111673e404SJohn Birrell  * and limitations under the License.
121673e404SJohn Birrell  *
131673e404SJohn Birrell  * When distributing Covered Code, include this CDDL HEADER in each
141673e404SJohn Birrell  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151673e404SJohn Birrell  * If applicable, add the following below this CDDL HEADER, with the
161673e404SJohn Birrell  * fields enclosed by brackets "[]" replaced with your own identifying
171673e404SJohn Birrell  * information: Portions Copyright [yyyy] [name of copyright owner]
181673e404SJohn Birrell  *
191673e404SJohn Birrell  * CDDL HEADER END
201673e404SJohn Birrell  */
211673e404SJohn Birrell /*
221670a1c2SRui Paulo  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
231673e404SJohn Birrell  * Use is subject to license terms.
241673e404SJohn Birrell  */
251673e404SJohn Birrell 
261673e404SJohn Birrell #pragma ident	"%Z%%M%	%I%	%E% SMI"
271673e404SJohn Birrell 
281673e404SJohn Birrell /*
291673e404SJohn Birrell  * Given several files containing CTF data, merge and uniquify that data into
301673e404SJohn Birrell  * a single CTF section in an output file.
311673e404SJohn Birrell  *
321673e404SJohn Birrell  * Merges can proceed independently.  As such, we perform the merges in parallel
331673e404SJohn Birrell  * using a worker thread model.  A given glob of CTF data (either all of the CTF
341673e404SJohn Birrell  * data from a single input file, or the result of one or more merges) can only
351673e404SJohn Birrell  * be involved in a single merge at any given time, so the process decreases in
361673e404SJohn Birrell  * parallelism, especially towards the end, as more and more files are
371673e404SJohn Birrell  * consolidated, finally resulting in a single merge of two large CTF graphs.
381673e404SJohn Birrell  * Unfortunately, the last merge is also the slowest, as the two graphs being
391673e404SJohn Birrell  * merged are each the product of merges of half of the input files.
401673e404SJohn Birrell  *
411673e404SJohn Birrell  * The algorithm consists of two phases, described in detail below.  The first
421673e404SJohn Birrell  * phase entails the merging of CTF data in groups of eight.  The second phase
431673e404SJohn Birrell  * takes the results of Phase I, and merges them two at a time.  This disparity
441673e404SJohn Birrell  * is due to an observation that the merge time increases at least quadratically
451673e404SJohn Birrell  * with the size of the CTF data being merged.  As such, merges of CTF graphs
461673e404SJohn Birrell  * newly read from input files are much faster than merges of CTF graphs that
471673e404SJohn Birrell  * are themselves the results of prior merges.
481673e404SJohn Birrell  *
491673e404SJohn Birrell  * A further complication is the need to ensure the repeatability of CTF merges.
501673e404SJohn Birrell  * That is, a merge should produce the same output every time, given the same
511673e404SJohn Birrell  * input.  In both phases, this consistency requirement is met by imposing an
521673e404SJohn Birrell  * ordering on the merge process, thus ensuring that a given set of input files
531673e404SJohn Birrell  * are merged in the same order every time.
541673e404SJohn Birrell  *
551673e404SJohn Birrell  *   Phase I
561673e404SJohn Birrell  *
571673e404SJohn Birrell  *   The main thread reads the input files one by one, transforming the CTF
581673e404SJohn Birrell  *   data they contain into tdata structures.  When a given file has been read
591673e404SJohn Birrell  *   and parsed, it is placed on the work queue for retrieval by worker threads.
601673e404SJohn Birrell  *
611673e404SJohn Birrell  *   Central to Phase I is the Work In Progress (wip) array, which is used to
621673e404SJohn Birrell  *   merge batches of files in a predictable order.  Files are read by the main
631673e404SJohn Birrell  *   thread, and are merged into wip array elements in round-robin order.  When
641673e404SJohn Birrell  *   the number of files merged into a given array slot equals the batch size,
651673e404SJohn Birrell  *   the merged CTF graph in that array is added to the done slot in order by
661673e404SJohn Birrell  *   array slot.
671673e404SJohn Birrell  *
681673e404SJohn Birrell  *   For example, consider a case where we have five input files, a batch size
691673e404SJohn Birrell  *   of two, a wip array size of two, and two worker threads (T1 and T2).
701673e404SJohn Birrell  *
711673e404SJohn Birrell  *    1. The wip array elements are assigned initial batch numbers 0 and 1.
721673e404SJohn Birrell  *    2. T1 reads an input file from the input queue (wq_queue).  This is the
731673e404SJohn Birrell  *       first input file, so it is placed into wip[0].  The second file is
741673e404SJohn Birrell  *       similarly read and placed into wip[1].  The wip array slots now contain
751673e404SJohn Birrell  *       one file each (wip_nmerged == 1).
761673e404SJohn Birrell  *    3. T1 reads the third input file, which it merges into wip[0].  The
771673e404SJohn Birrell  *       number of files in wip[0] is equal to the batch size.
781673e404SJohn Birrell  *    4. T2 reads the fourth input file, which it merges into wip[1].  wip[1]
791673e404SJohn Birrell  *       is now full too.
801673e404SJohn Birrell  *    5. T2 attempts to place the contents of wip[1] on the done queue
811673e404SJohn Birrell  *       (wq_done_queue), but it can't, since the batch ID for wip[1] is 1.
821673e404SJohn Birrell  *       Batch 0 needs to be on the done queue before batch 1 can be added, so
831673e404SJohn Birrell  *       T2 blocks on wip[1]'s cv.
841673e404SJohn Birrell  *    6. T1 attempts to place the contents of wip[0] on the done queue, and
851673e404SJohn Birrell  *       succeeds, updating wq_lastdonebatch to 0.  It clears wip[0], and sets
861673e404SJohn Birrell  *       its batch ID to 2.  T1 then signals wip[1]'s cv to awaken T2.
871673e404SJohn Birrell  *    7. T2 wakes up, notices that wq_lastdonebatch is 0, which means that
881673e404SJohn Birrell  *       batch 1 can now be added.  It adds wip[1] to the done queue, clears
891673e404SJohn Birrell  *       wip[1], and sets its batch ID to 3.  It signals wip[0]'s cv, and
901673e404SJohn Birrell  *       restarts.
911673e404SJohn Birrell  *
921673e404SJohn Birrell  *   The above process continues until all input files have been consumed.  At
931673e404SJohn Birrell  *   this point, a pair of barriers are used to allow a single thread to move
941673e404SJohn Birrell  *   any partial batches from the wip array to the done array in batch ID order.
951673e404SJohn Birrell  *   When this is complete, wq_done_queue is moved to wq_queue, and Phase II
961673e404SJohn Birrell  *   begins.
971673e404SJohn Birrell  *
981673e404SJohn Birrell  *	Locking Semantics (Phase I)
991673e404SJohn Birrell  *
1001673e404SJohn Birrell  *	The input queue (wq_queue) and the done queue (wq_done_queue) are
1011673e404SJohn Birrell  *	protected by separate mutexes - wq_queue_lock and wq_done_queue.  wip
1021673e404SJohn Birrell  *	array slots are protected by their own mutexes, which must be grabbed
1031673e404SJohn Birrell  *	before releasing the input queue lock.  The wip array lock is dropped
1041673e404SJohn Birrell  *	when the thread restarts the loop.  If the array slot was full, the
1051673e404SJohn Birrell  *	array lock will be held while the slot contents are added to the done
1061673e404SJohn Birrell  *	queue.  The done queue lock is used to protect the wip slot cv's.
1071673e404SJohn Birrell  *
1081673e404SJohn Birrell  *	The pow number is protected by the queue lock.  The master batch ID
1091673e404SJohn Birrell  *	and last completed batch (wq_lastdonebatch) counters are protected *in
1101673e404SJohn Birrell  *	Phase I* by the done queue lock.
1111673e404SJohn Birrell  *
1121673e404SJohn Birrell  *   Phase II
1131673e404SJohn Birrell  *
1141673e404SJohn Birrell  *   When Phase II begins, the queue consists of the merged batches from the
1151673e404SJohn Birrell  *   first phase.  Assume we have five batches:
1161673e404SJohn Birrell  *
1171673e404SJohn Birrell  *	Q:	a b c d e
1181673e404SJohn Birrell  *
1191673e404SJohn Birrell  *   Using the same batch ID mechanism we used in Phase I, but without the wip
1201673e404SJohn Birrell  *   array, worker threads remove two entries at a time from the beginning of
1211673e404SJohn Birrell  *   the queue.  These two entries are merged, and are added back to the tail
1221673e404SJohn Birrell  *   of the queue, as follows:
1231673e404SJohn Birrell  *
1241673e404SJohn Birrell  *	Q:	a b c d e	# start
1251673e404SJohn Birrell  *	Q:	c d e ab	# a, b removed, merged, added to end
1261673e404SJohn Birrell  *	Q:	e ab cd		# c, d removed, merged, added to end
1271673e404SJohn Birrell  *	Q:	cd eab		# e, ab removed, merged, added to end
1281673e404SJohn Birrell  *	Q:	cdeab		# cd, eab removed, merged, added to end
1291673e404SJohn Birrell  *
1301673e404SJohn Birrell  *   When one entry remains on the queue, with no merges outstanding, Phase II
1311673e404SJohn Birrell  *   finishes.  We pre-determine the stopping point by pre-calculating the
1321673e404SJohn Birrell  *   number of nodes that will appear on the list.  In the example above, the
1331673e404SJohn Birrell  *   number (wq_ninqueue) is 9.  When ninqueue is 1, we conclude Phase II by
1341673e404SJohn Birrell  *   signaling the main thread via wq_done_cv.
1351673e404SJohn Birrell  *
1361673e404SJohn Birrell  *	Locking Semantics (Phase II)
1371673e404SJohn Birrell  *
1381673e404SJohn Birrell  *	The queue (wq_queue), ninqueue, and the master batch ID and last
1391673e404SJohn Birrell  *	completed batch counters are protected by wq_queue_lock.  The done
1401673e404SJohn Birrell  *	queue and corresponding lock are unused in Phase II as is the wip array.
1411673e404SJohn Birrell  *
1421673e404SJohn Birrell  *   Uniquification
1431673e404SJohn Birrell  *
1441673e404SJohn Birrell  *   We want the CTF data that goes into a given module to be as small as
1451673e404SJohn Birrell  *   possible.  For example, we don't want it to contain any type data that may
1461673e404SJohn Birrell  *   be present in another common module.  As such, after creating the master
1471673e404SJohn Birrell  *   tdata_t for a given module, we can, if requested by the user, uniquify it
1481673e404SJohn Birrell  *   against the tdata_t from another module (genunix in the case of the SunOS
1491673e404SJohn Birrell  *   kernel).  We perform a merge between the tdata_t for this module and the
1501673e404SJohn Birrell  *   tdata_t from genunix.  Nodes found in this module that are not present in
1511673e404SJohn Birrell  *   genunix are added to a third tdata_t - the uniquified tdata_t.
1521673e404SJohn Birrell  *
1531673e404SJohn Birrell  *   Additive Merges
1541673e404SJohn Birrell  *
1551673e404SJohn Birrell  *   In some cases, for example if we are issuing a new version of a common
1561673e404SJohn Birrell  *   module in a patch, we need to make sure that the CTF data already present
1571673e404SJohn Birrell  *   in that module does not change.  Changes to this data would void the CTF
1581673e404SJohn Birrell  *   data in any module that uniquified against the common module.  To preserve
1591673e404SJohn Birrell  *   the existing data, we can perform what is known as an additive merge.  In
1601673e404SJohn Birrell  *   this case, a final uniquification is performed against the CTF data in the
1611673e404SJohn Birrell  *   previous version of the module.  The result will be the placement of new
1621673e404SJohn Birrell  *   and changed data after the existing data, thus preserving the existing type
1631673e404SJohn Birrell  *   ID space.
1641673e404SJohn Birrell  *
1651673e404SJohn Birrell  *   Saving the result
1661673e404SJohn Birrell  *
1671673e404SJohn Birrell  *   When the merges are complete, the resulting tdata_t is placed into the
1681673e404SJohn Birrell  *   output file, replacing the .SUNW_ctf section (if any) already in that file.
1691673e404SJohn Birrell  *
1701673e404SJohn Birrell  * The person who changes the merging thread code in this file without updating
1711673e404SJohn Birrell  * this comment will not live to see the stock hit five.
1721673e404SJohn Birrell  */
1731673e404SJohn Birrell 
1741673e404SJohn Birrell #include <stdio.h>
1751673e404SJohn Birrell #include <stdlib.h>
1761673e404SJohn Birrell #include <unistd.h>
1771673e404SJohn Birrell #include <pthread.h>
1781673e404SJohn Birrell #include <assert.h>
179bc96366cSSteven Hartland #ifdef illumos
1801673e404SJohn Birrell #include <synch.h>
1814cc75139SJohn Birrell #endif
1821673e404SJohn Birrell #include <signal.h>
1831673e404SJohn Birrell #include <libgen.h>
1841673e404SJohn Birrell #include <string.h>
1851673e404SJohn Birrell #include <errno.h>
186bc96366cSSteven Hartland #ifdef illumos
1871673e404SJohn Birrell #include <alloca.h>
1884cc75139SJohn Birrell #endif
1891673e404SJohn Birrell #include <sys/param.h>
1901673e404SJohn Birrell #include <sys/types.h>
1911673e404SJohn Birrell #include <sys/mman.h>
192bc96366cSSteven Hartland #ifdef illumos
1931673e404SJohn Birrell #include <sys/sysconf.h>
1944cc75139SJohn Birrell #endif
1951673e404SJohn Birrell 
1961673e404SJohn Birrell #include "ctf_headers.h"
1971673e404SJohn Birrell #include "ctftools.h"
1981673e404SJohn Birrell #include "ctfmerge.h"
1991673e404SJohn Birrell #include "traverse.h"
2001673e404SJohn Birrell #include "memory.h"
2011673e404SJohn Birrell #include "fifo.h"
2021673e404SJohn Birrell #include "barrier.h"
2031673e404SJohn Birrell 
2041673e404SJohn Birrell #pragma init(bigheap)
2051673e404SJohn Birrell 
2061673e404SJohn Birrell #define	MERGE_PHASE1_BATCH_SIZE		8
2071673e404SJohn Birrell #define	MERGE_PHASE1_MAX_SLOTS		5
2081673e404SJohn Birrell #define	MERGE_INPUT_THROTTLE_LEN	10
2091673e404SJohn Birrell 
2101673e404SJohn Birrell const char *progname;
2111673e404SJohn Birrell static char *outfile = NULL;
2121673e404SJohn Birrell static char *tmpname = NULL;
2131673e404SJohn Birrell static int dynsym;
2141673e404SJohn Birrell int debug_level = DEBUG_LEVEL;
2151673e404SJohn Birrell static size_t maxpgsize = 0x400000;
2161673e404SJohn Birrell 
2171673e404SJohn Birrell 
2181673e404SJohn Birrell void
usage(void)2191673e404SJohn Birrell usage(void)
2201673e404SJohn Birrell {
2211673e404SJohn Birrell 	(void) fprintf(stderr,
2221673e404SJohn Birrell 	    "Usage: %s [-fgstv] -l label | -L labelenv -o outfile file ...\n"
2231673e404SJohn Birrell 	    "       %s [-fgstv] -l label | -L labelenv -o outfile -d uniqfile\n"
2241673e404SJohn Birrell 	    "       %*s [-g] [-D uniqlabel] file ...\n"
2251673e404SJohn Birrell 	    "       %s [-fgstv] -l label | -L labelenv -o outfile -w withfile "
2261673e404SJohn Birrell 	    "file ...\n"
2271673e404SJohn Birrell 	    "       %s [-g] -c srcfile destfile\n"
2281673e404SJohn Birrell 	    "\n"
2291673e404SJohn Birrell 	    "  Note: if -L labelenv is specified and labelenv is not set in\n"
2301673e404SJohn Birrell 	    "  the environment, a default value is used.\n",
2311e02cf9bSDimitry Andric 	    progname, progname, (int)strlen(progname), " ",
2321673e404SJohn Birrell 	    progname, progname);
2331673e404SJohn Birrell }
2341673e404SJohn Birrell 
235bc96366cSSteven Hartland #ifdef illumos
2361673e404SJohn Birrell static void
bigheap(void)2371673e404SJohn Birrell bigheap(void)
2381673e404SJohn Birrell {
2391673e404SJohn Birrell 	size_t big, *size;
2401673e404SJohn Birrell 	int sizes;
2411673e404SJohn Birrell 	struct memcntl_mha mha;
2421673e404SJohn Birrell 
2431673e404SJohn Birrell 	/*
2441673e404SJohn Birrell 	 * First, get the available pagesizes.
2451673e404SJohn Birrell 	 */
2461673e404SJohn Birrell 	if ((sizes = getpagesizes(NULL, 0)) == -1)
2471673e404SJohn Birrell 		return;
2481673e404SJohn Birrell 
2491673e404SJohn Birrell 	if (sizes == 1 || (size = alloca(sizeof (size_t) * sizes)) == NULL)
2501673e404SJohn Birrell 		return;
2511673e404SJohn Birrell 
2521673e404SJohn Birrell 	if (getpagesizes(size, sizes) == -1)
2531673e404SJohn Birrell 		return;
2541673e404SJohn Birrell 
2551673e404SJohn Birrell 	while (size[sizes - 1] > maxpgsize)
2561673e404SJohn Birrell 		sizes--;
2571673e404SJohn Birrell 
2581673e404SJohn Birrell 	/* set big to the largest allowed page size */
2591673e404SJohn Birrell 	big = size[sizes - 1];
2601673e404SJohn Birrell 	if (big & (big - 1)) {
2611673e404SJohn Birrell 		/*
2621673e404SJohn Birrell 		 * The largest page size is not a power of two for some
2631673e404SJohn Birrell 		 * inexplicable reason; return.
2641673e404SJohn Birrell 		 */
2651673e404SJohn Birrell 		return;
2661673e404SJohn Birrell 	}
2671673e404SJohn Birrell 
2681673e404SJohn Birrell 	/*
2691673e404SJohn Birrell 	 * Now, align our break to the largest page size.
2701673e404SJohn Birrell 	 */
2711673e404SJohn Birrell 	if (brk((void *)((((uintptr_t)sbrk(0) - 1) & ~(big - 1)) + big)) != 0)
2721673e404SJohn Birrell 		return;
2731673e404SJohn Birrell 
2741673e404SJohn Birrell 	/*
2751673e404SJohn Birrell 	 * set the preferred page size for the heap
2761673e404SJohn Birrell 	 */
2771673e404SJohn Birrell 	mha.mha_cmd = MHA_MAPSIZE_BSSBRK;
2781673e404SJohn Birrell 	mha.mha_flags = 0;
2791673e404SJohn Birrell 	mha.mha_pagesize = big;
2801673e404SJohn Birrell 
2811673e404SJohn Birrell 	(void) memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mha, 0, 0);
2821673e404SJohn Birrell }
283bc96366cSSteven Hartland #endif	/* illumos */
2841673e404SJohn Birrell 
2851673e404SJohn Birrell static void
finalize_phase_one(workqueue_t * wq)2861673e404SJohn Birrell finalize_phase_one(workqueue_t *wq)
2871673e404SJohn Birrell {
2881673e404SJohn Birrell 	int startslot, i;
2891673e404SJohn Birrell 
2901673e404SJohn Birrell 	/*
2911673e404SJohn Birrell 	 * wip slots are cleared out only when maxbatchsz td's have been merged
2921673e404SJohn Birrell 	 * into them.  We're not guaranteed that the number of files we're
2931673e404SJohn Birrell 	 * merging is a multiple of maxbatchsz, so there will be some partial
2941673e404SJohn Birrell 	 * groups in the wip array.  Move them to the done queue in batch ID
2951673e404SJohn Birrell 	 * order, starting with the slot containing the next batch that would
2961673e404SJohn Birrell 	 * have been placed on the done queue, followed by the others.
2971673e404SJohn Birrell 	 * One thread will be doing this while the others wait at the barrier
2981673e404SJohn Birrell 	 * back in worker_thread(), so we don't need to worry about pesky things
2991673e404SJohn Birrell 	 * like locks.
3001673e404SJohn Birrell 	 */
3011673e404SJohn Birrell 
3021673e404SJohn Birrell 	for (startslot = -1, i = 0; i < wq->wq_nwipslots; i++) {
3031673e404SJohn Birrell 		if (wq->wq_wip[i].wip_batchid == wq->wq_lastdonebatch + 1) {
3041673e404SJohn Birrell 			startslot = i;
3051673e404SJohn Birrell 			break;
3061673e404SJohn Birrell 		}
3071673e404SJohn Birrell 	}
3081673e404SJohn Birrell 
3091673e404SJohn Birrell 	assert(startslot != -1);
3101673e404SJohn Birrell 
3111673e404SJohn Birrell 	for (i = startslot; i < startslot + wq->wq_nwipslots; i++) {
3121673e404SJohn Birrell 		int slotnum = i % wq->wq_nwipslots;
3131673e404SJohn Birrell 		wip_t *wipslot = &wq->wq_wip[slotnum];
3141673e404SJohn Birrell 
3151673e404SJohn Birrell 		if (wipslot->wip_td != NULL) {
3161673e404SJohn Birrell 			debug(2, "clearing slot %d (%d) (saving %d)\n",
3171673e404SJohn Birrell 			    slotnum, i, wipslot->wip_nmerged);
3181673e404SJohn Birrell 		} else
3191673e404SJohn Birrell 			debug(2, "clearing slot %d (%d)\n", slotnum, i);
3201673e404SJohn Birrell 
3211673e404SJohn Birrell 		if (wipslot->wip_td != NULL) {
3221673e404SJohn Birrell 			fifo_add(wq->wq_donequeue, wipslot->wip_td);
3231673e404SJohn Birrell 			wq->wq_wip[slotnum].wip_td = NULL;
3241673e404SJohn Birrell 		}
3251673e404SJohn Birrell 	}
3261673e404SJohn Birrell 
3271673e404SJohn Birrell 	wq->wq_lastdonebatch = wq->wq_next_batchid++;
3281673e404SJohn Birrell 
3291673e404SJohn Birrell 	debug(2, "phase one done: donequeue has %d items\n",
3301673e404SJohn Birrell 	    fifo_len(wq->wq_donequeue));
3311673e404SJohn Birrell }
3321673e404SJohn Birrell 
3331673e404SJohn Birrell static void
init_phase_two(workqueue_t * wq)3341673e404SJohn Birrell init_phase_two(workqueue_t *wq)
3351673e404SJohn Birrell {
3361673e404SJohn Birrell 	int num;
3371673e404SJohn Birrell 
3381673e404SJohn Birrell 	/*
3391673e404SJohn Birrell 	 * We're going to continually merge the first two entries on the queue,
3401673e404SJohn Birrell 	 * placing the result on the end, until there's nothing left to merge.
3411673e404SJohn Birrell 	 * At that point, everything will have been merged into one.  The
3421673e404SJohn Birrell 	 * initial value of ninqueue needs to be equal to the total number of
3431673e404SJohn Birrell 	 * entries that will show up on the queue, both at the start of the
3441673e404SJohn Birrell 	 * phase and as generated by merges during the phase.
3451673e404SJohn Birrell 	 */
3461673e404SJohn Birrell 	wq->wq_ninqueue = num = fifo_len(wq->wq_donequeue);
3471673e404SJohn Birrell 	while (num != 1) {
3481673e404SJohn Birrell 		wq->wq_ninqueue += num / 2;
3491673e404SJohn Birrell 		num = num / 2 + num % 2;
3501673e404SJohn Birrell 	}
3511673e404SJohn Birrell 
3521673e404SJohn Birrell 	/*
3531673e404SJohn Birrell 	 * Move the done queue to the work queue.  We won't be using the done
3541673e404SJohn Birrell 	 * queue in phase 2.
3551673e404SJohn Birrell 	 */
3561673e404SJohn Birrell 	assert(fifo_len(wq->wq_queue) == 0);
3571673e404SJohn Birrell 	fifo_free(wq->wq_queue, NULL);
3581673e404SJohn Birrell 	wq->wq_queue = wq->wq_donequeue;
3591673e404SJohn Birrell }
3601673e404SJohn Birrell 
3611673e404SJohn Birrell static void
wip_save_work(workqueue_t * wq,wip_t * slot,int slotnum)3621673e404SJohn Birrell wip_save_work(workqueue_t *wq, wip_t *slot, int slotnum)
3631673e404SJohn Birrell {
3641673e404SJohn Birrell 	pthread_mutex_lock(&wq->wq_donequeue_lock);
3651673e404SJohn Birrell 
3661673e404SJohn Birrell 	while (wq->wq_lastdonebatch + 1 < slot->wip_batchid)
3671673e404SJohn Birrell 		pthread_cond_wait(&slot->wip_cv, &wq->wq_donequeue_lock);
3681673e404SJohn Birrell 	assert(wq->wq_lastdonebatch + 1 == slot->wip_batchid);
3691673e404SJohn Birrell 
3701673e404SJohn Birrell 	fifo_add(wq->wq_donequeue, slot->wip_td);
3711673e404SJohn Birrell 	wq->wq_lastdonebatch++;
3721673e404SJohn Birrell 	pthread_cond_signal(&wq->wq_wip[(slotnum + 1) %
3731673e404SJohn Birrell 	    wq->wq_nwipslots].wip_cv);
3741673e404SJohn Birrell 
3751673e404SJohn Birrell 	/* reset the slot for next use */
3761673e404SJohn Birrell 	slot->wip_td = NULL;
3771673e404SJohn Birrell 	slot->wip_batchid = wq->wq_next_batchid++;
3781673e404SJohn Birrell 
3791673e404SJohn Birrell 	pthread_mutex_unlock(&wq->wq_donequeue_lock);
3801673e404SJohn Birrell }
3811673e404SJohn Birrell 
3821673e404SJohn Birrell static void
wip_add_work(wip_t * slot,tdata_t * pow)3831673e404SJohn Birrell wip_add_work(wip_t *slot, tdata_t *pow)
3841673e404SJohn Birrell {
3851673e404SJohn Birrell 	if (slot->wip_td == NULL) {
3861673e404SJohn Birrell 		slot->wip_td = pow;
3871673e404SJohn Birrell 		slot->wip_nmerged = 1;
3881673e404SJohn Birrell 	} else {
3891673e404SJohn Birrell 		debug(2, "%d: merging %p into %p\n", pthread_self(),
3901673e404SJohn Birrell 		    (void *)pow, (void *)slot->wip_td);
3911673e404SJohn Birrell 
3921673e404SJohn Birrell 		merge_into_master(pow, slot->wip_td, NULL, 0);
3931673e404SJohn Birrell 		tdata_free(pow);
3941673e404SJohn Birrell 
3951673e404SJohn Birrell 		slot->wip_nmerged++;
3961673e404SJohn Birrell 	}
3971673e404SJohn Birrell }
3981673e404SJohn Birrell 
3991673e404SJohn Birrell static void
worker_runphase1(workqueue_t * wq)4001673e404SJohn Birrell worker_runphase1(workqueue_t *wq)
4011673e404SJohn Birrell {
4021673e404SJohn Birrell 	wip_t *wipslot;
4031673e404SJohn Birrell 	tdata_t *pow;
4041673e404SJohn Birrell 	int wipslotnum, pownum;
4051673e404SJohn Birrell 
4061673e404SJohn Birrell 	for (;;) {
4071673e404SJohn Birrell 		pthread_mutex_lock(&wq->wq_queue_lock);
4081673e404SJohn Birrell 
4091673e404SJohn Birrell 		while (fifo_empty(wq->wq_queue)) {
4101673e404SJohn Birrell 			if (wq->wq_nomorefiles == 1) {
4111673e404SJohn Birrell 				pthread_cond_broadcast(&wq->wq_work_avail);
4121673e404SJohn Birrell 				pthread_mutex_unlock(&wq->wq_queue_lock);
4131673e404SJohn Birrell 
4141673e404SJohn Birrell 				/* on to phase 2 ... */
4151673e404SJohn Birrell 				return;
4161673e404SJohn Birrell 			}
4171673e404SJohn Birrell 
4181673e404SJohn Birrell 			pthread_cond_wait(&wq->wq_work_avail,
4191673e404SJohn Birrell 			    &wq->wq_queue_lock);
4201673e404SJohn Birrell 		}
4211673e404SJohn Birrell 
4221673e404SJohn Birrell 		/* there's work to be done! */
4231673e404SJohn Birrell 		pow = fifo_remove(wq->wq_queue);
4241673e404SJohn Birrell 		pownum = wq->wq_nextpownum++;
4251673e404SJohn Birrell 		pthread_cond_broadcast(&wq->wq_work_removed);
4261673e404SJohn Birrell 
4271673e404SJohn Birrell 		assert(pow != NULL);
4281673e404SJohn Birrell 
4291673e404SJohn Birrell 		/* merge it into the right slot */
4301673e404SJohn Birrell 		wipslotnum = pownum % wq->wq_nwipslots;
4311673e404SJohn Birrell 		wipslot = &wq->wq_wip[wipslotnum];
4321673e404SJohn Birrell 
4331673e404SJohn Birrell 		pthread_mutex_lock(&wipslot->wip_lock);
4341673e404SJohn Birrell 
4351673e404SJohn Birrell 		pthread_mutex_unlock(&wq->wq_queue_lock);
4361673e404SJohn Birrell 
4371673e404SJohn Birrell 		wip_add_work(wipslot, pow);
4381673e404SJohn Birrell 
4391673e404SJohn Birrell 		if (wipslot->wip_nmerged == wq->wq_maxbatchsz)
4401673e404SJohn Birrell 			wip_save_work(wq, wipslot, wipslotnum);
4411673e404SJohn Birrell 
4421673e404SJohn Birrell 		pthread_mutex_unlock(&wipslot->wip_lock);
4431673e404SJohn Birrell 	}
4441673e404SJohn Birrell }
4451673e404SJohn Birrell 
4461673e404SJohn Birrell static void
worker_runphase2(workqueue_t * wq)4471673e404SJohn Birrell worker_runphase2(workqueue_t *wq)
4481673e404SJohn Birrell {
4491673e404SJohn Birrell 	tdata_t *pow1, *pow2;
4501673e404SJohn Birrell 	int batchid;
4511673e404SJohn Birrell 
4521673e404SJohn Birrell 	for (;;) {
4531673e404SJohn Birrell 		pthread_mutex_lock(&wq->wq_queue_lock);
4541673e404SJohn Birrell 
4551673e404SJohn Birrell 		if (wq->wq_ninqueue == 1) {
4561673e404SJohn Birrell 			pthread_cond_broadcast(&wq->wq_work_avail);
4571673e404SJohn Birrell 			pthread_mutex_unlock(&wq->wq_queue_lock);
4581673e404SJohn Birrell 
4591673e404SJohn Birrell 			debug(2, "%d: entering p2 completion barrier\n",
4601673e404SJohn Birrell 			    pthread_self());
4611673e404SJohn Birrell 			if (barrier_wait(&wq->wq_bar1)) {
4621673e404SJohn Birrell 				pthread_mutex_lock(&wq->wq_queue_lock);
4631673e404SJohn Birrell 				wq->wq_alldone = 1;
4641673e404SJohn Birrell 				pthread_cond_signal(&wq->wq_alldone_cv);
4651673e404SJohn Birrell 				pthread_mutex_unlock(&wq->wq_queue_lock);
4661673e404SJohn Birrell 			}
4671673e404SJohn Birrell 
4681673e404SJohn Birrell 			return;
4691673e404SJohn Birrell 		}
4701673e404SJohn Birrell 
4711673e404SJohn Birrell 		if (fifo_len(wq->wq_queue) < 2) {
4721673e404SJohn Birrell 			pthread_cond_wait(&wq->wq_work_avail,
4731673e404SJohn Birrell 			    &wq->wq_queue_lock);
4741673e404SJohn Birrell 			pthread_mutex_unlock(&wq->wq_queue_lock);
4751673e404SJohn Birrell 			continue;
4761673e404SJohn Birrell 		}
4771673e404SJohn Birrell 
4781673e404SJohn Birrell 		/* there's work to be done! */
4791673e404SJohn Birrell 		pow1 = fifo_remove(wq->wq_queue);
4801673e404SJohn Birrell 		pow2 = fifo_remove(wq->wq_queue);
4811673e404SJohn Birrell 		wq->wq_ninqueue -= 2;
4821673e404SJohn Birrell 
4831673e404SJohn Birrell 		batchid = wq->wq_next_batchid++;
4841673e404SJohn Birrell 
4851673e404SJohn Birrell 		pthread_mutex_unlock(&wq->wq_queue_lock);
4861673e404SJohn Birrell 
4871673e404SJohn Birrell 		debug(2, "%d: merging %p into %p\n", pthread_self(),
4881673e404SJohn Birrell 		    (void *)pow1, (void *)pow2);
4891673e404SJohn Birrell 		merge_into_master(pow1, pow2, NULL, 0);
4901673e404SJohn Birrell 		tdata_free(pow1);
4911673e404SJohn Birrell 
4921673e404SJohn Birrell 		/*
4931673e404SJohn Birrell 		 * merging is complete.  place at the tail of the queue in
4941673e404SJohn Birrell 		 * proper order.
4951673e404SJohn Birrell 		 */
4961673e404SJohn Birrell 		pthread_mutex_lock(&wq->wq_queue_lock);
4971673e404SJohn Birrell 		while (wq->wq_lastdonebatch + 1 != batchid) {
4981673e404SJohn Birrell 			pthread_cond_wait(&wq->wq_done_cv,
4991673e404SJohn Birrell 			    &wq->wq_queue_lock);
5001673e404SJohn Birrell 		}
5011673e404SJohn Birrell 
5021673e404SJohn Birrell 		wq->wq_lastdonebatch = batchid;
5031673e404SJohn Birrell 
5041673e404SJohn Birrell 		fifo_add(wq->wq_queue, pow2);
5051673e404SJohn Birrell 		debug(2, "%d: added %p to queue, len now %d, ninqueue %d\n",
5061673e404SJohn Birrell 		    pthread_self(), (void *)pow2, fifo_len(wq->wq_queue),
5071673e404SJohn Birrell 		    wq->wq_ninqueue);
5081673e404SJohn Birrell 		pthread_cond_broadcast(&wq->wq_done_cv);
5091673e404SJohn Birrell 		pthread_cond_signal(&wq->wq_work_avail);
5101673e404SJohn Birrell 		pthread_mutex_unlock(&wq->wq_queue_lock);
5111673e404SJohn Birrell 	}
5121673e404SJohn Birrell }
5131673e404SJohn Birrell 
5141673e404SJohn Birrell /*
5151673e404SJohn Birrell  * Main loop for worker threads.
5161673e404SJohn Birrell  */
5171673e404SJohn Birrell static void
worker_thread(workqueue_t * wq)5181673e404SJohn Birrell worker_thread(workqueue_t *wq)
5191673e404SJohn Birrell {
5201673e404SJohn Birrell 	worker_runphase1(wq);
5211673e404SJohn Birrell 
5221673e404SJohn Birrell 	debug(2, "%d: entering first barrier\n", pthread_self());
5231673e404SJohn Birrell 
5241673e404SJohn Birrell 	if (barrier_wait(&wq->wq_bar1)) {
5251673e404SJohn Birrell 
5261673e404SJohn Birrell 		debug(2, "%d: doing work in first barrier\n", pthread_self());
5271673e404SJohn Birrell 
5281673e404SJohn Birrell 		finalize_phase_one(wq);
5291673e404SJohn Birrell 
5301673e404SJohn Birrell 		init_phase_two(wq);
5311673e404SJohn Birrell 
5321673e404SJohn Birrell 		debug(2, "%d: ninqueue is %d, %d on queue\n", pthread_self(),
5331673e404SJohn Birrell 		    wq->wq_ninqueue, fifo_len(wq->wq_queue));
5341673e404SJohn Birrell 	}
5351673e404SJohn Birrell 
5361673e404SJohn Birrell 	debug(2, "%d: entering second barrier\n", pthread_self());
5371673e404SJohn Birrell 
5381673e404SJohn Birrell 	(void) barrier_wait(&wq->wq_bar2);
5391673e404SJohn Birrell 
5401673e404SJohn Birrell 	debug(2, "%d: phase 1 complete\n", pthread_self());
5411673e404SJohn Birrell 
5421673e404SJohn Birrell 	worker_runphase2(wq);
5431673e404SJohn Birrell }
5441673e404SJohn Birrell 
5451673e404SJohn Birrell /*
5461673e404SJohn Birrell  * Pass a tdata_t tree, built from an input file, off to the work queue for
5471673e404SJohn Birrell  * consumption by worker threads.
5481673e404SJohn Birrell  */
5491673e404SJohn Birrell static int
merge_ctf_cb(tdata_t * td,char * name,void * arg)5501673e404SJohn Birrell merge_ctf_cb(tdata_t *td, char *name, void *arg)
5511673e404SJohn Birrell {
5521673e404SJohn Birrell 	workqueue_t *wq = arg;
5531673e404SJohn Birrell 
5541673e404SJohn Birrell 	debug(3, "Adding tdata %p for processing\n", (void *)td);
5551673e404SJohn Birrell 
5561673e404SJohn Birrell 	pthread_mutex_lock(&wq->wq_queue_lock);
5571673e404SJohn Birrell 	while (fifo_len(wq->wq_queue) > wq->wq_ithrottle) {
5581673e404SJohn Birrell 		debug(2, "Throttling input (len = %d, throttle = %d)\n",
5591673e404SJohn Birrell 		    fifo_len(wq->wq_queue), wq->wq_ithrottle);
5601673e404SJohn Birrell 		pthread_cond_wait(&wq->wq_work_removed, &wq->wq_queue_lock);
5611673e404SJohn Birrell 	}
5621673e404SJohn Birrell 
5631673e404SJohn Birrell 	fifo_add(wq->wq_queue, td);
5641673e404SJohn Birrell 	debug(1, "Thread %d announcing %s\n", pthread_self(), name);
5651673e404SJohn Birrell 	pthread_cond_broadcast(&wq->wq_work_avail);
5661673e404SJohn Birrell 	pthread_mutex_unlock(&wq->wq_queue_lock);
5671673e404SJohn Birrell 
5681673e404SJohn Birrell 	return (1);
5691673e404SJohn Birrell }
5701673e404SJohn Birrell 
5711673e404SJohn Birrell /*
5721673e404SJohn Birrell  * This program is intended to be invoked from a Makefile, as part of the build.
5731673e404SJohn Birrell  * As such, in the event of a failure or user-initiated interrupt (^C), we need
5741673e404SJohn Birrell  * to ensure that a subsequent re-make will cause ctfmerge to be executed again.
5751673e404SJohn Birrell  * Unfortunately, ctfmerge will usually be invoked directly after (and as part
5761673e404SJohn Birrell  * of the same Makefile rule as) a link, and will operate on the linked file
5771673e404SJohn Birrell  * in place.  If we merely exit upon receipt of a SIGINT, a subsequent make
5781673e404SJohn Birrell  * will notice that the *linked* file is newer than the object files, and thus
5791673e404SJohn Birrell  * will not reinvoke ctfmerge.  The only way to ensure that a subsequent make
5801673e404SJohn Birrell  * reinvokes ctfmerge, is to remove the file to which we are adding CTF
5811673e404SJohn Birrell  * data (confusingly named the output file).  This means that the link will need
5821673e404SJohn Birrell  * to happen again, but links are generally fast, and we can't allow the merge
5831673e404SJohn Birrell  * to be skipped.
5841673e404SJohn Birrell  *
5851673e404SJohn Birrell  * Another possibility would be to block SIGINT entirely - to always run to
5861673e404SJohn Birrell  * completion.  The run time of ctfmerge can, however, be measured in minutes
5871673e404SJohn Birrell  * in some cases, so this is not a valid option.
5881673e404SJohn Birrell  */
5891673e404SJohn Birrell static void
handle_sig(int sig)5901673e404SJohn Birrell handle_sig(int sig)
5911673e404SJohn Birrell {
5921673e404SJohn Birrell 	terminate("Caught signal %d - exiting\n", sig);
5931673e404SJohn Birrell }
5941673e404SJohn Birrell 
5951673e404SJohn Birrell static void
terminate_cleanup(void)5961673e404SJohn Birrell terminate_cleanup(void)
5971673e404SJohn Birrell {
5981673e404SJohn Birrell 	int dounlink = getenv("CTFMERGE_TERMINATE_NO_UNLINK") ? 0 : 1;
5991673e404SJohn Birrell 
6001673e404SJohn Birrell 	if (tmpname != NULL && dounlink)
6011673e404SJohn Birrell 		unlink(tmpname);
6021673e404SJohn Birrell 
6031673e404SJohn Birrell 	if (outfile == NULL)
6041673e404SJohn Birrell 		return;
6051673e404SJohn Birrell 
6064cc75139SJohn Birrell #if !defined(__FreeBSD__)
6071673e404SJohn Birrell 	if (dounlink) {
6081673e404SJohn Birrell 		fprintf(stderr, "Removing %s\n", outfile);
6091673e404SJohn Birrell 		unlink(outfile);
6101673e404SJohn Birrell 	}
6114cc75139SJohn Birrell #endif
6121673e404SJohn Birrell }
6131673e404SJohn Birrell 
6141673e404SJohn Birrell static void
copy_ctf_data(char * srcfile,char * destfile,int keep_stabs)6151673e404SJohn Birrell copy_ctf_data(char *srcfile, char *destfile, int keep_stabs)
6161673e404SJohn Birrell {
6171673e404SJohn Birrell 	tdata_t *srctd;
6181673e404SJohn Birrell 
6191673e404SJohn Birrell 	if (read_ctf(&srcfile, 1, NULL, read_ctf_save_cb, &srctd, 1) == 0)
6201673e404SJohn Birrell 		terminate("No CTF data found in source file %s\n", srcfile);
6211673e404SJohn Birrell 
6221673e404SJohn Birrell 	tmpname = mktmpname(destfile, ".ctf");
623a6425ab5SOleksandr Tymoshenko 	write_ctf(srctd, destfile, tmpname, CTF_COMPRESS | CTF_SWAP_BYTES | keep_stabs);
6241673e404SJohn Birrell 	if (rename(tmpname, destfile) != 0) {
6251673e404SJohn Birrell 		terminate("Couldn't rename temp file %s to %s", tmpname,
6261673e404SJohn Birrell 		    destfile);
6271673e404SJohn Birrell 	}
6281673e404SJohn Birrell 	free(tmpname);
6291673e404SJohn Birrell 	tdata_free(srctd);
6301673e404SJohn Birrell }
6311673e404SJohn Birrell 
6321673e404SJohn Birrell static void
wq_init(workqueue_t * wq,int nfiles)6331673e404SJohn Birrell wq_init(workqueue_t *wq, int nfiles)
6341673e404SJohn Birrell {
6351673e404SJohn Birrell 	int throttle, nslots, i;
6361673e404SJohn Birrell 
6371673e404SJohn Birrell 	if (getenv("CTFMERGE_MAX_SLOTS"))
6381673e404SJohn Birrell 		nslots = atoi(getenv("CTFMERGE_MAX_SLOTS"));
6391673e404SJohn Birrell 	else
6401673e404SJohn Birrell 		nslots = MERGE_PHASE1_MAX_SLOTS;
6411673e404SJohn Birrell 
6421673e404SJohn Birrell 	if (getenv("CTFMERGE_PHASE1_BATCH_SIZE"))
6431673e404SJohn Birrell 		wq->wq_maxbatchsz = atoi(getenv("CTFMERGE_PHASE1_BATCH_SIZE"));
6441673e404SJohn Birrell 	else
6451673e404SJohn Birrell 		wq->wq_maxbatchsz = MERGE_PHASE1_BATCH_SIZE;
6461673e404SJohn Birrell 
6471673e404SJohn Birrell 	nslots = MIN(nslots, (nfiles + wq->wq_maxbatchsz - 1) /
6481673e404SJohn Birrell 	    wq->wq_maxbatchsz);
6491673e404SJohn Birrell 
6501673e404SJohn Birrell 	wq->wq_wip = xcalloc(sizeof (wip_t) * nslots);
6511673e404SJohn Birrell 	wq->wq_nwipslots = nslots;
6521673e404SJohn Birrell 	wq->wq_nthreads = MIN(sysconf(_SC_NPROCESSORS_ONLN) * 3 / 2, nslots);
6531670a1c2SRui Paulo 	wq->wq_thread = xmalloc(sizeof (pthread_t) * wq->wq_nthreads);
6541673e404SJohn Birrell 
6551673e404SJohn Birrell 	if (getenv("CTFMERGE_INPUT_THROTTLE"))
6561673e404SJohn Birrell 		throttle = atoi(getenv("CTFMERGE_INPUT_THROTTLE"));
6571673e404SJohn Birrell 	else
6581673e404SJohn Birrell 		throttle = MERGE_INPUT_THROTTLE_LEN;
6591673e404SJohn Birrell 	wq->wq_ithrottle = throttle * wq->wq_nthreads;
6601673e404SJohn Birrell 
6611673e404SJohn Birrell 	debug(1, "Using %d slots, %d threads\n", wq->wq_nwipslots,
6621673e404SJohn Birrell 	    wq->wq_nthreads);
6631673e404SJohn Birrell 
6641673e404SJohn Birrell 	wq->wq_next_batchid = 0;
6651673e404SJohn Birrell 
6661673e404SJohn Birrell 	for (i = 0; i < nslots; i++) {
6671673e404SJohn Birrell 		pthread_mutex_init(&wq->wq_wip[i].wip_lock, NULL);
6685ac01ce0SAlex Richardson 		pthread_cond_init(&wq->wq_wip[i].wip_cv, NULL);
6691673e404SJohn Birrell 		wq->wq_wip[i].wip_batchid = wq->wq_next_batchid++;
6701673e404SJohn Birrell 	}
6711673e404SJohn Birrell 
6721673e404SJohn Birrell 	pthread_mutex_init(&wq->wq_queue_lock, NULL);
6731673e404SJohn Birrell 	wq->wq_queue = fifo_new();
6741673e404SJohn Birrell 	pthread_cond_init(&wq->wq_work_avail, NULL);
6751673e404SJohn Birrell 	pthread_cond_init(&wq->wq_work_removed, NULL);
6761673e404SJohn Birrell 	wq->wq_ninqueue = nfiles;
6771673e404SJohn Birrell 	wq->wq_nextpownum = 0;
6781673e404SJohn Birrell 
6791673e404SJohn Birrell 	pthread_mutex_init(&wq->wq_donequeue_lock, NULL);
6801673e404SJohn Birrell 	wq->wq_donequeue = fifo_new();
6811673e404SJohn Birrell 	wq->wq_lastdonebatch = -1;
6821673e404SJohn Birrell 
6831673e404SJohn Birrell 	pthread_cond_init(&wq->wq_done_cv, NULL);
6841673e404SJohn Birrell 
6851673e404SJohn Birrell 	pthread_cond_init(&wq->wq_alldone_cv, NULL);
6861673e404SJohn Birrell 	wq->wq_alldone = 0;
6871673e404SJohn Birrell 
6881673e404SJohn Birrell 	barrier_init(&wq->wq_bar1, wq->wq_nthreads);
6891673e404SJohn Birrell 	barrier_init(&wq->wq_bar2, wq->wq_nthreads);
6901673e404SJohn Birrell 
6911673e404SJohn Birrell 	wq->wq_nomorefiles = 0;
6921673e404SJohn Birrell }
6931673e404SJohn Birrell 
6941673e404SJohn Birrell static void
start_threads(workqueue_t * wq)6951673e404SJohn Birrell start_threads(workqueue_t *wq)
6961673e404SJohn Birrell {
6971673e404SJohn Birrell 	sigset_t sets;
6981673e404SJohn Birrell 	int i;
6991673e404SJohn Birrell 
7001673e404SJohn Birrell 	sigemptyset(&sets);
7011673e404SJohn Birrell 	sigaddset(&sets, SIGINT);
7021673e404SJohn Birrell 	sigaddset(&sets, SIGQUIT);
7031673e404SJohn Birrell 	sigaddset(&sets, SIGTERM);
7041673e404SJohn Birrell 	pthread_sigmask(SIG_BLOCK, &sets, NULL);
7051673e404SJohn Birrell 
7061673e404SJohn Birrell 	for (i = 0; i < wq->wq_nthreads; i++) {
7071670a1c2SRui Paulo 		pthread_create(&wq->wq_thread[i], NULL,
7081670a1c2SRui Paulo 		    (void *(*)(void *))worker_thread, wq);
7091673e404SJohn Birrell 	}
7101673e404SJohn Birrell 
711bc96366cSSteven Hartland #ifdef illumos
7121673e404SJohn Birrell 	sigset(SIGINT, handle_sig);
7131673e404SJohn Birrell 	sigset(SIGQUIT, handle_sig);
7141673e404SJohn Birrell 	sigset(SIGTERM, handle_sig);
7154cc75139SJohn Birrell #else
7164cc75139SJohn Birrell 	signal(SIGINT, handle_sig);
7174cc75139SJohn Birrell 	signal(SIGQUIT, handle_sig);
7184cc75139SJohn Birrell 	signal(SIGTERM, handle_sig);
7194cc75139SJohn Birrell #endif
7201673e404SJohn Birrell 	pthread_sigmask(SIG_UNBLOCK, &sets, NULL);
7211673e404SJohn Birrell }
7221673e404SJohn Birrell 
7231670a1c2SRui Paulo static void
join_threads(workqueue_t * wq)7241670a1c2SRui Paulo join_threads(workqueue_t *wq)
7251670a1c2SRui Paulo {
7261670a1c2SRui Paulo 	int i;
7271670a1c2SRui Paulo 
7281670a1c2SRui Paulo 	for (i = 0; i < wq->wq_nthreads; i++) {
7291670a1c2SRui Paulo 		pthread_join(wq->wq_thread[i], NULL);
7301670a1c2SRui Paulo 	}
7311670a1c2SRui Paulo }
7321670a1c2SRui Paulo 
7331673e404SJohn Birrell static int
strcompare(const void * p1,const void * p2)7341673e404SJohn Birrell strcompare(const void *p1, const void *p2)
7351673e404SJohn Birrell {
7361673e404SJohn Birrell 	char *s1 = *((char **)p1);
7371673e404SJohn Birrell 	char *s2 = *((char **)p2);
7381673e404SJohn Birrell 
7391673e404SJohn Birrell 	return (strcmp(s1, s2));
7401673e404SJohn Birrell }
7411673e404SJohn Birrell 
7421670a1c2SRui Paulo /*
7431670a1c2SRui Paulo  * Core work queue structure; passed to worker threads on thread creation
7441670a1c2SRui Paulo  * as the main point of coordination.  Allocate as a static structure; we
7451670a1c2SRui Paulo  * could have put this into a local variable in main, but passing a pointer
7461670a1c2SRui Paulo  * into your stack to another thread is fragile at best and leads to some
7471670a1c2SRui Paulo  * hard-to-debug failure modes.
7481670a1c2SRui Paulo  */
7491670a1c2SRui Paulo static workqueue_t wq;
7501670a1c2SRui Paulo 
7511673e404SJohn Birrell int
main(int argc,char ** argv)7521673e404SJohn Birrell main(int argc, char **argv)
7531673e404SJohn Birrell {
7541673e404SJohn Birrell 	tdata_t *mstrtd, *savetd;
7551673e404SJohn Birrell 	char *uniqfile = NULL, *uniqlabel = NULL;
7561673e404SJohn Birrell 	char *withfile = NULL;
7571673e404SJohn Birrell 	char *label = NULL;
7581673e404SJohn Birrell 	char **ifiles, **tifiles;
7591673e404SJohn Birrell 	int verbose = 0, docopy = 0;
7601673e404SJohn Birrell 	int write_fuzzy_match = 0;
7611673e404SJohn Birrell 	int keep_stabs = 0;
7621673e404SJohn Birrell 	int require_ctf = 0;
7631673e404SJohn Birrell 	int nifiles, nielems;
7641673e404SJohn Birrell 	int c, i, idx, tidx, err;
7651673e404SJohn Birrell 
7661673e404SJohn Birrell 	progname = basename(argv[0]);
7671673e404SJohn Birrell 
7681673e404SJohn Birrell 	if (getenv("CTFMERGE_DEBUG_LEVEL"))
7691673e404SJohn Birrell 		debug_level = atoi(getenv("CTFMERGE_DEBUG_LEVEL"));
7701673e404SJohn Birrell 
7711673e404SJohn Birrell 	err = 0;
7721673e404SJohn Birrell 	while ((c = getopt(argc, argv, ":cd:D:fgl:L:o:tvw:s")) != EOF) {
7731673e404SJohn Birrell 		switch (c) {
7741673e404SJohn Birrell 		case 'c':
7751673e404SJohn Birrell 			docopy = 1;
7761673e404SJohn Birrell 			break;
7771673e404SJohn Birrell 		case 'd':
7781673e404SJohn Birrell 			/* Uniquify against `uniqfile' */
7791673e404SJohn Birrell 			uniqfile = optarg;
7801673e404SJohn Birrell 			break;
7811673e404SJohn Birrell 		case 'D':
7821673e404SJohn Birrell 			/* Uniquify against label `uniqlabel' in `uniqfile' */
7831673e404SJohn Birrell 			uniqlabel = optarg;
7841673e404SJohn Birrell 			break;
7851673e404SJohn Birrell 		case 'f':
7861673e404SJohn Birrell 			write_fuzzy_match = CTF_FUZZY_MATCH;
7871673e404SJohn Birrell 			break;
7881673e404SJohn Birrell 		case 'g':
7891673e404SJohn Birrell 			keep_stabs = CTF_KEEP_STABS;
7901673e404SJohn Birrell 			break;
7911673e404SJohn Birrell 		case 'l':
7921673e404SJohn Birrell 			/* Label merged types with `label' */
7931673e404SJohn Birrell 			label = optarg;
7941673e404SJohn Birrell 			break;
7951673e404SJohn Birrell 		case 'L':
7961673e404SJohn Birrell 			/* Label merged types with getenv(`label`) */
7971673e404SJohn Birrell 			if ((label = getenv(optarg)) == NULL)
7981673e404SJohn Birrell 				label = CTF_DEFAULT_LABEL;
7991673e404SJohn Birrell 			break;
8001673e404SJohn Birrell 		case 'o':
8011673e404SJohn Birrell 			/* Place merged types in CTF section in `outfile' */
8021673e404SJohn Birrell 			outfile = optarg;
8031673e404SJohn Birrell 			break;
8041673e404SJohn Birrell 		case 't':
8051673e404SJohn Birrell 			/* Insist *all* object files built from C have CTF */
8061673e404SJohn Birrell 			require_ctf = 1;
8071673e404SJohn Birrell 			break;
8081673e404SJohn Birrell 		case 'v':
8091673e404SJohn Birrell 			/* More debugging information */
8101673e404SJohn Birrell 			verbose = 1;
8111673e404SJohn Birrell 			break;
8121673e404SJohn Birrell 		case 'w':
8131673e404SJohn Birrell 			/* Additive merge with data from `withfile' */
8141673e404SJohn Birrell 			withfile = optarg;
8151673e404SJohn Birrell 			break;
8161673e404SJohn Birrell 		case 's':
8171673e404SJohn Birrell 			/* use the dynsym rather than the symtab */
8181673e404SJohn Birrell 			dynsym = CTF_USE_DYNSYM;
8191673e404SJohn Birrell 			break;
8201673e404SJohn Birrell 		default:
8211673e404SJohn Birrell 			usage();
8221673e404SJohn Birrell 			exit(2);
8231673e404SJohn Birrell 		}
8241673e404SJohn Birrell 	}
8251673e404SJohn Birrell 
8261673e404SJohn Birrell 	/* Validate arguments */
8271673e404SJohn Birrell 	if (docopy) {
8281673e404SJohn Birrell 		if (uniqfile != NULL || uniqlabel != NULL || label != NULL ||
8291673e404SJohn Birrell 		    outfile != NULL || withfile != NULL || dynsym != 0)
8301673e404SJohn Birrell 			err++;
8311673e404SJohn Birrell 
8321673e404SJohn Birrell 		if (argc - optind != 2)
8331673e404SJohn Birrell 			err++;
8341673e404SJohn Birrell 	} else {
8351673e404SJohn Birrell 		if (uniqfile != NULL && withfile != NULL)
8361673e404SJohn Birrell 			err++;
8371673e404SJohn Birrell 
8381673e404SJohn Birrell 		if (uniqlabel != NULL && uniqfile == NULL)
8391673e404SJohn Birrell 			err++;
8401673e404SJohn Birrell 
8411673e404SJohn Birrell 		if (outfile == NULL || label == NULL)
8421673e404SJohn Birrell 			err++;
8431673e404SJohn Birrell 
8441673e404SJohn Birrell 		if (argc - optind == 0)
8451673e404SJohn Birrell 			err++;
8461673e404SJohn Birrell 	}
8471673e404SJohn Birrell 
8481673e404SJohn Birrell 	if (err) {
8491673e404SJohn Birrell 		usage();
8501673e404SJohn Birrell 		exit(2);
8511673e404SJohn Birrell 	}
8521673e404SJohn Birrell 
8531673e404SJohn Birrell 	if (getenv("STRIPSTABS_KEEP_STABS") != NULL)
8541673e404SJohn Birrell 		keep_stabs = CTF_KEEP_STABS;
8551673e404SJohn Birrell 
8561673e404SJohn Birrell 	if (uniqfile && access(uniqfile, R_OK) != 0) {
8571673e404SJohn Birrell 		warning("Uniquification file %s couldn't be opened and "
8581673e404SJohn Birrell 		    "will be ignored.\n", uniqfile);
8591673e404SJohn Birrell 		uniqfile = NULL;
8601673e404SJohn Birrell 	}
8611673e404SJohn Birrell 	if (withfile && access(withfile, R_OK) != 0) {
8621673e404SJohn Birrell 		warning("With file %s couldn't be opened and will be "
8631673e404SJohn Birrell 		    "ignored.\n", withfile);
8641673e404SJohn Birrell 		withfile = NULL;
8651673e404SJohn Birrell 	}
8661673e404SJohn Birrell 	if (outfile && access(outfile, R_OK|W_OK) != 0)
8671673e404SJohn Birrell 		terminate("Cannot open output file %s for r/w", outfile);
8681673e404SJohn Birrell 
8691673e404SJohn Birrell 	/*
8701673e404SJohn Birrell 	 * This is ugly, but we don't want to have to have a separate tool
8711673e404SJohn Birrell 	 * (yet) just for copying an ELF section with our specific requirements,
8721673e404SJohn Birrell 	 * so we shoe-horn a copier into ctfmerge.
8731673e404SJohn Birrell 	 */
8741673e404SJohn Birrell 	if (docopy) {
8751673e404SJohn Birrell 		copy_ctf_data(argv[optind], argv[optind + 1], keep_stabs);
8761673e404SJohn Birrell 
8771673e404SJohn Birrell 		exit(0);
8781673e404SJohn Birrell 	}
8791673e404SJohn Birrell 
8801673e404SJohn Birrell 	set_terminate_cleanup(terminate_cleanup);
8811673e404SJohn Birrell 
8821673e404SJohn Birrell 	/* Sort the input files and strip out duplicates */
8831673e404SJohn Birrell 	nifiles = argc - optind;
8841673e404SJohn Birrell 	ifiles = xmalloc(sizeof (char *) * nifiles);
8851673e404SJohn Birrell 	tifiles = xmalloc(sizeof (char *) * nifiles);
8861673e404SJohn Birrell 
8871673e404SJohn Birrell 	for (i = 0; i < nifiles; i++)
8881673e404SJohn Birrell 		tifiles[i] = argv[optind + i];
889f73124b0SMinsoo Choo 	qsort(tifiles, nifiles, sizeof (char *), strcompare);
8901673e404SJohn Birrell 
8911673e404SJohn Birrell 	ifiles[0] = tifiles[0];
8921673e404SJohn Birrell 	for (idx = 0, tidx = 1; tidx < nifiles; tidx++) {
8931673e404SJohn Birrell 		if (strcmp(ifiles[idx], tifiles[tidx]) != 0)
8941673e404SJohn Birrell 			ifiles[++idx] = tifiles[tidx];
8951673e404SJohn Birrell 	}
8961673e404SJohn Birrell 	nifiles = idx + 1;
8971673e404SJohn Birrell 
8981673e404SJohn Birrell 	/* Make sure they all exist */
8991673e404SJohn Birrell 	if ((nielems = count_files(ifiles, nifiles)) < 0)
9001673e404SJohn Birrell 		terminate("Some input files were inaccessible\n");
9011673e404SJohn Birrell 
9021673e404SJohn Birrell 	/* Prepare for the merge */
9031673e404SJohn Birrell 	wq_init(&wq, nielems);
9041673e404SJohn Birrell 
9051673e404SJohn Birrell 	start_threads(&wq);
9061673e404SJohn Birrell 
9071673e404SJohn Birrell 	/*
9081673e404SJohn Birrell 	 * Start the merge
9091673e404SJohn Birrell 	 *
9101673e404SJohn Birrell 	 * We're reading everything from each of the object files, so we
9111673e404SJohn Birrell 	 * don't need to specify labels.
9121673e404SJohn Birrell 	 */
9131673e404SJohn Birrell 	if (read_ctf(ifiles, nifiles, NULL, merge_ctf_cb,
9141673e404SJohn Birrell 	    &wq, require_ctf) == 0) {
915*95ca89cdSEd Maste 		warning("No ctf sections found to merge\n");
9161673e404SJohn Birrell 		exit(0);
9171673e404SJohn Birrell 	}
9181673e404SJohn Birrell 
9191673e404SJohn Birrell 	pthread_mutex_lock(&wq.wq_queue_lock);
9201673e404SJohn Birrell 	wq.wq_nomorefiles = 1;
9211673e404SJohn Birrell 	pthread_cond_broadcast(&wq.wq_work_avail);
9221673e404SJohn Birrell 	pthread_mutex_unlock(&wq.wq_queue_lock);
9231673e404SJohn Birrell 
9241673e404SJohn Birrell 	pthread_mutex_lock(&wq.wq_queue_lock);
9251673e404SJohn Birrell 	while (wq.wq_alldone == 0)
9261673e404SJohn Birrell 		pthread_cond_wait(&wq.wq_alldone_cv, &wq.wq_queue_lock);
9271673e404SJohn Birrell 	pthread_mutex_unlock(&wq.wq_queue_lock);
9281673e404SJohn Birrell 
9291670a1c2SRui Paulo 	join_threads(&wq);
9301670a1c2SRui Paulo 
9311673e404SJohn Birrell 	/*
9321673e404SJohn Birrell 	 * All requested files have been merged, with the resulting tree in
9331673e404SJohn Birrell 	 * mstrtd.  savetd is the tree that will be placed into the output file.
9341673e404SJohn Birrell 	 *
9351673e404SJohn Birrell 	 * Regardless of whether we're doing a normal uniquification or an
9361673e404SJohn Birrell 	 * additive merge, we need a type tree that has been uniquified
9371673e404SJohn Birrell 	 * against uniqfile or withfile, as appropriate.
9381673e404SJohn Birrell 	 *
9391673e404SJohn Birrell 	 * If we're doing a uniquification, we stuff the resulting tree into
9401673e404SJohn Birrell 	 * outfile.  Otherwise, we add the tree to the tree already in withfile.
9411673e404SJohn Birrell 	 */
9421673e404SJohn Birrell 	assert(fifo_len(wq.wq_queue) == 1);
9431673e404SJohn Birrell 	mstrtd = fifo_remove(wq.wq_queue);
9441673e404SJohn Birrell 
9451673e404SJohn Birrell 	if (verbose || debug_level) {
9461673e404SJohn Birrell 		debug(2, "Statistics for td %p\n", (void *)mstrtd);
9471673e404SJohn Birrell 
9481673e404SJohn Birrell 		iidesc_stats(mstrtd->td_iihash);
9491673e404SJohn Birrell 	}
9501673e404SJohn Birrell 
9511673e404SJohn Birrell 	if (uniqfile != NULL || withfile != NULL) {
9521673e404SJohn Birrell 		char *reffile, *reflabel = NULL;
9531673e404SJohn Birrell 		tdata_t *reftd;
9541673e404SJohn Birrell 
9551673e404SJohn Birrell 		if (uniqfile != NULL) {
9561673e404SJohn Birrell 			reffile = uniqfile;
9571673e404SJohn Birrell 			reflabel = uniqlabel;
9581673e404SJohn Birrell 		} else
9591673e404SJohn Birrell 			reffile = withfile;
9601673e404SJohn Birrell 
9611673e404SJohn Birrell 		if (read_ctf(&reffile, 1, reflabel, read_ctf_save_cb,
9621673e404SJohn Birrell 		    &reftd, require_ctf) == 0) {
9631673e404SJohn Birrell 			terminate("No CTF data found in reference file %s\n",
9641673e404SJohn Birrell 			    reffile);
9651673e404SJohn Birrell 		}
9661673e404SJohn Birrell 
9671673e404SJohn Birrell 		savetd = tdata_new();
9681673e404SJohn Birrell 
969bdf290cdSMark Johnston 		if (CTF_V3_TYPE_ISCHILD(reftd->td_nextid))
9701673e404SJohn Birrell 			terminate("No room for additional types in master\n");
9711673e404SJohn Birrell 
9721673e404SJohn Birrell 		savetd->td_nextid = withfile ? reftd->td_nextid :
973bdf290cdSMark Johnston 		    CTF_V3_INDEX_TO_TYPE(1, TRUE);
9741673e404SJohn Birrell 		merge_into_master(mstrtd, reftd, savetd, 0);
9751673e404SJohn Birrell 
9761673e404SJohn Birrell 		tdata_label_add(savetd, label, CTF_LABEL_LASTIDX);
9771673e404SJohn Birrell 
9781673e404SJohn Birrell 		if (withfile) {
9791673e404SJohn Birrell 			/*
9801673e404SJohn Birrell 			 * savetd holds the new data to be added to the withfile
9811673e404SJohn Birrell 			 */
9821673e404SJohn Birrell 			tdata_t *withtd = reftd;
9831673e404SJohn Birrell 
9841673e404SJohn Birrell 			tdata_merge(withtd, savetd);
9851673e404SJohn Birrell 
9861673e404SJohn Birrell 			savetd = withtd;
9871673e404SJohn Birrell 		} else {
9881673e404SJohn Birrell 			char uniqname[MAXPATHLEN];
9891673e404SJohn Birrell 			labelent_t *parle;
9901673e404SJohn Birrell 
9911673e404SJohn Birrell 			parle = tdata_label_top(reftd);
9921673e404SJohn Birrell 
9931673e404SJohn Birrell 			savetd->td_parlabel = xstrdup(parle->le_name);
9941673e404SJohn Birrell 
9951673e404SJohn Birrell 			strncpy(uniqname, reffile, sizeof (uniqname));
9961673e404SJohn Birrell 			uniqname[MAXPATHLEN - 1] = '\0';
9971673e404SJohn Birrell 			savetd->td_parname = xstrdup(basename(uniqname));
9981673e404SJohn Birrell 		}
9991673e404SJohn Birrell 
10001673e404SJohn Birrell 	} else {
10011673e404SJohn Birrell 		/*
10021673e404SJohn Birrell 		 * No post processing.  Write the merged tree as-is into the
10031673e404SJohn Birrell 		 * output file.
10041673e404SJohn Birrell 		 */
10051673e404SJohn Birrell 		tdata_label_free(mstrtd);
10061673e404SJohn Birrell 		tdata_label_add(mstrtd, label, CTF_LABEL_LASTIDX);
10071673e404SJohn Birrell 
10081673e404SJohn Birrell 		savetd = mstrtd;
10091673e404SJohn Birrell 	}
10101673e404SJohn Birrell 
10111673e404SJohn Birrell 	tmpname = mktmpname(outfile, ".ctf");
10121673e404SJohn Birrell 	write_ctf(savetd, outfile, tmpname,
1013a6425ab5SOleksandr Tymoshenko 	    CTF_COMPRESS | CTF_SWAP_BYTES | write_fuzzy_match | dynsym | keep_stabs);
10141673e404SJohn Birrell 	if (rename(tmpname, outfile) != 0)
10151673e404SJohn Birrell 		terminate("Couldn't rename output temp file %s", tmpname);
10161673e404SJohn Birrell 	free(tmpname);
10171673e404SJohn Birrell 
10181673e404SJohn Birrell 	return (0);
10191673e404SJohn Birrell }
1020