/*
 * pinfo.c
 *
 * Retrieve drive and partition info
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <nasd/nasd_getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <nasd/nasd_pdrive.h>
#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_control.h>
#include <nasd/nasd_general.h>
#include <nasd/nasd_security.h>

char *progname;

nasd_error_string_t error_text;
nasd_sec_keyring_t    keys;
nasd_uint16           protection;
nasd_drive_handle_t   h;
char                  *server_name;

int exclude_part_info = 0;
int exclude_drive_info = 0;
int include_cache_stats = 0;
int include_op_stats = 0;
int include_rpc_qdepth_stats = 0;
int include_io_stats = 0;
int summarize_op_stats = 0;
int zero_drive_stats = 0;
int include_layout_stats = 0;
int include_layout_name = 0;
int include_ioqueue_stats = 0;
int include_ioqueue_name = 0;
int include_total_op_time = 0;
int include_minmax = 0;

double total_optime, total_rpc_queuetime;

static char cache_type_string[NASD_DRIVE_CACHE_TYPES][14] = {
  "bogus",
  "node",
  "indirect",
  "free",
  "refcnt",
  "npt1",
  "data",
  "anon",
  "layout-dyn",
  "layout-stk",
  "npt2"
};

#define DUMP_OPSTAT(_opname_) \
  nasd_dump_opstat(&dr_opstats->_opname_,NASD_STRING(_opname_))

#define SUMMARIZE_OPSTAT(_opname_) \
  nasd_summarize_opstat(&dr_opstats->_opname_,NASD_STRING(_opname_))

#define ADD_OPSTAT(_opname_) { \
  d = (double)dr_opstats->_opname_.op_nsecs; \
  d /= (double)NASD_NSEC_PER_SEC; \
  total_optime += d; \
}

#define DUMP_CACHESTAT(_csname_) { \
  int _i; \
  _i = printf("  %s", NASD_STRING(_csname_)); \
  for(;_i<31;_i++) \
    printf(" "); \
  printf("%10"NASD_64u_FMT"\n", ca_stats->_csname_); \
}

#define DUMP_TIME_CACHESTAT(_csname_) { \
  char _str[128]; \
  int _i; \
  sprintf(_str, "%d:%09d", ca_stats->_csname_.ts_sec, ca_stats->_csname_.ts_nsec); \
  _i = printf("  %s", NASD_STRING(_csname_)); \
  for(;_i<41-strlen(_str);_i++) \
    printf(" "); \
  printf("%s\n", _str); \
}

#define DUMP_TYPED_CACHESTAT(_csname_) { \
  int _i, _j; \
  for(_j=0;_j<NASD_DRIVE_CACHE_TYPES;_j++) { \
    _i = printf("  %s[%s]", NASD_STRING(_csname_), cache_type_string[_j]); \
    for(;_i<31;_i++) \
      printf(" "); \
    printf("%10"NASD_64u_FMT"\n", ca_stats->_csname_[_j]); \
  } \
}

#define DUMP_UIOSTAT(_csname_) { \
  int _i; \
  _i = printf("  %s", NASD_STRING(_csname_)); \
  for(;_i<31;_i++) \
    printf(" "); \
  printf("%10"NASD_64u_FMT"\n", io_stats->_csname_); \
}

#define DUMP_TIME_UIOSTAT(_csname_) { \
  char _str[128]; \
  double _d; \
  int _i; \
  sprintf(_str, "%d:%09d", io_stats->_csname_.ts_sec, io_stats->_csname_.ts_nsec); \
  _d = (double)io_stats->_csname_.ts_nsec; \
  _d /= (double)NASD_NSEC_PER_SEC; \
  _d += (double)io_stats->_csname_.ts_sec; \
  if (io_stats->io_completion_count) { \
    _d /= (double)io_stats->io_completion_count; \
  } \
  _i = printf("  %s", NASD_STRING(_csname_)); \
  for(;_i<41-strlen(_str);_i++) \
    printf(" "); \
  printf("%s  (%.9f avg)\n", _str, _d); \
}

#define DUMP_LAYOUT_TIME(_tmname_) { \
  int _i; \
  _i = printf("  %s", NASD_STRING(_tmname_)); \
  for(;_i<32;_i++) \
    printf(" "); \
  printf("%2d:%09d\n", layout_stats->_tmname_.ts_sec, layout_stats->_tmname_.ts_nsec); \
}

#define DUMP_IOQUEUE_STAT(_stat_) { \
  int _i; \
  _i = printf("  %s", NASD_STRING(_stat_)); \
  for(;_i<32;_i++) \
    printf(" "); \
  printf("%12"NASD_64u_FMT"\n", ioqueue_stats->_stat_); \
}

#define DUMP_IOQUEUE_TIME(_tmname_) { \
  int _i; \
  _i = printf("  %s", NASD_STRING(_tmname_)); \
  for(;_i<32;_i++) \
    printf(" "); \
  printf("%2d:%09d\n", ioqueue_stats->_tmname_.ts_sec, ioqueue_stats->_tmname_.ts_nsec); \
}

void
usage()
{
  int i;
  fprintf(stderr, "USAGE: %s [options] servername master_password\n", progname);
  fprintf(stderr, "Options:\n");
  fprintf(stderr, "  -c include drive cache stats\n");
  fprintf(stderr, "  -D exclude drive basic info\n");
  fprintf(stderr, "  -i include drive ioqueue stats\n");
  fprintf(stderr, "  -I include drive ioqueue name\n");
  fprintf(stderr, "  -l include drive layout stats\n");
  fprintf(stderr, "  -L include drive layout name\n");
  fprintf(stderr, "  -m include minmax\n");
  fprintf(stderr, "  -M use message queues\n");
  fprintf(stderr, "  -o include drive op stats\n");
  fprintf(stderr, "  -O include drive op stats summary\n");
  fprintf(stderr, "  -P exclude partition info\n");
  fprintf(stderr, "  -q include rpc queue depth stats\n");
  fprintf(stderr, "  -s sec_level\n");
  fprintf(stderr, "  where sec_level is one of\n");
  for(i = 0; i <= NASD_MAX_SECURITY_LEVEL; i++) {
    fprintf(stderr, "     %d %s\n", i, nasd_sec_level_string(i));
  }
  fprintf(stderr, "  -t include total op times\n");
#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE
  fprintf(stderr, "  -T use DCE-TCP\n");
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE */
  fprintf(stderr, "  -u include drive I/O module stats\n");
  fprintf(stderr, "  -z zero out stats after printing (zero only printed stats)\n");
  fflush(stderr);
  exit(1);
}

void
nasd_summarize_opstat(
  nasd_opstat_t  *os,
  char           *name)
{
  unsigned long s, ns, mins, minns, maxs, maxns;

  s = os->op_nsecs / 1000000000UL;
  ns = os->op_nsecs % 1000000000UL;
  if (include_minmax) {
    mins = os->min_nsecs / 1000000000UL;
    minns = os->min_nsecs % 1000000000UL;
    maxs = os->max_nsecs / 1000000000UL;
    maxns = os->max_nsecs % 1000000000UL;
    printf("%15s %7" NASD_64u_FMT " %4ld:%09ld %4ld:%09ld %4ld:%09ld\n",
      name, os->num_ops, s, ns, mins, minns, maxs, maxns);
  }
  else {
    printf("%15s %7" NASD_64u_FMT " %4ld:%09ld\n",
      name, os->num_ops, s, ns);
  }
}

void
nasd_dump_opstat(
  nasd_opstat_t  *os,
  char           *name)
{
  unsigned long s, ns, rs, rns;
  double d;

  s = os->op_nsecs / 1000000000UL;
  ns = os->op_nsecs % 1000000000UL;
  if (os->num_ops) {
    d = (double)ns;
    d /= 1000000000.0;
    d += (double)s;
    d /= (double)os->num_ops;
    rs = (unsigned long)d;
    d -= (double)rs;
    d *= 1000000000.0;
    rns = (double)d;
  }
  else {
    d = 0.0;
    rs = 0;
    rns = 0;
  }
  printf("Operation: %s\n", name);
  printf("  time       %5lu:%09lu\n", s, ns);
  printf("  num_ops    %5"NASD_64u_FMT"\n", os->num_ops);
  printf("  in_flight  %5"NASD_64u_FMT"\n", os->in_flight);
  printf("  invalid    %5"NASD_64u_FMT"\n", os->invalid);
  printf("  avg time   %5ld.%09ld\n", rs, rns);
  if (include_minmax) {
    rs = os->min_nsecs / 1000000000UL;
    rns = os->min_nsecs % 1000000000UL;
    printf("  min time   %5ld.%09ld\n", rs, rns);
    rs = os->max_nsecs / 1000000000UL;
    rns = os->max_nsecs % 1000000000UL;
    printf("  max time   %5ld.%09ld\n", rs, rns);
  }
}

