/*
 * nasd_edrfs_getstats.c
 *
 * Retrieve and print EDRFS server stats
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 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 <nasd/nasd_types.h>
#include <nasd/nasd_general.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_common.h>
#include <nasd/nasd_edrfs_client.h>
#include <stdio.h>

char *progname;

nasd_edrfs_getstats_res_t  getstats_res;

int print_cachestats = 0;
int print_opstats = 1;
int print_rpc_queuedepths = 0;

#define DUMP_OPSTAT(_opname_) \
  nasd_edrfs_dump_opstat(&getstats_res.out_opstats._opname_,NASD_STRING(_opname_))

#define NASD_EDRFS_CSDUMP(_stat_) { \
  printf("  %24s %5" NASD_64u_FMT "\n", NASD_STRING(_stat_), getstats_res.out_cachestats._stat_); \
}

#define NASD_EDRFS_CSDUMP_TS(_stat_) { \
  printf("  %24s %5d:%09d\n", NASD_STRING(_stat_), \
    getstats_res.out_cachestats._stat_.ts_sec, \
    getstats_res.out_cachestats._stat_.ts_nsec); \
}

void
usage()
{
  fprintf(stderr, "USAGE: %s [options] servername\n", progname);
  fprintf(stderr, "  -C exclude cache stats\n");
  fprintf(stderr, "  -c include cache stats\n");
  fprintf(stderr, "  -O exclude opstats\n");
  fprintf(stderr, "  -o include opstats\n");
  fprintf(stderr, "  -Q exclude rpc queue depths\n");
  fprintf(stderr, "  -q include rpc queue depths\n");
  fflush(stderr);
  exit(1);
}

void
nasd_edrfs_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 = (unsigned long)d;
  }
  else {
    d = 0.0;
    rs = 0;
    rns = 0;
  }
  printf("Operation: %s\n", name);
  printf("  time       %lu:%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   %5lu.%09lu\n", rs, rns);
}

int
main(
  int     argc,
  char  **argv)
{
  nasd_edrfscli_error_string_t edrfserr_str;
  nasd_rpc_status_t status;
  nasd_edrfs_handle_t h;
  char *server_name;
  nasd_status_t rc;
  char c;
  int i;

  progname = argv[0];

  while (nasd_getopt(argc, argv, "cCoOqQ", &c)) {
    switch(c) {
      case 'c':
        print_cachestats = 1;
        break;
      case 'C':
        print_cachestats = 0;
        break;
      case 'o':
        print_opstats = 1;
        break;
      case 'O':
        print_opstats = 0;
        break;
      case 'q':
        print_rpc_queuedepths = 1;
        break;
      case 'Q':
        print_rpc_queuedepths = 0;
        break;
      default:
        fprintf(stderr, "Unknown option '%c'\n", c);
        usage();
    }
  }

  if (nasd_optind >= argc)
    usage();
  server_name = argv[nasd_optind];
  nasd_optind++;
  if (nasd_optind < argc) {
    fprintf(stderr, "Extra arguments detected on command line\n");
    usage();
  }

  rc = nasd_edrfscli_init();
  if (rc) {
    fprintf(stderr, "ERROR: failed nasd_edrfscli_init() rc=0x%x (%s)\n",
      rc, nasd_error_string(rc));
    exit(1);
  }

  rc = nasd_bind_to_edrfs_server(server_name, NASD_EDRFS_SERVER_PORT,
    NASD_BIND_DEFAULT, NULL, 0, &h);
  if (rc) {
    fprintf(stderr, "ERROR: unable to bind to server %s, rc=0x%x (%s)\n",
      server_name, rc, nasd_error_string(rc));
    exit(1);
  }

  nasd_edrfscli_getstats(h, &getstats_res, &status);
  rc = getstats_res.nasd_status;

  if (rc || status) {
    fprintf(stderr, "ERROR: nasd_edrfscli_getstats returned "
      "nasd_status 0x%x (%s) status 0x%x (%s)\n",
      rc, nasd_error_string(rc),
      status, nasd_edrfscli_error_string(h, status, edrfserr_str));
    exit(1);
  }

  if (print_opstats) {
    DUMP_OPSTAT(null);
    DUMP_OPSTAT(mount);
    DUMP_OPSTAT(fsstat);
    DUMP_OPSTAT(fsinfo);
    DUMP_OPSTAT(lookup);
    DUMP_OPSTAT(readdir);
    DUMP_OPSTAT(access);
    DUMP_OPSTAT(setattr);
    DUMP_OPSTAT(create);
    DUMP_OPSTAT(remove);
    DUMP_OPSTAT(mkdir);
    DUMP_OPSTAT(rmdir);
    DUMP_OPSTAT(newcookie);
    DUMP_OPSTAT(rename);
    DUMP_OPSTAT(symlink);
    DUMP_OPSTAT(getstats);
  }

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

  if (print_cachestats) {
    NASD_EDRFS_CSDUMP(dir_lookup);
    NASD_EDRFS_CSDUMP(dir_force);
    NASD_EDRFS_CSDUMP(dirc_load_attr);
    NASD_EDRFS_CSDUMP(dirc_not_dir);
    NASD_EDRFS_CSDUMP(dirc_load_dir);
    NASD_EDRFS_CSDUMP(dirc_dirty);
    NASD_EDRFS_CSDUMP(dirc_new_dirty);
    NASD_EDRFS_CSDUMP(dirc_mkfree_loop);
    NASD_EDRFS_CSDUMP(dirc_mkfree_wait);
    NASD_EDRFS_CSDUMP(dir_load);
    NASD_EDRFS_CSDUMP(post_attribute);
    NASD_EDRFS_CSDUMP(post_attribute_valid);
    NASD_EDRFS_CSDUMP(post_attribute_invalid);
    NASD_EDRFS_CSDUMP(lookup_post_attribute);
    NASD_EDRFS_CSDUMP(dir_writer_fls);
    NASD_EDRFS_CSDUMP(dir_writer_pages);
    NASD_EDRFS_CSDUMP(dir_load_attr);
    NASD_EDRFS_CSDUMP(do_getattr);
    NASD_EDRFS_CSDUMP(do_setattr);
    NASD_EDRFS_CSDUMP(access_lookup_fail);
    NASD_EDRFS_CSDUMP(access_not_dir);
    NASD_EDRFS_CSDUMP(access_fault_attr);
    NASD_EDRFS_CSDUMP(access_get_attr);
    NASD_EDRFS_CSDUMP(lookup_get_attr);
    NASD_EDRFS_CSDUMP(setattr_get_attr);
    NASD_EDRFS_CSDUMP_TS(drive_time);
  }

  rc = nasd_unbind_edrfs_server(&h);
  if (rc) {
    fprintf(stderr, "ERROR: got 0x%x (%s) unbinding EDRFS server\n",
      rc, nasd_error_string(rc));
    exit(1);
  }

  nasd_edrfscli_shutdown();

  exit(0);
}
