mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
ea0b1ccd41
This reverts commit 03c9a4ef4a
.
The fix is wrong. It was doing this: if the uninitialized
wait->m_class has some specific value, then don't initialize it.
7169 lines
216 KiB
C++
7169 lines
216 KiB
C++
/* Copyright (c) 2008, 2023, Oracle and/or its affiliates.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License, version 2.0, for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
|
|
|
|
/**
|
|
@file storage/perfschema/pfs.cc
|
|
The performance schema implementation of all instruments.
|
|
*/
|
|
#include "my_global.h"
|
|
#include "thr_lock.h"
|
|
|
|
/* Make sure exported prototypes match the implementation. */
|
|
#include "pfs_file_provider.h"
|
|
#include "pfs_idle_provider.h"
|
|
#include "pfs_memory_provider.h"
|
|
#include "pfs_metadata_provider.h"
|
|
#include "pfs_socket_provider.h"
|
|
#include "pfs_stage_provider.h"
|
|
#include "pfs_statement_provider.h"
|
|
#include "pfs_table_provider.h"
|
|
#include "pfs_thread_provider.h"
|
|
#include "pfs_transaction_provider.h"
|
|
|
|
#include "mysql/psi/psi.h"
|
|
#include "mysql/psi/mysql_thread.h"
|
|
#include "my_pthread.h"
|
|
#include "sql_const.h"
|
|
#include "pfs.h"
|
|
#include "pfs_instr_class.h"
|
|
#include "pfs_instr.h"
|
|
#include "pfs_host.h"
|
|
#include "pfs_user.h"
|
|
#include "pfs_account.h"
|
|
#include "pfs_global.h"
|
|
#include "pfs_column_values.h"
|
|
#include "pfs_timer.h"
|
|
#include "pfs_events_waits.h"
|
|
#include "pfs_events_stages.h"
|
|
#include "pfs_events_statements.h"
|
|
#include "pfs_events_transactions.h"
|
|
#include "pfs_setup_actor.h"
|
|
#include "pfs_setup_object.h"
|
|
#include "sql_error.h"
|
|
#include "sp_head.h"
|
|
#include "mdl.h" /* mdl_key_init */
|
|
#include "pfs_digest.h"
|
|
#include "pfs_program.h"
|
|
#include "pfs_prepared_stmt.h"
|
|
|
|
using std::min;
|
|
|
|
/*
|
|
This is a development tool to investigate memory statistics,
|
|
do not use in production.
|
|
*/
|
|
#undef PFS_PARANOID
|
|
|
|
#ifdef PFS_PARANOID
|
|
static void report_memory_accounting_error(
|
|
const char *api_name,
|
|
PFS_thread *new_thread,
|
|
size_t size,
|
|
PFS_memory_class *klass,
|
|
PFS_thread *old_thread)
|
|
{
|
|
pfs_print_error("%s "
|
|
"thread <%d> of class <%s> "
|
|
"not owner of <%d> bytes in class <%s> "
|
|
"allocated by thread <%d> of class <%s>\n",
|
|
api_name,
|
|
new_thread->m_thread_internal_id,
|
|
new_thread->m_class->m_name,
|
|
size, klass->m_name,
|
|
old_thread->m_thread_internal_id,
|
|
old_thread->m_class->m_name);
|
|
|
|
assert(strcmp(new_thread->m_class->m_name, "thread/sql/event_worker") != 0);
|
|
assert(strcmp(new_thread->m_class->m_name, "thread/sql/event_scheduler") != 0);
|
|
assert(strcmp(new_thread->m_class->m_name, "thread/sql/one_connection") != 0);
|
|
}
|
|
#endif /* PFS_PARANOID */
|
|
|
|
/**
|
|
@page PAGE_PERFORMANCE_SCHEMA The Performance Schema main page
|
|
MySQL PERFORMANCE_SCHEMA implementation.
|
|
|
|
@section INTRO Introduction
|
|
The PERFORMANCE_SCHEMA is a way to introspect the internal execution of
|
|
the server at runtime.
|
|
The performance schema focuses primarily on performance data,
|
|
as opposed to the INFORMATION_SCHEMA whose purpose is to inspect metadata.
|
|
|
|
From a user point of view, the performance schema consists of:
|
|
- a dedicated database schema, named PERFORMANCE_SCHEMA,
|
|
- SQL tables, used to query the server internal state or change
|
|
configuration settings.
|
|
|
|
From an implementation point of view, the performance schema is a dedicated
|
|
Storage Engine which exposes data collected by 'Instrumentation Points'
|
|
placed in the server code.
|
|
|
|
@section INTERFACES Multiple interfaces
|
|
|
|
The performance schema exposes many different interfaces,
|
|
for different components, and for different purposes.
|
|
|
|
@subsection INT_INSTRUMENTING Instrumenting interface
|
|
|
|
All the data representing the server internal state exposed
|
|
in the performance schema must be first collected:
|
|
this is the role of the instrumenting interface.
|
|
The instrumenting interface is a coding interface provided
|
|
by implementors (of the performance schema) to implementors
|
|
(of the server or server components).
|
|
|
|
This interface is available to:
|
|
- C implementations
|
|
- C++ implementations
|
|
- the core SQL layer (/sql)
|
|
- the mysys library (/mysys)
|
|
- MySQL plugins, including storage engines,
|
|
- third party plugins, including third party storage engines.
|
|
|
|
For details, see the @ref PAGE_INSTRUMENTATION_INTERFACE
|
|
"instrumentation interface page".
|
|
|
|
@subsection INT_COMPILING Compiling interface
|
|
|
|
The implementation of the performance schema can be enabled or disabled at
|
|
build time, when building MySQL from the source code.
|
|
|
|
When building with the performance schema code, some compilation flags
|
|
are available to change the default values used in the code, if required.
|
|
|
|
For more details, see:
|
|
@verbatim ./configure --help @endverbatim
|
|
|
|
To compile with the performance schema:
|
|
@verbatim ./configure --with-perfschema @endverbatim
|
|
|
|
The implementation of all the compiling options is located in
|
|
@verbatim ./storage/perfschema/plug.in @endverbatim
|
|
|
|
@subsection INT_STARTUP Server startup interface
|
|
|
|
The server startup interface consists of the "./mysqld ..."
|
|
command line used to start the server.
|
|
When the performance schema is compiled in the server binary,
|
|
extra command line options are available.
|
|
|
|
These extra start options allow the DBA to:
|
|
- enable or disable the performance schema
|
|
- specify some sizing parameters.
|
|
|
|
To see help for the performance schema startup options, see:
|
|
@verbatim ./sql/mysqld --verbose --help @endverbatim
|
|
|
|
The implementation of all the startup options is located in
|
|
@verbatim ./sql/mysqld.cc, my_long_options[] @endverbatim
|
|
|
|
@subsection INT_BOOTSTRAP Server bootstrap interface
|
|
|
|
The bootstrap interface is a private interface exposed by
|
|
the performance schema, and used by the SQL layer.
|
|
Its role is to advertise all the SQL tables natively
|
|
supported by the performance schema to the SQL server.
|
|
The code consists of creating MySQL tables for the
|
|
performance schema itself, and is used in './mysql --bootstrap'
|
|
mode when a server is installed.
|
|
|
|
The implementation of the database creation script is located in
|
|
@verbatim ./scripts/mysql_performance_tables.sql @endverbatim
|
|
|
|
@subsection INT_CONFIG Runtime configuration interface
|
|
|
|
When the performance schema is used at runtime, various configuration
|
|
parameters can be used to specify what kind of data is collected,
|
|
what kind of aggregations are computed, what kind of timers are used,
|
|
what events are timed, etc.
|
|
|
|
For all these capabilities, not a single statement or special syntax
|
|
was introduced in the parser.
|
|
Instead of new SQL statements, the interface consists of DML
|
|
(SELECT, INSERT, UPDATE, DELETE) against special "SETUP" tables.
|
|
|
|
For example:
|
|
@verbatim mysql> update performance_schema.SETUP_INSTRUMENTS
|
|
set ENABLED='YES', TIMED='YES';
|
|
Query OK, 234 rows affected (0.00 sec)
|
|
Rows matched: 234 Changed: 234 Warnings: 0 @endverbatim
|
|
|
|
@subsection INT_STATUS Internal audit interface
|
|
|
|
The internal audit interface is provided to the DBA to inspect if the
|
|
performance schema code itself is functioning properly.
|
|
This interface is necessary because a failure caused while
|
|
instrumenting code in the server should not cause failures in the
|
|
MySQL server itself, so that the performance schema implementation
|
|
never raises errors during runtime execution.
|
|
|
|
This auditing interface consists of:
|
|
@verbatim SHOW ENGINE PERFORMANCE_SCHEMA STATUS; @endverbatim
|
|
It displays data related to the memory usage of the performance schema,
|
|
as well as statistics about lost events, if any.
|
|
|
|
The SHOW STATUS command is implemented in
|
|
@verbatim ./storage/perfschema/pfs_engine_table.cc @endverbatim
|
|
|
|
@subsection INT_QUERY Query interface
|
|
|
|
The query interface is used to query the internal state of a running server.
|
|
It is provided as SQL tables.
|
|
|
|
For example:
|
|
@verbatim mysql> select * from performance_schema.EVENTS_WAITS_CURRENT;
|
|
@endverbatim
|
|
|
|
@section DESIGN_PRINCIPLES Design principles
|
|
|
|
@subsection PRINCIPLE_BEHAVIOR No behavior changes
|
|
|
|
The primary goal of the performance schema is to measure (instrument) the
|
|
execution of the server. A good measure should not cause any change
|
|
in behavior.
|
|
|
|
To achieve this, the overall design of the performance schema complies
|
|
with the following very severe design constraints:
|
|
|
|
The parser is unchanged. There are no new keywords, no new statements.
|
|
This guarantees that existing applications will run the same way with or
|
|
without the performance schema.
|
|
|
|
All the instrumentation points return "void", there are no error codes.
|
|
Even if the performance schema internally fails, execution of the server
|
|
code will proceed.
|
|
|
|
None of the instrumentation points allocate memory.
|
|
All the memory used by the performance schema is pre-allocated at startup,
|
|
and is considered "static" during the server life time.
|
|
|
|
None of the instrumentation points use any pthread_mutex, pthread_rwlock,
|
|
or pthread_cond (or platform equivalents).
|
|
Executing the instrumentation point should not cause thread scheduling to
|
|
change in the server.
|
|
|
|
In other words, the implementation of the instrumentation points,
|
|
including all the code called by the instrumentation points, is:
|
|
- malloc free
|
|
- mutex free
|
|
- rwlock free
|
|
|
|
TODO: All the code located in storage/perfschema is malloc free,
|
|
but unfortunately the usage of LF_HASH introduces some memory allocation.
|
|
This should be revised if possible, to use a lock-free,
|
|
malloc-free hash code table.
|
|
|
|
@subsection PRINCIPLE_PERFORMANCE No performance hit
|
|
|
|
The instrumentation of the server should be as fast as possible.
|
|
In cases when there are choices between:
|
|
- doing some processing when recording the performance data
|
|
in the instrumentation,
|
|
- doing some processing when retrieving the performance data,
|
|
|
|
priority is given in the design to make the instrumentation faster,
|
|
pushing some complexity to data retrieval.
|
|
|
|
As a result, some parts of the design, related to:
|
|
- the setup code path,
|
|
- the query code path,
|
|
|
|
might appear to be sub-optimal.
|
|
|
|
The criterion used here is to optimize primarily the critical path (data
|
|
collection), possibly at the expense of non-critical code paths.
|
|
|
|
@subsection PRINCIPLE_NOT_INTRUSIVE Unintrusive instrumentation
|
|
|
|
For the performance schema in general to be successful, the barrier
|
|
of entry for a developer should be low, so it's easy to instrument code.
|
|
|
|
In particular, the instrumentation interface:
|
|
- is available for C and C++ code (so it's a C interface),
|
|
- does not require parameters that the calling code can't easily provide,
|
|
- supports partial instrumentation (for example, instrumenting mutexes does
|
|
not require that every mutex is instrumented)
|
|
|
|
@subsection PRINCIPLE_EXTENDABLE Extendable instrumentation
|
|
|
|
As the content of the performance schema improves,
|
|
with more tables exposed and more data collected,
|
|
the instrumentation interface will also be augmented
|
|
to support instrumenting new concepts.
|
|
Existing instrumentations should not be affected when additional
|
|
instrumentation is made available, and making a new instrumentation
|
|
available should not require existing instrumented code to support it.
|
|
|
|
@subsection PRINCIPLE_VERSIONED Versioned instrumentation
|
|
|
|
Given that the instrumentation offered by the performance schema will
|
|
be augmented with time, when more features are implemented,
|
|
the interface itself should be versioned, to keep compatibility
|
|
with previous instrumented code.
|
|
|
|
For example, after both plugin-A and plugin-B have been instrumented for
|
|
mutexes, read write locks and conditions, using the instrumentation
|
|
interface, we can anticipate that the instrumentation interface
|
|
is expanded to support file based operations.
|
|
|
|
Plugin-A, a file based storage engine, will most likely use the expanded
|
|
interface and instrument its file usage, using the version 2
|
|
interface, while Plugin-B, a network based storage engine, will not change
|
|
its code and not release a new binary.
|
|
|
|
When later the instrumentation interface is expanded to support network
|
|
based operations (which will define interface version 3), the Plugin-B code
|
|
can then be changed to make use of it.
|
|
|
|
Note, this is just an example to illustrate the design concept here.
|
|
Both mutexes and file instrumentation are already available
|
|
since version 1 of the instrumentation interface.
|
|
|
|
@subsection PRINCIPLE_DEPLOYMENT Easy deployment
|
|
|
|
Internally, we might want every plugin implementation to upgrade the
|
|
instrumented code to the latest available, but this will cause additional
|
|
work and this is not practical if the code change is monolithic.
|
|
|
|
Externally, for third party plugin implementors, asking implementors to
|
|
always stay aligned to the latest instrumentation and make new releases,
|
|
even when the change does not provide new functionality for them,
|
|
is a bad idea.
|
|
|
|
For example, requiring a network based engine to re-release because the
|
|
instrumentation interface changed for file based operations, will create
|
|
too many deployment issues.
|
|
|
|
So, the performance schema implementation must support concurrently,
|
|
in the same deployment, multiple versions of the instrumentation
|
|
interface, and ensure binary compatibility with each version.
|
|
|
|
In addition to this, the performance schema can be included or excluded
|
|
from the server binary, using build time configuration options.
|
|
|
|
Regardless, the following types of deployment are valid:
|
|
- a server supporting the performance schema + a storage engine
|
|
that is not instrumented
|
|
- a server not supporting the performance schema + a storage engine
|
|
that is instrumented
|
|
*/
|
|
|
|
/**
|
|
@page PAGE_INSTRUMENTATION_INTERFACE Performance schema: instrumentation interface page.
|
|
MySQL performance schema instrumentation interface.
|
|
|
|
@section INTRO Introduction
|
|
|
|
The instrumentation interface consist of two layers:
|
|
- a raw ABI (Application Binary Interface) layer, that exposes the primitive
|
|
instrumentation functions exported by the performance schema instrumentation
|
|
- an API (Application Programing Interface) layer,
|
|
that provides many helpers for a developer instrumenting some code,
|
|
to make the instrumentation as easy as possible.
|
|
|
|
The ABI layer consists of:
|
|
@code
|
|
#include "mysql/psi/psi.h"
|
|
@endcode
|
|
|
|
The API layer consists of:
|
|
@code
|
|
#include "mysql/psi/mutex_mutex.h"
|
|
#include "mysql/psi/mutex_file.h"
|
|
@endcode
|
|
|
|
The first helper is for mutexes, rwlocks and conditions,
|
|
the second for file io.
|
|
|
|
The API layer exposes C macros and typedefs which will expand:
|
|
- either to non-instrumented code, when compiled without the performance
|
|
schema instrumentation
|
|
- or to instrumented code, that will issue the raw calls to the ABI layer
|
|
so that the implementation can collect data.
|
|
|
|
Note that all the names introduced (for example, @c mysql_mutex_lock) do not
|
|
collide with any other namespace.
|
|
In particular, the macro @c mysql_mutex_lock is on purpose not named
|
|
@c pthread_mutex_lock.
|
|
This is to:
|
|
- avoid overloading @c pthread_mutex_lock with yet another macro,
|
|
which is dangerous as it can affect user code and pollute
|
|
the end-user namespace.
|
|
- allow the developer instrumenting code to selectively instrument
|
|
some code but not all.
|
|
|
|
@section PRINCIPLES Design principles
|
|
|
|
The ABI part is designed as a facade, that exposes basic primitives.
|
|
The expectation is that each primitive will be very stable over time,
|
|
but the list will constantly grow when more instruments are supported.
|
|
To support binary compatibility with plugins compiled with a different
|
|
version of the instrumentation, the ABI itself is versioned
|
|
(see @c PSI_v1, @c PSI_v2).
|
|
|
|
For a given instrumentation point in the API, the basic coding pattern
|
|
used is:
|
|
- (a) notify the performance schema of the operation
|
|
about to be performed.
|
|
- (b) execute the instrumented code.
|
|
- (c) notify the performance schema that the operation
|
|
is completed.
|
|
|
|
An opaque "locker" pointer is returned by (a), that is given to (c).
|
|
This pointer helps the implementation to keep context, for performances.
|
|
|
|
The following code fragment is annotated to show how in detail this pattern
|
|
in implemented, when the instrumentation is compiled in:
|
|
|
|
@verbatim
|
|
static inline int mysql_mutex_lock(
|
|
mysql_mutex_t *that, myf flags, const char *src_file, uint src_line)
|
|
{
|
|
int result;
|
|
struct PSI_mutex_locker_state state;
|
|
struct PSI_mutex_locker *locker= NULL;
|
|
|
|
............... (a)
|
|
locker= PSI_MUTEX_CALL(start_mutex_wait)(&state, that->p_psi, PSI_MUTEX_LOCK,
|
|
locker, src_file, src_line);
|
|
|
|
............... (b)
|
|
result= pthread_mutex_lock(&that->m_mutex);
|
|
|
|
............... (c)
|
|
PSI_MUTEX_CALL(end_mutex_wait)(locker, result);
|
|
|
|
return result;
|
|
}
|
|
@endverbatim
|
|
|
|
When the performance schema instrumentation is not compiled in,
|
|
the code becomes simply a wrapper, expanded in line by the compiler:
|
|
|
|
@verbatim
|
|
static inline int mysql_mutex_lock(...)
|
|
{
|
|
int result;
|
|
|
|
............... (b)
|
|
result= pthread_mutex_lock(&that->m_mutex);
|
|
|
|
return result;
|
|
}
|
|
@endverbatim
|
|
|
|
When the performance schema instrumentation is compiled in,
|
|
and when the code compiled is internal to the server implementation,
|
|
PSI_MUTEX_CALL expands directly to functions calls in the performance schema,
|
|
to make (a) and (c) calls as efficient as possible.
|
|
|
|
@verbatim
|
|
static inline int mysql_mutex_lock(...)
|
|
{
|
|
int result;
|
|
struct PSI_mutex_locker_state state;
|
|
struct PSI_mutex_locker *locker= NULL;
|
|
|
|
............... (a)
|
|
locker= pfs_start_mutex_wait_v1(&state, that->p_psi, PSI_MUTEX_LOCK,
|
|
locker, src_file, src_line);
|
|
|
|
............... (b)
|
|
result= pthread_mutex_lock(&that->m_mutex);
|
|
|
|
............... (c)
|
|
pfs_end_mutex_wait_v1(locker, result);
|
|
|
|
return result;
|
|
}
|
|
@endverbatim
|
|
|
|
When the performance schema instrumentation is compiled in,
|
|
and when the code compiled is external to the server implementation
|
|
(typically, a dynamic plugin),
|
|
PSI_MUTEX_CALL expands to dynamic calls to the underlying implementation,
|
|
using the PSI_server entry point.
|
|
This makes (a) and (c) slower, as a function pointer is used instead of a static call,
|
|
but also independent of the implementation, for binary compatibility.
|
|
|
|
@verbatim
|
|
static inline int mysql_mutex_lock(...)
|
|
{
|
|
int result;
|
|
struct PSI_mutex_locker_state state;
|
|
struct PSI_mutex_locker *locker= NULL;
|
|
|
|
............... (a)
|
|
locker= PSI_server->start_mutex_wait(&state, that->p_psi, PSI_MUTEX_LOCK,
|
|
locker, src_file, src_line);
|
|
|
|
............... (b)
|
|
result= pthread_mutex_lock(&that->m_mutex);
|
|
|
|
............... (c)
|
|
PSI_server->end_mutex_wait(locker, result);
|
|
|
|
return result;
|
|
}
|
|
@endverbatim
|
|
|
|
*/
|
|
|
|
/**
|
|
@page PAGE_AGGREGATES Performance schema: the aggregates page.
|
|
Performance schema aggregates.
|
|
|
|
@section INTRO Introduction
|
|
|
|
Aggregates tables are tables that can be formally defined as
|
|
SELECT ... from EVENTS_WAITS_HISTORY_INFINITE ... group by 'group clause'.
|
|
|
|
Each group clause defines a different kind of aggregate, and corresponds to
|
|
a different table exposed by the performance schema.
|
|
|
|
Aggregates can be either:
|
|
- computed on the fly,
|
|
- computed on demand, based on other available data.
|
|
|
|
'EVENTS_WAITS_HISTORY_INFINITE' is a table that does not exist,
|
|
the best approximation is EVENTS_WAITS_HISTORY_LONG.
|
|
Aggregates computed on the fly in fact are based on EVENTS_WAITS_CURRENT,
|
|
while aggregates computed on demand are based on other
|
|
EVENTS_WAITS_SUMMARY_BY_xxx tables.
|
|
|
|
To better understand the implementation itself, a bit of math is
|
|
required first, to understand the model behind the code:
|
|
the code is deceptively simple, the real complexity resides
|
|
in the flyweight of pointers between various performance schema buffers.
|
|
|
|
@section DIMENSION Concept of dimension
|
|
|
|
An event measured by the instrumentation has many attributes.
|
|
An event is represented as a data point P(x1, x2, ..., xN),
|
|
where each x_i coordinate represents a given attribute value.
|
|
|
|
Examples of attributes are:
|
|
- the time waited
|
|
- the object waited on
|
|
- the instrument waited on
|
|
- the thread that waited
|
|
- the operation performed
|
|
- per object or per operation additional attributes, such as spins,
|
|
number of bytes, etc.
|
|
|
|
Computing an aggregate per thread is fundamentally different from
|
|
computing an aggregate by instrument, so the "_BY_THREAD" and
|
|
"_BY_EVENT_NAME" aggregates are different dimensions,
|
|
operating on different x_i and x_j coordinates.
|
|
These aggregates are "orthogonal".
|
|
|
|
@section PROJECTION Concept of projection
|
|
|
|
A given x_i attribute value can convey either just one basic information,
|
|
such as a number of bytes, or can convey implied information,
|
|
such as an object fully qualified name.
|
|
|
|
For example, from the value "test.t1", the name of the object schema
|
|
"test" can be separated from the object name "t1", so that now aggregates
|
|
by object schema can be implemented.
|
|
|
|
In math terms, that corresponds to defining a function:
|
|
F_i (x): x --> y
|
|
Applying this function to our point P gives another point P':
|
|
|
|
F_i (P):
|
|
P(x1, x2, ..., x{i-1}, x_i, x{i+1}, ..., x_N)
|
|
--> P' (x1, x2, ..., x{i-1}, f_i(x_i), x{i+1}, ..., x_N)
|
|
|
|
That function defines in fact an aggregate !
|
|
In SQL terms, this aggregate would look like the following table:
|
|
|
|
@verbatim
|
|
CREATE VIEW EVENTS_WAITS_SUMMARY_BY_Func_i AS
|
|
SELECT col_1, col_2, ..., col_{i-1},
|
|
Func_i(col_i),
|
|
COUNT(col_i),
|
|
MIN(col_i), AVG(col_i), MAX(col_i), -- if col_i is a numeric value
|
|
col_{i+1}, ..., col_N
|
|
FROM EVENTS_WAITS_HISTORY_INFINITE
|
|
group by col_1, col_2, ..., col_{i-1}, col{i+1}, ..., col_N.
|
|
@endverbatim
|
|
|
|
Note that not all columns have to be included,
|
|
in particular some columns that are dependent on the x_i column should
|
|
be removed, so that in practice, MySQL's aggregation method tends to
|
|
remove many attributes at each aggregation steps.
|
|
|
|
For example, when aggregating wait events by object instances,
|
|
- the wait_time and number_of_bytes can be summed,
|
|
and sum(wait_time) now becomes an object instance attribute.
|
|
- the source, timer_start, timer_end columns are not in the
|
|
_BY_INSTANCE table, because these attributes are only
|
|
meaningful for a wait.
|
|
|
|
@section COMPOSITION Concept of composition
|
|
|
|
Now, the "test.t1" --> "test" example was purely theory,
|
|
just to explain the concept, and does not lead very far.
|
|
Let's look at a more interesting example of data that can be derived
|
|
from the row event.
|
|
|
|
An event creates a transient object, PFS_wait_locker, per operation.
|
|
This object's life cycle is extremely short: it's created just
|
|
before the start_wait() instrumentation call, and is destroyed in
|
|
the end_wait() call.
|
|
|
|
The wait locker itself contains a pointer to the object instance
|
|
waited on.
|
|
That allows to implement a wait_locker --> object instance projection,
|
|
with m_target.
|
|
The object instance life cycle depends on _init and _destroy calls
|
|
from the code, such as mysql_mutex_init()
|
|
and mysql_mutex_destroy() for a mutex.
|
|
|
|
The object instance waited on contains a pointer to the object class,
|
|
which is represented by the instrument name.
|
|
That allows to implement an object instance --> object class projection.
|
|
The object class life cycle is permanent, as instruments are loaded in
|
|
the server and never removed.
|
|
|
|
The object class is named in such a way
|
|
(for example, "wait/sync/mutex/sql/LOCK_open",
|
|
"wait/io/file/maria/data_file) that the component ("sql", "maria")
|
|
that it belongs to can be inferred.
|
|
That allows to implement an object class --> server component projection.
|
|
|
|
Back to math again, we have, for example for mutexes:
|
|
|
|
F1 (l) : PFS_wait_locker l --> PFS_mutex m = l->m_target.m_mutex
|
|
|
|
F1_to_2 (m) : PFS_mutex m --> PFS_mutex_class i = m->m_class
|
|
|
|
F2_to_3 (i) : PFS_mutex_class i --> const char *component =
|
|
substring(i->m_name, ...)
|
|
|
|
Per components aggregates are not implemented, this is just an illustration.
|
|
|
|
F1 alone defines this aggregate:
|
|
|
|
EVENTS_WAITS_HISTORY_INFINITE --> EVENTS_WAITS_SUMMARY_BY_INSTANCE
|
|
(or MUTEX_INSTANCE)
|
|
|
|
F1_to_2 alone could define this aggregate:
|
|
|
|
EVENTS_WAITS_SUMMARY_BY_INSTANCE --> EVENTS_WAITS_SUMMARY_BY_EVENT_NAME
|
|
|
|
Alternatively, using function composition, with
|
|
F2 = F1_to_2 o F1, F2 defines:
|
|
|
|
EVENTS_WAITS_HISTORY_INFINITE --> EVENTS_WAITS_SUMMARY_BY_EVENT_NAME
|
|
|
|
Likewise, F_2_to_3 defines:
|
|
|
|
EVENTS_WAITS_SUMMARY_BY_EVENT_NAME --> EVENTS_WAITS_SUMMARY_BY_COMPONENT
|
|
|
|
and F3 = F_2_to_3 o F_1_to_2 o F1 defines:
|
|
|
|
EVENTS_WAITS_HISTORY_INFINITE --> EVENTS_WAITS_SUMMARY_BY_COMPONENT
|
|
|
|
What has all this to do with the code ?
|
|
|
|
Functions (or aggregates) such as F_3 are not implemented as is.
|
|
Instead, they are decomposed into F_2_to_3 o F_1_to_2 o F1,
|
|
and each intermediate aggregate is stored into an internal buffer.
|
|
This allows to support every F1, F2, F3 aggregates from shared
|
|
internal buffers, where computation already performed to compute F2
|
|
is reused when computing F3.
|
|
|
|
@section OBJECT_GRAPH Object graph
|
|
|
|
In terms of object instances, or records, pointers between
|
|
different buffers define an object instance graph.
|
|
|
|
For example, assuming the following scenario:
|
|
- A mutex class "M" is instrumented, the instrument name
|
|
is "wait/sync/mutex/sql/M"
|
|
- This mutex instrument has been instantiated twice,
|
|
mutex instances are noted M-1 and M-2
|
|
- Threads T-A and T-B are locking mutex instance M-1
|
|
- Threads T-C and T-D are locking mutex instance M-2
|
|
|
|
The performance schema will record the following data:
|
|
- EVENTS_WAITS_CURRENT has 4 rows, one for each mutex locker
|
|
- EVENTS_WAITS_SUMMARY_BY_INSTANCE shows 2 rows, for M-1 and M-2
|
|
- EVENTS_WAITS_SUMMARY_BY_EVENT_NAME shows 1 row, for M
|
|
|
|
The graph of structures will look like:
|
|
|
|
@verbatim
|
|
PFS_wait_locker (T-A, M-1) ----------
|
|
|
|
|
v
|
|
PFS_mutex (M-1)
|
|
- m_wait_stat ------------
|
|
^ |
|
|
| |
|
|
PFS_wait_locker (T-B, M-1) ---------- |
|
|
v
|
|
PFS_mutex_class (M)
|
|
- m_wait_stat
|
|
PFS_wait_locker (T-C, M-2) ---------- ^
|
|
| |
|
|
v |
|
|
PFS_mutex (M-2) |
|
|
- m_wait_stat ------------
|
|
^
|
|
|
|
|
PFS_wait_locker (T-D, M-2) ----------
|
|
|
|
|| || ||
|
|
|| || ||
|
|
vv vv vv
|
|
|
|
EVENTS_WAITS_CURRENT ..._SUMMARY_BY_INSTANCE ..._SUMMARY_BY_EVENT_NAME
|
|
@endverbatim
|
|
|
|
@section ON_THE_FLY On the fly aggregates
|
|
|
|
'On the fly' aggregates are computed during the code execution.
|
|
This is necessary because the data the aggregate is based on is volatile,
|
|
and can not be kept indefinitely.
|
|
|
|
With on the fly aggregates:
|
|
- the writer thread does all the computation
|
|
- the reader thread accesses the result directly
|
|
|
|
This model is to be avoided if possible, due to the overhead
|
|
caused when instrumenting code.
|
|
|
|
@section HIGHER_LEVEL Higher level aggregates
|
|
|
|
'Higher level' aggregates are implemented on demand only.
|
|
The code executing a SELECT from the aggregate table is
|
|
collecting data from multiple internal buffers to produce the result.
|
|
|
|
With higher level aggregates:
|
|
- the reader thread does all the computation
|
|
- the writer thread has no overhead.
|
|
|
|
@section MIXED Mixed level aggregates
|
|
|
|
The 'Mixed' model is a compromise between 'On the fly' and 'Higher level'
|
|
aggregates, for internal buffers that are not permanent.
|
|
|
|
While an object is present in a buffer, the higher level model is used.
|
|
When an object is about to be destroyed, statistics are saved into
|
|
a 'parent' buffer with a longer life cycle, to follow the on the fly model.
|
|
|
|
With mixed aggregates:
|
|
- the reader thread does a lot of complex computation,
|
|
- the writer thread has minimal overhead, on destroy events.
|
|
|
|
@section IMPL_WAIT Implementation for waits aggregates
|
|
|
|
For waits, the tables that contains aggregated wait data are:
|
|
- EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME
|
|
- EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME
|
|
- EVENTS_WAITS_SUMMARY_BY_INSTANCE
|
|
- EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME
|
|
- EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME
|
|
- EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME
|
|
- FILE_SUMMARY_BY_EVENT_NAME
|
|
- FILE_SUMMARY_BY_INSTANCE
|
|
- SOCKET_SUMMARY_BY_INSTANCE
|
|
- SOCKET_SUMMARY_BY_EVENT_NAME
|
|
- OBJECTS_SUMMARY_GLOBAL_BY_TYPE
|
|
|
|
The instrumented code that generates waits events consist of:
|
|
- mutexes (mysql_mutex_t)
|
|
- rwlocks (mysql_rwlock_t)
|
|
- conditions (mysql_cond_t)
|
|
- file io (MYSQL_FILE)
|
|
- socket io (MYSQL_SOCKET)
|
|
- table io
|
|
- table lock
|
|
- idle
|
|
|
|
The flow of data between aggregates tables varies for each instrumentation.
|
|
|
|
@subsection IMPL_WAIT_MUTEX Mutex waits
|
|
|
|
@verbatim
|
|
mutex_locker(T, M)
|
|
|
|
|
| [1]
|
|
|
|
|
|-> pfs_mutex(M) =====>> [B], [C]
|
|
| |
|
|
| | [2]
|
|
| |
|
|
| |-> pfs_mutex_class(M.class) =====>> [C]
|
|
|
|
|
|-> pfs_thread(T).event_name(M) =====>> [A], [D], [E], [F]
|
|
|
|
|
| [3]
|
|
|
|
|
3a |-> pfs_account(U, H).event_name(M) =====>> [D], [E], [F]
|
|
. |
|
|
. | [4-RESET]
|
|
. |
|
|
3b .....+-> pfs_user(U).event_name(M) =====>> [E]
|
|
. |
|
|
3c .....+-> pfs_host(H).event_name(M) =====>> [F]
|
|
@endverbatim
|
|
|
|
How to read this diagram:
|
|
- events that occur during the instrumented code execution are noted with numbers,
|
|
as in [1]. Code executed by these events has an impact on overhead.
|
|
- events that occur during TRUNCATE TABLE operations are noted with numbers,
|
|
followed by "-RESET", as in [4-RESET].
|
|
Code executed by these events has no impact on overhead,
|
|
since they are executed by independent monitoring sessions.
|
|
- events that occur when a reader extracts data from a performance schema table
|
|
are noted with letters, as in [A]. The name of the table involved,
|
|
and the method that builds a row are documented. Code executed by these events
|
|
has no impact on the instrumentation overhead. Note that the table
|
|
implementation may pull data from different buffers.
|
|
- nominal code paths are in plain lines. A "nominal" code path corresponds to
|
|
cases where the performance schema buffers are sized so that no records are lost.
|
|
- degenerated code paths are in dotted lines. A "degenerated" code path corresponds
|
|
to edge cases where parent buffers are full, which forces the code to aggregate to
|
|
grand parents directly.
|
|
|
|
Implemented as:
|
|
- [1] @c start_mutex_wait_v1(), @c end_mutex_wait_v1()
|
|
- [2] @c destroy_mutex_v1()
|
|
- [3] @c aggregate_thread_waits()
|
|
- [4] @c PFS_account::aggregate_waits()
|
|
- [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME,
|
|
@c table_ews_by_thread_by_event_name::make_row()
|
|
- [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE,
|
|
@c table_events_waits_summary_by_instance::make_mutex_row()
|
|
- [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME,
|
|
@c table_ews_global_by_event_name::make_mutex_row()
|
|
- [D] EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME,
|
|
@c table_ews_by_account_by_event_name::make_row()
|
|
- [E] EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME,
|
|
@c table_ews_by_user_by_event_name::make_row()
|
|
- [F] EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME,
|
|
@c table_ews_by_host_by_event_name::make_row()
|
|
|
|
Table EVENTS_WAITS_SUMMARY_BY_INSTANCE is a 'on the fly' aggregate,
|
|
because the data is collected on the fly by (1) and stored into a buffer,
|
|
pfs_mutex. The table implementation [B] simply reads the results directly
|
|
from this buffer.
|
|
|
|
Table EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME is a 'mixed' aggregate,
|
|
because some data is collected on the fly (1),
|
|
some data is preserved with (2) at a later time in the life cycle,
|
|
and two different buffers pfs_mutex and pfs_mutex_class are used to store the
|
|
statistics collected. The table implementation [C] is more complex, since
|
|
it reads from two buffers pfs_mutex and pfs_mutex_class.
|
|
|
|
@subsection IMPL_WAIT_RWLOCK Rwlock waits
|
|
|
|
@verbatim
|
|
rwlock_locker(T, R)
|
|
|
|
|
| [1]
|
|
|
|
|
|-> pfs_rwlock(R) =====>> [B], [C]
|
|
| |
|
|
| | [2]
|
|
| |
|
|
| |-> pfs_rwlock_class(R.class) =====>> [C]
|
|
|
|
|
|-> pfs_thread(T).event_name(R) =====>> [A]
|
|
|
|
|
...
|
|
@endverbatim
|
|
|
|
Implemented as:
|
|
- [1] @c start_rwlock_rdwait_v1(), @c end_rwlock_rdwait_v1(), ...
|
|
- [2] @c destroy_rwlock_v1()
|
|
- [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME,
|
|
@c table_ews_by_thread_by_event_name::make_row()
|
|
- [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE,
|
|
@c table_events_waits_summary_by_instance::make_rwlock_row()
|
|
- [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME,
|
|
@c table_ews_global_by_event_name::make_rwlock_row()
|
|
|
|
@subsection IMPL_WAIT_COND Cond waits
|
|
|
|
@verbatim
|
|
cond_locker(T, C)
|
|
|
|
|
| [1]
|
|
|
|
|
|-> pfs_cond(C) =====>> [B], [C]
|
|
| |
|
|
| | [2]
|
|
| |
|
|
| |-> pfs_cond_class(C.class) =====>> [C]
|
|
|
|
|
|-> pfs_thread(T).event_name(C) =====>> [A]
|
|
|
|
|
...
|
|
@endverbatim
|
|
|
|
Implemented as:
|
|
- [1] @c start_cond_wait_v1(), @c end_cond_wait_v1()
|
|
- [2] @c destroy_cond_v1()
|
|
- [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME,
|
|
@c table_ews_by_thread_by_event_name::make_row()
|
|
- [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE,
|
|
@c table_events_waits_summary_by_instance::make_cond_row()
|
|
- [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME,
|
|
@c table_ews_global_by_event_name::make_cond_row()
|
|
|
|
@subsection IMPL_WAIT_FILE File waits
|
|
|
|
@verbatim
|
|
file_locker(T, F)
|
|
|
|
|
| [1]
|
|
|
|
|
|-> pfs_file(F) =====>> [B], [C], [D], [E]
|
|
| |
|
|
| | [2]
|
|
| |
|
|
| |-> pfs_file_class(F.class) =====>> [C], [D]
|
|
|
|
|
|-> pfs_thread(T).event_name(F) =====>> [A]
|
|
|
|
|
...
|
|
@endverbatim
|
|
|
|
Implemented as:
|
|
- [1] @c get_thread_file_name_locker_v1(), @c start_file_wait_v1(),
|
|
@c end_file_wait_v1(), ...
|
|
- [2] @c close_file_v1()
|
|
- [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME,
|
|
@c table_ews_by_thread_by_event_name::make_row()
|
|
- [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE,
|
|
@c table_events_waits_summary_by_instance::make_file_row()
|
|
- [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME,
|
|
@c table_ews_global_by_event_name::make_file_row()
|
|
- [D] FILE_SUMMARY_BY_EVENT_NAME,
|
|
@c table_file_summary_by_event_name::make_row()
|
|
- [E] FILE_SUMMARY_BY_INSTANCE,
|
|
@c table_file_summary_by_instance::make_row()
|
|
|
|
@subsection IMPL_WAIT_SOCKET Socket waits
|
|
|
|
@verbatim
|
|
socket_locker(T, S)
|
|
|
|
|
| [1]
|
|
|
|
|
|-> pfs_socket(S) =====>> [A], [B], [C], [D], [E]
|
|
|
|
|
| [2]
|
|
|
|
|
|-> pfs_socket_class(S.class) =====>> [C], [D]
|
|
|
|
|
|-> pfs_thread(T).event_name(S) =====>> [A]
|
|
|
|
|
| [3]
|
|
|
|
|
3a |-> pfs_account(U, H).event_name(S) =====>> [F], [G], [H]
|
|
. |
|
|
. | [4-RESET]
|
|
. |
|
|
3b .....+-> pfs_user(U).event_name(S) =====>> [G]
|
|
. |
|
|
3c .....+-> pfs_host(H).event_name(S) =====>> [H]
|
|
@endverbatim
|
|
|
|
Implemented as:
|
|
- [1] @c start_socket_wait_v1(), @c end_socket_wait_v1().
|
|
- [2] @c close_socket_v1()
|
|
- [3] @c aggregate_thread_waits()
|
|
- [4] @c PFS_account::aggregate_waits()
|
|
- [5] @c PFS_host::aggregate_waits()
|
|
- [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME,
|
|
@c table_ews_by_thread_by_event_name::make_row()
|
|
- [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE,
|
|
@c table_events_waits_summary_by_instance::make_socket_row()
|
|
- [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME,
|
|
@c table_ews_global_by_event_name::make_socket_row()
|
|
- [D] SOCKET_SUMMARY_BY_EVENT_NAME,
|
|
@c table_socket_summary_by_event_name::make_row()
|
|
- [E] SOCKET_SUMMARY_BY_INSTANCE,
|
|
@c table_socket_summary_by_instance::make_row()
|
|
- [F] EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME,
|
|
@c table_ews_by_account_by_event_name::make_row()
|
|
- [G] EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME,
|
|
@c table_ews_by_user_by_event_name::make_row()
|
|
- [H] EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME,
|
|
@c table_ews_by_host_by_event_name::make_row()
|
|
|
|
@subsection IMPL_WAIT_TABLE Table waits
|
|
|
|
@verbatim
|
|
table_locker(Thread Th, Table Tb, Event = io or lock)
|
|
|
|
|
| [1]
|
|
|
|
|
1a |-> pfs_table(Tb) =====>> [A], [B], [C]
|
|
| |
|
|
| | [2]
|
|
| |
|
|
| |-> pfs_table_share(Tb.share) =====>> [B], [C]
|
|
| |
|
|
| | [3]
|
|
| |
|
|
| |-> global_table_io_stat =====>> [C]
|
|
| |
|
|
| |-> global_table_lock_stat =====>> [C]
|
|
|
|
|
1b |-> pfs_thread(Th).event_name(E) =====>> [D], [E], [F], [G]
|
|
| |
|
|
| | [ 4-RESET]
|
|
| |
|
|
| |-> pfs_account(U, H).event_name(E) =====>> [E], [F], [G]
|
|
| . |
|
|
| . | [5-RESET]
|
|
| . |
|
|
| .....+-> pfs_user(U).event_name(E) =====>> [F]
|
|
| . |
|
|
| .....+-> pfs_host(H).event_name(E) =====>> [G]
|
|
|
|
|
1c |-> pfs_thread(Th).waits_current(W) =====>> [H]
|
|
|
|
|
1d |-> pfs_thread(Th).waits_history(W) =====>> [I]
|
|
|
|
|
1e |-> waits_history_long(W) =====>> [J]
|
|
@endverbatim
|
|
|
|
Implemented as:
|
|
- [1] @c start_table_io_wait_v1(), @c end_table_io_wait_v1()
|
|
- [2] @c close_table_v1()
|
|
- [3] @c drop_table_share_v1()
|
|
- [4] @c TRUNCATE TABLE EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME
|
|
- [5] @c TRUNCATE TABLE EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME
|
|
- [A] EVENTS_WAITS_SUMMARY_BY_INSTANCE,
|
|
@c table_events_waits_summary_by_instance::make_table_row()
|
|
- [B] OBJECTS_SUMMARY_GLOBAL_BY_TYPE,
|
|
@c table_os_global_by_type::make_row()
|
|
- [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME,
|
|
@c table_ews_global_by_event_name::make_table_io_row(),
|
|
@c table_ews_global_by_event_name::make_table_lock_row()
|
|
- [D] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME,
|
|
@c table_ews_by_thread_by_event_name::make_row()
|
|
- [E] EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME,
|
|
@c table_ews_by_user_by_account_name::make_row()
|
|
- [F] EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME,
|
|
@c table_ews_by_user_by_event_name::make_row()
|
|
- [G] EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME,
|
|
@c table_ews_by_host_by_event_name::make_row()
|
|
- [H] EVENTS_WAITS_CURRENT,
|
|
@c table_events_waits_common::make_row()
|
|
- [I] EVENTS_WAITS_HISTORY,
|
|
@c table_events_waits_common::make_row()
|
|
- [J] EVENTS_WAITS_HISTORY_LONG,
|
|
@c table_events_waits_common::make_row()
|
|
|
|
@section IMPL_STAGE Implementation for stages aggregates
|
|
|
|
For stages, the tables that contains aggregated data are:
|
|
- EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME
|
|
- EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME
|
|
- EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME
|
|
- EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME
|
|
- EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME
|
|
|
|
@verbatim
|
|
start_stage(T, S)
|
|
|
|
|
| [1]
|
|
|
|
|
1a |-> pfs_thread(T).event_name(S) =====>> [A], [B], [C], [D], [E]
|
|
| |
|
|
| | [2]
|
|
| |
|
|
| 2a |-> pfs_account(U, H).event_name(S) =====>> [B], [C], [D], [E]
|
|
| . |
|
|
| . | [3-RESET]
|
|
| . |
|
|
| 2b .....+-> pfs_user(U).event_name(S) =====>> [C]
|
|
| . |
|
|
| 2c .....+-> pfs_host(H).event_name(S) =====>> [D], [E]
|
|
| . . |
|
|
| . . | [4-RESET]
|
|
| 2d . . |
|
|
1b |----+----+----+-> pfs_stage_class(S) =====>> [E]
|
|
|
|
@endverbatim
|
|
|
|
Implemented as:
|
|
- [1] @c start_stage_v1()
|
|
- [2] @c delete_thread_v1(), @c aggregate_thread_stages()
|
|
- [3] @c PFS_account::aggregate_stages()
|
|
- [4] @c PFS_host::aggregate_stages()
|
|
- [A] EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME,
|
|
@c table_esgs_by_thread_by_event_name::make_row()
|
|
- [B] EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME,
|
|
@c table_esgs_by_account_by_event_name::make_row()
|
|
- [C] EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME,
|
|
@c table_esgs_by_user_by_event_name::make_row()
|
|
- [D] EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME,
|
|
@c table_esgs_by_host_by_event_name::make_row()
|
|
- [E] EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME,
|
|
@c table_esgs_global_by_event_name::make_row()
|
|
|
|
@section IMPL_STATEMENT Implementation for statements consumers
|
|
|
|
For statements, the tables that contains individual event data are:
|
|
- EVENTS_STATEMENTS_CURRENT
|
|
- EVENTS_STATEMENTS_HISTORY
|
|
- EVENTS_STATEMENTS_HISTORY_LONG
|
|
|
|
For statements, the tables that contains aggregated data are:
|
|
- EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME
|
|
- EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME
|
|
- EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME
|
|
- EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME
|
|
- EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME
|
|
- EVENTS_STATEMENTS_SUMMARY_BY_DIGEST
|
|
|
|
@verbatim
|
|
statement_locker(T, S)
|
|
|
|
|
| [1]
|
|
|
|
|
1a |-> pfs_thread(T).event_name(S) =====>> [A], [B], [C], [D], [E]
|
|
| |
|
|
| | [2]
|
|
| |
|
|
| 2a |-> pfs_account(U, H).event_name(S) =====>> [B], [C], [D], [E]
|
|
| . |
|
|
| . | [3-RESET]
|
|
| . |
|
|
| 2b .....+-> pfs_user(U).event_name(S) =====>> [C]
|
|
| . |
|
|
| 2c .....+-> pfs_host(H).event_name(S) =====>> [D], [E]
|
|
| . . |
|
|
| . . | [4-RESET]
|
|
| 2d . . |
|
|
1b |----+----+----+-> pfs_statement_class(S) =====>> [E]
|
|
|
|
|
1c |-> pfs_thread(T).statement_current(S) =====>> [F]
|
|
|
|
|
1d |-> pfs_thread(T).statement_history(S) =====>> [G]
|
|
|
|
|
1e |-> statement_history_long(S) =====>> [H]
|
|
|
|
|
1f |-> statement_digest(S) =====>> [I]
|
|
|
|
@endverbatim
|
|
|
|
Implemented as:
|
|
- [1] @c start_statement_v1(), end_statement_v1()
|
|
(1a, 1b) is an aggregation by EVENT_NAME,
|
|
(1c, 1d, 1e) is an aggregation by TIME,
|
|
(1f) is an aggregation by DIGEST
|
|
all of these are orthogonal,
|
|
and implemented in end_statement_v1().
|
|
- [2] @c delete_thread_v1(), @c aggregate_thread_statements()
|
|
- [3] @c PFS_account::aggregate_statements()
|
|
- [4] @c PFS_host::aggregate_statements()
|
|
- [A] EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME,
|
|
@c table_esms_by_thread_by_event_name::make_row()
|
|
- [B] EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME,
|
|
@c table_esms_by_account_by_event_name::make_row()
|
|
- [C] EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME,
|
|
@c table_esms_by_user_by_event_name::make_row()
|
|
- [D] EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME,
|
|
@c table_esms_by_host_by_event_name::make_row()
|
|
- [E] EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME,
|
|
@c table_esms_global_by_event_name::make_row()
|
|
- [F] EVENTS_STATEMENTS_CURRENT,
|
|
@c table_events_statements_current::rnd_next(),
|
|
@c table_events_statements_common::make_row()
|
|
- [G] EVENTS_STATEMENTS_HISTORY,
|
|
@c table_events_statements_history::rnd_next(),
|
|
@c table_events_statements_common::make_row()
|
|
- [H] EVENTS_STATEMENTS_HISTORY_LONG,
|
|
@c table_events_statements_history_long::rnd_next(),
|
|
@c table_events_statements_common::make_row()
|
|
- [I] EVENTS_STATEMENTS_SUMMARY_BY_DIGEST
|
|
@c table_esms_by_digest::make_row()
|
|
|
|
@section IMPL_TRANSACTION Implementation for transactions consumers
|
|
|
|
For transactions, the tables that contains individual event data are:
|
|
- EVENTS_TRANSACTIONS_CURRENT
|
|
- EVENTS_TRANSACTIONS_HISTORY
|
|
- EVENTS_TRANSACTIONS_HISTORY_LONG
|
|
|
|
For transactions, the tables that contains aggregated data are:
|
|
- EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME
|
|
- EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME
|
|
- EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME
|
|
- EVENTS_TRANSACTIONS_SUMMARY_BY_USER_BY_EVENT_NAME
|
|
- EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME
|
|
|
|
@verbatim
|
|
transaction_locker(T, TX)
|
|
|
|
|
| [1]
|
|
|
|
|
1a |-> pfs_thread(T).event_name(TX) =====>> [A], [B], [C], [D], [E]
|
|
| |
|
|
| | [2]
|
|
| |
|
|
| 2a |-> pfs_account(U, H).event_name(TX) =====>> [B], [C], [D], [E]
|
|
| . |
|
|
| . | [3-RESET]
|
|
| . |
|
|
| 2b .....+-> pfs_user(U).event_name(TX) =====>> [C]
|
|
| . |
|
|
| 2c .....+-> pfs_host(H).event_name(TX) =====>> [D], [E]
|
|
| . . |
|
|
| . . | [4-RESET]
|
|
| 2d . . |
|
|
1b |----+----+----+-> pfs_transaction_class(TX) =====>> [E]
|
|
|
|
|
1c |-> pfs_thread(T).transaction_current(TX) =====>> [F]
|
|
|
|
|
1d |-> pfs_thread(T).transaction_history(TX) =====>> [G]
|
|
|
|
|
1e |-> transaction_history_long(TX) =====>> [H]
|
|
|
|
@endverbatim
|
|
|
|
Implemented as:
|
|
- [1] @c start_transaction_v1(), end_transaction_v1()
|
|
(1a, 1b) is an aggregation by EVENT_NAME,
|
|
(1c, 1d, 1e) is an aggregation by TIME,
|
|
all of these are orthogonal,
|
|
and implemented in end_transaction_v1().
|
|
- [2] @c delete_thread_v1(), @c aggregate_thread_transactions()
|
|
- [3] @c PFS_account::aggregate_transactions()
|
|
- [4] @c PFS_host::aggregate_transactions()
|
|
|
|
- [A] EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME,
|
|
@c table_ets_by_thread_by_event_name::make_row()
|
|
- [B] EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME,
|
|
@c table_ets_by_account_by_event_name::make_row()
|
|
- [C] EVENTS_TRANSACTIONS_SUMMARY_BY_USER_BY_EVENT_NAME,
|
|
@c table_ets_by_user_by_event_name::make_row()
|
|
- [D] EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME,
|
|
@c table_ets_by_host_by_event_name::make_row()
|
|
- [E] EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME,
|
|
@c table_ets_global_by_event_name::make_row()
|
|
- [F] EVENTS_TRANSACTIONS_CURRENT,
|
|
@c table_events_transactions_current::rnd_next(),
|
|
@c table_events_transactions_common::make_row()
|
|
- [G] EVENTS_TRANSACTIONS_HISTORY,
|
|
@c table_events_transactions_history::rnd_next(),
|
|
@c table_events_transactions_common::make_row()
|
|
- [H] EVENTS_TRANSACTIONS_HISTORY_LONG,
|
|
@c table_events_transactions_history_long::rnd_next(),
|
|
@c table_events_transactions_common::make_row()
|
|
|
|
@section IMPL_MEMORY Implementation for memory instruments
|
|
|
|
For memory, there are no tables that contains individual event data.
|
|
|
|
For memory, the tables that contains aggregated data are:
|
|
- MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME
|
|
- MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME
|
|
- MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME
|
|
- MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME
|
|
- MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME
|
|
|
|
@verbatim
|
|
memory_event(T, S)
|
|
|
|
|
| [1]
|
|
|
|
|
1a |-> pfs_thread(T).event_name(S) =====>> [A], [B], [C], [D], [E]
|
|
| |
|
|
| | [2]
|
|
| |
|
|
1+ | 2a |-> pfs_account(U, H).event_name(S) =====>> [B], [C], [D], [E]
|
|
| . |
|
|
| . | [3-RESET]
|
|
| . |
|
|
1+ | 2b .....+-> pfs_user(U).event_name(S) =====>> [C]
|
|
| . |
|
|
1+ | 2c .....+-> pfs_host(H).event_name(S) =====>> [D], [E]
|
|
| . . |
|
|
| . . | [4-RESET]
|
|
| 2d . . |
|
|
1b |----+----+----+-> global.event_name(S) =====>> [E]
|
|
|
|
@endverbatim
|
|
|
|
Implemented as:
|
|
- [1] @c pfs_memory_alloc_v1(),
|
|
@c pfs_memory_realloc_v1(),
|
|
@c pfs_memory_free_v1().
|
|
- [1+] are overflows that can happen during [1a],
|
|
implemented with @c carry_memory_stat_delta()
|
|
- [2] @c delete_thread_v1(), @c aggregate_thread_memory()
|
|
- [3] @c PFS_account::aggregate_memory()
|
|
- [4] @c PFS_host::aggregate_memory()
|
|
- [A] EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME,
|
|
@c table_mems_by_thread_by_event_name::make_row()
|
|
- [B] EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME,
|
|
@c table_mems_by_account_by_event_name::make_row()
|
|
- [C] EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME,
|
|
@c table_mems_by_user_by_event_name::make_row()
|
|
- [D] EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME,
|
|
@c table_mems_by_host_by_event_name::make_row()
|
|
- [E] EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME,
|
|
@c table_mems_global_by_event_name::make_row()
|
|
|
|
*/
|
|
|
|
/**
|
|
@defgroup Performance_schema Performance Schema
|
|
The performance schema component.
|
|
For details, see the
|
|
@ref PAGE_PERFORMANCE_SCHEMA "performance schema main page".
|
|
|
|
@defgroup Performance_schema_implementation Performance Schema Implementation
|
|
@ingroup Performance_schema
|
|
|
|
@defgroup Performance_schema_tables Performance Schema Tables
|
|
@ingroup Performance_schema_implementation
|
|
*/
|
|
|
|
thread_local_key_t THR_PFS;
|
|
thread_local_key_t THR_PFS_VG; // global_variables
|
|
thread_local_key_t THR_PFS_SV; // session_variables
|
|
thread_local_key_t THR_PFS_VBT; // variables_by_thread
|
|
thread_local_key_t THR_PFS_SG; // global_status
|
|
thread_local_key_t THR_PFS_SS; // session_status
|
|
thread_local_key_t THR_PFS_SBT; // status_by_thread
|
|
thread_local_key_t THR_PFS_SBU; // status_by_user
|
|
thread_local_key_t THR_PFS_SBH; // status_by_host
|
|
thread_local_key_t THR_PFS_SBA; // status_by_account
|
|
|
|
bool THR_PFS_initialized= false;
|
|
|
|
static inline PFS_thread*
|
|
my_thread_get_THR_PFS()
|
|
{
|
|
assert(THR_PFS_initialized);
|
|
PFS_thread *thread= static_cast<PFS_thread*>(my_get_thread_local(THR_PFS));
|
|
assert(thread == NULL || sanitize_thread(thread) != NULL);
|
|
return thread;
|
|
}
|
|
|
|
static inline void
|
|
my_thread_set_THR_PFS(PFS_thread *pfs)
|
|
{
|
|
assert(THR_PFS_initialized);
|
|
my_set_thread_local(THR_PFS, pfs);
|
|
}
|
|
|
|
/**
|
|
Conversion map from PSI_mutex_operation to enum_operation_type.
|
|
Indexed by enum PSI_mutex_operation.
|
|
*/
|
|
static enum_operation_type mutex_operation_map[]=
|
|
{
|
|
OPERATION_TYPE_LOCK,
|
|
OPERATION_TYPE_TRYLOCK
|
|
};
|
|
|
|
/**
|
|
Conversion map from PSI_rwlock_operation to enum_operation_type.
|
|
Indexed by enum PSI_rwlock_operation.
|
|
*/
|
|
static enum_operation_type rwlock_operation_map[]=
|
|
{
|
|
OPERATION_TYPE_READLOCK,
|
|
OPERATION_TYPE_WRITELOCK,
|
|
OPERATION_TYPE_TRYREADLOCK,
|
|
OPERATION_TYPE_TRYWRITELOCK,
|
|
|
|
OPERATION_TYPE_SHAREDLOCK,
|
|
OPERATION_TYPE_SHAREDEXCLUSIVELOCK,
|
|
OPERATION_TYPE_EXCLUSIVELOCK,
|
|
OPERATION_TYPE_TRYSHAREDLOCK,
|
|
OPERATION_TYPE_TRYSHAREDEXCLUSIVELOCK,
|
|
OPERATION_TYPE_TRYEXCLUSIVELOCK,
|
|
};
|
|
|
|
/**
|
|
Conversion map from PSI_cond_operation to enum_operation_type.
|
|
Indexed by enum PSI_cond_operation.
|
|
*/
|
|
static enum_operation_type cond_operation_map[]=
|
|
{
|
|
OPERATION_TYPE_WAIT,
|
|
OPERATION_TYPE_TIMEDWAIT
|
|
};
|
|
|
|
/**
|
|
Conversion map from PSI_file_operation to enum_operation_type.
|
|
Indexed by enum PSI_file_operation.
|
|
*/
|
|
static enum_operation_type file_operation_map[]=
|
|
{
|
|
OPERATION_TYPE_FILECREATE,
|
|
OPERATION_TYPE_FILECREATETMP,
|
|
OPERATION_TYPE_FILEOPEN,
|
|
OPERATION_TYPE_FILESTREAMOPEN,
|
|
OPERATION_TYPE_FILECLOSE,
|
|
OPERATION_TYPE_FILESTREAMCLOSE,
|
|
OPERATION_TYPE_FILEREAD,
|
|
OPERATION_TYPE_FILEWRITE,
|
|
OPERATION_TYPE_FILESEEK,
|
|
OPERATION_TYPE_FILETELL,
|
|
OPERATION_TYPE_FILEFLUSH,
|
|
OPERATION_TYPE_FILESTAT,
|
|
OPERATION_TYPE_FILEFSTAT,
|
|
OPERATION_TYPE_FILECHSIZE,
|
|
OPERATION_TYPE_FILEDELETE,
|
|
OPERATION_TYPE_FILERENAME,
|
|
OPERATION_TYPE_FILESYNC
|
|
};
|
|
|
|
/**
|
|
Conversion map from PSI_table_operation to enum_operation_type.
|
|
Indexed by enum PSI_table_io_operation.
|
|
*/
|
|
static enum_operation_type table_io_operation_map[]=
|
|
{
|
|
OPERATION_TYPE_TABLE_FETCH,
|
|
OPERATION_TYPE_TABLE_WRITE_ROW,
|
|
OPERATION_TYPE_TABLE_UPDATE_ROW,
|
|
OPERATION_TYPE_TABLE_DELETE_ROW
|
|
};
|
|
|
|
/**
|
|
Conversion map from enum PFS_TL_LOCK_TYPE to enum_operation_type.
|
|
Indexed by enum PFS_TL_LOCK_TYPE.
|
|
*/
|
|
static enum_operation_type table_lock_operation_map[]=
|
|
{
|
|
OPERATION_TYPE_TL_READ_NORMAL, /* PFS_TL_READ */
|
|
OPERATION_TYPE_TL_READ_WITH_SHARED_LOCKS, /* PFS_TL_READ_WITH_SHARED_LOCKS */
|
|
OPERATION_TYPE_TL_READ_HIGH_PRIORITY, /* PFS_TL_READ_HIGH_PRIORITY */
|
|
OPERATION_TYPE_TL_READ_NO_INSERTS, /* PFS_TL_READ_NO_INSERT */
|
|
OPERATION_TYPE_TL_WRITE_ALLOW_WRITE, /* PFS_TL_WRITE_ALLOW_WRITE */
|
|
OPERATION_TYPE_TL_WRITE_CONCURRENT_INSERT, /* PFS_TL_WRITE_CONCURRENT_INSERT */
|
|
OPERATION_TYPE_TL_WRITE_DELAYED, /* PFS_TL_WRITE_DELAYED */
|
|
OPERATION_TYPE_TL_WRITE_LOW_PRIORITY, /* PFS_TL_WRITE_LOW_PRIORITY */
|
|
OPERATION_TYPE_TL_WRITE_NORMAL, /* PFS_TL_WRITE */
|
|
OPERATION_TYPE_TL_READ_EXTERNAL, /* PFS_TL_READ_EXTERNAL */
|
|
OPERATION_TYPE_TL_WRITE_EXTERNAL /* PFS_TL_WRITE_EXTERNAL */
|
|
};
|
|
|
|
/**
|
|
Conversion map from PSI_socket_operation to enum_operation_type.
|
|
Indexed by enum PSI_socket_operation.
|
|
*/
|
|
static enum_operation_type socket_operation_map[]=
|
|
{
|
|
OPERATION_TYPE_SOCKETCREATE,
|
|
OPERATION_TYPE_SOCKETCONNECT,
|
|
OPERATION_TYPE_SOCKETBIND,
|
|
OPERATION_TYPE_SOCKETCLOSE,
|
|
OPERATION_TYPE_SOCKETSEND,
|
|
OPERATION_TYPE_SOCKETRECV,
|
|
OPERATION_TYPE_SOCKETSENDTO,
|
|
OPERATION_TYPE_SOCKETRECVFROM,
|
|
OPERATION_TYPE_SOCKETSENDMSG,
|
|
OPERATION_TYPE_SOCKETRECVMSG,
|
|
OPERATION_TYPE_SOCKETSEEK,
|
|
OPERATION_TYPE_SOCKETOPT,
|
|
OPERATION_TYPE_SOCKETSTAT,
|
|
OPERATION_TYPE_SOCKETSHUTDOWN,
|
|
OPERATION_TYPE_SOCKETSELECT
|
|
};
|
|
|
|
/**
|
|
Build the prefix name of a class of instruments in a category.
|
|
For example, this function builds the string 'wait/sync/mutex/sql/' from
|
|
a prefix 'wait/sync/mutex' and a category 'sql'.
|
|
This prefix is used later to build each instrument name, such as
|
|
'wait/sync/mutex/sql/LOCK_open'.
|
|
@param prefix Prefix for this class of instruments
|
|
@param category Category name
|
|
@param [out] output Buffer of length PFS_MAX_INFO_NAME_LENGTH.
|
|
@param [out] output_length Length of the resulting output string.
|
|
@return 0 for success, non zero for errors
|
|
*/
|
|
static int build_prefix(const LEX_CSTRING *prefix, const char *category,
|
|
char *output, size_t *output_length)
|
|
{
|
|
size_t len= strlen(category);
|
|
char *out_ptr= output;
|
|
size_t prefix_length= prefix->length;
|
|
|
|
if (unlikely((prefix_length + len + 1) >=
|
|
PFS_MAX_FULL_PREFIX_NAME_LENGTH))
|
|
{
|
|
pfs_print_error("build_prefix: prefix+category is too long <%s> <%s>\n",
|
|
prefix->str, category);
|
|
return 1;
|
|
}
|
|
|
|
if (unlikely(strchr(category, '/') != NULL))
|
|
{
|
|
pfs_print_error("build_prefix: invalid category <%s>\n",
|
|
category);
|
|
return 1;
|
|
}
|
|
|
|
/* output = prefix + category + '/' */
|
|
memcpy(out_ptr, prefix->str, prefix_length);
|
|
out_ptr+= prefix_length;
|
|
if (len > 0)
|
|
{
|
|
memcpy(out_ptr, category, len);
|
|
out_ptr+= len;
|
|
*out_ptr= '/';
|
|
out_ptr++;
|
|
}
|
|
*output_length= int(out_ptr - output);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define REGISTER_BODY_V1(KEY_T, PREFIX, REGISTER_FUNC) \
|
|
KEY_T key; \
|
|
char formatted_name[PFS_MAX_INFO_NAME_LENGTH]; \
|
|
size_t prefix_length; \
|
|
size_t len; \
|
|
size_t full_length; \
|
|
\
|
|
assert(category != NULL); \
|
|
assert(info != NULL); \
|
|
if (unlikely(build_prefix(&PREFIX, category, \
|
|
formatted_name, &prefix_length)) || \
|
|
! pfs_initialized) \
|
|
{ \
|
|
for (; count>0; count--, info++) \
|
|
*(info->m_key)= 0; \
|
|
return ; \
|
|
} \
|
|
\
|
|
for (; count>0; count--, info++) \
|
|
{ \
|
|
assert(info->m_key != NULL); \
|
|
assert(info->m_name != NULL); \
|
|
len= strlen(info->m_name); \
|
|
full_length= prefix_length + len; \
|
|
if (likely(full_length <= PFS_MAX_INFO_NAME_LENGTH)) \
|
|
{ \
|
|
memcpy(formatted_name + prefix_length, info->m_name, len); \
|
|
key= REGISTER_FUNC(formatted_name, (uint)full_length, info->m_flags); \
|
|
} \
|
|
else \
|
|
{ \
|
|
pfs_print_error("REGISTER_BODY_V1: name too long <%s> <%s>\n", \
|
|
category, info->m_name); \
|
|
key= 0; \
|
|
} \
|
|
\
|
|
*(info->m_key)= key; \
|
|
} \
|
|
return;
|
|
|
|
/* Use C linkage for the interface functions. */
|
|
|
|
C_MODE_START
|
|
|
|
/**
|
|
Implementation of the mutex instrumentation interface.
|
|
@sa PSI_v1::register_mutex.
|
|
*/
|
|
void pfs_register_mutex_v1(const char *category,
|
|
PSI_mutex_info_v1 *info,
|
|
int count)
|
|
{
|
|
REGISTER_BODY_V1(PSI_mutex_key,
|
|
mutex_instrument_prefix,
|
|
register_mutex_class)
|
|
}
|
|
|
|
/**
|
|
Implementation of the rwlock instrumentation interface.
|
|
@sa PSI_v1::register_rwlock.
|
|
*/
|
|
void pfs_register_rwlock_v1(const char *category,
|
|
PSI_rwlock_info_v1 *info,
|
|
int count)
|
|
{
|
|
PSI_rwlock_key key;
|
|
char rw_formatted_name[PFS_MAX_INFO_NAME_LENGTH];
|
|
char sx_formatted_name[PFS_MAX_INFO_NAME_LENGTH];
|
|
size_t rw_prefix_length;
|
|
size_t sx_prefix_length;
|
|
size_t len;
|
|
size_t full_length;
|
|
|
|
assert(category != NULL);
|
|
assert(info != NULL);
|
|
if (build_prefix(&rwlock_instrument_prefix, category,
|
|
rw_formatted_name, &rw_prefix_length) ||
|
|
build_prefix(&sxlock_instrument_prefix, category,
|
|
sx_formatted_name, &sx_prefix_length) ||
|
|
! pfs_initialized)
|
|
{
|
|
for (; count>0; count--, info++)
|
|
*(info->m_key)= 0;
|
|
return ;
|
|
}
|
|
|
|
for (; count>0; count--, info++)
|
|
{
|
|
assert(info->m_key != NULL);
|
|
assert(info->m_name != NULL);
|
|
len= strlen(info->m_name);
|
|
|
|
if (info->m_flags & PSI_RWLOCK_FLAG_SX)
|
|
{
|
|
full_length= sx_prefix_length + len;
|
|
if (likely(full_length <= PFS_MAX_INFO_NAME_LENGTH))
|
|
{
|
|
memcpy(sx_formatted_name + sx_prefix_length, info->m_name, len);
|
|
key= register_rwlock_class(sx_formatted_name, (uint)full_length, info->m_flags);
|
|
}
|
|
else
|
|
{
|
|
pfs_print_error("REGISTER_BODY_V1: (sx) name too long <%s> <%s>\n",
|
|
category, info->m_name);
|
|
key= 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
full_length= rw_prefix_length + len;
|
|
if (likely(full_length <= PFS_MAX_INFO_NAME_LENGTH))
|
|
{
|
|
memcpy(rw_formatted_name + rw_prefix_length, info->m_name, len);
|
|
key= register_rwlock_class(rw_formatted_name, (uint)full_length, info->m_flags);
|
|
}
|
|
else
|
|
{
|
|
pfs_print_error("REGISTER_BODY_V1: (rw) name too long <%s> <%s>\n",
|
|
category, info->m_name);
|
|
key= 0;
|
|
}
|
|
}
|
|
|
|
*(info->m_key)= key;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**
|
|
Implementation of the cond instrumentation interface.
|
|
@sa PSI_v1::register_cond.
|
|
*/
|
|
void pfs_register_cond_v1(const char *category,
|
|
PSI_cond_info_v1 *info,
|
|
int count)
|
|
{
|
|
REGISTER_BODY_V1(PSI_cond_key,
|
|
cond_instrument_prefix,
|
|
register_cond_class)
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::register_thread.
|
|
*/
|
|
void pfs_register_thread_v1(const char *category,
|
|
PSI_thread_info_v1 *info,
|
|
int count)
|
|
{
|
|
REGISTER_BODY_V1(PSI_thread_key,
|
|
thread_instrument_prefix,
|
|
register_thread_class)
|
|
}
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::register_file.
|
|
*/
|
|
void pfs_register_file_v1(const char *category,
|
|
PSI_file_info_v1 *info,
|
|
int count)
|
|
{
|
|
REGISTER_BODY_V1(PSI_file_key,
|
|
file_instrument_prefix,
|
|
register_file_class)
|
|
}
|
|
|
|
void pfs_register_stage_v1(const char *category,
|
|
PSI_stage_info_v1 **info_array,
|
|
int count)
|
|
{
|
|
char formatted_name[PFS_MAX_INFO_NAME_LENGTH];
|
|
size_t prefix_length;
|
|
size_t len;
|
|
size_t full_length;
|
|
PSI_stage_info_v1 *info;
|
|
|
|
assert(category != NULL);
|
|
assert(info_array != NULL);
|
|
if (unlikely(build_prefix(&stage_instrument_prefix, category,
|
|
formatted_name, &prefix_length)) ||
|
|
! pfs_initialized)
|
|
{
|
|
for (; count>0; count--, info_array++)
|
|
(*info_array)->m_key= 0;
|
|
return ;
|
|
}
|
|
|
|
for (; count>0; count--, info_array++)
|
|
{
|
|
info= *info_array;
|
|
DBUG_ASSERT(info != NULL);
|
|
DBUG_ASSERT(info->m_name != NULL);
|
|
len= (int)strlen(info->m_name);
|
|
DBUG_ASSERT(len <= 64); // see table_threads.cc near PROCESSLIST_STATE
|
|
full_length= prefix_length + len;
|
|
if (likely(full_length <= PFS_MAX_INFO_NAME_LENGTH))
|
|
{
|
|
memcpy(formatted_name + prefix_length, info->m_name, len);
|
|
info->m_key= register_stage_class(formatted_name,
|
|
(uint)prefix_length,
|
|
(uint)full_length,
|
|
info->m_flags);
|
|
}
|
|
else
|
|
{
|
|
pfs_print_error("register_stage_v1: name too long <%s> <%s>\n",
|
|
category, info->m_name);
|
|
info->m_key= 0;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void pfs_register_statement_v1(const char *category,
|
|
PSI_statement_info_v1 *info,
|
|
int count)
|
|
{
|
|
char formatted_name[PFS_MAX_INFO_NAME_LENGTH];
|
|
size_t prefix_length;
|
|
size_t len;
|
|
size_t full_length;
|
|
|
|
assert(category != NULL);
|
|
assert(info != NULL);
|
|
if (unlikely(build_prefix(&statement_instrument_prefix,
|
|
category, formatted_name, &prefix_length)) ||
|
|
! pfs_initialized)
|
|
{
|
|
for (; count>0; count--, info++)
|
|
info->m_key= 0;
|
|
return ;
|
|
}
|
|
|
|
for (; count>0; count--, info++)
|
|
{
|
|
if (info->m_name == NULL)
|
|
continue;
|
|
|
|
len= (int)strlen(info->m_name);
|
|
full_length= prefix_length + len;
|
|
if (likely(full_length <= PFS_MAX_INFO_NAME_LENGTH))
|
|
{
|
|
memcpy(formatted_name + prefix_length, info->m_name, len);
|
|
info->m_key= register_statement_class(formatted_name, (uint)full_length, info->m_flags);
|
|
}
|
|
else
|
|
{
|
|
pfs_print_error("register_statement_v1: name too long <%s>\n",
|
|
info->m_name);
|
|
info->m_key= 0;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void pfs_register_socket_v1(const char *category,
|
|
PSI_socket_info_v1 *info,
|
|
int count)
|
|
{
|
|
REGISTER_BODY_V1(PSI_socket_key,
|
|
socket_instrument_prefix,
|
|
register_socket_class)
|
|
}
|
|
|
|
#define INIT_BODY_V1(T, KEY, ID) \
|
|
PFS_##T##_class *klass; \
|
|
PFS_##T *pfs; \
|
|
klass= find_##T##_class(KEY); \
|
|
if (unlikely(klass == NULL)) \
|
|
return NULL; \
|
|
pfs= create_##T(klass, ID); \
|
|
return reinterpret_cast<PSI_##T *> (pfs)
|
|
|
|
/**
|
|
Implementation of the mutex instrumentation interface.
|
|
@sa PSI_v1::init_mutex.
|
|
*/
|
|
PSI_mutex*
|
|
pfs_init_mutex_v1(PSI_mutex_key key, void *identity)
|
|
{
|
|
INIT_BODY_V1(mutex, key, identity);
|
|
}
|
|
|
|
/**
|
|
Implementation of the mutex instrumentation interface.
|
|
@sa PSI_v1::destroy_mutex.
|
|
*/
|
|
void pfs_destroy_mutex_v1(PSI_mutex* mutex)
|
|
{
|
|
PFS_mutex *pfs= reinterpret_cast<PFS_mutex*> (mutex);
|
|
|
|
assert(pfs != NULL);
|
|
|
|
destroy_mutex(pfs);
|
|
}
|
|
|
|
/**
|
|
Implementation of the rwlock instrumentation interface.
|
|
@sa PSI_v1::init_rwlock.
|
|
*/
|
|
PSI_rwlock*
|
|
pfs_init_rwlock_v1(PSI_rwlock_key key, void *identity)
|
|
{
|
|
INIT_BODY_V1(rwlock, key, identity);
|
|
}
|
|
|
|
/**
|
|
Implementation of the rwlock instrumentation interface.
|
|
@sa PSI_v1::destroy_rwlock.
|
|
*/
|
|
void pfs_destroy_rwlock_v1(PSI_rwlock* rwlock)
|
|
{
|
|
PFS_rwlock *pfs= reinterpret_cast<PFS_rwlock*> (rwlock);
|
|
|
|
assert(pfs != NULL);
|
|
|
|
destroy_rwlock(pfs);
|
|
}
|
|
|
|
/**
|
|
Implementation of the cond instrumentation interface.
|
|
@sa PSI_v1::init_cond.
|
|
*/
|
|
PSI_cond*
|
|
pfs_init_cond_v1(PSI_cond_key key, void *identity)
|
|
{
|
|
INIT_BODY_V1(cond, key, identity);
|
|
}
|
|
|
|
/**
|
|
Implementation of the cond instrumentation interface.
|
|
@sa PSI_v1::destroy_cond.
|
|
*/
|
|
void pfs_destroy_cond_v1(PSI_cond* cond)
|
|
{
|
|
PFS_cond *pfs= reinterpret_cast<PFS_cond*> (cond);
|
|
|
|
assert(pfs != NULL);
|
|
|
|
destroy_cond(pfs);
|
|
}
|
|
|
|
/**
|
|
Implementation of the table instrumentation interface.
|
|
@sa PSI_v1::get_table_share.
|
|
*/
|
|
PSI_table_share*
|
|
pfs_get_table_share_v1(my_bool temporary, TABLE_SHARE *share)
|
|
{
|
|
/* Ignore temporary tables and views. */
|
|
if (temporary || share->is_view)
|
|
return NULL;
|
|
/* An instrumented thread is required, for LF_PINS. */
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
PFS_table_share* pfs_share;
|
|
pfs_share= find_or_create_table_share(pfs_thread, temporary, share);
|
|
return reinterpret_cast<PSI_table_share*> (pfs_share);
|
|
}
|
|
|
|
/**
|
|
Implementation of the table instrumentation interface.
|
|
@sa PSI_v1::release_table_share.
|
|
*/
|
|
void pfs_release_table_share_v1(PSI_table_share* share)
|
|
{
|
|
PFS_table_share* pfs= reinterpret_cast<PFS_table_share*> (share);
|
|
|
|
if (unlikely(pfs == NULL))
|
|
return;
|
|
|
|
release_table_share(pfs);
|
|
}
|
|
|
|
/**
|
|
Implementation of the table instrumentation interface.
|
|
@sa PSI_v1::drop_table_share.
|
|
*/
|
|
void
|
|
pfs_drop_table_share_v1(my_bool temporary,
|
|
const char *schema_name, int schema_name_length,
|
|
const char *table_name, int table_name_length)
|
|
{
|
|
/* Ignore temporary tables. */
|
|
if (temporary)
|
|
return;
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return;
|
|
/* TODO: temporary tables */
|
|
drop_table_share(pfs_thread, temporary, schema_name, schema_name_length,
|
|
table_name, table_name_length);
|
|
}
|
|
|
|
/**
|
|
Implementation of the table instrumentation interface.
|
|
@sa PSI_v1::open_table.
|
|
*/
|
|
PSI_table*
|
|
pfs_open_table_v1(PSI_table_share *share, const void *identity)
|
|
{
|
|
PFS_table_share *pfs_table_share= reinterpret_cast<PFS_table_share*> (share);
|
|
|
|
/*
|
|
When the performance schema is off, do not instrument anything.
|
|
Table handles have short life cycle, instrumentation will happen
|
|
again if needed during the next open().
|
|
*/
|
|
if (psi_unlikely(! flag_global_instrumentation))
|
|
return NULL;
|
|
|
|
if (unlikely(pfs_table_share == NULL))
|
|
return NULL;
|
|
|
|
/* This object is not to be instrumented. */
|
|
if (! pfs_table_share->m_enabled)
|
|
return NULL;
|
|
|
|
/* This object is instrumented, but all table instruments are disabled. */
|
|
if (! global_table_io_class.m_enabled && ! global_table_lock_class.m_enabled)
|
|
return NULL;
|
|
|
|
PFS_thread *thread= my_thread_get_THR_PFS();
|
|
|
|
if (unlikely(thread == NULL))
|
|
return NULL;
|
|
|
|
PFS_table *pfs_table= create_table(pfs_table_share, thread, identity);
|
|
return reinterpret_cast<PSI_table *> (pfs_table);
|
|
}
|
|
|
|
/**
|
|
Implementation of the table instrumentation interface.
|
|
@sa PSI_v1::unbind_table.
|
|
*/
|
|
void pfs_unbind_table_v1(PSI_table *table)
|
|
{
|
|
PFS_table *pfs= reinterpret_cast<PFS_table*> (table);
|
|
if (likely(pfs != NULL))
|
|
{
|
|
pfs->m_thread_owner= NULL;
|
|
pfs->m_owner_event_id= 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the table instrumentation interface.
|
|
@sa PSI_v1::rebind_table.
|
|
*/
|
|
PSI_table *
|
|
pfs_rebind_table_v1(PSI_table_share *share, const void *identity, PSI_table *table)
|
|
{
|
|
PFS_table *pfs= reinterpret_cast<PFS_table*> (table);
|
|
if (likely(pfs != NULL))
|
|
{
|
|
assert(pfs->m_thread_owner == NULL);
|
|
|
|
if (unlikely(! pfs->m_share->m_enabled))
|
|
{
|
|
destroy_table(pfs);
|
|
return NULL;
|
|
}
|
|
|
|
if (unlikely(! global_table_io_class.m_enabled && ! global_table_lock_class.m_enabled))
|
|
{
|
|
destroy_table(pfs);
|
|
return NULL;
|
|
}
|
|
|
|
if (psi_unlikely(! flag_global_instrumentation))
|
|
{
|
|
destroy_table(pfs);
|
|
return NULL;
|
|
}
|
|
|
|
/* The table handle was already instrumented, reuse it for this thread. */
|
|
PFS_thread *thread= my_thread_get_THR_PFS();
|
|
pfs->m_thread_owner= thread;
|
|
if (thread != NULL)
|
|
pfs->m_owner_event_id= thread->m_event_id;
|
|
else
|
|
pfs->m_owner_event_id= 0;
|
|
return table;
|
|
}
|
|
|
|
/* See open_table_v1() */
|
|
|
|
PFS_table_share *pfs_table_share= reinterpret_cast<PFS_table_share*> (share);
|
|
|
|
if (unlikely(pfs_table_share == NULL))
|
|
return NULL;
|
|
|
|
if (! pfs_table_share->m_enabled)
|
|
return NULL;
|
|
|
|
if (! global_table_io_class.m_enabled && ! global_table_lock_class.m_enabled)
|
|
return NULL;
|
|
|
|
if (! flag_global_instrumentation)
|
|
return NULL;
|
|
|
|
PFS_thread *thread= my_thread_get_THR_PFS();
|
|
if (unlikely(thread == NULL))
|
|
return NULL;
|
|
|
|
PFS_table *pfs_table= create_table(pfs_table_share, thread, identity);
|
|
return reinterpret_cast<PSI_table *> (pfs_table);
|
|
}
|
|
|
|
/**
|
|
Implementation of the table instrumentation interface.
|
|
@sa PSI_v1::close_table.
|
|
*/
|
|
void pfs_close_table_v1(TABLE_SHARE *server_share, PSI_table *table)
|
|
{
|
|
PFS_table *pfs= reinterpret_cast<PFS_table*> (table);
|
|
if (unlikely(pfs == NULL))
|
|
return;
|
|
pfs->aggregate(server_share);
|
|
destroy_table(pfs);
|
|
}
|
|
|
|
PSI_socket*
|
|
pfs_init_socket_v1(PSI_socket_key key, const my_socket *fd,
|
|
const struct sockaddr *addr, socklen_t addr_len)
|
|
{
|
|
PFS_socket_class *klass;
|
|
PFS_socket *pfs;
|
|
klass= find_socket_class(key);
|
|
if (unlikely(klass == NULL))
|
|
return NULL;
|
|
pfs= create_socket(klass, fd, addr, addr_len);
|
|
return reinterpret_cast<PSI_socket *> (pfs);
|
|
}
|
|
|
|
void pfs_destroy_socket_v1(PSI_socket *socket)
|
|
{
|
|
PFS_socket *pfs= reinterpret_cast<PFS_socket*> (socket);
|
|
|
|
assert(pfs != NULL);
|
|
|
|
destroy_socket(pfs);
|
|
}
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::create_file.
|
|
*/
|
|
void pfs_create_file_v1(PSI_file_key key, const char *name, File file)
|
|
{
|
|
if (psi_unlikely(! flag_global_instrumentation))
|
|
return;
|
|
int index= (int) file;
|
|
if (unlikely(index < 0))
|
|
return;
|
|
PFS_file_class *klass= find_file_class(key);
|
|
if (unlikely(klass == NULL))
|
|
return;
|
|
if (! klass->m_enabled)
|
|
return;
|
|
|
|
/* A thread is needed for LF_PINS */
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return;
|
|
|
|
if (flag_thread_instrumentation && ! pfs_thread->m_enabled)
|
|
return;
|
|
|
|
/*
|
|
We want this check after pfs_thread->m_enabled,
|
|
to avoid reporting false loss.
|
|
*/
|
|
if (unlikely(index >= file_handle_max))
|
|
{
|
|
file_handle_lost++;
|
|
return;
|
|
}
|
|
|
|
uint len= (uint)strlen(name);
|
|
PFS_file *pfs_file= find_or_create_file(pfs_thread, klass, name, len, true);
|
|
|
|
file_handle_array[index]= pfs_file;
|
|
}
|
|
|
|
/**
|
|
Arguments given from a parent to a child thread, packaged in one structure.
|
|
This data is used when spawning a new instrumented thread.
|
|
@sa pfs_spawn_thread.
|
|
*/
|
|
struct PFS_spawn_thread_arg
|
|
{
|
|
ulonglong m_thread_internal_id;
|
|
char m_username[USERNAME_LENGTH];
|
|
uint m_username_length;
|
|
char m_hostname[HOSTNAME_LENGTH];
|
|
uint m_hostname_length;
|
|
|
|
PSI_thread_key m_child_key;
|
|
const void *m_child_identity;
|
|
void *(*m_user_start_routine)(void*);
|
|
void *m_user_arg;
|
|
};
|
|
|
|
extern "C" void* pfs_spawn_thread(void *arg)
|
|
{
|
|
PFS_spawn_thread_arg *typed_arg= (PFS_spawn_thread_arg*) arg;
|
|
void *user_arg;
|
|
void *(*user_start_routine)(void*);
|
|
|
|
PFS_thread *pfs;
|
|
|
|
/* First, attach instrumentation to this newly created pthread. */
|
|
PFS_thread_class *klass= find_thread_class(typed_arg->m_child_key);
|
|
if (likely(klass != NULL))
|
|
{
|
|
pfs= create_thread(klass, typed_arg->m_child_identity, 0);
|
|
if (likely(pfs != NULL))
|
|
{
|
|
clear_thread_account(pfs);
|
|
|
|
pfs->m_parent_thread_internal_id= typed_arg->m_thread_internal_id;
|
|
|
|
memcpy(pfs->m_username, typed_arg->m_username, sizeof(pfs->m_username));
|
|
pfs->m_username_length= typed_arg->m_username_length;
|
|
|
|
memcpy(pfs->m_hostname, typed_arg->m_hostname, sizeof(pfs->m_hostname));
|
|
pfs->m_hostname_length= typed_arg->m_hostname_length;
|
|
|
|
set_thread_account(pfs);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pfs= NULL;
|
|
}
|
|
my_thread_set_THR_PFS(pfs);
|
|
|
|
/*
|
|
Secondly, free the memory allocated in spawn_thread_v1().
|
|
It is preferable to do this before invoking the user
|
|
routine, to avoid memory leaks at shutdown, in case
|
|
the server exits without waiting for this thread.
|
|
*/
|
|
user_start_routine= typed_arg->m_user_start_routine;
|
|
user_arg= typed_arg->m_user_arg;
|
|
my_free(typed_arg);
|
|
|
|
/* Then, execute the user code for this thread. */
|
|
(*user_start_routine)(user_arg);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::spawn_thread.
|
|
*/
|
|
int pfs_spawn_thread_v1(PSI_thread_key key,
|
|
my_thread_handle *thread, const my_thread_attr_t *attr,
|
|
void *(*start_routine)(void*), void *arg)
|
|
{
|
|
PFS_spawn_thread_arg *psi_arg;
|
|
PFS_thread *parent;
|
|
|
|
/* psi_arg can not be global, and can not be a local variable. */
|
|
psi_arg= (PFS_spawn_thread_arg*) my_malloc(PSI_NOT_INSTRUMENTED,
|
|
sizeof(PFS_spawn_thread_arg),
|
|
MYF(MY_WME));
|
|
if (unlikely(psi_arg == NULL))
|
|
return EAGAIN;
|
|
|
|
psi_arg->m_child_key= key;
|
|
psi_arg->m_child_identity= (arg ? arg : thread);
|
|
psi_arg->m_user_start_routine= start_routine;
|
|
psi_arg->m_user_arg= arg;
|
|
|
|
parent= my_thread_get_THR_PFS();
|
|
if (parent != NULL)
|
|
{
|
|
/*
|
|
Make a copy of the parent attributes.
|
|
This is required, because instrumentation for this thread (the parent)
|
|
may be destroyed before the child thread instrumentation is created.
|
|
*/
|
|
psi_arg->m_thread_internal_id= parent->m_thread_internal_id;
|
|
|
|
memcpy(psi_arg->m_username, parent->m_username, sizeof(psi_arg->m_username));
|
|
psi_arg->m_username_length= parent->m_username_length;
|
|
|
|
memcpy(psi_arg->m_hostname, parent->m_hostname, sizeof(psi_arg->m_hostname));
|
|
psi_arg->m_hostname_length= parent->m_hostname_length;
|
|
}
|
|
else
|
|
{
|
|
psi_arg->m_thread_internal_id= 0;
|
|
psi_arg->m_username_length= 0;
|
|
psi_arg->m_hostname_length= 0;
|
|
}
|
|
|
|
int result= my_thread_create(thread, attr, pfs_spawn_thread, psi_arg);
|
|
if (unlikely(result != 0))
|
|
my_free(psi_arg);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::new_thread.
|
|
*/
|
|
PSI_thread*
|
|
pfs_new_thread_v1(PSI_thread_key key, const void *identity, ulonglong processlist_id)
|
|
{
|
|
PFS_thread *pfs;
|
|
|
|
PFS_thread_class *klass= find_thread_class(key);
|
|
if (likely(klass != NULL))
|
|
{
|
|
pfs= create_thread(klass, identity, processlist_id);
|
|
if (pfs != NULL)
|
|
{
|
|
PFS_thread *parent= my_thread_get_THR_PFS();
|
|
if (parent != NULL)
|
|
pfs->m_parent_thread_internal_id= parent->m_parent_thread_internal_id;
|
|
}
|
|
}
|
|
else
|
|
pfs= NULL;
|
|
|
|
return reinterpret_cast<PSI_thread*> (pfs);
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::set_thread_id.
|
|
*/
|
|
void pfs_set_thread_id_v1(PSI_thread *thread, ulonglong processlist_id)
|
|
{
|
|
PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread);
|
|
if (unlikely(pfs == NULL))
|
|
return;
|
|
pfs->m_processlist_id= (ulong)processlist_id;
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::set_thread_THD.
|
|
*/
|
|
void pfs_set_thread_THD_v1(PSI_thread *thread, THD *thd)
|
|
{
|
|
PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread);
|
|
if (unlikely(pfs == NULL))
|
|
return;
|
|
pfs->m_thd= thd;
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::set_thread_os_thread_id.
|
|
*/
|
|
void pfs_set_thread_os_id_v1(PSI_thread *thread)
|
|
{
|
|
PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread);
|
|
if (unlikely(pfs == NULL))
|
|
return;
|
|
pfs->m_thread_os_id= my_thread_os_id();
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::get_thread_id.
|
|
*/
|
|
PSI_thread*
|
|
pfs_get_thread_v1(void)
|
|
{
|
|
PFS_thread *pfs= my_thread_get_THR_PFS();
|
|
return reinterpret_cast<PSI_thread*> (pfs);
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::set_thread_user.
|
|
*/
|
|
void pfs_set_thread_user_v1(const char *user, int user_len)
|
|
{
|
|
pfs_dirty_state dirty_state;
|
|
PFS_thread *pfs= my_thread_get_THR_PFS();
|
|
|
|
assert((user != NULL) || (user_len == 0));
|
|
assert(user_len >= 0);
|
|
assert((uint) user_len <= sizeof(pfs->m_username));
|
|
|
|
if (unlikely(pfs == NULL))
|
|
return;
|
|
|
|
aggregate_thread(pfs, pfs->m_account, pfs->m_user, pfs->m_host);
|
|
|
|
pfs->m_session_lock.allocated_to_dirty(& dirty_state);
|
|
|
|
clear_thread_account(pfs);
|
|
|
|
if (user_len > 0)
|
|
memcpy(pfs->m_username, user, user_len);
|
|
pfs->m_username_length= user_len;
|
|
|
|
set_thread_account(pfs);
|
|
|
|
bool enabled;
|
|
bool history;
|
|
if (pfs->m_account != NULL)
|
|
{
|
|
enabled= pfs->m_account->m_enabled;
|
|
history= pfs->m_account->m_history;
|
|
}
|
|
else
|
|
{
|
|
if ((pfs->m_username_length > 0) && (pfs->m_hostname_length > 0))
|
|
{
|
|
lookup_setup_actor(pfs,
|
|
pfs->m_username, pfs->m_username_length,
|
|
pfs->m_hostname, pfs->m_hostname_length,
|
|
&enabled, &history);
|
|
}
|
|
else
|
|
{
|
|
/* There is no setting for background threads */
|
|
enabled= true;
|
|
history= true;
|
|
}
|
|
}
|
|
pfs->set_enabled(enabled);
|
|
pfs->set_history(history);
|
|
|
|
pfs->m_session_lock.dirty_to_allocated(& dirty_state);
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::set_thread_account.
|
|
*/
|
|
void pfs_set_thread_account_v1(const char *user, int user_len,
|
|
const char *host, int host_len)
|
|
{
|
|
pfs_dirty_state dirty_state;
|
|
PFS_thread *pfs= my_thread_get_THR_PFS();
|
|
|
|
assert((user != NULL) || (user_len == 0));
|
|
assert(user_len >= 0);
|
|
assert((uint) user_len <= sizeof(pfs->m_username));
|
|
assert((host != NULL) || (host_len == 0));
|
|
assert(host_len >= 0);
|
|
|
|
host_len= MY_MIN(host_len, static_cast<int>(sizeof(pfs->m_hostname)));
|
|
|
|
if (unlikely(pfs == NULL))
|
|
return;
|
|
|
|
pfs->m_session_lock.allocated_to_dirty(& dirty_state);
|
|
|
|
clear_thread_account(pfs);
|
|
|
|
if (host_len > 0)
|
|
memcpy(pfs->m_hostname, host, host_len);
|
|
pfs->m_hostname_length= host_len;
|
|
|
|
if (user_len > 0)
|
|
memcpy(pfs->m_username, user, user_len);
|
|
pfs->m_username_length= user_len;
|
|
|
|
set_thread_account(pfs);
|
|
|
|
bool enabled;
|
|
bool history;
|
|
if (pfs->m_account != NULL)
|
|
{
|
|
enabled= pfs->m_account->m_enabled;
|
|
history= pfs->m_account->m_history;
|
|
}
|
|
else
|
|
{
|
|
if ((pfs->m_username_length > 0) && (pfs->m_hostname_length > 0))
|
|
{
|
|
lookup_setup_actor(pfs,
|
|
pfs->m_username, pfs->m_username_length,
|
|
pfs->m_hostname, pfs->m_hostname_length,
|
|
&enabled, &history);
|
|
}
|
|
else
|
|
{
|
|
/* There is no setting for background threads */
|
|
enabled= true;
|
|
history= true;
|
|
}
|
|
}
|
|
pfs->set_enabled(enabled);
|
|
pfs->set_history(history);
|
|
|
|
pfs->m_session_lock.dirty_to_allocated(& dirty_state);
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::set_thread_db.
|
|
*/
|
|
void pfs_set_thread_db_v1(const char* db, int db_len)
|
|
{
|
|
PFS_thread *pfs= my_thread_get_THR_PFS();
|
|
|
|
assert((db != NULL) || (db_len == 0));
|
|
assert(db_len >= 0);
|
|
assert((uint) db_len <= sizeof(pfs->m_dbname));
|
|
|
|
if (likely(pfs != NULL))
|
|
{
|
|
pfs_dirty_state dirty_state;
|
|
pfs->m_stmt_lock.allocated_to_dirty(& dirty_state);
|
|
if (db_len > 0)
|
|
memcpy(pfs->m_dbname, db, db_len);
|
|
pfs->m_dbname_length= db_len;
|
|
pfs->m_stmt_lock.dirty_to_allocated(& dirty_state);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::set_thread_command.
|
|
*/
|
|
void pfs_set_thread_command_v1(int command)
|
|
{
|
|
PFS_thread *pfs= my_thread_get_THR_PFS();
|
|
|
|
assert(command >= 0);
|
|
assert(command <= (int) COM_END);
|
|
|
|
if (likely(pfs != NULL))
|
|
{
|
|
pfs->m_command= command;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::set_thread_connection_type.
|
|
*/
|
|
void pfs_set_connection_type_v1(opaque_vio_type conn_type)
|
|
{
|
|
PFS_thread *pfs= my_thread_get_THR_PFS();
|
|
|
|
if (likely(pfs != NULL))
|
|
{
|
|
pfs->m_connection_type= static_cast<enum_vio_type> (conn_type);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::set_thread_start_time.
|
|
*/
|
|
void pfs_set_thread_start_time_v1(time_t start_time)
|
|
{
|
|
PFS_thread *pfs= my_thread_get_THR_PFS();
|
|
|
|
if (likely(pfs != NULL))
|
|
{
|
|
pfs->m_start_time= start_time;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::set_thread_state.
|
|
*/
|
|
void pfs_set_thread_state_v1(const char* state)
|
|
{
|
|
/* DEPRECATED. */
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::set_thread_info.
|
|
*/
|
|
void pfs_set_thread_info_v1(const char* info, uint info_len)
|
|
{
|
|
pfs_dirty_state dirty_state;
|
|
PFS_thread *pfs= my_thread_get_THR_PFS();
|
|
|
|
assert((info != NULL) || (info_len == 0));
|
|
|
|
if (likely(pfs != NULL))
|
|
{
|
|
if ((info != NULL) && (info_len > 0))
|
|
{
|
|
if (info_len > sizeof(pfs->m_processlist_info))
|
|
info_len= sizeof(pfs->m_processlist_info);
|
|
|
|
pfs->m_stmt_lock.allocated_to_dirty(& dirty_state);
|
|
memcpy(pfs->m_processlist_info, info, info_len);
|
|
pfs->m_processlist_info_length= info_len;
|
|
pfs->m_stmt_lock.dirty_to_allocated(& dirty_state);
|
|
}
|
|
else
|
|
{
|
|
pfs->m_stmt_lock.allocated_to_dirty(& dirty_state);
|
|
pfs->m_processlist_info_length= 0;
|
|
pfs->m_stmt_lock.dirty_to_allocated(& dirty_state);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::set_thread.
|
|
*/
|
|
void pfs_set_thread_v1(PSI_thread* thread)
|
|
{
|
|
PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread);
|
|
my_thread_set_THR_PFS(pfs);
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
*/
|
|
void pfs_set_thread_peer_port_v1(PSI_thread *thread, uint port) {
|
|
PFS_thread *pfs = reinterpret_cast<PFS_thread *>(thread);
|
|
if (likely(pfs != NULL)) {
|
|
pfs->m_peer_port = port;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::delete_current_thread.
|
|
*/
|
|
void pfs_delete_current_thread_v1(void)
|
|
{
|
|
PFS_thread *thread= my_thread_get_THR_PFS();
|
|
if (thread != NULL)
|
|
{
|
|
aggregate_thread(thread, thread->m_account, thread->m_user, thread->m_host);
|
|
my_thread_set_THR_PFS(NULL);
|
|
destroy_thread(thread);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread instrumentation interface.
|
|
@sa PSI_v1::delete_thread.
|
|
*/
|
|
void pfs_delete_thread_v1(PSI_thread *thread)
|
|
{
|
|
PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread);
|
|
|
|
if (pfs != NULL)
|
|
{
|
|
aggregate_thread(pfs, pfs->m_account, pfs->m_user, pfs->m_host);
|
|
destroy_thread(pfs);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the mutex instrumentation interface.
|
|
@sa PSI_v1::start_mutex_wait.
|
|
*/
|
|
PSI_mutex_locker*
|
|
pfs_start_mutex_wait_v1(PSI_mutex_locker_state *state,
|
|
PSI_mutex *mutex, PSI_mutex_operation op,
|
|
const char *src_file, uint src_line)
|
|
{
|
|
PFS_mutex *pfs_mutex= reinterpret_cast<PFS_mutex*> (mutex);
|
|
assert((int) op >= 0);
|
|
assert((uint) op < array_elements(mutex_operation_map));
|
|
assert(state != NULL);
|
|
|
|
assert(pfs_mutex != NULL);
|
|
assert(pfs_mutex->m_class != NULL);
|
|
|
|
if (! pfs_mutex->m_enabled)
|
|
return NULL;
|
|
|
|
uint flags;
|
|
ulonglong timer_start= 0;
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
if (! pfs_thread->m_enabled)
|
|
return NULL;
|
|
state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread);
|
|
flags= STATE_FLAG_THREAD;
|
|
|
|
if (pfs_mutex->m_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags|= STATE_FLAG_TIMED;
|
|
}
|
|
|
|
if (flag_events_waits_current)
|
|
{
|
|
if (unlikely(pfs_thread->m_events_waits_current >=
|
|
& pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE]))
|
|
{
|
|
locker_lost++;
|
|
return NULL;
|
|
}
|
|
PFS_events_waits *wait= pfs_thread->m_events_waits_current;
|
|
state->m_wait= wait;
|
|
flags|= STATE_FLAG_EVENT;
|
|
|
|
PFS_events_waits *parent_event= wait - 1;
|
|
wait->m_event_type= EVENT_TYPE_WAIT;
|
|
wait->m_nesting_event_id= parent_event->m_event_id;
|
|
wait->m_nesting_event_type= parent_event->m_event_type;
|
|
|
|
wait->m_thread_internal_id= pfs_thread->m_thread_internal_id;
|
|
wait->m_class= pfs_mutex->m_class;
|
|
wait->m_timer_start= timer_start;
|
|
wait->m_timer_end= 0;
|
|
wait->m_object_instance_addr= pfs_mutex->m_identity;
|
|
wait->m_event_id= pfs_thread->m_event_id++;
|
|
wait->m_end_event_id= 0;
|
|
wait->m_operation= mutex_operation_map[(int) op];
|
|
wait->m_source_file= src_file;
|
|
wait->m_source_line= src_line;
|
|
wait->m_wait_class= WAIT_CLASS_MUTEX;
|
|
|
|
pfs_thread->m_events_waits_current++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pfs_mutex->m_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags= STATE_FLAG_TIMED;
|
|
state->m_thread= NULL;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Complete shortcut.
|
|
*/
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */
|
|
pfs_mutex->m_mutex_stat.m_wait_stat.aggregate_counted();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
state->m_flags= flags;
|
|
state->m_mutex= mutex;
|
|
return reinterpret_cast<PSI_mutex_locker*> (state);
|
|
}
|
|
|
|
/**
|
|
Implementation of the rwlock instrumentation interface.
|
|
@sa PSI_v1::start_rwlock_rdwait
|
|
@sa PSI_v1::start_rwlock_wrwait
|
|
*/
|
|
PSI_rwlock_locker*
|
|
pfs_start_rwlock_wait_v1(PSI_rwlock_locker_state *state,
|
|
PSI_rwlock *rwlock,
|
|
PSI_rwlock_operation op,
|
|
const char *src_file, uint src_line)
|
|
{
|
|
PFS_rwlock *pfs_rwlock= reinterpret_cast<PFS_rwlock*> (rwlock);
|
|
assert(static_cast<int> (op) >= 0);
|
|
assert(static_cast<uint> (op) < array_elements(rwlock_operation_map));
|
|
assert(state != NULL);
|
|
assert(pfs_rwlock != NULL);
|
|
assert(pfs_rwlock->m_class != NULL);
|
|
|
|
/* Operations supported for READ WRITE LOCK */
|
|
|
|
assert( pfs_rwlock->m_class->is_shared_exclusive()
|
|
|| (op == PSI_RWLOCK_READLOCK)
|
|
|| (op == PSI_RWLOCK_WRITELOCK)
|
|
|| (op == PSI_RWLOCK_TRYREADLOCK)
|
|
|| (op == PSI_RWLOCK_TRYWRITELOCK)
|
|
);
|
|
|
|
/* Operations supported for SHARED EXCLUSIVE LOCK */
|
|
|
|
assert( ! pfs_rwlock->m_class->is_shared_exclusive()
|
|
|| (op == PSI_RWLOCK_SHAREDLOCK)
|
|
|| (op == PSI_RWLOCK_SHAREDEXCLUSIVELOCK)
|
|
|| (op == PSI_RWLOCK_EXCLUSIVELOCK)
|
|
|| (op == PSI_RWLOCK_TRYSHAREDLOCK)
|
|
|| (op == PSI_RWLOCK_TRYSHAREDEXCLUSIVELOCK)
|
|
|| (op == PSI_RWLOCK_TRYEXCLUSIVELOCK)
|
|
);
|
|
|
|
if (! pfs_rwlock->m_enabled)
|
|
return NULL;
|
|
|
|
uint flags;
|
|
ulonglong timer_start= 0;
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
if (! pfs_thread->m_enabled)
|
|
return NULL;
|
|
state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread);
|
|
flags= STATE_FLAG_THREAD;
|
|
|
|
if (pfs_rwlock->m_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags|= STATE_FLAG_TIMED;
|
|
}
|
|
|
|
if (flag_events_waits_current)
|
|
{
|
|
if (unlikely(pfs_thread->m_events_waits_current >=
|
|
& pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE]))
|
|
{
|
|
locker_lost++;
|
|
return NULL;
|
|
}
|
|
PFS_events_waits *wait= pfs_thread->m_events_waits_current;
|
|
state->m_wait= wait;
|
|
flags|= STATE_FLAG_EVENT;
|
|
|
|
PFS_events_waits *parent_event= wait - 1;
|
|
wait->m_event_type= EVENT_TYPE_WAIT;
|
|
wait->m_nesting_event_id= parent_event->m_event_id;
|
|
wait->m_nesting_event_type= parent_event->m_event_type;
|
|
|
|
wait->m_thread_internal_id= pfs_thread->m_thread_internal_id;
|
|
wait->m_class= pfs_rwlock->m_class;
|
|
wait->m_timer_start= timer_start;
|
|
wait->m_timer_end= 0;
|
|
wait->m_object_instance_addr= pfs_rwlock->m_identity;
|
|
wait->m_event_id= pfs_thread->m_event_id++;
|
|
wait->m_end_event_id= 0;
|
|
wait->m_operation= rwlock_operation_map[static_cast<int> (op)];
|
|
wait->m_source_file= src_file;
|
|
wait->m_source_line= src_line;
|
|
wait->m_wait_class= WAIT_CLASS_RWLOCK;
|
|
|
|
pfs_thread->m_events_waits_current++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pfs_rwlock->m_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags= STATE_FLAG_TIMED;
|
|
state->m_thread= NULL;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Complete shortcut.
|
|
*/
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */
|
|
pfs_rwlock->m_rwlock_stat.m_wait_stat.aggregate_counted();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
state->m_flags= flags;
|
|
state->m_rwlock= rwlock;
|
|
state->m_operation= op;
|
|
return reinterpret_cast<PSI_rwlock_locker*> (state);
|
|
}
|
|
|
|
PSI_rwlock_locker*
|
|
pfs_start_rwlock_rdwait_v1(PSI_rwlock_locker_state *state,
|
|
PSI_rwlock *rwlock,
|
|
PSI_rwlock_operation op,
|
|
const char *src_file, uint src_line)
|
|
{
|
|
assert((op == PSI_RWLOCK_READLOCK) ||
|
|
(op == PSI_RWLOCK_TRYREADLOCK) ||
|
|
(op == PSI_RWLOCK_SHAREDLOCK) ||
|
|
(op == PSI_RWLOCK_TRYSHAREDLOCK));
|
|
|
|
return pfs_start_rwlock_wait_v1(state, rwlock, op, src_file, src_line);
|
|
}
|
|
|
|
PSI_rwlock_locker*
|
|
pfs_start_rwlock_wrwait_v1(PSI_rwlock_locker_state *state,
|
|
PSI_rwlock *rwlock,
|
|
PSI_rwlock_operation op,
|
|
const char *src_file, uint src_line)
|
|
{
|
|
assert((op == PSI_RWLOCK_WRITELOCK) ||
|
|
(op == PSI_RWLOCK_TRYWRITELOCK) ||
|
|
(op == PSI_RWLOCK_SHAREDEXCLUSIVELOCK) ||
|
|
(op == PSI_RWLOCK_TRYSHAREDEXCLUSIVELOCK) ||
|
|
(op == PSI_RWLOCK_EXCLUSIVELOCK) ||
|
|
(op == PSI_RWLOCK_TRYEXCLUSIVELOCK));
|
|
|
|
return pfs_start_rwlock_wait_v1(state, rwlock, op, src_file, src_line);
|
|
}
|
|
|
|
/**
|
|
Implementation of the cond instrumentation interface.
|
|
@sa PSI_v1::start_cond_wait.
|
|
*/
|
|
PSI_cond_locker*
|
|
pfs_start_cond_wait_v1(PSI_cond_locker_state *state,
|
|
PSI_cond *cond, PSI_mutex *mutex,
|
|
PSI_cond_operation op,
|
|
const char *src_file, uint src_line)
|
|
{
|
|
/*
|
|
Note about the unused PSI_mutex *mutex parameter:
|
|
In the pthread library, a call to pthread_cond_wait()
|
|
causes an unlock() + lock() on the mutex associated with the condition.
|
|
This mutex operation is not instrumented, so the mutex will still
|
|
appear as locked when a thread is waiting on a condition.
|
|
This has no impact now, as unlock_mutex() is not recording events.
|
|
When unlock_mutex() is implemented by later work logs,
|
|
this parameter here will be used to adjust the mutex state,
|
|
in start_cond_wait_v1() and end_cond_wait_v1().
|
|
*/
|
|
PFS_cond *pfs_cond= reinterpret_cast<PFS_cond*> (cond);
|
|
assert(static_cast<int> (op) >= 0);
|
|
assert(static_cast<uint> (op) < array_elements(cond_operation_map));
|
|
assert(state != NULL);
|
|
assert(pfs_cond != NULL);
|
|
assert(pfs_cond->m_class != NULL);
|
|
|
|
if (! pfs_cond->m_enabled)
|
|
return NULL;
|
|
|
|
uint flags;
|
|
ulonglong timer_start= 0;
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
if (! pfs_thread->m_enabled)
|
|
return NULL;
|
|
state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread);
|
|
flags= STATE_FLAG_THREAD;
|
|
|
|
if (pfs_cond->m_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags|= STATE_FLAG_TIMED;
|
|
}
|
|
|
|
if (flag_events_waits_current)
|
|
{
|
|
if (unlikely(pfs_thread->m_events_waits_current >=
|
|
& pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE]))
|
|
{
|
|
locker_lost++;
|
|
return NULL;
|
|
}
|
|
PFS_events_waits *wait= pfs_thread->m_events_waits_current;
|
|
state->m_wait= wait;
|
|
flags|= STATE_FLAG_EVENT;
|
|
|
|
PFS_events_waits *parent_event= wait - 1;
|
|
wait->m_event_type= EVENT_TYPE_WAIT;
|
|
wait->m_nesting_event_id= parent_event->m_event_id;
|
|
wait->m_nesting_event_type= parent_event->m_event_type;
|
|
|
|
wait->m_thread_internal_id= pfs_thread->m_thread_internal_id;
|
|
wait->m_class= pfs_cond->m_class;
|
|
wait->m_timer_start= timer_start;
|
|
wait->m_timer_end= 0;
|
|
wait->m_object_instance_addr= pfs_cond->m_identity;
|
|
wait->m_event_id= pfs_thread->m_event_id++;
|
|
wait->m_end_event_id= 0;
|
|
wait->m_operation= cond_operation_map[static_cast<int> (op)];
|
|
wait->m_source_file= src_file;
|
|
wait->m_source_line= src_line;
|
|
wait->m_wait_class= WAIT_CLASS_COND;
|
|
|
|
pfs_thread->m_events_waits_current++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pfs_cond->m_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags= STATE_FLAG_TIMED;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Complete shortcut.
|
|
*/
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */
|
|
pfs_cond->m_cond_stat.m_wait_stat.aggregate_counted();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
state->m_flags= flags;
|
|
state->m_cond= cond;
|
|
state->m_mutex= mutex;
|
|
return reinterpret_cast<PSI_cond_locker*> (state);
|
|
}
|
|
|
|
static inline PFS_TL_LOCK_TYPE lock_flags_to_lock_type(uint flags)
|
|
{
|
|
enum thr_lock_type value= static_cast<enum thr_lock_type> (flags);
|
|
|
|
switch (value)
|
|
{
|
|
case TL_READ:
|
|
return PFS_TL_READ;
|
|
case TL_READ_WITH_SHARED_LOCKS:
|
|
return PFS_TL_READ_WITH_SHARED_LOCKS;
|
|
case TL_READ_HIGH_PRIORITY:
|
|
return PFS_TL_READ_HIGH_PRIORITY;
|
|
case TL_READ_NO_INSERT:
|
|
return PFS_TL_READ_NO_INSERT;
|
|
case TL_WRITE_ALLOW_WRITE:
|
|
return PFS_TL_WRITE_ALLOW_WRITE;
|
|
case TL_WRITE_CONCURRENT_INSERT:
|
|
return PFS_TL_WRITE_CONCURRENT_INSERT;
|
|
case TL_WRITE_DELAYED:
|
|
return PFS_TL_WRITE_DELAYED;
|
|
case TL_WRITE_LOW_PRIORITY:
|
|
return PFS_TL_WRITE_LOW_PRIORITY;
|
|
case TL_WRITE:
|
|
return PFS_TL_WRITE;
|
|
|
|
case TL_WRITE_ONLY:
|
|
case TL_IGNORE:
|
|
case TL_UNLOCK:
|
|
case TL_READ_DEFAULT:
|
|
case TL_WRITE_DEFAULT:
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
/* Dead code */
|
|
return PFS_TL_READ;
|
|
}
|
|
|
|
static inline PFS_TL_LOCK_TYPE external_lock_flags_to_lock_type(uint flags)
|
|
{
|
|
assert(flags == F_RDLCK || flags == F_WRLCK);
|
|
return (flags == F_RDLCK ? PFS_TL_READ_EXTERNAL : PFS_TL_WRITE_EXTERNAL);
|
|
}
|
|
|
|
/**
|
|
Implementation of the table instrumentation interface.
|
|
@sa PSI_v1::start_table_io_wait_v1
|
|
*/
|
|
PSI_table_locker*
|
|
pfs_start_table_io_wait_v1(PSI_table_locker_state *state,
|
|
PSI_table *table,
|
|
PSI_table_io_operation op,
|
|
uint index,
|
|
const char *src_file, uint src_line)
|
|
{
|
|
assert(static_cast<int> (op) >= 0);
|
|
assert(static_cast<uint> (op) < array_elements(table_io_operation_map));
|
|
assert(state != NULL);
|
|
PFS_table *pfs_table= reinterpret_cast<PFS_table*> (table);
|
|
assert(pfs_table != NULL);
|
|
assert(pfs_table->m_share != NULL);
|
|
|
|
if (! pfs_table->m_io_enabled)
|
|
return NULL;
|
|
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
|
|
uint flags;
|
|
ulonglong timer_start= 0;
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
if (pfs_thread == NULL)
|
|
return NULL;
|
|
if (! pfs_thread->m_enabled)
|
|
return NULL;
|
|
state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread);
|
|
flags= STATE_FLAG_THREAD;
|
|
|
|
if (pfs_table->m_io_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags|= STATE_FLAG_TIMED;
|
|
}
|
|
|
|
if (flag_events_waits_current)
|
|
{
|
|
if (unlikely(pfs_thread->m_events_waits_current >=
|
|
& pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE]))
|
|
{
|
|
locker_lost++;
|
|
return NULL;
|
|
}
|
|
PFS_events_waits *wait= pfs_thread->m_events_waits_current;
|
|
state->m_wait= wait;
|
|
flags|= STATE_FLAG_EVENT;
|
|
|
|
PFS_events_waits *parent_event= wait - 1;
|
|
wait->m_event_type= EVENT_TYPE_WAIT;
|
|
wait->m_nesting_event_id= parent_event->m_event_id;
|
|
wait->m_nesting_event_type= parent_event->m_event_type;
|
|
|
|
PFS_table_share *share= pfs_table->m_share;
|
|
wait->m_thread_internal_id= pfs_thread->m_thread_internal_id;
|
|
wait->m_class= &global_table_io_class;
|
|
wait->m_timer_start= timer_start;
|
|
wait->m_timer_end= 0;
|
|
wait->m_object_instance_addr= pfs_table->m_identity;
|
|
wait->m_event_id= pfs_thread->m_event_id++;
|
|
wait->m_end_event_id= 0;
|
|
wait->m_operation= table_io_operation_map[static_cast<int> (op)];
|
|
wait->m_flags= 0;
|
|
wait->m_object_type= share->get_object_type();
|
|
wait->m_weak_table_share= share;
|
|
wait->m_weak_version= share->get_version();
|
|
wait->m_index= index;
|
|
wait->m_source_file= src_file;
|
|
wait->m_source_line= src_line;
|
|
wait->m_wait_class= WAIT_CLASS_TABLE;
|
|
|
|
pfs_thread->m_events_waits_current++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pfs_table->m_io_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags= STATE_FLAG_TIMED;
|
|
}
|
|
else
|
|
{
|
|
/* TODO: consider a shortcut here */
|
|
flags= 0;
|
|
}
|
|
}
|
|
|
|
state->m_flags= flags;
|
|
state->m_table= table;
|
|
state->m_io_operation= op;
|
|
state->m_index= index;
|
|
return reinterpret_cast<PSI_table_locker*> (state);
|
|
}
|
|
|
|
/**
|
|
Implementation of the table instrumentation interface.
|
|
@sa PSI_v1::start_table_lock_wait.
|
|
*/
|
|
PSI_table_locker*
|
|
pfs_start_table_lock_wait_v1(PSI_table_locker_state *state,
|
|
PSI_table *table,
|
|
PSI_table_lock_operation op,
|
|
ulong op_flags,
|
|
const char *src_file, uint src_line)
|
|
{
|
|
assert(state != NULL);
|
|
assert((op == PSI_TABLE_LOCK) || (op == PSI_TABLE_EXTERNAL_LOCK));
|
|
|
|
PFS_table *pfs_table= reinterpret_cast<PFS_table*> (table);
|
|
|
|
assert(pfs_table != NULL);
|
|
assert(pfs_table->m_share != NULL);
|
|
|
|
if (! pfs_table->m_lock_enabled)
|
|
return NULL;
|
|
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
|
|
PFS_TL_LOCK_TYPE lock_type;
|
|
|
|
switch (op)
|
|
{
|
|
case PSI_TABLE_LOCK:
|
|
lock_type= lock_flags_to_lock_type(op_flags);
|
|
pfs_table->m_internal_lock= lock_type;
|
|
break;
|
|
case PSI_TABLE_EXTERNAL_LOCK:
|
|
/*
|
|
See the handler::external_lock() API design,
|
|
there is no handler::external_unlock().
|
|
*/
|
|
if (op_flags == F_UNLCK)
|
|
{
|
|
pfs_table->m_external_lock= PFS_TL_NONE;
|
|
return NULL;
|
|
}
|
|
lock_type= external_lock_flags_to_lock_type(op_flags);
|
|
pfs_table->m_external_lock= lock_type;
|
|
break;
|
|
default:
|
|
lock_type= PFS_TL_READ;
|
|
assert(false);
|
|
}
|
|
|
|
assert((uint) lock_type < array_elements(table_lock_operation_map));
|
|
|
|
uint flags;
|
|
ulonglong timer_start= 0;
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
if (pfs_thread == NULL)
|
|
return NULL;
|
|
if (! pfs_thread->m_enabled)
|
|
return NULL;
|
|
state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread);
|
|
flags= STATE_FLAG_THREAD;
|
|
|
|
if (pfs_table->m_lock_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags|= STATE_FLAG_TIMED;
|
|
}
|
|
|
|
if (flag_events_waits_current)
|
|
{
|
|
if (unlikely(pfs_thread->m_events_waits_current >=
|
|
& pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE]))
|
|
{
|
|
locker_lost++;
|
|
return NULL;
|
|
}
|
|
PFS_events_waits *wait= pfs_thread->m_events_waits_current;
|
|
state->m_wait= wait;
|
|
flags|= STATE_FLAG_EVENT;
|
|
|
|
PFS_events_waits *parent_event= wait - 1;
|
|
wait->m_event_type= EVENT_TYPE_WAIT;
|
|
wait->m_nesting_event_id= parent_event->m_event_id;
|
|
wait->m_nesting_event_type= parent_event->m_event_type;
|
|
|
|
PFS_table_share *share= pfs_table->m_share;
|
|
wait->m_thread_internal_id= pfs_thread->m_thread_internal_id;
|
|
wait->m_class= &global_table_lock_class;
|
|
wait->m_timer_start= timer_start;
|
|
wait->m_timer_end= 0;
|
|
wait->m_object_instance_addr= pfs_table->m_identity;
|
|
wait->m_event_id= pfs_thread->m_event_id++;
|
|
wait->m_end_event_id= 0;
|
|
wait->m_operation= table_lock_operation_map[lock_type];
|
|
wait->m_flags= 0;
|
|
wait->m_object_type= share->get_object_type();
|
|
wait->m_weak_table_share= share;
|
|
wait->m_weak_version= share->get_version();
|
|
wait->m_index= 0;
|
|
wait->m_source_file= src_file;
|
|
wait->m_source_line= src_line;
|
|
wait->m_wait_class= WAIT_CLASS_TABLE;
|
|
|
|
pfs_thread->m_events_waits_current++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pfs_table->m_lock_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags= STATE_FLAG_TIMED;
|
|
}
|
|
else
|
|
{
|
|
/* TODO: consider a shortcut here */
|
|
flags= 0;
|
|
}
|
|
}
|
|
|
|
state->m_flags= flags;
|
|
state->m_table= table;
|
|
state->m_index= lock_type;
|
|
return reinterpret_cast<PSI_table_locker*> (state);
|
|
}
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::get_thread_file_name_locker.
|
|
*/
|
|
PSI_file_locker*
|
|
pfs_get_thread_file_name_locker_v1(PSI_file_locker_state *state,
|
|
PSI_file_key key,
|
|
PSI_file_operation op,
|
|
const char *name, const void *identity)
|
|
{
|
|
assert(static_cast<int> (op) >= 0);
|
|
assert(static_cast<uint> (op) < array_elements(file_operation_map));
|
|
assert(state != NULL);
|
|
|
|
if (psi_unlikely(! flag_global_instrumentation))
|
|
return NULL;
|
|
PFS_file_class *klass= find_file_class(key);
|
|
if (unlikely(klass == NULL))
|
|
return NULL;
|
|
if (! klass->m_enabled)
|
|
return NULL;
|
|
|
|
/* Needed for the LF_HASH */
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
|
|
if (flag_thread_instrumentation && ! pfs_thread->m_enabled)
|
|
return NULL;
|
|
|
|
uint flags;
|
|
|
|
state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread);
|
|
flags= STATE_FLAG_THREAD;
|
|
|
|
if (klass->m_timed)
|
|
flags|= STATE_FLAG_TIMED;
|
|
|
|
if (flag_events_waits_current)
|
|
{
|
|
if (unlikely(pfs_thread->m_events_waits_current >=
|
|
& pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE]))
|
|
{
|
|
locker_lost++;
|
|
return NULL;
|
|
}
|
|
PFS_events_waits *wait= pfs_thread->m_events_waits_current;
|
|
state->m_wait= wait;
|
|
flags|= STATE_FLAG_EVENT;
|
|
|
|
PFS_events_waits *parent_event= wait - 1;
|
|
wait->m_event_type= EVENT_TYPE_WAIT;
|
|
wait->m_nesting_event_id= parent_event->m_event_id;
|
|
wait->m_nesting_event_type= parent_event->m_event_type;
|
|
|
|
wait->m_thread_internal_id= pfs_thread->m_thread_internal_id;
|
|
wait->m_class= klass;
|
|
wait->m_timer_start= 0;
|
|
wait->m_timer_end= 0;
|
|
wait->m_object_instance_addr= NULL;
|
|
wait->m_weak_file= NULL;
|
|
wait->m_weak_version= 0;
|
|
wait->m_event_id= pfs_thread->m_event_id++;
|
|
wait->m_end_event_id= 0;
|
|
wait->m_operation= file_operation_map[static_cast<int> (op)];
|
|
wait->m_wait_class= WAIT_CLASS_FILE;
|
|
|
|
pfs_thread->m_events_waits_current++;
|
|
}
|
|
|
|
state->m_flags= flags;
|
|
state->m_file= NULL;
|
|
state->m_name= name;
|
|
state->m_class= klass;
|
|
state->m_operation= op;
|
|
return reinterpret_cast<PSI_file_locker*> (state);
|
|
}
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::get_thread_file_stream_locker.
|
|
*/
|
|
PSI_file_locker*
|
|
pfs_get_thread_file_stream_locker_v1(PSI_file_locker_state *state,
|
|
PSI_file *file, PSI_file_operation op)
|
|
{
|
|
PFS_file *pfs_file= reinterpret_cast<PFS_file*> (file);
|
|
assert(static_cast<int> (op) >= 0);
|
|
assert(static_cast<uint> (op) < array_elements(file_operation_map));
|
|
assert(state != NULL);
|
|
|
|
if (unlikely(pfs_file == NULL))
|
|
return NULL;
|
|
assert(pfs_file->m_class != NULL);
|
|
PFS_file_class *klass= pfs_file->m_class;
|
|
|
|
if (! pfs_file->m_enabled)
|
|
return NULL;
|
|
|
|
/* Needed for the LF_HASH */
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
|
|
uint flags;
|
|
|
|
/* Always populated */
|
|
state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread);
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
if (! pfs_thread->m_enabled)
|
|
return NULL;
|
|
flags= STATE_FLAG_THREAD;
|
|
|
|
if (pfs_file->m_timed)
|
|
flags|= STATE_FLAG_TIMED;
|
|
|
|
if (flag_events_waits_current)
|
|
{
|
|
if (unlikely(pfs_thread->m_events_waits_current >=
|
|
& pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE]))
|
|
{
|
|
locker_lost++;
|
|
return NULL;
|
|
}
|
|
PFS_events_waits *wait= pfs_thread->m_events_waits_current;
|
|
state->m_wait= wait;
|
|
flags|= STATE_FLAG_EVENT;
|
|
|
|
PFS_events_waits *parent_event= wait - 1;
|
|
wait->m_event_type= EVENT_TYPE_WAIT;
|
|
wait->m_nesting_event_id= parent_event->m_event_id;
|
|
wait->m_nesting_event_type= parent_event->m_event_type;
|
|
|
|
wait->m_thread_internal_id= pfs_thread->m_thread_internal_id;
|
|
wait->m_class= klass;
|
|
wait->m_timer_start= 0;
|
|
wait->m_timer_end= 0;
|
|
wait->m_object_instance_addr= pfs_file;
|
|
wait->m_weak_file= pfs_file;
|
|
wait->m_weak_version= pfs_file->get_version();
|
|
wait->m_event_id= pfs_thread->m_event_id++;
|
|
wait->m_end_event_id= 0;
|
|
wait->m_operation= file_operation_map[static_cast<int> (op)];
|
|
wait->m_wait_class= WAIT_CLASS_FILE;
|
|
|
|
pfs_thread->m_events_waits_current++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pfs_file->m_timed)
|
|
{
|
|
flags= STATE_FLAG_TIMED;
|
|
}
|
|
else
|
|
{
|
|
/* TODO: consider a shortcut. */
|
|
flags= 0;
|
|
}
|
|
}
|
|
|
|
state->m_flags= flags;
|
|
state->m_file= reinterpret_cast<PSI_file*> (pfs_file);
|
|
state->m_operation= op;
|
|
state->m_name= NULL;
|
|
state->m_class= klass;
|
|
return reinterpret_cast<PSI_file_locker*> (state);
|
|
}
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::get_thread_file_descriptor_locker.
|
|
*/
|
|
PSI_file_locker*
|
|
pfs_get_thread_file_descriptor_locker_v1(PSI_file_locker_state *state,
|
|
File file, PSI_file_operation op)
|
|
{
|
|
int index= static_cast<int> (file);
|
|
assert(static_cast<int> (op) >= 0);
|
|
assert(static_cast<uint> (op) < array_elements(file_operation_map));
|
|
assert(state != NULL);
|
|
|
|
if (unlikely((index < 0) || (index >= file_handle_max)))
|
|
return NULL;
|
|
|
|
PFS_file *pfs_file= file_handle_array[index];
|
|
if (unlikely(pfs_file == NULL))
|
|
return NULL;
|
|
|
|
/*
|
|
We are about to close a file by descriptor number,
|
|
and the calling code still holds the descriptor.
|
|
Cleanup the file descriptor <--> file instrument association.
|
|
Remove the instrumentation *before* the close to avoid race
|
|
conditions with another thread opening a file
|
|
(that could be given the same descriptor).
|
|
*/
|
|
if (op == PSI_FILE_CLOSE)
|
|
file_handle_array[index]= NULL;
|
|
|
|
if (! pfs_file->m_enabled)
|
|
return NULL;
|
|
|
|
assert(pfs_file->m_class != NULL);
|
|
PFS_file_class *klass= pfs_file->m_class;
|
|
|
|
/* Needed for the LF_HASH */
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
|
|
uint flags;
|
|
|
|
/* Always populated */
|
|
state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread);
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
if (! pfs_thread->m_enabled)
|
|
return NULL;
|
|
flags= STATE_FLAG_THREAD;
|
|
|
|
if (pfs_file->m_timed)
|
|
flags|= STATE_FLAG_TIMED;
|
|
|
|
if (flag_events_waits_current)
|
|
{
|
|
if (unlikely(pfs_thread->m_events_waits_current >=
|
|
& pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE]))
|
|
{
|
|
locker_lost++;
|
|
return NULL;
|
|
}
|
|
PFS_events_waits *wait= pfs_thread->m_events_waits_current;
|
|
state->m_wait= wait;
|
|
flags|= STATE_FLAG_EVENT;
|
|
|
|
PFS_events_waits *parent_event= wait - 1;
|
|
wait->m_event_type= EVENT_TYPE_WAIT;
|
|
wait->m_nesting_event_id= parent_event->m_event_id;
|
|
wait->m_nesting_event_type= parent_event->m_event_type;
|
|
|
|
wait->m_thread_internal_id= pfs_thread->m_thread_internal_id;
|
|
wait->m_class= klass;
|
|
wait->m_timer_start= 0;
|
|
wait->m_timer_end= 0;
|
|
wait->m_object_instance_addr= pfs_file;
|
|
wait->m_weak_file= pfs_file;
|
|
wait->m_weak_version= pfs_file->get_version();
|
|
wait->m_event_id= pfs_thread->m_event_id++;
|
|
wait->m_end_event_id= 0;
|
|
wait->m_operation= file_operation_map[static_cast<int> (op)];
|
|
wait->m_wait_class= WAIT_CLASS_FILE;
|
|
|
|
pfs_thread->m_events_waits_current++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pfs_file->m_timed)
|
|
{
|
|
flags= STATE_FLAG_TIMED;
|
|
}
|
|
else
|
|
{
|
|
/* TODO: consider a shortcut. */
|
|
flags= 0;
|
|
}
|
|
}
|
|
|
|
state->m_flags= flags;
|
|
state->m_file= reinterpret_cast<PSI_file*> (pfs_file);
|
|
state->m_operation= op;
|
|
state->m_name= NULL;
|
|
state->m_class= klass;
|
|
return reinterpret_cast<PSI_file_locker*> (state);
|
|
}
|
|
|
|
/** Socket locker */
|
|
|
|
PSI_socket_locker*
|
|
pfs_start_socket_wait_v1(PSI_socket_locker_state *state,
|
|
PSI_socket *socket,
|
|
PSI_socket_operation op,
|
|
size_t count,
|
|
const char *src_file, uint src_line)
|
|
{
|
|
assert(static_cast<int> (op) >= 0);
|
|
assert(static_cast<uint> (op) < array_elements(socket_operation_map));
|
|
assert(state != NULL);
|
|
PFS_socket *pfs_socket= reinterpret_cast<PFS_socket*> (socket);
|
|
|
|
assert(pfs_socket != NULL);
|
|
assert(pfs_socket->m_class != NULL);
|
|
|
|
if (!pfs_socket->m_enabled || pfs_socket->m_idle)
|
|
return NULL;
|
|
|
|
uint flags= 0;
|
|
ulonglong timer_start= 0;
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
/*
|
|
Do not use pfs_socket->m_thread_owner here,
|
|
as different threads may use concurrently the same socket,
|
|
for example during a KILL.
|
|
*/
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
|
|
if (!pfs_thread->m_enabled)
|
|
return NULL;
|
|
|
|
state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread);
|
|
flags= STATE_FLAG_THREAD;
|
|
|
|
if (pfs_socket->m_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags|= STATE_FLAG_TIMED;
|
|
}
|
|
|
|
if (flag_events_waits_current)
|
|
{
|
|
if (unlikely(pfs_thread->m_events_waits_current >=
|
|
& pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE]))
|
|
{
|
|
locker_lost++;
|
|
return NULL;
|
|
}
|
|
PFS_events_waits *wait= pfs_thread->m_events_waits_current;
|
|
state->m_wait= wait;
|
|
flags|= STATE_FLAG_EVENT;
|
|
|
|
PFS_events_waits *parent_event= wait - 1;
|
|
wait->m_event_type= EVENT_TYPE_WAIT;
|
|
wait->m_nesting_event_id= parent_event->m_event_id;
|
|
wait->m_nesting_event_type= parent_event->m_event_type;
|
|
wait->m_thread_internal_id= pfs_thread->m_thread_internal_id;
|
|
wait->m_class= pfs_socket->m_class;
|
|
wait->m_timer_start= timer_start;
|
|
wait->m_timer_end= 0;
|
|
wait->m_object_instance_addr= pfs_socket->m_identity;
|
|
wait->m_weak_socket= pfs_socket;
|
|
wait->m_weak_version= pfs_socket->get_version();
|
|
wait->m_event_id= pfs_thread->m_event_id++;
|
|
wait->m_end_event_id= 0;
|
|
wait->m_operation= socket_operation_map[static_cast<int>(op)];
|
|
wait->m_source_file= src_file;
|
|
wait->m_source_line= src_line;
|
|
wait->m_number_of_bytes= count;
|
|
wait->m_wait_class= WAIT_CLASS_SOCKET;
|
|
|
|
pfs_thread->m_events_waits_current++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pfs_socket->m_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags= STATE_FLAG_TIMED;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Even if timing is disabled, end_socket_wait() still needs a locker to
|
|
capture the number of bytes sent or received by the socket operation.
|
|
For operations that do not have a byte count, then just increment the
|
|
event counter and return a NULL locker.
|
|
*/
|
|
switch (op)
|
|
{
|
|
case PSI_SOCKET_CONNECT:
|
|
case PSI_SOCKET_CREATE:
|
|
case PSI_SOCKET_BIND:
|
|
case PSI_SOCKET_SEEK:
|
|
case PSI_SOCKET_OPT:
|
|
case PSI_SOCKET_STAT:
|
|
case PSI_SOCKET_SHUTDOWN:
|
|
case PSI_SOCKET_CLOSE:
|
|
case PSI_SOCKET_SELECT:
|
|
pfs_socket->m_socket_stat.m_io_stat.m_misc.aggregate_counted();
|
|
return NULL;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
state->m_flags= flags;
|
|
state->m_socket= socket;
|
|
state->m_operation= op;
|
|
return reinterpret_cast<PSI_socket_locker*> (state);
|
|
}
|
|
|
|
/**
|
|
Implementation of the mutex instrumentation interface.
|
|
@sa PSI_v1::unlock_mutex.
|
|
*/
|
|
void pfs_unlock_mutex_v1(PSI_mutex *mutex)
|
|
{
|
|
PFS_mutex *pfs_mutex= reinterpret_cast<PFS_mutex*> (mutex);
|
|
|
|
assert(pfs_mutex != NULL);
|
|
|
|
/*
|
|
Note that this code is still protected by the instrumented mutex,
|
|
and therefore is thread safe. See inline_mysql_mutex_unlock().
|
|
*/
|
|
|
|
/* Always update the instrumented state */
|
|
pfs_mutex->m_owner= NULL;
|
|
pfs_mutex->m_last_locked= 0;
|
|
|
|
#ifdef LATER_WL2333
|
|
/*
|
|
See WL#2333: SHOW ENGINE ... LOCK STATUS.
|
|
PFS_mutex::m_lock_stat is not exposed in user visible tables
|
|
currently, so there is no point spending time computing it.
|
|
*/
|
|
if (! pfs_mutex->m_enabled)
|
|
return;
|
|
|
|
if (! pfs_mutex->m_timed)
|
|
return;
|
|
|
|
ulonglong locked_time;
|
|
locked_time= get_timer_pico_value(wait_timer) - pfs_mutex->m_last_locked;
|
|
pfs_mutex->m_mutex_stat.m_lock_stat.aggregate_value(locked_time);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
Implementation of the rwlock instrumentation interface.
|
|
@sa PSI_v1::unlock_rwlock.
|
|
*/
|
|
void pfs_unlock_rwlock_v1(PSI_rwlock *rwlock)
|
|
{
|
|
PFS_rwlock *pfs_rwlock= reinterpret_cast<PFS_rwlock*> (rwlock);
|
|
assert(pfs_rwlock != NULL);
|
|
assert(pfs_rwlock == sanitize_rwlock(pfs_rwlock));
|
|
assert(pfs_rwlock->m_class != NULL);
|
|
assert(pfs_rwlock->m_lock.is_populated());
|
|
|
|
bool last_writer= false;
|
|
bool last_reader= false;
|
|
|
|
/*
|
|
Note that this code is still protected by the instrumented rwlock,
|
|
and therefore is:
|
|
- thread safe for write locks
|
|
- almost thread safe for read locks (pfs_rwlock->m_readers is unsafe).
|
|
See inline_mysql_rwlock_unlock()
|
|
*/
|
|
|
|
/* Always update the instrumented state */
|
|
if (pfs_rwlock->m_writer != NULL)
|
|
{
|
|
/* Nominal case, a writer is unlocking. */
|
|
last_writer= true;
|
|
pfs_rwlock->m_writer= NULL;
|
|
/* Reset the readers stats, they could be off */
|
|
pfs_rwlock->m_readers= 0;
|
|
}
|
|
else if (likely(pfs_rwlock->m_readers > 0))
|
|
{
|
|
/* Nominal case, a reader is unlocking. */
|
|
if (--(pfs_rwlock->m_readers) == 0)
|
|
last_reader= true;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Edge case, we have no writer and no readers,
|
|
on an unlock event.
|
|
This is possible for:
|
|
- partial instrumentation
|
|
- instrumentation disabled at runtime,
|
|
see when get_thread_rwlock_locker_v1() returns NULL
|
|
No further action is taken here, the next
|
|
write lock will put the statistics is a valid state.
|
|
*/
|
|
}
|
|
|
|
#ifdef LATER_WL2333
|
|
/* See WL#2333: SHOW ENGINE ... LOCK STATUS. */
|
|
|
|
if (! pfs_rwlock->m_enabled)
|
|
return;
|
|
|
|
if (! pfs_rwlock->m_timed)
|
|
return;
|
|
|
|
ulonglong locked_time;
|
|
if (last_writer)
|
|
{
|
|
locked_time= get_timer_pico_value(wait_timer) - pfs_rwlock->m_last_written;
|
|
pfs_rwlock->m_rwlock_stat.m_write_lock_stat.aggregate_value(locked_time);
|
|
}
|
|
else if (last_reader)
|
|
{
|
|
locked_time= get_timer_pico_value(wait_timer) - pfs_rwlock->m_last_read;
|
|
pfs_rwlock->m_rwlock_stat.m_read_lock_stat.aggregate_value(locked_time);
|
|
}
|
|
#else
|
|
(void) last_reader;
|
|
(void) last_writer;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
Implementation of the cond instrumentation interface.
|
|
@sa PSI_v1::signal_cond.
|
|
*/
|
|
void pfs_signal_cond_v1(PSI_cond* cond)
|
|
{
|
|
#ifdef PFS_LATER
|
|
PFS_cond *pfs_cond= reinterpret_cast<PFS_cond*> (cond);
|
|
|
|
assert(pfs_cond != NULL);
|
|
|
|
pfs_cond->m_cond_stat.m_signal_count++;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
Implementation of the cond instrumentation interface.
|
|
@sa PSI_v1::broadcast_cond.
|
|
*/
|
|
void pfs_broadcast_cond_v1(PSI_cond* cond)
|
|
{
|
|
#ifdef PFS_LATER
|
|
PFS_cond *pfs_cond= reinterpret_cast<PFS_cond*> (cond);
|
|
|
|
assert(pfs_cond != NULL);
|
|
|
|
pfs_cond->m_cond_stat.m_broadcast_count++;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
Implementation of the idle instrumentation interface.
|
|
@sa PSI_v1::start_idle_wait.
|
|
*/
|
|
PSI_idle_locker*
|
|
pfs_start_idle_wait_v1(PSI_idle_locker_state* state, const char *src_file, uint src_line)
|
|
{
|
|
assert(state != NULL);
|
|
|
|
if (psi_unlikely(! flag_global_instrumentation))
|
|
return NULL;
|
|
|
|
if (!global_idle_class.m_enabled)
|
|
return NULL;
|
|
|
|
uint flags= 0;
|
|
ulonglong timer_start= 0;
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
if (!pfs_thread->m_enabled)
|
|
return NULL;
|
|
state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread);
|
|
flags= STATE_FLAG_THREAD;
|
|
|
|
assert(pfs_thread->m_events_statements_count == 0);
|
|
|
|
if (global_idle_class.m_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(idle_timer, &state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags|= STATE_FLAG_TIMED;
|
|
}
|
|
|
|
if (flag_events_waits_current)
|
|
{
|
|
if (unlikely(pfs_thread->m_events_waits_current >=
|
|
& pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE]))
|
|
{
|
|
locker_lost++;
|
|
return NULL;
|
|
}
|
|
PFS_events_waits *wait= pfs_thread->m_events_waits_current;
|
|
state->m_wait= wait;
|
|
flags|= STATE_FLAG_EVENT;
|
|
|
|
wait->m_event_type= EVENT_TYPE_WAIT;
|
|
/*
|
|
IDLE events are waits, but by definition we know that
|
|
such waits happen outside of any STAGE and STATEMENT,
|
|
so they have no parents.
|
|
*/
|
|
wait->m_nesting_event_id= 0;
|
|
/* no need to set wait->m_nesting_event_type */
|
|
|
|
wait->m_thread_internal_id= pfs_thread->m_thread_internal_id;
|
|
wait->m_class= &global_idle_class;
|
|
wait->m_timer_start= timer_start;
|
|
wait->m_timer_end= 0;
|
|
wait->m_event_id= pfs_thread->m_event_id++;
|
|
wait->m_end_event_id= 0;
|
|
wait->m_operation= OPERATION_TYPE_IDLE;
|
|
wait->m_source_file= src_file;
|
|
wait->m_source_line= src_line;
|
|
wait->m_wait_class= WAIT_CLASS_IDLE;
|
|
|
|
pfs_thread->m_events_waits_current++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (global_idle_class.m_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(idle_timer, &state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags= STATE_FLAG_TIMED;
|
|
}
|
|
}
|
|
|
|
state->m_flags= flags;
|
|
return reinterpret_cast<PSI_idle_locker*> (state);
|
|
}
|
|
|
|
/**
|
|
Implementation of the mutex instrumentation interface.
|
|
@sa PSI_v1::end_idle_wait.
|
|
*/
|
|
void pfs_end_idle_wait_v1(PSI_idle_locker* locker)
|
|
{
|
|
PSI_idle_locker_state *state= reinterpret_cast<PSI_idle_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
ulonglong timer_end= 0;
|
|
ulonglong wait_time= 0;
|
|
|
|
uint flags= state->m_flags;
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_end= state->m_timer();
|
|
wait_time= timer_end - state->m_timer_start;
|
|
}
|
|
|
|
if (flags & STATE_FLAG_THREAD)
|
|
{
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread);
|
|
PFS_single_stat *event_name_array;
|
|
event_name_array= thread->write_instr_class_waits_stats();
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */
|
|
event_name_array[GLOBAL_IDLE_EVENT_INDEX].aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */
|
|
event_name_array[GLOBAL_IDLE_EVENT_INDEX].aggregate_counted();
|
|
}
|
|
|
|
if (flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait);
|
|
assert(wait != NULL);
|
|
|
|
wait->m_timer_end= timer_end;
|
|
wait->m_end_event_id= thread->m_event_id;
|
|
if (thread->m_flag_events_waits_history)
|
|
insert_events_waits_history(thread, wait);
|
|
if (thread->m_flag_events_waits_history_long)
|
|
insert_events_waits_history_long(wait);
|
|
thread->m_events_waits_current--;
|
|
|
|
assert(wait == thread->m_events_waits_current);
|
|
}
|
|
}
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (timed) */
|
|
global_idle_stat.aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (counted) */
|
|
global_idle_stat.aggregate_counted();
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the mutex instrumentation interface.
|
|
@sa PSI_v1::end_mutex_wait.
|
|
*/
|
|
void pfs_end_mutex_wait_v1(PSI_mutex_locker* locker, int rc)
|
|
{
|
|
PSI_mutex_locker_state *state= reinterpret_cast<PSI_mutex_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
ulonglong timer_end= 0;
|
|
ulonglong wait_time= 0;
|
|
|
|
PFS_mutex *mutex= reinterpret_cast<PFS_mutex *> (state->m_mutex);
|
|
assert(mutex != NULL);
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread);
|
|
|
|
uint flags= state->m_flags;
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_end= state->m_timer();
|
|
wait_time= timer_end - state->m_timer_start;
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */
|
|
mutex->m_mutex_stat.m_wait_stat.aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */
|
|
mutex->m_mutex_stat.m_wait_stat.aggregate_counted();
|
|
}
|
|
|
|
if (likely(rc == 0))
|
|
{
|
|
mutex->m_owner= thread;
|
|
mutex->m_last_locked= timer_end;
|
|
}
|
|
|
|
if (flags & STATE_FLAG_THREAD)
|
|
{
|
|
PFS_single_stat *event_name_array;
|
|
event_name_array= thread->write_instr_class_waits_stats();
|
|
uint index= mutex->m_class->m_event_name_index;
|
|
|
|
assert(index <= wait_class_max);
|
|
assert(sanitize_thread(thread) != NULL);
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */
|
|
event_name_array[index].aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */
|
|
event_name_array[index].aggregate_counted();
|
|
}
|
|
|
|
if (flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait);
|
|
assert(wait != NULL);
|
|
|
|
wait->m_timer_end= timer_end;
|
|
wait->m_end_event_id= thread->m_event_id;
|
|
if (thread->m_flag_events_waits_history)
|
|
insert_events_waits_history(thread, wait);
|
|
if (thread->m_flag_events_waits_history_long)
|
|
insert_events_waits_history_long(wait);
|
|
thread->m_events_waits_current--;
|
|
|
|
assert(wait == thread->m_events_waits_current);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the rwlock instrumentation interface.
|
|
@sa PSI_v1::end_rwlock_rdwait.
|
|
*/
|
|
void pfs_end_rwlock_rdwait_v1(PSI_rwlock_locker* locker, int rc)
|
|
{
|
|
PSI_rwlock_locker_state *state= reinterpret_cast<PSI_rwlock_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
ulonglong timer_end= 0;
|
|
ulonglong wait_time= 0;
|
|
|
|
PFS_rwlock *rwlock= reinterpret_cast<PFS_rwlock *> (state->m_rwlock);
|
|
assert(rwlock != NULL);
|
|
|
|
if (state->m_flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_end= state->m_timer();
|
|
wait_time= timer_end - state->m_timer_start;
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */
|
|
rwlock->m_rwlock_stat.m_wait_stat.aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */
|
|
rwlock->m_rwlock_stat.m_wait_stat.aggregate_counted();
|
|
}
|
|
|
|
if (rc == 0)
|
|
{
|
|
/*
|
|
Warning:
|
|
Multiple threads can execute this section concurrently
|
|
(since multiple readers can execute in parallel).
|
|
The statistics generated are not safe, which is why they are
|
|
just statistics, not facts.
|
|
*/
|
|
if (rwlock->m_readers == 0)
|
|
rwlock->m_last_read= timer_end;
|
|
rwlock->m_writer= NULL;
|
|
rwlock->m_readers++;
|
|
}
|
|
|
|
if (state->m_flags & STATE_FLAG_THREAD)
|
|
{
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread);
|
|
assert(thread != NULL);
|
|
|
|
PFS_single_stat *event_name_array;
|
|
event_name_array= thread->write_instr_class_waits_stats();
|
|
uint index= rwlock->m_class->m_event_name_index;
|
|
|
|
if (state->m_flags & STATE_FLAG_TIMED)
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */
|
|
event_name_array[index].aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */
|
|
event_name_array[index].aggregate_counted();
|
|
}
|
|
|
|
if (state->m_flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait);
|
|
assert(wait != NULL);
|
|
|
|
wait->m_timer_end= timer_end;
|
|
wait->m_end_event_id= thread->m_event_id;
|
|
if (thread->m_flag_events_waits_history)
|
|
insert_events_waits_history(thread, wait);
|
|
if (thread->m_flag_events_waits_history_long)
|
|
insert_events_waits_history_long(wait);
|
|
thread->m_events_waits_current--;
|
|
|
|
assert(wait == thread->m_events_waits_current);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the rwlock instrumentation interface.
|
|
@sa PSI_v1::end_rwlock_wrwait.
|
|
*/
|
|
void pfs_end_rwlock_wrwait_v1(PSI_rwlock_locker* locker, int rc)
|
|
{
|
|
PSI_rwlock_locker_state *state= reinterpret_cast<PSI_rwlock_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
ulonglong timer_end= 0;
|
|
ulonglong wait_time= 0;
|
|
|
|
PFS_rwlock *rwlock= reinterpret_cast<PFS_rwlock *> (state->m_rwlock);
|
|
assert(rwlock != NULL);
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread);
|
|
|
|
if (state->m_flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_end= state->m_timer();
|
|
wait_time= timer_end - state->m_timer_start;
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */
|
|
rwlock->m_rwlock_stat.m_wait_stat.aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */
|
|
rwlock->m_rwlock_stat.m_wait_stat.aggregate_counted();
|
|
}
|
|
|
|
if (likely(rc == 0))
|
|
{
|
|
/* Thread safe : we are protected by the instrumented rwlock */
|
|
rwlock->m_writer= thread;
|
|
rwlock->m_last_written= timer_end;
|
|
|
|
if ((state->m_operation != PSI_RWLOCK_SHAREDEXCLUSIVELOCK) &&
|
|
(state->m_operation != PSI_RWLOCK_TRYSHAREDEXCLUSIVELOCK))
|
|
{
|
|
/* Reset the readers stats, they could be off */
|
|
rwlock->m_readers= 0;
|
|
rwlock->m_last_read= 0;
|
|
}
|
|
}
|
|
|
|
if (state->m_flags & STATE_FLAG_THREAD)
|
|
{
|
|
PFS_single_stat *event_name_array;
|
|
event_name_array= thread->write_instr_class_waits_stats();
|
|
uint index= rwlock->m_class->m_event_name_index;
|
|
|
|
if (state->m_flags & STATE_FLAG_TIMED)
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */
|
|
event_name_array[index].aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */
|
|
event_name_array[index].aggregate_counted();
|
|
}
|
|
|
|
if (state->m_flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait);
|
|
assert(wait != NULL);
|
|
|
|
wait->m_timer_end= timer_end;
|
|
wait->m_end_event_id= thread->m_event_id;
|
|
if (thread->m_flag_events_waits_history)
|
|
insert_events_waits_history(thread, wait);
|
|
if (thread->m_flag_events_waits_history_long)
|
|
insert_events_waits_history_long(wait);
|
|
thread->m_events_waits_current--;
|
|
|
|
assert(wait == thread->m_events_waits_current);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the cond instrumentation interface.
|
|
@sa PSI_v1::end_cond_wait.
|
|
*/
|
|
void pfs_end_cond_wait_v1(PSI_cond_locker* locker, int rc)
|
|
{
|
|
PSI_cond_locker_state *state= reinterpret_cast<PSI_cond_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
ulonglong timer_end= 0;
|
|
ulonglong wait_time= 0;
|
|
|
|
PFS_cond *cond= reinterpret_cast<PFS_cond *> (state->m_cond);
|
|
/* PFS_mutex *mutex= reinterpret_cast<PFS_mutex *> (state->m_mutex); */
|
|
|
|
if (state->m_flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_end= state->m_timer();
|
|
wait_time= timer_end - state->m_timer_start;
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */
|
|
cond->m_cond_stat.m_wait_stat.aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */
|
|
cond->m_cond_stat.m_wait_stat.aggregate_counted();
|
|
}
|
|
|
|
if (state->m_flags & STATE_FLAG_THREAD)
|
|
{
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread);
|
|
assert(thread != NULL);
|
|
|
|
PFS_single_stat *event_name_array;
|
|
event_name_array= thread->write_instr_class_waits_stats();
|
|
uint index= cond->m_class->m_event_name_index;
|
|
|
|
if (state->m_flags & STATE_FLAG_TIMED)
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */
|
|
event_name_array[index].aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */
|
|
event_name_array[index].aggregate_counted();
|
|
}
|
|
|
|
if (state->m_flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait);
|
|
assert(wait != NULL);
|
|
|
|
wait->m_timer_end= timer_end;
|
|
wait->m_end_event_id= thread->m_event_id;
|
|
if (thread->m_flag_events_waits_history)
|
|
insert_events_waits_history(thread, wait);
|
|
if (thread->m_flag_events_waits_history_long)
|
|
insert_events_waits_history_long(wait);
|
|
thread->m_events_waits_current--;
|
|
|
|
assert(wait == thread->m_events_waits_current);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the table instrumentation interface.
|
|
@sa PSI_v1::end_table_io_wait.
|
|
*/
|
|
void pfs_end_table_io_wait_v1(PSI_table_locker* locker, ulonglong numrows)
|
|
{
|
|
PSI_table_locker_state *state= reinterpret_cast<PSI_table_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
ulonglong timer_end= 0;
|
|
ulonglong wait_time= 0;
|
|
|
|
PFS_table *table= reinterpret_cast<PFS_table *> (state->m_table);
|
|
assert(table != NULL);
|
|
|
|
PFS_single_stat *stat;
|
|
PFS_table_io_stat *table_io_stat;
|
|
|
|
assert((state->m_index < table->m_share->m_key_count) ||
|
|
(state->m_index == MAX_INDEXES));
|
|
|
|
table_io_stat= & table->m_table_stat.m_index_stat[state->m_index];
|
|
table_io_stat->m_has_data= true;
|
|
|
|
switch (state->m_io_operation)
|
|
{
|
|
case PSI_TABLE_FETCH_ROW:
|
|
stat= & table_io_stat->m_fetch;
|
|
break;
|
|
case PSI_TABLE_WRITE_ROW:
|
|
stat= & table_io_stat->m_insert;
|
|
break;
|
|
case PSI_TABLE_UPDATE_ROW:
|
|
stat= & table_io_stat->m_update;
|
|
break;
|
|
case PSI_TABLE_DELETE_ROW:
|
|
stat= & table_io_stat->m_delete;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
stat= NULL;
|
|
break;
|
|
}
|
|
|
|
uint flags= state->m_flags;
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_end= state->m_timer();
|
|
wait_time= timer_end - state->m_timer_start;
|
|
stat->aggregate_many_value(wait_time, numrows);
|
|
}
|
|
else
|
|
{
|
|
stat->aggregate_counted(numrows);
|
|
}
|
|
|
|
if (flags & STATE_FLAG_THREAD)
|
|
{
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread);
|
|
assert(thread != NULL);
|
|
|
|
PFS_single_stat *event_name_array;
|
|
event_name_array= thread->write_instr_class_waits_stats();
|
|
|
|
/*
|
|
Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME
|
|
(for wait/io/table/sql/handler)
|
|
*/
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
event_name_array[GLOBAL_TABLE_IO_EVENT_INDEX].aggregate_many_value(wait_time, numrows);
|
|
}
|
|
else
|
|
{
|
|
event_name_array[GLOBAL_TABLE_IO_EVENT_INDEX].aggregate_counted(numrows);
|
|
}
|
|
|
|
if (flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait);
|
|
assert(wait != NULL);
|
|
|
|
wait->m_timer_end= timer_end;
|
|
wait->m_end_event_id= thread->m_event_id;
|
|
wait->m_number_of_bytes= static_cast<size_t>(numrows);
|
|
if (thread->m_flag_events_waits_history)
|
|
insert_events_waits_history(thread, wait);
|
|
if (thread->m_flag_events_waits_history_long)
|
|
insert_events_waits_history_long(wait);
|
|
thread->m_events_waits_current--;
|
|
|
|
assert(wait == thread->m_events_waits_current);
|
|
}
|
|
}
|
|
|
|
table->m_has_io_stats= true;
|
|
}
|
|
|
|
/**
|
|
Implementation of the table instrumentation interface.
|
|
@sa PSI_v1::end_table_lock_wait.
|
|
*/
|
|
void pfs_end_table_lock_wait_v1(PSI_table_locker* locker)
|
|
{
|
|
PSI_table_locker_state *state= reinterpret_cast<PSI_table_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
ulonglong timer_end= 0;
|
|
ulonglong wait_time= 0;
|
|
|
|
PFS_table *table= reinterpret_cast<PFS_table *> (state->m_table);
|
|
assert(table != NULL);
|
|
|
|
PFS_single_stat *stat= & table->m_table_stat.m_lock_stat.m_stat[state->m_index];
|
|
|
|
uint flags= state->m_flags;
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_end= state->m_timer();
|
|
wait_time= timer_end - state->m_timer_start;
|
|
stat->aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
stat->aggregate_counted();
|
|
}
|
|
|
|
if (flags & STATE_FLAG_THREAD)
|
|
{
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread);
|
|
assert(thread != NULL);
|
|
|
|
PFS_single_stat *event_name_array;
|
|
event_name_array= thread->write_instr_class_waits_stats();
|
|
|
|
/*
|
|
Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME
|
|
(for wait/lock/table/sql/handler)
|
|
*/
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
event_name_array[GLOBAL_TABLE_LOCK_EVENT_INDEX].aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
event_name_array[GLOBAL_TABLE_LOCK_EVENT_INDEX].aggregate_counted();
|
|
}
|
|
|
|
if (flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait);
|
|
assert(wait != NULL);
|
|
|
|
wait->m_timer_end= timer_end;
|
|
wait->m_end_event_id= thread->m_event_id;
|
|
if (thread->m_flag_events_waits_history)
|
|
insert_events_waits_history(thread, wait);
|
|
if (thread->m_flag_events_waits_history_long)
|
|
insert_events_waits_history_long(wait);
|
|
thread->m_events_waits_current--;
|
|
|
|
assert(wait == thread->m_events_waits_current);
|
|
}
|
|
}
|
|
|
|
table->m_has_lock_stats= true;
|
|
}
|
|
|
|
void pfs_start_file_wait_v1(PSI_file_locker *locker,
|
|
size_t count,
|
|
const char *src_file,
|
|
uint src_line);
|
|
|
|
void pfs_end_file_wait_v1(PSI_file_locker *locker,
|
|
size_t count);
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::start_file_open_wait.
|
|
*/
|
|
void pfs_start_file_open_wait_v1(PSI_file_locker *locker,
|
|
const char *src_file,
|
|
uint src_line)
|
|
{
|
|
pfs_start_file_wait_v1(locker, 0, src_file, src_line);
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::end_file_open_wait.
|
|
*/
|
|
PSI_file*
|
|
pfs_end_file_open_wait_v1(PSI_file_locker *locker,
|
|
void *result)
|
|
{
|
|
PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
switch (state->m_operation)
|
|
{
|
|
case PSI_FILE_STAT:
|
|
case PSI_FILE_RENAME:
|
|
break;
|
|
case PSI_FILE_STREAM_OPEN:
|
|
case PSI_FILE_CREATE:
|
|
case PSI_FILE_OPEN:
|
|
if (result != NULL)
|
|
{
|
|
PFS_file_class *klass= reinterpret_cast<PFS_file_class*> (state->m_class);
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread*> (state->m_thread);
|
|
const char *name= state->m_name;
|
|
uint len= (uint)strlen(name);
|
|
PFS_file *pfs_file= find_or_create_file(thread, klass, name, len, true);
|
|
state->m_file= reinterpret_cast<PSI_file*> (pfs_file);
|
|
}
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
pfs_end_file_wait_v1(locker, 0);
|
|
|
|
return state->m_file;
|
|
}
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::end_file_open_wait_and_bind_to_descriptor.
|
|
*/
|
|
void pfs_end_file_open_wait_and_bind_to_descriptor_v1
|
|
(PSI_file_locker *locker, File file)
|
|
{
|
|
PFS_file *pfs_file= NULL;
|
|
int index= (int) file;
|
|
PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
if (index >= 0)
|
|
{
|
|
PFS_file_class *klass= reinterpret_cast<PFS_file_class*> (state->m_class);
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread*> (state->m_thread);
|
|
const char *name= state->m_name;
|
|
uint len= (uint)strlen(name);
|
|
pfs_file= find_or_create_file(thread, klass, name, len, true);
|
|
state->m_file= reinterpret_cast<PSI_file*> (pfs_file);
|
|
}
|
|
|
|
pfs_end_file_wait_v1(locker, 0);
|
|
|
|
if (likely(index >= 0))
|
|
{
|
|
if (likely(index < file_handle_max))
|
|
file_handle_array[index]= pfs_file;
|
|
else
|
|
{
|
|
if (pfs_file != NULL)
|
|
release_file(pfs_file);
|
|
file_handle_lost++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::end_temp_file_open_wait_and_bind_to_descriptor.
|
|
*/
|
|
void pfs_end_temp_file_open_wait_and_bind_to_descriptor_v1
|
|
(PSI_file_locker *locker, File file, const char *filename)
|
|
{
|
|
assert(filename != NULL);
|
|
PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
/* Set filename that was generated during creation of temporary file. */
|
|
state->m_name= filename;
|
|
pfs_end_file_open_wait_and_bind_to_descriptor_v1(locker, file);
|
|
|
|
PFS_file *pfs_file= reinterpret_cast<PFS_file *> (state->m_file);
|
|
if (pfs_file != NULL)
|
|
{
|
|
pfs_file->m_temporary= true;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::start_file_wait.
|
|
*/
|
|
void pfs_start_file_wait_v1(PSI_file_locker *locker,
|
|
size_t count,
|
|
const char *src_file,
|
|
uint src_line)
|
|
{
|
|
ulonglong timer_start= 0;
|
|
PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
uint flags= state->m_flags;
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
}
|
|
|
|
if (flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait);
|
|
assert(wait != NULL);
|
|
|
|
wait->m_timer_start= timer_start;
|
|
wait->m_source_file= src_file;
|
|
wait->m_source_line= src_line;
|
|
wait->m_number_of_bytes= count;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::end_file_wait.
|
|
*/
|
|
void pfs_end_file_wait_v1(PSI_file_locker *locker,
|
|
size_t byte_count)
|
|
{
|
|
PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
PFS_file *file= reinterpret_cast<PFS_file *> (state->m_file);
|
|
PFS_file_class *klass= reinterpret_cast<PFS_file_class *> (state->m_class);
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread);
|
|
|
|
ulonglong timer_end= 0;
|
|
ulonglong wait_time= 0;
|
|
PFS_byte_stat *byte_stat;
|
|
uint flags= state->m_flags;
|
|
size_t bytes= ((int)byte_count > -1 ? byte_count : 0);
|
|
|
|
PFS_file_stat *file_stat;
|
|
|
|
if (file != NULL)
|
|
{
|
|
file_stat= & file->m_file_stat;
|
|
}
|
|
else
|
|
{
|
|
file_stat= & klass->m_file_stat;
|
|
}
|
|
|
|
switch (state->m_operation)
|
|
{
|
|
/* Group read operations */
|
|
case PSI_FILE_READ:
|
|
byte_stat= &file_stat->m_io_stat.m_read;
|
|
break;
|
|
/* Group write operations */
|
|
case PSI_FILE_WRITE:
|
|
byte_stat= &file_stat->m_io_stat.m_write;
|
|
break;
|
|
/* Group remaining operations as miscellaneous */
|
|
case PSI_FILE_CREATE:
|
|
case PSI_FILE_CREATE_TMP:
|
|
case PSI_FILE_OPEN:
|
|
case PSI_FILE_STREAM_OPEN:
|
|
case PSI_FILE_STREAM_CLOSE:
|
|
case PSI_FILE_SEEK:
|
|
case PSI_FILE_TELL:
|
|
case PSI_FILE_FLUSH:
|
|
case PSI_FILE_FSTAT:
|
|
case PSI_FILE_CHSIZE:
|
|
case PSI_FILE_DELETE:
|
|
case PSI_FILE_RENAME:
|
|
case PSI_FILE_SYNC:
|
|
case PSI_FILE_STAT:
|
|
case PSI_FILE_CLOSE:
|
|
byte_stat= &file_stat->m_io_stat.m_misc;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
byte_stat= NULL;
|
|
break;
|
|
}
|
|
|
|
/* Aggregation for EVENTS_WAITS_SUMMARY_BY_INSTANCE */
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_end= state->m_timer();
|
|
wait_time= timer_end - state->m_timer_start;
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */
|
|
byte_stat->aggregate(wait_time, bytes);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */
|
|
byte_stat->aggregate_counted(bytes);
|
|
}
|
|
|
|
if (flags & STATE_FLAG_THREAD)
|
|
{
|
|
assert(thread != NULL);
|
|
|
|
PFS_single_stat *event_name_array;
|
|
event_name_array= thread->write_instr_class_waits_stats();
|
|
uint index= klass->m_event_name_index;
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */
|
|
event_name_array[index].aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */
|
|
event_name_array[index].aggregate_counted();
|
|
}
|
|
|
|
if (state->m_flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait);
|
|
assert(wait != NULL);
|
|
|
|
wait->m_timer_end= timer_end;
|
|
wait->m_number_of_bytes= bytes;
|
|
wait->m_end_event_id= thread->m_event_id;
|
|
wait->m_object_instance_addr= file;
|
|
wait->m_weak_file= file;
|
|
wait->m_weak_version= (file ? file->get_version() : 0);
|
|
|
|
if (thread->m_flag_events_waits_history)
|
|
insert_events_waits_history(thread, wait);
|
|
if (thread->m_flag_events_waits_history_long)
|
|
insert_events_waits_history_long(wait);
|
|
thread->m_events_waits_current--;
|
|
|
|
assert(wait == thread->m_events_waits_current);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::start_file_close_wait.
|
|
*/
|
|
void pfs_start_file_close_wait_v1(PSI_file_locker *locker,
|
|
const char *src_file,
|
|
uint src_line)
|
|
{
|
|
PFS_thread *thread;
|
|
const char *name;
|
|
uint len;
|
|
PFS_file *pfs_file;
|
|
PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
switch (state->m_operation)
|
|
{
|
|
case PSI_FILE_DELETE:
|
|
thread= reinterpret_cast<PFS_thread*> (state->m_thread);
|
|
name= state->m_name;
|
|
len= (uint)strlen(name);
|
|
pfs_file= find_or_create_file(thread, NULL, name, len, false);
|
|
state->m_file= reinterpret_cast<PSI_file*> (pfs_file);
|
|
break;
|
|
case PSI_FILE_STREAM_CLOSE:
|
|
case PSI_FILE_CLOSE:
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
pfs_start_file_wait_v1(locker, 0, src_file, src_line);
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::end_file_close_wait.
|
|
*/
|
|
void pfs_end_file_close_wait_v1(PSI_file_locker *locker, int rc)
|
|
{
|
|
PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
pfs_end_file_wait_v1(locker, 0);
|
|
|
|
if (rc == 0)
|
|
{
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread*> (state->m_thread);
|
|
PFS_file *file= reinterpret_cast<PFS_file*> (state->m_file);
|
|
|
|
/* Release or destroy the file if necessary */
|
|
switch(state->m_operation)
|
|
{
|
|
case PSI_FILE_CLOSE:
|
|
if (file != NULL)
|
|
{
|
|
if (file->m_temporary)
|
|
{
|
|
assert(file->m_file_stat.m_open_count <= 1);
|
|
destroy_file(thread, file);
|
|
}
|
|
else
|
|
release_file(file);
|
|
}
|
|
break;
|
|
case PSI_FILE_STREAM_CLOSE:
|
|
if (file != NULL)
|
|
release_file(file);
|
|
break;
|
|
case PSI_FILE_DELETE:
|
|
if (file != NULL)
|
|
destroy_file(thread, file);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**
|
|
Implementation of the file instrumentation interface.
|
|
@sa PSI_v1::end_file_rename_wait.
|
|
*/
|
|
void pfs_end_file_rename_wait_v1(PSI_file_locker *locker, const char *old_name,
|
|
const char *new_name, int rc)
|
|
{
|
|
PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
assert(state->m_operation == PSI_FILE_RENAME);
|
|
|
|
if (rc == 0)
|
|
{
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread);
|
|
|
|
uint old_len= (uint)strlen(old_name);
|
|
uint new_len= (uint)strlen(new_name);
|
|
|
|
find_and_rename_file(thread, old_name, old_len, new_name, new_len);
|
|
}
|
|
|
|
pfs_end_file_wait_v1(locker, 0);
|
|
return;
|
|
}
|
|
|
|
PSI_stage_progress*
|
|
pfs_start_stage_v1(PSI_stage_key key, const char *src_file, int src_line)
|
|
{
|
|
ulonglong timer_value= 0;
|
|
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
|
|
/* Always update column threads.processlist_state. */
|
|
pfs_thread->m_stage= key;
|
|
/* Default value when the stage is not instrumented for progress */
|
|
pfs_thread->m_stage_progress= NULL;
|
|
|
|
if (psi_unlikely(! flag_global_instrumentation))
|
|
return NULL;
|
|
|
|
if (flag_thread_instrumentation && ! pfs_thread->m_enabled)
|
|
return NULL;
|
|
|
|
PFS_events_stages *pfs= & pfs_thread->m_stage_current;
|
|
PFS_events_waits *child_wait= & pfs_thread->m_events_waits_stack[0];
|
|
PFS_events_statements *parent_statement= & pfs_thread->m_statement_stack[0];
|
|
|
|
PFS_instr_class *old_class= pfs->m_class;
|
|
if (old_class != NULL)
|
|
{
|
|
PFS_stage_stat *event_name_array;
|
|
event_name_array= pfs_thread->write_instr_class_stages_stats();
|
|
uint index= old_class->m_event_name_index;
|
|
|
|
/* Finish old event */
|
|
if (old_class->m_timed)
|
|
{
|
|
timer_value= get_timer_raw_value(stage_timer);;
|
|
pfs->m_timer_end= timer_value;
|
|
|
|
/* Aggregate to EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */
|
|
ulonglong stage_time= timer_value - pfs->m_timer_start;
|
|
event_name_array[index].aggregate_value(stage_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */
|
|
event_name_array[index].aggregate_counted();
|
|
}
|
|
|
|
if (flag_events_stages_current)
|
|
{
|
|
pfs->m_end_event_id= pfs_thread->m_event_id;
|
|
if (pfs_thread->m_flag_events_stages_history)
|
|
insert_events_stages_history(pfs_thread, pfs);
|
|
if (pfs_thread->m_flag_events_stages_history_long)
|
|
insert_events_stages_history_long(pfs);
|
|
}
|
|
|
|
/* This stage event is now complete. */
|
|
pfs->m_class= NULL;
|
|
|
|
/* New waits will now be attached directly to the parent statement. */
|
|
child_wait->m_event_id= parent_statement->m_event.m_event_id;
|
|
child_wait->m_event_type= parent_statement->m_event.m_event_type;
|
|
/* See below for new stages, that may overwrite this. */
|
|
}
|
|
|
|
/* Start new event */
|
|
|
|
PFS_stage_class *new_klass= find_stage_class(key);
|
|
if (unlikely(new_klass == NULL))
|
|
return NULL;
|
|
|
|
if (! new_klass->m_enabled)
|
|
return NULL;
|
|
|
|
pfs->m_class= new_klass;
|
|
if (new_klass->m_timed)
|
|
{
|
|
/*
|
|
Do not call the timer again if we have a
|
|
TIMER_END for the previous stage already.
|
|
*/
|
|
if (timer_value == 0)
|
|
timer_value= get_timer_raw_value(stage_timer);
|
|
pfs->m_timer_start= timer_value;
|
|
}
|
|
else
|
|
pfs->m_timer_start= 0;
|
|
pfs->m_timer_end= 0;
|
|
|
|
if (flag_events_stages_current)
|
|
{
|
|
pfs->m_thread_internal_id= pfs_thread->m_thread_internal_id;
|
|
pfs->m_event_id= pfs_thread->m_event_id++;
|
|
pfs->m_end_event_id= 0;
|
|
pfs->m_source_file= src_file;
|
|
pfs->m_source_line= src_line;
|
|
|
|
/* New wait events will have this new stage as parent. */
|
|
child_wait->m_event_id= pfs->m_event_id;
|
|
child_wait->m_event_type= EVENT_TYPE_STAGE;
|
|
}
|
|
|
|
if (new_klass->is_progress())
|
|
{
|
|
pfs_thread->m_stage_progress= & pfs->m_progress;
|
|
pfs->m_progress.m_work_completed= 0;
|
|
pfs->m_progress.m_work_estimated= 0;
|
|
}
|
|
|
|
return pfs_thread->m_stage_progress;
|
|
}
|
|
|
|
PSI_stage_progress*
|
|
pfs_get_current_stage_progress_v1(void)
|
|
{
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
|
|
return pfs_thread->m_stage_progress;
|
|
}
|
|
|
|
void pfs_end_stage_v1()
|
|
{
|
|
ulonglong timer_value= 0;
|
|
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return;
|
|
|
|
pfs_thread->m_stage= 0;
|
|
pfs_thread->m_stage_progress= NULL;
|
|
|
|
if (psi_unlikely(! flag_global_instrumentation))
|
|
return;
|
|
|
|
if (flag_thread_instrumentation && ! pfs_thread->m_enabled)
|
|
return;
|
|
|
|
PFS_events_stages *pfs= & pfs_thread->m_stage_current;
|
|
|
|
PFS_instr_class *old_class= pfs->m_class;
|
|
if (old_class != NULL)
|
|
{
|
|
PFS_stage_stat *event_name_array;
|
|
event_name_array= pfs_thread->write_instr_class_stages_stats();
|
|
uint index= old_class->m_event_name_index;
|
|
|
|
/* Finish old event */
|
|
if (old_class->m_timed)
|
|
{
|
|
timer_value= get_timer_raw_value(stage_timer);;
|
|
pfs->m_timer_end= timer_value;
|
|
|
|
/* Aggregate to EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */
|
|
ulonglong stage_time= timer_value - pfs->m_timer_start;
|
|
event_name_array[index].aggregate_value(stage_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */
|
|
event_name_array[index].aggregate_counted();
|
|
}
|
|
|
|
if (flag_events_stages_current)
|
|
{
|
|
pfs->m_end_event_id= pfs_thread->m_event_id;
|
|
if (pfs_thread->m_flag_events_stages_history)
|
|
insert_events_stages_history(pfs_thread, pfs);
|
|
if (pfs_thread->m_flag_events_stages_history_long)
|
|
insert_events_stages_history_long(pfs);
|
|
}
|
|
|
|
/* New waits will now be attached directly to the parent statement. */
|
|
PFS_events_waits *child_wait= & pfs_thread->m_events_waits_stack[0];
|
|
PFS_events_statements *parent_statement= & pfs_thread->m_statement_stack[0];
|
|
child_wait->m_event_id= parent_statement->m_event.m_event_id;
|
|
child_wait->m_event_type= parent_statement->m_event.m_event_type;
|
|
|
|
/* This stage is completed */
|
|
pfs->m_class= NULL;
|
|
}
|
|
}
|
|
|
|
PSI_statement_locker*
|
|
pfs_get_thread_statement_locker_v1(PSI_statement_locker_state *state,
|
|
PSI_statement_key key,
|
|
const void *charset, PSI_sp_share *sp_share)
|
|
{
|
|
DBUG_ASSERT(state != NULL);
|
|
DBUG_ASSERT(charset != NULL);
|
|
|
|
if (psi_unlikely(! flag_global_instrumentation))
|
|
return NULL;
|
|
PFS_statement_class *klass= find_statement_class(key);
|
|
if (unlikely(klass == NULL))
|
|
return NULL;
|
|
if (! klass->m_enabled)
|
|
return NULL;
|
|
|
|
uint flags;
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
if (! pfs_thread->m_enabled)
|
|
return NULL;
|
|
state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread);
|
|
flags= STATE_FLAG_THREAD;
|
|
|
|
if (klass->m_timed)
|
|
flags|= STATE_FLAG_TIMED;
|
|
|
|
if (flag_events_statements_current)
|
|
{
|
|
ulonglong event_id= pfs_thread->m_event_id++;
|
|
|
|
if (pfs_thread->m_events_statements_count >= statement_stack_max)
|
|
{
|
|
nested_statement_lost++;
|
|
return NULL;
|
|
}
|
|
|
|
pfs_dirty_state dirty_state;
|
|
pfs_thread->m_stmt_lock.allocated_to_dirty(& dirty_state);
|
|
PFS_events_statements *pfs= & pfs_thread->m_statement_stack[pfs_thread->m_events_statements_count];
|
|
pfs->m_event.m_thread_internal_id= pfs_thread->m_thread_internal_id;
|
|
pfs->m_event.m_event_id= event_id;
|
|
pfs->m_event.m_event_type= EVENT_TYPE_STATEMENT;
|
|
pfs->m_event.m_end_event_id= 0;
|
|
pfs->m_event.m_class= klass;
|
|
pfs->m_event.m_timer_start= 0;
|
|
pfs->m_event.m_timer_end= 0;
|
|
pfs->m_lock_time= 0;
|
|
pfs->m_current_schema_name_length= 0;
|
|
pfs->m_sqltext_length= 0;
|
|
pfs->m_sqltext_truncated= false;
|
|
pfs->m_sqltext_cs_number= system_charset_info->number; /* default */
|
|
|
|
pfs->m_message_text[0]= '\0';
|
|
pfs->m_sql_errno= 0;
|
|
pfs->m_sqlstate[0]= '\0';
|
|
pfs->m_error_count= 0;
|
|
pfs->m_warning_count= 0;
|
|
pfs->m_rows_affected= 0;
|
|
|
|
pfs->m_rows_sent= 0;
|
|
pfs->m_rows_examined= 0;
|
|
pfs->m_created_tmp_disk_tables= 0;
|
|
pfs->m_created_tmp_tables= 0;
|
|
pfs->m_select_full_join= 0;
|
|
pfs->m_select_full_range_join= 0;
|
|
pfs->m_select_range= 0;
|
|
pfs->m_select_range_check= 0;
|
|
pfs->m_select_scan= 0;
|
|
pfs->m_sort_merge_passes= 0;
|
|
pfs->m_sort_range= 0;
|
|
pfs->m_sort_rows= 0;
|
|
pfs->m_sort_scan= 0;
|
|
pfs->m_no_index_used= 0;
|
|
pfs->m_no_good_index_used= 0;
|
|
pfs->m_digest_storage.reset();
|
|
|
|
/* New stages will have this statement as parent */
|
|
PFS_events_stages *child_stage= & pfs_thread->m_stage_current;
|
|
child_stage->m_nesting_event_id= event_id;
|
|
child_stage->m_nesting_event_type= EVENT_TYPE_STATEMENT;
|
|
|
|
/* New waits will have this statement as parent, if no stage is instrumented */
|
|
PFS_events_waits *child_wait= & pfs_thread->m_events_waits_stack[0];
|
|
child_wait->m_event_id= event_id;
|
|
child_wait->m_event_type= EVENT_TYPE_STATEMENT;
|
|
|
|
PFS_events_statements *parent_statement= NULL;
|
|
PFS_events_transactions *parent_transaction= &pfs_thread->m_transaction_current;
|
|
ulonglong parent_event= 0;
|
|
enum_event_type parent_type= EVENT_TYPE_STATEMENT;
|
|
uint parent_level= 0;
|
|
|
|
if (pfs_thread->m_events_statements_count > 0)
|
|
{
|
|
parent_statement= pfs - 1;
|
|
parent_event= parent_statement->m_event.m_event_id;
|
|
parent_type= parent_statement->m_event.m_event_type;
|
|
parent_level= parent_statement->m_event.m_nesting_event_level + 1;
|
|
}
|
|
|
|
if (parent_transaction->m_state == TRANS_STATE_ACTIVE &&
|
|
parent_transaction->m_event_id > parent_event)
|
|
{
|
|
parent_event= parent_transaction->m_event_id;
|
|
parent_type= parent_transaction->m_event_type;
|
|
}
|
|
|
|
pfs->m_event.m_nesting_event_id= parent_event;
|
|
pfs->m_event.m_nesting_event_type= parent_type;
|
|
pfs->m_event.m_nesting_event_level= parent_level;
|
|
|
|
/* Set parent Stored Procedure information for this statement. */
|
|
if(sp_share)
|
|
{
|
|
PFS_program *parent_sp= reinterpret_cast<PFS_program*>(sp_share);
|
|
pfs->m_sp_type= parent_sp->m_type;
|
|
memcpy(pfs->m_schema_name, parent_sp->m_schema_name,
|
|
parent_sp->m_schema_name_length);
|
|
pfs->m_schema_name_length= parent_sp->m_schema_name_length;
|
|
memcpy(pfs->m_object_name, parent_sp->m_object_name,
|
|
parent_sp->m_object_name_length);
|
|
pfs->m_object_name_length= parent_sp->m_object_name_length;
|
|
}
|
|
else
|
|
{
|
|
pfs->m_sp_type= NO_OBJECT_TYPE;
|
|
pfs->m_schema_name_length= 0;
|
|
pfs->m_object_name_length= 0;
|
|
}
|
|
|
|
state->m_statement= pfs;
|
|
flags|= STATE_FLAG_EVENT;
|
|
|
|
pfs_thread->m_events_statements_count++;
|
|
pfs_thread->m_stmt_lock.dirty_to_allocated(& dirty_state);
|
|
}
|
|
else
|
|
{
|
|
state->m_statement= NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
state->m_statement= NULL;
|
|
|
|
if (klass->m_timed)
|
|
flags= STATE_FLAG_TIMED;
|
|
else
|
|
flags= 0;
|
|
}
|
|
|
|
if (flag_statements_digest)
|
|
{
|
|
flags|= STATE_FLAG_DIGEST;
|
|
}
|
|
|
|
state->m_discarded= false;
|
|
state->m_class= klass;
|
|
state->m_flags= flags;
|
|
|
|
state->m_lock_time= 0;
|
|
state->m_rows_sent= 0;
|
|
state->m_rows_examined= 0;
|
|
state->m_created_tmp_disk_tables= 0;
|
|
state->m_created_tmp_tables= 0;
|
|
state->m_select_full_join= 0;
|
|
state->m_select_full_range_join= 0;
|
|
state->m_select_range= 0;
|
|
state->m_select_range_check= 0;
|
|
state->m_select_scan= 0;
|
|
state->m_sort_merge_passes= 0;
|
|
state->m_sort_range= 0;
|
|
state->m_sort_rows= 0;
|
|
state->m_sort_scan= 0;
|
|
state->m_no_index_used= 0;
|
|
state->m_no_good_index_used= 0;
|
|
|
|
state->m_digest= NULL;
|
|
state->m_cs_number= ((CHARSET_INFO *)charset)->number;
|
|
|
|
state->m_schema_name_length= 0;
|
|
state->m_parent_sp_share= sp_share;
|
|
state->m_parent_prepared_stmt= NULL;
|
|
|
|
return reinterpret_cast<PSI_statement_locker*> (state);
|
|
}
|
|
|
|
PSI_statement_locker*
|
|
pfs_refine_statement_v1(PSI_statement_locker *locker,
|
|
PSI_statement_key key)
|
|
{
|
|
PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker);
|
|
if (state == NULL)
|
|
return NULL;
|
|
assert(state->m_class != NULL);
|
|
PFS_statement_class *klass;
|
|
/* Only refine statements for mutable instrumentation */
|
|
klass= reinterpret_cast<PFS_statement_class*> (state->m_class);
|
|
assert(klass->is_mutable());
|
|
klass= find_statement_class(key);
|
|
|
|
uint flags= state->m_flags;
|
|
|
|
if (unlikely(klass == NULL) || !klass->m_enabled)
|
|
{
|
|
/* pop statement stack */
|
|
if (flags & STATE_FLAG_THREAD)
|
|
{
|
|
PFS_thread *pfs_thread= reinterpret_cast<PFS_thread *> (state->m_thread);
|
|
assert(pfs_thread != NULL);
|
|
if (pfs_thread->m_events_statements_count > 0)
|
|
pfs_thread->m_events_statements_count--;
|
|
}
|
|
|
|
state->m_discarded= true;
|
|
return NULL;
|
|
}
|
|
|
|
if ((flags & STATE_FLAG_TIMED) && ! klass->m_timed)
|
|
flags= flags & ~STATE_FLAG_TIMED;
|
|
|
|
if (flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_statements *pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement);
|
|
assert(pfs != NULL);
|
|
|
|
/* mutate EVENTS_STATEMENTS_CURRENT.EVENT_NAME */
|
|
pfs->m_event.m_class= klass;
|
|
}
|
|
|
|
state->m_class= klass;
|
|
state->m_flags= flags;
|
|
return reinterpret_cast<PSI_statement_locker*> (state);
|
|
}
|
|
|
|
void pfs_start_statement_v1(PSI_statement_locker *locker,
|
|
const char *db, uint db_len,
|
|
const char *src_file, uint src_line)
|
|
{
|
|
PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
uint flags= state->m_flags;
|
|
ulonglong timer_start= 0;
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(statement_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
}
|
|
|
|
compile_time_assert(PSI_SCHEMA_NAME_LEN == NAME_LEN);
|
|
assert(db_len <= sizeof(state->m_schema_name));
|
|
|
|
if (db_len > 0)
|
|
memcpy(state->m_schema_name, db, db_len);
|
|
state->m_schema_name_length= db_len;
|
|
|
|
if (flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_statements *pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement);
|
|
assert(pfs != NULL);
|
|
|
|
pfs->m_event.m_timer_start= timer_start;
|
|
pfs->m_event.m_source_file= src_file;
|
|
pfs->m_event.m_source_line= src_line;
|
|
|
|
assert(db_len <= sizeof(pfs->m_current_schema_name));
|
|
if (db_len > 0)
|
|
memcpy(pfs->m_current_schema_name, db, db_len);
|
|
pfs->m_current_schema_name_length= db_len;
|
|
}
|
|
}
|
|
|
|
void pfs_set_statement_text_v1(PSI_statement_locker *locker,
|
|
const char *text, uint text_len)
|
|
{
|
|
PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
if (state->m_discarded)
|
|
return;
|
|
|
|
if (state->m_flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_statements *pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement);
|
|
assert(pfs != NULL);
|
|
if (text_len > pfs_max_sqltext)
|
|
{
|
|
text_len= (uint)pfs_max_sqltext;
|
|
pfs->m_sqltext_truncated= true;
|
|
}
|
|
if (text_len)
|
|
memcpy(pfs->m_sqltext, text, text_len);
|
|
pfs->m_sqltext_length= text_len;
|
|
pfs->m_sqltext_cs_number= state->m_cs_number;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#define SET_STATEMENT_ATTR_BODY(LOCKER, ATTR, VALUE) \
|
|
PSI_statement_locker_state *state; \
|
|
state= reinterpret_cast<PSI_statement_locker_state*> (LOCKER); \
|
|
if (unlikely(state == NULL)) \
|
|
return; \
|
|
if (state->m_discarded) \
|
|
return; \
|
|
state->ATTR= VALUE; \
|
|
if (state->m_flags & STATE_FLAG_EVENT) \
|
|
{ \
|
|
PFS_events_statements *pfs; \
|
|
pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); \
|
|
assert(pfs != NULL); \
|
|
pfs->ATTR= VALUE; \
|
|
} \
|
|
return;
|
|
|
|
#define INC_STATEMENT_ATTR_BODY(LOCKER, ATTR, VALUE) \
|
|
PSI_statement_locker_state *state; \
|
|
state= reinterpret_cast<PSI_statement_locker_state*> (LOCKER); \
|
|
if (unlikely(state == NULL)) \
|
|
return; \
|
|
if (state->m_discarded) \
|
|
return; \
|
|
state->ATTR+= VALUE; \
|
|
if (state->m_flags & STATE_FLAG_EVENT) \
|
|
{ \
|
|
PFS_events_statements *pfs; \
|
|
pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); \
|
|
assert(pfs != NULL); \
|
|
pfs->ATTR+= VALUE; \
|
|
} \
|
|
return;
|
|
|
|
void pfs_set_statement_lock_time_v1(PSI_statement_locker *locker,
|
|
ulonglong count)
|
|
{
|
|
SET_STATEMENT_ATTR_BODY(locker, m_lock_time, count);
|
|
}
|
|
|
|
void pfs_set_statement_rows_sent_v1(PSI_statement_locker *locker,
|
|
ulonglong count)
|
|
{
|
|
SET_STATEMENT_ATTR_BODY(locker, m_rows_sent, count);
|
|
}
|
|
|
|
void pfs_set_statement_rows_examined_v1(PSI_statement_locker *locker,
|
|
ulonglong count)
|
|
{
|
|
SET_STATEMENT_ATTR_BODY(locker, m_rows_examined, count);
|
|
}
|
|
|
|
void pfs_inc_statement_created_tmp_disk_tables_v1(PSI_statement_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_STATEMENT_ATTR_BODY(locker, m_created_tmp_disk_tables, count);
|
|
}
|
|
|
|
void pfs_inc_statement_created_tmp_tables_v1(PSI_statement_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_STATEMENT_ATTR_BODY(locker, m_created_tmp_tables, count);
|
|
}
|
|
|
|
void pfs_inc_statement_select_full_join_v1(PSI_statement_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_STATEMENT_ATTR_BODY(locker, m_select_full_join, count);
|
|
}
|
|
|
|
void pfs_inc_statement_select_full_range_join_v1(PSI_statement_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_STATEMENT_ATTR_BODY(locker, m_select_full_range_join, count);
|
|
}
|
|
|
|
void pfs_inc_statement_select_range_v1(PSI_statement_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_STATEMENT_ATTR_BODY(locker, m_select_range, count);
|
|
}
|
|
|
|
void pfs_inc_statement_select_range_check_v1(PSI_statement_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_STATEMENT_ATTR_BODY(locker, m_select_range_check, count);
|
|
}
|
|
|
|
void pfs_inc_statement_select_scan_v1(PSI_statement_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_STATEMENT_ATTR_BODY(locker, m_select_scan, count);
|
|
}
|
|
|
|
void pfs_inc_statement_sort_merge_passes_v1(PSI_statement_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_STATEMENT_ATTR_BODY(locker, m_sort_merge_passes, count);
|
|
}
|
|
|
|
void pfs_inc_statement_sort_range_v1(PSI_statement_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_STATEMENT_ATTR_BODY(locker, m_sort_range, count);
|
|
}
|
|
|
|
void pfs_inc_statement_sort_rows_v1(PSI_statement_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_STATEMENT_ATTR_BODY(locker, m_sort_rows, count);
|
|
}
|
|
|
|
void pfs_inc_statement_sort_scan_v1(PSI_statement_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_STATEMENT_ATTR_BODY(locker, m_sort_scan, count);
|
|
}
|
|
|
|
void pfs_set_statement_no_index_used_v1(PSI_statement_locker *locker)
|
|
{
|
|
SET_STATEMENT_ATTR_BODY(locker, m_no_index_used, 1);
|
|
}
|
|
|
|
void pfs_set_statement_no_good_index_used_v1(PSI_statement_locker *locker)
|
|
{
|
|
SET_STATEMENT_ATTR_BODY(locker, m_no_good_index_used, 1);
|
|
}
|
|
|
|
void pfs_end_statement_v1(PSI_statement_locker *locker, void *stmt_da)
|
|
{
|
|
PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker);
|
|
Diagnostics_area *da= reinterpret_cast<Diagnostics_area*> (stmt_da);
|
|
assert(state != NULL);
|
|
assert(da != NULL);
|
|
|
|
if (state->m_discarded)
|
|
return;
|
|
|
|
PFS_statement_class *klass= reinterpret_cast<PFS_statement_class *> (state->m_class);
|
|
assert(klass != NULL);
|
|
|
|
ulonglong timer_end= 0;
|
|
ulonglong wait_time= 0;
|
|
uint flags= state->m_flags;
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_end= state->m_timer();
|
|
wait_time= timer_end - state->m_timer_start;
|
|
}
|
|
|
|
PFS_statement_stat *event_name_array;
|
|
uint index= klass->m_event_name_index;
|
|
PFS_statement_stat *stat;
|
|
|
|
/*
|
|
Capture statement stats by digest.
|
|
*/
|
|
const sql_digest_storage *digest_storage= NULL;
|
|
PFS_statement_stat *digest_stat= NULL;
|
|
PFS_program *pfs_program= NULL;
|
|
PFS_prepared_stmt *pfs_prepared_stmt= NULL;
|
|
|
|
if (flags & STATE_FLAG_THREAD)
|
|
{
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread);
|
|
assert(thread != NULL);
|
|
event_name_array= thread->write_instr_class_statements_stats();
|
|
/* Aggregate to EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME */
|
|
stat= & event_name_array[index];
|
|
|
|
if (flags & STATE_FLAG_DIGEST)
|
|
{
|
|
digest_storage= state->m_digest;
|
|
|
|
if (digest_storage != NULL)
|
|
{
|
|
/* Populate PFS_statements_digest_stat with computed digest information.*/
|
|
digest_stat= find_or_create_digest(thread, digest_storage,
|
|
state->m_schema_name,
|
|
state->m_schema_name_length);
|
|
}
|
|
}
|
|
|
|
if (flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_statements *pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement);
|
|
assert(pfs != NULL);
|
|
|
|
pfs_dirty_state dirty_state;
|
|
thread->m_stmt_lock.allocated_to_dirty(& dirty_state);
|
|
|
|
switch(da->status())
|
|
{
|
|
case Diagnostics_area::DA_OK_BULK:
|
|
case Diagnostics_area::DA_EOF_BULK:
|
|
case Diagnostics_area::DA_EMPTY:
|
|
break;
|
|
case Diagnostics_area::DA_OK:
|
|
memcpy(pfs->m_message_text, da->message(),
|
|
MYSQL_ERRMSG_SIZE);
|
|
pfs->m_message_text[MYSQL_ERRMSG_SIZE]= 0;
|
|
pfs->m_rows_affected= da->affected_rows();
|
|
pfs->m_warning_count= da->statement_warn_count();
|
|
memcpy(pfs->m_sqlstate, "00000", SQLSTATE_LENGTH);
|
|
break;
|
|
case Diagnostics_area::DA_EOF:
|
|
pfs->m_warning_count= da->statement_warn_count();
|
|
break;
|
|
case Diagnostics_area::DA_ERROR:
|
|
memcpy(pfs->m_message_text, da->message(),
|
|
MYSQL_ERRMSG_SIZE);
|
|
pfs->m_message_text[MYSQL_ERRMSG_SIZE]= 0;
|
|
pfs->m_sql_errno= da->sql_errno();
|
|
memcpy(pfs->m_sqlstate, da->get_sqlstate(), SQLSTATE_LENGTH);
|
|
pfs->m_error_count++;
|
|
break;
|
|
case Diagnostics_area::DA_DISABLED:
|
|
break;
|
|
}
|
|
|
|
pfs->m_event.m_timer_end= timer_end;
|
|
pfs->m_event.m_end_event_id= thread->m_event_id;
|
|
|
|
if (digest_storage != NULL)
|
|
{
|
|
/*
|
|
The following columns in events_statement_current:
|
|
- DIGEST,
|
|
- DIGEST_TEXT
|
|
are computed from the digest storage.
|
|
*/
|
|
pfs->m_digest_storage.copy(digest_storage);
|
|
}
|
|
|
|
pfs_program= reinterpret_cast<PFS_program*>(state->m_parent_sp_share);
|
|
pfs_prepared_stmt= reinterpret_cast<PFS_prepared_stmt*>(state->m_parent_prepared_stmt);
|
|
|
|
if (thread->m_flag_events_statements_history)
|
|
insert_events_statements_history(thread, pfs);
|
|
if (thread->m_flag_events_statements_history_long)
|
|
insert_events_statements_history_long(pfs);
|
|
|
|
assert(thread->m_events_statements_count > 0);
|
|
thread->m_events_statements_count--;
|
|
thread->m_stmt_lock.dirty_to_allocated(& dirty_state);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (flags & STATE_FLAG_DIGEST)
|
|
{
|
|
PFS_thread *thread= my_thread_get_THR_PFS();
|
|
|
|
/* An instrumented thread is required, for LF_PINS. */
|
|
if (thread != NULL)
|
|
{
|
|
/* Set digest stat. */
|
|
digest_storage= state->m_digest;
|
|
|
|
if (digest_storage != NULL)
|
|
{
|
|
/* Populate statements_digest_stat with computed digest information. */
|
|
digest_stat= find_or_create_digest(thread, digest_storage,
|
|
state->m_schema_name,
|
|
state->m_schema_name_length);
|
|
}
|
|
}
|
|
}
|
|
|
|
event_name_array= global_instr_class_statements_array;
|
|
/* Aggregate to EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME */
|
|
stat= & event_name_array[index];
|
|
}
|
|
|
|
stat->mark_used();
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
/* Aggregate to EVENTS_STATEMENTS_SUMMARY_..._BY_EVENT_NAME (timed) */
|
|
stat->aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_STATEMENTS_SUMMARY_..._BY_EVENT_NAME (counted) */
|
|
stat->aggregate_counted();
|
|
}
|
|
|
|
stat->m_lock_time+= state->m_lock_time;
|
|
stat->m_rows_sent+= state->m_rows_sent;
|
|
stat->m_rows_examined+= state->m_rows_examined;
|
|
stat->m_created_tmp_disk_tables+= state->m_created_tmp_disk_tables;
|
|
stat->m_created_tmp_tables+= state->m_created_tmp_tables;
|
|
stat->m_select_full_join+= state->m_select_full_join;
|
|
stat->m_select_full_range_join+= state->m_select_full_range_join;
|
|
stat->m_select_range+= state->m_select_range;
|
|
stat->m_select_range_check+= state->m_select_range_check;
|
|
stat->m_select_scan+= state->m_select_scan;
|
|
stat->m_sort_merge_passes+= state->m_sort_merge_passes;
|
|
stat->m_sort_range+= state->m_sort_range;
|
|
stat->m_sort_rows+= state->m_sort_rows;
|
|
stat->m_sort_scan+= state->m_sort_scan;
|
|
stat->m_no_index_used+= state->m_no_index_used;
|
|
stat->m_no_good_index_used+= state->m_no_good_index_used;
|
|
|
|
if (digest_stat != NULL)
|
|
{
|
|
digest_stat->mark_used();
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
digest_stat->aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
digest_stat->aggregate_counted();
|
|
}
|
|
|
|
digest_stat->m_lock_time+= state->m_lock_time;
|
|
digest_stat->m_rows_sent+= state->m_rows_sent;
|
|
digest_stat->m_rows_examined+= state->m_rows_examined;
|
|
digest_stat->m_created_tmp_disk_tables+= state->m_created_tmp_disk_tables;
|
|
digest_stat->m_created_tmp_tables+= state->m_created_tmp_tables;
|
|
digest_stat->m_select_full_join+= state->m_select_full_join;
|
|
digest_stat->m_select_full_range_join+= state->m_select_full_range_join;
|
|
digest_stat->m_select_range+= state->m_select_range;
|
|
digest_stat->m_select_range_check+= state->m_select_range_check;
|
|
digest_stat->m_select_scan+= state->m_select_scan;
|
|
digest_stat->m_sort_merge_passes+= state->m_sort_merge_passes;
|
|
digest_stat->m_sort_range+= state->m_sort_range;
|
|
digest_stat->m_sort_rows+= state->m_sort_rows;
|
|
digest_stat->m_sort_scan+= state->m_sort_scan;
|
|
digest_stat->m_no_index_used+= state->m_no_index_used;
|
|
digest_stat->m_no_good_index_used+= state->m_no_good_index_used;
|
|
}
|
|
|
|
if(pfs_program != NULL)
|
|
{
|
|
PFS_statement_stat *sub_stmt_stat= NULL;
|
|
sub_stmt_stat= &pfs_program->m_stmt_stat;
|
|
if(sub_stmt_stat != NULL)
|
|
{
|
|
sub_stmt_stat->mark_used();
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
sub_stmt_stat->aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
sub_stmt_stat->aggregate_counted();
|
|
}
|
|
|
|
sub_stmt_stat->m_lock_time+= state->m_lock_time;
|
|
sub_stmt_stat->m_rows_sent+= state->m_rows_sent;
|
|
sub_stmt_stat->m_rows_examined+= state->m_rows_examined;
|
|
sub_stmt_stat->m_created_tmp_disk_tables+= state->m_created_tmp_disk_tables;
|
|
sub_stmt_stat->m_created_tmp_tables+= state->m_created_tmp_tables;
|
|
sub_stmt_stat->m_select_full_join+= state->m_select_full_join;
|
|
sub_stmt_stat->m_select_full_range_join+= state->m_select_full_range_join;
|
|
sub_stmt_stat->m_select_range+= state->m_select_range;
|
|
sub_stmt_stat->m_select_range_check+= state->m_select_range_check;
|
|
sub_stmt_stat->m_select_scan+= state->m_select_scan;
|
|
sub_stmt_stat->m_sort_merge_passes+= state->m_sort_merge_passes;
|
|
sub_stmt_stat->m_sort_range+= state->m_sort_range;
|
|
sub_stmt_stat->m_sort_rows+= state->m_sort_rows;
|
|
sub_stmt_stat->m_sort_scan+= state->m_sort_scan;
|
|
sub_stmt_stat->m_no_index_used+= state->m_no_index_used;
|
|
sub_stmt_stat->m_no_good_index_used+= state->m_no_good_index_used;
|
|
}
|
|
}
|
|
|
|
if (pfs_prepared_stmt != NULL)
|
|
{
|
|
if(state->m_in_prepare)
|
|
{
|
|
PFS_single_stat *prepared_stmt_stat= NULL;
|
|
prepared_stmt_stat= &pfs_prepared_stmt->m_prepare_stat;
|
|
if(prepared_stmt_stat != NULL)
|
|
{
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
prepared_stmt_stat->aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
prepared_stmt_stat->aggregate_counted();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PFS_statement_stat *prepared_stmt_stat= NULL;
|
|
prepared_stmt_stat= &pfs_prepared_stmt->m_execute_stat;
|
|
if(prepared_stmt_stat != NULL)
|
|
{
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
prepared_stmt_stat->aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
prepared_stmt_stat->aggregate_counted();
|
|
}
|
|
|
|
prepared_stmt_stat->m_lock_time+= state->m_lock_time;
|
|
prepared_stmt_stat->m_rows_sent+= state->m_rows_sent;
|
|
prepared_stmt_stat->m_rows_examined+= state->m_rows_examined;
|
|
prepared_stmt_stat->m_created_tmp_disk_tables+= state->m_created_tmp_disk_tables;
|
|
prepared_stmt_stat->m_created_tmp_tables+= state->m_created_tmp_tables;
|
|
prepared_stmt_stat->m_select_full_join+= state->m_select_full_join;
|
|
prepared_stmt_stat->m_select_full_range_join+= state->m_select_full_range_join;
|
|
prepared_stmt_stat->m_select_range+= state->m_select_range;
|
|
prepared_stmt_stat->m_select_range_check+= state->m_select_range_check;
|
|
prepared_stmt_stat->m_select_scan+= state->m_select_scan;
|
|
prepared_stmt_stat->m_sort_merge_passes+= state->m_sort_merge_passes;
|
|
prepared_stmt_stat->m_sort_range+= state->m_sort_range;
|
|
prepared_stmt_stat->m_sort_rows+= state->m_sort_rows;
|
|
prepared_stmt_stat->m_sort_scan+= state->m_sort_scan;
|
|
prepared_stmt_stat->m_no_index_used+= state->m_no_index_used;
|
|
prepared_stmt_stat->m_no_good_index_used+= state->m_no_good_index_used;
|
|
}
|
|
}
|
|
}
|
|
|
|
PFS_statement_stat *sub_stmt_stat= NULL;
|
|
if (pfs_program != NULL)
|
|
sub_stmt_stat= &pfs_program->m_stmt_stat;
|
|
|
|
PFS_statement_stat *prepared_stmt_stat= NULL;
|
|
if (pfs_prepared_stmt != NULL && !state->m_in_prepare)
|
|
prepared_stmt_stat= &pfs_prepared_stmt->m_execute_stat;
|
|
|
|
switch (da->status())
|
|
{
|
|
case Diagnostics_area::DA_OK_BULK:
|
|
case Diagnostics_area::DA_EOF_BULK:
|
|
case Diagnostics_area::DA_EMPTY:
|
|
break;
|
|
case Diagnostics_area::DA_OK:
|
|
stat->m_rows_affected+= da->affected_rows();
|
|
stat->m_warning_count+= da->statement_warn_count();
|
|
if (digest_stat != NULL)
|
|
{
|
|
digest_stat->m_rows_affected+= da->affected_rows();
|
|
digest_stat->m_warning_count+= da->statement_warn_count();
|
|
}
|
|
if(sub_stmt_stat != NULL)
|
|
{
|
|
sub_stmt_stat->m_rows_affected+= da->affected_rows();
|
|
sub_stmt_stat->m_warning_count+= da->statement_warn_count();
|
|
}
|
|
if (prepared_stmt_stat != NULL)
|
|
{
|
|
prepared_stmt_stat->m_rows_affected+= da->affected_rows();
|
|
prepared_stmt_stat->m_warning_count+= da->statement_warn_count();
|
|
}
|
|
break;
|
|
case Diagnostics_area::DA_EOF:
|
|
stat->m_warning_count+= da->statement_warn_count();
|
|
if (digest_stat != NULL)
|
|
{
|
|
digest_stat->m_warning_count+= da->statement_warn_count();
|
|
}
|
|
if(sub_stmt_stat != NULL)
|
|
{
|
|
sub_stmt_stat->m_warning_count+= da->statement_warn_count();
|
|
}
|
|
if (prepared_stmt_stat != NULL)
|
|
{
|
|
prepared_stmt_stat->m_warning_count+= da->statement_warn_count();
|
|
}
|
|
break;
|
|
case Diagnostics_area::DA_ERROR:
|
|
stat->m_error_count++;
|
|
if (digest_stat != NULL)
|
|
{
|
|
digest_stat->m_error_count++;
|
|
}
|
|
if (sub_stmt_stat != NULL)
|
|
{
|
|
sub_stmt_stat->m_error_count++;
|
|
}
|
|
if (prepared_stmt_stat != NULL)
|
|
{
|
|
prepared_stmt_stat->m_error_count++;
|
|
}
|
|
break;
|
|
case Diagnostics_area::DA_DISABLED:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline enum_object_type sp_type_to_object_type(uint sp_type)
|
|
{
|
|
enum enum_sp_type value= static_cast<enum enum_sp_type> (sp_type);
|
|
|
|
switch (value)
|
|
{
|
|
case SP_TYPE_FUNCTION:
|
|
return OBJECT_TYPE_FUNCTION;
|
|
case SP_TYPE_PROCEDURE:
|
|
return OBJECT_TYPE_PROCEDURE;
|
|
case SP_TYPE_PACKAGE:
|
|
return OBJECT_TYPE_PACKAGE;
|
|
case SP_TYPE_PACKAGE_BODY:
|
|
return OBJECT_TYPE_PACKAGE_BODY;
|
|
case SP_TYPE_TRIGGER:
|
|
return OBJECT_TYPE_TRIGGER;
|
|
case SP_TYPE_EVENT:
|
|
return OBJECT_TYPE_EVENT;
|
|
default:
|
|
assert(false);
|
|
/* Dead code */
|
|
return NO_OBJECT_TYPE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the stored program instrumentation interface.
|
|
@sa PSI_v1::get_sp_share.
|
|
*/
|
|
PSI_sp_share *pfs_get_sp_share_v1(uint sp_type,
|
|
const char* schema_name,
|
|
uint schema_name_length,
|
|
const char* object_name,
|
|
uint object_name_length)
|
|
{
|
|
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
|
|
if (object_name_length > COL_OBJECT_NAME_SIZE)
|
|
object_name_length= COL_OBJECT_NAME_SIZE;
|
|
if (schema_name_length > COL_OBJECT_SCHEMA_SIZE)
|
|
schema_name_length= COL_OBJECT_SCHEMA_SIZE;
|
|
|
|
PFS_program *pfs_program;
|
|
pfs_program= find_or_create_program(pfs_thread,
|
|
sp_type_to_object_type(sp_type),
|
|
object_name,
|
|
object_name_length,
|
|
schema_name,
|
|
schema_name_length);
|
|
|
|
return reinterpret_cast<PSI_sp_share *>(pfs_program);
|
|
}
|
|
|
|
void pfs_release_sp_share_v1(PSI_sp_share* sp_share)
|
|
{
|
|
/* Unused */
|
|
return;
|
|
}
|
|
|
|
PSI_sp_locker* pfs_start_sp_v1(PSI_sp_locker_state *state,
|
|
PSI_sp_share *sp_share)
|
|
{
|
|
assert(state != NULL);
|
|
if (! flag_global_instrumentation)
|
|
return NULL;
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
if (! pfs_thread->m_enabled)
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
sp share might be null in case when stat array is full and no new
|
|
stored program stats are being inserted into it.
|
|
*/
|
|
PFS_program *pfs_program= reinterpret_cast<PFS_program*>(sp_share);
|
|
if (pfs_program == NULL || !pfs_program->m_enabled)
|
|
return NULL;
|
|
|
|
state->m_flags= 0;
|
|
|
|
if(pfs_program->m_timed)
|
|
{
|
|
state->m_flags|= STATE_FLAG_TIMED;
|
|
state->m_timer_start= get_timer_raw_value_and_function(statement_timer,
|
|
& state->m_timer);
|
|
}
|
|
|
|
state->m_sp_share= sp_share;
|
|
|
|
return reinterpret_cast<PSI_sp_locker*> (state);
|
|
}
|
|
|
|
void pfs_end_sp_v1(PSI_sp_locker *locker)
|
|
{
|
|
PSI_sp_locker_state *state= reinterpret_cast<PSI_sp_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
ulonglong timer_end;
|
|
ulonglong wait_time;
|
|
|
|
PFS_program *pfs_program= reinterpret_cast<PFS_program *>(state->m_sp_share);
|
|
PFS_sp_stat *stat= &pfs_program->m_sp_stat;
|
|
|
|
if (state->m_flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_end= state->m_timer();
|
|
wait_time= timer_end - state->m_timer_start;
|
|
|
|
/* Now use this timer_end and wait_time for timing information. */
|
|
stat->aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
stat->aggregate_counted();
|
|
}
|
|
}
|
|
|
|
void pfs_drop_sp_v1(uint sp_type,
|
|
const char* schema_name,
|
|
uint schema_name_length,
|
|
const char* object_name,
|
|
uint object_name_length)
|
|
{
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return;
|
|
|
|
if (object_name_length > COL_OBJECT_NAME_SIZE)
|
|
object_name_length= COL_OBJECT_NAME_SIZE;
|
|
if (schema_name_length > COL_OBJECT_SCHEMA_SIZE)
|
|
schema_name_length= COL_OBJECT_SCHEMA_SIZE;
|
|
|
|
drop_program(pfs_thread,
|
|
sp_type_to_object_type(sp_type),
|
|
object_name, object_name_length,
|
|
schema_name, schema_name_length);
|
|
}
|
|
|
|
PSI_transaction_locker*
|
|
pfs_get_thread_transaction_locker_v1(PSI_transaction_locker_state *state,
|
|
const void *xid,
|
|
ulonglong trxid,
|
|
int isolation_level,
|
|
my_bool read_only,
|
|
my_bool autocommit)
|
|
{
|
|
assert(state != NULL);
|
|
|
|
if (!flag_global_instrumentation)
|
|
return NULL;
|
|
|
|
if (!global_transaction_class.m_enabled)
|
|
return NULL;
|
|
|
|
uint flags;
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
if (!pfs_thread->m_enabled)
|
|
return NULL;
|
|
state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread);
|
|
flags= STATE_FLAG_THREAD;
|
|
|
|
if (global_transaction_class.m_timed)
|
|
flags|= STATE_FLAG_TIMED;
|
|
|
|
if (flag_events_transactions_current)
|
|
{
|
|
ulonglong event_id= pfs_thread->m_event_id++;
|
|
|
|
PFS_events_transactions *pfs= &pfs_thread->m_transaction_current;
|
|
pfs->m_thread_internal_id = pfs_thread->m_thread_internal_id;
|
|
pfs->m_event_id= event_id;
|
|
pfs->m_event_type= EVENT_TYPE_TRANSACTION;
|
|
pfs->m_end_event_id= 0;
|
|
pfs->m_class= &global_transaction_class;
|
|
pfs->m_timer_start= 0;
|
|
pfs->m_timer_end= 0;
|
|
if (xid != NULL)
|
|
pfs->m_xid= *(PSI_xid *)xid;
|
|
pfs->m_xa= false;
|
|
pfs->m_xa_state= TRANS_STATE_XA_NOTR;
|
|
pfs->m_trxid= trxid;
|
|
pfs->m_isolation_level= (enum_isolation_level)isolation_level;
|
|
pfs->m_read_only= read_only;
|
|
pfs->m_autocommit= autocommit;
|
|
pfs->m_savepoint_count= 0;
|
|
pfs->m_rollback_to_savepoint_count= 0;
|
|
pfs->m_release_savepoint_count= 0;
|
|
|
|
uint statements_count= pfs_thread->m_events_statements_count;
|
|
if (statements_count > 0)
|
|
{
|
|
PFS_events_statements *pfs_statement=
|
|
&pfs_thread->m_statement_stack[statements_count - 1];
|
|
pfs->m_nesting_event_id= pfs_statement->m_event.m_event_id;
|
|
pfs->m_nesting_event_type= pfs_statement->m_event.m_event_type;
|
|
}
|
|
else
|
|
{
|
|
pfs->m_nesting_event_id= 0;
|
|
/* pfs->m_nesting_event_type not used when m_nesting_event_id is 0 */
|
|
}
|
|
|
|
state->m_transaction= pfs;
|
|
flags|= STATE_FLAG_EVENT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (global_transaction_class.m_timed)
|
|
flags= STATE_FLAG_TIMED;
|
|
else
|
|
flags= 0;
|
|
}
|
|
|
|
state->m_class= &global_transaction_class;
|
|
state->m_flags= flags;
|
|
state->m_autocommit= autocommit;
|
|
state->m_read_only= read_only;
|
|
state->m_savepoint_count= 0;
|
|
state->m_rollback_to_savepoint_count= 0;
|
|
state->m_release_savepoint_count= 0;
|
|
|
|
return reinterpret_cast<PSI_transaction_locker*> (state);
|
|
}
|
|
|
|
void pfs_start_transaction_v1(PSI_transaction_locker *locker,
|
|
const char *src_file, uint src_line)
|
|
{
|
|
PSI_transaction_locker_state *state= reinterpret_cast<PSI_transaction_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
uint flags= state->m_flags;
|
|
ulonglong timer_start= 0;
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(transaction_timer, &state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
}
|
|
|
|
if (flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction);
|
|
assert(pfs != NULL);
|
|
|
|
pfs->m_timer_start= timer_start;
|
|
pfs->m_source_file= src_file;
|
|
pfs->m_source_line= src_line;
|
|
pfs->m_state= TRANS_STATE_ACTIVE;
|
|
//pfs->m_sid.clear();
|
|
bzero(&pfs->m_gtid_spec, sizeof(pfs->m_gtid_spec));
|
|
}
|
|
}
|
|
|
|
void pfs_set_transaction_gtid_v1(PSI_transaction_locker *locker,
|
|
const void *sid,
|
|
const void *gtid_spec)
|
|
{
|
|
PSI_transaction_locker_state *state= reinterpret_cast<PSI_transaction_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
assert(sid != NULL);
|
|
assert(gtid_spec != NULL);
|
|
|
|
if (state->m_flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction);
|
|
DBUG_ASSERT(pfs != NULL);
|
|
//pfs->m_sid= *(rpl_sid *)sid;
|
|
pfs->m_gtid_spec= *(Gtid_specification*)gtid_spec;
|
|
}
|
|
}
|
|
|
|
void pfs_set_transaction_xid_v1(PSI_transaction_locker *locker,
|
|
const void *xid,
|
|
int xa_state)
|
|
{
|
|
PSI_transaction_locker_state *state= reinterpret_cast<PSI_transaction_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
if (state->m_flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction);
|
|
assert(pfs != NULL);
|
|
assert(xid != NULL);
|
|
|
|
pfs->m_xid= *(PSI_xid *)xid;
|
|
pfs->m_xa_state= (enum_xa_transaction_state)xa_state;
|
|
pfs->m_xa= true;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void pfs_set_transaction_xa_state_v1(PSI_transaction_locker *locker,
|
|
int xa_state)
|
|
{
|
|
PSI_transaction_locker_state *state= reinterpret_cast<PSI_transaction_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
if (state->m_flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction);
|
|
assert(pfs != NULL);
|
|
|
|
pfs->m_xa_state= (enum_xa_transaction_state)xa_state;
|
|
pfs->m_xa= true;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void pfs_set_transaction_trxid_v1(PSI_transaction_locker *locker,
|
|
const ulonglong *trxid)
|
|
{
|
|
assert(trxid != NULL);
|
|
|
|
PSI_transaction_locker_state *state= reinterpret_cast<PSI_transaction_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
if (state->m_flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction);
|
|
assert(pfs != NULL);
|
|
|
|
if (pfs->m_trxid == 0)
|
|
pfs->m_trxid= *trxid;
|
|
}
|
|
}
|
|
|
|
#define INC_TRANSACTION_ATTR_BODY(LOCKER, ATTR, VALUE) \
|
|
PSI_transaction_locker_state *state; \
|
|
state= reinterpret_cast<PSI_transaction_locker_state*> (LOCKER); \
|
|
if (unlikely(state == NULL)) \
|
|
return; \
|
|
state->ATTR+= VALUE; \
|
|
if (state->m_flags & STATE_FLAG_EVENT) \
|
|
{ \
|
|
PFS_events_transactions *pfs; \
|
|
pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction); \
|
|
assert(pfs != NULL); \
|
|
pfs->ATTR+= VALUE; \
|
|
} \
|
|
return;
|
|
|
|
|
|
void pfs_inc_transaction_savepoints_v1(PSI_transaction_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_TRANSACTION_ATTR_BODY(locker, m_savepoint_count, count);
|
|
}
|
|
|
|
void pfs_inc_transaction_rollback_to_savepoint_v1(PSI_transaction_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_TRANSACTION_ATTR_BODY(locker, m_rollback_to_savepoint_count, count);
|
|
}
|
|
|
|
void pfs_inc_transaction_release_savepoint_v1(PSI_transaction_locker *locker,
|
|
ulong count)
|
|
{
|
|
INC_TRANSACTION_ATTR_BODY(locker, m_release_savepoint_count, count);
|
|
}
|
|
|
|
void pfs_end_transaction_v1(PSI_transaction_locker *locker, my_bool commit)
|
|
{
|
|
PSI_transaction_locker_state *state= reinterpret_cast<PSI_transaction_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
ulonglong timer_end= 0;
|
|
ulonglong wait_time= 0;
|
|
uint flags= state->m_flags;
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_end= state->m_timer();
|
|
wait_time= timer_end - state->m_timer_start;
|
|
}
|
|
|
|
PFS_transaction_stat *stat;
|
|
|
|
if (flags & STATE_FLAG_THREAD)
|
|
{
|
|
PFS_thread *pfs_thread= reinterpret_cast<PFS_thread *> (state->m_thread);
|
|
assert(pfs_thread != NULL);
|
|
|
|
/* Aggregate to EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME */
|
|
stat= &pfs_thread->write_instr_class_transactions_stats()[GLOBAL_TRANSACTION_INDEX];
|
|
|
|
if (flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction);
|
|
assert(pfs != NULL);
|
|
|
|
/* events_transactions_current may have been cleared while the transaction was active */
|
|
if (unlikely(pfs->m_class == NULL))
|
|
return;
|
|
|
|
pfs->m_timer_end= timer_end;
|
|
pfs->m_end_event_id= pfs_thread->m_event_id;
|
|
|
|
pfs->m_state= (commit ? TRANS_STATE_COMMITTED : TRANS_STATE_ROLLED_BACK);
|
|
|
|
if (pfs->m_xa)
|
|
pfs->m_xa_state= (commit ? TRANS_STATE_XA_COMMITTED : TRANS_STATE_XA_ROLLBACK_ONLY);
|
|
|
|
if (pfs_thread->m_flag_events_transactions_history)
|
|
insert_events_transactions_history(pfs_thread, pfs);
|
|
if (pfs_thread->m_flag_events_transactions_history_long)
|
|
insert_events_transactions_history_long(pfs);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME */
|
|
stat= &global_transaction_stat;
|
|
}
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
/* Aggregate to EVENTS_TRANSACTIONS_SUMMARY_..._BY_EVENT_NAME (timed) */
|
|
if(state->m_read_only)
|
|
stat->m_read_only_stat.aggregate_value(wait_time);
|
|
else
|
|
stat->m_read_write_stat.aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_TRANSACTIONS_SUMMARY_..._BY_EVENT_NAME (counted) */
|
|
if(state->m_read_only)
|
|
stat->m_read_only_stat.aggregate_counted();
|
|
else
|
|
stat->m_read_write_stat.aggregate_counted();
|
|
}
|
|
|
|
stat->m_savepoint_count+= state->m_savepoint_count;
|
|
stat->m_rollback_to_savepoint_count+= state->m_rollback_to_savepoint_count;
|
|
stat->m_release_savepoint_count+= state->m_release_savepoint_count;
|
|
}
|
|
|
|
|
|
/**
|
|
Implementation of the socket instrumentation interface.
|
|
@sa PSI_v1::end_socket_wait.
|
|
*/
|
|
void pfs_end_socket_wait_v1(PSI_socket_locker *locker, size_t byte_count)
|
|
{
|
|
PSI_socket_locker_state *state= reinterpret_cast<PSI_socket_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
PFS_socket *socket= reinterpret_cast<PFS_socket *>(state->m_socket);
|
|
assert(socket != NULL);
|
|
|
|
ulonglong timer_end= 0;
|
|
ulonglong wait_time= 0;
|
|
PFS_byte_stat *byte_stat;
|
|
uint flags= state->m_flags;
|
|
size_t bytes= ((int)byte_count > -1 ? byte_count : 0);
|
|
|
|
switch (state->m_operation)
|
|
{
|
|
/* Group read operations */
|
|
case PSI_SOCKET_RECV:
|
|
case PSI_SOCKET_RECVFROM:
|
|
case PSI_SOCKET_RECVMSG:
|
|
byte_stat= &socket->m_socket_stat.m_io_stat.m_read;
|
|
break;
|
|
/* Group write operations */
|
|
case PSI_SOCKET_SEND:
|
|
case PSI_SOCKET_SENDTO:
|
|
case PSI_SOCKET_SENDMSG:
|
|
byte_stat= &socket->m_socket_stat.m_io_stat.m_write;
|
|
break;
|
|
/* Group remaining operations as miscellaneous */
|
|
case PSI_SOCKET_CONNECT:
|
|
case PSI_SOCKET_CREATE:
|
|
case PSI_SOCKET_BIND:
|
|
case PSI_SOCKET_SEEK:
|
|
case PSI_SOCKET_OPT:
|
|
case PSI_SOCKET_STAT:
|
|
case PSI_SOCKET_SHUTDOWN:
|
|
case PSI_SOCKET_SELECT:
|
|
case PSI_SOCKET_CLOSE:
|
|
byte_stat= &socket->m_socket_stat.m_io_stat.m_misc;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
byte_stat= NULL;
|
|
break;
|
|
}
|
|
|
|
/* Aggregation for EVENTS_WAITS_SUMMARY_BY_INSTANCE */
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_end= state->m_timer();
|
|
wait_time= timer_end - state->m_timer_start;
|
|
|
|
/* Aggregate to the socket instrument for now (timed) */
|
|
byte_stat->aggregate(wait_time, bytes);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to the socket instrument (event count and byte count) */
|
|
byte_stat->aggregate_counted(bytes);
|
|
}
|
|
|
|
/* Aggregate to EVENTS_WAITS_HISTORY and EVENTS_WAITS_HISTORY_LONG */
|
|
if (flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread *>(state->m_thread);
|
|
assert(thread != NULL);
|
|
PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait);
|
|
assert(wait != NULL);
|
|
|
|
wait->m_timer_end= timer_end;
|
|
wait->m_end_event_id= thread->m_event_id;
|
|
wait->m_number_of_bytes= bytes;
|
|
|
|
if (thread->m_flag_events_waits_history)
|
|
insert_events_waits_history(thread, wait);
|
|
if (thread->m_flag_events_waits_history_long)
|
|
insert_events_waits_history_long(wait);
|
|
thread->m_events_waits_current--;
|
|
|
|
assert(wait == thread->m_events_waits_current);
|
|
}
|
|
}
|
|
|
|
void pfs_set_socket_state_v1(PSI_socket *socket, PSI_socket_state state)
|
|
{
|
|
assert((state == PSI_SOCKET_STATE_IDLE) || (state == PSI_SOCKET_STATE_ACTIVE));
|
|
PFS_socket *pfs= reinterpret_cast<PFS_socket*>(socket);
|
|
assert(pfs != NULL);
|
|
assert(pfs->m_idle || (state == PSI_SOCKET_STATE_IDLE));
|
|
assert(!pfs->m_idle || (state == PSI_SOCKET_STATE_ACTIVE));
|
|
pfs->m_idle= (state == PSI_SOCKET_STATE_IDLE);
|
|
}
|
|
|
|
/**
|
|
Set socket descriptor and address info.
|
|
*/
|
|
void pfs_set_socket_info_v1(PSI_socket *socket,
|
|
const my_socket *fd,
|
|
const struct sockaddr *addr,
|
|
socklen_t addr_len)
|
|
{
|
|
PFS_socket *pfs= reinterpret_cast<PFS_socket*>(socket);
|
|
assert(pfs != NULL);
|
|
|
|
/** Set socket descriptor */
|
|
if (fd != NULL)
|
|
pfs->m_fd= (uint)*fd;
|
|
|
|
/** Set raw socket address and length */
|
|
if (likely(addr != NULL && addr_len > 0))
|
|
{
|
|
pfs->m_addr_len= addr_len;
|
|
|
|
/** Restrict address length to size of struct */
|
|
if (unlikely(pfs->m_addr_len > sizeof(sockaddr_storage)))
|
|
pfs->m_addr_len= sizeof(struct sockaddr_storage);
|
|
|
|
memcpy(&pfs->m_sock_addr, addr, pfs->m_addr_len);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implementation of the socket instrumentation interface.
|
|
@sa PSI_v1::set_socket_info.
|
|
*/
|
|
void pfs_set_socket_thread_owner_v1(PSI_socket *socket)
|
|
{
|
|
PFS_socket *pfs_socket= reinterpret_cast<PFS_socket*>(socket);
|
|
assert(pfs_socket != NULL);
|
|
pfs_socket->m_thread_owner= my_thread_get_THR_PFS();
|
|
}
|
|
|
|
struct PSI_digest_locker*
|
|
pfs_digest_start_v1(PSI_statement_locker *locker)
|
|
{
|
|
PSI_statement_locker_state *statement_state;
|
|
statement_state= reinterpret_cast<PSI_statement_locker_state*> (locker);
|
|
assert(statement_state != NULL);
|
|
|
|
if (statement_state->m_discarded)
|
|
return NULL;
|
|
|
|
if (statement_state->m_flags & STATE_FLAG_DIGEST)
|
|
{
|
|
return reinterpret_cast<PSI_digest_locker*> (locker);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void pfs_digest_end_v1(PSI_digest_locker *locker, const sql_digest_storage *digest)
|
|
{
|
|
PSI_statement_locker_state *statement_state;
|
|
statement_state= reinterpret_cast<PSI_statement_locker_state*> (locker);
|
|
assert(statement_state != NULL);
|
|
assert(digest != NULL);
|
|
|
|
if (statement_state->m_discarded)
|
|
return;
|
|
|
|
if (statement_state->m_flags & STATE_FLAG_DIGEST)
|
|
{
|
|
statement_state->m_digest= digest;
|
|
}
|
|
}
|
|
|
|
PSI_prepared_stmt*
|
|
pfs_create_prepared_stmt_v1(void *identity, uint stmt_id,
|
|
PSI_statement_locker *locker,
|
|
const char *stmt_name, size_t stmt_name_length)
|
|
{
|
|
PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker);
|
|
PFS_events_statements *pfs_stmt= reinterpret_cast<PFS_events_statements*> (state->m_statement);
|
|
PFS_program *pfs_program= reinterpret_cast<PFS_program *>(state->m_parent_sp_share);
|
|
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
|
|
PFS_prepared_stmt *pfs= create_prepared_stmt(identity,
|
|
pfs_thread, pfs_program,
|
|
pfs_stmt, stmt_id,
|
|
stmt_name, static_cast<uint>(stmt_name_length));
|
|
|
|
state->m_parent_prepared_stmt= reinterpret_cast<PSI_prepared_stmt*>(pfs);
|
|
state->m_in_prepare= true;
|
|
|
|
return reinterpret_cast<PSI_prepared_stmt*>(pfs);
|
|
}
|
|
|
|
void pfs_execute_prepared_stmt_v1 (PSI_statement_locker *locker,
|
|
PSI_prepared_stmt* ps)
|
|
{
|
|
PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
state->m_parent_prepared_stmt= ps;
|
|
state->m_in_prepare= false;
|
|
}
|
|
|
|
void pfs_destroy_prepared_stmt_v1(PSI_prepared_stmt* prepared_stmt)
|
|
{
|
|
PFS_prepared_stmt *pfs_prepared_stmt= reinterpret_cast<PFS_prepared_stmt*>(prepared_stmt);
|
|
delete_prepared_stmt(pfs_prepared_stmt);
|
|
return;
|
|
}
|
|
|
|
void pfs_reprepare_prepared_stmt_v1(PSI_prepared_stmt* prepared_stmt)
|
|
{
|
|
PFS_prepared_stmt *pfs_prepared_stmt= reinterpret_cast<PFS_prepared_stmt*>(prepared_stmt);
|
|
PFS_single_stat *prepared_stmt_stat= &pfs_prepared_stmt->m_reprepare_stat;
|
|
|
|
if (prepared_stmt_stat != NULL)
|
|
prepared_stmt_stat->aggregate_counted();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
Implementation of the thread attribute connection interface
|
|
@sa PSI_v1::set_thread_connect_attr.
|
|
*/
|
|
int pfs_set_thread_connect_attrs_v1(const char *buffer, uint length,
|
|
const void *from_cs)
|
|
{
|
|
PFS_thread *thd= my_thread_get_THR_PFS();
|
|
|
|
assert(buffer != NULL);
|
|
|
|
if (likely(thd != NULL) && session_connect_attrs_size_per_thread > 0)
|
|
{
|
|
pfs_dirty_state dirty_state;
|
|
const CHARSET_INFO *cs = static_cast<const CHARSET_INFO *> (from_cs);
|
|
|
|
/* copy from the input buffer as much as we can fit */
|
|
uint copy_size= (uint)(length < session_connect_attrs_size_per_thread ?
|
|
length : session_connect_attrs_size_per_thread);
|
|
thd->m_session_lock.allocated_to_dirty(& dirty_state);
|
|
memcpy(thd->m_session_connect_attrs, buffer, copy_size);
|
|
thd->m_session_connect_attrs_length= copy_size;
|
|
thd->m_session_connect_attrs_cs_number= cs->number;
|
|
thd->m_session_lock.dirty_to_allocated(& dirty_state);
|
|
|
|
if (copy_size == length)
|
|
return 0;
|
|
|
|
session_connect_attrs_lost++;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void pfs_register_memory_v1(const char *category,
|
|
PSI_memory_info_v1 *info,
|
|
int count)
|
|
{
|
|
REGISTER_BODY_V1(PSI_memory_key,
|
|
memory_instrument_prefix,
|
|
register_memory_class)
|
|
}
|
|
|
|
PSI_memory_key pfs_memory_alloc_v1(PSI_memory_key key, size_t size, PSI_thread **owner)
|
|
{
|
|
PFS_thread ** owner_thread= reinterpret_cast<PFS_thread**>(owner);
|
|
assert(owner_thread != NULL);
|
|
|
|
if (! flag_global_instrumentation)
|
|
{
|
|
*owner_thread= NULL;
|
|
return PSI_NOT_INSTRUMENTED;
|
|
}
|
|
|
|
PFS_memory_class *klass= find_memory_class(key);
|
|
if (klass == NULL)
|
|
{
|
|
*owner_thread= NULL;
|
|
return PSI_NOT_INSTRUMENTED;
|
|
}
|
|
|
|
if (! klass->m_enabled)
|
|
{
|
|
*owner_thread= NULL;
|
|
return PSI_NOT_INSTRUMENTED;
|
|
}
|
|
|
|
PFS_memory_stat *event_name_array;
|
|
PFS_memory_stat *stat;
|
|
uint index= klass->m_event_name_index;
|
|
PFS_memory_stat_delta delta_buffer;
|
|
PFS_memory_stat_delta *delta;
|
|
|
|
if (flag_thread_instrumentation && ! klass->is_global())
|
|
{
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
{
|
|
*owner_thread= NULL;
|
|
return PSI_NOT_INSTRUMENTED;
|
|
}
|
|
if (! pfs_thread->m_enabled)
|
|
{
|
|
*owner_thread= NULL;
|
|
return PSI_NOT_INSTRUMENTED;
|
|
}
|
|
|
|
/* Aggregate to MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME */
|
|
event_name_array= pfs_thread->write_instr_class_memory_stats();
|
|
stat= & event_name_array[index];
|
|
delta= stat->count_alloc(size, &delta_buffer);
|
|
|
|
if (delta != NULL)
|
|
{
|
|
pfs_thread->carry_memory_stat_delta(delta, index);
|
|
}
|
|
|
|
/* Flag this memory as owned by the current thread. */
|
|
*owner_thread= pfs_thread;
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME */
|
|
event_name_array= global_instr_class_memory_array;
|
|
stat= & event_name_array[index];
|
|
(void) stat->count_alloc(size, &delta_buffer);
|
|
|
|
*owner_thread= NULL;
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
PSI_memory_key pfs_memory_realloc_v1(PSI_memory_key key, size_t old_size, size_t new_size, PSI_thread **owner)
|
|
{
|
|
PFS_thread ** owner_thread_hdl= reinterpret_cast<PFS_thread**>(owner);
|
|
assert(owner != NULL);
|
|
|
|
PFS_memory_class *klass= find_memory_class(key);
|
|
if (klass == NULL)
|
|
{
|
|
*owner_thread_hdl= NULL;
|
|
return PSI_NOT_INSTRUMENTED;
|
|
}
|
|
|
|
PFS_memory_stat *event_name_array;
|
|
PFS_memory_stat *stat;
|
|
uint index= klass->m_event_name_index;
|
|
PFS_memory_stat_delta delta_buffer;
|
|
PFS_memory_stat_delta *delta;
|
|
|
|
if (flag_thread_instrumentation && ! klass->is_global())
|
|
{
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (likely(pfs_thread != NULL))
|
|
{
|
|
#ifdef PFS_PARANOID
|
|
PFS_thread *owner_thread= *owner_thread_hdl;
|
|
if (owner_thread != pfs_thread)
|
|
{
|
|
owner_thread= sanitize_thread(owner_thread);
|
|
if (owner_thread != NULL)
|
|
{
|
|
report_memory_accounting_error("pfs_memory_realloc_v1",
|
|
pfs_thread, old_size, klass, owner_thread);
|
|
}
|
|
}
|
|
#endif /* PFS_PARANOID */
|
|
|
|
/* Aggregate to MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME */
|
|
event_name_array= pfs_thread->write_instr_class_memory_stats();
|
|
stat= & event_name_array[index];
|
|
|
|
if (flag_global_instrumentation && klass->m_enabled)
|
|
{
|
|
delta= stat->count_realloc(old_size, new_size, &delta_buffer);
|
|
*owner_thread_hdl= pfs_thread;
|
|
}
|
|
else
|
|
{
|
|
delta= stat->count_free(old_size, &delta_buffer);
|
|
*owner_thread_hdl= NULL;
|
|
key= PSI_NOT_INSTRUMENTED;
|
|
}
|
|
|
|
if (delta != NULL)
|
|
{
|
|
pfs_thread->carry_memory_stat_delta(delta, index);
|
|
}
|
|
return key;
|
|
}
|
|
}
|
|
|
|
/* Aggregate to MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME */
|
|
event_name_array= global_instr_class_memory_array;
|
|
stat= & event_name_array[index];
|
|
|
|
if (flag_global_instrumentation && klass->m_enabled)
|
|
{
|
|
(void) stat->count_realloc(old_size, new_size, &delta_buffer);
|
|
}
|
|
else
|
|
{
|
|
(void) stat->count_free(old_size, &delta_buffer);
|
|
key= PSI_NOT_INSTRUMENTED;
|
|
}
|
|
|
|
*owner_thread_hdl= NULL;
|
|
return key;
|
|
}
|
|
|
|
PSI_memory_key pfs_memory_claim_v1(PSI_memory_key key, size_t size, PSI_thread **owner)
|
|
{
|
|
PFS_thread ** owner_thread= reinterpret_cast<PFS_thread**>(owner);
|
|
assert(owner_thread != NULL);
|
|
|
|
PFS_memory_class *klass= find_memory_class(key);
|
|
if (klass == NULL)
|
|
{
|
|
*owner_thread= NULL;
|
|
return PSI_NOT_INSTRUMENTED;
|
|
}
|
|
|
|
/*
|
|
Do not check klass->m_enabled.
|
|
Do not check flag_global_instrumentation.
|
|
If a memory alloc was instrumented,
|
|
the corresponding free must be instrumented.
|
|
*/
|
|
|
|
PFS_memory_stat *event_name_array;
|
|
PFS_memory_stat *stat;
|
|
uint index= klass->m_event_name_index;
|
|
PFS_memory_stat_delta delta_buffer;
|
|
PFS_memory_stat_delta *delta;
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
PFS_thread *old_thread= sanitize_thread(*owner_thread);
|
|
PFS_thread *new_thread= my_thread_get_THR_PFS();
|
|
if (old_thread != new_thread)
|
|
{
|
|
if (old_thread != NULL)
|
|
{
|
|
event_name_array= old_thread->write_instr_class_memory_stats();
|
|
stat= & event_name_array[index];
|
|
delta= stat->count_free(size, &delta_buffer);
|
|
|
|
if (delta != NULL)
|
|
{
|
|
old_thread->carry_memory_stat_delta(delta, index);
|
|
}
|
|
}
|
|
|
|
if (new_thread != NULL)
|
|
{
|
|
event_name_array= new_thread->write_instr_class_memory_stats();
|
|
stat= & event_name_array[index];
|
|
delta= stat->count_alloc(size, &delta_buffer);
|
|
|
|
if (delta != NULL)
|
|
{
|
|
new_thread->carry_memory_stat_delta(delta, index);
|
|
}
|
|
}
|
|
|
|
*owner_thread= new_thread;
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
*owner_thread= NULL;
|
|
return key;
|
|
}
|
|
|
|
void pfs_memory_free_v1(PSI_memory_key key, size_t size, PSI_thread *owner)
|
|
{
|
|
PFS_memory_class *klass= find_memory_class(key);
|
|
if (klass == NULL)
|
|
return;
|
|
|
|
/*
|
|
Do not check klass->m_enabled.
|
|
Do not check flag_global_instrumentation.
|
|
If a memory alloc was instrumented,
|
|
the corresponding free must be instrumented.
|
|
*/
|
|
|
|
PFS_memory_stat *event_name_array;
|
|
PFS_memory_stat *stat;
|
|
uint index= klass->m_event_name_index;
|
|
PFS_memory_stat_delta delta_buffer;
|
|
PFS_memory_stat_delta *delta;
|
|
|
|
if (flag_thread_instrumentation && ! klass->is_global())
|
|
{
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (likely(pfs_thread != NULL))
|
|
{
|
|
#ifdef PFS_PARANOID
|
|
PFS_thread *owner_thread= reinterpret_cast<PFS_thread*>(owner);
|
|
|
|
if (owner_thread != pfs_thread)
|
|
{
|
|
owner_thread= sanitize_thread(owner_thread);
|
|
if (owner_thread != NULL)
|
|
{
|
|
report_memory_accounting_error("pfs_memory_free_v1",
|
|
pfs_thread, size, klass, owner_thread);
|
|
}
|
|
}
|
|
#endif /* PFS_PARANOID */
|
|
|
|
/*
|
|
Do not check pfs_thread->m_enabled.
|
|
If a memory alloc was instrumented,
|
|
the corresponding free must be instrumented.
|
|
*/
|
|
/* Aggregate to MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME */
|
|
event_name_array= pfs_thread->write_instr_class_memory_stats();
|
|
stat= & event_name_array[index];
|
|
delta= stat->count_free(size, &delta_buffer);
|
|
|
|
if (delta != NULL)
|
|
{
|
|
pfs_thread->carry_memory_stat_delta(delta, index);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Aggregate to MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME */
|
|
event_name_array= global_instr_class_memory_array;
|
|
if (event_name_array)
|
|
{
|
|
stat= & event_name_array[index];
|
|
(void) stat->count_free(size, &delta_buffer);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void pfs_unlock_table_v1(PSI_table *table)
|
|
{
|
|
PFS_table *pfs_table= reinterpret_cast<PFS_table*> (table);
|
|
|
|
assert(pfs_table != NULL);
|
|
|
|
pfs_table->m_internal_lock= PFS_TL_NONE;
|
|
return;
|
|
}
|
|
|
|
PSI_metadata_lock *
|
|
pfs_create_metadata_lock_v1(
|
|
void *identity,
|
|
const MDL_key *mdl_key,
|
|
opaque_mdl_type mdl_type,
|
|
opaque_mdl_duration mdl_duration,
|
|
opaque_mdl_status mdl_status,
|
|
const char *src_file,
|
|
uint src_line)
|
|
{
|
|
if (! flag_global_instrumentation)
|
|
return NULL;
|
|
|
|
if (! global_metadata_class.m_enabled)
|
|
return NULL;
|
|
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (pfs_thread == NULL)
|
|
return NULL;
|
|
|
|
PFS_metadata_lock *pfs;
|
|
pfs= create_metadata_lock(identity, mdl_key,
|
|
mdl_type, mdl_duration, mdl_status,
|
|
src_file, src_line);
|
|
|
|
if (pfs != NULL)
|
|
{
|
|
pfs->m_owner_thread_id= pfs_thread->m_thread_internal_id;
|
|
pfs->m_owner_event_id= pfs_thread->m_event_id;
|
|
}
|
|
|
|
return reinterpret_cast<PSI_metadata_lock *> (pfs);
|
|
}
|
|
|
|
void
|
|
pfs_set_metadata_lock_status_v1(PSI_metadata_lock *lock, opaque_mdl_status mdl_status)
|
|
{
|
|
PFS_metadata_lock *pfs= reinterpret_cast<PFS_metadata_lock*> (lock);
|
|
assert(pfs != NULL);
|
|
pfs->m_mdl_status= mdl_status;
|
|
}
|
|
|
|
void
|
|
pfs_destroy_metadata_lock_v1(PSI_metadata_lock *lock)
|
|
{
|
|
PFS_metadata_lock *pfs= reinterpret_cast<PFS_metadata_lock*> (lock);
|
|
assert(pfs != NULL);
|
|
destroy_metadata_lock(pfs);
|
|
}
|
|
|
|
PSI_metadata_locker *
|
|
pfs_start_metadata_wait_v1(PSI_metadata_locker_state *state,
|
|
PSI_metadata_lock *lock,
|
|
const char *src_file,
|
|
uint src_line)
|
|
{
|
|
PFS_metadata_lock *pfs_lock= reinterpret_cast<PFS_metadata_lock*> (lock);
|
|
assert(state != NULL);
|
|
assert(pfs_lock != NULL);
|
|
|
|
if (! pfs_lock->m_enabled)
|
|
return NULL;
|
|
|
|
uint flags;
|
|
ulonglong timer_start= 0;
|
|
|
|
if (flag_thread_instrumentation)
|
|
{
|
|
PFS_thread *pfs_thread= my_thread_get_THR_PFS();
|
|
if (unlikely(pfs_thread == NULL))
|
|
return NULL;
|
|
if (! pfs_thread->m_enabled)
|
|
return NULL;
|
|
state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread);
|
|
flags= STATE_FLAG_THREAD;
|
|
|
|
if (pfs_lock->m_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags|= STATE_FLAG_TIMED;
|
|
}
|
|
|
|
if (flag_events_waits_current)
|
|
{
|
|
if (unlikely(pfs_thread->m_events_waits_current >=
|
|
& pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE]))
|
|
{
|
|
locker_lost++;
|
|
return NULL;
|
|
}
|
|
PFS_events_waits *wait= pfs_thread->m_events_waits_current;
|
|
state->m_wait= wait;
|
|
flags|= STATE_FLAG_EVENT;
|
|
|
|
PFS_events_waits *parent_event= wait - 1;
|
|
wait->m_event_type= EVENT_TYPE_WAIT;
|
|
wait->m_nesting_event_id= parent_event->m_event_id;
|
|
wait->m_nesting_event_type= parent_event->m_event_type;
|
|
|
|
wait->m_thread_internal_id= pfs_thread->m_thread_internal_id;
|
|
wait->m_class= &global_metadata_class;
|
|
wait->m_timer_start= timer_start;
|
|
wait->m_timer_end= 0;
|
|
wait->m_object_instance_addr= pfs_lock->m_identity;
|
|
wait->m_event_id= pfs_thread->m_event_id++;
|
|
wait->m_end_event_id= 0;
|
|
wait->m_weak_metadata_lock= pfs_lock;
|
|
wait->m_weak_version= pfs_lock->get_version();
|
|
wait->m_operation= OPERATION_TYPE_METADATA;
|
|
wait->m_source_file= src_file;
|
|
wait->m_source_line= src_line;
|
|
wait->m_wait_class= WAIT_CLASS_METADATA;
|
|
|
|
pfs_thread->m_events_waits_current++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pfs_lock->m_timed)
|
|
{
|
|
timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer);
|
|
state->m_timer_start= timer_start;
|
|
flags= STATE_FLAG_TIMED;
|
|
state->m_thread= NULL;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Complete shortcut.
|
|
*/
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (counted) */
|
|
global_metadata_stat.aggregate_counted();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
state->m_flags= flags;
|
|
state->m_metadata_lock= lock;
|
|
return reinterpret_cast<PSI_metadata_locker*> (state);
|
|
}
|
|
|
|
void
|
|
pfs_end_metadata_wait_v1(PSI_metadata_locker *locker,
|
|
int rc)
|
|
{
|
|
PSI_metadata_locker_state *state= reinterpret_cast<PSI_metadata_locker_state*> (locker);
|
|
assert(state != NULL);
|
|
|
|
ulonglong timer_end= 0;
|
|
ulonglong wait_time= 0;
|
|
|
|
PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread);
|
|
|
|
uint flags= state->m_flags;
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
timer_end= state->m_timer();
|
|
wait_time= timer_end - state->m_timer_start;
|
|
}
|
|
|
|
if (flags & STATE_FLAG_THREAD)
|
|
{
|
|
PFS_single_stat *event_name_array;
|
|
event_name_array= thread->write_instr_class_waits_stats();
|
|
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */
|
|
event_name_array[GLOBAL_METADATA_EVENT_INDEX].aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */
|
|
event_name_array[GLOBAL_METADATA_EVENT_INDEX].aggregate_counted();
|
|
}
|
|
|
|
if (flags & STATE_FLAG_EVENT)
|
|
{
|
|
PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait);
|
|
assert(wait != NULL);
|
|
|
|
wait->m_timer_end= timer_end;
|
|
wait->m_end_event_id= thread->m_event_id;
|
|
if (thread->m_flag_events_waits_history)
|
|
insert_events_waits_history(thread, wait);
|
|
if (thread->m_flag_events_waits_history_long)
|
|
insert_events_waits_history_long(wait);
|
|
thread->m_events_waits_current--;
|
|
|
|
assert(wait == thread->m_events_waits_current);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (flags & STATE_FLAG_TIMED)
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (timed) */
|
|
global_metadata_stat.aggregate_value(wait_time);
|
|
}
|
|
else
|
|
{
|
|
/* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (counted) */
|
|
global_metadata_stat.aggregate_counted();
|
|
}
|
|
}
|
|
}
|
|
|
|
void pfs_set_prepared_stmt_text_v1(PSI_prepared_stmt *prepared_stmt,
|
|
const char *text,
|
|
uint text_len)
|
|
{
|
|
PFS_prepared_stmt *pfs_prepared_stmt =
|
|
reinterpret_cast<PFS_prepared_stmt *>(prepared_stmt);
|
|
DBUG_ASSERT(pfs_prepared_stmt != NULL);
|
|
|
|
uint max_len = COL_INFO_SIZE;
|
|
if (text_len > max_len)
|
|
{
|
|
text_len = max_len;
|
|
}
|
|
|
|
memcpy(pfs_prepared_stmt->m_sqltext, text, text_len);
|
|
pfs_prepared_stmt->m_sqltext_length = text_len;
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
Implementation of the instrumentation interface.
|
|
@sa PSI_v1.
|
|
*/
|
|
PSI_v1 PFS_v1=
|
|
{
|
|
pfs_register_mutex_v1,
|
|
pfs_register_rwlock_v1,
|
|
pfs_register_cond_v1,
|
|
pfs_register_thread_v1,
|
|
pfs_register_file_v1,
|
|
pfs_register_stage_v1,
|
|
pfs_register_statement_v1,
|
|
pfs_register_socket_v1,
|
|
pfs_init_mutex_v1,
|
|
pfs_destroy_mutex_v1,
|
|
pfs_init_rwlock_v1,
|
|
pfs_destroy_rwlock_v1,
|
|
pfs_init_cond_v1,
|
|
pfs_destroy_cond_v1,
|
|
pfs_init_socket_v1,
|
|
pfs_destroy_socket_v1,
|
|
pfs_get_table_share_v1,
|
|
pfs_release_table_share_v1,
|
|
pfs_drop_table_share_v1,
|
|
pfs_open_table_v1,
|
|
pfs_unbind_table_v1,
|
|
pfs_rebind_table_v1,
|
|
pfs_close_table_v1,
|
|
pfs_create_file_v1,
|
|
pfs_spawn_thread_v1,
|
|
pfs_new_thread_v1,
|
|
pfs_set_thread_id_v1,
|
|
pfs_set_thread_THD_v1,
|
|
pfs_set_thread_os_id_v1,
|
|
pfs_get_thread_v1,
|
|
pfs_set_thread_user_v1,
|
|
pfs_set_thread_account_v1,
|
|
pfs_set_thread_db_v1,
|
|
pfs_set_thread_command_v1,
|
|
pfs_set_connection_type_v1,
|
|
pfs_set_thread_start_time_v1,
|
|
pfs_set_thread_state_v1,
|
|
pfs_set_thread_info_v1,
|
|
pfs_set_thread_v1,
|
|
pfs_delete_current_thread_v1,
|
|
pfs_delete_thread_v1,
|
|
pfs_get_thread_file_name_locker_v1,
|
|
pfs_get_thread_file_stream_locker_v1,
|
|
pfs_get_thread_file_descriptor_locker_v1,
|
|
pfs_unlock_mutex_v1,
|
|
pfs_unlock_rwlock_v1,
|
|
pfs_signal_cond_v1,
|
|
pfs_broadcast_cond_v1,
|
|
pfs_start_idle_wait_v1,
|
|
pfs_end_idle_wait_v1,
|
|
pfs_start_mutex_wait_v1,
|
|
pfs_end_mutex_wait_v1,
|
|
pfs_start_rwlock_rdwait_v1,
|
|
pfs_end_rwlock_rdwait_v1,
|
|
pfs_start_rwlock_wrwait_v1,
|
|
pfs_end_rwlock_wrwait_v1,
|
|
pfs_start_cond_wait_v1,
|
|
pfs_end_cond_wait_v1,
|
|
pfs_start_table_io_wait_v1,
|
|
pfs_end_table_io_wait_v1,
|
|
pfs_start_table_lock_wait_v1,
|
|
pfs_end_table_lock_wait_v1,
|
|
pfs_start_file_open_wait_v1,
|
|
pfs_end_file_open_wait_v1,
|
|
pfs_end_file_open_wait_and_bind_to_descriptor_v1,
|
|
pfs_end_temp_file_open_wait_and_bind_to_descriptor_v1,
|
|
pfs_start_file_wait_v1,
|
|
pfs_end_file_wait_v1,
|
|
pfs_start_file_close_wait_v1,
|
|
pfs_end_file_close_wait_v1,
|
|
pfs_end_file_rename_wait_v1,
|
|
pfs_start_stage_v1,
|
|
pfs_get_current_stage_progress_v1,
|
|
pfs_end_stage_v1,
|
|
pfs_get_thread_statement_locker_v1,
|
|
pfs_refine_statement_v1,
|
|
pfs_start_statement_v1,
|
|
pfs_set_statement_text_v1,
|
|
pfs_set_statement_lock_time_v1,
|
|
pfs_set_statement_rows_sent_v1,
|
|
pfs_set_statement_rows_examined_v1,
|
|
pfs_inc_statement_created_tmp_disk_tables_v1,
|
|
pfs_inc_statement_created_tmp_tables_v1,
|
|
pfs_inc_statement_select_full_join_v1,
|
|
pfs_inc_statement_select_full_range_join_v1,
|
|
pfs_inc_statement_select_range_v1,
|
|
pfs_inc_statement_select_range_check_v1,
|
|
pfs_inc_statement_select_scan_v1,
|
|
pfs_inc_statement_sort_merge_passes_v1,
|
|
pfs_inc_statement_sort_range_v1,
|
|
pfs_inc_statement_sort_rows_v1,
|
|
pfs_inc_statement_sort_scan_v1,
|
|
pfs_set_statement_no_index_used_v1,
|
|
pfs_set_statement_no_good_index_used_v1,
|
|
pfs_end_statement_v1,
|
|
pfs_get_thread_transaction_locker_v1,
|
|
pfs_start_transaction_v1,
|
|
pfs_set_transaction_xid_v1,
|
|
pfs_set_transaction_xa_state_v1,
|
|
pfs_set_transaction_gtid_v1,
|
|
pfs_set_transaction_trxid_v1,
|
|
pfs_inc_transaction_savepoints_v1,
|
|
pfs_inc_transaction_rollback_to_savepoint_v1,
|
|
pfs_inc_transaction_release_savepoint_v1,
|
|
pfs_end_transaction_v1,
|
|
pfs_start_socket_wait_v1,
|
|
pfs_end_socket_wait_v1,
|
|
pfs_set_socket_state_v1,
|
|
pfs_set_socket_info_v1,
|
|
pfs_set_socket_thread_owner_v1,
|
|
pfs_create_prepared_stmt_v1,
|
|
pfs_destroy_prepared_stmt_v1,
|
|
pfs_reprepare_prepared_stmt_v1,
|
|
pfs_execute_prepared_stmt_v1,
|
|
pfs_set_prepared_stmt_text_v1,
|
|
pfs_digest_start_v1,
|
|
pfs_digest_end_v1,
|
|
pfs_set_thread_connect_attrs_v1,
|
|
pfs_start_sp_v1,
|
|
pfs_end_sp_v1,
|
|
pfs_drop_sp_v1,
|
|
pfs_get_sp_share_v1,
|
|
pfs_release_sp_share_v1,
|
|
pfs_register_memory_v1,
|
|
pfs_memory_alloc_v1,
|
|
pfs_memory_realloc_v1,
|
|
pfs_memory_claim_v1,
|
|
pfs_memory_free_v1,
|
|
pfs_unlock_table_v1,
|
|
pfs_create_metadata_lock_v1,
|
|
pfs_set_metadata_lock_status_v1,
|
|
pfs_destroy_metadata_lock_v1,
|
|
pfs_start_metadata_wait_v1,
|
|
pfs_end_metadata_wait_v1,
|
|
pfs_set_thread_peer_port_v1
|
|
};
|
|
|
|
static void* get_interface(int version)
|
|
{
|
|
switch (version)
|
|
{
|
|
case PSI_VERSION_1:
|
|
return &PFS_v1;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
C_MODE_END
|
|
|
|
struct PSI_bootstrap PFS_bootstrap=
|
|
{
|
|
get_interface
|
|
};
|