VirtualBox

Changeset 3189 in kBuild


Ignore:
Timestamp:
Mar 25, 2018 5:05:19 PM (7 years ago)
Author:
bird
Message:

kmk/output: working on memory buffering rather than file buffering of job output.

Location:
trunk/src/kmk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kmk/Makefile.kmk

    r3173 r3189  
    222222kmk_DEFS.x86 = CONFIG_WITH_OPTIMIZATION_HACKS
    223223kmk_DEFS.amd64 = CONFIG_WITH_OPTIMIZATION_HACKS
    224 kmk_DEFS.win = CONFIG_NEW_WIN32_CTRL_EVENT
     224kmk_DEFS.win = CONFIG_NEW_WIN32_CTRL_EVENT CONFIG_WITH_OUTPUT_IN_MEMORY
    225225kmk_DEFS.debug = CONFIG_WITH_MAKE_STATS
    226226ifdef CONFIG_WITH_MAKE_STATS
  • trunk/src/kmk/function.c

    r3186 r3189  
    25702570  output_start ();
    25712571
     2572#ifdef CONFIG_WITH_OUTPUT_IN_MEMORY
     2573  errfd = -1; /** @todo fixme */
     2574#else
    25722575  errfd = (output_context && output_context->err >= 0
    25732576           ? output_context->err : FD_STDERR);
     2577#endif
    25742578
    25752579#if defined(__MSDOS__)
  • trunk/src/kmk/output.c

    r3188 r3189  
    6060#endif
    6161
     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
     70static void *acquire_semaphore (void);
     71static void  release_semaphore (void *);
     72static int   log_working_directory (int);
     73
     74/* Internal worker for output_dump and membuf_dump_most. */
     75static 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.  */
     167static size_t
     168membuf_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.  */
     220static 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. */
     239static size_t
     240membuf_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. */
     314static void
     315membuf_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. */
     322void
     323output_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
    62359/* Write a string to the current STDOUT or STDERR.  */
    63360static void
    64361_outputs (struct output *out, int is_err, const char *msg)
    65362{
     363#ifdef CONFIG_WITH_OUTPUT_IN_MEMORY
     364  output_write (out, is_err, msg, strlen (msg));
     365#else  /* !CONFIG_WITH_OUTPUT_IN_MEMORY */
    66366  if (! out || ! out->syncout)
    67367    {
    68368      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
    71370      maybe_con_fwrite(msg, strlen(msg), 1, f);
    72 #else
     371# else
    73372      fputs (msg, f);
    74 #endif
     373# endif
    75374      fflush (f);
    76375    }
     
    91390        }
    92391    }
     392#endif /* !CONFIG_WITH_OUTPUT_IN_MEMORY */
    93393}
    94394
     
    229529}
    230530
     531#ifndef CONFIG_WITH_OUTPUT_IN_MEMORY
    231532/* Support routine for output_sync() */
    232533static void
    233534pump_from_tmp (int from, FILE *to)
    234535{
     536# ifdef KMK
     537  char buffer[8192];
     538# else
    235539  static char buffer[8192];
     540#endif
    236541
    237542#ifdef WINDOWS32
     
    268573#endif
    269574}
     575#endif /* CONFIG_WITH_OUTPUT_IN_MEMORY */
    270576
    271577/* Obtain the lock for writing output.  */
     
    294600    perror ("fcntl()");
    295601}
     602
     603#ifndef CONFIG_WITH_OUTPUT_IN_MEMORY
    296604
    297605/* Returns a file descriptor to a temporary file.  The file is automatically
     
    362670}
    363671
     672#endif /* CONFIG_WITH_OUTPUT_IN_MEMORY */
     673
    364674/* Synchronize the output of jobs in -j mode to keep the results of
    365675   each job together. This is done by holding the results in temp files,
     
    370680output_dump (struct output *out)
    371681{
     682#ifdef CONFIG_WITH_OUTPUT_IN_MEMORY
     683  membuf_dump (out);
     684#else
    372685  int outfd_not_empty = FD_NOT_EMPTY (out->out);
    373686  int errfd_not_empty = FD_NOT_EMPTY (out->err);
     
    412725        }
    413726    }
     727#endif
    414728}
    415729#endif /* NO_OUTPUT_SYNC */
     
    523837  if (out)
    524838    {
     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
    525852      out->out = out->err = OUTPUT_NONE;
     853#endif
    526854      out->syncout = !!output_sync;
    527855      return;
     
    566894#endif
    567895
     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
    568902  if (out->out >= 0)
    569903    close (out->out);
    570904  if (out->err >= 0 && out->err != out->out)
    571905    close (out->err);
     906#endif
    572907
    573908  output_init (out);
     
    578913output_start (void)
    579914{
     915#ifndef CONFIG_WITH_OUTPUT_IN_MEMORY
    580916#ifndef NO_OUTPUT_SYNC
    581917  /* If we're syncing output make sure the temporary file is set up.  */
     
    583919    if (! OUTPUT_ISSET(output_context))
    584920      setup_tmpfile (output_context);
     921#endif
    585922#endif
    586923
  • trunk/src/kmk/output.h

    r3140 r3189  
    1515this program.  If not, see <http://www.gnu.org/licenses/>.  */
    1616
     17#ifdef CONFIG_WITH_OUTPUT_IN_MEMORY
     18/*  Output run. */
     19struct 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. */
     27struct 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. */
     35struct 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
    1747struct output
    1848  {
     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
    1954    int out;
    2055    int err;
     56#endif
    2157    unsigned int syncout:1;     /* True if we want to synchronize output.  */
    2258 };
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette