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> 1794cc75139SJohn Birrell #if defined(sun) 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> 1864cc75139SJohn Birrell #if defined(sun) 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> 1924cc75139SJohn Birrell #if defined(sun) 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 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 2354cc75139SJohn Birrell #if defined(sun) 2361673e404SJohn Birrell static 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 } 2834cc75139SJohn Birrell #endif 2841673e404SJohn Birrell 2851673e404SJohn Birrell static void 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 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 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 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 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 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 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 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 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 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 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"); 623*a6425ab5SOleksandr 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 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); 6681673e404SJohn Birrell wq->wq_wip[i].wip_batchid = wq->wq_next_batchid++; 6691673e404SJohn Birrell } 6701673e404SJohn Birrell 6711673e404SJohn Birrell pthread_mutex_init(&wq->wq_queue_lock, NULL); 6721673e404SJohn Birrell wq->wq_queue = fifo_new(); 6731673e404SJohn Birrell pthread_cond_init(&wq->wq_work_avail, NULL); 6741673e404SJohn Birrell pthread_cond_init(&wq->wq_work_removed, NULL); 6751673e404SJohn Birrell wq->wq_ninqueue = nfiles; 6761673e404SJohn Birrell wq->wq_nextpownum = 0; 6771673e404SJohn Birrell 6781673e404SJohn Birrell pthread_mutex_init(&wq->wq_donequeue_lock, NULL); 6791673e404SJohn Birrell wq->wq_donequeue = fifo_new(); 6801673e404SJohn Birrell wq->wq_lastdonebatch = -1; 6811673e404SJohn Birrell 6821673e404SJohn Birrell pthread_cond_init(&wq->wq_done_cv, NULL); 6831673e404SJohn Birrell 6841673e404SJohn Birrell pthread_cond_init(&wq->wq_alldone_cv, NULL); 6851673e404SJohn Birrell wq->wq_alldone = 0; 6861673e404SJohn Birrell 6871673e404SJohn Birrell barrier_init(&wq->wq_bar1, wq->wq_nthreads); 6881673e404SJohn Birrell barrier_init(&wq->wq_bar2, wq->wq_nthreads); 6891673e404SJohn Birrell 6901673e404SJohn Birrell wq->wq_nomorefiles = 0; 6911673e404SJohn Birrell } 6921673e404SJohn Birrell 6931673e404SJohn Birrell static void 6941673e404SJohn Birrell start_threads(workqueue_t *wq) 6951673e404SJohn Birrell { 6961673e404SJohn Birrell sigset_t sets; 6971673e404SJohn Birrell int i; 6981673e404SJohn Birrell 6991673e404SJohn Birrell sigemptyset(&sets); 7001673e404SJohn Birrell sigaddset(&sets, SIGINT); 7011673e404SJohn Birrell sigaddset(&sets, SIGQUIT); 7021673e404SJohn Birrell sigaddset(&sets, SIGTERM); 7031673e404SJohn Birrell pthread_sigmask(SIG_BLOCK, &sets, NULL); 7041673e404SJohn Birrell 7051673e404SJohn Birrell for (i = 0; i < wq->wq_nthreads; i++) { 7061670a1c2SRui Paulo pthread_create(&wq->wq_thread[i], NULL, 7071670a1c2SRui Paulo (void *(*)(void *))worker_thread, wq); 7081673e404SJohn Birrell } 7091673e404SJohn Birrell 7104cc75139SJohn Birrell #if defined(sun) 7111673e404SJohn Birrell sigset(SIGINT, handle_sig); 7121673e404SJohn Birrell sigset(SIGQUIT, handle_sig); 7131673e404SJohn Birrell sigset(SIGTERM, handle_sig); 7144cc75139SJohn Birrell #else 7154cc75139SJohn Birrell signal(SIGINT, handle_sig); 7164cc75139SJohn Birrell signal(SIGQUIT, handle_sig); 7174cc75139SJohn Birrell signal(SIGTERM, handle_sig); 7184cc75139SJohn Birrell #endif 7191673e404SJohn Birrell pthread_sigmask(SIG_UNBLOCK, &sets, NULL); 7201673e404SJohn Birrell } 7211673e404SJohn Birrell 7221670a1c2SRui Paulo static void 7231670a1c2SRui Paulo join_threads(workqueue_t *wq) 7241670a1c2SRui Paulo { 7251670a1c2SRui Paulo int i; 7261670a1c2SRui Paulo 7271670a1c2SRui Paulo for (i = 0; i < wq->wq_nthreads; i++) { 7281670a1c2SRui Paulo pthread_join(wq->wq_thread[i], NULL); 7291670a1c2SRui Paulo } 7301670a1c2SRui Paulo } 7311670a1c2SRui Paulo 7321673e404SJohn Birrell static int 7331673e404SJohn Birrell strcompare(const void *p1, const void *p2) 7341673e404SJohn Birrell { 7351673e404SJohn Birrell char *s1 = *((char **)p1); 7361673e404SJohn Birrell char *s2 = *((char **)p2); 7371673e404SJohn Birrell 7381673e404SJohn Birrell return (strcmp(s1, s2)); 7391673e404SJohn Birrell } 7401673e404SJohn Birrell 7411670a1c2SRui Paulo /* 7421670a1c2SRui Paulo * Core work queue structure; passed to worker threads on thread creation 7431670a1c2SRui Paulo * as the main point of coordination. Allocate as a static structure; we 7441670a1c2SRui Paulo * could have put this into a local variable in main, but passing a pointer 7451670a1c2SRui Paulo * into your stack to another thread is fragile at best and leads to some 7461670a1c2SRui Paulo * hard-to-debug failure modes. 7471670a1c2SRui Paulo */ 7481670a1c2SRui Paulo static workqueue_t wq; 7491670a1c2SRui Paulo 7501673e404SJohn Birrell int 7511673e404SJohn Birrell main(int argc, char **argv) 7521673e404SJohn Birrell { 7531673e404SJohn Birrell tdata_t *mstrtd, *savetd; 7541673e404SJohn Birrell char *uniqfile = NULL, *uniqlabel = NULL; 7551673e404SJohn Birrell char *withfile = NULL; 7561673e404SJohn Birrell char *label = NULL; 7571673e404SJohn Birrell char **ifiles, **tifiles; 7581673e404SJohn Birrell int verbose = 0, docopy = 0; 7591673e404SJohn Birrell int write_fuzzy_match = 0; 7601673e404SJohn Birrell int keep_stabs = 0; 7611673e404SJohn Birrell int require_ctf = 0; 7621673e404SJohn Birrell int nifiles, nielems; 7631673e404SJohn Birrell int c, i, idx, tidx, err; 7641673e404SJohn Birrell 7651673e404SJohn Birrell progname = basename(argv[0]); 7661673e404SJohn Birrell 7671673e404SJohn Birrell if (getenv("CTFMERGE_DEBUG_LEVEL")) 7681673e404SJohn Birrell debug_level = atoi(getenv("CTFMERGE_DEBUG_LEVEL")); 7691673e404SJohn Birrell 7701673e404SJohn Birrell err = 0; 7711673e404SJohn Birrell while ((c = getopt(argc, argv, ":cd:D:fgl:L:o:tvw:s")) != EOF) { 7721673e404SJohn Birrell switch (c) { 7731673e404SJohn Birrell case 'c': 7741673e404SJohn Birrell docopy = 1; 7751673e404SJohn Birrell break; 7761673e404SJohn Birrell case 'd': 7771673e404SJohn Birrell /* Uniquify against `uniqfile' */ 7781673e404SJohn Birrell uniqfile = optarg; 7791673e404SJohn Birrell break; 7801673e404SJohn Birrell case 'D': 7811673e404SJohn Birrell /* Uniquify against label `uniqlabel' in `uniqfile' */ 7821673e404SJohn Birrell uniqlabel = optarg; 7831673e404SJohn Birrell break; 7841673e404SJohn Birrell case 'f': 7851673e404SJohn Birrell write_fuzzy_match = CTF_FUZZY_MATCH; 7861673e404SJohn Birrell break; 7871673e404SJohn Birrell case 'g': 7881673e404SJohn Birrell keep_stabs = CTF_KEEP_STABS; 7891673e404SJohn Birrell break; 7901673e404SJohn Birrell case 'l': 7911673e404SJohn Birrell /* Label merged types with `label' */ 7921673e404SJohn Birrell label = optarg; 7931673e404SJohn Birrell break; 7941673e404SJohn Birrell case 'L': 7951673e404SJohn Birrell /* Label merged types with getenv(`label`) */ 7961673e404SJohn Birrell if ((label = getenv(optarg)) == NULL) 7971673e404SJohn Birrell label = CTF_DEFAULT_LABEL; 7981673e404SJohn Birrell break; 7991673e404SJohn Birrell case 'o': 8001673e404SJohn Birrell /* Place merged types in CTF section in `outfile' */ 8011673e404SJohn Birrell outfile = optarg; 8021673e404SJohn Birrell break; 8031673e404SJohn Birrell case 't': 8041673e404SJohn Birrell /* Insist *all* object files built from C have CTF */ 8051673e404SJohn Birrell require_ctf = 1; 8061673e404SJohn Birrell break; 8071673e404SJohn Birrell case 'v': 8081673e404SJohn Birrell /* More debugging information */ 8091673e404SJohn Birrell verbose = 1; 8101673e404SJohn Birrell break; 8111673e404SJohn Birrell case 'w': 8121673e404SJohn Birrell /* Additive merge with data from `withfile' */ 8131673e404SJohn Birrell withfile = optarg; 8141673e404SJohn Birrell break; 8151673e404SJohn Birrell case 's': 8161673e404SJohn Birrell /* use the dynsym rather than the symtab */ 8171673e404SJohn Birrell dynsym = CTF_USE_DYNSYM; 8181673e404SJohn Birrell break; 8191673e404SJohn Birrell default: 8201673e404SJohn Birrell usage(); 8211673e404SJohn Birrell exit(2); 8221673e404SJohn Birrell } 8231673e404SJohn Birrell } 8241673e404SJohn Birrell 8251673e404SJohn Birrell /* Validate arguments */ 8261673e404SJohn Birrell if (docopy) { 8271673e404SJohn Birrell if (uniqfile != NULL || uniqlabel != NULL || label != NULL || 8281673e404SJohn Birrell outfile != NULL || withfile != NULL || dynsym != 0) 8291673e404SJohn Birrell err++; 8301673e404SJohn Birrell 8311673e404SJohn Birrell if (argc - optind != 2) 8321673e404SJohn Birrell err++; 8331673e404SJohn Birrell } else { 8341673e404SJohn Birrell if (uniqfile != NULL && withfile != NULL) 8351673e404SJohn Birrell err++; 8361673e404SJohn Birrell 8371673e404SJohn Birrell if (uniqlabel != NULL && uniqfile == NULL) 8381673e404SJohn Birrell err++; 8391673e404SJohn Birrell 8401673e404SJohn Birrell if (outfile == NULL || label == NULL) 8411673e404SJohn Birrell err++; 8421673e404SJohn Birrell 8431673e404SJohn Birrell if (argc - optind == 0) 8441673e404SJohn Birrell err++; 8451673e404SJohn Birrell } 8461673e404SJohn Birrell 8471673e404SJohn Birrell if (err) { 8481673e404SJohn Birrell usage(); 8491673e404SJohn Birrell exit(2); 8501673e404SJohn Birrell } 8511673e404SJohn Birrell 8521673e404SJohn Birrell if (getenv("STRIPSTABS_KEEP_STABS") != NULL) 8531673e404SJohn Birrell keep_stabs = CTF_KEEP_STABS; 8541673e404SJohn Birrell 8551673e404SJohn Birrell if (uniqfile && access(uniqfile, R_OK) != 0) { 8561673e404SJohn Birrell warning("Uniquification file %s couldn't be opened and " 8571673e404SJohn Birrell "will be ignored.\n", uniqfile); 8581673e404SJohn Birrell uniqfile = NULL; 8591673e404SJohn Birrell } 8601673e404SJohn Birrell if (withfile && access(withfile, R_OK) != 0) { 8611673e404SJohn Birrell warning("With file %s couldn't be opened and will be " 8621673e404SJohn Birrell "ignored.\n", withfile); 8631673e404SJohn Birrell withfile = NULL; 8641673e404SJohn Birrell } 8651673e404SJohn Birrell if (outfile && access(outfile, R_OK|W_OK) != 0) 8661673e404SJohn Birrell terminate("Cannot open output file %s for r/w", outfile); 8671673e404SJohn Birrell 8681673e404SJohn Birrell /* 8691673e404SJohn Birrell * This is ugly, but we don't want to have to have a separate tool 8701673e404SJohn Birrell * (yet) just for copying an ELF section with our specific requirements, 8711673e404SJohn Birrell * so we shoe-horn a copier into ctfmerge. 8721673e404SJohn Birrell */ 8731673e404SJohn Birrell if (docopy) { 8741673e404SJohn Birrell copy_ctf_data(argv[optind], argv[optind + 1], keep_stabs); 8751673e404SJohn Birrell 8761673e404SJohn Birrell exit(0); 8771673e404SJohn Birrell } 8781673e404SJohn Birrell 8791673e404SJohn Birrell set_terminate_cleanup(terminate_cleanup); 8801673e404SJohn Birrell 8811673e404SJohn Birrell /* Sort the input files and strip out duplicates */ 8821673e404SJohn Birrell nifiles = argc - optind; 8831673e404SJohn Birrell ifiles = xmalloc(sizeof (char *) * nifiles); 8841673e404SJohn Birrell tifiles = xmalloc(sizeof (char *) * nifiles); 8851673e404SJohn Birrell 8861673e404SJohn Birrell for (i = 0; i < nifiles; i++) 8871673e404SJohn Birrell tifiles[i] = argv[optind + i]; 8881673e404SJohn Birrell qsort(tifiles, nifiles, sizeof (char *), (int (*)())strcompare); 8891673e404SJohn Birrell 8901673e404SJohn Birrell ifiles[0] = tifiles[0]; 8911673e404SJohn Birrell for (idx = 0, tidx = 1; tidx < nifiles; tidx++) { 8921673e404SJohn Birrell if (strcmp(ifiles[idx], tifiles[tidx]) != 0) 8931673e404SJohn Birrell ifiles[++idx] = tifiles[tidx]; 8941673e404SJohn Birrell } 8951673e404SJohn Birrell nifiles = idx + 1; 8961673e404SJohn Birrell 8971673e404SJohn Birrell /* Make sure they all exist */ 8981673e404SJohn Birrell if ((nielems = count_files(ifiles, nifiles)) < 0) 8991673e404SJohn Birrell terminate("Some input files were inaccessible\n"); 9001673e404SJohn Birrell 9011673e404SJohn Birrell /* Prepare for the merge */ 9021673e404SJohn Birrell wq_init(&wq, nielems); 9031673e404SJohn Birrell 9041673e404SJohn Birrell start_threads(&wq); 9051673e404SJohn Birrell 9061673e404SJohn Birrell /* 9071673e404SJohn Birrell * Start the merge 9081673e404SJohn Birrell * 9091673e404SJohn Birrell * We're reading everything from each of the object files, so we 9101673e404SJohn Birrell * don't need to specify labels. 9111673e404SJohn Birrell */ 9121673e404SJohn Birrell if (read_ctf(ifiles, nifiles, NULL, merge_ctf_cb, 9131673e404SJohn Birrell &wq, require_ctf) == 0) { 9141673e404SJohn Birrell /* 9151673e404SJohn Birrell * If we're verifying that C files have CTF, it's safe to 9161673e404SJohn Birrell * assume that in this case, we're building only from assembly 9171673e404SJohn Birrell * inputs. 9181673e404SJohn Birrell */ 9191673e404SJohn Birrell if (require_ctf) 9201673e404SJohn Birrell exit(0); 9211673e404SJohn Birrell terminate("No ctf sections found to merge\n"); 9221673e404SJohn Birrell } 9231673e404SJohn Birrell 9241673e404SJohn Birrell pthread_mutex_lock(&wq.wq_queue_lock); 9251673e404SJohn Birrell wq.wq_nomorefiles = 1; 9261673e404SJohn Birrell pthread_cond_broadcast(&wq.wq_work_avail); 9271673e404SJohn Birrell pthread_mutex_unlock(&wq.wq_queue_lock); 9281673e404SJohn Birrell 9291673e404SJohn Birrell pthread_mutex_lock(&wq.wq_queue_lock); 9301673e404SJohn Birrell while (wq.wq_alldone == 0) 9311673e404SJohn Birrell pthread_cond_wait(&wq.wq_alldone_cv, &wq.wq_queue_lock); 9321673e404SJohn Birrell pthread_mutex_unlock(&wq.wq_queue_lock); 9331673e404SJohn Birrell 9341670a1c2SRui Paulo join_threads(&wq); 9351670a1c2SRui Paulo 9361673e404SJohn Birrell /* 9371673e404SJohn Birrell * All requested files have been merged, with the resulting tree in 9381673e404SJohn Birrell * mstrtd. savetd is the tree that will be placed into the output file. 9391673e404SJohn Birrell * 9401673e404SJohn Birrell * Regardless of whether we're doing a normal uniquification or an 9411673e404SJohn Birrell * additive merge, we need a type tree that has been uniquified 9421673e404SJohn Birrell * against uniqfile or withfile, as appropriate. 9431673e404SJohn Birrell * 9441673e404SJohn Birrell * If we're doing a uniquification, we stuff the resulting tree into 9451673e404SJohn Birrell * outfile. Otherwise, we add the tree to the tree already in withfile. 9461673e404SJohn Birrell */ 9471673e404SJohn Birrell assert(fifo_len(wq.wq_queue) == 1); 9481673e404SJohn Birrell mstrtd = fifo_remove(wq.wq_queue); 9491673e404SJohn Birrell 9501673e404SJohn Birrell if (verbose || debug_level) { 9511673e404SJohn Birrell debug(2, "Statistics for td %p\n", (void *)mstrtd); 9521673e404SJohn Birrell 9531673e404SJohn Birrell iidesc_stats(mstrtd->td_iihash); 9541673e404SJohn Birrell } 9551673e404SJohn Birrell 9561673e404SJohn Birrell if (uniqfile != NULL || withfile != NULL) { 9571673e404SJohn Birrell char *reffile, *reflabel = NULL; 9581673e404SJohn Birrell tdata_t *reftd; 9591673e404SJohn Birrell 9601673e404SJohn Birrell if (uniqfile != NULL) { 9611673e404SJohn Birrell reffile = uniqfile; 9621673e404SJohn Birrell reflabel = uniqlabel; 9631673e404SJohn Birrell } else 9641673e404SJohn Birrell reffile = withfile; 9651673e404SJohn Birrell 9661673e404SJohn Birrell if (read_ctf(&reffile, 1, reflabel, read_ctf_save_cb, 9671673e404SJohn Birrell &reftd, require_ctf) == 0) { 9681673e404SJohn Birrell terminate("No CTF data found in reference file %s\n", 9691673e404SJohn Birrell reffile); 9701673e404SJohn Birrell } 9711673e404SJohn Birrell 9721673e404SJohn Birrell savetd = tdata_new(); 9731673e404SJohn Birrell 9741673e404SJohn Birrell if (CTF_TYPE_ISCHILD(reftd->td_nextid)) 9751673e404SJohn Birrell terminate("No room for additional types in master\n"); 9761673e404SJohn Birrell 9771673e404SJohn Birrell savetd->td_nextid = withfile ? reftd->td_nextid : 9781673e404SJohn Birrell CTF_INDEX_TO_TYPE(1, TRUE); 9791673e404SJohn Birrell merge_into_master(mstrtd, reftd, savetd, 0); 9801673e404SJohn Birrell 9811673e404SJohn Birrell tdata_label_add(savetd, label, CTF_LABEL_LASTIDX); 9821673e404SJohn Birrell 9831673e404SJohn Birrell if (withfile) { 9841673e404SJohn Birrell /* 9851673e404SJohn Birrell * savetd holds the new data to be added to the withfile 9861673e404SJohn Birrell */ 9871673e404SJohn Birrell tdata_t *withtd = reftd; 9881673e404SJohn Birrell 9891673e404SJohn Birrell tdata_merge(withtd, savetd); 9901673e404SJohn Birrell 9911673e404SJohn Birrell savetd = withtd; 9921673e404SJohn Birrell } else { 9931673e404SJohn Birrell char uniqname[MAXPATHLEN]; 9941673e404SJohn Birrell labelent_t *parle; 9951673e404SJohn Birrell 9961673e404SJohn Birrell parle = tdata_label_top(reftd); 9971673e404SJohn Birrell 9981673e404SJohn Birrell savetd->td_parlabel = xstrdup(parle->le_name); 9991673e404SJohn Birrell 10001673e404SJohn Birrell strncpy(uniqname, reffile, sizeof (uniqname)); 10011673e404SJohn Birrell uniqname[MAXPATHLEN - 1] = '\0'; 10021673e404SJohn Birrell savetd->td_parname = xstrdup(basename(uniqname)); 10031673e404SJohn Birrell } 10041673e404SJohn Birrell 10051673e404SJohn Birrell } else { 10061673e404SJohn Birrell /* 10071673e404SJohn Birrell * No post processing. Write the merged tree as-is into the 10081673e404SJohn Birrell * output file. 10091673e404SJohn Birrell */ 10101673e404SJohn Birrell tdata_label_free(mstrtd); 10111673e404SJohn Birrell tdata_label_add(mstrtd, label, CTF_LABEL_LASTIDX); 10121673e404SJohn Birrell 10131673e404SJohn Birrell savetd = mstrtd; 10141673e404SJohn Birrell } 10151673e404SJohn Birrell 10161673e404SJohn Birrell tmpname = mktmpname(outfile, ".ctf"); 10171673e404SJohn Birrell write_ctf(savetd, outfile, tmpname, 1018*a6425ab5SOleksandr Tymoshenko CTF_COMPRESS | CTF_SWAP_BYTES | write_fuzzy_match | dynsym | keep_stabs); 10191673e404SJohn Birrell if (rename(tmpname, outfile) != 0) 10201673e404SJohn Birrell terminate("Couldn't rename output temp file %s", tmpname); 10211673e404SJohn Birrell free(tmpname); 10221673e404SJohn Birrell 10231673e404SJohn Birrell return (0); 10241673e404SJohn Birrell } 1025