Changeset 3189 in kBuild
- Timestamp:
- Mar 25, 2018 5:05:19 PM (7 years ago)
- Location:
- trunk/src/kmk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kmk/Makefile.kmk
r3173 r3189 222 222 kmk_DEFS.x86 = CONFIG_WITH_OPTIMIZATION_HACKS 223 223 kmk_DEFS.amd64 = CONFIG_WITH_OPTIMIZATION_HACKS 224 kmk_DEFS.win = CONFIG_NEW_WIN32_CTRL_EVENT 224 kmk_DEFS.win = CONFIG_NEW_WIN32_CTRL_EVENT CONFIG_WITH_OUTPUT_IN_MEMORY 225 225 kmk_DEFS.debug = CONFIG_WITH_MAKE_STATS 226 226 ifdef CONFIG_WITH_MAKE_STATS -
trunk/src/kmk/function.c
r3186 r3189 2570 2570 output_start (); 2571 2571 2572 #ifdef CONFIG_WITH_OUTPUT_IN_MEMORY 2573 errfd = -1; /** @todo fixme */ 2574 #else 2572 2575 errfd = (output_context && output_context->err >= 0 2573 2576 ? output_context->err : FD_STDERR); 2577 #endif 2574 2578 2575 2579 #if defined(__MSDOS__) -
trunk/src/kmk/output.c
r3188 r3189 60 60 #endif 61 61 62 63 #ifdef CONFIG_WITH_OUTPUT_IN_MEMORY 64 # define MEMBUF_MIN_SEG_SIZE 4096 65 # define MEMBUF_MAX_SEG_SIZE (512*1024) 66 # define MEMBUF_MAX_MOVE_LEN ( MEMBUF_MIN_SEG_SIZE \ 67 - offsetof (struct output_segment, runs) \ 68 - sizeof (struct output_run)) 69 70 static void *acquire_semaphore (void); 71 static void release_semaphore (void *); 72 static int log_working_directory (int); 73 74 /* Internal worker for output_dump and membuf_dump_most. */ 75 static void membuf_dump (struct output *out) 76 { 77 if (out->out.total || out->err.total) 78 { 79 int traced = 0; 80 struct output_run *err_run; 81 struct output_run *out_run; 82 struct output_segment *seg; 83 FILE *prevdst; 84 85 /* Try to acquire the semaphore. If it fails, dump the output 86 unsynchronized; still better than silently discarding it. 87 We want to keep this lock for as little time as possible. */ 88 void *sem = acquire_semaphore (); 89 90 /* Log the working directory for this dump. */ 91 if (print_directory_flag && output_sync != OUTPUT_SYNC_RECURSE) 92 traced = log_working_directory (1); 93 94 /* Work the out and err sequences in parallel. */ 95 out_run = out->out.head_run; 96 err_run = out->err.head_run; 97 prevdst = NULL; 98 while (err_run || out_run) 99 { 100 FILE *dst; 101 const void *src; 102 size_t len; 103 if (out_run && (!err_run || out_run->seqno <= err_run->seqno)) 104 { 105 src = out_run + 1; 106 len = out_run->len; 107 dst = stdout; 108 out_run = out_run->next; 109 } 110 else 111 { 112 src = err_run + 1; 113 len = err_run->len; 114 dst = stderr; 115 err_run = err_run->next; 116 } 117 if (dst != prevdst) 118 fflush(prevdst); 119 prevdst = dst; 120 #ifdef KBUILD_OS_WINDOWS 121 maybe_con_fwrite (src, len, 1, dst); 122 #else 123 fwrite (src, len, 1, dst); 124 #endif 125 } 126 if (prevdst) 127 fflush (prevdst); 128 129 if (traced) 130 log_working_directory (0); 131 132 /* Exit the critical section. */ 133 if (sem) 134 release_semaphore (sem); 135 136 /* Free the segments and reset the state. */ 137 while ((seg = out->out.head_seg)) 138 { 139 out->out.head_seg = seg->next; 140 free (seg); 141 } 142 out->out.tail_seg = NULL; 143 out->out.tail_run = NULL; 144 out->out.head_run = NULL; 145 out->out.left = 0; 146 out->out.total = 0; 147 148 while ((seg = out->err.head_seg)) 149 { 150 out->err.head_seg = seg->next; 151 free (seg); 152 } 153 out->err.tail_seg = NULL; 154 out->err.tail_run = NULL; 155 out->err.head_run = NULL; 156 out->err.left = 0; 157 out->err.total = 0; 158 159 out->seqno = 0; 160 } 161 else 162 assert (out->out.head_seg == NULL && out->err.head_seg == NULL); 163 } 164 165 /* Writes up to LEN bytes to the given segment. 166 Returns how much was actually written. */ 167 static size_t 168 membuf_write_segment (struct output_membuf *membuf, struct output_segment *seg, 169 const char *src, size_t len, unsigned int *pseqno) 170 { 171 size_t written = 0; 172 if (seg && membuf->left > 0) 173 { 174 struct output_run *run = membuf->tail_run; 175 char *dst = (char *)(run + 1) + run->len; 176 assert ((uintptr_t)run - (uintptr_t)seg < seg->size); 177 178 /* If the sequence number didn't change, then we can append 179 to the current run without further considerations. */ 180 if (run->seqno == *pseqno) 181 written = len; 182 /* If the current run does not end with a newline, don't start a new 183 run till we encounter one. */ 184 else if (dst[-1] != '\n') 185 { 186 char const *srcnl = (const char *)memchr (src, '\n', len); 187 written = srcnl ? srcnl - src + 1 : len; 188 } 189 /* Try create a new empty run and append to it. */ 190 else 191 { 192 size_t const offnextrun = ( (uintptr_t)dst - (uintptr_t)(seg) 193 + sizeof(void *) - 1) 194 & ~(sizeof(void *) - 1); 195 if (offnextrun > seg->size - sizeof (struct output_run) * 2) 196 return 0; /* need new segment */ 197 198 run = run->next = (struct output_run *)((char *)seg + offnextrun); 199 run->next = NULL; 200 run->seqno = ++(*pseqno); 201 run->len = 0; 202 membuf->tail_run = run; 203 membuf->left = seg->size - (offnextrun + sizeof (*run)); 204 dst = (char *)(run + 1); 205 written = len; 206 } 207 208 /* Append to the current run. */ 209 if (written > membuf->left) 210 written = membuf->left; 211 memcpy (dst, src, written); 212 run->len += written; 213 membuf->left -= written; 214 } 215 return written; 216 } 217 218 /* Helper for membuf_new_segment_write that figures out now much data needs to 219 be moved from the previous run in order to make it end with a newline. */ 220 static size_t membuf_calc_move_len (struct output_run *tail_run) 221 { 222 size_t to_move = 0; 223 if (tail_run) 224 { 225 const char *data = (const char *)(tail_run + 1); 226 size_t off = tail_run->len; 227 while (off > 0 && data[off - 1] != '\n') 228 off--; 229 to_move = tail_run->len - off; 230 if (to_move >= MEMBUF_MAX_MOVE_LEN) 231 to_move = 0; 232 } 233 return to_move; 234 } 235 236 /* Allocates a new segment and writes to it. 237 This will take care to make sure the previous run terminates with 238 a newline so that we pass whole lines to fwrite when dumping. */ 239 static size_t 240 membuf_new_segment_write (struct output_membuf *membuf, const char *src, 241 size_t len, unsigned int *pseqno) 242 { 243 struct output_run *prev_run = membuf->tail_run; 244 struct output_segment *prev_seg = membuf->tail_seg; 245 size_t const to_move = membuf_calc_move_len (prev_run); 246 struct output_segment *new_seg; 247 size_t written; 248 char *dst; 249 250 /* Figure the the segment size. We start with MEMBUF_MIN_SEG_SIZE and double 251 it each time till we reach MEMBUF_MAX_SEG_SIZE. */ 252 size_t const offset_runs = offsetof (struct output_segment, runs); 253 size_t segsize = !prev_seg ? MEMBUF_MIN_SEG_SIZE 254 : prev_seg->size >= MEMBUF_MAX_SEG_SIZE ? MEMBUF_MAX_SEG_SIZE 255 : prev_seg->size * 2; 256 while ( segsize < to_move + len + offset_runs + sizeof (struct output_run) * 2 257 && segsize < MEMBUF_MAX_SEG_SIZE) 258 segsize *= 2; 259 260 /* Allocate the segment and link it and the first run. */ 261 new_seg = (struct output_segment *)xmalloc (segsize); 262 new_seg->size = segsize; 263 new_seg->next = NULL; 264 new_seg->runs[0].next = NULL; 265 if (!prev_seg) 266 { 267 membuf->head_seg = new_seg; 268 membuf->head_run = &new_seg->runs[0]; 269 } 270 else 271 { 272 prev_seg->next = new_seg; 273 prev_run->next = &new_seg->runs[0]; 274 } 275 membuf->tail_seg = new_seg; 276 membuf->tail_run = &new_seg->runs[0]; 277 membuf->total += segsize; 278 membuf->left = segsize - sizeof (struct output_run); 279 280 /* Initialize and write data to the first run. */ 281 dst = (char *)&new_seg->runs[0]; /* Try bypass gcc array size cleverness. */ 282 dst += sizeof (struct output_run); 283 assert (MEMBUF_MAX_MOVE_LEN < MEMBUF_MIN_SEG_SIZE); 284 if (to_move > 0) 285 { 286 /* Move to_move bytes from the previous run in hope that we'll get a 287 newline to soon. Afterwards call membuf_segment_write to work SRC. */ 288 assert (prev_run != NULL); 289 assert (membuf->left >= to_move); 290 prev_run->len -= to_move; 291 new_seg->runs[0].len = to_move; 292 new_seg->runs[0].seqno = prev_run->seqno; 293 memcpy (dst, (const char *)(prev_run + 1) + prev_run->len, to_move); 294 membuf->left -= to_move; 295 296 written = membuf_write_segment (membuf, new_seg, src, len, pseqno); 297 } 298 else 299 { 300 /* Create a run with up to LEN from SRC. */ 301 written = len; 302 if (written > membuf->left) 303 written = membuf->left; 304 new_seg->runs[0].len = written; 305 new_seg->runs[0].seqno = ++(*pseqno); 306 memcpy (dst, src, written); 307 membuf->left -= written; 308 } 309 return written; 310 } 311 312 /* Worker for output_write that will try dump as much as possible of the 313 output, but making sure to try leave unfinished lines. */ 314 static void 315 membuf_dump_most (struct output *out) 316 { 317 /** @todo check last segment and make local copies. */ 318 membuf_dump (out); 319 } 320 321 /* write/fwrite like function. */ 322 void 323 output_write (struct output *out, int is_err, const char *src, size_t len) 324 { 325 if (!out || !out->syncout) 326 { 327 FILE *f = is_err ? stderr : stdout; 328 #ifdef KBUILD_OS_WINDOWS 329 maybe_con_fwrite (src, len, 1, f); 330 #else 331 fwrite (src, len, 1, f); 332 #endif 333 fflush (f); 334 } 335 else 336 { 337 struct output_membuf *membuf = is_err ? &out->err : &out->out; 338 while (len > 0) 339 { 340 size_t runlen = membuf_write_segment (membuf, membuf->tail_seg, src, len, &out->seqno); 341 if (!runlen) 342 { 343 size_t max_total = sizeof (membuf) <= 4 ? 512*1024 : 16*1024*1024; 344 if (membuf->total < max_total) 345 runlen = membuf_new_segment_write (membuf, src, len, &out->seqno); 346 else 347 membuf_dump_most (out); 348 } 349 /* advance */ 350 len -= runlen; 351 src += runlen; 352 } 353 } 354 } 355 356 #endif /* CONFIG_WITH_OUTPUT_IN_MEMORY */ 357 358 62 359 /* Write a string to the current STDOUT or STDERR. */ 63 360 static void 64 361 _outputs (struct output *out, int is_err, const char *msg) 65 362 { 363 #ifdef CONFIG_WITH_OUTPUT_IN_MEMORY 364 output_write (out, is_err, msg, strlen (msg)); 365 #else /* !CONFIG_WITH_OUTPUT_IN_MEMORY */ 66 366 if (! out || ! out->syncout) 67 367 { 68 368 FILE *f = is_err ? stderr : stdout; 69 #ifdef KBUILD_OS_WINDOWS 70 /** @todo check if fputs is also subject to char-by-char stupidity */ 369 # ifdef KBUILD_OS_WINDOWS 71 370 maybe_con_fwrite(msg, strlen(msg), 1, f); 72 # else371 # else 73 372 fputs (msg, f); 74 # endif373 # endif 75 374 fflush (f); 76 375 } … … 91 390 } 92 391 } 392 #endif /* !CONFIG_WITH_OUTPUT_IN_MEMORY */ 93 393 } 94 394 … … 229 529 } 230 530 531 #ifndef CONFIG_WITH_OUTPUT_IN_MEMORY 231 532 /* Support routine for output_sync() */ 232 533 static void 233 534 pump_from_tmp (int from, FILE *to) 234 535 { 536 # ifdef KMK 537 char buffer[8192]; 538 # else 235 539 static char buffer[8192]; 540 #endif 236 541 237 542 #ifdef WINDOWS32 … … 268 573 #endif 269 574 } 575 #endif /* CONFIG_WITH_OUTPUT_IN_MEMORY */ 270 576 271 577 /* Obtain the lock for writing output. */ … … 294 600 perror ("fcntl()"); 295 601 } 602 603 #ifndef CONFIG_WITH_OUTPUT_IN_MEMORY 296 604 297 605 /* Returns a file descriptor to a temporary file. The file is automatically … … 362 670 } 363 671 672 #endif /* CONFIG_WITH_OUTPUT_IN_MEMORY */ 673 364 674 /* Synchronize the output of jobs in -j mode to keep the results of 365 675 each job together. This is done by holding the results in temp files, … … 370 680 output_dump (struct output *out) 371 681 { 682 #ifdef CONFIG_WITH_OUTPUT_IN_MEMORY 683 membuf_dump (out); 684 #else 372 685 int outfd_not_empty = FD_NOT_EMPTY (out->out); 373 686 int errfd_not_empty = FD_NOT_EMPTY (out->err); … … 412 725 } 413 726 } 727 #endif 414 728 } 415 729 #endif /* NO_OUTPUT_SYNC */ … … 523 837 if (out) 524 838 { 839 #ifdef CONFIG_WITH_OUTPUT_IN_MEMORY 840 out->out.head_seg = NULL; 841 out->out.tail_seg = NULL; 842 out->out.head_run = NULL; 843 out->out.tail_run = NULL; 844 out->err.head_seg = NULL; 845 out->err.tail_seg = NULL; 846 out->err.head_run = NULL; 847 out->err.tail_run = NULL; 848 out->err.total = 0; 849 out->out.total = 0; 850 out->seqno = 0; 851 #else 525 852 out->out = out->err = OUTPUT_NONE; 853 #endif 526 854 out->syncout = !!output_sync; 527 855 return; … … 566 894 #endif 567 895 896 #ifdef CONFIG_WITH_OUTPUT_IN_MEMORY 897 assert (out->out.total == 0); 898 assert (out->out.head_seg == NULL); 899 assert (out->err.total == 0); 900 assert (out->err.head_seg == NULL); 901 #else 568 902 if (out->out >= 0) 569 903 close (out->out); 570 904 if (out->err >= 0 && out->err != out->out) 571 905 close (out->err); 906 #endif 572 907 573 908 output_init (out); … … 578 913 output_start (void) 579 914 { 915 #ifndef CONFIG_WITH_OUTPUT_IN_MEMORY 580 916 #ifndef NO_OUTPUT_SYNC 581 917 /* If we're syncing output make sure the temporary file is set up. */ … … 583 919 if (! OUTPUT_ISSET(output_context)) 584 920 setup_tmpfile (output_context); 921 #endif 585 922 #endif 586 923 -
trunk/src/kmk/output.h
r3140 r3189 15 15 this program. If not, see <http://www.gnu.org/licenses/>. */ 16 16 17 #ifdef CONFIG_WITH_OUTPUT_IN_MEMORY 18 /* Output run. */ 19 struct output_run 20 { 21 unsigned int seqno; /* For interleaving out/err output. */ 22 unsigned int len; /* The length of the output. */ 23 struct output_run *next; /* Pointer to the next run. */ 24 }; 25 26 /* Output segment. */ 27 struct output_segment 28 { 29 struct output_segment *next; 30 size_t size; /* Segment size, everything included. */ 31 struct output_run runs[1]; 32 }; 33 34 /* Output memory buffer. */ 35 struct output_membuf 36 { 37 struct output_run *head_run; 38 struct output_run *tail_run; /* Always in tail_seg. */ 39 struct output_segment *head_seg; 40 struct output_segment *tail_seg; 41 size_t left; /* Number of bytes that can be appended to 42 the tail_run. */ 43 size_t total; /* Total segment allocation size. */ 44 }; 45 #endif /* CONFIG_WITH_OUTPUT_IN_MEMORY */ 46 17 47 struct output 18 48 { 49 #ifdef CONFIG_WITH_OUTPUT_IN_MEMORY 50 struct output_membuf out; 51 struct output_membuf err; 52 unsigned int seqno; /* The current run sequence number. */ 53 #else 19 54 int out; 20 55 int err; 56 #endif 21 57 unsigned int syncout:1; /* True if we want to synchronize output. */ 22 58 };
Note:
See TracChangeset
for help on using the changeset viewer.