int
main(
  int     argc,
  char  **argv)
{
  nasd_info_page_t stat_page, opstat_page, r_ca_stats;
  nasd_security_param_t sec_param;
  nasd_ctrl_io_size_stat_t *io_size_stats;
  nasd_ctrl_ioqueue_stat_t *ioqueue_stats;
  nasd_ctrl_layout_stat_t *layout_stats;
  nasd_ctrl_drive_opstats_t *dr_opstats;
  nasd_ctrl_cache_stat_t *ca_stats;
  nasd_ctrl_io_stat_t *io_stats;
  nasd_ctrl_drive_info_t drinfo;
  nasd_ctrl_part_info_t ptinfo;
  nasd_uint64 n, m, k;
  nasd_status_t rc;
  double d;
  char c;
  int i;
  int binding_type = NASD_BIND_DEFAULT;
  int sec_level = 0;

  progname = argv[0];

  while (nasd_getopt(argc, argv, "cDIiLlMmoOPqs:tTuz", &c)) {
    switch(c) {
      case 'c':
        include_cache_stats = 1;
        break;
      case 'D':
        exclude_drive_info = 1;
        break;
      case 'I':
        include_ioqueue_name = 1;
        break;
      case 'i':
        include_ioqueue_stats = 1;
        break;
      case 'L':
        include_layout_name = 1;
        break;
      case 'l':
        include_layout_stats = 1;
        break;
      case 'M':
        binding_type = NASD_BIND_MSGQ;
        break;
      case 'm':
        include_minmax = 1;
        break;
      case 'o':
        include_op_stats = 1;
        break;
      case 'O':
        summarize_op_stats = 1;
        break;
      case 'P':
        exclude_part_info = 1;
        break;
      case 'q':
        include_rpc_qdepth_stats = 1;
        break;
      case 's':
        sec_level = atoi(nasd_optarg);
        break;
      case 't':
        include_total_op_time = 1;
        break;
#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE
      case 'T':
        binding_type = NASD_BIND_DCE_DIRECT_TCP;
        break;
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE */
      case 'u':
        include_io_stats = 1;
        break;
      case 'z':
        zero_drive_stats = 1;
        break;
      default:
        fprintf(stderr, "Unknown option '%c'\n", nasd_optopt);
        usage();
    }
  }

  if (nasd_optind >= argc)
    usage();

  server_name = argv[nasd_optind];
  nasd_optind++;

  if (nasd_optind >= argc)
    usage();

  nasd_sec_password_to_keys(argv[nasd_optind], 0, &keys);
  nasd_optind++;

  if (nasd_optind < argc)
    usage();

  rc = nasd_sec_seclevel_to_protection(sec_level, &protection);
  if(rc) {
    fprintf(stderr, "ERROR: invalid security level %d\n", sec_level);
    usage();
  }

  total_optime = (double)0.0;
  total_rpc_queuetime = (double)0.0;

  rc = nasd_cl_p_init();
  if (rc) {
    printf("ERROR (%s:%d): cannot init client library, rc=0x%x (%s)\n",
      __FILE__, __LINE__, rc, nasd_error_string(rc));
    return(rc);
  }
  rc = nasd_bind_to_drive(server_name, NASD_PDRIVE_PORT,
    binding_type, NULL, 0, &h);
  if (rc) {
    fprintf(stderr, "ERROR: cannot bind to server %s\n", server_name);
    fflush(stderr);
    exit(1);
  }

  if (exclude_drive_info == 0) {
    printf("Drive %s\n", server_name);
  }

  sec_param.type = NASD_BLACK_KEY;
  sec_param.partnum = 1;
  sec_param.actual_protection = protection;

  rc = nasd_cl_p_ctrl_get_drive_info(h, keys.black_key, &sec_param, NULL,
                                     &drinfo);
  if (rc) {
    fprintf(stderr, "ERROR: got 0x%x (%s) from nasd_cl_p_get_drive_info\n",
      rc, nasd_error_string(rc));
    if (rc == NASD_CTRL_ID_CHECK_FAILED) {
      fprintf(stderr, "Got id 0x%" NASD_ID_FMT "\n",
        drinfo.ctrl_id);
    }
    fflush(stderr);
    exit(1);
  }
  NASD_ASSERT(drinfo.ctrl_id == NASD_CTRL_DRIVE_INFO);

  if (exclude_drive_info == 0) {
    printf("Drive supports a maximum of %" NASD_64u_FMT " partitions, %" NASD_64u_FMT " currently configured.\n",
      drinfo.max_parts, drinfo.num_parts);
    n = drinfo.num_blocks * drinfo.blocksize;
    m = n / (1024*1024);
    k = (((n-m)*10)/(1024*1024))%10;
    printf("Drive using blocks of %" NASD_64u_FMT " bytes, %" NASD_64u_FMT
      " blocks on drive (%" NASD_64u_FMT " bytes, %" NASD_64u_FMT
      ".%" NASD_64u_FMT " MB)\n",
      drinfo.blocksize, drinfo.num_blocks, n, m, k);
    n = drinfo.num_blocks - drinfo.blocks_allocated;
    printf("Drive has %" NASD_64u_FMT " blocks allocated, %" NASD_64u_FMT " unallocated\n",
      drinfo.blocks_allocated, n);
  }
  

  dr_opstats = (nasd_ctrl_drive_opstats_t *)opstat_page;

  rc = nasd_cl_p_ctrl_get_drive_op_stats(h, keys.black_key,
    &sec_param, NULL, dr_opstats);
  if (rc) {
    fprintf(stderr, "ERROR: got 0x%x (%s) from nasd_cl_p_get_drive_op_stats\n",
      rc, nasd_error_string(rc));
    if (rc == NASD_CTRL_ID_CHECK_FAILED) {
      fprintf(stderr, "Got id 0x%" NASD_ID_FMT "\n",
        dr_opstats->ctrl_id);
    }
    fflush(stderr);
    exit(1);
  }

  if (include_op_stats) {
    DUMP_OPSTAT(null);
    DUMP_OPSTAT(sync);
    DUMP_OPSTAT(part_creat);
    DUMP_OPSTAT(create);
    DUMP_OPSTAT(getattr);
    DUMP_OPSTAT(read_simple);
    DUMP_OPSTAT(read2_simple);
    DUMP_OPSTAT(tread_simple);
    DUMP_OPSTAT(write_simple);
    DUMP_OPSTAT(setattr);
    DUMP_OPSTAT(flush_obj);
    DUMP_OPSTAT(remove);
    DUMP_OPSTAT(initialize);
    DUMP_OPSTAT(change_key);
    DUMP_OPSTAT(rshutdown);
    DUMP_OPSTAT(getinfo);
  }

  if (include_rpc_qdepth_stats) {
    for(i=0;i<NASD_CTRL_DRIVE_OPDEPTHS-1;i++) {
      printf("  rpc_queue_depth[%03d]        %4d:%09d\n", i+1,
        dr_opstats->opdepths[i].ts_sec, 
        dr_opstats->opdepths[i].ts_nsec);
    }
    printf("  rpc_queue_depth[%02d+]        %4d:%09d\n", i+1,
      dr_opstats->opdepths[i].ts_sec, 
      dr_opstats->opdepths[i].ts_nsec);
  }

  if (summarize_op_stats) {
    SUMMARIZE_OPSTAT(null);
    SUMMARIZE_OPSTAT(sync);
    SUMMARIZE_OPSTAT(part_creat);
    SUMMARIZE_OPSTAT(create);
    SUMMARIZE_OPSTAT(getattr);
    SUMMARIZE_OPSTAT(read_simple);
    SUMMARIZE_OPSTAT(read2_simple);
    SUMMARIZE_OPSTAT(tread_simple);
    SUMMARIZE_OPSTAT(write_simple);
    SUMMARIZE_OPSTAT(setattr);
    SUMMARIZE_OPSTAT(flush_obj);
    SUMMARIZE_OPSTAT(remove);
    SUMMARIZE_OPSTAT(initialize);
    SUMMARIZE_OPSTAT(change_key);
    SUMMARIZE_OPSTAT(rshutdown);
    SUMMARIZE_OPSTAT(getinfo);
  }

  if (include_cache_stats) {
    ca_stats = (nasd_ctrl_cache_stat_t *)r_ca_stats;

    rc = nasd_cl_p_ctrl_get_drive_cache_stats(h, keys.black_key,
      &sec_param, NULL, ca_stats);
    if (rc) {
      fprintf(stderr, "ERROR: got 0x%x (%s) from nasd_cl_p_get_drive_cache_stats\n",
        rc, nasd_error_string(rc));
      if (rc == NASD_CTRL_ID_CHECK_FAILED) {
        fprintf(stderr, "Got id 0x%" NASD_ID_FMT "\n",
          ca_stats->ctrl_id);
      }
      fflush(stderr);
      exit(1);
    }
  }

  if (include_cache_stats) {
    DUMP_CACHESTAT(drive_cpu_speed);
    DUMP_TIME_CACHESTAT(timestamp);
    DUMP_TIME_CACHESTAT(idle);
    DUMP_CACHESTAT(region_blocks);
    DUMP_CACHESTAT(mem_allocated);
    DUMP_TYPED_CACHESTAT(lookup);
    DUMP_TYPED_CACHESTAT(lookup_logical);
    DUMP_TYPED_CACHESTAT(force);
    DUMP_TYPED_CACHESTAT(miss);
    DUMP_TYPED_CACHESTAT(get1);
    DUMP_TYPED_CACHESTAT(get2);
    DUMP_TYPED_CACHESTAT(get_logical);
    DUMP_TYPED_CACHESTAT(falloc);
    DUMP_CACHESTAT(alloc_succeed);
    DUMP_CACHESTAT(alloc_fail);
    DUMP_CACHESTAT(grab_fl);
    DUMP_TYPED_CACHESTAT(grab_consider);
    DUMP_CACHESTAT(grab_stillref);
    DUMP_CACHESTAT(grab_dirty_wait);
    DUMP_CACHESTAT(grab_lru_maxtrips);
    DUMP_CACHESTAT(dirty_threshold_kicks);
    DUMP_TYPED_CACHESTAT(logical_found);
    DUMP_CACHESTAT(logical_no_digest);

    DUMP_CACHESTAT(anon_eject);
    DUMP_CACHESTAT(anon_replace);
    DUMP_CACHESTAT(anon_before);
    DUMP_CACHESTAT(anon_before_fail);
    DUMP_CACHESTAT(anon_before_noref);
    DUMP_CACHESTAT(anon_before_incore);
    DUMP_CACHESTAT(anon_after);
    DUMP_CACHESTAT(anon_after_fail);
    DUMP_CACHESTAT(anon_after_noref);
    DUMP_CACHESTAT(anon_after_incore);
    DUMP_CACHESTAT(anon_issue_noorder);

    DUMP_CACHESTAT(anon_notused);
    DUMP_TYPED_CACHESTAT(anon_promoted);

    DUMP_TIME_CACHESTAT(read_pipe_stall_time);
    DUMP_TIME_CACHESTAT(read_ilookup_time);
    DUMP_TIME_CACHESTAT(read_bmap_time);
    DUMP_TIME_CACHESTAT(read_stall_time);
    DUMP_TIME_CACHESTAT(read_lget_time);
    DUMP_TIME_CACHESTAT(read_pget_time);
    DUMP_TIME_CACHESTAT(read_setup_time);
    DUMP_TIME_CACHESTAT(read_issue_time);
    DUMP_TIME_CACHESTAT(read_core_time);
    DUMP_CACHESTAT(read_bytes);

    DUMP_TIME_CACHESTAT(read2_pipe_stall_time);
    DUMP_TIME_CACHESTAT(read2_ilookup_time);
    DUMP_TIME_CACHESTAT(read2_bmap_time);
    DUMP_TIME_CACHESTAT(read2_stall_time);
    DUMP_TIME_CACHESTAT(read2_lget_time);
    DUMP_TIME_CACHESTAT(read2_pget_time);
    DUMP_TIME_CACHESTAT(read2_setup_time);
    DUMP_TIME_CACHESTAT(read2_issue_time);
    DUMP_TIME_CACHESTAT(read2_core_time);
    DUMP_CACHESTAT(read2_bytes);

    DUMP_TIME_CACHESTAT(write_pipe_stall_time);
    DUMP_TIME_CACHESTAT(write_ilookup_time);
    DUMP_TIME_CACHESTAT(write_bmap_time);
    DUMP_TIME_CACHESTAT(write_stall_time);
    DUMP_CACHESTAT(write_bytes);

    DUMP_CACHESTAT(rescue_eject);
    DUMP_CACHESTAT(create_busy);

    DUMP_TIME_CACHESTAT(node_xlate_time);
    DUMP_TIME_CACHESTAT(node_wait_check_time);
    DUMP_TIME_CACHESTAT(node_compute_time);
    DUMP_TIME_CACHESTAT(node_hash_get_time);
    DUMP_TIME_CACHESTAT(node_block_get_time);
  }

  if (include_io_stats) {
    io_stats = (nasd_ctrl_io_stat_t *)stat_page;

    rc = nasd_cl_p_ctrl_get_drive_io_stats(h, keys.black_key,
      &sec_param, NULL, io_stats);
    if (rc) {
      fprintf(stderr, "ERROR: got 0x%x (%s) from nasd_cl_p_get_drive_io_stats\n",
        rc, nasd_error_string(rc));
      if (rc == NASD_CTRL_ID_CHECK_FAILED) {
        fprintf(stderr, "Got id 0x%" NASD_ID_FMT "\n",
          io_stats->ctrl_id);
      }
      fflush(stderr);
      exit(1);
    }

    DUMP_UIOSTAT(num_io_reads);
    DUMP_UIOSTAT(num_io_writes);
    DUMP_UIOSTAT(auto_flush);
    DUMP_UIOSTAT(header_read);
    DUMP_UIOSTAT(header_write);
    DUMP_UIOSTAT(pull_ios);
    DUMP_UIOSTAT(try_raise_pri);
    DUMP_UIOSTAT(raise_pri_busy);
    DUMP_UIOSTAT(raise_lower_pri);
    DUMP_UIOSTAT(raise_pri);
    DUMP_UIOSTAT(retries);
    DUMP_UIOSTAT(header_force_sync);
    DUMP_UIOSTAT(header1_write);
    DUMP_UIOSTAT(header2_write);
    DUMP_TIME_UIOSTAT(io_subsys_time);
    DUMP_TIME_UIOSTAT(io_outstanding_time);
    DUMP_UIOSTAT(coalesce_stop_dir);
    DUMP_UIOSTAT(coalesce_stop_max_read);
    DUMP_UIOSTAT(coalesce_stop_contig_read);
    DUMP_UIOSTAT(coalesce_stop_align_read);
    DUMP_UIOSTAT(coalesce_stop_max_write);
    DUMP_UIOSTAT(coalesce_stop_contig_write);
    DUMP_UIOSTAT(coalesce_stop_align_write);
    DUMP_UIOSTAT(io_blocks_enqueued);
    DUMP_UIOSTAT(io_blocks_launched);
    DUMP_UIOSTAT(io_blocks_finished);
    DUMP_UIOSTAT(io_completion_count);

    io_size_stats = (nasd_ctrl_io_size_stat_t *)stat_page;

    rc = nasd_cl_p_ctrl_get_drive_io_read_size_stats(h, keys.black_key,
      &sec_param, NULL, io_size_stats);
    if (rc) {
      fprintf(stderr, "ERROR: got 0x%x (%s) from nasd_cl_p_get_drive_io_read_size_stats\n",
        rc, nasd_error_string(rc));
      if (rc == NASD_CTRL_ID_CHECK_FAILED) {
        fprintf(stderr, "Got id 0x%" NASD_ID_FMT "\n",
          io_size_stats->ctrl_id);
      }
      fflush(stderr);
      exit(1);
    }

    for(i=0;i<NASD_CTRL_IO_SIZEBUCKETS;i++) {
      if (io_size_stats->io_sizes[i]) {
        int j;
        j = printf("  io_read_sizes[%04d]", i);
        for(;j<31;j++)
          printf(" ");
        printf("%10"NASD_64u_FMT"\n", io_size_stats->io_sizes[i]);
      }
    }

    rc = nasd_cl_p_ctrl_get_drive_io_write_size_stats(h, keys.black_key,
      &sec_param, NULL, io_size_stats);
    if (rc) {
      fprintf(stderr, "ERROR: got 0x%x (%s) from nasd_cl_p_get_drive_io_write_size_stats\n",
        rc, nasd_error_string(rc));
      if (rc == NASD_CTRL_ID_CHECK_FAILED) {
        fprintf(stderr, "Got id 0x%" NASD_ID_FMT "\n",
          io_size_stats->ctrl_id);
      }
      fflush(stderr);
      exit(1);
    }

    for(i=0;i<NASD_CTRL_IO_SIZEBUCKETS;i++) {
      if (io_size_stats->io_sizes[i]) {
        int j;
        j = printf("  io_write_sizes[%04d]", i);
        for(;j<31;j++)
          printf(" ");
        printf("%10"NASD_64u_FMT"\n", io_size_stats->io_sizes[i]);
      }
    }
  }

  if (include_layout_stats || include_layout_name) {
    layout_stats = (nasd_ctrl_layout_stat_t *)stat_page;

    rc = nasd_cl_p_ctrl_get_drive_layout_stats(h, keys.black_key,
      &sec_param, NULL, layout_stats);
    if (rc) {
      fprintf(stderr,
        "ERROR: got 0x%x (%s) from nasd_cl_p_get_drive_layout_stats\n",
        rc, nasd_error_string(rc));
      if (rc == NASD_CTRL_ID_CHECK_FAILED) {
        fprintf(stderr, "Got id 0x%" NASD_ID_FMT "\n",
          layout_stats->ctrl_id);
      }
      fflush(stderr);
      exit(1);
    }

    if (include_layout_name) {
      char str[NASD_CTRL_LAYOUT_NAME_LEN+1];
      bcopy((char *)layout_stats->layout_name, str,
        NASD_CTRL_LAYOUT_NAME_LEN);
      str[NASD_CTRL_LAYOUT_NAME_LEN] = '\0';
      printf("Layout type: %s\n", str);
    }

    if (include_layout_stats) {
      DUMP_LAYOUT_TIME(get_node_block_time);
      DUMP_LAYOUT_TIME(node_fail_create_time);
      DUMP_LAYOUT_TIME(release_oneblock_time);
      DUMP_LAYOUT_TIME(release_blocks_time);
      DUMP_LAYOUT_TIME(init_adj_prealloc_time);
      DUMP_LAYOUT_TIME(cancel_adj_prealloc_time);
      DUMP_LAYOUT_TIME(adj_prealloc_time);
      DUMP_LAYOUT_TIME(surrender_prealloc_time);
      DUMP_LAYOUT_TIME(node_deleting_time);
      DUMP_LAYOUT_TIME(alloc_blocks_time);
      DUMP_LAYOUT_TIME(get_prealloc_time);
    }
  }

  if (include_ioqueue_stats || include_ioqueue_name) {
    ioqueue_stats = (nasd_ctrl_ioqueue_stat_t *)stat_page;

    rc = nasd_cl_p_ctrl_get_drive_ioqueue_stats(h, keys.black_key,
      &sec_param, NULL, ioqueue_stats);
    if (rc) {
      fprintf(stderr,
        "ERROR: got 0x%x (%s) from nasd_cl_p_get_drive_ioqueue_stats\n",
        rc, nasd_error_string(rc));
      if (rc == NASD_CTRL_ID_CHECK_FAILED) {
        fprintf(stderr, "Got id 0x%" NASD_ID_FMT "\n",
          ioqueue_stats->ctrl_id);
      }
      fflush(stderr);
      exit(1);
    }

    if (include_ioqueue_name) {
      char str[NASD_CTRL_IOQUEUE_NAME_LEN+1];
      bcopy((char *)ioqueue_stats->ioqueue_name, str,
        NASD_CTRL_IOQUEUE_NAME_LEN);
      str[NASD_CTRL_IOQUEUE_NAME_LEN] = '\0';
      printf("I/O queue type: %s\n", str);
    }

    if (include_ioqueue_stats) {
      DUMP_IOQUEUE_STAT(max_ios_outstanding);
      DUMP_IOQUEUE_TIME(io_sort_time);
      DUMP_IOQUEUE_TIME(io_coalesce_time);
    }
  }

  if (include_total_op_time) {
    ADD_OPSTAT(null);
    ADD_OPSTAT(sync);
    ADD_OPSTAT(part_creat);
    ADD_OPSTAT(create);
    ADD_OPSTAT(getattr);
    ADD_OPSTAT(read_simple);
    ADD_OPSTAT(read2_simple);
    ADD_OPSTAT(tread_simple);
    ADD_OPSTAT(write_simple);
    ADD_OPSTAT(setattr);
    ADD_OPSTAT(flush_obj);
    ADD_OPSTAT(remove);
    ADD_OPSTAT(initialize);
    ADD_OPSTAT(change_key);
    ADD_OPSTAT(rshutdown);
    ADD_OPSTAT(getinfo);
    for(i=0;i<NASD_CTRL_DRIVE_OPDEPTHS;i++) {
      d = (double)dr_opstats->opdepths[i].ts_nsec;
      d /= (double)NASD_NSEC_PER_SEC;
      d += (double)dr_opstats->opdepths[i].ts_sec;
      total_rpc_queuetime += d;
    }
    printf("  total_op_time         %f\n", total_optime);
    printf("  total_rpc_queuetime   %f\n", total_rpc_queuetime);
  }

  if (exclude_part_info == 0) {
    for(i=0;i<drinfo.max_parts;i++) {
      sec_param.partnum = i;
      rc = nasd_cl_p_ctrl_get_part_info(h, keys.black_key, &sec_param, NULL,
                                        i, &ptinfo);

      if (rc && (rc != NASD_CTRL_ID_CHECK_FAILED)
             && (rc != NASD_BAD_PARTITION))
      {
        fprintf(stderr,
          "ERROR: got 0x%x (%s) from nasd_cl_p_get_part_info, partition %d\n",
          rc, nasd_error_string(rc), i);
        fflush(stderr);
        exit(1);
      }
      if ((rc == NASD_CTRL_ID_CHECK_FAILED)
        || (rc == NASD_BAD_PARTITION))
      {
        continue;
      }
      else {
        NASD_ASSERT(ptinfo.ctrl_id == NASD_CTRL_PART_INFO);
        printf("Partition %d on %s\n", i, server_name);
        printf(" Base object 0x%" NASD_ID_FMT "\n", ptinfo.first_obj);
        printf(" %" NASD_64u_FMT " of a maximum of %" NASD_64u_FMT " objects exist\n",
          ptinfo.num_obj, ptinfo.max_objs);
        n = ptinfo.part_size - ptinfo.blocks_allocated;
        printf(" Partition blocksize %" NASD_64u_FMT "\n", ptinfo.blocksize);
        printf(" Partition contains %" NASD_64u_FMT " blocks, of which %" NASD_64u_FMT " are used, %" NASD_64u_FMT " are allocated, and %" NASD_64u_FMT " are free\n",
          ptinfo.part_size, ptinfo.blocks_used, ptinfo.blocks_allocated, n);
      }
    }
  }

  if (zero_drive_stats) {
    sec_param.type = NASD_DRIVE_KEY;
    sec_param.partnum = 1;
    bzero((char *)stat_page, NASD_INFO_PAGESIZE);
    dr_opstats = (nasd_ctrl_drive_opstats_t *)stat_page;
    ca_stats = (nasd_ctrl_cache_stat_t *)stat_page;
    io_stats = (nasd_ctrl_io_stat_t *)stat_page;
    layout_stats = (nasd_ctrl_layout_stat_t *)stat_page;
    ioqueue_stats = (nasd_ctrl_ioqueue_stat_t *)stat_page;

    if (include_op_stats) {
      rc = nasd_cl_p_ctrl_put_drive_op_stats(h, keys.black_key,
        &sec_param, NULL, dr_opstats);
      if (rc) {
        fprintf(stderr,
          "ERROR: got 0x%x (%s) from nasd_cl_p_ctrl_put_drive_op_stats\n",
          rc, nasd_error_string(rc));
        fflush(stderr);
        exit(1);
      }
    }
    if (include_cache_stats) {
      rc = nasd_cl_p_ctrl_put_drive_cache_stats(h, keys.black_key,
        &sec_param, NULL, ca_stats);
      if (rc) {
        fprintf(stderr,
          "ERROR: got 0x%x (%s) from nasd_cl_p_ctrl_put_cache_stats\n",
          rc, nasd_error_string(rc));
        fflush(stderr);
        exit(1);
      }
    }
    if (include_io_stats) {
      rc = nasd_cl_p_ctrl_put_drive_io_stats(h, keys.black_key,
        &sec_param, NULL, io_stats);
      if (rc) {
        fprintf(stderr,
          "ERROR: got 0x%x (%s) from nasd_cl_p_ctrl_put_io_stats\n",
          rc, nasd_error_string(rc));
        fflush(stderr);
        exit(1);
      }
    }
    if (include_layout_stats) {
      rc = nasd_cl_p_ctrl_put_drive_layout_stats(h, keys.black_key,
        &sec_param, NULL, layout_stats);
      if (rc) {
        fprintf(stderr,
          "ERROR: got 0x%x (%s) from nasd_cl_p_ctrl_put_layout_stats\n",
          rc, nasd_error_string(rc));
        fflush(stderr);
        exit(1);
      }
    }
    if (include_ioqueue_stats) {
      rc = nasd_cl_p_ctrl_put_drive_ioqueue_stats(h, keys.black_key,
        &sec_param, NULL, ioqueue_stats);
      if (rc) {
        fprintf(stderr,
          "ERROR: got 0x%x (%s) from nasd_cl_p_ctrl_put_ioqueue_stats\n",
          rc, nasd_error_string(rc));
        fflush(stderr);
        exit(1);
      }
    }
  }

  rc = nasd_unbind_drive(&h);
  if (rc) {
    fprintf(stderr, "ERROR: got 0x%x (%s) from nasd_unbind_drive()\n",
      rc, nasd_error_string(rc));
    fflush(stderr);
    exit(2);
  }

  nasd_cl_p_shutdown();

  exit(0);
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
