int main(int argc, char** argv )
{
	printf("%25s\t%15s\t%15s\t%15s\n", "clock", "res (ns)", "secs", "nsecs");
	struct timeval tv;
	gettimeofday(&tv, NULL);
	printf("%25s\t%15s\t%15s\t", "gettimeofday","1,000", commaprint(tv.tv_sec));
	printf("%15s\n", commaprint(tv.tv_usec*1000));
	#if _POSIX_TIMERS > 0
	#ifdef CLOCK_REALTIME
	do_clock(CLOCK_REALTIME);
	#endif
	#ifdef CLOCK_REALTIME_COARSE
	do_clock(CLOCK_REALTIME_COARSE);
	#endif
	#ifdef CLOCK_REALTIME_HR
	do_clock(CLOCK_REALTIME_HR);
	#endif
	#ifdef CLOCK_MONOTONIC
	do_clock(CLOCK_MONOTONIC);
	#endif
	#ifdef CLOCK_MONOTONIC_RAW
	do_clock(CLOCK_MONOTONIC_RAW);
	#endif
	#ifdef CLOCK_MONOTONIC_COARSE
	do_clock(CLOCK_MONOTONIC_COARSE);
	#endif
	#endif
	return 0;
}
static void render_update_stats(SlidingWindow *priv) {
    werase(priv->access_time_stats);
    unsigned int i;
    for (i = 0; i < 6; i++)
        wprintw(priv->access_time_stats, "%d\n", priv->access_time_stats_accum[i]);
    for (i = 1; i < 7; i++)
        wprintw(priv->access_time_stats, "%d\n", priv->error_stats_accum[i]);
    wnoutrefresh(priv->access_time_stats);

    if (priv->avg_processing_speed != 0) {
        werase(priv->avg_speed);
        wprintw(priv->avg_speed, "SPEED %7"PRIu64" kb/s", priv->avg_processing_speed / 1024);
        wnoutrefresh(priv->avg_speed);
    }

    if (priv->eta_time != 0) {
        unsigned int minute, second;
        second = priv->eta_time % 60;
        minute = priv->eta_time / 60;
        werase(priv->eta);
        wprintw(priv->eta, "ETA %11u:%02u", minute, second);
        wnoutrefresh(priv->eta);
    }

    werase(priv->w_cur_lba);
    char comma_lba_buf[30], *comma_lba_p;
    comma_lba_p = commaprint(priv->cur_lba, comma_lba_buf, sizeof(comma_lba_buf));
    wprintw(priv->w_cur_lba, "LBA: %14s", comma_lba_p);
    wnoutrefresh(priv->w_cur_lba);
}
Exemple #3
0
static void render_update_stats(WholeSpace *priv) {
    if (priv->avg_processing_speed != 0) {
        werase(priv->avg_speed);
        wprintw(priv->avg_speed, "SPEED %7"PRIu64" kb/s", priv->avg_processing_speed / 1024);
        wnoutrefresh(priv->avg_speed);
    }

    if (priv->eta_time != 0) {
        unsigned int minute, second;
        second = priv->eta_time % 60;
        minute = priv->eta_time / 60;
        werase(priv->eta);
        wprintw(priv->eta, "ETA %11u:%02u", minute, second);
        wnoutrefresh(priv->eta);
    }

    werase(priv->w_cur_lba);
    char comma_lba_buf[30], *comma_lba_p;
    comma_lba_p = commaprint(priv->cur_lba, comma_lba_buf, sizeof(comma_lba_buf));
    wprintw(priv->w_cur_lba, "LBA: %14s", comma_lba_p);
    wnoutrefresh(priv->w_cur_lba);

    werase(priv->w_stats);
    print_vis(priv->w_stats, bs_vis[0]);
    wattrset(priv->w_stats, A_NORMAL);
    wprintw(priv->w_stats, " %"PRIu64"\n", priv->unread_count);

    print_vis(priv->w_stats, bs_vis[3]);
    wattrset(priv->w_stats, A_NORMAL);
    wprintw(priv->w_stats, " %"PRIu64"\n", priv->read_ok_count);

    print_vis(priv->w_stats, error_vis[3]);
    wattrset(priv->w_stats, A_NORMAL);
    wprintw(priv->w_stats, " %"PRIu64"\n", priv->errors_count);

    wnoutrefresh(priv->w_stats);
}
/*
25x80

                                                             <--LEGEND_WIDTH=20->
+--------------------------------------------------------------------------------+
|                   LBA:       xxx,xxx / xxx,xxx,xxx         ETA           xx:xx |
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx SPEED    xxxxx kb/s |
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                     |
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x <3ms     xxxx     |^
|xxxxxxxxxxxxxxxxxxxxxxxxx                                   x <10ms    xxx      ||
|                                                            x <50ms    xx       ||
|                                                            x <150ms   x        ||
|                                                            x <500ms   x        ||
|                                                            x >500ms   x        || LEGEND_HEIGHT=12
|                                                            x ERR      x        ||
|                                                            ? TIME     x        ||
|                                                            x UNC      x        ||
|                                                            S IDNF     x        ||
|                                                            ! ABRT     x        ||
|                                                            A AMNF     x        |v
|                                                                                |
|                                                            Read test /dev/XXX  |
|                                                            Block = 131072 bytes|
|                                                                                |
|                                                            Ctrl+C to abort     |
|                                                                                |
|                                                                                |
|                                                                                |
|                                                                                |
| WHDD rev. X.X-X-gXXXXXXX                                                       |
+--------------------------------------------------------------------------------+
*/
static int Open(DC_RendererCtx *ctx) {
    SlidingWindow *priv = ctx->priv;
    DC_ProcedureCtx *actctx = ctx->procedure_ctx;

    // TODO Raise error message
    if (LINES < 25 || COLS < 80)
        return -1;

#define LBA_WIDTH 20
#define LEGEND_WIDTH 20
#define LEGEND_HEIGHT 12
#define LEGEND_VERT_OFFSET 3 /* ETA & SPEED are above, 1 for spacing */

    priv->w_cur_lba = derwin(stdscr, 1, LBA_WIDTH, 0 /* at the top */, COLS - LEGEND_WIDTH - 1 - (LBA_WIDTH * 2) );
    assert(priv->w_cur_lba);
    wbkgd(priv->w_cur_lba, COLOR_PAIR(MY_COLOR_GRAY));

    priv->w_end_lba = derwin(stdscr, 1, LBA_WIDTH, 0 /* at the top */, COLS - LEGEND_WIDTH - 1 - LBA_WIDTH);
    assert(priv->w_end_lba);
    wbkgd(priv->w_end_lba, COLOR_PAIR(MY_COLOR_GRAY));

    priv->eta = derwin(stdscr, 1, LEGEND_WIDTH, 0 /* at the top */, COLS-LEGEND_WIDTH);
    assert(priv->eta);
    wbkgd(priv->eta, COLOR_PAIR(MY_COLOR_GRAY));

    priv->avg_speed = derwin(stdscr, 1, LEGEND_WIDTH, 1 /* ETA is above */, COLS-LEGEND_WIDTH);
    assert(priv->avg_speed);
    wbkgd(priv->avg_speed, COLOR_PAIR(MY_COLOR_GRAY));

    priv->legend = derwin(stdscr, LEGEND_HEIGHT, LEGEND_WIDTH/2, LEGEND_VERT_OFFSET, COLS-LEGEND_WIDTH);
    assert(priv->legend);
    wbkgd(priv->legend, COLOR_PAIR(MY_COLOR_GRAY));

    priv->access_time_stats = derwin(stdscr, LEGEND_HEIGHT, LEGEND_WIDTH/2, LEGEND_VERT_OFFSET, COLS-LEGEND_WIDTH/2);
    assert(priv->access_time_stats);
    wbkgd(priv->access_time_stats, COLOR_PAIR(MY_COLOR_GRAY));
    show_legend(priv->legend);

#define SUMMARY_VERT_OFFSET ( 1 /* ETA */ + 1 + /* SPEED */ + 1 /* spacing */ + LEGEND_HEIGHT + 1 /* spacing */ )
#define SUMMARY_HEIGHT ( LINES - SUMMARY_VERT_OFFSET - 1 /* don't touch bottom line */ )
    priv->summary = derwin(stdscr, SUMMARY_HEIGHT, LEGEND_WIDTH, SUMMARY_VERT_OFFSET, COLS-LEGEND_WIDTH);
    assert(priv->summary);
    wbkgd(priv->summary, COLOR_PAIR(MY_COLOR_GRAY));

    priv->vis = derwin(stdscr, LINES-2 /* version is below, LBA is above */, COLS-LEGEND_WIDTH-1, 1 /* LBA is above */, 0);
    assert(priv->vis);
    scrollok(priv->vis, TRUE);
    wrefresh(priv->vis);

    priv->reports[0].seqno = 1; // anything but zero

    char comma_lba_buf[30], *comma_lba_p;
    comma_lba_p = commaprint(actctx->dev->capacity / 512, comma_lba_buf, sizeof(comma_lba_buf));
    wprintw(priv->w_end_lba, "/ %s", comma_lba_p);
    wnoutrefresh(priv->w_end_lba);
    wprintw(priv->summary,
            "%s %s\n"
            "Block = %d bytes\n"
            "Ctrl+C to abort\n",
            actctx->procedure->display_name, actctx->dev->dev_path, actctx->blk_size);
    wrefresh(priv->summary);
    int r = pthread_create(&priv->render_thread, NULL, render_thread_proc, priv);
    if (r)
        return r; // FIXME leak
    return 0;
}
/* Function:  main()
 * Synopsis:  Run set of queries against an FM
 * Purpose:   Read in a FM and a file of query sequences.
 *            For each query, find matching FM interval, then collect positions in
 *            the original text T for the corresponding occurrences. These positions
 *            are 0-based (so first character is position 0).
 */
