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