diff --git a/checkin_notes b/checkin_notes index de218af72e..630fe44239 100644 --- a/checkin_notes +++ b/checkin_notes @@ -6640,3 +6640,18 @@ Rom 5 Nov 2012 samples\vboxwrapper\ vboxwrapper.cpp + +David 7 Nov 2012 + - remote job submission: show 20 batches, with link to show all + - XML parser: make low-level functions inline, in an attempt + (unsuccessful, as far as I can tell) to boost performance + + html/user/ + submit.php + lib/ + cc_config.h + parse.cpp,h + proxy_info.h + sched/ + sched_main.cpp + diff --git a/doc/index.php b/doc/index.php index 8c92b149b8..3a0e000bdd 100644 --- a/doc/index.php +++ b/doc/index.php @@ -36,7 +36,8 @@ function show_participant() { function show_totals() { $fn = "boinc_state.xml"; if (!file_exists($fn) || filemtime($fn) < time()-86400) { - $x = file_get_contents("http://boincstats.com/en/xml/boincState"); + $uid = time(); + $x = file_get_contents("http://boincstats.com/en/xml/boincState?uid=$uid"); if ($x) { $f = fopen($fn, "w"); fwrite($f, $x); diff --git a/html/user/submit.php b/html/user/submit.php index 03599abe9c..dfec3efce1 100644 --- a/html/user/submit.php +++ b/html/user/submit.php @@ -27,34 +27,43 @@ error_reporting(E_ALL); ini_set('display_errors', true); ini_set('display_startup_errors', true); -// show a set of batches -// -function show_batches($batches) { - foreach ($batches as $batch) { - if ($batch->state < BATCH_STATE_COMPLETE || $batch->fraction_done < 1) { - $wus = BoincWorkunit::enum("batch = $batch->id"); - $batch = get_batch_params($batch, $wus); - } - $app = BoincApp::lookup_id($batch->app_id); - if ($app) { - $batch->app_name = $app->name; - } else { - $batch->app_name = "unknown"; - } - $user = BoincUser::lookup_id($batch->user_id); - if ($user) { - $batch->user_name = $user->name; - } else { - $batch->user_name = "missing user $batch->user_id"; - } - } +define("PAGE_SIZE", 20); +function state_count($batches, $state) { + $n = 0; + foreach ($batches as $batch) { + if ($batch->state == $state) $n++; + } + return $n; +} + +function show_all_link($batches, $state, $limit, $user, $app) { + $n = state_count($batches, $state); + if ($n > $limit) { + if ($user) $userid = $user->id; + else $userid = 0; + if ($app) $appid = $app->id; + else $appid = 0; + + echo "Showing the most recent $limit of $n batches. + Show all $n + "; + } +} + +function show_in_progress($batches, $limit, $user, $app) { $first = true; + $n = 0; foreach ($batches as $batch) { if ($batch->state != BATCH_STATE_IN_PROGRESS) continue; + if ($limit && $n == $limit) break; + $n++; if ($first) { $first = false; echo "

Batches in progress

\n"; + if ($limit) { + show_all_link($batches, BATCH_STATE_IN_PROGRESS, $limit, $user, $app); + } start_table(); table_header("name", "ID", "user", "app", "# jobs", "progress", "submitted"); } @@ -74,13 +83,21 @@ function show_batches($batches) { } else { end_table(); } +} +function show_complete($batches, $limit, $user, $app) { $first = true; + $n = 0; foreach ($batches as $batch) { if ($batch->state != BATCH_STATE_COMPLETE) continue; + if ($limit && $n == $limit) break; + $n++; if ($first) { $first = false; echo "

Completed batches

\n"; + if ($limit) { + show_all_link($batches, BATCH_STATE_COMPLETE, $limit, $user, $app); + } start_table(); table_header("name", "ID", "user", "app", "# jobs", "fraction done", "submitted"); } @@ -100,13 +117,21 @@ function show_batches($batches) { } else { end_table(); } +} +function show_aborted($batches, $limit, $user, $app) { $first = true; + $n = 0; foreach ($batches as $batch) { if ($batch->state != BATCH_STATE_ABORTED) continue; + if ($limit && $n == $limit) break; + $n++; if ($first) { $first = false; echo "

Aborted batches

\n"; + if ($limit) { + show_all_link($batches, BATCH_STATE_ABORTED, $limit, $user, $app); + } start_table(); table_header("name", "ID", "user", "app", "# jobs", "submitted"); } @@ -124,6 +149,38 @@ function show_batches($batches) { } } +// fill in the app and user names +// +function fill_in_app_and_user_names(&$batches) { + foreach ($batches as $batch) { + if ($batch->state < BATCH_STATE_COMPLETE || $batch->fraction_done < 1) { + $wus = BoincWorkunit::enum("batch = $batch->id"); + $batch = get_batch_params($batch, $wus); + } + $app = BoincApp::lookup_id($batch->app_id); + if ($app) { + $batch->app_name = $app->name; + } else { + $batch->app_name = "unknown"; + } + $user = BoincUser::lookup_id($batch->user_id); + if ($user) { + $batch->user_name = $user->name; + } else { + $batch->user_name = "missing user $batch->user_id"; + } + } +} + +// show a set of batches +// +function show_batches($batches, $limit, $user, $app) { + fill_in_app_and_user_names($batches); + show_in_progress($batches, $limit, $user, $app); + show_complete($batches, $limit, $user, $app); + show_aborted($batches, $limit, $user, $app); +} + // the job submission "home page": // show the user's in-progress and completed batches, // and a button for creating a new batch @@ -165,13 +222,12 @@ function handle_main($user) { echo ""; $batches = BoincBatch::enum("user_id = $user->id order by id desc"); - show_batches($batches); + show_batches($batches, PAGE_SIZE, $user, null); page_tail(); } -function handle_admin($user) { - $app_id = get_int("app_id"); +function check_admin_access($user, $app_id) { $user_submit = BoincUserSubmit::lookup_userid($user->id); if (!$user_submit) error_page("no access"); if ($app_id) { @@ -179,16 +235,24 @@ function handle_admin($user) { $usa = BoincUserSubmitApp::lookup("user_id = $user->id and app_id=$app_id"); if (!$usa) error_page("no access"); } + } else { + if (!$user_submit->manage_all) error_page("no access"); + } +} + +function handle_admin($user) { + $app_id = get_int("app_id"); + check_admin_access($user, $app_id); + if ($app_id) { $app = BoincApp::lookup_id($app_id); if (!$app) error_page("no such app"); page_head("Administer $app->user_friendly_name"); $batches = BoincBatch::enum("app_id = $app_id order by id desc"); - show_batches($batches); + show_batches($batches, PAGE_SIZE, null, $app); } else { - if (!$user_submit->manage_all) error_page("no access"); page_head("Administer all apps"); $batches = BoincBatch::enum("true order by id desc"); - show_batches($batches); + show_batches($batches, PAGE_SIZE, null, null); } page_tail(); } @@ -412,6 +476,51 @@ function handle_retire_batch($user) { page_tail(); } +function show_batches_in_state($batches, $state) { + switch ($state) { + case BATCH_STATE_IN_PROGRESS: + page_head("Batches in progress"); + show_in_progress($batches, 0, null, null); + break; + case BATCH_STATE_COMPLETE: + page_head("Completed batches"); + show_complete($batches, 0, null, null); + break; + case BATCH_STATE_ABORTED: + page_head("Aborted batches"); + show_aborted($batches, 0, null, null); + break; + } + page_tail(); +} + +function handle_show_all($user) { + $userid = get_int("userid"); + $appid = get_int("appid"); + $state = get_int("state"); + if ($userid) { + // user looking at their own batches + // + if ($userid != $user->id) error_page("wrong user"); + $batches = BoincBatch::enum("user_id = $user->id and state=$state order by id desc"); + fill_in_app_and_user_names($batches); + show_batches_in_state($batches, $state); + } else { + // admin looking at batches + // + check_admin_access($user, $appid); + if ($appid) { + $app = BoincApp::lookup_id($app_id); + if (!$app) error_page("no such app"); + $batches = BoincBatch::enum("app_id = $app_id and state=$state order by id desc"); + } else { + $batches = BoincBatch::enum("state=$state order by id desc"); + } + fill_in_app_and_user_names($batches); + show_batches_in_state($batches, $state); + } +} + $user = get_logged_in_user(); $action = get_str('action', true); @@ -425,6 +534,7 @@ case 'query_batch': handle_query_batch($user); break; case 'query_job': handle_query_job($user); break; case 'retire_batch': handle_retire_batch($user); break; case 'retire_batch_confirm': handle_retire_batch_confirm(); break; +case 'show_all': handle_show_all($user); break; default: error_page('no such action'); } diff --git a/lib/cc_config.h b/lib/cc_config.h index 59805980c1..e4263c9403 100644 --- a/lib/cc_config.h +++ b/lib/cc_config.h @@ -30,7 +30,7 @@ #include "proxy_info.h" #include "coproc.h" -class XML_PARSER; +struct XML_PARSER; #define MAX_FILE_XFERS_PER_PROJECT 2 #define MAX_FILE_XFERS 8 diff --git a/lib/parse.cpp b/lib/parse.cpp index b64b337780..824975a717 100644 --- a/lib/parse.cpp +++ b/lib/parse.cpp @@ -483,28 +483,6 @@ XML_PARSER::XML_PARSER(MIOFILE* _f) { f = _f; } -// read until find non-whitespace char. -// Return the char in the reference param -// Return true iff reached EOF -// -bool XML_PARSER::scan_nonws(int& first_char) { - char c; - while (1) { - c = f->_getc(); - if (c == EOF) return true; - unsigned char uc = c; - if (isspace(uc)) continue; - first_char = c; - return false; - } -} - -#define XML_PARSE_COMMENT 1 -#define XML_PARSE_EOF 2 -#define XML_PARSE_CDATA 3 -#define XML_PARSE_TAG 4 -#define XML_PARSE_DATA 5 - int XML_PARSER::scan_comment() { char buf[256]; char* p = buf; @@ -544,132 +522,6 @@ int XML_PARSER::scan_cdata(char* buf, int len) { } } -// we just read a <; read until we find a >. -// Given : -// - copy tag (or tag/) to buf -// - copy "attr=val attr=val" to attr_buf -// -// Return either -// XML_PARSE_TAG -// XML_PARSE_COMMENT -// XML_PARSE_EOF -// XML_PARSE_CDATA -// -int XML_PARSER::scan_tag( - char* buf, int _tag_len, char* attr_buf, int attr_len -) { - int c; - char* buf_start = buf; - bool found_space = false; - int tag_len = _tag_len; - - for (int i=0; ; i++) { - c = f->_getc(); - if (c == EOF) return XML_PARSE_EOF; - if (c == '>') { - *buf = 0; - if (attr_buf) *attr_buf = 0; - return XML_PARSE_TAG; - } - if (isspace(c)) { - if (found_space && attr_buf) { - if (--attr_len > 0) { - *attr_buf++ = c; - } - } - found_space = true; - } else if (c == '/') { - if (--tag_len > 0) { - *buf++ = c; - } - } else { - if (found_space) { - if (attr_buf) { - if (--attr_len > 0) { - *attr_buf++ = c; - } - } - } else { - if (--tag_len > 0) { - *buf++ = c; - } - } - } - - // check for comment start - // - if (i==2 && !strncmp(buf_start, "!--", 3)) { - return scan_comment(); - } - if (i==7 && !strncmp(buf_start, "![CDATA[", 8)) { - return scan_cdata(buf_start, tag_len); - } - } -} - -// read and copy text to buf; stop when find a <; -// ungetc() that so we read it again -// Return true iff reached EOF -// -bool XML_PARSER::copy_until_tag(char* buf, int len) { - int c; - while (1) { - c = f->_getc(); - if (c == EOF) return true; - if (c == '<') { - f->_ungetc(c); - *buf = 0; - return false; - } - if (--len > 0) { - *buf++ = c; - } - } -} - -// Scan something, either tag or text. -// Strip whitespace at start and end. -// Return true iff reached EOF -// -int XML_PARSER::get_aux(char* buf, int len, char* attr_buf, int attr_len) { - bool eof; - int c, retval; - - while (1) { - eof = scan_nonws(c); - if (eof) return XML_PARSE_EOF; - if (c == '<') { - retval = scan_tag(buf, len, attr_buf, attr_len); - if (retval == XML_PARSE_EOF) return retval; - if (retval == XML_PARSE_COMMENT) continue; - } else { - buf[0] = c; - eof = copy_until_tag(buf+1, len-1); - if (eof) return XML_PARSE_EOF; - retval = XML_PARSE_DATA; - } - strip_whitespace(buf); - return retval; - } -} - -bool XML_PARSER::get( - char* buf, int len, bool& _is_tag, char* attr_buf, int attr_len -) { - switch (get_aux(buf, len, attr_buf, attr_len)) { - case XML_PARSE_EOF: return true; - case XML_PARSE_TAG: - _is_tag = true; - break; - case XML_PARSE_DATA: - case XML_PARSE_CDATA: - default: - _is_tag = false; - break; - } - return false; -} - #define MAX_XML_STRING 262144 // We just parsed "parsed_tag". @@ -932,59 +784,6 @@ bool XML_PARSER::parse_start(const char* start_tag) { return true; } -// copy everything up to (but not including) the given end tag. -// The copied text may include XML tags. -// strips whitespace. -// -int XML_PARSER::element_contents(const char* end_tag, char* buf, int buflen) { - int n=0; - int retval=0; - while (1) { - if (n == buflen-1) { - retval = ERR_XML_PARSE; - break; - } - int c = f->_getc(); - if (c == EOF) { - retval = ERR_XML_PARSE; - break; - } - buf[n++] = c; - buf[n] = 0; - char* p = strstr(buf, end_tag); - if (p) { - *p = 0; - break; - } - } - buf[n] = 0; - strip_whitespace(buf); - return retval; -} - -#if 0 -int XML_PARSER::element_contents(const char* end_tag, string& buf) { - int retval=0; - while (1) { - int c = f->_getc(); - if (c == EOF) { - retval = ERR_XML_PARSE; - break; - } - buf += c; - char* p = strstr(buf.c_str(), end_tag); - if (p) { - int k = strlen(end_tag); - int n = buf.length(); - buf.erase(n-k, k); - break; - } - } - strip_whitespace(buf); - return retval; -} -#endif - // We got an unexpected tag. // If it's an end tag, do nothing. // Otherwise skip until the end tag, if any diff --git a/lib/parse.h b/lib/parse.h index c7d67ff0f0..731e393da3 100644 --- a/lib/parse.h +++ b/lib/parse.h @@ -24,17 +24,20 @@ #include #include "miofile.h" +#include "error_numbers.h" #include "str_util.h" // see parse_test.cpp for example usage of XML_PARSER -class XML_PARSER { - bool scan_nonws(int&); +#define XML_PARSE_COMMENT 1 +#define XML_PARSE_EOF 2 +#define XML_PARSE_CDATA 3 +#define XML_PARSE_TAG 4 +#define XML_PARSE_DATA 5 + +struct XML_PARSER { int scan_comment(); - int scan_tag(char*, int, char* ab=0, int al=0); int scan_cdata(char*, int); - bool copy_until_tag(char*, int); -public: char parsed_tag[4096]; bool is_tag; MIOFILE* f; @@ -42,14 +45,187 @@ public: void init(MIOFILE* mf) { f = mf; } - bool get(char*, int, bool&, char* ab=0, int al=0); + // read and copy text to buf; stop when find a <; + // ungetc() that so we read it again + // Return true iff reached EOF + // + inline bool copy_until_tag(char* buf, int len) { + int c; + while (1) { + c = f->_getc(); + if (c == EOF) return true; + if (c == '<') { + f->_ungetc(c); + *buf = 0; + return false; + } + if (--len > 0) { + *buf++ = c; + } + } + } + + inline bool get( + char* buf, int len, bool& _is_tag, char* attr_buf=0, int attr_len=0 + ) { + switch (get_aux(buf, len, attr_buf, attr_len)) { + case XML_PARSE_EOF: return true; + case XML_PARSE_TAG: + _is_tag = true; + break; + case XML_PARSE_DATA: + case XML_PARSE_CDATA: + default: + _is_tag = false; + break; + } + return false; + } + inline bool get_tag(char* ab=0, int al=0) { return get(parsed_tag, sizeof(parsed_tag), is_tag, ab, al); } inline bool match_tag(const char* tag) { return !strcmp(parsed_tag, tag); } - int get_aux(char* buf, int len, char* attr_buf, int attr_len); + + // read until find non-whitespace char. + // Return the char in the reference param + // Return true iff reached EOF + // + inline bool scan_nonws(int& first_char) { + char c; + while (1) { + c = f->_getc(); + if (c == EOF) return true; + unsigned char uc = c; + if (isspace(uc)) continue; + first_char = c; + return false; + } + } + + // Scan something, either tag or text. + // Strip whitespace at start and end. + // Return true iff reached EOF + // + inline int get_aux( + char* buf, int len, char* attr_buf, int attr_len + ) { + bool eof; + int c, retval; + + while (1) { + eof = scan_nonws(c); + if (eof) return XML_PARSE_EOF; + if (c == '<') { + retval = scan_tag(buf, len, attr_buf, attr_len); + if (retval == XML_PARSE_EOF) return retval; + if (retval == XML_PARSE_COMMENT) continue; + } else { + buf[0] = c; + eof = copy_until_tag(buf+1, len-1); + if (eof) return XML_PARSE_EOF; + retval = XML_PARSE_DATA; + } + strip_whitespace(buf); + return retval; + } + } + + // we just read a <; read until we find a >. + // Given : + // - copy tag (or tag/) to buf + // - copy "attr=val attr=val" to attr_buf + // + // Return either + // XML_PARSE_TAG + // XML_PARSE_COMMENT + // XML_PARSE_EOF + // XML_PARSE_CDATA + // + inline int scan_tag( + char* buf, int _tag_len, char* attr_buf=0, int attr_len=0 + ) { + int c; + char* buf_start = buf; + bool found_space = false; + int tag_len = _tag_len; + + for (int i=0; ; i++) { + c = f->_getc(); + if (c == EOF) return XML_PARSE_EOF; + if (c == '>') { + *buf = 0; + if (attr_buf) *attr_buf = 0; + return XML_PARSE_TAG; + } + if (isspace(c)) { + if (found_space && attr_buf) { + if (--attr_len > 0) { + *attr_buf++ = c; + } + } + found_space = true; + } else if (c == '/') { + if (--tag_len > 0) { + *buf++ = c; + } + } else { + if (found_space) { + if (attr_buf) { + if (--attr_len > 0) { + *attr_buf++ = c; + } + } + } else { + if (--tag_len > 0) { + *buf++ = c; + } + } + } + + // check for comment start + // + if (i==2 && !strncmp(buf_start, "!--", 3)) { + return scan_comment(); + } + if (i==7 && !strncmp(buf_start, "![CDATA[", 8)) { + return scan_cdata(buf_start, tag_len); + } + } + } + + // copy everything up to (but not including) the given end tag. + // The copied text may include XML tags. + // strips whitespace. + // + inline int element_contents(const char* end_tag, char* buf, int buflen) { + int n=0; + int retval=0; + while (1) { + if (n == buflen-1) { + retval = ERR_XML_PARSE; + break; + } + int c = f->_getc(); + if (c == EOF) { + retval = ERR_XML_PARSE; + break; + } + buf[n++] = c; + buf[n] = 0; + char* p = strstr(buf, end_tag); + if (p) { + *p = 0; + break; + } + } + buf[n] = 0; + strip_whitespace(buf); + return retval; + } + bool parse_start(const char*); bool parse_str(const char*, char*, int); bool parse_string(const char*, std::string&); @@ -58,8 +234,6 @@ public: bool parse_ulong(const char*, unsigned long&); bool parse_ulonglong(const char*, unsigned long long&); bool parse_bool(const char*, bool&); - int element_contents(const char*, char*, int); - int element_contents(const char*, std::string&); int copy_element(std::string&); void skip_unexpected(const char*, bool verbose, const char*); void skip_unexpected(bool verbose=false, const char* msg="") { diff --git a/lib/proxy_info.h b/lib/proxy_info.h index 53a2210291..3525c7cb0d 100644 --- a/lib/proxy_info.h +++ b/lib/proxy_info.h @@ -18,7 +18,7 @@ #ifndef _PROXY_INFO_ #define _PROXY_INFO_ -class XML_PARSER; +struct XML_PARSER; class MIOFILE; // info on whether HTTP requests need to go through a proxy diff --git a/sched/sched_main.cpp b/sched/sched_main.cpp index 90bd725250..3ce90524ab 100644 --- a/sched/sched_main.cpp +++ b/sched/sched_main.cpp @@ -349,6 +349,20 @@ inline static const char* get_remote_addr() { return r ? r : "?.?.?.?"; } +#if 0 // performance test for XML parsing (use a large request) +int main(int, char**) { + SCHEDULER_REQUEST sreq; + FILE* f = fopen("req", "r"); + MIOFILE mf; + XML_PARSER xp(&mf); + mf.init_file(f); + for (int i=0; i<10; i++) { + sreq.parse(xp); + fseek(f, 0, SEEK_SET); + } +} +#else + #if !defined(PLAN_CLASS_TEST) int main(int argc, char** argv) { @@ -651,6 +665,7 @@ done: } } #endif +#endif // the following stuff is here because if you put it in sched_limit.cpp // you get "ssp undefined" in programs other than cgi