From e4757031a2319de524cc4da6c881b8b2d306ee20 Mon Sep 17 00:00:00 2001 From: Jon Meredith Date: Wed, 27 May 2009 16:49:18 -0600 Subject: [PATCH] Added mutex stats. --- c_src/bdberl_drv.c | 100 +++++++++++++++++++++++++++++++++++++++++- c_src/bdberl_drv.h | 2 + include/bdberl.hrl | 6 ++- src/bdberl.erl | 74 +++++++++++++++++++++++++++++++ test/bdberl_SUITE.erl | 7 ++- 5 files changed, 184 insertions(+), 5 deletions(-) diff --git a/c_src/bdberl_drv.c b/c_src/bdberl_drv.c index 0c9292a..f14ca4f 100644 --- a/c_src/bdberl_drv.c +++ b/c_src/bdberl_drv.c @@ -38,6 +38,7 @@ static void do_async_stat(void* arg); static void do_async_lock_stat(void* arg); static void do_async_log_stat(void* arg); static void do_async_memp_stat(void* arg); +static void do_async_mutex_stat(void* arg); static int add_dbref(PortData* data, int dbref); static int del_dbref(PortData* data, int dbref); @@ -966,6 +967,45 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd, int rc = G_DB_ENV->memp_stat_print(G_DB_ENV, flags); RETURN_INT(rc, outbuf); } + case CMD_MUTEX_STAT: + { + FAIL_IF_ASYNC_PENDING(d, outbuf); + + // Inbuf is <> + // If the working buffer is large enough, copy the data to put/get into it. Otherwise, realloc + // until it is large enough + if (d->work_buffer_sz < inbuf_sz) + { + d->work_buffer = driver_realloc(d->work_buffer, inbuf_sz); + d->work_buffer_sz = inbuf_sz; + } + + // Copy the payload into place + memcpy(d->work_buffer, inbuf, inbuf_sz); + d->work_buffer_offset = inbuf_sz; + + // Mark the port as busy and then schedule the appropriate async operation + d->async_op = cmd; + d->async_pool = G_TPOOL_GENERAL; + bdberl_tpool_run(d->async_pool, &do_async_mutex_stat, d, 0, &d->async_job); + + // Let caller know that the operation is in progress + // Outbuf is: <<0:32>> + RETURN_INT(0, outbuf); + } + case CMD_MUTEX_STAT_PRINT: + { + FAIL_IF_ASYNC_PENDING(d, outbuf); + + // Inbuf is << Flags:32 >> + unsigned int flags = UNPACK_INT(inbuf, 0); + + // Outbuf is <> + // Run the command on the VM thread - this is for debugging only, + // any real monitoring will use the async lock_stat + int rc = G_DB_ENV->mutex_stat_print(G_DB_ENV, flags); + RETURN_INT(rc, outbuf); + } } *outbuf = 0; return 0; @@ -1640,6 +1680,37 @@ static void async_cleanup_and_send_memp_stats(PortData* d, DB_MPOOL_STAT *gsp, } +static void async_cleanup_and_send_mutex_stats(PortData* d, DB_MUTEX_STAT *msp) +{ + // Save the port and pid references -- we need copies independent from the PortData + // structure. Once we release the port_lock after clearing the cmd, it's possible that + // the port could go away without waiting on us to finish. This is acceptable, but we need + // to be certain that there is no overlap of data between the two threads. driver_send_term + // is safe to use from a thread, even if the port you're sending from has already expired. + ErlDrvPort port = d->port; + ErlDrvTermData pid = d->port_owner; + async_cleanup(d); + + ErlDrvTermData response[] = { + ERL_DRV_ATOM, driver_mk_atom("ok"), + // Start of list + ST_STATS_TUPLE(msp, mutex_align), /* Mutex alignment */ + ST_STATS_TUPLE(msp, mutex_tas_spins), /* Mutex test-and-set spins */ + ST_STATS_TUPLE(msp, mutex_cnt), /* Mutex count */ + ST_STATS_TUPLE(msp, mutex_free), /* Available mutexes */ + ST_STATS_TUPLE(msp, mutex_inuse), /* Mutexes in use */ + ST_STATS_TUPLE(msp, mutex_inuse_max), /* Maximum mutexes ever in use */ + ST_STATS_TUPLE(msp, region_wait), /* Region lock granted after wait. */ + ST_STATS_TUPLE(msp, region_nowait), /* Region lock granted without wait. */ + ST_STATS_TUPLE(msp, regsize), /* Region size. */ + // End of list + ERL_DRV_NIL, + ERL_DRV_LIST, 9+1, + ERL_DRV_TUPLE, 2 + }; + driver_send_term(port, pid, response, sizeof(response) / sizeof(response[0])); +} + static void do_async_put(void* arg) { @@ -1955,7 +2026,7 @@ static void do_async_log_stat(void* arg) async_cleanup_and_send_log_stats(d, lsp); } - // Finally, clean up lock stats + // Finally, clean up stats if (NULL != lsp) { free(lsp); @@ -1982,7 +2053,7 @@ static void do_async_memp_stat(void* arg) async_cleanup_and_send_memp_stats(d, gsp, fsp); } - // Finally, clean up lock stats + // Finally, clean up stats if (NULL != gsp) { free(gsp); @@ -1993,6 +2064,31 @@ static void do_async_memp_stat(void* arg) } } +static void do_async_mutex_stat(void* arg) +{ + // Payload is: <> + PortData* d = (PortData*)arg; + + // Extract operation flags + unsigned flags = UNPACK_INT(d->work_buffer, 0); + + DB_MUTEX_STAT *msp = NULL; + int rc = G_DB_ENV->mutex_stat(G_DB_ENV, &msp, flags); + if (rc != 0 || msp == NULL) + { + async_cleanup_and_send_rc(d, rc); + } + else + { + async_cleanup_and_send_mutex_stats(d, msp); + } + + // Finally, clean up stats + if (NULL != msp) + { + free(msp); + } +} static void* zalloc(unsigned int size) { diff --git a/c_src/bdberl_drv.h b/c_src/bdberl_drv.h index bea6194..b30bc5b 100644 --- a/c_src/bdberl_drv.h +++ b/c_src/bdberl_drv.h @@ -55,6 +55,8 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd, #define CMD_LOG_STAT_PRINT 24 #define CMD_MEMP_STAT 25 #define CMD_MEMP_STAT_PRINT 26 +#define CMD_MUTEX_STAT 27 +#define CMD_MUTEX_STAT_PRINT 28 /** * Command status values diff --git a/include/bdberl.hrl b/include/bdberl.hrl index a206c1a..c4cee89 100644 --- a/include/bdberl.hrl +++ b/include/bdberl.hrl @@ -30,8 +30,10 @@ -define(CMD_LOCK_STAT_PRINT, 22). -define(CMD_LOG_STAT, 23). -define(CMD_LOG_STAT_PRINT, 24). --define(CMD_MEMP_STAT, 25). --define(CMD_MEMP_STAT_PRINT,26). +-define(CMD_MEMP_STAT, 25). +-define(CMD_MEMP_STAT_PRINT, 26). +-define(CMD_MUTEX_STAT, 27). +-define(CMD_MUTEX_STAT_PRINT,28). -define(DB_TYPE_BTREE, 1). -define(DB_TYPE_HASH, 2). diff --git a/src/bdberl.erl b/src/bdberl.erl index 974bd22..84fb557 100644 --- a/src/bdberl.erl +++ b/src/bdberl.erl @@ -22,6 +22,8 @@ log_stat_print/1, memp_stat/1, memp_stat_print/1, + mutex_stat/1, + mutex_stat_print/1, env_stat_print/1, transaction/1, transaction/2, transaction/3, put/3, put/4, @@ -1613,6 +1615,78 @@ memp_stat_print(Opts) -> Error -> {error, Error} end. +%%-------------------------------------------------------------------- +%% @doc +%% Retrieve mutex stats +%% +%% This function retrieves mutex statistics +%% +%% === Options === +%% +%%
+%%
stat_clear
+%%
Reset statistics after returning their values
+%%
+%% +%% @spec mutex_stat(Opts) -> {ok, [{atom(), number()}]} | {error, Error} +%% where +%% Opts = [atom()] +%% +%% @end +%%-------------------------------------------------------------------- +-spec mutex_stat(Opts :: db_flags()) -> + {ok, [{atom(), number()}]} | db_error(). + +mutex_stat(Opts) -> + Flags = process_flags(Opts), + Cmd = <>, + <> = erlang:port_control(get_port(), ?CMD_MUTEX_STAT, Cmd), + case decode_rc(Result) of + ok -> + receive + {error, Reason} -> + {error, decode_rc(Reason)}; + {ok, Stats} -> + {ok, Stats} + end; + Error -> + {error, Error} + end. + + +%%-------------------------------------------------------------------- +%% @doc +%% Print mutex stats +%% +%% This function prints mutex statistics to wherever +%% BDB messages are being sent +%% +%% === Options === +%% +%%
+%%
stat_all
+%%
Display all available information.
+%%
stat_clear
+%%
Reset statistics after displaying their values.
+%%
+%% +%% @spec mutex_stat_print(Opts) -> ok | {error, Error} +%% where +%% Opts = [atom()] +%% +%% @end +%%-------------------------------------------------------------------- +-spec mutex_stat_print(Opts :: db_flags()) -> + ok | db_error(). +mutex_stat_print(Opts) -> + Flags = process_flags(Opts), + Cmd = <>, + <> = erlang:port_control(get_port(), ?CMD_MUTEX_STAT_PRINT, Cmd), + case decode_rc(Result) of + ok -> ok; + Error -> {error, Error} + end. + %%-------------------------------------------------------------------- %% @doc diff --git a/test/bdberl_SUITE.erl b/test/bdberl_SUITE.erl index 1c9dd78..55e9d5d 100644 --- a/test/bdberl_SUITE.erl +++ b/test/bdberl_SUITE.erl @@ -37,7 +37,8 @@ all() -> stat_should_fail_on_bad_dbref, lock_stat_should_report_on_success, log_stat_should_report_on_success, - memp_stat_should_report_on_success]. + memp_stat_should_report_on_success, + mutex_stat_should_report_on_success]. dbconfig(Config) -> @@ -298,3 +299,7 @@ memp_stat_should_report_on_success(_Config) -> true = is_list(Fstat), true = is_list(Gstat), done. + +mutex_stat_should_report_on_success(_Config) -> + {ok, _Stat} = bdberl:mutex_stat([]), + done.