17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5ae115bc7Smrj * Common Development and Distribution License (the "License"). 6ae115bc7Smrj * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 228168dffcSsommerfe * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* 277c478bd9Sstevel@tonic-gate * Given several files containing CTF data, merge and uniquify that data into 287c478bd9Sstevel@tonic-gate * a single CTF section in an output file. 297c478bd9Sstevel@tonic-gate * 307c478bd9Sstevel@tonic-gate * Merges can proceed independently. As such, we perform the merges in parallel 317c478bd9Sstevel@tonic-gate * using a worker thread model. A given glob of CTF data (either all of the CTF 327c478bd9Sstevel@tonic-gate * data from a single input file, or the result of one or more merges) can only 337c478bd9Sstevel@tonic-gate * be involved in a single merge at any given time, so the process decreases in 347c478bd9Sstevel@tonic-gate * parallelism, especially towards the end, as more and more files are 357c478bd9Sstevel@tonic-gate * consolidated, finally resulting in a single merge of two large CTF graphs. 367c478bd9Sstevel@tonic-gate * Unfortunately, the last merge is also the slowest, as the two graphs being 377c478bd9Sstevel@tonic-gate * merged are each the product of merges of half of the input files. 387c478bd9Sstevel@tonic-gate * 397c478bd9Sstevel@tonic-gate * The algorithm consists of two phases, described in detail below. The first 407c478bd9Sstevel@tonic-gate * phase entails the merging of CTF data in groups of eight. The second phase 417c478bd9Sstevel@tonic-gate * takes the results of Phase I, and merges them two at a time. This disparity 427c478bd9Sstevel@tonic-gate * is due to an observation that the merge time increases at least quadratically 437c478bd9Sstevel@tonic-gate * with the size of the CTF data being merged. As such, merges of CTF graphs 447c478bd9Sstevel@tonic-gate * newly read from input files are much faster than merges of CTF graphs that 457c478bd9Sstevel@tonic-gate * are themselves the results of prior merges. 467c478bd9Sstevel@tonic-gate * 477c478bd9Sstevel@tonic-gate * A further complication is the need to ensure the repeatability of CTF merges. 487c478bd9Sstevel@tonic-gate * That is, a merge should produce the same output every time, given the same 497c478bd9Sstevel@tonic-gate * input. In both phases, this consistency requirement is met by imposing an 507c478bd9Sstevel@tonic-gate * ordering on the merge process, thus ensuring that a given set of input files 517c478bd9Sstevel@tonic-gate * are merged in the same order every time. 527c478bd9Sstevel@tonic-gate * 537c478bd9Sstevel@tonic-gate * Phase I 547c478bd9Sstevel@tonic-gate * 557c478bd9Sstevel@tonic-gate * The main thread reads the input files one by one, transforming the CTF 567c478bd9Sstevel@tonic-gate * data they contain into tdata structures. When a given file has been read 577c478bd9Sstevel@tonic-gate * and parsed, it is placed on the work queue for retrieval by worker threads. 587c478bd9Sstevel@tonic-gate * 597c478bd9Sstevel@tonic-gate * Central to Phase I is the Work In Progress (wip) array, which is used to 607c478bd9Sstevel@tonic-gate * merge batches of files in a predictable order. Files are read by the main 617c478bd9Sstevel@tonic-gate * thread, and are merged into wip array elements in round-robin order. When 627c478bd9Sstevel@tonic-gate * the number of files merged into a given array slot equals the batch size, 637c478bd9Sstevel@tonic-gate * the merged CTF graph in that array is added to the done slot in order by 647c478bd9Sstevel@tonic-gate * array slot. 657c478bd9Sstevel@tonic-gate * 667c478bd9Sstevel@tonic-gate * For example, consider a case where we have five input files, a batch size 677c478bd9Sstevel@tonic-gate * of two, a wip array size of two, and two worker threads (T1 and T2). 687c478bd9Sstevel@tonic-gate * 697c478bd9Sstevel@tonic-gate * 1. The wip array elements are assigned initial batch numbers 0 and 1. 707c478bd9Sstevel@tonic-gate * 2. T1 reads an input file from the input queue (wq_queue). This is the 717c478bd9Sstevel@tonic-gate * first input file, so it is placed into wip[0]. The second file is 727c478bd9Sstevel@tonic-gate * similarly read and placed into wip[1]. The wip array slots now contain 737c478bd9Sstevel@tonic-gate * one file each (wip_nmerged == 1). 747c478bd9Sstevel@tonic-gate * 3. T1 reads the third input file, which it merges into wip[0]. The 757c478bd9Sstevel@tonic-gate * number of files in wip[0] is equal to the batch size. 767c478bd9Sstevel@tonic-gate * 4. T2 reads the fourth input file, which it merges into wip[1]. wip[1] 777c478bd9Sstevel@tonic-gate * is now full too. 787c478bd9Sstevel@tonic-gate * 5. T2 attempts to place the contents of wip[1] on the done queue 797c478bd9Sstevel@tonic-gate * (wq_done_queue), but it can't, since the batch ID for wip[1] is 1. 807c478bd9Sstevel@tonic-gate * Batch 0 needs to be on the done queue before batch 1 can be added, so 817c478bd9Sstevel@tonic-gate * T2 blocks on wip[1]'s cv. 827c478bd9Sstevel@tonic-gate * 6. T1 attempts to place the contents of wip[0] on the done queue, and 837c478bd9Sstevel@tonic-gate * succeeds, updating wq_lastdonebatch to 0. It clears wip[0], and sets 847c478bd9Sstevel@tonic-gate * its batch ID to 2. T1 then signals wip[1]'s cv to awaken T2. 857c478bd9Sstevel@tonic-gate * 7. T2 wakes up, notices that wq_lastdonebatch is 0, which means that 867c478bd9Sstevel@tonic-gate * batch 1 can now be added. It adds wip[1] to the done queue, clears 877c478bd9Sstevel@tonic-gate * wip[1], and sets its batch ID to 3. It signals wip[0]'s cv, and 887c478bd9Sstevel@tonic-gate * restarts. 897c478bd9Sstevel@tonic-gate * 907c478bd9Sstevel@tonic-gate * The above process continues until all input files have been consumed. At 917c478bd9Sstevel@tonic-gate * this point, a pair of barriers are used to allow a single thread to move 927c478bd9Sstevel@tonic-gate * any partial batches from the wip array to the done array in batch ID order. 937c478bd9Sstevel@tonic-gate * When this is complete, wq_done_queue is moved to wq_queue, and Phase II 947c478bd9Sstevel@tonic-gate * begins. 957c478bd9Sstevel@tonic-gate * 967c478bd9Sstevel@tonic-gate * Locking Semantics (Phase I) 977c478bd9Sstevel@tonic-gate * 987c478bd9Sstevel@tonic-gate * The input queue (wq_queue) and the done queue (wq_done_queue) are 997c478bd9Sstevel@tonic-gate * protected by separate mutexes - wq_queue_lock and wq_done_queue. wip 1007c478bd9Sstevel@tonic-gate * array slots are protected by their own mutexes, which must be grabbed 1017c478bd9Sstevel@tonic-gate * before releasing the input queue lock. The wip array lock is dropped 1027c478bd9Sstevel@tonic-gate * when the thread restarts the loop. If the array slot was full, the 1037c478bd9Sstevel@tonic-gate * array lock will be held while the slot contents are added to the done 1047c478bd9Sstevel@tonic-gate * queue. The done queue lock is used to protect the wip slot cv's. 1057c478bd9Sstevel@tonic-gate * 1067c478bd9Sstevel@tonic-gate * The pow number is protected by the queue lock. The master batch ID 1077c478bd9Sstevel@tonic-gate * and last completed batch (wq_lastdonebatch) counters are protected *in 1087c478bd9Sstevel@tonic-gate * Phase I* by the done queue lock. 1097c478bd9Sstevel@tonic-gate * 1107c478bd9Sstevel@tonic-gate * Phase II 1117c478bd9Sstevel@tonic-gate * 1127c478bd9Sstevel@tonic-gate * When Phase II begins, the queue consists of the merged batches from the 1137c478bd9Sstevel@tonic-gate * first phase. Assume we have five batches: 1147c478bd9Sstevel@tonic-gate * 1157c478bd9Sstevel@tonic-gate * Q: a b c d e 1167c478bd9Sstevel@tonic-gate * 1177c478bd9Sstevel@tonic-gate * Using the same batch ID mechanism we used in Phase I, but without the wip 1187c478bd9Sstevel@tonic-gate * array, worker threads remove two entries at a time from the beginning of 1197c478bd9Sstevel@tonic-gate * the queue. These two entries are merged, and are added back to the tail 1207c478bd9Sstevel@tonic-gate * of the queue, as follows: 1217c478bd9Sstevel@tonic-gate * 1227c478bd9Sstevel@tonic-gate * Q: a b c d e # start 1237c478bd9Sstevel@tonic-gate * Q: c d e ab # a, b removed, merged, added to end 1247c478bd9Sstevel@tonic-gate * Q: e ab cd # c, d removed, merged, added to end 1257c478bd9Sstevel@tonic-gate * Q: cd eab # e, ab removed, merged, added to end 1267c478bd9Sstevel@tonic-gate * Q: cdeab # cd, eab removed, merged, added to end 1277c478bd9Sstevel@tonic-gate * 1287c478bd9Sstevel@tonic-gate * When one entry remains on the queue, with no merges outstanding, Phase II 1297c478bd9Sstevel@tonic-gate * finishes. We pre-determine the stopping point by pre-calculating the 1307c478bd9Sstevel@tonic-gate * number of nodes that will appear on the list. In the example above, the 1317c478bd9Sstevel@tonic-gate * number (wq_ninqueue) is 9. When ninqueue is 1, we conclude Phase II by 1327c478bd9Sstevel@tonic-gate * signaling the main thread via wq_done_cv. 1337c478bd9Sstevel@tonic-gate * 1347c478bd9Sstevel@tonic-gate * Locking Semantics (Phase II) 1357c478bd9Sstevel@tonic-gate * 1367c478bd9Sstevel@tonic-gate * The queue (wq_queue), ninqueue, and the master batch ID and last 1377c478bd9Sstevel@tonic-gate * completed batch counters are protected by wq_queue_lock. The done 1387c478bd9Sstevel@tonic-gate * queue and corresponding lock are unused in Phase II as is the wip array. 1397c478bd9Sstevel@tonic-gate * 1407c478bd9Sstevel@tonic-gate * Uniquification 1417c478bd9Sstevel@tonic-gate * 1427c478bd9Sstevel@tonic-gate * We want the CTF data that goes into a given module to be as small as 1437c478bd9Sstevel@tonic-gate * possible. For example, we don't want it to contain any type data that may 1447c478bd9Sstevel@tonic-gate * be present in another common module. As such, after creating the master 1457c478bd9Sstevel@tonic-gate * tdata_t for a given module, we can, if requested by the user, uniquify it 1467c478bd9Sstevel@tonic-gate * against the tdata_t from another module (genunix in the case of the SunOS 1477c478bd9Sstevel@tonic-gate * kernel). We perform a merge between the tdata_t for this module and the 1487c478bd9Sstevel@tonic-gate * tdata_t from genunix. Nodes found in this module that are not present in 1497c478bd9Sstevel@tonic-gate * genunix are added to a third tdata_t - the uniquified tdata_t. 1507c478bd9Sstevel@tonic-gate * 1517c478bd9Sstevel@tonic-gate * Additive Merges 1527c478bd9Sstevel@tonic-gate * 1537c478bd9Sstevel@tonic-gate * In some cases, for example if we are issuing a new version of a common 1547c478bd9Sstevel@tonic-gate * module in a patch, we need to make sure that the CTF data already present 1557c478bd9Sstevel@tonic-gate * in that module does not change. Changes to this data would void the CTF 1567c478bd9Sstevel@tonic-gate * data in any module that uniquified against the common module. To preserve 1577c478bd9Sstevel@tonic-gate * the existing data, we can perform what is known as an additive merge. In 1587c478bd9Sstevel@tonic-gate * this case, a final uniquification is performed against the CTF data in the 1597c478bd9Sstevel@tonic-gate * previous version of the module. The result will be the placement of new 1607c478bd9Sstevel@tonic-gate * and changed data after the existing data, thus preserving the existing type 1617c478bd9Sstevel@tonic-gate * ID space. 1627c478bd9Sstevel@tonic-gate * 1637c478bd9Sstevel@tonic-gate * Saving the result 1647c478bd9Sstevel@tonic-gate * 1657c478bd9Sstevel@tonic-gate * When the merges are complete, the resulting tdata_t is placed into the 1667c478bd9Sstevel@tonic-gate * output file, replacing the .SUNW_ctf section (if any) already in that file. 1677c478bd9Sstevel@tonic-gate * 1687c478bd9Sstevel@tonic-gate * The person who changes the merging thread code in this file without updating 1697c478bd9Sstevel@tonic-gate * this comment will not live to see the stock hit five. 1707c478bd9Sstevel@tonic-gate */ 1717c478bd9Sstevel@tonic-gate 1727c478bd9Sstevel@tonic-gate #include <stdio.h> 1737c478bd9Sstevel@tonic-gate #include <stdlib.h> 1747c478bd9Sstevel@tonic-gate #include <unistd.h> 1757c478bd9Sstevel@tonic-gate #include <pthread.h> 1767c478bd9Sstevel@tonic-gate #include <assert.h> 1777c478bd9Sstevel@tonic-gate #include <synch.h> 1787c478bd9Sstevel@tonic-gate #include <signal.h> 1797c478bd9Sstevel@tonic-gate #include <libgen.h> 1807c478bd9Sstevel@tonic-gate #include <string.h> 1817c478bd9Sstevel@tonic-gate #include <errno.h> 1827c478bd9Sstevel@tonic-gate #include <alloca.h> 1837c478bd9Sstevel@tonic-gate #include <sys/param.h> 1847c478bd9Sstevel@tonic-gate #include <sys/types.h> 1857c478bd9Sstevel@tonic-gate #include <sys/mman.h> 1867c478bd9Sstevel@tonic-gate #include <sys/sysconf.h> 1877c478bd9Sstevel@tonic-gate 1887c478bd9Sstevel@tonic-gate #include "ctf_headers.h" 1897c478bd9Sstevel@tonic-gate #include "ctftools.h" 1907c478bd9Sstevel@tonic-gate #include "ctfmerge.h" 1917c478bd9Sstevel@tonic-gate #include "traverse.h" 1927c478bd9Sstevel@tonic-gate #include "memory.h" 1937c478bd9Sstevel@tonic-gate #include "fifo.h" 1947c478bd9Sstevel@tonic-gate #include "barrier.h" 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate #pragma init(bigheap) 1977c478bd9Sstevel@tonic-gate 1987c478bd9Sstevel@tonic-gate #define MERGE_PHASE1_BATCH_SIZE 8 1997c478bd9Sstevel@tonic-gate #define MERGE_PHASE1_MAX_SLOTS 5 2007c478bd9Sstevel@tonic-gate #define MERGE_INPUT_THROTTLE_LEN 10 2017c478bd9Sstevel@tonic-gate 2027c478bd9Sstevel@tonic-gate const char *progname; 2037c478bd9Sstevel@tonic-gate static char *outfile = NULL; 2047c478bd9Sstevel@tonic-gate static char *tmpname = NULL; 2057c478bd9Sstevel@tonic-gate static int dynsym; 2067c478bd9Sstevel@tonic-gate int debug_level = DEBUG_LEVEL; 20702bc52beSkchow static size_t maxpgsize = 0x400000; 20802bc52beSkchow 2097c478bd9Sstevel@tonic-gate 2107c478bd9Sstevel@tonic-gate void 2117c478bd9Sstevel@tonic-gate usage(void) 2127c478bd9Sstevel@tonic-gate { 2137c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 214c79a74a8SRichard Lowe "Usage: %s [-fstv] -l label | -L labelenv -o outfile file ...\n" 215c79a74a8SRichard Lowe " %s [-fstv] -l label | -L labelenv -o outfile -d uniqfile\n" 216c79a74a8SRichard Lowe " %*s [-D uniqlabel] file ...\n" 217c79a74a8SRichard Lowe " %s [-fstv] -l label | -L labelenv -o outfile -w withfile " 2187c478bd9Sstevel@tonic-gate "file ...\n" 219c79a74a8SRichard Lowe " %s -c srcfile destfile\n" 2207c478bd9Sstevel@tonic-gate "\n" 2217c478bd9Sstevel@tonic-gate " Note: if -L labelenv is specified and labelenv is not set in\n" 2227c478bd9Sstevel@tonic-gate " the environment, a default value is used.\n", 2237c478bd9Sstevel@tonic-gate progname, progname, strlen(progname), " ", 2247c478bd9Sstevel@tonic-gate progname, progname); 2257c478bd9Sstevel@tonic-gate } 2267c478bd9Sstevel@tonic-gate 2277c478bd9Sstevel@tonic-gate static void 2287c478bd9Sstevel@tonic-gate bigheap(void) 2297c478bd9Sstevel@tonic-gate { 2307c478bd9Sstevel@tonic-gate size_t big, *size; 23102bc52beSkchow int sizes; 2327c478bd9Sstevel@tonic-gate struct memcntl_mha mha; 2337c478bd9Sstevel@tonic-gate 2347c478bd9Sstevel@tonic-gate /* 2357c478bd9Sstevel@tonic-gate * First, get the available pagesizes. 2367c478bd9Sstevel@tonic-gate */ 2377c478bd9Sstevel@tonic-gate if ((sizes = getpagesizes(NULL, 0)) == -1) 2387c478bd9Sstevel@tonic-gate return; 2397c478bd9Sstevel@tonic-gate 24002bc52beSkchow if (sizes == 1 || (size = alloca(sizeof (size_t) * sizes)) == NULL) 2417c478bd9Sstevel@tonic-gate return; 2427c478bd9Sstevel@tonic-gate 24302bc52beSkchow if (getpagesizes(size, sizes) == -1) 2447c478bd9Sstevel@tonic-gate return; 2457c478bd9Sstevel@tonic-gate 24602bc52beSkchow while (size[sizes - 1] > maxpgsize) 24702bc52beSkchow sizes--; 24802bc52beSkchow 24902bc52beSkchow /* set big to the largest allowed page size */ 2507c478bd9Sstevel@tonic-gate big = size[sizes - 1]; 2517c478bd9Sstevel@tonic-gate if (big & (big - 1)) { 2527c478bd9Sstevel@tonic-gate /* 2537c478bd9Sstevel@tonic-gate * The largest page size is not a power of two for some 2547c478bd9Sstevel@tonic-gate * inexplicable reason; return. 2557c478bd9Sstevel@tonic-gate */ 2567c478bd9Sstevel@tonic-gate return; 2577c478bd9Sstevel@tonic-gate } 2587c478bd9Sstevel@tonic-gate 2597c478bd9Sstevel@tonic-gate /* 2607c478bd9Sstevel@tonic-gate * Now, align our break to the largest page size. 2617c478bd9Sstevel@tonic-gate */ 2627c478bd9Sstevel@tonic-gate if (brk((void *)((((uintptr_t)sbrk(0) - 1) & ~(big - 1)) + big)) != 0) 2637c478bd9Sstevel@tonic-gate return; 2647c478bd9Sstevel@tonic-gate 2657c478bd9Sstevel@tonic-gate /* 26602bc52beSkchow * set the preferred page size for the heap 2677c478bd9Sstevel@tonic-gate */ 2687c478bd9Sstevel@tonic-gate mha.mha_cmd = MHA_MAPSIZE_BSSBRK; 2697c478bd9Sstevel@tonic-gate mha.mha_flags = 0; 27002bc52beSkchow mha.mha_pagesize = big; 2717c478bd9Sstevel@tonic-gate 27202bc52beSkchow (void) memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mha, 0, 0); 2737c478bd9Sstevel@tonic-gate } 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate static void 2767c478bd9Sstevel@tonic-gate finalize_phase_one(workqueue_t *wq) 2777c478bd9Sstevel@tonic-gate { 2787c478bd9Sstevel@tonic-gate int startslot, i; 2797c478bd9Sstevel@tonic-gate 2807c478bd9Sstevel@tonic-gate /* 2817c478bd9Sstevel@tonic-gate * wip slots are cleared out only when maxbatchsz td's have been merged 2827c478bd9Sstevel@tonic-gate * into them. We're not guaranteed that the number of files we're 2837c478bd9Sstevel@tonic-gate * merging is a multiple of maxbatchsz, so there will be some partial 2847c478bd9Sstevel@tonic-gate * groups in the wip array. Move them to the done queue in batch ID 2857c478bd9Sstevel@tonic-gate * order, starting with the slot containing the next batch that would 2867c478bd9Sstevel@tonic-gate * have been placed on the done queue, followed by the others. 2877c478bd9Sstevel@tonic-gate * One thread will be doing this while the others wait at the barrier 2887c478bd9Sstevel@tonic-gate * back in worker_thread(), so we don't need to worry about pesky things 2897c478bd9Sstevel@tonic-gate * like locks. 2907c478bd9Sstevel@tonic-gate */ 2917c478bd9Sstevel@tonic-gate 2927c478bd9Sstevel@tonic-gate for (startslot = -1, i = 0; i < wq->wq_nwipslots; i++) { 2937c478bd9Sstevel@tonic-gate if (wq->wq_wip[i].wip_batchid == wq->wq_lastdonebatch + 1) { 2947c478bd9Sstevel@tonic-gate startslot = i; 2957c478bd9Sstevel@tonic-gate break; 2967c478bd9Sstevel@tonic-gate } 2977c478bd9Sstevel@tonic-gate } 2987c478bd9Sstevel@tonic-gate 2997c478bd9Sstevel@tonic-gate assert(startslot != -1); 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate for (i = startslot; i < startslot + wq->wq_nwipslots; i++) { 3027c478bd9Sstevel@tonic-gate int slotnum = i % wq->wq_nwipslots; 3037c478bd9Sstevel@tonic-gate wip_t *wipslot = &wq->wq_wip[slotnum]; 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate if (wipslot->wip_td != NULL) { 3067c478bd9Sstevel@tonic-gate debug(2, "clearing slot %d (%d) (saving %d)\n", 3077c478bd9Sstevel@tonic-gate slotnum, i, wipslot->wip_nmerged); 3087c478bd9Sstevel@tonic-gate } else 3097c478bd9Sstevel@tonic-gate debug(2, "clearing slot %d (%d)\n", slotnum, i); 3107c478bd9Sstevel@tonic-gate 3117c478bd9Sstevel@tonic-gate if (wipslot->wip_td != NULL) { 3127c478bd9Sstevel@tonic-gate fifo_add(wq->wq_donequeue, wipslot->wip_td); 3137c478bd9Sstevel@tonic-gate wq->wq_wip[slotnum].wip_td = NULL; 3147c478bd9Sstevel@tonic-gate } 3157c478bd9Sstevel@tonic-gate } 3167c478bd9Sstevel@tonic-gate 3177c478bd9Sstevel@tonic-gate wq->wq_lastdonebatch = wq->wq_next_batchid++; 3187c478bd9Sstevel@tonic-gate 3197c478bd9Sstevel@tonic-gate debug(2, "phase one done: donequeue has %d items\n", 3207c478bd9Sstevel@tonic-gate fifo_len(wq->wq_donequeue)); 3217c478bd9Sstevel@tonic-gate } 3227c478bd9Sstevel@tonic-gate 3237c478bd9Sstevel@tonic-gate static void 3247c478bd9Sstevel@tonic-gate init_phase_two(workqueue_t *wq) 3257c478bd9Sstevel@tonic-gate { 3267c478bd9Sstevel@tonic-gate int num; 3277c478bd9Sstevel@tonic-gate 3287c478bd9Sstevel@tonic-gate /* 3297c478bd9Sstevel@tonic-gate * We're going to continually merge the first two entries on the queue, 3307c478bd9Sstevel@tonic-gate * placing the result on the end, until there's nothing left to merge. 3317c478bd9Sstevel@tonic-gate * At that point, everything will have been merged into one. The 3327c478bd9Sstevel@tonic-gate * initial value of ninqueue needs to be equal to the total number of 3337c478bd9Sstevel@tonic-gate * entries that will show up on the queue, both at the start of the 3347c478bd9Sstevel@tonic-gate * phase and as generated by merges during the phase. 3357c478bd9Sstevel@tonic-gate */ 3367c478bd9Sstevel@tonic-gate wq->wq_ninqueue = num = fifo_len(wq->wq_donequeue); 3377c478bd9Sstevel@tonic-gate while (num != 1) { 3387c478bd9Sstevel@tonic-gate wq->wq_ninqueue += num / 2; 3397c478bd9Sstevel@tonic-gate num = num / 2 + num % 2; 3407c478bd9Sstevel@tonic-gate } 3417c478bd9Sstevel@tonic-gate 3427c478bd9Sstevel@tonic-gate /* 3437c478bd9Sstevel@tonic-gate * Move the done queue to the work queue. We won't be using the done 3447c478bd9Sstevel@tonic-gate * queue in phase 2. 3457c478bd9Sstevel@tonic-gate */ 3467c478bd9Sstevel@tonic-gate assert(fifo_len(wq->wq_queue) == 0); 3477c478bd9Sstevel@tonic-gate fifo_free(wq->wq_queue, NULL); 3487c478bd9Sstevel@tonic-gate wq->wq_queue = wq->wq_donequeue; 3497c478bd9Sstevel@tonic-gate } 3507c478bd9Sstevel@tonic-gate 3517c478bd9Sstevel@tonic-gate static void 3527c478bd9Sstevel@tonic-gate wip_save_work(workqueue_t *wq, wip_t *slot, int slotnum) 3537c478bd9Sstevel@tonic-gate { 3547c478bd9Sstevel@tonic-gate pthread_mutex_lock(&wq->wq_donequeue_lock); 3557c478bd9Sstevel@tonic-gate 3567c478bd9Sstevel@tonic-gate while (wq->wq_lastdonebatch + 1 < slot->wip_batchid) 3577c478bd9Sstevel@tonic-gate pthread_cond_wait(&slot->wip_cv, &wq->wq_donequeue_lock); 3587c478bd9Sstevel@tonic-gate assert(wq->wq_lastdonebatch + 1 == slot->wip_batchid); 3597c478bd9Sstevel@tonic-gate 3607c478bd9Sstevel@tonic-gate fifo_add(wq->wq_donequeue, slot->wip_td); 3617c478bd9Sstevel@tonic-gate wq->wq_lastdonebatch++; 3627c478bd9Sstevel@tonic-gate pthread_cond_signal(&wq->wq_wip[(slotnum + 1) % 3637c478bd9Sstevel@tonic-gate wq->wq_nwipslots].wip_cv); 3647c478bd9Sstevel@tonic-gate 3657c478bd9Sstevel@tonic-gate /* reset the slot for next use */ 3667c478bd9Sstevel@tonic-gate slot->wip_td = NULL; 3677c478bd9Sstevel@tonic-gate slot->wip_batchid = wq->wq_next_batchid++; 3687c478bd9Sstevel@tonic-gate 3697c478bd9Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_donequeue_lock); 3707c478bd9Sstevel@tonic-gate } 3717c478bd9Sstevel@tonic-gate 3727c478bd9Sstevel@tonic-gate static void 3737c478bd9Sstevel@tonic-gate wip_add_work(wip_t *slot, tdata_t *pow) 3747c478bd9Sstevel@tonic-gate { 3757c478bd9Sstevel@tonic-gate if (slot->wip_td == NULL) { 3767c478bd9Sstevel@tonic-gate slot->wip_td = pow; 3777c478bd9Sstevel@tonic-gate slot->wip_nmerged = 1; 3787c478bd9Sstevel@tonic-gate } else { 3797c478bd9Sstevel@tonic-gate debug(2, "%d: merging %p into %p\n", pthread_self(), 3807c478bd9Sstevel@tonic-gate (void *)pow, (void *)slot->wip_td); 3817c478bd9Sstevel@tonic-gate 3827c478bd9Sstevel@tonic-gate merge_into_master(pow, slot->wip_td, NULL, 0); 3837c478bd9Sstevel@tonic-gate tdata_free(pow); 3847c478bd9Sstevel@tonic-gate 3857c478bd9Sstevel@tonic-gate slot->wip_nmerged++; 3867c478bd9Sstevel@tonic-gate } 3877c478bd9Sstevel@tonic-gate } 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate static void 3907c478bd9Sstevel@tonic-gate worker_runphase1(workqueue_t *wq) 3917c478bd9Sstevel@tonic-gate { 3927c478bd9Sstevel@tonic-gate wip_t *wipslot; 3937c478bd9Sstevel@tonic-gate tdata_t *pow; 3947c478bd9Sstevel@tonic-gate int wipslotnum, pownum; 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate for (;;) { 3977c478bd9Sstevel@tonic-gate pthread_mutex_lock(&wq->wq_queue_lock); 3987c478bd9Sstevel@tonic-gate 3997c478bd9Sstevel@tonic-gate while (fifo_empty(wq->wq_queue)) { 4007c478bd9Sstevel@tonic-gate if (wq->wq_nomorefiles == 1) { 4017c478bd9Sstevel@tonic-gate pthread_cond_broadcast(&wq->wq_work_avail); 4027c478bd9Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 4037c478bd9Sstevel@tonic-gate 4047c478bd9Sstevel@tonic-gate /* on to phase 2 ... */ 4057c478bd9Sstevel@tonic-gate return; 4067c478bd9Sstevel@tonic-gate } 4077c478bd9Sstevel@tonic-gate 4087c478bd9Sstevel@tonic-gate pthread_cond_wait(&wq->wq_work_avail, 4097c478bd9Sstevel@tonic-gate &wq->wq_queue_lock); 4107c478bd9Sstevel@tonic-gate } 4117c478bd9Sstevel@tonic-gate 4127c478bd9Sstevel@tonic-gate /* there's work to be done! */ 4137c478bd9Sstevel@tonic-gate pow = fifo_remove(wq->wq_queue); 4147c478bd9Sstevel@tonic-gate pownum = wq->wq_nextpownum++; 4157c478bd9Sstevel@tonic-gate pthread_cond_broadcast(&wq->wq_work_removed); 4167c478bd9Sstevel@tonic-gate 4177c478bd9Sstevel@tonic-gate assert(pow != NULL); 4187c478bd9Sstevel@tonic-gate 4197c478bd9Sstevel@tonic-gate /* merge it into the right slot */ 4207c478bd9Sstevel@tonic-gate wipslotnum = pownum % wq->wq_nwipslots; 4217c478bd9Sstevel@tonic-gate wipslot = &wq->wq_wip[wipslotnum]; 4227c478bd9Sstevel@tonic-gate 4237c478bd9Sstevel@tonic-gate pthread_mutex_lock(&wipslot->wip_lock); 4247c478bd9Sstevel@tonic-gate 4257c478bd9Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 4267c478bd9Sstevel@tonic-gate 4277c478bd9Sstevel@tonic-gate wip_add_work(wipslot, pow); 4287c478bd9Sstevel@tonic-gate 4297c478bd9Sstevel@tonic-gate if (wipslot->wip_nmerged == wq->wq_maxbatchsz) 4307c478bd9Sstevel@tonic-gate wip_save_work(wq, wipslot, wipslotnum); 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate pthread_mutex_unlock(&wipslot->wip_lock); 4337c478bd9Sstevel@tonic-gate } 4347c478bd9Sstevel@tonic-gate } 4357c478bd9Sstevel@tonic-gate 4367c478bd9Sstevel@tonic-gate static void 4377c478bd9Sstevel@tonic-gate worker_runphase2(workqueue_t *wq) 4387c478bd9Sstevel@tonic-gate { 4397c478bd9Sstevel@tonic-gate tdata_t *pow1, *pow2; 4407c478bd9Sstevel@tonic-gate int batchid; 4417c478bd9Sstevel@tonic-gate 4427c478bd9Sstevel@tonic-gate for (;;) { 4437c478bd9Sstevel@tonic-gate pthread_mutex_lock(&wq->wq_queue_lock); 4447c478bd9Sstevel@tonic-gate 4457c478bd9Sstevel@tonic-gate if (wq->wq_ninqueue == 1) { 4467c478bd9Sstevel@tonic-gate pthread_cond_broadcast(&wq->wq_work_avail); 4477c478bd9Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 4487c478bd9Sstevel@tonic-gate 4497c478bd9Sstevel@tonic-gate debug(2, "%d: entering p2 completion barrier\n", 4507c478bd9Sstevel@tonic-gate pthread_self()); 4517c478bd9Sstevel@tonic-gate if (barrier_wait(&wq->wq_bar1)) { 4527c478bd9Sstevel@tonic-gate pthread_mutex_lock(&wq->wq_queue_lock); 4537c478bd9Sstevel@tonic-gate wq->wq_alldone = 1; 4547c478bd9Sstevel@tonic-gate pthread_cond_signal(&wq->wq_alldone_cv); 4557c478bd9Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 4567c478bd9Sstevel@tonic-gate } 4577c478bd9Sstevel@tonic-gate 4587c478bd9Sstevel@tonic-gate return; 4597c478bd9Sstevel@tonic-gate } 4607c478bd9Sstevel@tonic-gate 4617c478bd9Sstevel@tonic-gate if (fifo_len(wq->wq_queue) < 2) { 4627c478bd9Sstevel@tonic-gate pthread_cond_wait(&wq->wq_work_avail, 4637c478bd9Sstevel@tonic-gate &wq->wq_queue_lock); 4647c478bd9Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 4657c478bd9Sstevel@tonic-gate continue; 4667c478bd9Sstevel@tonic-gate } 4677c478bd9Sstevel@tonic-gate 4687c478bd9Sstevel@tonic-gate /* there's work to be done! */ 4697c478bd9Sstevel@tonic-gate pow1 = fifo_remove(wq->wq_queue); 4707c478bd9Sstevel@tonic-gate pow2 = fifo_remove(wq->wq_queue); 4717c478bd9Sstevel@tonic-gate wq->wq_ninqueue -= 2; 4727c478bd9Sstevel@tonic-gate 4737c478bd9Sstevel@tonic-gate batchid = wq->wq_next_batchid++; 4747c478bd9Sstevel@tonic-gate 4757c478bd9Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 4767c478bd9Sstevel@tonic-gate 4777c478bd9Sstevel@tonic-gate debug(2, "%d: merging %p into %p\n", pthread_self(), 4787c478bd9Sstevel@tonic-gate (void *)pow1, (void *)pow2); 4797c478bd9Sstevel@tonic-gate merge_into_master(pow1, pow2, NULL, 0); 4807c478bd9Sstevel@tonic-gate tdata_free(pow1); 4817c478bd9Sstevel@tonic-gate 4827c478bd9Sstevel@tonic-gate /* 4837c478bd9Sstevel@tonic-gate * merging is complete. place at the tail of the queue in 4847c478bd9Sstevel@tonic-gate * proper order. 4857c478bd9Sstevel@tonic-gate */ 4867c478bd9Sstevel@tonic-gate pthread_mutex_lock(&wq->wq_queue_lock); 4877c478bd9Sstevel@tonic-gate while (wq->wq_lastdonebatch + 1 != batchid) { 4887c478bd9Sstevel@tonic-gate pthread_cond_wait(&wq->wq_done_cv, 4897c478bd9Sstevel@tonic-gate &wq->wq_queue_lock); 4907c478bd9Sstevel@tonic-gate } 4917c478bd9Sstevel@tonic-gate 4927c478bd9Sstevel@tonic-gate wq->wq_lastdonebatch = batchid; 4937c478bd9Sstevel@tonic-gate 4947c478bd9Sstevel@tonic-gate fifo_add(wq->wq_queue, pow2); 4957c478bd9Sstevel@tonic-gate debug(2, "%d: added %p to queue, len now %d, ninqueue %d\n", 4967c478bd9Sstevel@tonic-gate pthread_self(), (void *)pow2, fifo_len(wq->wq_queue), 4977c478bd9Sstevel@tonic-gate wq->wq_ninqueue); 4987c478bd9Sstevel@tonic-gate pthread_cond_broadcast(&wq->wq_done_cv); 4997c478bd9Sstevel@tonic-gate pthread_cond_signal(&wq->wq_work_avail); 5007c478bd9Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 5017c478bd9Sstevel@tonic-gate } 5027c478bd9Sstevel@tonic-gate } 5037c478bd9Sstevel@tonic-gate 5047c478bd9Sstevel@tonic-gate /* 5057c478bd9Sstevel@tonic-gate * Main loop for worker threads. 5067c478bd9Sstevel@tonic-gate */ 5077c478bd9Sstevel@tonic-gate static void 5087c478bd9Sstevel@tonic-gate worker_thread(workqueue_t *wq) 5097c478bd9Sstevel@tonic-gate { 5107c478bd9Sstevel@tonic-gate worker_runphase1(wq); 5117c478bd9Sstevel@tonic-gate 5127c478bd9Sstevel@tonic-gate debug(2, "%d: entering first barrier\n", pthread_self()); 5137c478bd9Sstevel@tonic-gate 5147c478bd9Sstevel@tonic-gate if (barrier_wait(&wq->wq_bar1)) { 5157c478bd9Sstevel@tonic-gate 5167c478bd9Sstevel@tonic-gate debug(2, "%d: doing work in first barrier\n", pthread_self()); 5177c478bd9Sstevel@tonic-gate 5187c478bd9Sstevel@tonic-gate finalize_phase_one(wq); 5197c478bd9Sstevel@tonic-gate 5207c478bd9Sstevel@tonic-gate init_phase_two(wq); 5217c478bd9Sstevel@tonic-gate 5227c478bd9Sstevel@tonic-gate debug(2, "%d: ninqueue is %d, %d on queue\n", pthread_self(), 5237c478bd9Sstevel@tonic-gate wq->wq_ninqueue, fifo_len(wq->wq_queue)); 5247c478bd9Sstevel@tonic-gate } 5257c478bd9Sstevel@tonic-gate 5267c478bd9Sstevel@tonic-gate debug(2, "%d: entering second barrier\n", pthread_self()); 5277c478bd9Sstevel@tonic-gate 5287c478bd9Sstevel@tonic-gate (void) barrier_wait(&wq->wq_bar2); 5297c478bd9Sstevel@tonic-gate 5307c478bd9Sstevel@tonic-gate debug(2, "%d: phase 1 complete\n", pthread_self()); 5317c478bd9Sstevel@tonic-gate 5327c478bd9Sstevel@tonic-gate worker_runphase2(wq); 5337c478bd9Sstevel@tonic-gate } 5347c478bd9Sstevel@tonic-gate 5357c478bd9Sstevel@tonic-gate /* 5367c478bd9Sstevel@tonic-gate * Pass a tdata_t tree, built from an input file, off to the work queue for 5377c478bd9Sstevel@tonic-gate * consumption by worker threads. 5387c478bd9Sstevel@tonic-gate */ 5397c478bd9Sstevel@tonic-gate static int 5407c478bd9Sstevel@tonic-gate merge_ctf_cb(tdata_t *td, char *name, void *arg) 5417c478bd9Sstevel@tonic-gate { 5427c478bd9Sstevel@tonic-gate workqueue_t *wq = arg; 5437c478bd9Sstevel@tonic-gate 5447c478bd9Sstevel@tonic-gate debug(3, "Adding tdata %p for processing\n", (void *)td); 5457c478bd9Sstevel@tonic-gate 5467c478bd9Sstevel@tonic-gate pthread_mutex_lock(&wq->wq_queue_lock); 5477c478bd9Sstevel@tonic-gate while (fifo_len(wq->wq_queue) > wq->wq_ithrottle) { 5487c478bd9Sstevel@tonic-gate debug(2, "Throttling input (len = %d, throttle = %d)\n", 5497c478bd9Sstevel@tonic-gate fifo_len(wq->wq_queue), wq->wq_ithrottle); 5507c478bd9Sstevel@tonic-gate pthread_cond_wait(&wq->wq_work_removed, &wq->wq_queue_lock); 5517c478bd9Sstevel@tonic-gate } 5527c478bd9Sstevel@tonic-gate 5537c478bd9Sstevel@tonic-gate fifo_add(wq->wq_queue, td); 5547c478bd9Sstevel@tonic-gate debug(1, "Thread %d announcing %s\n", pthread_self(), name); 5557c478bd9Sstevel@tonic-gate pthread_cond_broadcast(&wq->wq_work_avail); 5567c478bd9Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 5577c478bd9Sstevel@tonic-gate 5587c478bd9Sstevel@tonic-gate return (1); 5597c478bd9Sstevel@tonic-gate } 5607c478bd9Sstevel@tonic-gate 5617c478bd9Sstevel@tonic-gate /* 5627c478bd9Sstevel@tonic-gate * This program is intended to be invoked from a Makefile, as part of the build. 5637c478bd9Sstevel@tonic-gate * As such, in the event of a failure or user-initiated interrupt (^C), we need 5647c478bd9Sstevel@tonic-gate * to ensure that a subsequent re-make will cause ctfmerge to be executed again. 5657c478bd9Sstevel@tonic-gate * Unfortunately, ctfmerge will usually be invoked directly after (and as part 5667c478bd9Sstevel@tonic-gate * of the same Makefile rule as) a link, and will operate on the linked file 5677c478bd9Sstevel@tonic-gate * in place. If we merely exit upon receipt of a SIGINT, a subsequent make 5687c478bd9Sstevel@tonic-gate * will notice that the *linked* file is newer than the object files, and thus 5697c478bd9Sstevel@tonic-gate * will not reinvoke ctfmerge. The only way to ensure that a subsequent make 5707c478bd9Sstevel@tonic-gate * reinvokes ctfmerge, is to remove the file to which we are adding CTF 5717c478bd9Sstevel@tonic-gate * data (confusingly named the output file). This means that the link will need 5727c478bd9Sstevel@tonic-gate * to happen again, but links are generally fast, and we can't allow the merge 5737c478bd9Sstevel@tonic-gate * to be skipped. 5747c478bd9Sstevel@tonic-gate * 5757c478bd9Sstevel@tonic-gate * Another possibility would be to block SIGINT entirely - to always run to 5767c478bd9Sstevel@tonic-gate * completion. The run time of ctfmerge can, however, be measured in minutes 5777c478bd9Sstevel@tonic-gate * in some cases, so this is not a valid option. 5787c478bd9Sstevel@tonic-gate */ 5797c478bd9Sstevel@tonic-gate static void 5807c478bd9Sstevel@tonic-gate handle_sig(int sig) 5817c478bd9Sstevel@tonic-gate { 5827c478bd9Sstevel@tonic-gate terminate("Caught signal %d - exiting\n", sig); 5837c478bd9Sstevel@tonic-gate } 5847c478bd9Sstevel@tonic-gate 5857c478bd9Sstevel@tonic-gate static void 5867c478bd9Sstevel@tonic-gate terminate_cleanup(void) 5877c478bd9Sstevel@tonic-gate { 5887c478bd9Sstevel@tonic-gate int dounlink = getenv("CTFMERGE_TERMINATE_NO_UNLINK") ? 0 : 1; 5897c478bd9Sstevel@tonic-gate 5907c478bd9Sstevel@tonic-gate if (tmpname != NULL && dounlink) 5917c478bd9Sstevel@tonic-gate unlink(tmpname); 5927c478bd9Sstevel@tonic-gate 5937c478bd9Sstevel@tonic-gate if (outfile == NULL) 5947c478bd9Sstevel@tonic-gate return; 5957c478bd9Sstevel@tonic-gate 5967c478bd9Sstevel@tonic-gate if (dounlink) { 5977c478bd9Sstevel@tonic-gate fprintf(stderr, "Removing %s\n", outfile); 5987c478bd9Sstevel@tonic-gate unlink(outfile); 5997c478bd9Sstevel@tonic-gate } 6007c478bd9Sstevel@tonic-gate } 6017c478bd9Sstevel@tonic-gate 6027c478bd9Sstevel@tonic-gate static void 603495021bdSRichard Lowe copy_ctf_data(char *srcfile, char *destfile) 6047c478bd9Sstevel@tonic-gate { 6057c478bd9Sstevel@tonic-gate tdata_t *srctd; 6067c478bd9Sstevel@tonic-gate 6077c478bd9Sstevel@tonic-gate if (read_ctf(&srcfile, 1, NULL, read_ctf_save_cb, &srctd, 1) == 0) 6087c478bd9Sstevel@tonic-gate terminate("No CTF data found in source file %s\n", srcfile); 6097c478bd9Sstevel@tonic-gate 6107c478bd9Sstevel@tonic-gate tmpname = mktmpname(destfile, ".ctf"); 611495021bdSRichard Lowe write_ctf(srctd, destfile, tmpname, CTF_COMPRESS); 6127c478bd9Sstevel@tonic-gate if (rename(tmpname, destfile) != 0) { 6137c478bd9Sstevel@tonic-gate terminate("Couldn't rename temp file %s to %s", tmpname, 6147c478bd9Sstevel@tonic-gate destfile); 6157c478bd9Sstevel@tonic-gate } 6167c478bd9Sstevel@tonic-gate free(tmpname); 6177c478bd9Sstevel@tonic-gate tdata_free(srctd); 6187c478bd9Sstevel@tonic-gate } 6197c478bd9Sstevel@tonic-gate 6207c478bd9Sstevel@tonic-gate static void 6217c478bd9Sstevel@tonic-gate wq_init(workqueue_t *wq, int nfiles) 6227c478bd9Sstevel@tonic-gate { 6237c478bd9Sstevel@tonic-gate int throttle, nslots, i; 6247c478bd9Sstevel@tonic-gate 6257c478bd9Sstevel@tonic-gate if (getenv("CTFMERGE_MAX_SLOTS")) 6267c478bd9Sstevel@tonic-gate nslots = atoi(getenv("CTFMERGE_MAX_SLOTS")); 6277c478bd9Sstevel@tonic-gate else 6287c478bd9Sstevel@tonic-gate nslots = MERGE_PHASE1_MAX_SLOTS; 6297c478bd9Sstevel@tonic-gate 6307c478bd9Sstevel@tonic-gate if (getenv("CTFMERGE_PHASE1_BATCH_SIZE")) 6317c478bd9Sstevel@tonic-gate wq->wq_maxbatchsz = atoi(getenv("CTFMERGE_PHASE1_BATCH_SIZE")); 6327c478bd9Sstevel@tonic-gate else 6337c478bd9Sstevel@tonic-gate wq->wq_maxbatchsz = MERGE_PHASE1_BATCH_SIZE; 6347c478bd9Sstevel@tonic-gate 6357c478bd9Sstevel@tonic-gate nslots = MIN(nslots, (nfiles + wq->wq_maxbatchsz - 1) / 6367c478bd9Sstevel@tonic-gate wq->wq_maxbatchsz); 6377c478bd9Sstevel@tonic-gate 6387c478bd9Sstevel@tonic-gate wq->wq_wip = xcalloc(sizeof (wip_t) * nslots); 6397c478bd9Sstevel@tonic-gate wq->wq_nwipslots = nslots; 6407c478bd9Sstevel@tonic-gate wq->wq_nthreads = MIN(sysconf(_SC_NPROCESSORS_ONLN) * 3 / 2, nslots); 6418168dffcSsommerfe wq->wq_thread = xmalloc(sizeof (pthread_t) * wq->wq_nthreads); 6427c478bd9Sstevel@tonic-gate 6437c478bd9Sstevel@tonic-gate if (getenv("CTFMERGE_INPUT_THROTTLE")) 6447c478bd9Sstevel@tonic-gate throttle = atoi(getenv("CTFMERGE_INPUT_THROTTLE")); 6457c478bd9Sstevel@tonic-gate else 6467c478bd9Sstevel@tonic-gate throttle = MERGE_INPUT_THROTTLE_LEN; 6477c478bd9Sstevel@tonic-gate wq->wq_ithrottle = throttle * wq->wq_nthreads; 6487c478bd9Sstevel@tonic-gate 6497c478bd9Sstevel@tonic-gate debug(1, "Using %d slots, %d threads\n", wq->wq_nwipslots, 6507c478bd9Sstevel@tonic-gate wq->wq_nthreads); 6517c478bd9Sstevel@tonic-gate 6527c478bd9Sstevel@tonic-gate wq->wq_next_batchid = 0; 6537c478bd9Sstevel@tonic-gate 6547c478bd9Sstevel@tonic-gate for (i = 0; i < nslots; i++) { 6557c478bd9Sstevel@tonic-gate pthread_mutex_init(&wq->wq_wip[i].wip_lock, NULL); 6567c478bd9Sstevel@tonic-gate wq->wq_wip[i].wip_batchid = wq->wq_next_batchid++; 6577c478bd9Sstevel@tonic-gate } 6587c478bd9Sstevel@tonic-gate 6597c478bd9Sstevel@tonic-gate pthread_mutex_init(&wq->wq_queue_lock, NULL); 6607c478bd9Sstevel@tonic-gate wq->wq_queue = fifo_new(); 6617c478bd9Sstevel@tonic-gate pthread_cond_init(&wq->wq_work_avail, NULL); 6627c478bd9Sstevel@tonic-gate pthread_cond_init(&wq->wq_work_removed, NULL); 6637c478bd9Sstevel@tonic-gate wq->wq_ninqueue = nfiles; 6647c478bd9Sstevel@tonic-gate wq->wq_nextpownum = 0; 6657c478bd9Sstevel@tonic-gate 6667c478bd9Sstevel@tonic-gate pthread_mutex_init(&wq->wq_donequeue_lock, NULL); 6677c478bd9Sstevel@tonic-gate wq->wq_donequeue = fifo_new(); 6687c478bd9Sstevel@tonic-gate wq->wq_lastdonebatch = -1; 6697c478bd9Sstevel@tonic-gate 6707c478bd9Sstevel@tonic-gate pthread_cond_init(&wq->wq_done_cv, NULL); 6717c478bd9Sstevel@tonic-gate 6727c478bd9Sstevel@tonic-gate pthread_cond_init(&wq->wq_alldone_cv, NULL); 6737c478bd9Sstevel@tonic-gate wq->wq_alldone = 0; 6747c478bd9Sstevel@tonic-gate 6757c478bd9Sstevel@tonic-gate barrier_init(&wq->wq_bar1, wq->wq_nthreads); 6767c478bd9Sstevel@tonic-gate barrier_init(&wq->wq_bar2, wq->wq_nthreads); 6777c478bd9Sstevel@tonic-gate 6787c478bd9Sstevel@tonic-gate wq->wq_nomorefiles = 0; 6797c478bd9Sstevel@tonic-gate } 6807c478bd9Sstevel@tonic-gate 6817c478bd9Sstevel@tonic-gate static void 6827c478bd9Sstevel@tonic-gate start_threads(workqueue_t *wq) 6837c478bd9Sstevel@tonic-gate { 6847c478bd9Sstevel@tonic-gate sigset_t sets; 6857c478bd9Sstevel@tonic-gate int i; 6867c478bd9Sstevel@tonic-gate 6877c478bd9Sstevel@tonic-gate sigemptyset(&sets); 6887c478bd9Sstevel@tonic-gate sigaddset(&sets, SIGINT); 6897c478bd9Sstevel@tonic-gate sigaddset(&sets, SIGQUIT); 6907c478bd9Sstevel@tonic-gate sigaddset(&sets, SIGTERM); 6917c478bd9Sstevel@tonic-gate pthread_sigmask(SIG_BLOCK, &sets, NULL); 6927c478bd9Sstevel@tonic-gate 6937c478bd9Sstevel@tonic-gate for (i = 0; i < wq->wq_nthreads; i++) { 6948168dffcSsommerfe pthread_create(&wq->wq_thread[i], NULL, 6958168dffcSsommerfe (void *(*)(void *))worker_thread, wq); 6967c478bd9Sstevel@tonic-gate } 6977c478bd9Sstevel@tonic-gate 6987c478bd9Sstevel@tonic-gate sigset(SIGINT, handle_sig); 6997c478bd9Sstevel@tonic-gate sigset(SIGQUIT, handle_sig); 7007c478bd9Sstevel@tonic-gate sigset(SIGTERM, handle_sig); 7017c478bd9Sstevel@tonic-gate pthread_sigmask(SIG_UNBLOCK, &sets, NULL); 7027c478bd9Sstevel@tonic-gate } 7037c478bd9Sstevel@tonic-gate 7048168dffcSsommerfe static void 7058168dffcSsommerfe join_threads(workqueue_t *wq) 7068168dffcSsommerfe { 7078168dffcSsommerfe int i; 7088168dffcSsommerfe 7098168dffcSsommerfe for (i = 0; i < wq->wq_nthreads; i++) { 7108168dffcSsommerfe pthread_join(wq->wq_thread[i], NULL); 7118168dffcSsommerfe } 7128168dffcSsommerfe } 7138168dffcSsommerfe 7147c478bd9Sstevel@tonic-gate static int 7157c478bd9Sstevel@tonic-gate strcompare(const void *p1, const void *p2) 7167c478bd9Sstevel@tonic-gate { 7177c478bd9Sstevel@tonic-gate char *s1 = *((char **)p1); 7187c478bd9Sstevel@tonic-gate char *s2 = *((char **)p2); 7197c478bd9Sstevel@tonic-gate 7207c478bd9Sstevel@tonic-gate return (strcmp(s1, s2)); 7217c478bd9Sstevel@tonic-gate } 7227c478bd9Sstevel@tonic-gate 7238168dffcSsommerfe /* 7248168dffcSsommerfe * Core work queue structure; passed to worker threads on thread creation 7258168dffcSsommerfe * as the main point of coordination. Allocate as a static structure; we 7268168dffcSsommerfe * could have put this into a local variable in main, but passing a pointer 7278168dffcSsommerfe * into your stack to another thread is fragile at best and leads to some 7288168dffcSsommerfe * hard-to-debug failure modes. 7298168dffcSsommerfe */ 7308168dffcSsommerfe static workqueue_t wq; 7318168dffcSsommerfe 7327c478bd9Sstevel@tonic-gate int 7337c478bd9Sstevel@tonic-gate main(int argc, char **argv) 7347c478bd9Sstevel@tonic-gate { 7357c478bd9Sstevel@tonic-gate tdata_t *mstrtd, *savetd; 7367c478bd9Sstevel@tonic-gate char *uniqfile = NULL, *uniqlabel = NULL; 7377c478bd9Sstevel@tonic-gate char *withfile = NULL; 7387c478bd9Sstevel@tonic-gate char *label = NULL; 7397c478bd9Sstevel@tonic-gate char **ifiles, **tifiles; 7407c478bd9Sstevel@tonic-gate int verbose = 0, docopy = 0; 7417c478bd9Sstevel@tonic-gate int write_fuzzy_match = 0; 7427c478bd9Sstevel@tonic-gate int require_ctf = 0; 7437c478bd9Sstevel@tonic-gate int nifiles, nielems; 7447c478bd9Sstevel@tonic-gate int c, i, idx, tidx, err; 7457c478bd9Sstevel@tonic-gate 7467c478bd9Sstevel@tonic-gate progname = basename(argv[0]); 7477c478bd9Sstevel@tonic-gate 748*7fd79137SRobert Mustacchi ctf_altexec("CTFMERGE_ALTEXEC", argc, argv); 749*7fd79137SRobert Mustacchi 7507c478bd9Sstevel@tonic-gate if (getenv("CTFMERGE_DEBUG_LEVEL")) 7517c478bd9Sstevel@tonic-gate debug_level = atoi(getenv("CTFMERGE_DEBUG_LEVEL")); 7527c478bd9Sstevel@tonic-gate 7537c478bd9Sstevel@tonic-gate err = 0; 754495021bdSRichard Lowe while ((c = getopt(argc, argv, ":cd:D:fl:L:o:tvw:s")) != EOF) { 7557c478bd9Sstevel@tonic-gate switch (c) { 7567c478bd9Sstevel@tonic-gate case 'c': 7577c478bd9Sstevel@tonic-gate docopy = 1; 7587c478bd9Sstevel@tonic-gate break; 7597c478bd9Sstevel@tonic-gate case 'd': 7607c478bd9Sstevel@tonic-gate /* Uniquify against `uniqfile' */ 7617c478bd9Sstevel@tonic-gate uniqfile = optarg; 7627c478bd9Sstevel@tonic-gate break; 7637c478bd9Sstevel@tonic-gate case 'D': 7647c478bd9Sstevel@tonic-gate /* Uniquify against label `uniqlabel' in `uniqfile' */ 7657c478bd9Sstevel@tonic-gate uniqlabel = optarg; 7667c478bd9Sstevel@tonic-gate break; 7677c478bd9Sstevel@tonic-gate case 'f': 7687c478bd9Sstevel@tonic-gate write_fuzzy_match = CTF_FUZZY_MATCH; 7697c478bd9Sstevel@tonic-gate break; 7707c478bd9Sstevel@tonic-gate case 'l': 7717c478bd9Sstevel@tonic-gate /* Label merged types with `label' */ 7727c478bd9Sstevel@tonic-gate label = optarg; 7737c478bd9Sstevel@tonic-gate break; 7747c478bd9Sstevel@tonic-gate case 'L': 7757c478bd9Sstevel@tonic-gate /* Label merged types with getenv(`label`) */ 7767c478bd9Sstevel@tonic-gate if ((label = getenv(optarg)) == NULL) 7777c478bd9Sstevel@tonic-gate label = CTF_DEFAULT_LABEL; 7787c478bd9Sstevel@tonic-gate break; 7797c478bd9Sstevel@tonic-gate case 'o': 7807c478bd9Sstevel@tonic-gate /* Place merged types in CTF section in `outfile' */ 7817c478bd9Sstevel@tonic-gate outfile = optarg; 7827c478bd9Sstevel@tonic-gate break; 7837c478bd9Sstevel@tonic-gate case 't': 7847c478bd9Sstevel@tonic-gate /* Insist *all* object files built from C have CTF */ 7857c478bd9Sstevel@tonic-gate require_ctf = 1; 7867c478bd9Sstevel@tonic-gate break; 7877c478bd9Sstevel@tonic-gate case 'v': 7887c478bd9Sstevel@tonic-gate /* More debugging information */ 7897c478bd9Sstevel@tonic-gate verbose = 1; 7907c478bd9Sstevel@tonic-gate break; 7917c478bd9Sstevel@tonic-gate case 'w': 7927c478bd9Sstevel@tonic-gate /* Additive merge with data from `withfile' */ 7937c478bd9Sstevel@tonic-gate withfile = optarg; 7947c478bd9Sstevel@tonic-gate break; 7957c478bd9Sstevel@tonic-gate case 's': 7967c478bd9Sstevel@tonic-gate /* use the dynsym rather than the symtab */ 7977c478bd9Sstevel@tonic-gate dynsym = CTF_USE_DYNSYM; 7987c478bd9Sstevel@tonic-gate break; 7997c478bd9Sstevel@tonic-gate default: 8007c478bd9Sstevel@tonic-gate usage(); 8017c478bd9Sstevel@tonic-gate exit(2); 8027c478bd9Sstevel@tonic-gate } 8037c478bd9Sstevel@tonic-gate } 8047c478bd9Sstevel@tonic-gate 8057c478bd9Sstevel@tonic-gate /* Validate arguments */ 8067c478bd9Sstevel@tonic-gate if (docopy) { 8077c478bd9Sstevel@tonic-gate if (uniqfile != NULL || uniqlabel != NULL || label != NULL || 8087c478bd9Sstevel@tonic-gate outfile != NULL || withfile != NULL || dynsym != 0) 8097c478bd9Sstevel@tonic-gate err++; 8107c478bd9Sstevel@tonic-gate 8117c478bd9Sstevel@tonic-gate if (argc - optind != 2) 8127c478bd9Sstevel@tonic-gate err++; 8137c478bd9Sstevel@tonic-gate } else { 8147c478bd9Sstevel@tonic-gate if (uniqfile != NULL && withfile != NULL) 8157c478bd9Sstevel@tonic-gate err++; 8167c478bd9Sstevel@tonic-gate 8177c478bd9Sstevel@tonic-gate if (uniqlabel != NULL && uniqfile == NULL) 8187c478bd9Sstevel@tonic-gate err++; 8197c478bd9Sstevel@tonic-gate 8207c478bd9Sstevel@tonic-gate if (outfile == NULL || label == NULL) 8217c478bd9Sstevel@tonic-gate err++; 8227c478bd9Sstevel@tonic-gate 8237c478bd9Sstevel@tonic-gate if (argc - optind == 0) 8247c478bd9Sstevel@tonic-gate err++; 8257c478bd9Sstevel@tonic-gate } 8267c478bd9Sstevel@tonic-gate 8277c478bd9Sstevel@tonic-gate if (err) { 8287c478bd9Sstevel@tonic-gate usage(); 8297c478bd9Sstevel@tonic-gate exit(2); 8307c478bd9Sstevel@tonic-gate } 8317c478bd9Sstevel@tonic-gate 8327c478bd9Sstevel@tonic-gate if (uniqfile && access(uniqfile, R_OK) != 0) { 8337c478bd9Sstevel@tonic-gate warning("Uniquification file %s couldn't be opened and " 8347c478bd9Sstevel@tonic-gate "will be ignored.\n", uniqfile); 8357c478bd9Sstevel@tonic-gate uniqfile = NULL; 8367c478bd9Sstevel@tonic-gate } 8377c478bd9Sstevel@tonic-gate if (withfile && access(withfile, R_OK) != 0) { 8387c478bd9Sstevel@tonic-gate warning("With file %s couldn't be opened and will be " 8397c478bd9Sstevel@tonic-gate "ignored.\n", withfile); 8407c478bd9Sstevel@tonic-gate withfile = NULL; 8417c478bd9Sstevel@tonic-gate } 8427c478bd9Sstevel@tonic-gate if (outfile && access(outfile, R_OK|W_OK) != 0) 8437c478bd9Sstevel@tonic-gate terminate("Cannot open output file %s for r/w", outfile); 8447c478bd9Sstevel@tonic-gate 8457c478bd9Sstevel@tonic-gate /* 8467c478bd9Sstevel@tonic-gate * This is ugly, but we don't want to have to have a separate tool 8477c478bd9Sstevel@tonic-gate * (yet) just for copying an ELF section with our specific requirements, 8487c478bd9Sstevel@tonic-gate * so we shoe-horn a copier into ctfmerge. 8497c478bd9Sstevel@tonic-gate */ 8507c478bd9Sstevel@tonic-gate if (docopy) { 851495021bdSRichard Lowe copy_ctf_data(argv[optind], argv[optind + 1]); 8527c478bd9Sstevel@tonic-gate 8537c478bd9Sstevel@tonic-gate exit(0); 8547c478bd9Sstevel@tonic-gate } 8557c478bd9Sstevel@tonic-gate 8567c478bd9Sstevel@tonic-gate set_terminate_cleanup(terminate_cleanup); 8577c478bd9Sstevel@tonic-gate 8587c478bd9Sstevel@tonic-gate /* Sort the input files and strip out duplicates */ 8597c478bd9Sstevel@tonic-gate nifiles = argc - optind; 8607c478bd9Sstevel@tonic-gate ifiles = xmalloc(sizeof (char *) * nifiles); 8617c478bd9Sstevel@tonic-gate tifiles = xmalloc(sizeof (char *) * nifiles); 8627c478bd9Sstevel@tonic-gate 8637c478bd9Sstevel@tonic-gate for (i = 0; i < nifiles; i++) 8647c478bd9Sstevel@tonic-gate tifiles[i] = argv[optind + i]; 8657c478bd9Sstevel@tonic-gate qsort(tifiles, nifiles, sizeof (char *), (int (*)())strcompare); 8667c478bd9Sstevel@tonic-gate 8677c478bd9Sstevel@tonic-gate ifiles[0] = tifiles[0]; 8687c478bd9Sstevel@tonic-gate for (idx = 0, tidx = 1; tidx < nifiles; tidx++) { 8697c478bd9Sstevel@tonic-gate if (strcmp(ifiles[idx], tifiles[tidx]) != 0) 8707c478bd9Sstevel@tonic-gate ifiles[++idx] = tifiles[tidx]; 8717c478bd9Sstevel@tonic-gate } 8727c478bd9Sstevel@tonic-gate nifiles = idx + 1; 8737c478bd9Sstevel@tonic-gate 8747c478bd9Sstevel@tonic-gate /* Make sure they all exist */ 8757c478bd9Sstevel@tonic-gate if ((nielems = count_files(ifiles, nifiles)) < 0) 8767c478bd9Sstevel@tonic-gate terminate("Some input files were inaccessible\n"); 8777c478bd9Sstevel@tonic-gate 8787c478bd9Sstevel@tonic-gate /* Prepare for the merge */ 8797c478bd9Sstevel@tonic-gate wq_init(&wq, nielems); 8807c478bd9Sstevel@tonic-gate 8817c478bd9Sstevel@tonic-gate start_threads(&wq); 8827c478bd9Sstevel@tonic-gate 8837c478bd9Sstevel@tonic-gate /* 8847c478bd9Sstevel@tonic-gate * Start the merge 8857c478bd9Sstevel@tonic-gate * 8867c478bd9Sstevel@tonic-gate * We're reading everything from each of the object files, so we 8877c478bd9Sstevel@tonic-gate * don't need to specify labels. 8887c478bd9Sstevel@tonic-gate */ 8897c478bd9Sstevel@tonic-gate if (read_ctf(ifiles, nifiles, NULL, merge_ctf_cb, 890ae115bc7Smrj &wq, require_ctf) == 0) { 891ae115bc7Smrj /* 892ae115bc7Smrj * If we're verifying that C files have CTF, it's safe to 893ae115bc7Smrj * assume that in this case, we're building only from assembly 894ae115bc7Smrj * inputs. 895ae115bc7Smrj */ 896ae115bc7Smrj if (require_ctf) 897ae115bc7Smrj exit(0); 8987c478bd9Sstevel@tonic-gate terminate("No ctf sections found to merge\n"); 899ae115bc7Smrj } 9007c478bd9Sstevel@tonic-gate 9017c478bd9Sstevel@tonic-gate pthread_mutex_lock(&wq.wq_queue_lock); 9027c478bd9Sstevel@tonic-gate wq.wq_nomorefiles = 1; 9037c478bd9Sstevel@tonic-gate pthread_cond_broadcast(&wq.wq_work_avail); 9047c478bd9Sstevel@tonic-gate pthread_mutex_unlock(&wq.wq_queue_lock); 9057c478bd9Sstevel@tonic-gate 9067c478bd9Sstevel@tonic-gate pthread_mutex_lock(&wq.wq_queue_lock); 9077c478bd9Sstevel@tonic-gate while (wq.wq_alldone == 0) 9087c478bd9Sstevel@tonic-gate pthread_cond_wait(&wq.wq_alldone_cv, &wq.wq_queue_lock); 9097c478bd9Sstevel@tonic-gate pthread_mutex_unlock(&wq.wq_queue_lock); 9107c478bd9Sstevel@tonic-gate 9118168dffcSsommerfe join_threads(&wq); 9128168dffcSsommerfe 9137c478bd9Sstevel@tonic-gate /* 9147c478bd9Sstevel@tonic-gate * All requested files have been merged, with the resulting tree in 9157c478bd9Sstevel@tonic-gate * mstrtd. savetd is the tree that will be placed into the output file. 9167c478bd9Sstevel@tonic-gate * 9177c478bd9Sstevel@tonic-gate * Regardless of whether we're doing a normal uniquification or an 9187c478bd9Sstevel@tonic-gate * additive merge, we need a type tree that has been uniquified 9197c478bd9Sstevel@tonic-gate * against uniqfile or withfile, as appropriate. 9207c478bd9Sstevel@tonic-gate * 9217c478bd9Sstevel@tonic-gate * If we're doing a uniquification, we stuff the resulting tree into 9227c478bd9Sstevel@tonic-gate * outfile. Otherwise, we add the tree to the tree already in withfile. 9237c478bd9Sstevel@tonic-gate */ 9247c478bd9Sstevel@tonic-gate assert(fifo_len(wq.wq_queue) == 1); 9257c478bd9Sstevel@tonic-gate mstrtd = fifo_remove(wq.wq_queue); 9267c478bd9Sstevel@tonic-gate 9277c478bd9Sstevel@tonic-gate if (verbose || debug_level) { 9287c478bd9Sstevel@tonic-gate debug(2, "Statistics for td %p\n", (void *)mstrtd); 9297c478bd9Sstevel@tonic-gate 9307c478bd9Sstevel@tonic-gate iidesc_stats(mstrtd->td_iihash); 9317c478bd9Sstevel@tonic-gate } 9327c478bd9Sstevel@tonic-gate 9337c478bd9Sstevel@tonic-gate if (uniqfile != NULL || withfile != NULL) { 9347c478bd9Sstevel@tonic-gate char *reffile, *reflabel = NULL; 9357c478bd9Sstevel@tonic-gate tdata_t *reftd; 9367c478bd9Sstevel@tonic-gate 9377c478bd9Sstevel@tonic-gate if (uniqfile != NULL) { 9387c478bd9Sstevel@tonic-gate reffile = uniqfile; 9397c478bd9Sstevel@tonic-gate reflabel = uniqlabel; 9407c478bd9Sstevel@tonic-gate } else 9417c478bd9Sstevel@tonic-gate reffile = withfile; 9427c478bd9Sstevel@tonic-gate 9437c478bd9Sstevel@tonic-gate if (read_ctf(&reffile, 1, reflabel, read_ctf_save_cb, 9447c478bd9Sstevel@tonic-gate &reftd, require_ctf) == 0) { 9457c478bd9Sstevel@tonic-gate terminate("No CTF data found in reference file %s\n", 9467c478bd9Sstevel@tonic-gate reffile); 9477c478bd9Sstevel@tonic-gate } 9487c478bd9Sstevel@tonic-gate 9497c478bd9Sstevel@tonic-gate savetd = tdata_new(); 9507c478bd9Sstevel@tonic-gate 9517c478bd9Sstevel@tonic-gate if (CTF_TYPE_ISCHILD(reftd->td_nextid)) 9527c478bd9Sstevel@tonic-gate terminate("No room for additional types in master\n"); 9537c478bd9Sstevel@tonic-gate 9547c478bd9Sstevel@tonic-gate savetd->td_nextid = withfile ? reftd->td_nextid : 9557c478bd9Sstevel@tonic-gate CTF_INDEX_TO_TYPE(1, TRUE); 9567c478bd9Sstevel@tonic-gate merge_into_master(mstrtd, reftd, savetd, 0); 9577c478bd9Sstevel@tonic-gate 9587c478bd9Sstevel@tonic-gate tdata_label_add(savetd, label, CTF_LABEL_LASTIDX); 9597c478bd9Sstevel@tonic-gate 9607c478bd9Sstevel@tonic-gate if (withfile) { 9617c478bd9Sstevel@tonic-gate /* 9627c478bd9Sstevel@tonic-gate * savetd holds the new data to be added to the withfile 9637c478bd9Sstevel@tonic-gate */ 9647c478bd9Sstevel@tonic-gate tdata_t *withtd = reftd; 9657c478bd9Sstevel@tonic-gate 9667c478bd9Sstevel@tonic-gate tdata_merge(withtd, savetd); 9677c478bd9Sstevel@tonic-gate 9687c478bd9Sstevel@tonic-gate savetd = withtd; 9697c478bd9Sstevel@tonic-gate } else { 9707c478bd9Sstevel@tonic-gate char uniqname[MAXPATHLEN]; 9717c478bd9Sstevel@tonic-gate labelent_t *parle; 9727c478bd9Sstevel@tonic-gate 9737c478bd9Sstevel@tonic-gate parle = tdata_label_top(reftd); 9747c478bd9Sstevel@tonic-gate 9757c478bd9Sstevel@tonic-gate savetd->td_parlabel = xstrdup(parle->le_name); 9767c478bd9Sstevel@tonic-gate 9777c478bd9Sstevel@tonic-gate strncpy(uniqname, reffile, sizeof (uniqname)); 9787c478bd9Sstevel@tonic-gate uniqname[MAXPATHLEN - 1] = '\0'; 9797c478bd9Sstevel@tonic-gate savetd->td_parname = xstrdup(basename(uniqname)); 9807c478bd9Sstevel@tonic-gate } 9817c478bd9Sstevel@tonic-gate 9827c478bd9Sstevel@tonic-gate } else { 9837c478bd9Sstevel@tonic-gate /* 9847c478bd9Sstevel@tonic-gate * No post processing. Write the merged tree as-is into the 9857c478bd9Sstevel@tonic-gate * output file. 9867c478bd9Sstevel@tonic-gate */ 9877c478bd9Sstevel@tonic-gate tdata_label_free(mstrtd); 9887c478bd9Sstevel@tonic-gate tdata_label_add(mstrtd, label, CTF_LABEL_LASTIDX); 9897c478bd9Sstevel@tonic-gate 9907c478bd9Sstevel@tonic-gate savetd = mstrtd; 9917c478bd9Sstevel@tonic-gate } 9927c478bd9Sstevel@tonic-gate 9937c478bd9Sstevel@tonic-gate tmpname = mktmpname(outfile, ".ctf"); 9947c478bd9Sstevel@tonic-gate write_ctf(savetd, outfile, tmpname, 995495021bdSRichard Lowe CTF_COMPRESS | write_fuzzy_match | dynsym); 9967c478bd9Sstevel@tonic-gate if (rename(tmpname, outfile) != 0) 9977c478bd9Sstevel@tonic-gate terminate("Couldn't rename output temp file %s", tmpname); 9987c478bd9Sstevel@tonic-gate free(tmpname); 9997c478bd9Sstevel@tonic-gate 10007c478bd9Sstevel@tonic-gate return (0); 10017c478bd9Sstevel@tonic-gate } 1002