Merge branch 'master' of https://github.com/grpc/grpc
diff --git a/Makefile b/Makefile
index d28a21f..68cef53 100644
--- a/Makefile
+++ b/Makefile
@@ -2469,6 +2469,21 @@
 endif
 
 ifeq ($(NO_PROTOC),true)
+$(GENDIR)/test/cpp/qps/user_data.pb.cc: protoc_dep_error
+$(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc: protoc_dep_error
+else
+$(GENDIR)/test/cpp/qps/user_data.pb.cc: test/cpp/qps/user_data.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+	$(E) "[PROTOC]  Generating protobuf CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --cpp_out=$(GENDIR) $<
+
+$(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc: test/cpp/qps/user_data.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $<
+endif
+
+ifeq ($(NO_PROTOC),true)
 $(GENDIR)/test/cpp/util/echo.pb.cc: protoc_dep_error
 $(GENDIR)/test/cpp/util/echo.grpc.pb.cc: protoc_dep_error
 else
@@ -3465,7 +3480,9 @@
 
 LIBGRPC++_BENCHMARK_CONFIG_SRC = \
     $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc \
+    $(GENDIR)/test/cpp/qps/user_data.pb.cc $(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc \
     test/cpp/qps/report.cc \
+    test/cpp/qps/user_data_client.cc \
     test/cpp/util/benchmark_config.cc \
 
 
@@ -3510,8 +3527,9 @@
 -include $(LIBGRPC++_BENCHMARK_CONFIG_OBJS:.o=.dep)
 endif
 endif
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/report.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/util/benchmark_config.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/report.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/user_data.pb.cc $(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/user_data_client.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/user_data.pb.cc $(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/util/benchmark_config.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/user_data.pb.cc $(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc
 
 
 LIBGRPC++_TEST_CONFIG_SRC = \
@@ -4024,6 +4042,7 @@
 
 LIBQPS_SRC = \
     $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc \
+    $(GENDIR)/test/cpp/qps/user_data.pb.cc $(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc \
     test/cpp/qps/client_async.cc \
     test/cpp/qps/client_sync.cc \
     test/cpp/qps/driver.cc \
@@ -4031,6 +4050,7 @@
     test/cpp/qps/server_async.cc \
     test/cpp/qps/server_sync.cc \
     test/cpp/qps/timer.cc \
+    test/cpp/qps/user_data_client.cc \
 
 
 LIBQPS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBQPS_SRC))))
@@ -4074,13 +4094,14 @@
 -include $(LIBQPS_OBJS:.o=.dep)
 endif
 endif
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_async.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_sync.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/driver.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_worker.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_async.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_sync.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/timer.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_async.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/user_data.pb.cc $(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_sync.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/user_data.pb.cc $(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/driver.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/user_data.pb.cc $(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_worker.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/user_data.pb.cc $(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_async.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/user_data.pb.cc $(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_sync.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/user_data.pb.cc $(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/timer.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/user_data.pb.cc $(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/user_data_client.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/user_data.pb.cc $(GENDIR)/test/cpp/qps/user_data.grpc.pb.cc
 
 
 LIBGRPC_CSHARP_EXT_SRC = \
diff --git a/build.json b/build.json
index aeb36fd..957e43c 100644
--- a/build.json
+++ b/build.json
@@ -537,7 +537,9 @@
       "language": "c++",
       "src": [
         "test/cpp/qps/qpstest.proto",
+        "test/cpp/qps/user_data.proto",
         "test/cpp/qps/report.cc",
+        "test/cpp/qps/user_data_client.cc",
         "test/cpp/util/benchmark_config.cc"
       ]
     },
@@ -708,13 +710,15 @@
       ],
       "src": [
         "test/cpp/qps/qpstest.proto",
+        "test/cpp/qps/user_data.proto",
         "test/cpp/qps/client_async.cc",
         "test/cpp/qps/client_sync.cc",
         "test/cpp/qps/driver.cc",
         "test/cpp/qps/qps_worker.cc",
         "test/cpp/qps/server_async.cc",
         "test/cpp/qps/server_sync.cc",
-        "test/cpp/qps/timer.cc"
+        "test/cpp/qps/timer.cc",
+        "test/cpp/qps/user_data_client.cc"
       ],
       "deps": [
         "grpc_test_util",
diff --git a/test/cpp/qps/report.cc b/test/cpp/qps/report.cc
index 678ea080..91fe952 100644
--- a/test/cpp/qps/report.cc
+++ b/test/cpp/qps/report.cc
@@ -35,6 +35,7 @@
 
 #include <grpc/support/log.h>
 #include "test/cpp/qps/stats.h"
+#include "user_data_client.h"
 
 namespace grpc {
 namespace testing {
@@ -118,5 +119,76 @@
                   [](ResourceUsage u) { return u.wall_time; }));
 }
 
+UserDataClient userDataClient(grpc::CreateChannel("localhost:50052", grpc::InsecureCredentials(),
+                          ChannelArguments()));
+
+//Leaderboard Reported implementation.
+void UserDatabaseReporter::ReportQPS(const ScenarioResult& result) const {
+  auto qps = result.latencies.Count() /
+              average(result.client_resources,
+                      [](ResourceUsage u) { return u.wall_time; });
+
+  userDataClient.setQPS(qps);
+  userDataClient.setConfigs(result.client_config, result.server_config);
+}
+
+void UserDatabaseReporter::ReportQPSPerCore(const ScenarioResult& result) const {
+  auto qps = result.latencies.Count() /
+            average(result.client_resources,
+                    [](ResourceUsage u) { return u.wall_time; });
+
+  auto qpsPerCore = qps / result.server_config.threads();
+
+  userDataClient.setQPSPerCore(qpsPerCore);
+  userDataClient.setConfigs(result.client_config, result.server_config);
+}
+
+void UserDatabaseReporter::ReportLatency(const ScenarioResult& result) const {
+  userDataClient.setLatencies(result.latencies.Percentile(50) / 1000,
+                              result.latencies.Percentile(90) / 1000,
+                              result.latencies.Percentile(95) / 1000,
+                              result.latencies.Percentile(99) / 1000,
+                              result.latencies.Percentile(99.9) / 1000);
+  userDataClient.setConfigs(result.client_config, result.server_config);
+}
+
+void UserDatabaseReporter::ReportTimes(const ScenarioResult& result) const {
+  double serverSystemTime = 100.0 * sum(result.server_resources,
+                  [](ResourceUsage u) { return u.system_time; }) /
+                    sum(result.server_resources,
+                  [](ResourceUsage u) { return u.wall_time; });
+  double serverUserTime = 100.0 * sum(result.server_resources,
+                  [](ResourceUsage u) { return u.user_time; }) /
+                    sum(result.server_resources,
+                  [](ResourceUsage u) { return u.wall_time; });
+  double clientSystemTime = 100.0 * sum(result.client_resources,
+                  [](ResourceUsage u) { return u.system_time; }) /
+                    sum(result.client_resources,
+                  [](ResourceUsage u) { return u.wall_time; });
+  double clientUserTime = 100.0 * sum(result.client_resources,
+                  [](ResourceUsage u) { return u.user_time; }) /
+                    sum(result.client_resources,
+                  [](ResourceUsage u) { return u.wall_time; });
+
+  userDataClient.setTimes(serverSystemTime, serverUserTime, 
+    clientSystemTime, clientUserTime);
+  userDataClient.setConfigs(result.client_config, result.server_config);
+}
+
+void UserDatabaseReporter::SendData() const {
+  //send data to performance database
+  int userDataState = userDataClient.sendData(access_token_, test_name_, sys_info_);
+
+  //check state of data sending
+  switch(userDataState) {
+    case 1:
+      gpr_log(GPR_INFO, "Data sent to user database successfully");
+      break;
+    case -1:
+      gpr_log(GPR_INFO, "Data could not be sent to user database");
+      break;
+  }
+}
+
 }  // namespace testing
 }  // namespace grpc
diff --git a/test/cpp/qps/report.h b/test/cpp/qps/report.h
index 0cce088..432ed5d 100644
--- a/test/cpp/qps/report.h
+++ b/test/cpp/qps/report.h
@@ -103,6 +103,24 @@
   void ReportTimes(const ScenarioResult& result) const GRPC_OVERRIDE;
 };
 
+/** Reporter for client leaderboard. */
+class UserDatabaseReporter : public Reporter {
+ public:
+  UserDatabaseReporter(const string& name, const string& access_token, const string& test_name, const string& sys_info)
+   : Reporter(name), access_token_(access_token), test_name_(test_name), sys_info_(sys_info) {}
+  ~UserDatabaseReporter() { SendData(); };
+
+ private:
+  std::string access_token_;
+  std::string test_name_;
+  std::string sys_info_;
+  void ReportQPS(const ScenarioResult& result) const GRPC_OVERRIDE;
+  void ReportQPSPerCore(const ScenarioResult& result) const GRPC_OVERRIDE;
+  void ReportLatency(const ScenarioResult& result) const GRPC_OVERRIDE;
+  void ReportTimes(const ScenarioResult& result) const GRPC_OVERRIDE;
+  void SendData() const;
+};
+
 }  // namespace testing
 }  // namespace grpc
 
diff --git a/test/cpp/qps/run_auth_test.py b/test/cpp/qps/run_auth_test.py
new file mode 100755
index 0000000..c327a50
--- /dev/null
+++ b/test/cpp/qps/run_auth_test.py
@@ -0,0 +1,239 @@
+#
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#!/usr/bin/python
+
+import os
+import sys
+import re
+import urllib2
+import urllib
+import json
+import time
+import subprocess
+import fnmatch
+
+CLIENT_ID = '1018396037782-tv81fshn76nemr24uuhuginceb9hni2m.apps.googleusercontent.com'
+CLIENT_SECRET = '_HGHXg4DAA59r4w4x8p6ARzD'
+GRANT_TYPE = 'http://oauth.net/grant_type/device/1.0'
+ACCESS_TOKENS_DIR = '/tmp/auth_lead_access_tokens'
+AUTH_TOKEN_LINK = 'https://www.googleapis.com/oauth2/v3/token'
+GOOGLE_ACCOUNTS_LINK = 'https://accounts.google.com/o/oauth2/device/code'
+USER_INFO_LINK = 'https://www.googleapis.com/oauth2/v1/userinfo'
+
+# Fetches JSON reply object, given a url and parameters
+def fetchJSON(url, paramDict):
+  if len(paramDict) == 0:
+    req = urllib2.Request(url)
+  else:
+    data = urllib.urlencode(paramDict)
+    req = urllib2.Request(url, data)
+
+  try:
+    response = urllib2.urlopen(req)
+    result = response.read()
+    
+  except urllib2.HTTPError, error:
+    result = error.read()
+
+  return result
+
+# Fetch user info; used to check if access token is valid
+def getUserInfo(accessToken):
+  url = USER_INFO_LINK + '?access_token=' + accessToken
+  paramDict = {}
+  JSONBody = fetchJSON(url, paramDict)
+  data = json.loads(JSONBody)
+  
+  return data
+
+# Returns true if stored access token is valid
+def isAccessTokenValid(accessToken):
+  data = getUserInfo(accessToken);
+
+  if 'id' in data:
+    return True
+  else:
+    return False
+
+# Returns user id given a working access token
+def getUserId(accessToken):
+  data = getUserInfo(accessToken)
+
+  email = data['email']
+  userId = getUserIdFromEmail(email)
+
+  return userId
+
+# Extracts a unique user id from an email address
+def getUserIdFromEmail(email):
+  email = email.split('@')[0].lower() # take username and convert to lower case
+  userId = re.sub('[.]', '', email) # remove periods
+
+  return userId
+
+# Use an existing access token
+def useAccessToken(userTokFile):
+  with open(userTokFile, "r") as data_file:
+    data = json.load(data_file) # load JSON data from file
+    accessToken = data["access_token"]
+
+    # If access token has gone stale, refresh it
+    if not isAccessTokenValid(accessToken):
+      return refreshAccessToken(data["refresh_token"], userTokFile)
+
+    return accessToken
+
+# refresh stale access token
+def refreshAccessToken(refreshToken, userTokFile):
+  # Parameters for request
+  paramDict = {'refresh_token':refreshToken, 'client_id':CLIENT_ID, 'client_secret':CLIENT_SECRET, 'grant_type':'refresh_token'}
+  # Fetch reply to request
+  JSONBody = fetchJSON(AUTH_TOKEN_LINK, paramDict)
+  data = json.loads(JSONBody)
+
+  if not 'access_token' in data:
+    # Refresh token has gone stale, re-authentication required
+    return reauthenticate()
+  else:
+    # write fresh access token to tokens file
+    tokenData = {}
+
+    with open(userTokFile, "r") as data_file:
+      tokenData = json.load(data_file)
+    
+    with open(userTokFile, "w") as data_file:
+      tokenData['access_token'] = data['access_token']
+      json.dump(tokenData, data_file)
+
+    # return fresh access token
+    return data['access_token']
+
+def reauthenticate():
+  # Request parameters
+  paramDict = {'client_id':CLIENT_ID, 'scope':'email profile'}
+  JSONBody = fetchJSON(GOOGLE_ACCOUNTS_LINK, paramDict)
+  data = json.loads(JSONBody)
+
+  print 'User authorization required\n'
+  print 'Please use the following code in you browser: ', data['user_code'] # Code to be entered by user in browser
+  print 'Verification URL: ', data['verification_url'] # Authentication link
+  print '\nAwaiting user authorization. May take a few more seconds after authorizing...\n'
+
+  authData = {}
+
+  while not 'access_token' in authData:
+    # Request parameters
+    authDict = {'client_id':CLIENT_ID, 'client_secret':CLIENT_SECRET, 'code':data['device_code'], 'grant_type':GRANT_TYPE}
+    JSONBody = fetchJSON(AUTH_TOKEN_LINK, authDict)
+    authData = json.loads(JSONBody)
+    # If server pinged too quickly, will get slowdown message; need to wait for specified interval
+    time.sleep(data['interval'])
+
+  # File to write tokens
+  newUserTokFile = ACCESS_TOKENS_DIR + '/' + getUserId(authData['access_token'])
+
+  # Write tokens to file
+  with open(newUserTokFile, "w") as data_file:
+    json.dump(authData, data_file)
+
+  # return working access token
+  return authData['access_token']
+
+# Fetch a working access token given user entered email id; authntication may be required
+def getAccessToken(email):
+  # Get unique user id from email address
+  userId = getUserIdFromEmail(email)
+
+  # Token file
+  userTokFile = ACCESS_TOKENS_DIR + '/' + userId
+
+  accessToken = ''
+
+  if os.path.exists(userTokFile):
+    # File containing access token exists; unless refresh token has expired, user authentication will not be required
+    accessToken = useAccessToken(userTokFile)
+  else:
+    # User authentication required
+    accessToken = reauthenticate()
+
+  return accessToken
+
+# If user has not entered full path to test, recursively searches for given test in parent folders
+def findTestPath(test):
+  # If user entered full path to test, return it
+  if(os.path.isfile(test)):
+    return test
+
+  testName = test.split('/')[-1] # Extract just test name
+  testPath = ''
+
+  # Search for test
+  for root, dirnames, filenames in os.walk('../../../'):
+      for fileName in fnmatch.filter(filenames, '*'+testName):
+        testPath = os.path.join(root, fileName)
+
+  return testPath
+
+def main():
+  # If tokens directory does not exist, creates it
+  if not os.path.exists(ACCESS_TOKENS_DIR):
+    os.makedirs(ACCESS_TOKENS_DIR)
+
+  if len(sys.argv) > 1:
+    test = sys.argv[1]
+  else:
+    test = raw_input('Enter the test path/name: ')
+
+  if len(sys.argv) > 2:
+    email = sys.argv[2]
+  else:
+    email = raw_input('Enter your e-mail id: ')
+
+  try:
+    # Fetch working access token
+    accessToken = getAccessToken(email)
+  except Exception, e:
+    print 'Error in authentication'
+
+  try:
+    testPath = findTestPath(test) # Get path to test
+    testName = testPath.split('/')[-1] # Get test name
+
+    # Fetch system information
+    sysInfo = os.popen('lscpu').readlines()
+
+    # Run the test
+    subprocess.call([testPath, '--access_token='+accessToken, '--test_name='+testName, '--sys_info='+str(sysInfo).strip('[]')])
+  except OSError:
+    print 'Could not execute the test, please check test name'
+
+if __name__ == "__main__":
+  main()
\ No newline at end of file
diff --git a/test/cpp/qps/user_data.proto b/test/cpp/qps/user_data.proto
new file mode 100644
index 0000000..947ccce
--- /dev/null
+++ b/test/cpp/qps/user_data.proto
@@ -0,0 +1,122 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+import "test/cpp/qps/qpstest.proto";
+
+package grpc.testing;
+
+service UserDataTransfer {
+  // Sends client info
+  rpc RecordSingleClientData (SingleUserRecordRequest) returns (SingleUserRecordReply) {}
+
+  rpc RetrieveSingleUserData (SingleUserRetrieveRequest) returns (SingleUserRetrieveReply) {}
+
+  rpc RetrieveAllUsersData (AllUsersRetrieveRequest) returns (AllUsersRetrieveReply) {}
+}
+
+//Metrics to be stored
+message Metrics {
+  optional double qps = 1;
+  optional double qps_per_core = 2;
+  optional double perc_lat_50 = 3;
+  optional double perc_lat_90 = 4;
+  optional double perc_lat_95 = 5;
+  optional double perc_lat_99 = 6;
+  optional double perc_lat_99_point_9 = 7;
+  optional double server_system_time = 8;
+  optional double server_user_time = 9;
+  optional double client_system_time = 10;
+  optional double client_user_time = 11;
+}
+
+//Timestamped details
+message DataDetails {
+  optional string timestamp = 1;
+  optional string test_name = 2;
+  optional string sys_info = 3;
+  optional Metrics metrics = 4;
+  optional ClientConfig client_config = 5;
+  optional ServerConfig server_config = 6;
+}
+
+//User details
+message UserDetails {
+  optional string id = 1;
+  optional string email = 2;
+  optional bool verified_email = 3;
+  optional string name = 4;
+  optional string given_name = 5;
+  optional string family_name = 6;
+  optional string link = 7;
+  optional string picture = 8;
+  optional string gender = 9;
+  optional string locale = 10;
+  optional string hd = 11;
+}
+
+//Stored to database
+message SingleUserDetails {
+  repeated DataDetails data_details = 1;
+  optional UserDetails user_details = 2;
+}
+
+//Request for storing a single user's data
+message SingleUserRecordRequest {
+  optional string access_token = 1;
+  optional string test_name = 2;
+  optional string sys_info = 3;
+  optional Metrics metrics = 4;
+  optional ClientConfig client_config = 5;
+  optional ServerConfig server_config = 6;
+}
+
+//Reply to request for storing single user's data
+message SingleUserRecordReply {
+}
+
+//Request for retrieving single user's data
+message SingleUserRetrieveRequest {
+  optional string user_id = 1;
+}
+
+//Reply for request to retrieve single user's data
+message SingleUserRetrieveReply {
+  optional SingleUserDetails details = 1;
+}
+
+//Request for retrieving all users' data
+message AllUsersRetrieveReply {
+  repeated SingleUserDetails user_data = 1;
+}
+
+//Reply to request for retrieving all users' data
+message AllUsersRetrieveRequest {
+}
\ No newline at end of file
diff --git a/test/cpp/qps/user_data_client.cc b/test/cpp/qps/user_data_client.cc
new file mode 100644
index 0000000..a8ced75
--- /dev/null
+++ b/test/cpp/qps/user_data_client.cc
@@ -0,0 +1,114 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "user_data_client.h"
+
+namespace grpc {
+namespace testing {
+
+//sets the client and server config information
+void UserDataClient::setConfigs(const ClientConfig& clientConfig, const ServerConfig& serverConfig) {
+  clientConfig_ = clientConfig;
+  serverConfig_ = serverConfig;
+}
+
+//sets the QPS
+void UserDataClient::setQPS(double QPS) {
+  QPS_ = QPS;
+}
+
+//sets the QPS per core
+void UserDataClient::setQPSPerCore(double QPSPerCore) {
+  QPSPerCore_ = QPSPerCore;
+}
+
+//sets the 50th, 90th, 95th, 99th and 99.9th percentile latency
+void UserDataClient::setLatencies(double percentileLatency50, double percentileLatency90,
+    double percentileLatency95, double percentileLatency99, double percentileLatency99Point9) {
+  percentileLatency50_ = percentileLatency50;
+  percentileLatency90_ = percentileLatency90;
+  percentileLatency95_ = percentileLatency95;
+  percentileLatency99_ = percentileLatency99;
+  percentileLatency99Point9_ = percentileLatency99Point9;
+}
+
+//sets the server and client, user and system times
+void UserDataClient::setTimes(double serverSystemTime, double serverUserTime, 
+    double clientSystemTime, double clientUserTime) {
+  serverSystemTime_ = serverSystemTime;
+  serverUserTime_ = serverUserTime;
+  clientSystemTime_ = clientSystemTime;
+  clientUserTime_ = clientUserTime;
+}
+
+//sends the data to the performancew database server
+int UserDataClient::sendData(std::string access_token, std::string test_name, std::string sys_info) {
+  //Data record request object
+  SingleUserRecordRequest singleUserRecordRequest;
+
+  //setting access token, name of the test and the system information
+  singleUserRecordRequest.set_access_token(access_token);
+  singleUserRecordRequest.set_test_name(test_name);
+  singleUserRecordRequest.set_sys_info(sys_info);
+
+  //setting configs
+  *(singleUserRecordRequest.mutable_client_config()) = clientConfig_;
+  *(singleUserRecordRequest.mutable_server_config()) = serverConfig_;
+  
+  Metrics* metrics = singleUserRecordRequest.mutable_metrics();
+
+  //setting metrcs in data record request
+  if(QPS_ != DBL_MIN) metrics->set_qps(QPS_);
+  if(QPSPerCore_ != DBL_MIN) metrics->set_qps_per_core(QPSPerCore_);
+  if(percentileLatency50_ != DBL_MIN) metrics->set_perc_lat_50(percentileLatency50_);
+  if(percentileLatency90_ != DBL_MIN) metrics->set_perc_lat_90(percentileLatency90_);
+  if(percentileLatency95_ != DBL_MIN) metrics->set_perc_lat_95(percentileLatency95_);
+  if(percentileLatency99_ != DBL_MIN) metrics->set_perc_lat_99(percentileLatency99_);
+  if(percentileLatency99Point9_ != DBL_MIN) metrics->set_perc_lat_99_point_9(percentileLatency99Point9_);
+  if(serverSystemTime_ != DBL_MIN) metrics->set_server_system_time(serverSystemTime_);
+  if(serverUserTime_ != DBL_MIN) metrics->set_server_user_time(serverUserTime_);
+  if(clientSystemTime_ != DBL_MIN) metrics->set_client_system_time(clientSystemTime_);
+  if(clientUserTime_ != DBL_MIN) metrics->set_client_user_time(clientUserTime_);
+
+  SingleUserRecordReply singleUserRecordReply;
+  ClientContext context;
+
+  Status status = stub_->RecordSingleClientData(&context, singleUserRecordRequest, &singleUserRecordReply);
+  if (status.IsOk()) {
+    return 1;  //data sent to database successfully
+  } else {
+    return -1;  //error in data sending
+  }
+}
+}  //testing
+}  //grpc
\ No newline at end of file
diff --git a/test/cpp/qps/user_data_client.h b/test/cpp/qps/user_data_client.h
new file mode 100644
index 0000000..c2e07ef
--- /dev/null
+++ b/test/cpp/qps/user_data_client.h
@@ -0,0 +1,100 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <cfloat>
+
+#include <grpc/grpc.h>
+#include <grpc++/channel_arguments.h>
+#include <grpc++/channel_interface.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/credentials.h>
+#include <grpc++/status.h>
+#include "test/cpp/qps/user_data.grpc.pb.h"
+
+
+namespace grpc{
+namespace testing {
+
+//Manages data sending to performance database server
+class UserDataClient {
+public:
+  UserDataClient(std::shared_ptr<ChannelInterface> channel)
+    : stub_(UserDataTransfer::NewStub(channel)) {}
+  
+  ~UserDataClient() {}
+
+  //sets the client and server config information
+  void setConfigs(const ClientConfig& clientConfig, const ServerConfig& serverConfig);
+  
+  //sets the QPS
+  void setQPS(double QPS);
+
+  //sets the QPS per core
+  void setQPSPerCore(double QPSPerCore);
+
+  //sets the 50th, 90th, 95th, 99th and 99.9th percentile latency
+  void setLatencies(double percentileLatency50, double percentileLatency90,
+     double percentileLatency95, double percentileLatency99, double percentileLatency99Point9);
+
+  //sets the server and client, user and system times
+  void setTimes(double serverSystemTime, double serverUserTime, 
+    double clientSystemTime, double clientUserTime);
+
+  //sends the data to the performancew database server
+  int sendData(std::string access_token, std::string test_name, std::string sys_info);
+
+private:
+  std::unique_ptr<UserDataTransfer::Stub> stub_;
+  ClientConfig clientConfig_;
+  ServerConfig serverConfig_;
+  double QPS_ = DBL_MIN;
+  double QPSPerCore_ = DBL_MIN;
+  double percentileLatency50_ = DBL_MIN;
+  double percentileLatency90_ = DBL_MIN;
+  double percentileLatency95_ = DBL_MIN;
+  double percentileLatency99_ = DBL_MIN;
+  double percentileLatency99Point9_ = DBL_MIN;
+  double serverSystemTime_ = DBL_MIN;
+  double serverUserTime_ = DBL_MIN;
+  double clientSystemTime_ = DBL_MIN;
+  double clientUserTime_ = DBL_MIN;
+};
+
+} //namespace testing
+} //namespace grpc
+
+
diff --git a/test/cpp/util/benchmark_config.cc b/test/cpp/util/benchmark_config.cc
index 5b3c1da..82663b6 100644
--- a/test/cpp/util/benchmark_config.cc
+++ b/test/cpp/util/benchmark_config.cc
@@ -37,6 +37,12 @@
 DEFINE_bool(enable_log_reporter, true,
             "Enable reporting of benchmark results through GprLog");
 
+DEFINE_string(access_token, "", "Authorizing JSON string for leaderboard");
+
+DEFINE_string(test_name, "", "Name of the test being executed");
+
+DEFINE_string(sys_info, "", "System information");
+
 // In some distros, gflags is in the namespace google, and in some others,
 // in gflags. This hack is enabling us to find both.
 namespace google {}
@@ -57,6 +63,10 @@
     composite_reporter->add(
         std::unique_ptr<Reporter>(new GprLogReporter("LogReporter")));
   }
+  if(!FLAGS_access_token.empty())
+    composite_reporter->add(
+      std::unique_ptr<Reporter>(new UserDatabaseReporter("UserDataReporter", FLAGS_access_token, FLAGS_test_name, FLAGS_sys_info)));
+
   return std::shared_ptr<Reporter>(composite_reporter);
 }