]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rados: make 'rados bench' support json format output 5736/head
authortianshan <qutianshan@gmail.com>
Mon, 31 Aug 2015 10:37:42 +0000 (18:37 +0800)
committertianshan <qutianshan@gmail.com>
Tue, 1 Sep 2015 13:33:00 +0000 (21:33 +0800)
Fixes: #12864
rados bench add '[--format json]' and '[-o | --output outfile]' support.
output option only take effect in json format.
now we can use the bench result draw performance graph easily.

Signed-off-by: Tianshan Qu <qutianshan@gmail.com>
src/common/obj_bencher.cc
src/common/obj_bencher.h
src/test/test_rados_tool.sh
src/tools/rados/rados.cc

index baaff266c9aeb75b6b2f17b2d8ce2a69c38ef782..3772eb865781fa97fe285266c3371fe5e3d5d452 100644 (file)
@@ -73,6 +73,8 @@ ostream& ObjBencher::out(ostream& os)
 void *ObjBencher::status_printer(void *_bencher) {
   ObjBencher *bencher = static_cast<ObjBencher *>(_bencher);
   bench_data& data = bencher->data;
+  Formatter *formatter = bencher->formatter;
+  ostream *outstream = bencher->outstream;
   Cond cond;
   int i = 0;
   int previous_writes = 0;
@@ -82,10 +84,12 @@ void *ObjBencher::status_printer(void *_bencher) {
   utime_t ONE_SECOND;
   ONE_SECOND.set_from_double(1.0);
   bencher->lock.Lock();
+  if (formatter)
+    formatter->open_array_section("datas");
   while(!data.done) {
     utime_t cur_time = ceph_clock_now(bencher->cct);
 
-    if (i % 20 == 0) {
+    if (i % 20 == 0 && !formatter) {
       if (i > 0)
         cur_time.localtime(cout) << " min lat: " << data.min_latency
           << " max lat: " << data.max_latency
@@ -132,13 +136,17 @@ void *ObjBencher::status_printer(void *_bencher) {
 
       data.history.iops.push_back(iops);
     }
+    
+    if (formatter)
+      formatter->open_object_section("data");
 
     double avg_bandwidth = (double) (data.object_size) * (data.finished)
       / (double)(cur_time - data.start_time) / (1024*1024);
     if (previous_writes != data.finished) {
       previous_writes = data.finished;
       cycleSinceChange = 0;
-      bencher->out(cout, cur_time) << setfill(' ')
+      if (!formatter) {
+        bencher->out(cout, cur_time) << setfill(' ')
           << setw(5) << i
           << setw(8) << data.in_flight
           << setw(10) << data.started
@@ -147,9 +155,20 @@ void *ObjBencher::status_printer(void *_bencher) {
           << setw(10) << bandwidth
           << setw(10) << (double)data.cur_latency
           << setw(10) << data.avg_latency << std::endl;
+      } else {
+        formatter->dump_format("sec", "%d", i);
+        formatter->dump_format("cur_ops", "%d", data.in_flight);
+        formatter->dump_format("started", "%d", data.started);
+        formatter->dump_format("finished", "%d", data.finished);
+        formatter->dump_format("avg_bw", "%f", avg_bandwidth);
+        formatter->dump_format("cur_bw", "%f", bandwidth);
+        formatter->dump_format("last_lat", "%f", (double)data.cur_latency);
+        formatter->dump_format("avg_lat", "%f", data.avg_latency);
+      }
     }
     else {
-      bencher->out(cout, cur_time) << setfill(' ')
+      if (!formatter) {
+        bencher->out(cout, cur_time) << setfill(' ')
           << setw(5) << i
           << setw(8) << data.in_flight
           << setw(10) << data.started
@@ -158,11 +177,27 @@ void *ObjBencher::status_printer(void *_bencher) {
           << setw(10) << '0'
           << setw(10) << '-'
           << setw(10) << data.avg_latency << std::endl;
+      } else {
+        formatter->dump_format("sec", "%d", i);
+        formatter->dump_format("cur_ops", "%d", data.in_flight);
+        formatter->dump_format("started", "%d", data.started);
+        formatter->dump_format("finished", "%d", data.finished);
+        formatter->dump_format("avg_bw", "%f", avg_bandwidth);
+        formatter->dump_format("cur_bw", "%f", 0);
+        formatter->dump_format("last_lat", "%f", 0);
+        formatter->dump_format("avg_lat", "%f", data.avg_latency);
+      }
+    }
+    if (formatter) {
+      formatter->close_section(); // data
+      formatter->flush(*outstream);
     }
     ++i;
     ++cycleSinceChange;
     cond.WaitInterval(bencher->cct, bencher->lock, ONE_SECOND);
   }
+  if (formatter)
+    formatter->close_section(); //datas
   bencher->lock.Unlock();
   return NULL;
 }
@@ -207,6 +242,9 @@ int ObjBencher::aio_bench(
   //fill in contentsChars deterministically so we can check returns
   sanitize_object_contents(&data, data.object_size);
 
+  if (formatter)
+    formatter->open_object_section("bench");
+
   if (OP_WRITE == operation) {
     r = write_bench(secondsToRun, concurrentios, run_name_meta);
     if (r != 0) goto out;
@@ -237,6 +275,11 @@ int ObjBencher::aio_bench(
   }
 
  out:
+  if (formatter) {
+    formatter->close_section(); // bench
+    formatter->flush(*outstream);
+    *outstream << std::endl;
+  }
   delete[] contentsChars;
   return r;
 }
@@ -303,15 +346,24 @@ int ObjBencher::write_bench(int secondsToRun,
                            int concurrentios, const string& run_name_meta) {
   if (concurrentios <= 0) 
     return -EINVAL;
-
-  out(cout) << "Maintaining " << concurrentios << " concurrent writes of "
-           << data.object_size << " bytes for up to "
-           << secondsToRun << " seconds"
-           << std::endl;
+  
+  if (!formatter) {
+    out(cout) << "Maintaining " << concurrentios << " concurrent writes of "
+           << data.object_size << " bytes for up to "
+           << secondsToRun << " seconds"
+           << std::endl;
+  } else {
+    formatter->dump_format("concurrent_ios", "%d", concurrentios);
+    formatter->dump_format("object_size", "%d", data.object_size);
+    formatter->dump_format("seconds_to_run", "%d", secondsToRun);
+  }
   bufferlist* newContents = 0;
 
   std::string prefix = generate_object_prefix();
-  out(cout) << "Object prefix: " << prefix << std::endl;
+  if (!formatter)
+    out(cout) << "Object prefix: " << prefix << std::endl;
+  else
+    formatter->dump_string("object_prefix", prefix);
 
   std::vector<string> name(concurrentios);
   std::string newName;
@@ -461,7 +513,8 @@ int ObjBencher::write_bench(int secondsToRun,
   bandwidth = ((double)data.finished)*((double)data.object_size)/(double)timePassed;
   bandwidth = bandwidth/(1024*1024); // we want it in MB/sec
 
-  out(cout) << "Total time run:         " << timePassed << std::endl
+  if (!formatter) {
+    out(cout) << "Total time run:         " << timePassed << std::endl
        << "Total writes made:      " << data.finished << std::endl
        << "Write size:             " << data.object_size << std::endl
        << "Bandwidth (MB/sec):     " << setprecision(3) << bandwidth << std::endl
@@ -476,7 +529,23 @@ int ObjBencher::write_bench(int secondsToRun,
        << "Stddev Latency:         " << vec_stddev(data.history.latency) << std::endl
        << "Max latency:            " << data.max_latency << std::endl
        << "Min latency:            " << data.min_latency << std::endl;
-
+  } else {
+    formatter->dump_format("total_time_run", "%f", (double)timePassed);
+    formatter->dump_format("total_writes_made", "%d", data.finished);
+    formatter->dump_format("write_size", "%d", data.object_size);
+    formatter->dump_format("bandwidth", "%f", bandwidth);
+    formatter->dump_format("stddev_bandwidth", "%f", vec_stddev(data.history.bandwidth));
+    formatter->dump_format("max_bandwidth", "%f", data.idata.max_bandwidth);
+    formatter->dump_format("min_bandwidth", "%f", data.idata.min_bandwidth);
+    formatter->dump_format("average_iops", "%d", (int)(data.finished/timePassed));
+    formatter->dump_format("stddev_iops", "%d", vec_stddev(data.history.iops));
+    formatter->dump_format("max_iops", "%d", data.idata.max_iops);
+    formatter->dump_format("min_iops", "%d", data.idata.min_iops);
+    formatter->dump_format("average_latency", "%f", data.avg_latency);
+    formatter->dump_format("stddev_latency", "%f", vec_stddev(data.history.latency));
+    formatter->dump_format("max_latency:", "%f", data.max_latency);
+    formatter->dump_format("min_latency", "%f", data.min_latency);
+  }
   //write object size/number data for read benchmarks
   ::encode(data.object_size, b_write);
   ::encode(data.finished, b_write);
@@ -680,7 +749,8 @@ int ObjBencher::seq_read_bench(int seconds_to_run, int num_objects, int concurre
   bandwidth = ((double)data.finished)*((double)data.object_size)/(double)runtime;
   bandwidth = bandwidth/(1024*1024); // we want it in MB/sec
 
-  out(cout) << "Total time run:       " << runtime << std::endl
+  if (!formatter) {
+    out(cout) << "Total time run:       " << runtime << std::endl
        << "Total reads made:     " << data.finished << std::endl
        << "Read size:            " << data.object_size << std::endl
        << "Bandwidth (MB/sec):   " << setprecision(3) << bandwidth << std::endl
@@ -691,6 +761,19 @@ int ObjBencher::seq_read_bench(int seconds_to_run, int num_objects, int concurre
        << "Average Latency:      " << data.avg_latency << std::endl
        << "Max latency:          " << data.max_latency << std::endl
        << "Min latency:          " << data.min_latency << std::endl;
+  } else {
+    formatter->dump_format("total_time_run", "%f", (double)runtime);
+    formatter->dump_format("total_reads_made", "%d", data.finished);
+    formatter->dump_format("read_size", "%d", data.object_size);
+    formatter->dump_format("bandwidth", "%f", bandwidth);
+    formatter->dump_format("average_iops", "%d", (int)(data.finished/runtime));
+    formatter->dump_format("stddev_iops", "%d", vec_stddev(data.history.iops));
+    formatter->dump_format("max_iops", "%d", data.idata.max_iops);
+    formatter->dump_format("min_iops", "%d", data.idata.min_iops);
+    formatter->dump_format("average_latency", "%f", data.avg_latency);
+    formatter->dump_format("max_latency", "%f", data.max_latency);
+    formatter->dump_format("min_latency", "%f", data.min_latency);
+  }
 
   completions_done();
 
@@ -888,7 +971,8 @@ int ObjBencher::rand_read_bench(int seconds_to_run, int num_objects, int concurr
   bandwidth = ((double)data.finished)*((double)data.object_size)/(double)runtime;
   bandwidth = bandwidth/(1024*1024); // we want it in MB/sec
 
-  out(cout) << "Total time run:       " << runtime << std::endl
+  if (!formatter) {
+    out(cout) << "Total time run:       " << runtime << std::endl
        << "Total reads made:     " << data.finished << std::endl
        << "Read size:            " << data.object_size << std::endl
        << "Bandwidth (MB/sec):   " << setprecision(3) << bandwidth << std::endl
@@ -899,7 +983,19 @@ int ObjBencher::rand_read_bench(int seconds_to_run, int num_objects, int concurr
        << "Average Latency:      " << data.avg_latency << std::endl
        << "Max latency:          " << data.max_latency << std::endl
        << "Min latency:          " << data.min_latency << std::endl;
-
+  } else {
+    formatter->dump_format("total_time_run", "%f", (double)runtime);
+    formatter->dump_format("total_reads_made", "%d", data.finished);
+    formatter->dump_format("read_size", "%d", data.object_size);
+    formatter->dump_format("bandwidth", "%f", bandwidth);
+    formatter->dump_format("average_iops", "%d", (int)(data.finished/runtime));
+    formatter->dump_format("stddev_iops", "%d", vec_stddev(data.history.iops));
+    formatter->dump_format("max_iops", "%d", data.idata.max_iops);
+    formatter->dump_format("min_iops", "%d", data.idata.min_iops);
+    formatter->dump_format("average_latency", "%f", data.avg_latency);
+    formatter->dump_format("max_latency", "%f", data.max_latency);
+    formatter->dump_format("min_latency", "%f", data.min_latency);
+  }
   completions_done();
 
   return 0;
index 9d40ccb0e1809d653dcac7b48500bd5b8700c70c..34e22c2b599a5e19b369043132e0d60a94d81a78 100644 (file)
@@ -18,6 +18,7 @@
 #include "common/config.h"
 #include "common/Cond.h"
 #include "common/ceph_context.h"
+#include "common/Formatter.h"
 #include <cfloat>
 
 struct bench_interval_data {
@@ -59,6 +60,8 @@ typedef std::pair<std::string, std::string> Object;
 
 class ObjBencher {
   bool show_time;
+  Formatter *formatter = NULL;
+  ostream *outstream = NULL;
 public:
   CephContext *cct;
 protected:
@@ -110,6 +113,12 @@ public:
   void set_show_time(bool dt) {
     show_time = dt;
   }
+  void set_formatter(Formatter *f) {
+    formatter = f;
+  }
+  void set_outstream(ostream& os) {
+    outstream = &os;
+  }
   int clean_up_slow(const std::string& prefix, int concurrentios);
 };
 
index 08756ed98e7ceca0614523d56dc16cf7539c01d4..bf51072f30bf3e65ed4d55a07c42041b847b0189 100755 (executable)
@@ -219,6 +219,11 @@ run_expect_fail "$RADOS_TOOL" mkpool delete_me_mkpool_test3 0 0k
 
 run_expect_succ "$RADOS_TOOL" --pool "$POOL" bench 1 write
 run_expect_fail "$RADOS_TOOL" --pool "$POOL" bench 1k write
+run_expect_succ "$RADOS_TOOL" --pool "$POOL" bench 1 write --format json --output "$TDIR/bench.json"
+run_expect_fail "$RADOS_TOOL" --pool "$POOL" bench 1 write --output "$TDIR/bench.json"
+run_expect_succ "$RADOS_TOOL" --pool "$POOL" bench 5 write --format json --no-cleanup
+run_expect_succ "$RADOS_TOOL" --pool "$POOL" bench 1 rand --format json
+run_expect_succ "$RADOS_TOOL" --pool "$POOL" bench 1 seq --format json
 
 
 echo "SUCCESS!"
index 82b88bbd7a781007851dd9b6b8f30f7bae9ed488..c845d30027f748c4151a3c573234fcd662b5e68c 100644 (file)
@@ -1208,6 +1208,7 @@ static int rados_tool_common(const std::map < std::string, std::string > &opts,
 
   Formatter *formatter = NULL;
   bool pretty_format = false;
+  const char *output = NULL;
 
   Rados rados;
   IoCtx io_ctx;
@@ -1360,6 +1361,10 @@ static int rados_tool_common(const std::map < std::string, std::string > &opts,
   if (i != opts.end()) {
     no_verify = true;
   }
+  i = opts.find("output");
+  if (i != opts.end()) {
+    output = i->second.c_str();
+  }
 
 
   // open rados
@@ -2406,12 +2411,29 @@ static int rados_tool_common(const std::map < std::string, std::string > &opts,
       ret = -EINVAL;
       goto out;
     }
+    if (!formatter && output) {
+      cerr << "-o|--output option can be used only with '--format' option"
+           << std::endl;
+      ret = -EINVAL;
+      goto out;
+    }
     RadosBencher bencher(g_ceph_context, rados, io_ctx);
     bencher.set_show_time(show_time);
+    ostream *outstream = NULL;
+    if (formatter) {
+      bencher.set_formatter(formatter);
+      if (output)
+        outstream = new ofstream(output);
+      else
+        outstream = &cout;
+      bencher.set_outstream(*outstream);
+    }
     ret = bencher.aio_bench(operation, seconds,
                            concurrent_ios, op_size, cleanup, run_name, no_verify);
     if (ret != 0)
       cerr << "error during benchmark: " << ret << std::endl;
+    if (formatter && output)
+      delete outstream;
   }
   else if (strcmp(nargs[0], "cleanup") == 0) {
     if (!pool_name)
@@ -2926,6 +2948,8 @@ int main(int argc, const char **argv)
       opts["all"] = "true";
     } else if (ceph_argparse_flag(args, i, "--default", (char*)NULL)) {
       opts["default"] = "true";
+    } else if (ceph_argparse_witharg(args, i, &val, "-o", "--output", (char*)NULL)) {
+      opts["output"] = val;
     } else {
       if (val[0] == '-')
         usage_exit();