diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 1d5751a00a..0caba0600f 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -428,6 +428,9 @@ int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name) general_on_error(); END_ERROR; + BURP_verbose(369); + // msg 369 total statistics + FINISH ON_ERROR general_on_error(); diff --git a/src/burp/burp.cpp b/src/burp/burp.cpp index c0abe3e4a2..2ec496d19f 100644 --- a/src/burp/burp.cpp +++ b/src/burp/burp.cpp @@ -112,13 +112,29 @@ static gbak_action open_files(const TEXT *, const TEXT**, bool, USHORT, static int svc_api_gbak(Firebird::UtilSvc*, const Switches& switches); static void burp_output(bool err, const SCHAR*, ...) ATTRIBUTE_FORMAT(2,3); static void burp_usage(const Switches& switches); -static Switches::in_sw_tab_t* findSwitchOrThrow(Switches& switches, Firebird::string& sw); +static Switches::in_sw_tab_t* findSwitchOrThrow(Firebird::UtilSvc*, Switches& switches, Firebird::string& sw); // fil.fil_length is FB_UINT64 const ULONG KBYTE = 1024; const ULONG MBYTE = KBYTE * KBYTE; const ULONG GBYTE = MBYTE * KBYTE; +// Must be consistent with enum BurpGlobals::StatCounter +struct StatFormat +{ + const char* header; + const char* format; + char width; +}; +static const char* STAT_CHARS = "TDRW"; +static const StatFormat STAT_FORMATS[] = +{ + {"time", "%4lu.%03u ", 9}, + {"delta", "%2lu.%03u ", 7}, + {"reads", "%6"UQUADFORMAT" ", 7}, + {"writes", "%6"UQUADFORMAT" ", 7} +}; + int BURP_main(Firebird::UtilSvc* uSvc) { @@ -237,15 +253,13 @@ static int svc_api_gbak(Firebird::UtilSvc* uSvc, const Switches& switches) BURP_error(333, true, SafeArg() << inSw->in_sw_name << verbint_val); if (itr >= argc - 1) BURP_error(326, true); // verbose interval value parameter missing - argv[itr++] = 0; - verbint_val = get_number(argv[itr]); + verbint_val = get_number(argv[++itr]); if (verbint_val < MIN_VERBOSE_INTERVAL) { // verbose interval value cannot be smaller than @1 BURP_error(327, true, SafeArg() << MIN_VERBOSE_INTERVAL); } flag_verbint = true; - argv[itr] = 0; break; #ifdef TRUSTED_AUTH case IN_SW_BURP_TRUSTED_AUTH: // use trusted auth @@ -399,7 +413,7 @@ static int svc_api_gbak(Firebird::UtilSvc* uSvc, const Switches& switches) } -static Switches::in_sw_tab_t* findSwitchOrThrow(Switches& switches, Firebird::string& sw) +static Switches::in_sw_tab_t* findSwitchOrThrow(Firebird::UtilSvc* uSvc, Switches& switches, Firebird::string& sw) { /************************************** * @@ -420,11 +434,19 @@ static Switches::in_sw_tab_t* findSwitchOrThrow(Switches& switches, Firebird::st if (invalid) { - BURP_print(true, 137, sw.c_str()); - // msg 137 unknown switch %s - burp_usage(switches); - BURP_error(1, true); - // msg 1: found unknown switch + if (! uSvc->isService()) + { + BURP_print(true, 137, sw.c_str()); + // msg 137 unknown switch %s + burp_usage(switches); + BURP_error(1, true); + // msg 1: found unknown switch + } + else + { + BURP_error(137, true, sw.c_str()); + // msg 137 unknown switch %s + } } return NULL; @@ -538,7 +560,7 @@ int gbak(Firebird::UtilSvc* uSvc) str = none; } - Switches::in_sw_tab_t* const in_sw_tab = findSwitchOrThrow(switches, str); + Switches::in_sw_tab_t* const in_sw_tab = findSwitchOrThrow(uSvc, switches, str); fb_assert(in_sw_tab); //in_sw_tab->in_sw_state = true; It's not enough with switches that have multiple spellings switches.activate(in_sw_tab->in_sw); @@ -813,6 +835,28 @@ int gbak(Firebird::UtilSvc* uSvc) BURP_error(334, true, SafeArg() << in_sw_tab->in_sw_name); tdgbl->gbl_sw_verbose = true; break; + case IN_SW_BURP_STATS: + if (tdgbl->gbl_stat_flags) + BURP_error(334, true, SafeArg() << in_sw_tab->in_sw_name); + if (++itr >= argc) + BURP_error(366, true); // statistics parameter missing + + { + const char* perf_val = argv[itr]; + const char* c = perf_val; + int len = strlen(STAT_CHARS); + for (; *c && len; c++, len--) + { + const char* pos = strchr(STAT_CHARS, toupper(*c)); + if (!pos) + BURP_error(367, true, SafeArg() << *c); // wrong char "@1" at statistics parameter + + tdgbl->gbl_stat_flags |= 1 << (pos - STAT_CHARS); + } + if (*c) + BURP_error(368, true); // 'too many chars at statistics parameter' + } + break; case IN_SW_BURP_CO: if (tdgbl->gbl_sw_convert_ext_tables) BURP_error(334, true, SafeArg() << in_sw_tab->in_sw_name); @@ -1628,7 +1672,12 @@ void BURP_verbose(USHORT number, const SafeArg& arg) BurpGlobals* tdgbl = BurpGlobals::getSpecific(); if (tdgbl->gbl_sw_verbose) - BURP_print(false, number, arg); + { + tdgbl->print_stats_header(); + BURP_msg_partial(false, 169); // msg 169: gbak: + tdgbl->print_stats(number); + BURP_msg_put(false, number, arg); + } else burp_output(false, "%s", ""); } @@ -1651,7 +1700,12 @@ void BURP_verbose(USHORT number, const char* str) BurpGlobals* tdgbl = BurpGlobals::getSpecific(); if (tdgbl->gbl_sw_verbose) - BURP_print(false, number, str); + { + tdgbl->print_stats_header(); + BURP_msg_partial(false, 169); // msg 169: gbak: + tdgbl->print_stats(number); + BURP_msg_put(false, number, SafeArg() << str); + } else burp_output(false, "%s", ""); } @@ -2399,6 +2453,123 @@ bool BurpGlobals::skipRelation(const char* name) return skipDataMatcher->result(); } +void BurpGlobals::read_stats(SINT64* stats) +{ + if (!db_handle) + return; + + const char info[] = + { + isc_info_reads, + isc_info_writes + }; + + ISC_STATUS_ARRAY status = {0}; + char buffer[sizeof(info) * (1 + 2 + 8) + 2]; + + isc_database_info(status, &db_handle, sizeof(info), info, sizeof(buffer), buffer); + + char *p = buffer, *const e = buffer + sizeof(buffer); + while (p < e) + { + int flag = -1; + switch (*p) + { + case isc_info_reads: + flag = READS; + break; + + case isc_info_writes: + flag = WRITES; + break; + + case isc_info_end: + default: + p = e; + } + + if (flag != -1) + { + const int len = isc_vax_integer(p + 1, 2); + stats[flag] = isc_portable_integer((ISC_UCHAR*)p + 1 + 2, len); + p += len + 3; + } + } +} + +void BurpGlobals::print_stats(USHORT number) +{ + if (!gbl_stat_flags || gbl_stat_done) + return; + + const bool total = (number == 369); + // msg 369 total statistics + + burp_output(false, " "); + + const int time_mask = (1 << TIME_TOTAL) | (1 << TIME_DELTA); + if (gbl_stat_flags & time_mask) + { + const SINT64 t0 = fb_utils::query_performance_counter(); + const SINT64 freq_ms = fb_utils::query_performance_frequency() / 1000; + + if (gbl_stat_flags & (1 << TIME_TOTAL)) + { + SINT64 t1 = (t0 - gbl_stats[TIME_TOTAL]) / freq_ms; + burp_output(false, STAT_FORMATS[TIME_TOTAL].format, (int)(t1 / 1000), (int)(t1 % 1000)); + } + + if (gbl_stat_flags & (1 << TIME_DELTA)) + { + SINT64 t2 = (t0 - gbl_stats[TIME_DELTA]) / freq_ms; + burp_output(false, STAT_FORMATS[TIME_DELTA].format, (int)(t2 / 1000), (int)(t2 % 1000)); + + gbl_stats[TIME_DELTA] = t0; + } + } + + SINT64 cur_stats[LAST_COUNTER] = {0}; + if((gbl_stat_flags & ~time_mask) && !gbl_stat_done) + read_stats(cur_stats); + + for (int i = READS; i < LAST_COUNTER; i++) + { + if (gbl_stat_flags & (1 << i)) + { + SINT64 val = 0; + if (total || gbl_stat_done) + val = cur_stats[i]; + else + val = cur_stats[i] - gbl_stats[i]; + + gbl_stats[i] = cur_stats[i]; + + burp_output(false, STAT_FORMATS[i].format, val); + } + } + + if (total) + gbl_stat_done = true; +} + +void BurpGlobals::print_stats_header() +{ + if (gbl_stat_header || !gbl_stat_flags) + return; + + gbl_stat_header = true; + + BURP_msg_partial(false, 169); // msg 169: gbak: + burp_output(false, " "); + + for (int i = 0; i < LAST_COUNTER; i++) + { + if (gbl_stat_flags & (1 << i)) + burp_output(false, "%-*s", STAT_FORMATS[i].width, STAT_FORMATS[i].header); + } + burp_output(false, "\n"); +} + UnicodeCollationHolder::UnicodeCollationHolder(MemoryPool& pool) { cs = FB_NEW(pool) charset; diff --git a/src/burp/burp.h b/src/burp/burp.h index 811bfbeb8b..89dcdffbd3 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -922,6 +922,12 @@ public: &veryEnd - reinterpret_cast(&gbl_database_file_name)); memset(status_vector, 0, sizeof(status_vector)); + gbl_stat_flags = 0; + gbl_stat_header = false; + gbl_stat_done = false; + memset(gbl_stats, 0, sizeof(gbl_stats)); + gbl_stats[TIME_TOTAL] = gbl_stats[TIME_DELTA] = fb_utils::query_performance_counter(); + // normal code follows exit_code = FINI_ERROR; // prevent FINI_OK in case of unknown error thrown // would be set to FINI_OK (==0) in exit_local @@ -1087,6 +1093,17 @@ public: public: Firebird::string toSystem(const Firebird::PathName& from); + + enum StatCounter { TIME_TOTAL = 0, TIME_DELTA, READS, WRITES, LAST_COUNTER}; + + void read_stats(SINT64* stats); + void print_stats(USHORT number); + void print_stats_header(); + + int gbl_stat_flags; // bitmask, bit numbers see at enum StatCounter + bool gbl_stat_header; // true, if stats header was printed + bool gbl_stat_done; // true, if main process is done, stop to collect db-level stats + SINT64 gbl_stats[LAST_COUNTER]; }; // CVC: This aux routine declared here to not force inclusion of burp.h with burp_proto.h diff --git a/src/burp/burpswi.h b/src/burp/burpswi.h index cc4b244b9b..2f673abfbe 100644 --- a/src/burp/burpswi.h +++ b/src/burp/burpswi.h @@ -89,6 +89,7 @@ const int IN_SW_BURP_FIX_FSS_METADATA = 44; // fix unicode_fss metadata const int IN_SW_BURP_FETCHPASS = 45; // fetch default password from file to use on attach const int IN_SW_BURP_VERBINT = 46; // verbose but with specific interval +const int IN_SW_BURP_STATS = 47; // print statistics /**************************************************************************/ // used 0BCDEFGILMNOPRSTUVYZ available AHJQWX @@ -165,6 +166,16 @@ static const Switches::in_sw_tab_t reference_burp_in_sw_table[] = // msg 277: @1SE(RVICE) use services manager {IN_SW_BURP_SKIP_DATA, isc_spb_res_skip_data, "SKIP_DATA", 0, 0, 0, false, 355, 6, NULL, boGeneral}, // msg 355: @1SKIP_DATA skip data for table + {IN_SW_BURP_STATS, isc_spb_bkp_stat, "STATISTICS", 0, 0, 0, false, 361, 2, NULL, boGeneral}, + // msg 361: @1ST(ATISTICS) TDRW show statistics: + {-1, 0, " ", 0, 0, 0, false, 362, 0, NULL, boGeneral}, + // msg 362: T time from start + {-1, 0, " ", 0, 0, 0, false, 363, 0, NULL, boGeneral}, + // msg 363: D delta time + {-1, 0, " ", 0, 0, 0, false, 364, 0, NULL, boGeneral}, + // msg 364: R page reads + {-1, 0, " ", 0, 0, 0, false, 365, 0, NULL, boGeneral}, + // msg 365: W page writes {IN_SW_BURP_T, 0, "TRANSPORTABLE", 0, 0, 0, false, 175, 1, NULL, boBackup}, // msg 175: @1TRANSPORTABLE transportable backup -- data in XDR format #ifdef TRUSTED_AUTH diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 4c53b21264..00c7fa90c9 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -536,6 +536,9 @@ int RESTORE_restore (const TEXT* file_name, const TEXT* database_name) //FB_UINT64 cumul_count = MVOL_fini_read(); + BURP_verbose(369); + // msg 369 total statistics + // Close database before we attach to it again. FINISH ON_ERROR diff --git a/src/common/classes/ClumpletReader.cpp b/src/common/classes/ClumpletReader.cpp index 8e29724fbd..003dcae650 100644 --- a/src/common/classes/ClumpletReader.cpp +++ b/src/common/classes/ClumpletReader.cpp @@ -331,6 +331,7 @@ ClumpletReader::ClumpletType ClumpletReader::getClumpletType(UCHAR tag) const case isc_spb_dbname: case isc_spb_res_fix_fss_data: case isc_spb_res_fix_fss_metadata: + case isc_spb_bkp_stat: return StringSpb; case isc_spb_bkp_factor: case isc_spb_bkp_length: diff --git a/src/include/consts_pub.h b/src/include/consts_pub.h index 683a8736b5..ed22593c03 100644 --- a/src/include/consts_pub.h +++ b/src/include/consts_pub.h @@ -383,6 +383,7 @@ #define isc_spb_bkp_factor 6 #define isc_spb_bkp_length 7 #define isc_spb_bkp_skip_data 8 +#define isc_spb_bkp_stat 15 #define isc_spb_bkp_ignore_checksums 0x01 #define isc_spb_bkp_ignore_limbo 0x02 #define isc_spb_bkp_metadata_only 0x04 @@ -489,6 +490,7 @@ #define isc_spb_res_access_mode 12 #define isc_spb_res_fix_fss_data 13 #define isc_spb_res_fix_fss_metadata 14 +#define isc_spb_res_stat isc_spb_bkp_stat #define isc_spb_res_metadata_only isc_spb_bkp_metadata_only #define isc_spb_res_deactivate_idx 0x0100 #define isc_spb_res_no_shadow 0x0200 diff --git a/src/jrd/svc.cpp b/src/jrd/svc.cpp index 77e5ea6d3b..a5e9672aa0 100644 --- a/src/jrd/svc.cpp +++ b/src/jrd/svc.cpp @@ -136,24 +136,6 @@ namespace { GlobalPtr allServices; // protected by globalServicesMutex volatile bool svcShutdown = false; - void put_status_arg(Arg::StatusVector& status, const MsgFormat::safe_cell& value) - { - using MsgFormat::safe_cell; - - switch (value.type) - { - case safe_cell::at_int64: - case safe_cell::at_uint64: - status << Arg::Num(static_cast(value.i_value)); // May truncate number! - break; - case safe_cell::at_str: - status << value.st_value.s_string; - break; - default: - break; - } - } - void spbVersionError() { ERR_post(Arg::Gds(isc_bad_spb_form) << @@ -506,6 +488,7 @@ void Service::setServiceStatus(const USHORT facility, const USHORT errcode, status << Arg::Gds(ENCODE_ISC_MSG(errcode, facility)); // stuff params + svc_arg_ptr = svc_arg_conv; for (unsigned int loop = 0; loop < args.getCount(); ++loop) { put_status_arg(status, args.getCell(loop)); @@ -514,6 +497,33 @@ void Service::setServiceStatus(const USHORT facility, const USHORT errcode, ERR_post_nothrow(status, &svc_status); } + +void Service::put_status_arg(Arg::StatusVector& status, const MsgFormat::safe_cell& value) +{ + using MsgFormat::safe_cell; + + switch (value.type) + { + case safe_cell::at_int64: + case safe_cell::at_uint64: + status << Arg::Num(static_cast(value.i_value)); // May truncate number! + break; + case safe_cell::at_str: + status << value.st_value.s_string; + break; + case safe_cell::at_char: + svc_arg_ptr[0] = value.c_value; + svc_arg_ptr[1] = 0; + status << svc_arg_ptr; + svc_arg_ptr += 2; + break; + default: + fb_assert(false); + break; + } +} + + void Service::hidePasswd(ArgvType&, int) { // no action @@ -2770,6 +2780,7 @@ bool Service::process_switches(ClumpletReader& spb, string& switches) break; case isc_spb_res_fix_fss_data: case isc_spb_res_fix_fss_metadata: + case isc_spb_bkp_stat: if (!get_action_svc_parameter(spb.getClumpTag(), reference_burp_in_sw_table, switches)) { return false; diff --git a/src/jrd/svc.h b/src/jrd/svc.h index c4bbeb6b1f..df17b057c6 100644 --- a/src/jrd/svc.h +++ b/src/jrd/svc.h @@ -230,6 +230,8 @@ private: // Sends stdin for a service // Returns number of bytes service wants more ULONG put(const UCHAR* buffer, ULONG length); + // Copies argument value to status vector + void put_status_arg(Firebird::Arg::StatusVector& status, const MsgFormat::safe_cell& value); // Increment circular buffer pointer static ULONG add_one(ULONG i); @@ -287,6 +289,8 @@ private: bool svc_do_shutdown; bool svc_shutdown_in_progress; bool svc_timeout; + char svc_arg_conv[MsgFormat::SAFEARG_MAX_ARG * 2]; + char* svc_arg_ptr; Firebird::string svc_username; Firebird::string svc_sql_role; diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index 82cbf1c382..e7ac225fd2 100644 --- a/src/msgs/facilities2.sql +++ b/src/msgs/facilities2.sql @@ -9,7 +9,7 @@ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUM ('2014-04-22 16:39:03', 'DYN', 8, 290) ('1996-11-07 13:39:40', 'INSTALL', 10, 1) ('1996-11-07 13:38:41', 'TEST', 11, 4) -('2014-05-09 01:30:36', 'GBAK', 12, 361) +('2015-07-23 14:20:00', 'GBAK', 12, 370) ('2015-05-04 13:00:00', 'SQLERR', 13, 1044) ('1996-11-07 13:38:42', 'SQLWARN', 14, 613) ('2006-09-10 03:04:31', 'JRD_BUGCHK', 15, 307) diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index 6f5e25c4ae..ce77efb07e 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -2333,6 +2333,15 @@ ERROR: Backup incomplete', NULL, NULL); (NULL, 'update_ownership', 'restore.epp', NULL, 12, 358, NULL, 'updating ownership of packages, procedures and tables', NULL, NULL); (NULL, 'fix_missing_privileges', 'restore.epp', NULL, 12, 359, NULL, 'adding missing privileges', NULL, NULL); (NULL, 'RESTORE_restore', 'restore.epp', NULL, 12, 360, NULL, 'adjusting the ONLINE and FORCED WRITES flags', NULL, NULL); +(NULL, 'burp_usage', 'burp.c', NULL, 12, 361, NULL, ' @1ST(ATISTICS) TDRW show statistics:', NULL, NULL); +(NULL, 'burp_usage', 'burp.c', NULL, 12, 362, NULL, ' T time from start', NULL, NULL); +(NULL, 'burp_usage', 'burp.c', NULL, 12, 363, NULL, ' D delta time', NULL, NULL); +(NULL, 'burp_usage', 'burp.c', NULL, 12, 364, NULL, ' R page reads', NULL, NULL); +(NULL, 'burp_usage', 'burp.c', NULL, 12, 365, NULL, ' W page writes', NULL, NULL); +('gbak_missing_perf', 'api_gbak/gbak', 'burp.cpp', NULL, 12, 366, NULL, 'statistics parameter missing', NULL, NULL); +('gbak_wrong_perf', 'api_gbak/gbak', 'burp.cpp', NULL, 12, 367, NULL, 'wrong char "@1" at statistics parameter', NULL, NULL); +('gbak_too_long_perf', 'api_gbak/gbak', 'burp.cpp', NULL, 12, 368, NULL, 'too many chars at statistics parameter', NULL, NULL); +(NULL, 'api_gbak/gbak', 'burp.cpp', NULL, 12, 369, NULL, 'total statistics', NULL, NULL); -- SQLERR (NULL, NULL, NULL, NULL, 13, 1, NULL, 'Firebird error', NULL, NULL); (NULL, NULL, NULL, NULL, 13, 74, NULL, 'Rollback not performed', NULL, NULL); diff --git a/src/utilities/fbsvcmgr/fbsvcmgr.cpp b/src/utilities/fbsvcmgr/fbsvcmgr.cpp index 82df5caad4..588b504b3e 100644 --- a/src/utilities/fbsvcmgr/fbsvcmgr.cpp +++ b/src/utilities/fbsvcmgr/fbsvcmgr.cpp @@ -362,6 +362,7 @@ const SvcSwitches backupOptions[] = {"bkp_no_triggers", putOption, 0, isc_spb_bkp_no_triggers, 0}, {"verbint", putNumericArgument, 0, isc_spb_verbint, 0}, {"bkp_skip_data", putStringArgument, 0, isc_spb_bkp_skip_data, 0}, + {"bkp_stat", putStringArgument, 0, isc_spb_bkp_stat, 0 }, {0, 0, 0, 0, 0} }; @@ -386,6 +387,7 @@ const SvcSwitches restoreOptions[] = {"res_metadata_only", putOption, 0, isc_spb_res_metadata_only, 0}, {"verbint", putNumericArgument, 0, isc_spb_verbint, 0}, {"res_skip_data", putStringArgument, 0, isc_spb_res_skip_data, 0}, + {"res_stat", putStringArgument, 0, isc_spb_res_stat, 0 }, {0, 0, 0, 0, 0} };