Skip to content

Commit 5832232

Browse files
Volodymyr Verovkinfacebook-github-bot
authored andcommitted
Implemented measuring CPU usage for queries instrumented with query attributes. Added system table QUERY_PERF_COUNTER to output results of measurements.
Summary: Implemented measuring CPU usage for queries instrumented with query attributes. Added system table QUERY_PERF_COUNTER to output results of measurements. Reviewed By: anirbanr-fb Differential Revision: D7479522 fbshipit-source-id: ed64515
1 parent ee761fc commit 5832232

File tree

10 files changed

+253
-21
lines changed

10 files changed

+253
-21
lines changed

client/mysql.cc

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ static int com_quit(String *str,char*),
278278
com_notee(String *str, char*), com_charset(String *str,char*),
279279
com_prompt(String *str, char*), com_delimiter(String *str, char*),
280280
com_warnings(String *str, char*), com_nowarnings(String *str, char*),
281-
com_resetconnection(String *str, char*);
281+
com_resetconnection(String *str, char*), com_attr(String *str,char*);
282282

283283
#ifdef USE_POPEN
284284
static int com_nopager(String *str, char*), com_pager(String *str, char*),
@@ -329,6 +329,7 @@ typedef struct {
329329
} COMMANDS;
330330

331331
static COMMANDS commands[] = {
332+
{ "setattr",'z', com_attr, 1, "Set query attribute."},
332333
{ "?", '?', com_help, 1, "Synonym for `help'." },
333334
{ "clear", 'c', com_clear, 0, "Clear the current input statement."},
334335
{ "connect",'r', com_connect,1,
@@ -4344,6 +4345,29 @@ com_shell(String *buffer MY_ATTRIBUTE((unused)),
43444345
}
43454346
#endif
43464347

4348+
static int
4349+
com_attr(String *buffer MY_ATTRIBUTE((unused)), char *line)
4350+
{
4351+
static const char *delim = " \t";
4352+
char *ptr = nullptr;
4353+
char *buf = strdup(line);
4354+
const char *cmd __attribute__((unused)) = strtok_r(buf, delim, &ptr);
4355+
const char *key = strtok_r(nullptr, delim, &ptr);
4356+
const char *val = strtok_r(nullptr, delim, &ptr);
4357+
4358+
if (!key || !val)
4359+
{
4360+
put_info("Usage: setattr key value", INFO_ERROR);
4361+
free(buf);
4362+
return -1;
4363+
}
4364+
4365+
mysql_options4(&mysql, MYSQL_OPT_QUERY_ATTR_ADD, key, val);
4366+
4367+
free(buf);
4368+
return 0;
4369+
}
4370+
43474371

43484372
static int
43494373
com_print(String *buffer,char *line MY_ATTRIBUTE((unused)))

sql/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ SET(SQL_SHARED_SOURCES
119119
password.c
120120
procedure.cc
121121
protocol.cc
122+
query_tag_perf_counter.cc
122123
records.cc
123124
rpl_handler.cc
124125
scheduler.cc

sql/handler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,7 @@ enum enum_schema_tables
714714
SCH_TRANSACTION_LIST,
715715
SCH_CONNECTION_ATTRIBUTES,
716716
SCH_QUERY_ATTRIBUTES,
717+
SCH_QUERY_PERF_COUNTER,
717718
SCH_PROFILES,
718719
SCH_REFERENTIAL_CONSTRAINTS,
719720
SCH_PROCEDURES,

sql/query_tag_perf_counter.cc

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright 2004-present Facebook. All Rights Reserved.
2+
3+
#include <unordered_map>
4+
#include <mutex>
5+
#include <memory>
6+
#include <deque>
7+
#include <boost/optional.hpp>
8+
#include <boost/lexical_cast.hpp>
9+
#include <boost/property_tree/ptree.hpp>
10+
#include <boost/property_tree/json_parser.hpp>
11+
12+
13+
#include "query_tag_perf_counter.h"
14+
#include "sql_class.h"
15+
#include "sql_show.h"
16+
17+
namespace qutils {
18+
19+
namespace pt = boost::property_tree;
20+
21+
static int64_t timespec_diff(const timespec& start, const timespec& stop);
22+
23+
static std::mutex stats_mutex;
24+
static std::unordered_map<std::string, uint64_t> stats;
25+
26+
query_tag_perf_counter::query_tag_perf_counter(THD* _thd)
27+
: started(false), thd(_thd)
28+
{
29+
if(!thd->query_type.empty() && thd->num_queries > 0)
30+
{
31+
this->started =
32+
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &this->starttime) == 0;
33+
}
34+
}
35+
36+
query_tag_perf_counter::~query_tag_perf_counter()
37+
{
38+
if(!this->started)
39+
return;
40+
struct timespec endtime;
41+
if(clock_gettime(CLOCK_THREAD_CPUTIME_ID, &endtime) != 0)
42+
return;
43+
44+
int64_t cputime = timespec_diff(this->starttime, endtime);
45+
if(cputime < 0)
46+
return; // skip if overflow
47+
48+
assert(thd->num_queries > 0);
49+
int64_t cputime_per_query = cputime / thd->num_queries;
50+
51+
std::lock_guard<std::mutex> lock(stats_mutex);
52+
stats[thd->query_type] += cputime_per_query;
53+
}
54+
55+
int fill_query_tag_perf_counter(THD* thd, TABLE_LIST* tables, Item* cond)
56+
{
57+
std::lock_guard<std::mutex> lock(stats_mutex);
58+
59+
DBUG_ENTER("fill_query_tag_perf_counter");
60+
61+
TABLE* table= tables->table;
62+
for (const auto& row : stats)
63+
{
64+
65+
restore_record(table, s->default_values);
66+
Field **field = table->field;
67+
68+
const std::string& query_type = row.first;
69+
uint64_t cpu_time = row.second;
70+
71+
field[0]->store(query_type.c_str(),
72+
query_type.length(), system_charset_info);
73+
field[1]->store(cpu_time, TRUE);
74+
75+
if (schema_table_store_record(thd, table))
76+
{
77+
DBUG_RETURN(-1);
78+
}
79+
}
80+
DBUG_RETURN(0);
81+
}
82+
83+
const uint query_type_field_length = 254;
84+
85+
ST_FIELD_INFO query_tag_perf_fields_info[]=
86+
{
87+
{"QUERY_TYPE", query_type_field_length, MYSQL_TYPE_STRING,
88+
0, 0, 0, SKIP_OPEN_TABLE},
89+
{"CPU", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG,
90+
0, 0, 0, SKIP_OPEN_TABLE},
91+
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
92+
};
93+
94+
static int64_t timespec_diff(const timespec& start, const timespec& stop)
95+
{
96+
const int64_t sec = stop.tv_sec - start.tv_sec;
97+
const int64_t nsec = stop.tv_nsec - start.tv_nsec;
98+
const int64_t diff = sec * 1000000000LL + nsec;
99+
return diff;
100+
}
101+
} // namespace qutils
102+

sql/query_tag_perf_counter.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2004-present Facebook. All Rights Reserved.
2+
3+
#ifndef QUERY_TAG_PERF_COUNTER_H_
4+
#define QUERY_TAG_PERF_COUNTER_H_
5+
6+
#include <ctime>
7+
#include <string>
8+
9+
class Item;
10+
class THD;
11+
struct TABLE_LIST;
12+
typedef struct st_field_info ST_FIELD_INFO;
13+
14+
namespace qutils {
15+
16+
extern ST_FIELD_INFO query_tag_perf_fields_info[];
17+
extern int
18+
fill_query_tag_perf_counter(THD* thd, TABLE_LIST* tables, Item* cond);
19+
20+
class query_tag_perf_counter {
21+
public:
22+
query_tag_perf_counter(THD *thd);
23+
~query_tag_perf_counter();
24+
private:
25+
struct timespec starttime; // query start timestamp
26+
bool started; // query_info attribute detected
27+
THD* thd; // reference to outer THD
28+
};
29+
30+
} // namespace qutils
31+
32+
#endif /* QUERY_TAG_PERF_COUNTER_H_ */

sql/sql_class.cc

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@
6565

6666
#include <mysql/psi/mysql_statement.h>
6767

68+
#include <boost/optional.hpp>
69+
#include <boost/lexical_cast.hpp>
70+
#include <boost/property_tree/ptree.hpp>
71+
#include <boost/property_tree/json_parser.hpp>
72+
#include <sstream>
73+
6874
#ifdef TARGET_OS_LINUX
6975
#include <sys/syscall.h>
7076
#endif // TARGET_OS_LINUX
@@ -5536,7 +5542,6 @@ bool THD::skip_unique_check()
55365542
return rli_slave && rli_slave->get_skip_unique_check();
55375543
}
55385544

5539-
55405545
/**
55415546
This function selects which session tracker to use. If a Srv_session
55425547
is currently attached to this connection we want to redirect all session
@@ -5548,3 +5553,60 @@ Session_tracker* THD::get_tracker() {
55485553
: &session_tracker;
55495554
}
55505555

5556+
static std::string net_read_str(const char **ptr)
5557+
{
5558+
size_t len = net_field_length((uchar**)ptr);
5559+
const char *str = *ptr;
5560+
*ptr += len;
5561+
return std::string(str, len);
5562+
}
5563+
5564+
void THD::set_query_attrs(const char *attrs, size_t length)
5565+
{
5566+
query_attrs_string = std::string(attrs, length);
5567+
const char *ptr = query_attrs_string.c_str();
5568+
const char *end = ptr + length;
5569+
5570+
query_attrs_map.clear();
5571+
while (ptr < end)
5572+
{
5573+
std::string key = net_read_str(&ptr);
5574+
std::string value = net_read_str(&ptr);
5575+
query_attrs_map[key] = value;
5576+
}
5577+
5578+
}
5579+
5580+
int THD::parse_query_info_attr()
5581+
{
5582+
static const std::string query_info_key = "query_info";
5583+
5584+
auto it = this->query_attrs_map.find(query_info_key);
5585+
if (it == this->query_attrs_map.end())
5586+
return 0;
5587+
ptree root;
5588+
try
5589+
{
5590+
std::istringstream query_info_attr(it->second);
5591+
boost::property_tree::read_json(query_info_attr, root);
5592+
}
5593+
catch(const boost::property_tree::json_parser::json_parser_error& e)
5594+
{
5595+
return -1; // invalid json
5596+
}
5597+
try
5598+
{
5599+
boost::optional<std::string> trace_id =
5600+
root.get_optional<std::string>("traceid");
5601+
if (trace_id)
5602+
this->trace_id = *trace_id;
5603+
this->query_type = root.get<std::string>("query_type");
5604+
this->num_queries = root.get<uint64_t>("num_queries");
5605+
}
5606+
catch(const boost::property_tree::ptree_error& e)
5607+
{
5608+
return -1; // invalid key or value
5609+
}
5610+
return 0;
5611+
5612+
}

sql/sql_class.h

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4634,19 +4634,20 @@ class THD :public MDL_context_owner,
46344634

46354635
void mark_transaction_to_rollback(bool all);
46364636

4637-
void set_query_attrs(const CSET_STRING &arg)
4638-
{
4639-
query_attrs_string= arg;
4640-
set_attrs_map(query_attrs_string.str(), query_attrs_string.length(),
4641-
query_attrs_map, true);
4642-
}
4637+
void set_query_attrs(const char *attrs, size_t length);
4638+
int parse_query_info_attr();
46434639
void reset_query_attrs()
46444640
{
4645-
query_attrs_string= CSET_STRING();
4641+
query_attrs_string.clear();
46464642
query_attrs_map.clear();
46474643
trace_id.clear();
4644+
num_queries = 0;
4645+
query_type.clear();
4646+
}
4647+
inline const char *query_attrs() const
4648+
{
4649+
return query_attrs_string.c_str();
46484650
}
4649-
inline char *query_attrs() const { return query_attrs_string.str(); }
46504651
inline uint32 query_attrs_length() const
46514652
{
46524653
return query_attrs_string.length();
@@ -4725,8 +4726,6 @@ class THD :public MDL_context_owner,
47254726
#endif
47264727

47274728
private:
4728-
CSET_STRING query_attrs_string;
4729-
47304729
/** The current internal error handler for this thread, or NULL. */
47314730
Internal_error_handler *m_internal_handler;
47324731

@@ -4859,6 +4858,9 @@ class THD :public MDL_context_owner,
48594858
std::shared_ptr<utils::PerfCounter> query_perf;
48604859
std::string trace_id;
48614860
uint64_t pc_val;
4861+
std::string query_attrs_string;
4862+
uint64_t num_queries;
4863+
std::string query_type;
48624864

48634865
void copy_client_charset_settings(const THD* other) {
48644866
variables.character_set_client= other->variables.character_set_client;

sql/sql_parse.cc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@
123123
#ifndef EMBEDDED_LIBRARY
124124
#include "perf_counters.h"
125125
#endif
126+
#include "query_tag_perf_counter.h"
126127

127128
#ifdef HAVE_JEMALLOC
128129
#ifndef EMBEDDED_LIBRARY
@@ -1584,7 +1585,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, char* packet,
15841585
Save query attributes.
15851586
*/
15861587
size_t attrs_len= net_field_length((uchar**) &packet_ptr);
1587-
thd->set_query_attrs(CSET_STRING(packet_ptr, attrs_len, thd->charset()));
1588+
thd->set_query_attrs(packet_ptr, attrs_len);
15881589

15891590
auto bytes_to_skip = attrs_len + net_length_size(attrs_len);
15901591
packet_length -= bytes_to_skip;
@@ -1593,12 +1594,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, char* packet,
15931594

15941595
#ifndef EMBEDDED_LIBRARY
15951596
// do collection of samples for passed in "traceid"
1596-
std::string traceId;
1597-
std::unordered_map<std::string, std::string>::const_iterator tid_iter;
1598-
if (!thd->query_attrs_map.empty() &&
1599-
(tid_iter= thd->query_attrs_map.find("traceid")) !=
1600-
thd->query_attrs_map.end()) {
1601-
thd->trace_id = tid_iter->second;
1597+
thd->parse_query_info_attr();
1598+
if (!thd->trace_id.empty()) {
16021599
if (!thd->query_perf) {
16031600
thd->query_perf= pc_factory->makeSharedPerfCounter(
16041601
utils::PerfCounterMode::PCM_THREAD,
@@ -1868,6 +1865,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, char* packet,
18681865
size_t query_len= packet_length;
18691866
if (alloc_query(thd, query_ptr, query_len))
18701867
break; // fatal error is set
1868+
1869+
qutils::query_tag_perf_counter counter(thd);
1870+
18711871
MYSQL_QUERY_START(thd->query(), thd->thread_id,
18721872
(char *) (thd->db ? thd->db : ""),
18731873
&thd->security_ctx->priv_user[0],

sql/sql_show.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767
using std::max;
6868
using std::min;
6969

70+
#include "query_tag_perf_counter.h"
71+
7072
#define STR_OR_NIL(S) ((S) ? (S) : "<nil>")
7173

7274
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -9523,6 +9525,9 @@ ST_SCHEMA_TABLE schema_tables[]=
95239525
{"QUERY_ATTRIBUTES", query_attrs_fields_info,
95249526
create_schema_table, fill_schema_query_attrs,
95259527
NULL, NULL, -1, -1, false, 0},
9528+
{"QUERY_PERF_COUNTER", qutils::query_tag_perf_fields_info,
9529+
create_schema_table, qutils::fill_query_tag_perf_counter,
9530+
NULL, NULL, -1, -1, false, 0},
95269531
{"PROFILING", query_profile_statistics_info, create_schema_table,
95279532
fill_query_profile_statistics_info, make_profile_table_for_show,
95289533
NULL, -1, -1, false, 0},

0 commit comments

Comments
 (0)