| 1 | #include "mysql_version.h" |
| 2 | #include "my_global.h" |
| 3 | #ifdef HAVE_RESPONSE_TIME_DISTRIBUTION |
| 4 | #include "mysql_com.h" |
| 5 | #include "rpl_tblmap.h" |
| 6 | #include "table.h" |
| 7 | #include "field.h" |
| 8 | #include "sql_show.h" |
| 9 | #include "query_response_time.h" |
| 10 | |
| 11 | #define TIME_STRING_POSITIVE_POWER_LENGTH QRT_TIME_STRING_POSITIVE_POWER_LENGTH |
| 12 | #define TIME_STRING_NEGATIVE_POWER_LENGTH 6 |
| 13 | #define TOTAL_STRING_POSITIVE_POWER_LENGTH QRT_TOTAL_STRING_POSITIVE_POWER_LENGTH |
| 14 | #define TOTAL_STRING_NEGATIVE_POWER_LENGTH 6 |
| 15 | #define MINIMUM_BASE 2 |
| 16 | #define MAXIMUM_BASE QRT_MAXIMUM_BASE |
| 17 | #define POSITIVE_POWER_FILLER QRT_POSITIVE_POWER_FILLER |
| 18 | #define NEGATIVE_POWER_FILLER QRT_NEGATIVE_POWER_FILLER |
| 19 | #define TIME_OVERFLOW QRT_TIME_OVERFLOW |
| 20 | #define DEFAULT_BASE QRT_DEFAULT_BASE |
| 21 | |
| 22 | #define do_xstr(s) do_str(s) |
| 23 | #define do_str(s) #s |
| 24 | #define do_format(filler,width) "%" filler width "lld" |
| 25 | /* |
| 26 | Format strings for snprintf. Generate from: |
| 27 | POSITIVE_POWER_FILLER and TIME_STRING_POSITIVE_POWER_LENGTH |
| 28 | NEFATIVE_POWER_FILLER and TIME_STRING_NEGATIVE_POWER_LENGTH |
| 29 | */ |
| 30 | #define TIME_STRING_POSITIVE_POWER_FORMAT do_format(POSITIVE_POWER_FILLER,do_xstr(TIME_STRING_POSITIVE_POWER_LENGTH)) |
| 31 | #define TIME_STRING_NEGATIVE_POWER_FORMAT do_format(NEGATIVE_POWER_FILLER,do_xstr(TIME_STRING_NEGATIVE_POWER_LENGTH)) |
| 32 | #define TIME_STRING_FORMAT TIME_STRING_POSITIVE_POWER_FORMAT "." TIME_STRING_NEGATIVE_POWER_FORMAT |
| 33 | |
| 34 | #define TOTAL_STRING_POSITIVE_POWER_FORMAT do_format(POSITIVE_POWER_FILLER,do_xstr(TOTAL_STRING_POSITIVE_POWER_LENGTH)) |
| 35 | #define TOTAL_STRING_NEGATIVE_POWER_FORMAT do_format(NEGATIVE_POWER_FILLER,do_xstr(TOTAL_STRING_NEGATIVE_POWER_LENGTH)) |
| 36 | #define TOTAL_STRING_FORMAT TOTAL_STRING_POSITIVE_POWER_FORMAT "." TOTAL_STRING_NEGATIVE_POWER_FORMAT |
| 37 | |
| 38 | #define TIME_STRING_LENGTH QRT_TIME_STRING_LENGTH |
| 39 | #define TIME_STRING_BUFFER_LENGTH (TIME_STRING_LENGTH + 1 /* '\0' */) |
| 40 | |
| 41 | #define TOTAL_STRING_LENGTH QRT_TOTAL_STRING_LENGTH |
| 42 | #define TOTAL_STRING_BUFFER_LENGTH (TOTAL_STRING_LENGTH + 1 /* '\0' */) |
| 43 | |
| 44 | /* |
| 45 | Calculate length of "log linear" |
| 46 | 1) |
| 47 | (MINIMUM_BASE ^ result) <= (10 ^ STRING_POWER_LENGTH) < (MINIMUM_BASE ^ (result + 1)) |
| 48 | |
| 49 | 2) |
| 50 | (MINIMUM_BASE ^ result) <= (10 ^ STRING_POWER_LENGTH) |
| 51 | and |
| 52 | (MINIMUM_BASE ^ (result + 1)) > (10 ^ STRING_POWER_LENGTH) |
| 53 | |
| 54 | 3) |
| 55 | result <= LOG(MINIMUM_BASE, 10 ^ STRING_POWER_LENGTH)= STRING_POWER_LENGTH * LOG(MINIMUM_BASE,10) |
| 56 | result + 1 > LOG(MINIMUM_BASE, 10 ^ STRING_POWER_LENGTH)= STRING_POWER_LENGTH * LOG(MINIMUM_BASE,10) |
| 57 | |
| 58 | 4) STRING_POWER_LENGTH * LOG(MINIMUM_BASE,10) - 1 < result <= STRING_POWER_LENGTH * LOG(MINIMUM_BASE,10) |
| 59 | |
| 60 | MINIMUM_BASE= 2 always, LOG(MINIMUM_BASE,10)= 3.3219280948873626, result= (int)3.3219280948873626 * STRING_POWER_LENGTH |
| 61 | |
| 62 | Last counter always use for time overflow |
| 63 | */ |
| 64 | #define POSITIVE_POWER_COUNT ((int)(3.32192809 * TIME_STRING_POSITIVE_POWER_LENGTH)) |
| 65 | #define NEGATIVE_POWER_COUNT ((int)(3.32192809 * TIME_STRING_NEGATIVE_POWER_LENGTH)) |
| 66 | #define OVERALL_POWER_COUNT (NEGATIVE_POWER_COUNT + 1 + POSITIVE_POWER_COUNT) |
| 67 | |
| 68 | #define MILLION ((unsigned long)1000 * 1000) |
| 69 | |
| 70 | namespace query_response_time |
| 71 | { |
| 72 | |
| 73 | class utility |
| 74 | { |
| 75 | public: |
| 76 | utility() : m_base(0) |
| 77 | { |
| 78 | m_max_dec_value= MILLION; |
| 79 | for(int i= 0; TIME_STRING_POSITIVE_POWER_LENGTH > i; ++i) |
| 80 | m_max_dec_value *= 10; |
| 81 | setup(DEFAULT_BASE); |
| 82 | } |
| 83 | public: |
| 84 | uint base() const { return m_base; } |
| 85 | uint negative_count() const { return m_negative_count; } |
| 86 | uint positive_count() const { return m_positive_count; } |
| 87 | uint bound_count() const { return m_bound_count; } |
| 88 | ulonglong max_dec_value() const { return m_max_dec_value; } |
| 89 | ulonglong bound(uint index) const { return m_bound[ index ]; } |
| 90 | public: |
| 91 | void setup(uint base) |
| 92 | { |
| 93 | if(base != m_base) |
| 94 | { |
| 95 | m_base= base; |
| 96 | |
| 97 | const ulonglong million= 1000 * 1000; |
| 98 | ulonglong value= million; |
| 99 | m_negative_count= 0; |
| 100 | while(value > 0) |
| 101 | { |
| 102 | m_negative_count += 1; |
| 103 | value /= m_base; |
| 104 | } |
| 105 | m_negative_count -= 1; |
| 106 | |
| 107 | value= million; |
| 108 | m_positive_count= 0; |
| 109 | while(value < m_max_dec_value) |
| 110 | { |
| 111 | m_positive_count += 1; |
| 112 | value *= m_base; |
| 113 | } |
| 114 | m_bound_count= m_negative_count + m_positive_count; |
| 115 | |
| 116 | value= million; |
| 117 | for(uint i= 0; i < m_negative_count; ++i) |
| 118 | { |
| 119 | value /= m_base; |
| 120 | m_bound[m_negative_count - i - 1]= value; |
| 121 | } |
| 122 | value= million; |
| 123 | for(uint i= 0; i < m_positive_count; ++i) |
| 124 | { |
| 125 | m_bound[m_negative_count + i]= value; |
| 126 | value *= m_base; |
| 127 | } |
| 128 | } |
| 129 | } |
| 130 | private: |
| 131 | uint m_base; |
| 132 | uint m_negative_count; |
| 133 | uint m_positive_count; |
| 134 | uint m_bound_count; |
| 135 | ulonglong m_max_dec_value; /* for TIME_STRING_POSITIVE_POWER_LENGTH=7 is 10000000 */ |
| 136 | ulonglong m_bound[OVERALL_POWER_COUNT]; |
| 137 | }; |
| 138 | |
| 139 | static |
| 140 | void print_time(char* buffer, std::size_t buffer_size, const char* format, |
| 141 | uint64 value) |
| 142 | { |
| 143 | ulonglong second= (value / MILLION); |
| 144 | ulonglong microsecond= (value % MILLION); |
| 145 | my_snprintf(buffer, buffer_size, format, second, microsecond); |
| 146 | } |
| 147 | |
| 148 | class time_collector |
| 149 | { |
| 150 | public: |
| 151 | time_collector(utility& u) : m_utility(&u) |
| 152 | { } |
| 153 | ~time_collector() |
| 154 | { } |
| 155 | uint32 count(uint index) |
| 156 | { |
| 157 | return my_atomic_load32((int32*)&m_count[index]); |
| 158 | } |
| 159 | uint64 total(uint index) |
| 160 | { |
| 161 | return my_atomic_load64((int64*)&m_total[index]); |
| 162 | } |
| 163 | public: |
| 164 | void flush() |
| 165 | { |
| 166 | memset((void*)&m_count,0,sizeof(m_count)); |
| 167 | memset((void*)&m_total,0,sizeof(m_total)); |
| 168 | } |
| 169 | void collect(uint64 time) |
| 170 | { |
| 171 | int i= 0; |
| 172 | for(int count= m_utility->bound_count(); count > i; ++i) |
| 173 | { |
| 174 | if(m_utility->bound(i) > time) |
| 175 | { |
| 176 | my_atomic_add32((int32*)(&m_count[i]), 1); |
| 177 | my_atomic_add64((int64*)(&m_total[i]), time); |
| 178 | break; |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | private: |
| 183 | utility* m_utility; |
| 184 | uint32 m_count[OVERALL_POWER_COUNT + 1]; |
| 185 | uint64 m_total[OVERALL_POWER_COUNT + 1]; |
| 186 | }; |
| 187 | |
| 188 | class collector |
| 189 | { |
| 190 | public: |
| 191 | collector() : m_time(m_utility) |
| 192 | { |
| 193 | m_utility.setup(DEFAULT_BASE); |
| 194 | m_time.flush(); |
| 195 | } |
| 196 | public: |
| 197 | void flush() |
| 198 | { |
| 199 | m_utility.setup(opt_query_response_time_range_base); |
| 200 | m_time.flush(); |
| 201 | } |
| 202 | int fill(THD* thd, TABLE_LIST *tables, COND *cond) |
| 203 | { |
| 204 | DBUG_ENTER("fill_schema_query_response_time" ); |
| 205 | TABLE *table= static_cast<TABLE*>(tables->table); |
| 206 | Field **fields= table->field; |
| 207 | for(uint i= 0, count= bound_count() + 1 /* with overflow */; count > i; ++i) |
| 208 | { |
| 209 | char time[TIME_STRING_BUFFER_LENGTH]; |
| 210 | char total[TOTAL_STRING_BUFFER_LENGTH]; |
| 211 | if(i == bound_count()) |
| 212 | { |
| 213 | assert(sizeof(TIME_OVERFLOW) <= TIME_STRING_BUFFER_LENGTH); |
| 214 | assert(sizeof(TIME_OVERFLOW) <= TOTAL_STRING_BUFFER_LENGTH); |
| 215 | memcpy(time,TIME_OVERFLOW,sizeof(TIME_OVERFLOW)); |
| 216 | memcpy(total,TIME_OVERFLOW,sizeof(TIME_OVERFLOW)); |
| 217 | } |
| 218 | else |
| 219 | { |
| 220 | print_time(time, sizeof(time), TIME_STRING_FORMAT, this->bound(i)); |
| 221 | print_time(total, sizeof(total), TOTAL_STRING_FORMAT, this->total(i)); |
| 222 | } |
| 223 | fields[0]->store(time,strlen(time),system_charset_info); |
| 224 | fields[1]->store((longlong)this->count(i),true); |
| 225 | fields[2]->store(total,strlen(total),system_charset_info); |
| 226 | if (schema_table_store_record(thd, table)) |
| 227 | { |
| 228 | DBUG_RETURN(1); |
| 229 | } |
| 230 | } |
| 231 | DBUG_RETURN(0); |
| 232 | } |
| 233 | void collect(ulonglong time) |
| 234 | { |
| 235 | m_time.collect(time); |
| 236 | } |
| 237 | uint bound_count() const |
| 238 | { |
| 239 | return m_utility.bound_count(); |
| 240 | } |
| 241 | ulonglong bound(uint index) |
| 242 | { |
| 243 | return m_utility.bound(index); |
| 244 | } |
| 245 | ulonglong count(uint index) |
| 246 | { |
| 247 | return m_time.count(index); |
| 248 | } |
| 249 | ulonglong total(uint index) |
| 250 | { |
| 251 | return m_time.total(index); |
| 252 | } |
| 253 | private: |
| 254 | utility m_utility; |
| 255 | time_collector m_time; |
| 256 | }; |
| 257 | |
| 258 | static collector g_collector; |
| 259 | |
| 260 | } // namespace query_response_time |
| 261 | |
| 262 | void query_response_time_init() |
| 263 | { |
| 264 | } |
| 265 | |
| 266 | void query_response_time_free() |
| 267 | { |
| 268 | query_response_time::g_collector.flush(); |
| 269 | } |
| 270 | |
| 271 | int query_response_time_flush() |
| 272 | { |
| 273 | query_response_time::g_collector.flush(); |
| 274 | return 0; |
| 275 | } |
| 276 | void query_response_time_collect(ulonglong query_time) |
| 277 | { |
| 278 | query_response_time::g_collector.collect(query_time); |
| 279 | } |
| 280 | |
| 281 | int query_response_time_fill(THD* thd, TABLE_LIST *tables, COND *cond) |
| 282 | { |
| 283 | return query_response_time::g_collector.fill(thd,tables,cond); |
| 284 | } |
| 285 | #endif // HAVE_RESPONSE_TIME_DISTRIBUTION |
| 286 | |