1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate /* 30*0Sstevel@tonic-gate * Given several files containing CTF data, merge and uniquify that data into 31*0Sstevel@tonic-gate * a single CTF section in an output file. 32*0Sstevel@tonic-gate * 33*0Sstevel@tonic-gate * Merges can proceed independently. As such, we perform the merges in parallel 34*0Sstevel@tonic-gate * using a worker thread model. A given glob of CTF data (either all of the CTF 35*0Sstevel@tonic-gate * data from a single input file, or the result of one or more merges) can only 36*0Sstevel@tonic-gate * be involved in a single merge at any given time, so the process decreases in 37*0Sstevel@tonic-gate * parallelism, especially towards the end, as more and more files are 38*0Sstevel@tonic-gate * consolidated, finally resulting in a single merge of two large CTF graphs. 39*0Sstevel@tonic-gate * Unfortunately, the last merge is also the slowest, as the two graphs being 40*0Sstevel@tonic-gate * merged are each the product of merges of half of the input files. 41*0Sstevel@tonic-gate * 42*0Sstevel@tonic-gate * The algorithm consists of two phases, described in detail below. The first 43*0Sstevel@tonic-gate * phase entails the merging of CTF data in groups of eight. The second phase 44*0Sstevel@tonic-gate * takes the results of Phase I, and merges them two at a time. This disparity 45*0Sstevel@tonic-gate * is due to an observation that the merge time increases at least quadratically 46*0Sstevel@tonic-gate * with the size of the CTF data being merged. As such, merges of CTF graphs 47*0Sstevel@tonic-gate * newly read from input files are much faster than merges of CTF graphs that 48*0Sstevel@tonic-gate * are themselves the results of prior merges. 49*0Sstevel@tonic-gate * 50*0Sstevel@tonic-gate * A further complication is the need to ensure the repeatability of CTF merges. 51*0Sstevel@tonic-gate * That is, a merge should produce the same output every time, given the same 52*0Sstevel@tonic-gate * input. In both phases, this consistency requirement is met by imposing an 53*0Sstevel@tonic-gate * ordering on the merge process, thus ensuring that a given set of input files 54*0Sstevel@tonic-gate * are merged in the same order every time. 55*0Sstevel@tonic-gate * 56*0Sstevel@tonic-gate * Phase I 57*0Sstevel@tonic-gate * 58*0Sstevel@tonic-gate * The main thread reads the input files one by one, transforming the CTF 59*0Sstevel@tonic-gate * data they contain into tdata structures. When a given file has been read 60*0Sstevel@tonic-gate * and parsed, it is placed on the work queue for retrieval by worker threads. 61*0Sstevel@tonic-gate * 62*0Sstevel@tonic-gate * Central to Phase I is the Work In Progress (wip) array, which is used to 63*0Sstevel@tonic-gate * merge batches of files in a predictable order. Files are read by the main 64*0Sstevel@tonic-gate * thread, and are merged into wip array elements in round-robin order. When 65*0Sstevel@tonic-gate * the number of files merged into a given array slot equals the batch size, 66*0Sstevel@tonic-gate * the merged CTF graph in that array is added to the done slot in order by 67*0Sstevel@tonic-gate * array slot. 68*0Sstevel@tonic-gate * 69*0Sstevel@tonic-gate * For example, consider a case where we have five input files, a batch size 70*0Sstevel@tonic-gate * of two, a wip array size of two, and two worker threads (T1 and T2). 71*0Sstevel@tonic-gate * 72*0Sstevel@tonic-gate * 1. The wip array elements are assigned initial batch numbers 0 and 1. 73*0Sstevel@tonic-gate * 2. T1 reads an input file from the input queue (wq_queue). This is the 74*0Sstevel@tonic-gate * first input file, so it is placed into wip[0]. The second file is 75*0Sstevel@tonic-gate * similarly read and placed into wip[1]. The wip array slots now contain 76*0Sstevel@tonic-gate * one file each (wip_nmerged == 1). 77*0Sstevel@tonic-gate * 3. T1 reads the third input file, which it merges into wip[0]. The 78*0Sstevel@tonic-gate * number of files in wip[0] is equal to the batch size. 79*0Sstevel@tonic-gate * 4. T2 reads the fourth input file, which it merges into wip[1]. wip[1] 80*0Sstevel@tonic-gate * is now full too. 81*0Sstevel@tonic-gate * 5. T2 attempts to place the contents of wip[1] on the done queue 82*0Sstevel@tonic-gate * (wq_done_queue), but it can't, since the batch ID for wip[1] is 1. 83*0Sstevel@tonic-gate * Batch 0 needs to be on the done queue before batch 1 can be added, so 84*0Sstevel@tonic-gate * T2 blocks on wip[1]'s cv. 85*0Sstevel@tonic-gate * 6. T1 attempts to place the contents of wip[0] on the done queue, and 86*0Sstevel@tonic-gate * succeeds, updating wq_lastdonebatch to 0. It clears wip[0], and sets 87*0Sstevel@tonic-gate * its batch ID to 2. T1 then signals wip[1]'s cv to awaken T2. 88*0Sstevel@tonic-gate * 7. T2 wakes up, notices that wq_lastdonebatch is 0, which means that 89*0Sstevel@tonic-gate * batch 1 can now be added. It adds wip[1] to the done queue, clears 90*0Sstevel@tonic-gate * wip[1], and sets its batch ID to 3. It signals wip[0]'s cv, and 91*0Sstevel@tonic-gate * restarts. 92*0Sstevel@tonic-gate * 93*0Sstevel@tonic-gate * The above process continues until all input files have been consumed. At 94*0Sstevel@tonic-gate * this point, a pair of barriers are used to allow a single thread to move 95*0Sstevel@tonic-gate * any partial batches from the wip array to the done array in batch ID order. 96*0Sstevel@tonic-gate * When this is complete, wq_done_queue is moved to wq_queue, and Phase II 97*0Sstevel@tonic-gate * begins. 98*0Sstevel@tonic-gate * 99*0Sstevel@tonic-gate * Locking Semantics (Phase I) 100*0Sstevel@tonic-gate * 101*0Sstevel@tonic-gate * The input queue (wq_queue) and the done queue (wq_done_queue) are 102*0Sstevel@tonic-gate * protected by separate mutexes - wq_queue_lock and wq_done_queue. wip 103*0Sstevel@tonic-gate * array slots are protected by their own mutexes, which must be grabbed 104*0Sstevel@tonic-gate * before releasing the input queue lock. The wip array lock is dropped 105*0Sstevel@tonic-gate * when the thread restarts the loop. If the array slot was full, the 106*0Sstevel@tonic-gate * array lock will be held while the slot contents are added to the done 107*0Sstevel@tonic-gate * queue. The done queue lock is used to protect the wip slot cv's. 108*0Sstevel@tonic-gate * 109*0Sstevel@tonic-gate * The pow number is protected by the queue lock. The master batch ID 110*0Sstevel@tonic-gate * and last completed batch (wq_lastdonebatch) counters are protected *in 111*0Sstevel@tonic-gate * Phase I* by the done queue lock. 112*0Sstevel@tonic-gate * 113*0Sstevel@tonic-gate * Phase II 114*0Sstevel@tonic-gate * 115*0Sstevel@tonic-gate * When Phase II begins, the queue consists of the merged batches from the 116*0Sstevel@tonic-gate * first phase. Assume we have five batches: 117*0Sstevel@tonic-gate * 118*0Sstevel@tonic-gate * Q: a b c d e 119*0Sstevel@tonic-gate * 120*0Sstevel@tonic-gate * Using the same batch ID mechanism we used in Phase I, but without the wip 121*0Sstevel@tonic-gate * array, worker threads remove two entries at a time from the beginning of 122*0Sstevel@tonic-gate * the queue. These two entries are merged, and are added back to the tail 123*0Sstevel@tonic-gate * of the queue, as follows: 124*0Sstevel@tonic-gate * 125*0Sstevel@tonic-gate * Q: a b c d e # start 126*0Sstevel@tonic-gate * Q: c d e ab # a, b removed, merged, added to end 127*0Sstevel@tonic-gate * Q: e ab cd # c, d removed, merged, added to end 128*0Sstevel@tonic-gate * Q: cd eab # e, ab removed, merged, added to end 129*0Sstevel@tonic-gate * Q: cdeab # cd, eab removed, merged, added to end 130*0Sstevel@tonic-gate * 131*0Sstevel@tonic-gate * When one entry remains on the queue, with no merges outstanding, Phase II 132*0Sstevel@tonic-gate * finishes. We pre-determine the stopping point by pre-calculating the 133*0Sstevel@tonic-gate * number of nodes that will appear on the list. In the example above, the 134*0Sstevel@tonic-gate * number (wq_ninqueue) is 9. When ninqueue is 1, we conclude Phase II by 135*0Sstevel@tonic-gate * signaling the main thread via wq_done_cv. 136*0Sstevel@tonic-gate * 137*0Sstevel@tonic-gate * Locking Semantics (Phase II) 138*0Sstevel@tonic-gate * 139*0Sstevel@tonic-gate * The queue (wq_queue), ninqueue, and the master batch ID and last 140*0Sstevel@tonic-gate * completed batch counters are protected by wq_queue_lock. The done 141*0Sstevel@tonic-gate * queue and corresponding lock are unused in Phase II as is the wip array. 142*0Sstevel@tonic-gate * 143*0Sstevel@tonic-gate * Uniquification 144*0Sstevel@tonic-gate * 145*0Sstevel@tonic-gate * We want the CTF data that goes into a given module to be as small as 146*0Sstevel@tonic-gate * possible. For example, we don't want it to contain any type data that may 147*0Sstevel@tonic-gate * be present in another common module. As such, after creating the master 148*0Sstevel@tonic-gate * tdata_t for a given module, we can, if requested by the user, uniquify it 149*0Sstevel@tonic-gate * against the tdata_t from another module (genunix in the case of the SunOS 150*0Sstevel@tonic-gate * kernel). We perform a merge between the tdata_t for this module and the 151*0Sstevel@tonic-gate * tdata_t from genunix. Nodes found in this module that are not present in 152*0Sstevel@tonic-gate * genunix are added to a third tdata_t - the uniquified tdata_t. 153*0Sstevel@tonic-gate * 154*0Sstevel@tonic-gate * Additive Merges 155*0Sstevel@tonic-gate * 156*0Sstevel@tonic-gate * In some cases, for example if we are issuing a new version of a common 157*0Sstevel@tonic-gate * module in a patch, we need to make sure that the CTF data already present 158*0Sstevel@tonic-gate * in that module does not change. Changes to this data would void the CTF 159*0Sstevel@tonic-gate * data in any module that uniquified against the common module. To preserve 160*0Sstevel@tonic-gate * the existing data, we can perform what is known as an additive merge. In 161*0Sstevel@tonic-gate * this case, a final uniquification is performed against the CTF data in the 162*0Sstevel@tonic-gate * previous version of the module. The result will be the placement of new 163*0Sstevel@tonic-gate * and changed data after the existing data, thus preserving the existing type 164*0Sstevel@tonic-gate * ID space. 165*0Sstevel@tonic-gate * 166*0Sstevel@tonic-gate * Saving the result 167*0Sstevel@tonic-gate * 168*0Sstevel@tonic-gate * When the merges are complete, the resulting tdata_t is placed into the 169*0Sstevel@tonic-gate * output file, replacing the .SUNW_ctf section (if any) already in that file. 170*0Sstevel@tonic-gate * 171*0Sstevel@tonic-gate * The person who changes the merging thread code in this file without updating 172*0Sstevel@tonic-gate * this comment will not live to see the stock hit five. 173*0Sstevel@tonic-gate */ 174*0Sstevel@tonic-gate 175*0Sstevel@tonic-gate #include <stdio.h> 176*0Sstevel@tonic-gate #include <stdlib.h> 177*0Sstevel@tonic-gate #include <unistd.h> 178*0Sstevel@tonic-gate #include <pthread.h> 179*0Sstevel@tonic-gate #include <assert.h> 180*0Sstevel@tonic-gate #include <synch.h> 181*0Sstevel@tonic-gate #include <signal.h> 182*0Sstevel@tonic-gate #include <libgen.h> 183*0Sstevel@tonic-gate #include <string.h> 184*0Sstevel@tonic-gate #include <errno.h> 185*0Sstevel@tonic-gate #include <alloca.h> 186*0Sstevel@tonic-gate #include <sys/param.h> 187*0Sstevel@tonic-gate #include <sys/types.h> 188*0Sstevel@tonic-gate #include <sys/mman.h> 189*0Sstevel@tonic-gate #include <sys/sysconf.h> 190*0Sstevel@tonic-gate 191*0Sstevel@tonic-gate #include "ctf_headers.h" 192*0Sstevel@tonic-gate #include "ctftools.h" 193*0Sstevel@tonic-gate #include "ctfmerge.h" 194*0Sstevel@tonic-gate #include "traverse.h" 195*0Sstevel@tonic-gate #include "memory.h" 196*0Sstevel@tonic-gate #include "fifo.h" 197*0Sstevel@tonic-gate #include "barrier.h" 198*0Sstevel@tonic-gate 199*0Sstevel@tonic-gate #pragma init(bigheap) 200*0Sstevel@tonic-gate 201*0Sstevel@tonic-gate #define MERGE_PHASE1_BATCH_SIZE 8 202*0Sstevel@tonic-gate #define MERGE_PHASE1_MAX_SLOTS 5 203*0Sstevel@tonic-gate #define MERGE_INPUT_THROTTLE_LEN 10 204*0Sstevel@tonic-gate 205*0Sstevel@tonic-gate const char *progname; 206*0Sstevel@tonic-gate static char *outfile = NULL; 207*0Sstevel@tonic-gate static char *tmpname = NULL; 208*0Sstevel@tonic-gate static int dynsym; 209*0Sstevel@tonic-gate int debug_level = DEBUG_LEVEL; 210*0Sstevel@tonic-gate 211*0Sstevel@tonic-gate void 212*0Sstevel@tonic-gate usage(void) 213*0Sstevel@tonic-gate { 214*0Sstevel@tonic-gate (void) fprintf(stderr, 215*0Sstevel@tonic-gate "Usage: %s [-fgstv] -l label | -L labelenv -o outfile file ...\n" 216*0Sstevel@tonic-gate " %s [-fgstv] -l label | -L labelenv -o outfile -d uniqfile\n" 217*0Sstevel@tonic-gate " %*s [-g] [-D uniqlabel] file ...\n" 218*0Sstevel@tonic-gate " %s [-fgstv] -l label | -L labelenv -o outfile -w withfile " 219*0Sstevel@tonic-gate "file ...\n" 220*0Sstevel@tonic-gate " %s [-g] -c srcfile destfile\n" 221*0Sstevel@tonic-gate "\n" 222*0Sstevel@tonic-gate " Note: if -L labelenv is specified and labelenv is not set in\n" 223*0Sstevel@tonic-gate " the environment, a default value is used.\n", 224*0Sstevel@tonic-gate progname, progname, strlen(progname), " ", 225*0Sstevel@tonic-gate progname, progname); 226*0Sstevel@tonic-gate } 227*0Sstevel@tonic-gate 228*0Sstevel@tonic-gate static void 229*0Sstevel@tonic-gate bigheap(void) 230*0Sstevel@tonic-gate { 231*0Sstevel@tonic-gate size_t big, *size; 232*0Sstevel@tonic-gate int sizes, i; 233*0Sstevel@tonic-gate struct memcntl_mha mha; 234*0Sstevel@tonic-gate 235*0Sstevel@tonic-gate /* 236*0Sstevel@tonic-gate * First, get the available pagesizes. 237*0Sstevel@tonic-gate */ 238*0Sstevel@tonic-gate if ((sizes = getpagesizes(NULL, 0)) == -1) 239*0Sstevel@tonic-gate return; 240*0Sstevel@tonic-gate 241*0Sstevel@tonic-gate if ((size = alloca(sizeof (size_t) * sizes)) == NULL) 242*0Sstevel@tonic-gate return; 243*0Sstevel@tonic-gate 244*0Sstevel@tonic-gate if (getpagesizes(size, sizes) == -1 || sizes == 1) 245*0Sstevel@tonic-gate return; 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate big = size[sizes - 1]; 248*0Sstevel@tonic-gate if (big & (big - 1)) { 249*0Sstevel@tonic-gate /* 250*0Sstevel@tonic-gate * The largest page size is not a power of two for some 251*0Sstevel@tonic-gate * inexplicable reason; return. 252*0Sstevel@tonic-gate */ 253*0Sstevel@tonic-gate return; 254*0Sstevel@tonic-gate } 255*0Sstevel@tonic-gate 256*0Sstevel@tonic-gate /* 257*0Sstevel@tonic-gate * Now, align our break to the largest page size. 258*0Sstevel@tonic-gate */ 259*0Sstevel@tonic-gate if (brk((void *)((((uintptr_t)sbrk(0) - 1) & ~(big - 1)) + big)) != 0) 260*0Sstevel@tonic-gate return; 261*0Sstevel@tonic-gate 262*0Sstevel@tonic-gate /* 263*0Sstevel@tonic-gate * Finally, set our heap to use the largest page size for which the 264*0Sstevel@tonic-gate * MC_HAT_ADVISE doesn't return EAGAIN. 265*0Sstevel@tonic-gate */ 266*0Sstevel@tonic-gate mha.mha_cmd = MHA_MAPSIZE_BSSBRK; 267*0Sstevel@tonic-gate mha.mha_flags = 0; 268*0Sstevel@tonic-gate 269*0Sstevel@tonic-gate for (i = sizes - 1; i >= 0; i--) { 270*0Sstevel@tonic-gate mha.mha_pagesize = size[i]; 271*0Sstevel@tonic-gate 272*0Sstevel@tonic-gate if (memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mha, 0, 0) != -1) 273*0Sstevel@tonic-gate break; 274*0Sstevel@tonic-gate 275*0Sstevel@tonic-gate if (errno != EAGAIN) 276*0Sstevel@tonic-gate break; 277*0Sstevel@tonic-gate } 278*0Sstevel@tonic-gate } 279*0Sstevel@tonic-gate 280*0Sstevel@tonic-gate static void 281*0Sstevel@tonic-gate finalize_phase_one(workqueue_t *wq) 282*0Sstevel@tonic-gate { 283*0Sstevel@tonic-gate int startslot, i; 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate /* 286*0Sstevel@tonic-gate * wip slots are cleared out only when maxbatchsz td's have been merged 287*0Sstevel@tonic-gate * into them. We're not guaranteed that the number of files we're 288*0Sstevel@tonic-gate * merging is a multiple of maxbatchsz, so there will be some partial 289*0Sstevel@tonic-gate * groups in the wip array. Move them to the done queue in batch ID 290*0Sstevel@tonic-gate * order, starting with the slot containing the next batch that would 291*0Sstevel@tonic-gate * have been placed on the done queue, followed by the others. 292*0Sstevel@tonic-gate * One thread will be doing this while the others wait at the barrier 293*0Sstevel@tonic-gate * back in worker_thread(), so we don't need to worry about pesky things 294*0Sstevel@tonic-gate * like locks. 295*0Sstevel@tonic-gate */ 296*0Sstevel@tonic-gate 297*0Sstevel@tonic-gate for (startslot = -1, i = 0; i < wq->wq_nwipslots; i++) { 298*0Sstevel@tonic-gate if (wq->wq_wip[i].wip_batchid == wq->wq_lastdonebatch + 1) { 299*0Sstevel@tonic-gate startslot = i; 300*0Sstevel@tonic-gate break; 301*0Sstevel@tonic-gate } 302*0Sstevel@tonic-gate } 303*0Sstevel@tonic-gate 304*0Sstevel@tonic-gate assert(startslot != -1); 305*0Sstevel@tonic-gate 306*0Sstevel@tonic-gate for (i = startslot; i < startslot + wq->wq_nwipslots; i++) { 307*0Sstevel@tonic-gate int slotnum = i % wq->wq_nwipslots; 308*0Sstevel@tonic-gate wip_t *wipslot = &wq->wq_wip[slotnum]; 309*0Sstevel@tonic-gate 310*0Sstevel@tonic-gate if (wipslot->wip_td != NULL) { 311*0Sstevel@tonic-gate debug(2, "clearing slot %d (%d) (saving %d)\n", 312*0Sstevel@tonic-gate slotnum, i, wipslot->wip_nmerged); 313*0Sstevel@tonic-gate } else 314*0Sstevel@tonic-gate debug(2, "clearing slot %d (%d)\n", slotnum, i); 315*0Sstevel@tonic-gate 316*0Sstevel@tonic-gate if (wipslot->wip_td != NULL) { 317*0Sstevel@tonic-gate fifo_add(wq->wq_donequeue, wipslot->wip_td); 318*0Sstevel@tonic-gate wq->wq_wip[slotnum].wip_td = NULL; 319*0Sstevel@tonic-gate } 320*0Sstevel@tonic-gate } 321*0Sstevel@tonic-gate 322*0Sstevel@tonic-gate wq->wq_lastdonebatch = wq->wq_next_batchid++; 323*0Sstevel@tonic-gate 324*0Sstevel@tonic-gate debug(2, "phase one done: donequeue has %d items\n", 325*0Sstevel@tonic-gate fifo_len(wq->wq_donequeue)); 326*0Sstevel@tonic-gate } 327*0Sstevel@tonic-gate 328*0Sstevel@tonic-gate static void 329*0Sstevel@tonic-gate init_phase_two(workqueue_t *wq) 330*0Sstevel@tonic-gate { 331*0Sstevel@tonic-gate int num; 332*0Sstevel@tonic-gate 333*0Sstevel@tonic-gate /* 334*0Sstevel@tonic-gate * We're going to continually merge the first two entries on the queue, 335*0Sstevel@tonic-gate * placing the result on the end, until there's nothing left to merge. 336*0Sstevel@tonic-gate * At that point, everything will have been merged into one. The 337*0Sstevel@tonic-gate * initial value of ninqueue needs to be equal to the total number of 338*0Sstevel@tonic-gate * entries that will show up on the queue, both at the start of the 339*0Sstevel@tonic-gate * phase and as generated by merges during the phase. 340*0Sstevel@tonic-gate */ 341*0Sstevel@tonic-gate wq->wq_ninqueue = num = fifo_len(wq->wq_donequeue); 342*0Sstevel@tonic-gate while (num != 1) { 343*0Sstevel@tonic-gate wq->wq_ninqueue += num / 2; 344*0Sstevel@tonic-gate num = num / 2 + num % 2; 345*0Sstevel@tonic-gate } 346*0Sstevel@tonic-gate 347*0Sstevel@tonic-gate /* 348*0Sstevel@tonic-gate * Move the done queue to the work queue. We won't be using the done 349*0Sstevel@tonic-gate * queue in phase 2. 350*0Sstevel@tonic-gate */ 351*0Sstevel@tonic-gate assert(fifo_len(wq->wq_queue) == 0); 352*0Sstevel@tonic-gate fifo_free(wq->wq_queue, NULL); 353*0Sstevel@tonic-gate wq->wq_queue = wq->wq_donequeue; 354*0Sstevel@tonic-gate } 355*0Sstevel@tonic-gate 356*0Sstevel@tonic-gate static void 357*0Sstevel@tonic-gate wip_save_work(workqueue_t *wq, wip_t *slot, int slotnum) 358*0Sstevel@tonic-gate { 359*0Sstevel@tonic-gate pthread_mutex_lock(&wq->wq_donequeue_lock); 360*0Sstevel@tonic-gate 361*0Sstevel@tonic-gate while (wq->wq_lastdonebatch + 1 < slot->wip_batchid) 362*0Sstevel@tonic-gate pthread_cond_wait(&slot->wip_cv, &wq->wq_donequeue_lock); 363*0Sstevel@tonic-gate assert(wq->wq_lastdonebatch + 1 == slot->wip_batchid); 364*0Sstevel@tonic-gate 365*0Sstevel@tonic-gate fifo_add(wq->wq_donequeue, slot->wip_td); 366*0Sstevel@tonic-gate wq->wq_lastdonebatch++; 367*0Sstevel@tonic-gate pthread_cond_signal(&wq->wq_wip[(slotnum + 1) % 368*0Sstevel@tonic-gate wq->wq_nwipslots].wip_cv); 369*0Sstevel@tonic-gate 370*0Sstevel@tonic-gate /* reset the slot for next use */ 371*0Sstevel@tonic-gate slot->wip_td = NULL; 372*0Sstevel@tonic-gate slot->wip_batchid = wq->wq_next_batchid++; 373*0Sstevel@tonic-gate 374*0Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_donequeue_lock); 375*0Sstevel@tonic-gate } 376*0Sstevel@tonic-gate 377*0Sstevel@tonic-gate static void 378*0Sstevel@tonic-gate wip_add_work(wip_t *slot, tdata_t *pow) 379*0Sstevel@tonic-gate { 380*0Sstevel@tonic-gate if (slot->wip_td == NULL) { 381*0Sstevel@tonic-gate slot->wip_td = pow; 382*0Sstevel@tonic-gate slot->wip_nmerged = 1; 383*0Sstevel@tonic-gate } else { 384*0Sstevel@tonic-gate debug(2, "%d: merging %p into %p\n", pthread_self(), 385*0Sstevel@tonic-gate (void *)pow, (void *)slot->wip_td); 386*0Sstevel@tonic-gate 387*0Sstevel@tonic-gate merge_into_master(pow, slot->wip_td, NULL, 0); 388*0Sstevel@tonic-gate tdata_free(pow); 389*0Sstevel@tonic-gate 390*0Sstevel@tonic-gate slot->wip_nmerged++; 391*0Sstevel@tonic-gate } 392*0Sstevel@tonic-gate } 393*0Sstevel@tonic-gate 394*0Sstevel@tonic-gate static void 395*0Sstevel@tonic-gate worker_runphase1(workqueue_t *wq) 396*0Sstevel@tonic-gate { 397*0Sstevel@tonic-gate wip_t *wipslot; 398*0Sstevel@tonic-gate tdata_t *pow; 399*0Sstevel@tonic-gate int wipslotnum, pownum; 400*0Sstevel@tonic-gate 401*0Sstevel@tonic-gate for (;;) { 402*0Sstevel@tonic-gate pthread_mutex_lock(&wq->wq_queue_lock); 403*0Sstevel@tonic-gate 404*0Sstevel@tonic-gate while (fifo_empty(wq->wq_queue)) { 405*0Sstevel@tonic-gate if (wq->wq_nomorefiles == 1) { 406*0Sstevel@tonic-gate pthread_cond_broadcast(&wq->wq_work_avail); 407*0Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 408*0Sstevel@tonic-gate 409*0Sstevel@tonic-gate /* on to phase 2 ... */ 410*0Sstevel@tonic-gate return; 411*0Sstevel@tonic-gate } 412*0Sstevel@tonic-gate 413*0Sstevel@tonic-gate pthread_cond_wait(&wq->wq_work_avail, 414*0Sstevel@tonic-gate &wq->wq_queue_lock); 415*0Sstevel@tonic-gate } 416*0Sstevel@tonic-gate 417*0Sstevel@tonic-gate /* there's work to be done! */ 418*0Sstevel@tonic-gate pow = fifo_remove(wq->wq_queue); 419*0Sstevel@tonic-gate pownum = wq->wq_nextpownum++; 420*0Sstevel@tonic-gate pthread_cond_broadcast(&wq->wq_work_removed); 421*0Sstevel@tonic-gate 422*0Sstevel@tonic-gate assert(pow != NULL); 423*0Sstevel@tonic-gate 424*0Sstevel@tonic-gate /* merge it into the right slot */ 425*0Sstevel@tonic-gate wipslotnum = pownum % wq->wq_nwipslots; 426*0Sstevel@tonic-gate wipslot = &wq->wq_wip[wipslotnum]; 427*0Sstevel@tonic-gate 428*0Sstevel@tonic-gate pthread_mutex_lock(&wipslot->wip_lock); 429*0Sstevel@tonic-gate 430*0Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 431*0Sstevel@tonic-gate 432*0Sstevel@tonic-gate wip_add_work(wipslot, pow); 433*0Sstevel@tonic-gate 434*0Sstevel@tonic-gate if (wipslot->wip_nmerged == wq->wq_maxbatchsz) 435*0Sstevel@tonic-gate wip_save_work(wq, wipslot, wipslotnum); 436*0Sstevel@tonic-gate 437*0Sstevel@tonic-gate pthread_mutex_unlock(&wipslot->wip_lock); 438*0Sstevel@tonic-gate } 439*0Sstevel@tonic-gate } 440*0Sstevel@tonic-gate 441*0Sstevel@tonic-gate static void 442*0Sstevel@tonic-gate worker_runphase2(workqueue_t *wq) 443*0Sstevel@tonic-gate { 444*0Sstevel@tonic-gate tdata_t *pow1, *pow2; 445*0Sstevel@tonic-gate int batchid; 446*0Sstevel@tonic-gate 447*0Sstevel@tonic-gate for (;;) { 448*0Sstevel@tonic-gate pthread_mutex_lock(&wq->wq_queue_lock); 449*0Sstevel@tonic-gate 450*0Sstevel@tonic-gate if (wq->wq_ninqueue == 1) { 451*0Sstevel@tonic-gate pthread_cond_broadcast(&wq->wq_work_avail); 452*0Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 453*0Sstevel@tonic-gate 454*0Sstevel@tonic-gate debug(2, "%d: entering p2 completion barrier\n", 455*0Sstevel@tonic-gate pthread_self()); 456*0Sstevel@tonic-gate if (barrier_wait(&wq->wq_bar1)) { 457*0Sstevel@tonic-gate pthread_mutex_lock(&wq->wq_queue_lock); 458*0Sstevel@tonic-gate wq->wq_alldone = 1; 459*0Sstevel@tonic-gate pthread_cond_signal(&wq->wq_alldone_cv); 460*0Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 461*0Sstevel@tonic-gate } 462*0Sstevel@tonic-gate 463*0Sstevel@tonic-gate return; 464*0Sstevel@tonic-gate } 465*0Sstevel@tonic-gate 466*0Sstevel@tonic-gate if (fifo_len(wq->wq_queue) < 2) { 467*0Sstevel@tonic-gate pthread_cond_wait(&wq->wq_work_avail, 468*0Sstevel@tonic-gate &wq->wq_queue_lock); 469*0Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 470*0Sstevel@tonic-gate continue; 471*0Sstevel@tonic-gate } 472*0Sstevel@tonic-gate 473*0Sstevel@tonic-gate /* there's work to be done! */ 474*0Sstevel@tonic-gate pow1 = fifo_remove(wq->wq_queue); 475*0Sstevel@tonic-gate pow2 = fifo_remove(wq->wq_queue); 476*0Sstevel@tonic-gate wq->wq_ninqueue -= 2; 477*0Sstevel@tonic-gate 478*0Sstevel@tonic-gate batchid = wq->wq_next_batchid++; 479*0Sstevel@tonic-gate 480*0Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 481*0Sstevel@tonic-gate 482*0Sstevel@tonic-gate debug(2, "%d: merging %p into %p\n", pthread_self(), 483*0Sstevel@tonic-gate (void *)pow1, (void *)pow2); 484*0Sstevel@tonic-gate merge_into_master(pow1, pow2, NULL, 0); 485*0Sstevel@tonic-gate tdata_free(pow1); 486*0Sstevel@tonic-gate 487*0Sstevel@tonic-gate /* 488*0Sstevel@tonic-gate * merging is complete. place at the tail of the queue in 489*0Sstevel@tonic-gate * proper order. 490*0Sstevel@tonic-gate */ 491*0Sstevel@tonic-gate pthread_mutex_lock(&wq->wq_queue_lock); 492*0Sstevel@tonic-gate while (wq->wq_lastdonebatch + 1 != batchid) { 493*0Sstevel@tonic-gate pthread_cond_wait(&wq->wq_done_cv, 494*0Sstevel@tonic-gate &wq->wq_queue_lock); 495*0Sstevel@tonic-gate } 496*0Sstevel@tonic-gate 497*0Sstevel@tonic-gate wq->wq_lastdonebatch = batchid; 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate fifo_add(wq->wq_queue, pow2); 500*0Sstevel@tonic-gate debug(2, "%d: added %p to queue, len now %d, ninqueue %d\n", 501*0Sstevel@tonic-gate pthread_self(), (void *)pow2, fifo_len(wq->wq_queue), 502*0Sstevel@tonic-gate wq->wq_ninqueue); 503*0Sstevel@tonic-gate pthread_cond_broadcast(&wq->wq_done_cv); 504*0Sstevel@tonic-gate pthread_cond_signal(&wq->wq_work_avail); 505*0Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 506*0Sstevel@tonic-gate } 507*0Sstevel@tonic-gate } 508*0Sstevel@tonic-gate 509*0Sstevel@tonic-gate /* 510*0Sstevel@tonic-gate * Main loop for worker threads. 511*0Sstevel@tonic-gate */ 512*0Sstevel@tonic-gate static void 513*0Sstevel@tonic-gate worker_thread(workqueue_t *wq) 514*0Sstevel@tonic-gate { 515*0Sstevel@tonic-gate worker_runphase1(wq); 516*0Sstevel@tonic-gate 517*0Sstevel@tonic-gate debug(2, "%d: entering first barrier\n", pthread_self()); 518*0Sstevel@tonic-gate 519*0Sstevel@tonic-gate if (barrier_wait(&wq->wq_bar1)) { 520*0Sstevel@tonic-gate 521*0Sstevel@tonic-gate debug(2, "%d: doing work in first barrier\n", pthread_self()); 522*0Sstevel@tonic-gate 523*0Sstevel@tonic-gate finalize_phase_one(wq); 524*0Sstevel@tonic-gate 525*0Sstevel@tonic-gate init_phase_two(wq); 526*0Sstevel@tonic-gate 527*0Sstevel@tonic-gate debug(2, "%d: ninqueue is %d, %d on queue\n", pthread_self(), 528*0Sstevel@tonic-gate wq->wq_ninqueue, fifo_len(wq->wq_queue)); 529*0Sstevel@tonic-gate } 530*0Sstevel@tonic-gate 531*0Sstevel@tonic-gate debug(2, "%d: entering second barrier\n", pthread_self()); 532*0Sstevel@tonic-gate 533*0Sstevel@tonic-gate (void) barrier_wait(&wq->wq_bar2); 534*0Sstevel@tonic-gate 535*0Sstevel@tonic-gate debug(2, "%d: phase 1 complete\n", pthread_self()); 536*0Sstevel@tonic-gate 537*0Sstevel@tonic-gate worker_runphase2(wq); 538*0Sstevel@tonic-gate } 539*0Sstevel@tonic-gate 540*0Sstevel@tonic-gate /* 541*0Sstevel@tonic-gate * Pass a tdata_t tree, built from an input file, off to the work queue for 542*0Sstevel@tonic-gate * consumption by worker threads. 543*0Sstevel@tonic-gate */ 544*0Sstevel@tonic-gate static int 545*0Sstevel@tonic-gate merge_ctf_cb(tdata_t *td, char *name, void *arg) 546*0Sstevel@tonic-gate { 547*0Sstevel@tonic-gate workqueue_t *wq = arg; 548*0Sstevel@tonic-gate 549*0Sstevel@tonic-gate debug(3, "Adding tdata %p for processing\n", (void *)td); 550*0Sstevel@tonic-gate 551*0Sstevel@tonic-gate pthread_mutex_lock(&wq->wq_queue_lock); 552*0Sstevel@tonic-gate while (fifo_len(wq->wq_queue) > wq->wq_ithrottle) { 553*0Sstevel@tonic-gate debug(2, "Throttling input (len = %d, throttle = %d)\n", 554*0Sstevel@tonic-gate fifo_len(wq->wq_queue), wq->wq_ithrottle); 555*0Sstevel@tonic-gate pthread_cond_wait(&wq->wq_work_removed, &wq->wq_queue_lock); 556*0Sstevel@tonic-gate } 557*0Sstevel@tonic-gate 558*0Sstevel@tonic-gate fifo_add(wq->wq_queue, td); 559*0Sstevel@tonic-gate debug(1, "Thread %d announcing %s\n", pthread_self(), name); 560*0Sstevel@tonic-gate pthread_cond_broadcast(&wq->wq_work_avail); 561*0Sstevel@tonic-gate pthread_mutex_unlock(&wq->wq_queue_lock); 562*0Sstevel@tonic-gate 563*0Sstevel@tonic-gate return (1); 564*0Sstevel@tonic-gate } 565*0Sstevel@tonic-gate 566*0Sstevel@tonic-gate /* 567*0Sstevel@tonic-gate * This program is intended to be invoked from a Makefile, as part of the build. 568*0Sstevel@tonic-gate * As such, in the event of a failure or user-initiated interrupt (^C), we need 569*0Sstevel@tonic-gate * to ensure that a subsequent re-make will cause ctfmerge to be executed again. 570*0Sstevel@tonic-gate * Unfortunately, ctfmerge will usually be invoked directly after (and as part 571*0Sstevel@tonic-gate * of the same Makefile rule as) a link, and will operate on the linked file 572*0Sstevel@tonic-gate * in place. If we merely exit upon receipt of a SIGINT, a subsequent make 573*0Sstevel@tonic-gate * will notice that the *linked* file is newer than the object files, and thus 574*0Sstevel@tonic-gate * will not reinvoke ctfmerge. The only way to ensure that a subsequent make 575*0Sstevel@tonic-gate * reinvokes ctfmerge, is to remove the file to which we are adding CTF 576*0Sstevel@tonic-gate * data (confusingly named the output file). This means that the link will need 577*0Sstevel@tonic-gate * to happen again, but links are generally fast, and we can't allow the merge 578*0Sstevel@tonic-gate * to be skipped. 579*0Sstevel@tonic-gate * 580*0Sstevel@tonic-gate * Another possibility would be to block SIGINT entirely - to always run to 581*0Sstevel@tonic-gate * completion. The run time of ctfmerge can, however, be measured in minutes 582*0Sstevel@tonic-gate * in some cases, so this is not a valid option. 583*0Sstevel@tonic-gate */ 584*0Sstevel@tonic-gate static void 585*0Sstevel@tonic-gate handle_sig(int sig) 586*0Sstevel@tonic-gate { 587*0Sstevel@tonic-gate terminate("Caught signal %d - exiting\n", sig); 588*0Sstevel@tonic-gate } 589*0Sstevel@tonic-gate 590*0Sstevel@tonic-gate static void 591*0Sstevel@tonic-gate terminate_cleanup(void) 592*0Sstevel@tonic-gate { 593*0Sstevel@tonic-gate int dounlink = getenv("CTFMERGE_TERMINATE_NO_UNLINK") ? 0 : 1; 594*0Sstevel@tonic-gate 595*0Sstevel@tonic-gate if (tmpname != NULL && dounlink) 596*0Sstevel@tonic-gate unlink(tmpname); 597*0Sstevel@tonic-gate 598*0Sstevel@tonic-gate if (outfile == NULL) 599*0Sstevel@tonic-gate return; 600*0Sstevel@tonic-gate 601*0Sstevel@tonic-gate if (dounlink) { 602*0Sstevel@tonic-gate fprintf(stderr, "Removing %s\n", outfile); 603*0Sstevel@tonic-gate unlink(outfile); 604*0Sstevel@tonic-gate } 605*0Sstevel@tonic-gate } 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gate static void 608*0Sstevel@tonic-gate copy_ctf_data(char *srcfile, char *destfile, int keep_stabs) 609*0Sstevel@tonic-gate { 610*0Sstevel@tonic-gate tdata_t *srctd; 611*0Sstevel@tonic-gate 612*0Sstevel@tonic-gate if (read_ctf(&srcfile, 1, NULL, read_ctf_save_cb, &srctd, 1) == 0) 613*0Sstevel@tonic-gate terminate("No CTF data found in source file %s\n", srcfile); 614*0Sstevel@tonic-gate 615*0Sstevel@tonic-gate tmpname = mktmpname(destfile, ".ctf"); 616*0Sstevel@tonic-gate write_ctf(srctd, destfile, tmpname, CTF_COMPRESS | keep_stabs); 617*0Sstevel@tonic-gate if (rename(tmpname, destfile) != 0) { 618*0Sstevel@tonic-gate terminate("Couldn't rename temp file %s to %s", tmpname, 619*0Sstevel@tonic-gate destfile); 620*0Sstevel@tonic-gate } 621*0Sstevel@tonic-gate free(tmpname); 622*0Sstevel@tonic-gate tdata_free(srctd); 623*0Sstevel@tonic-gate } 624*0Sstevel@tonic-gate 625*0Sstevel@tonic-gate static void 626*0Sstevel@tonic-gate wq_init(workqueue_t *wq, int nfiles) 627*0Sstevel@tonic-gate { 628*0Sstevel@tonic-gate int throttle, nslots, i; 629*0Sstevel@tonic-gate 630*0Sstevel@tonic-gate if (getenv("CTFMERGE_MAX_SLOTS")) 631*0Sstevel@tonic-gate nslots = atoi(getenv("CTFMERGE_MAX_SLOTS")); 632*0Sstevel@tonic-gate else 633*0Sstevel@tonic-gate nslots = MERGE_PHASE1_MAX_SLOTS; 634*0Sstevel@tonic-gate 635*0Sstevel@tonic-gate if (getenv("CTFMERGE_PHASE1_BATCH_SIZE")) 636*0Sstevel@tonic-gate wq->wq_maxbatchsz = atoi(getenv("CTFMERGE_PHASE1_BATCH_SIZE")); 637*0Sstevel@tonic-gate else 638*0Sstevel@tonic-gate wq->wq_maxbatchsz = MERGE_PHASE1_BATCH_SIZE; 639*0Sstevel@tonic-gate 640*0Sstevel@tonic-gate nslots = MIN(nslots, (nfiles + wq->wq_maxbatchsz - 1) / 641*0Sstevel@tonic-gate wq->wq_maxbatchsz); 642*0Sstevel@tonic-gate 643*0Sstevel@tonic-gate wq->wq_wip = xcalloc(sizeof (wip_t) * nslots); 644*0Sstevel@tonic-gate wq->wq_nwipslots = nslots; 645*0Sstevel@tonic-gate wq->wq_nthreads = MIN(sysconf(_SC_NPROCESSORS_ONLN) * 3 / 2, nslots); 646*0Sstevel@tonic-gate 647*0Sstevel@tonic-gate if (getenv("CTFMERGE_INPUT_THROTTLE")) 648*0Sstevel@tonic-gate throttle = atoi(getenv("CTFMERGE_INPUT_THROTTLE")); 649*0Sstevel@tonic-gate else 650*0Sstevel@tonic-gate throttle = MERGE_INPUT_THROTTLE_LEN; 651*0Sstevel@tonic-gate wq->wq_ithrottle = throttle * wq->wq_nthreads; 652*0Sstevel@tonic-gate 653*0Sstevel@tonic-gate debug(1, "Using %d slots, %d threads\n", wq->wq_nwipslots, 654*0Sstevel@tonic-gate wq->wq_nthreads); 655*0Sstevel@tonic-gate 656*0Sstevel@tonic-gate wq->wq_next_batchid = 0; 657*0Sstevel@tonic-gate 658*0Sstevel@tonic-gate for (i = 0; i < nslots; i++) { 659*0Sstevel@tonic-gate pthread_mutex_init(&wq->wq_wip[i].wip_lock, NULL); 660*0Sstevel@tonic-gate wq->wq_wip[i].wip_batchid = wq->wq_next_batchid++; 661*0Sstevel@tonic-gate } 662*0Sstevel@tonic-gate 663*0Sstevel@tonic-gate pthread_mutex_init(&wq->wq_queue_lock, NULL); 664*0Sstevel@tonic-gate wq->wq_queue = fifo_new(); 665*0Sstevel@tonic-gate pthread_cond_init(&wq->wq_work_avail, NULL); 666*0Sstevel@tonic-gate pthread_cond_init(&wq->wq_work_removed, NULL); 667*0Sstevel@tonic-gate wq->wq_ninqueue = nfiles; 668*0Sstevel@tonic-gate wq->wq_nextpownum = 0; 669*0Sstevel@tonic-gate 670*0Sstevel@tonic-gate pthread_mutex_init(&wq->wq_donequeue_lock, NULL); 671*0Sstevel@tonic-gate wq->wq_donequeue = fifo_new(); 672*0Sstevel@tonic-gate wq->wq_lastdonebatch = -1; 673*0Sstevel@tonic-gate 674*0Sstevel@tonic-gate pthread_cond_init(&wq->wq_done_cv, NULL); 675*0Sstevel@tonic-gate 676*0Sstevel@tonic-gate pthread_cond_init(&wq->wq_alldone_cv, NULL); 677*0Sstevel@tonic-gate wq->wq_alldone = 0; 678*0Sstevel@tonic-gate 679*0Sstevel@tonic-gate barrier_init(&wq->wq_bar1, wq->wq_nthreads); 680*0Sstevel@tonic-gate barrier_init(&wq->wq_bar2, wq->wq_nthreads); 681*0Sstevel@tonic-gate 682*0Sstevel@tonic-gate wq->wq_nomorefiles = 0; 683*0Sstevel@tonic-gate } 684*0Sstevel@tonic-gate 685*0Sstevel@tonic-gate static void 686*0Sstevel@tonic-gate start_threads(workqueue_t *wq) 687*0Sstevel@tonic-gate { 688*0Sstevel@tonic-gate pthread_t thrid; 689*0Sstevel@tonic-gate sigset_t sets; 690*0Sstevel@tonic-gate int i; 691*0Sstevel@tonic-gate 692*0Sstevel@tonic-gate sigemptyset(&sets); 693*0Sstevel@tonic-gate sigaddset(&sets, SIGINT); 694*0Sstevel@tonic-gate sigaddset(&sets, SIGQUIT); 695*0Sstevel@tonic-gate sigaddset(&sets, SIGTERM); 696*0Sstevel@tonic-gate pthread_sigmask(SIG_BLOCK, &sets, NULL); 697*0Sstevel@tonic-gate 698*0Sstevel@tonic-gate for (i = 0; i < wq->wq_nthreads; i++) { 699*0Sstevel@tonic-gate pthread_create(&thrid, NULL, (void *(*)(void *))worker_thread, 700*0Sstevel@tonic-gate wq); 701*0Sstevel@tonic-gate } 702*0Sstevel@tonic-gate 703*0Sstevel@tonic-gate sigset(SIGINT, handle_sig); 704*0Sstevel@tonic-gate sigset(SIGQUIT, handle_sig); 705*0Sstevel@tonic-gate sigset(SIGTERM, handle_sig); 706*0Sstevel@tonic-gate pthread_sigmask(SIG_UNBLOCK, &sets, NULL); 707*0Sstevel@tonic-gate } 708*0Sstevel@tonic-gate 709*0Sstevel@tonic-gate static int 710*0Sstevel@tonic-gate strcompare(const void *p1, const void *p2) 711*0Sstevel@tonic-gate { 712*0Sstevel@tonic-gate char *s1 = *((char **)p1); 713*0Sstevel@tonic-gate char *s2 = *((char **)p2); 714*0Sstevel@tonic-gate 715*0Sstevel@tonic-gate return (strcmp(s1, s2)); 716*0Sstevel@tonic-gate } 717*0Sstevel@tonic-gate 718*0Sstevel@tonic-gate int 719*0Sstevel@tonic-gate main(int argc, char **argv) 720*0Sstevel@tonic-gate { 721*0Sstevel@tonic-gate workqueue_t wq; 722*0Sstevel@tonic-gate tdata_t *mstrtd, *savetd; 723*0Sstevel@tonic-gate char *uniqfile = NULL, *uniqlabel = NULL; 724*0Sstevel@tonic-gate char *withfile = NULL; 725*0Sstevel@tonic-gate char *label = NULL; 726*0Sstevel@tonic-gate char **ifiles, **tifiles; 727*0Sstevel@tonic-gate int verbose = 0, docopy = 0; 728*0Sstevel@tonic-gate int write_fuzzy_match = 0; 729*0Sstevel@tonic-gate int keep_stabs = 0; 730*0Sstevel@tonic-gate int require_ctf = 0; 731*0Sstevel@tonic-gate int nifiles, nielems; 732*0Sstevel@tonic-gate int c, i, idx, tidx, err; 733*0Sstevel@tonic-gate 734*0Sstevel@tonic-gate progname = basename(argv[0]); 735*0Sstevel@tonic-gate 736*0Sstevel@tonic-gate if (getenv("CTFMERGE_DEBUG_LEVEL")) 737*0Sstevel@tonic-gate debug_level = atoi(getenv("CTFMERGE_DEBUG_LEVEL")); 738*0Sstevel@tonic-gate 739*0Sstevel@tonic-gate err = 0; 740*0Sstevel@tonic-gate while ((c = getopt(argc, argv, ":cd:D:fgl:L:o:tvw:s")) != EOF) { 741*0Sstevel@tonic-gate switch (c) { 742*0Sstevel@tonic-gate case 'c': 743*0Sstevel@tonic-gate docopy = 1; 744*0Sstevel@tonic-gate break; 745*0Sstevel@tonic-gate case 'd': 746*0Sstevel@tonic-gate /* Uniquify against `uniqfile' */ 747*0Sstevel@tonic-gate uniqfile = optarg; 748*0Sstevel@tonic-gate break; 749*0Sstevel@tonic-gate case 'D': 750*0Sstevel@tonic-gate /* Uniquify against label `uniqlabel' in `uniqfile' */ 751*0Sstevel@tonic-gate uniqlabel = optarg; 752*0Sstevel@tonic-gate break; 753*0Sstevel@tonic-gate case 'f': 754*0Sstevel@tonic-gate write_fuzzy_match = CTF_FUZZY_MATCH; 755*0Sstevel@tonic-gate break; 756*0Sstevel@tonic-gate case 'g': 757*0Sstevel@tonic-gate keep_stabs = CTF_KEEP_STABS; 758*0Sstevel@tonic-gate break; 759*0Sstevel@tonic-gate case 'l': 760*0Sstevel@tonic-gate /* Label merged types with `label' */ 761*0Sstevel@tonic-gate label = optarg; 762*0Sstevel@tonic-gate break; 763*0Sstevel@tonic-gate case 'L': 764*0Sstevel@tonic-gate /* Label merged types with getenv(`label`) */ 765*0Sstevel@tonic-gate if ((label = getenv(optarg)) == NULL) 766*0Sstevel@tonic-gate label = CTF_DEFAULT_LABEL; 767*0Sstevel@tonic-gate break; 768*0Sstevel@tonic-gate case 'o': 769*0Sstevel@tonic-gate /* Place merged types in CTF section in `outfile' */ 770*0Sstevel@tonic-gate outfile = optarg; 771*0Sstevel@tonic-gate break; 772*0Sstevel@tonic-gate case 't': 773*0Sstevel@tonic-gate /* Insist *all* object files built from C have CTF */ 774*0Sstevel@tonic-gate require_ctf = 1; 775*0Sstevel@tonic-gate break; 776*0Sstevel@tonic-gate case 'v': 777*0Sstevel@tonic-gate /* More debugging information */ 778*0Sstevel@tonic-gate verbose = 1; 779*0Sstevel@tonic-gate break; 780*0Sstevel@tonic-gate case 'w': 781*0Sstevel@tonic-gate /* Additive merge with data from `withfile' */ 782*0Sstevel@tonic-gate withfile = optarg; 783*0Sstevel@tonic-gate break; 784*0Sstevel@tonic-gate case 's': 785*0Sstevel@tonic-gate /* use the dynsym rather than the symtab */ 786*0Sstevel@tonic-gate dynsym = CTF_USE_DYNSYM; 787*0Sstevel@tonic-gate break; 788*0Sstevel@tonic-gate default: 789*0Sstevel@tonic-gate usage(); 790*0Sstevel@tonic-gate exit(2); 791*0Sstevel@tonic-gate } 792*0Sstevel@tonic-gate } 793*0Sstevel@tonic-gate 794*0Sstevel@tonic-gate /* Validate arguments */ 795*0Sstevel@tonic-gate if (docopy) { 796*0Sstevel@tonic-gate if (uniqfile != NULL || uniqlabel != NULL || label != NULL || 797*0Sstevel@tonic-gate outfile != NULL || withfile != NULL || dynsym != 0) 798*0Sstevel@tonic-gate err++; 799*0Sstevel@tonic-gate 800*0Sstevel@tonic-gate if (argc - optind != 2) 801*0Sstevel@tonic-gate err++; 802*0Sstevel@tonic-gate } else { 803*0Sstevel@tonic-gate if (uniqfile != NULL && withfile != NULL) 804*0Sstevel@tonic-gate err++; 805*0Sstevel@tonic-gate 806*0Sstevel@tonic-gate if (uniqlabel != NULL && uniqfile == NULL) 807*0Sstevel@tonic-gate err++; 808*0Sstevel@tonic-gate 809*0Sstevel@tonic-gate if (outfile == NULL || label == NULL) 810*0Sstevel@tonic-gate err++; 811*0Sstevel@tonic-gate 812*0Sstevel@tonic-gate if (argc - optind == 0) 813*0Sstevel@tonic-gate err++; 814*0Sstevel@tonic-gate } 815*0Sstevel@tonic-gate 816*0Sstevel@tonic-gate if (err) { 817*0Sstevel@tonic-gate usage(); 818*0Sstevel@tonic-gate exit(2); 819*0Sstevel@tonic-gate } 820*0Sstevel@tonic-gate 821*0Sstevel@tonic-gate if (getenv("STRIPSTABS_KEEP_STABS") != NULL) 822*0Sstevel@tonic-gate keep_stabs = CTF_KEEP_STABS; 823*0Sstevel@tonic-gate 824*0Sstevel@tonic-gate if (uniqfile && access(uniqfile, R_OK) != 0) { 825*0Sstevel@tonic-gate warning("Uniquification file %s couldn't be opened and " 826*0Sstevel@tonic-gate "will be ignored.\n", uniqfile); 827*0Sstevel@tonic-gate uniqfile = NULL; 828*0Sstevel@tonic-gate } 829*0Sstevel@tonic-gate if (withfile && access(withfile, R_OK) != 0) { 830*0Sstevel@tonic-gate warning("With file %s couldn't be opened and will be " 831*0Sstevel@tonic-gate "ignored.\n", withfile); 832*0Sstevel@tonic-gate withfile = NULL; 833*0Sstevel@tonic-gate } 834*0Sstevel@tonic-gate if (outfile && access(outfile, R_OK|W_OK) != 0) 835*0Sstevel@tonic-gate terminate("Cannot open output file %s for r/w", outfile); 836*0Sstevel@tonic-gate 837*0Sstevel@tonic-gate /* 838*0Sstevel@tonic-gate * This is ugly, but we don't want to have to have a separate tool 839*0Sstevel@tonic-gate * (yet) just for copying an ELF section with our specific requirements, 840*0Sstevel@tonic-gate * so we shoe-horn a copier into ctfmerge. 841*0Sstevel@tonic-gate */ 842*0Sstevel@tonic-gate if (docopy) { 843*0Sstevel@tonic-gate copy_ctf_data(argv[optind], argv[optind + 1], keep_stabs); 844*0Sstevel@tonic-gate 845*0Sstevel@tonic-gate exit(0); 846*0Sstevel@tonic-gate } 847*0Sstevel@tonic-gate 848*0Sstevel@tonic-gate set_terminate_cleanup(terminate_cleanup); 849*0Sstevel@tonic-gate 850*0Sstevel@tonic-gate /* Sort the input files and strip out duplicates */ 851*0Sstevel@tonic-gate nifiles = argc - optind; 852*0Sstevel@tonic-gate ifiles = xmalloc(sizeof (char *) * nifiles); 853*0Sstevel@tonic-gate tifiles = xmalloc(sizeof (char *) * nifiles); 854*0Sstevel@tonic-gate 855*0Sstevel@tonic-gate for (i = 0; i < nifiles; i++) 856*0Sstevel@tonic-gate tifiles[i] = argv[optind + i]; 857*0Sstevel@tonic-gate qsort(tifiles, nifiles, sizeof (char *), (int (*)())strcompare); 858*0Sstevel@tonic-gate 859*0Sstevel@tonic-gate ifiles[0] = tifiles[0]; 860*0Sstevel@tonic-gate for (idx = 0, tidx = 1; tidx < nifiles; tidx++) { 861*0Sstevel@tonic-gate if (strcmp(ifiles[idx], tifiles[tidx]) != 0) 862*0Sstevel@tonic-gate ifiles[++idx] = tifiles[tidx]; 863*0Sstevel@tonic-gate } 864*0Sstevel@tonic-gate nifiles = idx + 1; 865*0Sstevel@tonic-gate 866*0Sstevel@tonic-gate /* Make sure they all exist */ 867*0Sstevel@tonic-gate if ((nielems = count_files(ifiles, nifiles)) < 0) 868*0Sstevel@tonic-gate terminate("Some input files were inaccessible\n"); 869*0Sstevel@tonic-gate 870*0Sstevel@tonic-gate /* Prepare for the merge */ 871*0Sstevel@tonic-gate wq_init(&wq, nielems); 872*0Sstevel@tonic-gate 873*0Sstevel@tonic-gate start_threads(&wq); 874*0Sstevel@tonic-gate 875*0Sstevel@tonic-gate /* 876*0Sstevel@tonic-gate * Start the merge 877*0Sstevel@tonic-gate * 878*0Sstevel@tonic-gate * We're reading everything from each of the object files, so we 879*0Sstevel@tonic-gate * don't need to specify labels. 880*0Sstevel@tonic-gate */ 881*0Sstevel@tonic-gate if (read_ctf(ifiles, nifiles, NULL, merge_ctf_cb, 882*0Sstevel@tonic-gate &wq, require_ctf) == 0) 883*0Sstevel@tonic-gate terminate("No ctf sections found to merge\n"); 884*0Sstevel@tonic-gate 885*0Sstevel@tonic-gate pthread_mutex_lock(&wq.wq_queue_lock); 886*0Sstevel@tonic-gate wq.wq_nomorefiles = 1; 887*0Sstevel@tonic-gate pthread_cond_broadcast(&wq.wq_work_avail); 888*0Sstevel@tonic-gate pthread_mutex_unlock(&wq.wq_queue_lock); 889*0Sstevel@tonic-gate 890*0Sstevel@tonic-gate pthread_mutex_lock(&wq.wq_queue_lock); 891*0Sstevel@tonic-gate while (wq.wq_alldone == 0) 892*0Sstevel@tonic-gate pthread_cond_wait(&wq.wq_alldone_cv, &wq.wq_queue_lock); 893*0Sstevel@tonic-gate pthread_mutex_unlock(&wq.wq_queue_lock); 894*0Sstevel@tonic-gate 895*0Sstevel@tonic-gate /* 896*0Sstevel@tonic-gate * All requested files have been merged, with the resulting tree in 897*0Sstevel@tonic-gate * mstrtd. savetd is the tree that will be placed into the output file. 898*0Sstevel@tonic-gate * 899*0Sstevel@tonic-gate * Regardless of whether we're doing a normal uniquification or an 900*0Sstevel@tonic-gate * additive merge, we need a type tree that has been uniquified 901*0Sstevel@tonic-gate * against uniqfile or withfile, as appropriate. 902*0Sstevel@tonic-gate * 903*0Sstevel@tonic-gate * If we're doing a uniquification, we stuff the resulting tree into 904*0Sstevel@tonic-gate * outfile. Otherwise, we add the tree to the tree already in withfile. 905*0Sstevel@tonic-gate */ 906*0Sstevel@tonic-gate assert(fifo_len(wq.wq_queue) == 1); 907*0Sstevel@tonic-gate mstrtd = fifo_remove(wq.wq_queue); 908*0Sstevel@tonic-gate 909*0Sstevel@tonic-gate if (verbose || debug_level) { 910*0Sstevel@tonic-gate debug(2, "Statistics for td %p\n", (void *)mstrtd); 911*0Sstevel@tonic-gate 912*0Sstevel@tonic-gate iidesc_stats(mstrtd->td_iihash); 913*0Sstevel@tonic-gate } 914*0Sstevel@tonic-gate 915*0Sstevel@tonic-gate if (uniqfile != NULL || withfile != NULL) { 916*0Sstevel@tonic-gate char *reffile, *reflabel = NULL; 917*0Sstevel@tonic-gate tdata_t *reftd; 918*0Sstevel@tonic-gate 919*0Sstevel@tonic-gate if (uniqfile != NULL) { 920*0Sstevel@tonic-gate reffile = uniqfile; 921*0Sstevel@tonic-gate reflabel = uniqlabel; 922*0Sstevel@tonic-gate } else 923*0Sstevel@tonic-gate reffile = withfile; 924*0Sstevel@tonic-gate 925*0Sstevel@tonic-gate if (read_ctf(&reffile, 1, reflabel, read_ctf_save_cb, 926*0Sstevel@tonic-gate &reftd, require_ctf) == 0) { 927*0Sstevel@tonic-gate terminate("No CTF data found in reference file %s\n", 928*0Sstevel@tonic-gate reffile); 929*0Sstevel@tonic-gate } 930*0Sstevel@tonic-gate 931*0Sstevel@tonic-gate savetd = tdata_new(); 932*0Sstevel@tonic-gate 933*0Sstevel@tonic-gate if (CTF_TYPE_ISCHILD(reftd->td_nextid)) 934*0Sstevel@tonic-gate terminate("No room for additional types in master\n"); 935*0Sstevel@tonic-gate 936*0Sstevel@tonic-gate savetd->td_nextid = withfile ? reftd->td_nextid : 937*0Sstevel@tonic-gate CTF_INDEX_TO_TYPE(1, TRUE); 938*0Sstevel@tonic-gate merge_into_master(mstrtd, reftd, savetd, 0); 939*0Sstevel@tonic-gate 940*0Sstevel@tonic-gate tdata_label_add(savetd, label, CTF_LABEL_LASTIDX); 941*0Sstevel@tonic-gate 942*0Sstevel@tonic-gate if (withfile) { 943*0Sstevel@tonic-gate /* 944*0Sstevel@tonic-gate * savetd holds the new data to be added to the withfile 945*0Sstevel@tonic-gate */ 946*0Sstevel@tonic-gate tdata_t *withtd = reftd; 947*0Sstevel@tonic-gate 948*0Sstevel@tonic-gate tdata_merge(withtd, savetd); 949*0Sstevel@tonic-gate 950*0Sstevel@tonic-gate savetd = withtd; 951*0Sstevel@tonic-gate } else { 952*0Sstevel@tonic-gate char uniqname[MAXPATHLEN]; 953*0Sstevel@tonic-gate labelent_t *parle; 954*0Sstevel@tonic-gate 955*0Sstevel@tonic-gate parle = tdata_label_top(reftd); 956*0Sstevel@tonic-gate 957*0Sstevel@tonic-gate savetd->td_parlabel = xstrdup(parle->le_name); 958*0Sstevel@tonic-gate 959*0Sstevel@tonic-gate strncpy(uniqname, reffile, sizeof (uniqname)); 960*0Sstevel@tonic-gate uniqname[MAXPATHLEN - 1] = '\0'; 961*0Sstevel@tonic-gate savetd->td_parname = xstrdup(basename(uniqname)); 962*0Sstevel@tonic-gate } 963*0Sstevel@tonic-gate 964*0Sstevel@tonic-gate } else { 965*0Sstevel@tonic-gate /* 966*0Sstevel@tonic-gate * No post processing. Write the merged tree as-is into the 967*0Sstevel@tonic-gate * output file. 968*0Sstevel@tonic-gate */ 969*0Sstevel@tonic-gate tdata_label_free(mstrtd); 970*0Sstevel@tonic-gate tdata_label_add(mstrtd, label, CTF_LABEL_LASTIDX); 971*0Sstevel@tonic-gate 972*0Sstevel@tonic-gate savetd = mstrtd; 973*0Sstevel@tonic-gate } 974*0Sstevel@tonic-gate 975*0Sstevel@tonic-gate tmpname = mktmpname(outfile, ".ctf"); 976*0Sstevel@tonic-gate write_ctf(savetd, outfile, tmpname, 977*0Sstevel@tonic-gate CTF_COMPRESS | write_fuzzy_match | dynsym | keep_stabs); 978*0Sstevel@tonic-gate if (rename(tmpname, outfile) != 0) 979*0Sstevel@tonic-gate terminate("Couldn't rename output temp file %s", tmpname); 980*0Sstevel@tonic-gate free(tmpname); 981*0Sstevel@tonic-gate 982*0Sstevel@tonic-gate return (0); 983*0Sstevel@tonic-gate } 984