00001
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <config.h>
00024 #include "perftest.h"
00025
00026 #include "backendmanager.h"
00027 #include "freemem.h"
00028 #include "omassert.h"
00029 #include "perftest/perftest_all.h"
00030 #include "realtime.h"
00031 #include "runprocess.h"
00032 #include "str.h"
00033 #include "stringutils.h"
00034 #include "testrunner.h"
00035 #include "testsuite.h"
00036
00037 #include <cstdlib>
00038 #include <iostream>
00039
00040 #include "safeunistd.h"
00041 #ifdef HAVE_SYS_UTSNAME_H
00042 # include <sys/utsname.h>
00043 #endif
00044
00045 #ifdef __WIN32__
00046 # include "safewindows.h"
00047 # include "safewinsock2.h"
00048 #endif
00049
00050 using namespace std;
00051
00052 PerfTestLogger logger;
00053
00054 static string
00055 escape_xml(const string & str)
00056 {
00057 string res;
00058 string::size_type p = 0;
00059 while (p < str.size()) {
00060 char ch = str[p++];
00061 switch (ch) {
00062 case '<':
00063 res += "<";
00064 continue;
00065 case '>':
00066 res += ">";
00067 continue;
00068 case '&':
00069 res += "&";
00070 continue;
00071 case '"':
00072 res += """;
00073 continue;
00074 default:
00075 res += ch;
00076 }
00077 }
00078 return res;
00079 }
00080
00081 PerfTestLogger::PerfTestLogger()
00082 : testcase_started(false),
00083 indexing_started(false),
00084 searching_started(false)
00085 {}
00086
00087 PerfTestLogger::~PerfTestLogger()
00088 {
00089 close();
00090 }
00091
00093 static string
00094 get_hostname()
00095 {
00096 #ifdef __WIN32__
00097 char buf[256];
00098 WORD WSAVerReq = MAKEWORD(1,1);
00099 WSADATA WSAData;
00100
00101 if (WSAStartup(WSAVerReq, &WSAData) != 0) {
00102
00103 return string();
00104 }
00105 if (gethostname(buf, sizeof(buf)) != 0) {
00106 *buf = '\0';
00107 }
00108 WSACleanup();
00109 return buf;
00110 #elif defined HAVE_SYS_UTSNAME_H
00111 struct utsname uname_buf;
00112 if (uname(&uname_buf) != 0) {
00113 uname_buf.nodename[0] = '\0';
00114 }
00115 return uname_buf.nodename;
00116 #elif defined HAVE_GETHOSTNAME
00117 char buf[256];
00118 if (gethostname(buf, sizeof(buf)) != 0) {
00119 *buf = '\0';
00120 }
00121 return buf;
00122 #else
00123 return string();
00124 #endif
00125 }
00126
00128 static string
00129 get_loadavg()
00130 {
00131 #ifdef __WIN32__
00132 return string();
00133 #else
00134 string loadavg;
00135 try {
00136 loadavg = stdout_to_string("uptime 2>/dev/null | sed 's/.*: \\([0-9][0-9]*\\)/\\1/;s/, .*//'");
00137 } catch (NoSuchProgram) {} catch (ReadError) {}
00138 return loadavg;
00139 #endif
00140 }
00141
00143 static string
00144 get_ncpus()
00145 {
00146 string ncpus;
00147 #ifdef __WIN32__
00148 SYSTEM_INFO siSysInfo;
00149 GetSystemInfo(&siSysInfo);
00150 ncpus = str(siSysInfo.dwNumberOfProcessors);
00151 #else
00152 try {
00153
00154 ncpus = stdout_to_string("getconf _NPROCESSORS_ONLN 2>/dev/null | grep -v '[^0-9]'");
00155 } catch (NoSuchProgram) {} catch (ReadError) {}
00156 if (ncpus.empty())
00157 try {
00158
00159 ncpus = stdout_to_string("sysctl hw.ncpu 2>/dev/null | sed 's/.*=//'");
00160 } catch (NoSuchProgram) {} catch (ReadError) {}
00161 if (ncpus.empty())
00162 try {
00163
00164 ncpus = stdout_to_string("PATH=/usr/sbin:$PATH psrinfo 2>/dev/null | grep -c on-line");
00165 } catch (NoSuchProgram) {} catch (ReadError) {}
00166 if (ncpus.empty())
00167 try {
00168
00169
00170
00171 ncpus = stdout_to_string("grep -c processor /proc/cpuinfo 2>/dev/null");
00172 } catch (NoSuchProgram) {} catch (ReadError) {}
00173 #endif
00174 return ncpus;
00175 }
00176
00178 static string
00179 get_distro()
00180 {
00181 string distro;
00182 #ifdef __WIN32__
00183 OSVERSIONINFO osvi;
00184 ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
00185 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
00186 GetVersionEx(&osvi);
00187 distro = "Microsoft Windows v";
00188 distro += str(osvi.dwMajorVersion);
00189 distro += '.';
00190 distro += str(osvi.dwMinorVersion);
00191 distro += '.';
00192 distro += str(osvi.dwBuildNumber);
00193 #else
00194 try {
00195 distro = stdout_to_string("perftest/get_machine_info 2>/dev/null");
00196 } catch (NoSuchProgram) {} catch (ReadError) {}
00197 #endif
00198 return distro;
00199 }
00200
00202 static string
00203 get_svnrev()
00204 {
00205 string svnrev;
00206 try {
00207 svnrev = stdout_to_string("svn info --non-interactive -R $srcdir/.. 2>/dev/null | sed 's/^Revision: //p;d' | sort -rn | head -n 1");
00208 } catch (NoSuchProgram) {} catch (ReadError) {}
00209
00210 return svnrev;
00211 }
00212
00214 static string
00215 get_svnbranch()
00216 {
00217 string branch;
00218 try {
00219 string svnurl = stdout_to_string("svn info --non-interactive $srcdir/.. 2>/dev/null | sed 's/^URL: //p;d'");
00220 string svnroot = stdout_to_string("svn info --non-interactive $srcdir/.. 2>/dev/null | sed 's/^Repository Root: //p;d'");
00221 string svnst = stdout_to_string("svn status --non-interactive -q --no-ignore $srcdir/.. 2>/dev/null");
00222 svnurl.erase(0, svnroot.size());
00223
00224 if (startswith(svnurl, '/'))
00225 svnurl.erase(0, 1);
00226
00227
00228 string::size_type i = svnurl.find('/');
00229 if (i != svnurl.npos) {
00230 if (i == CONST_STRLEN("trunk") && startswith(svnurl, "trunk")) {
00231
00232 branch = "trunk";
00233 } else if (i == CONST_STRLEN("branches") && startswith(svnurl, "branches")) {
00234 svnurl.erase(0, i + 1);
00235 i = svnurl.find('/');
00236 if (i != svnurl.npos) {
00237 branch = svnurl.substr(0, i);
00238 } else {
00239 branch = svnurl;
00240 }
00241 }
00242 }
00243 if (!branch.empty() && !svnst.empty())
00244 branch += " (modified)";
00245 } catch (NoSuchProgram) {} catch (ReadError) {}
00246
00247 return branch;
00248 }
00249
00250 bool
00251 PerfTestLogger::open(const string & logpath)
00252 {
00253 out.open(logpath.c_str(), ios::out | ios::binary | ios::trunc);
00254 if (!out.is_open()) {
00255 cerr << "Couldn't open output logfile '" << logpath << "'" << endl;
00256 return false;
00257 }
00258
00259 string loadavg = get_loadavg();
00260 string hostname = get_hostname();
00261 string ncpus = get_ncpus();
00262 string distro = get_distro();
00263
00264
00265 write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<testrun>\n"
00266 " <machineinfo>\n");
00267 if (!hostname.empty())
00268 write(" <hostname>" + hostname + "</hostname>\n");
00269 if (!loadavg.empty())
00270 write(" <loadavg>" + loadavg + "</loadavg>\n");
00271 if (!ncpus.empty())
00272 write(" <ncpus>" + ncpus + "</ncpus>\n");
00273 if (!distro.empty())
00274 write(" <distro>" + distro + "</distro>\n");
00275 write(" <physmem>" + str(get_total_physical_memory()) + "</physmem>\n");
00276 write(" </machineinfo>\n");
00277
00278 string svnrev = get_svnrev();
00279 string svnbranch = get_svnbranch();
00280
00281 write(" <sourceinfo>\n");
00282 if (!svnrev.empty())
00283 write(" <svnrev>" + svnrev + "</svnrev>\n");
00284 if (!svnbranch.empty())
00285 write(" <svnbranch>" + svnbranch + "</svnbranch>\n");
00286 write(" <version>" + string(Xapian::version_string()) + "</version>\n");
00287 write(" </sourceinfo>\n");
00288
00289
00290 return true;
00291 }
00292
00293 void
00294 PerfTestLogger::write(const string & text)
00295 {
00296 out.write(text.data(), text.size());
00297 out.flush();
00298 }
00299
00300 void
00301 PerfTestLogger::close()
00302 {
00303 repetition_end();
00304 if (out.is_open()) {
00305 write("</testrun>\n");
00306 out.close();
00307 }
00308 }
00309
00310 void
00311 PerfTestLogger::indexing_begin(const string & dbname,
00312 const std::map<std::string, std::string> & params)
00313 {
00314 searching_end();
00315 indexing_end();
00316 write(" <indexrun dbname=\"" + dbname + "\">\n <params>\n");
00317 std::map<std::string, std::string>::const_iterator i;
00318 for (i = params.begin(); i != params.end(); ++i) {
00319 write(" <param name=\"" + i->first + "\">" + escape_xml(i->second) + "</param>\n");
00320 }
00321 i = params.find("flush_threshold");
00322 if (i == params.end()) {
00323 Xapian::doccount flush_threshold = 0;
00324 const char *p = getenv("XAPIAN_FLUSH_THRESHOLD");
00325 if (p)
00326 flush_threshold = atoi(p);
00327 if (flush_threshold == 0)
00328 flush_threshold = 10000;
00329 write(" <param name=\"flush_threshold\">" +
00330 escape_xml(str(flush_threshold)) + "</param>\n");
00331 }
00332 write(" </params>\n");
00333 indexing_addcount = 0;
00334 indexing_unlogged_changes = false;
00335 indexing_timer = RealTime::now();
00336 last_indexlog_timer = indexing_timer;
00337 indexing_started = true;
00338
00339 indexing_log();
00340 }
00341
00342 void
00343 PerfTestLogger::indexing_log()
00344 {
00345 Assert(indexing_started);
00346 last_indexlog_timer = RealTime::now();
00347 double elapsed(last_indexlog_timer - indexing_timer);
00348 write(" <item>"
00349 "<time>" + str(elapsed) + "</time>"
00350 "<adds>" + str(indexing_addcount) + "</adds>"
00351 "</item>\n");
00352 indexing_unlogged_changes = false;
00353 }
00354
00355 void
00356 PerfTestLogger::indexing_add()
00357 {
00358 ++indexing_addcount;
00359 indexing_unlogged_changes = true;
00360
00361 if (indexing_addcount % 1000 == 0) {
00362 indexing_log();
00363 } else {
00364
00365 double now = RealTime::now();
00366 if (now > last_indexlog_timer + 5)
00367 indexing_log();
00368 }
00369 }
00370
00371 void
00372 PerfTestLogger::indexing_end()
00373 {
00374 if (indexing_started) {
00375 indexing_log();
00376 write(" </indexrun>\n");
00377 indexing_started = false;
00378 }
00379 }
00380
00381 void
00382 PerfTestLogger::searching_start(const string & description)
00383 {
00384 indexing_end();
00385 searching_end();
00386 write(" <searchrun>\n"
00387 " <description>" + escape_xml(description) + "</description>\n");
00388 searching_started = true;
00389 search_start();
00390 }
00391
00392 void
00393 PerfTestLogger::search_start()
00394 {
00395 searching_timer = RealTime::now();
00396 }
00397
00398 void
00399 PerfTestLogger::search_end(const Xapian::Query & query,
00400 const Xapian::MSet & mset)
00401 {
00402 Assert(searching_started);
00403 double elapsed(RealTime::now() - searching_timer);
00404 write(" <search>"
00405 "<time>" + str(elapsed) + "</time>"
00406 "<query>" + escape_xml(query.get_description()) + "</query>"
00407 "<mset>"
00408 "<size>" + str(mset.size()) + "</size>"
00409 "<lb>" + str(mset.get_matches_lower_bound()) + "</lb>"
00410 "<est>" + str(mset.get_matches_estimated()) + "</est>"
00411 "<ub>" + str(mset.get_matches_upper_bound()) + "</ub>"
00412 "</mset>"
00413 "</search>\n");
00414 search_start();
00415 }
00416
00417 void
00418 PerfTestLogger::searching_end()
00419 {
00420 if (searching_started) {
00421 write(" </searchrun>\n");
00422 searching_started = false;
00423 }
00424 }
00425
00426 void
00427 PerfTestLogger::testcase_begin(const string & testcase)
00428 {
00429 testcase_end();
00430 write(" <testcase name=\"" + testcase + "\" backend=\"" +
00431 backendmanager->get_dbtype() + "\" repnum=\"" +
00432 str(repetition_number) + "\">\n");
00433 testcase_started = true;
00434 }
00435
00436 void
00437 PerfTestLogger::testcase_end()
00438 {
00439 indexing_end();
00440 if (testcase_started) {
00441 write(" </testcase>\n");
00442 testcase_started = false;
00443 }
00444 }
00445
00446 void
00447 PerfTestLogger::repetition_begin(int num)
00448 {
00449 repetition_end();
00450 repetition_number = num;
00451 }
00452
00453 void
00454 PerfTestLogger::repetition_end()
00455 {
00456 testcase_end();
00457 }
00458
00459
00460 class PerfTestRunner : public TestRunner
00461 {
00462 string repetitions_string;
00463 mutable bool repetitions_parsed;
00464 mutable int repetitions;
00465 public:
00466 PerfTestRunner()
00467 : repetitions_parsed(false), repetitions(5)
00468 {
00469 test_driver::add_command_line_option("repetitions", 'r',
00470 &repetitions_string);
00471 }
00472
00473 int run() const {
00474 int result = 0;
00475 if (!repetitions_parsed) {
00476 if (!repetitions_string.empty()) {
00477 repetitions = atoi(repetitions_string.c_str());
00478 }
00479 repetitions_parsed = true;
00480 }
00481 for (int i = 0; i != repetitions; ++i) {
00482 logger.repetition_begin(i + 1);
00483 #include "perftest/perftest_collated.h"
00484 logger.repetition_end();
00485 }
00486 return result;
00487 }
00488 };
00489
00490 int main(int argc, char **argv)
00491 {
00492 if (!logger.open("perflog.xml"))
00493 return 1;
00494
00495 PerfTestRunner runner;
00496
00497 return runner.run_tests(argc, argv);
00498 }