int
main(int argc,  char *argv[]) 
{
  void* tmp; // used for RALLOC calls
  clock_t t1, t2;
  struct tms ts1, ts2;
  char *fname_fm      = NULL;
  char *fname_queries = NULL;
  FM_HIT *hits        = NULL;
  char *line          = NULL;
  int status        = eslOK;
  int hit_cnt       = 0;
  int hit_indiv_cnt = 0;
  int miss_cnt      = 0;
  int hit_num       = 0;
  int hit_num2       = 0;
  int hits_size     = 0;
  int i,j;
  int count_only    = 0;

  FM_INTERVAL interval;
  FM_DATA *fmsf = NULL;
  FM_DATA *fmsb = NULL;
  FILE* fp_fm   = NULL;
  FILE* fp      = NULL;
  FILE* out     = NULL;
  char *outname = NULL;

  ESL_GETOPTS     *go  = NULL;    /* command line processing                 */
  FM_CFG *cfg;
  FM_METADATA *meta;

  ESL_SQ       *tmpseq;  // used for sequence validation
  ESL_ALPHABET *abc = NULL;


  //start timer
  t1 = times(&ts1);

  process_commandline(argc, argv, &go, &fname_fm, &fname_queries);


  if (esl_opt_IsOn(go, "--out")) {
    outname = esl_opt_GetString(go, "--out");
    if ( esl_strcmp ("-", outname) == 0 ) {
      out = stdout;
      outname = "stdout";
    } else {
      out = fopen(outname,"w");
    }
  }

  if (esl_opt_IsOn(go, "--count_only"))
    count_only = 1;


  if((fp_fm = fopen(fname_fm, "rb")) == NULL)
    esl_fatal("Cannot open file `%s': ", fname_fm);


  fm_configAlloc(&cfg);
  cfg->occCallCnt = 0;
  meta = cfg->meta;
  meta->fp = fp_fm;


  fm_readFMmeta( meta);



  if      (meta->alph_type == fm_DNA)   abc     = esl_alphabet_Create(eslDNA);
  else if (meta->alph_type == fm_AMINO) abc     = esl_alphabet_Create(eslAMINO);
  tmpseq = esl_sq_CreateDigital(abc);



  //read in FM-index blocks
  ESL_ALLOC(fmsf, meta->block_count * sizeof(FM_DATA) );
  if (!meta->fwd_only)
    ESL_ALLOC(fmsb, meta->block_count * sizeof(FM_DATA) );

  for (i=0; i<meta->block_count; i++) {
    fm_FM_read( fmsf+i,meta, TRUE );

    if (!meta->fwd_only) {
      fm_FM_read(fmsb+i, meta, FALSE );
      fmsb[i].SA = fmsf[i].SA;
      fmsb[i].T = fmsf[i].T;
    }
  }
  fclose(fp_fm);

  output_header(meta, stdout, go, fname_fm, fname_queries);


  /* initialize a few global variables, then call initGlobals
   * to do architecture-specific initialization
   */
  fm_configInit(cfg, NULL);

  fm_alphabetCreate(meta, NULL); // don't override charBits

  fp = fopen(fname_queries,"r");
  if (fp == NULL)
    esl_fatal("Unable to open file %s\n", fname_queries);

  ESL_ALLOC(line, FM_MAX_LINE * sizeof(char));

  hits_size = 200;
  ESL_ALLOC(hits, hits_size * sizeof(FM_HIT));

  while(fgets(line, FM_MAX_LINE, fp) ) {
    int qlen=0;
    while (line[qlen] != '\0' && line[qlen] != '\n')  qlen++;
    if (line[qlen] == '\n')  line[qlen] = '\0';

    hit_num = 0;

    for (i=0; i<meta->block_count; i++) {

      fm_getSARangeReverse(fmsf+i, cfg, line, meta->inv_alph, &interval);
      if (interval.lower>=0 && interval.lower <= interval.upper) {
        int new_hit_num =  interval.upper - interval.lower + 1;
        hit_num += new_hit_num;
        if (!count_only) {
          if (hit_num > hits_size) {
            hits_size = 2*hit_num;
            ESL_RALLOC(hits, tmp, hits_size * sizeof(FM_HIT));
          }
          getFMHits(fmsf+i, cfg, &interval, i, hit_num-new_hit_num, qlen, hits, fm_forward);
        }

      }


      /* find reverse hits, using backward search on the forward FM*/
      if (!meta->fwd_only) {
        fm_getSARangeForward(fmsb+i, cfg, line, meta->inv_alph, &interval);// yes, use the backward fm to produce the equivalent of a forward search on the forward fm
        if (interval.lower>=0 && interval.lower <= interval.upper) {
          int new_hit_num =  interval.upper - interval.lower + 1;
          hit_num += new_hit_num;
          if (!count_only) {
            if (hit_num > hits_size) {
              hits_size = 2*hit_num;
              ESL_RALLOC(hits, tmp, hits_size * sizeof(FM_HIT));
            }
            //even though I used fmsb above, use fmsf here, since we'll now do a backward trace
            //in the FM-index to find the next sampled SA position
            getFMHits(fmsf+i, cfg, &interval, i, hit_num-new_hit_num, qlen, hits, fm_backward);
          }
        }

      }

    }


    if (hit_num > 0) {
      if (count_only) {
        hit_cnt++;
        hit_indiv_cnt += hit_num;
      } else {
        hit_num2 = 0;

        //for each hit, identify the sequence id and position within that sequence
        for (i = 0; i< hit_num; i++) {

          status = fm_getOriginalPosition (fmsf, meta, hits[i].block, hits[i].length, fm_forward, hits[i].start,  &(hits[i].block), &(hits[i].start) );
          hits[i].sortkey = (status==eslERANGE ? -1 : meta->seq_data[ hits[i].block ].target_id);

          //validate match - if any characters in orig sequence were ambiguities, reject
          fm_convertRange2DSQ( fmsf, meta, hits[i].start, hits[i].length, p7_NOCOMPLEMENT, tmpseq, TRUE );
          for (j=1; j<=hits[i].length; j++) {
            if (tmpseq->dsq[j] >= abc->K) {
              hits[i].sortkey = -1; //reject
              j = hits[i].length+1; //quit looking
            }
          }

          if (hits[i].sortkey != -1)
            hit_num2++; // legitimate hit

        }
        if (hit_num2 > 0)
          hit_cnt++;

        //now sort according the the sequence_id corresponding to that seq_offset
        qsort(hits, hit_num, sizeof(FM_HIT), hit_sorter);

        //skim past the skipped entries
        i = 0;
        while ( i < hit_num ) {
          if (hits[i].sortkey != -1 )
            break;  //
          i++;
        }


        if (i < hit_num) {
          if (out != NULL) {
            fprintf (out, "%s\n",line);
            //fprintf (out, "\t%10s (%8d %s)\n",meta->seq_data[ hits[i].block ].name, hits[i].start, (hits[i].direction==fm_forward?"+":"-"));
            fprintf (out, "    %8ld %s %10s\n", (long)(hits[i].start), (hits[i].direction==fm_forward?"f":"r"), meta->seq_data[ hits[i].block ].name);
          }
          hit_indiv_cnt++;
          i++; // skip the first one, since I'll be comparing each to the previous

          for (  ; i< hit_num; i++) {
            if ( //meta->seq_data[ hits[i].block ].id != meta->seq_data[ hits[i-1].block ].id ||
                 hits[i].sortkey   != hits[i-1].sortkey ||  //sortkey is seq_data[].id
                 hits[i].direction != hits[i-1].direction ||
                 hits[i].start     != hits[i-1].start )
            {
              if (out != NULL)
                //fprintf (out, "\t%10s (%8d %s)\n",meta->seq_data[ hits[i].block ].name, hits[i].start, (hits[i].direction==fm_forward?"+":"-"));
                fprintf (out, "    %8ld %s %10s\n", (long)(hits[i].start), (hits[i].direction==fm_forward?"f":"r"), meta->seq_data[ hits[i].block ].name);
              hit_indiv_cnt++;
            }
          }
          if (out != NULL)
            fprintf (out, "\n");
        }
      }
    } else {
      miss_cnt++;
    }


  }

  for (i=0; i<meta->block_count; i++) {
    fm_FM_destroy( fmsf+i, 1 );
    if (!meta->fwd_only)
      fm_FM_destroy( fmsb+i, 0 );
  }


  free (hits);
  free (line);
  fclose(fp);

  fm_configDestroy(cfg);


  // compute and print the elapsed time in millisec
  t2 = times(&ts2);
  {
    double clk_ticks = sysconf(_SC_CLK_TCK);
    double elapsedTime = (t2-t1)/clk_ticks;
    double throughput = cfg->occCallCnt/elapsedTime;

    fprintf (stderr, "hit: %-10d  (%d)\n", hit_cnt, hit_indiv_cnt);
    fprintf (stderr, "miss:%-10d\n", miss_cnt);
    fprintf (stderr, "run time:  %.2f seconds\n", elapsedTime);
    fprintf (stderr, "occ calls: %12s\n", commaprint(cfg->occCallCnt));
    fprintf (stderr, "occ/sec:   %12s\n", commaprint(throughput));
  }

  exit(eslOK);


ERROR:
  printf ("failure allocating memory for hits\n");
  exit(status);


}
Exemple #6
0
static int Open(DC_RendererCtx *ctx) {
    WholeSpace *priv = ctx->priv;
    DC_ProcedureCtx *actctx = ctx->procedure_ctx;

    priv->nb_blocks = actctx->dev->capacity / actctx->blk_size;
    if (actctx->dev->capacity % actctx->blk_size)
        priv->nb_blocks++;
    priv->unread_count = actctx->progress.den;
    priv->sectors_per_block = actctx->blk_size / 512;
    priv->blocks_map = calloc(priv->nb_blocks, sizeof(uint8_t));
    assert(priv->blocks_map);
    int journal_fd = ((CopyPriv*)actctx->priv)->journal_fd;
    lseek(journal_fd, 0, SEEK_SET);
    if (((CopyPriv*)actctx->priv)->use_journal) {
        priv->unread_count = 0;
        uint8_t journal_chunk[1*1024*1024];
        int64_t chunklen;
        int64_t end_lba = ((CopyPriv*)actctx->priv)->end_lba;
        for (int64_t i = 0; i < priv->nb_blocks; i++) {
            int64_t lba = i * priv->sectors_per_block;
            if (lba % sizeof(journal_chunk) == 0) {
                chunklen = (end_lba - lba) < (int64_t)sizeof(journal_chunk) ? (end_lba - lba) : (int64_t)sizeof(journal_chunk);
                int ret = read(journal_fd, journal_chunk, chunklen);
                if (ret != chunklen)
                    return 1;
            }
            char sector_status = journal_chunk[lba % sizeof(journal_chunk)];
            priv->blocks_map[i] = sector_status;
            int sectors_in_block = priv->sectors_per_block;
            if (i == priv->nb_blocks - 1)  // Last block may be smaller
                sectors_in_block = (actctx->dev->capacity % actctx->blk_size) / 512;
            switch ((enum SectorStatus)sector_status) {
                case SectorStatus_eUnread:
                    priv->unread_count += sectors_in_block;
                    break;
                case SectorStatus_eReadOk:
                    priv->read_ok_count += sectors_in_block;
                    break;
                case SectorStatus_eBlockReadError:
                case SectorStatus_eSectorReadError:
                    priv->errors_count += sectors_in_block;
                    break;
            }
        }
    }
    priv->legend = derwin(stdscr, 9 /* legend win height */, LEGEND_WIDTH, 4, COLS-LEGEND_WIDTH); // leave 1st and last lines untouched
    assert(priv->legend);
    wbkgd(priv->legend, COLOR_PAIR(MY_COLOR_GRAY));

    priv->w_stats = derwin(stdscr, 3, LEGEND_WIDTH, 4+9, COLS-LEGEND_WIDTH);
    assert(priv->w_stats);

    priv->vis_height = LINES - 5;
    priv->vis_width = COLS - LEGEND_WIDTH - 1;
    int vis_cells_avail = priv->vis_height * priv->vis_width;
    priv->blocks_per_vis = priv->nb_blocks / vis_cells_avail;
    if (priv->nb_blocks % vis_cells_avail)
        priv->blocks_per_vis++;
    priv->vis = derwin(stdscr, priv->vis_height, priv->vis_width, 2, 0); // leave 1st and last lines untouched
    assert(priv->vis);
    wrefresh(priv->vis);

    whole_space_show_legend(priv);

    priv->avg_speed = derwin(stdscr, 1, LEGEND_WIDTH, 2, COLS-LEGEND_WIDTH);
    assert(priv->avg_speed);
    wbkgd(priv->avg_speed, COLOR_PAIR(MY_COLOR_GRAY));

    priv->eta = derwin(stdscr, 1, LEGEND_WIDTH, 1, COLS-LEGEND_WIDTH);
    assert(priv->eta);
    wbkgd(priv->eta, COLOR_PAIR(MY_COLOR_GRAY));

    priv->summary = derwin(stdscr, 10, LEGEND_WIDTH, 16, COLS-LEGEND_WIDTH);
    assert(priv->summary);
    wbkgd(priv->summary, COLOR_PAIR(MY_COLOR_GRAY));

    priv->w_end_lba = derwin(stdscr, 1, 20, 1, COLS-41);
    assert(priv->w_end_lba);
    wbkgd(priv->w_end_lba, COLOR_PAIR(MY_COLOR_GRAY));

    priv->w_cur_lba = derwin(stdscr, 1, 20, 1, COLS-61);
    assert(priv->w_cur_lba);
    wbkgd(priv->w_cur_lba, COLOR_PAIR(MY_COLOR_GRAY));

    priv->w_log = derwin(stdscr, 2, COLS, LINES-3, 0);
    assert(priv->w_log);
    scrollok(priv->w_log, TRUE);
    wbkgd(priv->w_log, COLOR_PAIR(MY_COLOR_GRAY));

    priv->reports[0].seqno = 1; // anything but zero

    char comma_lba_buf[30], *comma_lba_p;
    comma_lba_p = commaprint(actctx->dev->capacity / 512, comma_lba_buf, sizeof(comma_lba_buf));
    wprintw(priv->w_end_lba, "/ %s", comma_lba_p);
    wnoutrefresh(priv->w_end_lba);
    wprintw(priv->summary,
            "%s %s\n"
            "Ctrl+C to abort\n",
            actctx->procedure->display_name, actctx->dev->dev_path);
    wrefresh(priv->summary);
    int r = pthread_create(&priv->render_thread, NULL, render_thread_proc, priv);
    if (r)
        return r; // FIXME leak
    return 0;
}
/*
25x80

                                                             <--LEGEND_WIDTH=20->
+--------------------------------------------------------------------------------+
|                   LBA:       xxx,xxx / xxx,xxx,xxx         ETA           xx:xx |
|ZZZZZZZZZZZZZZZxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx SPEED    xxxxx kb/s |
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                     |
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x unread space      |^
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                     ||
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x copied space,     ||
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   no read errors    ||
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x read errors       ||
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   occured           || LEGEND_HEIGHT=9
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Display block is XXX||
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx XXX blocks by 256 se||
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ctors               |v
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x NNNNNNNNNN        |^
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x NNNNNNNNNN        || W_STATS_HEIGHT=3
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x NNNNNNNNNN        |v
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                     |
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Device copying /dev/|^
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx XXX                 ||
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Ctrl+C to abort     ||
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                     || SUMMARY
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                     ||
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                     ||
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                     ||
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                               |v
| WHDD rev. X.X-X-gXXXXXXX                                                       |
+--------------------------------------------------------------------------------+
*/
static int Open(DC_RendererCtx *ctx) {
    WholeSpace *priv = ctx->priv;
    DC_ProcedureCtx *actctx = ctx->procedure_ctx;

    // TODO Raise error message
    if (LINES < 25 || COLS < 80)
        return -1;

    priv->nb_blocks = actctx->dev->capacity / actctx->blk_size;
    if (actctx->dev->capacity % actctx->blk_size)
        priv->nb_blocks++;
    priv->unread_count = actctx->progress.den;
    priv->sectors_per_block = actctx->blk_size / 512;
    priv->blocks_map = calloc(priv->nb_blocks, sizeof(uint8_t));
    assert(priv->blocks_map);
    int journal_fd = ((CopyPriv*)actctx->priv)->journal_fd;
    lseek(journal_fd, 0, SEEK_SET);
    if (((CopyPriv*)actctx->priv)->use_journal) {
        priv->unread_count = 0;
        uint8_t journal_chunk[1*1024*1024];
        int64_t chunklen;
        int64_t end_lba = ((CopyPriv*)actctx->priv)->end_lba;
        for (int64_t i = 0; i < priv->nb_blocks; i++) {
            int64_t lba = i * priv->sectors_per_block;
            if (lba % sizeof(journal_chunk) == 0) {
                chunklen = (end_lba - lba) < (int64_t)sizeof(journal_chunk) ? (end_lba - lba) : (int64_t)sizeof(journal_chunk);
                int ret = read(journal_fd, journal_chunk, chunklen);
                if (ret != chunklen)
                    return 1;
            }
            char sector_status = journal_chunk[lba % sizeof(journal_chunk)];
            priv->blocks_map[i] = sector_status;
            int sectors_in_block = priv->sectors_per_block;
            if (i == priv->nb_blocks - 1)  // Last block may be smaller
                sectors_in_block = (actctx->dev->capacity % actctx->blk_size) / 512;
            switch ((enum SectorStatus)sector_status) {
                case SectorStatus_eUnread:
                    priv->unread_count += sectors_in_block;
                    break;
                case SectorStatus_eReadOk:
                    priv->read_ok_count += sectors_in_block;
                    break;
                case SectorStatus_eBlockReadError:
                case SectorStatus_eSectorReadError:
                    priv->errors_count += sectors_in_block;
                    break;
            }
        }
    }

#define LBA_WIDTH 20
#define LEGEND_WIDTH 20
#define LEGEND_HEIGHT 9
#define LEGEND_VERT_OFFSET 3 /* ETA & SPEED are above, 1 for spacing */

    priv->w_cur_lba = derwin(stdscr, 1, LBA_WIDTH, 0 /* at the top */, COLS - LEGEND_WIDTH - 1 - (LBA_WIDTH * 2) );
    assert(priv->w_cur_lba);
    wbkgd(priv->w_cur_lba, COLOR_PAIR(MY_COLOR_GRAY));

    priv->w_end_lba = derwin(stdscr, 1, LBA_WIDTH, 0 /* at the top */, COLS - LEGEND_WIDTH - 1 - LBA_WIDTH);
    assert(priv->w_end_lba);
    wbkgd(priv->w_end_lba, COLOR_PAIR(MY_COLOR_GRAY));

    priv->eta = derwin(stdscr, 1, LEGEND_WIDTH, 0 /* at the top */, COLS-LEGEND_WIDTH);
    assert(priv->eta);
    wbkgd(priv->eta, COLOR_PAIR(MY_COLOR_GRAY));

    priv->avg_speed = derwin(stdscr, 1, LEGEND_WIDTH, 1 /* ETA is above */, COLS-LEGEND_WIDTH);
    assert(priv->avg_speed);
    wbkgd(priv->avg_speed, COLOR_PAIR(MY_COLOR_GRAY));

    priv->legend = derwin(stdscr, LEGEND_HEIGHT, LEGEND_WIDTH, LEGEND_VERT_OFFSET, COLS-LEGEND_WIDTH);
    assert(priv->legend);
    wbkgd(priv->legend, COLOR_PAIR(MY_COLOR_GRAY));

#define W_STATS_HEIGHT 3
#define W_STATS_VERT_OFFSET ( LEGEND_VERT_OFFSET + LEGEND_HEIGHT + 1 /* spacing */ )
    priv->w_stats = derwin(stdscr, W_STATS_HEIGHT, LEGEND_WIDTH, W_STATS_VERT_OFFSET, COLS-LEGEND_WIDTH);
    assert(priv->w_stats);

#define SUMMARY_VERT_OFFSET ( W_STATS_VERT_OFFSET + W_STATS_HEIGHT + 1 /* spacing */ )
#define SUMMARY_HEIGHT ( LINES - SUMMARY_VERT_OFFSET - 1 /* don't touch bottom line */ )
    priv->summary = derwin(stdscr, SUMMARY_HEIGHT, LEGEND_WIDTH, SUMMARY_VERT_OFFSET, COLS-LEGEND_WIDTH);
    assert(priv->summary);
    wbkgd(priv->summary, COLOR_PAIR(MY_COLOR_GRAY));

    priv->vis_height = LINES - 2; /* LBA is above, version is below */
    priv->vis_width = COLS - LEGEND_WIDTH - 1;
    int vis_cells_avail = priv->vis_height * priv->vis_width;
    priv->blocks_per_vis = priv->nb_blocks / vis_cells_avail;
    if (priv->nb_blocks % vis_cells_avail)
        priv->blocks_per_vis++;
    priv->vis = derwin(stdscr, priv->vis_height, priv->vis_width, 1 /* LBA is above */, 0);
    assert(priv->vis);
    wrefresh(priv->vis);

    whole_space_show_legend(priv);

    priv->reports[0].seqno = 1; // anything but zero

    char comma_lba_buf[30], *comma_lba_p;
    comma_lba_p = commaprint(actctx->dev->capacity / 512, comma_lba_buf, sizeof(comma_lba_buf));
    wprintw(priv->w_end_lba, "/ %s", comma_lba_p);
    wnoutrefresh(priv->w_end_lba);
    wprintw(priv->summary,
            "%s %s\n"
            "Ctrl+C to abort\n",
            actctx->procedure->display_name, actctx->dev->dev_path);
    wrefresh(priv->summary);
    int r = pthread_create(&priv->render_thread, NULL, render_thread_proc, priv);
    if (r)
        return r; // FIXME leak
    return 0;
}