diff --git a/lib/resty/wasmx/shm.lua b/lib/resty/wasmx/shm.lua new file mode 100644 index 000000000..0c87c921c --- /dev/null +++ b/lib/resty/wasmx/shm.lua @@ -0,0 +1,264 @@ +-- vim:set ts=4 sw=4 sts=4 et: + +local ffi = require "ffi" +local wasmx = require "resty.wasmx" + + +local C = ffi.C +local error = error +local type = type +local table_new = table.new +local ffi_cast = ffi.cast +local ffi_fill = ffi.fill +local ffi_new = ffi.new +local ffi_str = ffi.string +local initialized = false +local FFI_DECLINED = wasmx.FFI_DECLINED +local FFI_ERROR = wasmx.FFI_ERROR +local WASM_SHM_KEY = {} -- ensures shm's only locally accessible + +local get_zones_handler + +local types = { + ffi_shm = { + SHM_TYPE_KV = 0, + SHM_TYPE_QUEUE = 1, + SHM_TYPE_METRICS = 2, + }, + ffi_metric = { + COUNTER = 0, + GAUGE = 1, + HISTOGRAM = 2, + } +} + +local _M = {} + + +ffi.cdef [[ + typedef unsigned char u_char; + typedef uintptr_t ngx_uint_t; + typedef ngx_uint_t ngx_msec_t; + typedef struct ngx_log_s ngx_log_t; + typedef struct ngx_slab_pool_s ngx_slab_pool_t; + + typedef enum { + NGX_WASM_SHM_TYPE_KV, + NGX_WASM_SHM_TYPE_QUEUE, + NGX_WASM_SHM_TYPE_METRICS, + } ngx_wasm_shm_type_e; + + typedef enum { + NGX_WASM_SHM_EVICTION_LRU, + NGX_WASM_SHM_EVICTION_SLRU, + NGX_WASM_SHM_EVICTION_NONE, + } ngx_wasm_shm_eviction_e; + + typedef struct { + ngx_wasm_shm_type_e type; + ngx_wasm_shm_eviction_e eviction; + ngx_str_t name; + ngx_log_t *log; + ngx_slab_pool_t *shpool; + void *data; + } ngx_wasm_shm_t; + + typedef enum { + NGX_WA_METRIC_COUNTER, + NGX_WA_METRIC_GAUGE, + NGX_WA_METRIC_HISTOGRAM, + } ngx_wa_metric_type_e; + + + typedef struct { + ngx_uint_t value; + ngx_msec_t last_update; + } ngx_wa_metrics_gauge_t; + + typedef struct { + uint32_t upper_bound; + uint32_t count; + } ngx_wa_metrics_bin_t; + + typedef struct { + uint8_t n_bins; + ngx_wa_metrics_bin_t bins[]; + } ngx_wa_metrics_histogram_t; + + typedef union { + ngx_uint_t counter; + ngx_wa_metrics_gauge_t gauge; + ngx_wa_metrics_histogram_t *histogram; + } ngx_wa_metric_val_t; + + typedef struct { + ngx_wa_metric_type_e metric_type; + ngx_wa_metric_val_t slots[]; + } ngx_wa_metric_t; + + + typedef void (*ngx_wa_ffi_shm_get_zones_handler_pt)(ngx_wasm_shm_t *shm); + + + int ngx_wa_ffi_shm_get_zones(ngx_wa_ffi_shm_get_zones_handler_pt handler); + int ngx_wa_ffi_shm_get_keys(ngx_wasm_shm_t *shm, + ngx_uint_t n, + ngx_str_t **keys); + int ngx_wa_ffi_shm_get_kv_value(ngx_wasm_shm_t *shm, + ngx_str_t *k, + ngx_str_t **v, + uint32_t *cas); + int ngx_wa_ffi_shm_get_metric(ngx_str_t *k, + u_char *mbuf, size_t mbs, + u_char *hbuf, size_t hbs); + int ngx_wa_ffi_shm_one_slot_metric_size(); + int ngx_wa_ffi_shm_max_histogram_size(); +]] + + +local function get_keys(zone, max) + if max ~= nil and (type(max) ~= "number" or max < 1) then + error("n must be a positive number", 2) + end + + local shm = zone[WASM_SHM_KEY] + local nkeys = C.ngx_wa_ffi_shm_get_keys(shm, 0, nil) + if nkeys == FFI_ERROR then + return nil, "failed retrieving shm zone keys total" + end + + nkeys = (max and nkeys > max) and max or nkeys + + local ckeys = ffi_new("ngx_str_t *[?]", nkeys) + local pckeys = ffi_cast("ngx_str_t **", ckeys) + local rc = C.ngx_wa_ffi_shm_get_keys(shm, nkeys, pckeys) + if rc == FFI_ERROR then + return nil, "failed retrieving shm zone keys" + end + + local keys = table_new(0, nkeys) + + for i = 1, nkeys do + local ckey = ckeys[i - 1] + keys[i] = ffi_str(ckey.data, ckey.len) + end + + return keys +end + + +local function get_kv_value(zone, key) + if type(key) ~= "string" then + error("key must be a string", 2) + end + + local shm = zone[WASM_SHM_KEY] + local cname = ffi_new("ngx_str_t", { data = key, len = #key }) + local cvalue = ffi_new("ngx_str_t *[1]") + local ccas = ffi_new("uint32_t [1]") + + local rc = C.ngx_wa_ffi_shm_get_kv_value(shm, cname, cvalue, ccas) + if rc == FFI_ERROR then + return nil, "failed retrieving kv value" + end + + if rc == FFI_DECLINED then + return nil, "key not found" + end + + return ffi_str(cvalue[0].data, cvalue[0].len) +end + + +local function new_zero_filled_buffer(ctype, size) + local buf = ffi_new(ctype, size) + ffi_fill(buf, size) + + return buf +end + + +local function get_metric(zone, name) + if type(name) ~= "string" then + error("name must be a string", 2) + end + + local cname = ffi_new("ngx_str_t", { data = name, len = #name }) + local mbs = C.ngx_wa_ffi_shm_one_slot_metric_size() + local hbs = C.ngx_wa_ffi_shm_max_histogram_size() + local mbuf = new_zero_filled_buffer("u_char [?]", mbs) + local hbuf = new_zero_filled_buffer("u_char [?]", hbs) + + local rc = C.ngx_wa_ffi_shm_get_metric(cname, mbuf, mbs, hbuf, hbs) + if rc == FFI_ERROR then + return nil, "failed retrieving metric" + end + + if rc == FFI_DECLINED then + return nil, "metric not found" + end + + local cmetric = ffi_cast("ngx_wa_metric_t *", mbuf) + + if cmetric.metric_type == types.ffi_metric.COUNTER then + return { type = "counter", value = tonumber(cmetric.slots[0].counter) } + + elseif cmetric.metric_type == types.ffi_metric.GAUGE then + return { type = "gauge", value = tonumber(cmetric.slots[0].gauge.value) } + + elseif cmetric.metric_type == types.ffi_metric.HISTOGRAM then + local ch = ffi_cast("ngx_wa_metrics_histogram_t *", hbuf) + local h = { type = "histogram", value = {} } + + for i = 0, ch.n_bins do + local cb = ch.bins[i] + + if cb.upper_bound == 0 then + break + end + + h.value[cb.upper_bound] = cb.count + end + + return h + end +end + + +local get_zones_handler = ffi_cast("ngx_wa_ffi_shm_get_zones_handler_pt", +function(shm) + local zone_name = ffi_str(shm.name.data, shm.name.len) + _M[zone_name] = { [WASM_SHM_KEY] = shm } + + if shm.type == types.ffi_shm.SHM_TYPE_KV then + _M[zone_name].get_keys = get_keys + _M[zone_name].get = get_kv_value + + elseif shm.type == types.ffi_shm.SHM_TYPE_QUEUE then + -- NYI + + elseif shm.type == types.ffi_shm.SHM_TYPE_METRICS then + _M[zone_name].get_keys = get_keys + _M[zone_name].get = get_metric + end +end) + + +function _M.init() + if initialized then + return + end + + local rc = C.ngx_wa_ffi_shm_get_zones(get_zones_handler) + if rc == FFI_DECLINED then + ngx.log(ngx.INFO, "no shm zones found") + end + + initialized = true +end + + +_M.init() + + +return _M diff --git a/src/common/lua/ngx_wasm_lua_ffi.c b/src/common/lua/ngx_wasm_lua_ffi.c index 1c24ae600..b3e35a841 100644 --- a/src/common/lua/ngx_wasm_lua_ffi.c +++ b/src/common/lua/ngx_wasm_lua_ffi.c @@ -4,6 +4,7 @@ #include "ddebug.h" #include +#include #include @@ -256,4 +257,110 @@ ngx_http_wasm_ffi_set_host_properties_handlers(ngx_http_request_t *r, return ngx_proxy_wasm_properties_set_ffi_handlers(pwctx, getter, setter, r); } + + +ngx_int_t +ngx_wa_ffi_shm_get_zones(ngx_wa_ffi_shm_get_zones_handler_pt handler) +{ + ngx_uint_t i; + ngx_cycle_t *cycle = (ngx_cycle_t *) ngx_cycle; + ngx_array_t *shms = ngx_wasm_core_shms(cycle); + ngx_wasm_shm_mapping_t *mappings; + ngx_wasm_shm_t *shm; + + if (!handler) { + return NGX_ERROR; + } + + if (!shms) { + return NGX_DECLINED; + } + + mappings = shms->elts; + + for (i = 0; i < shms->nelts; i++) { + shm = mappings[i].zone->data; + handler(shm); + } + + return NGX_OK; +} + + +static void +shm_kv_retrieve_keys(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, + ngx_int_t *total, ngx_int_t limit, ngx_str_t **keys) +{ + ngx_wasm_shm_kv_node_t *n = (ngx_wasm_shm_kv_node_t *) node; + + if (!node || node == sentinel || (limit > 0 && *total == limit)) { + return; + } + + if (keys) { + keys[*total] = &n->key.str; + } + + (*total)++; + + shm_kv_retrieve_keys(node->right, sentinel, total, limit, keys); + shm_kv_retrieve_keys(node->left, sentinel, total, limit, keys); +} + + +ngx_int_t +ngx_wa_ffi_shm_get_keys(ngx_wasm_shm_t *shm, ngx_uint_t n, ngx_str_t **keys) +{ + ngx_int_t total = 0; + ngx_wasm_shm_kv_t *kv; + + if (!shm || shm->type == NGX_WASM_SHM_TYPE_QUEUE) { + return NGX_ERROR; + } + + kv = shm->data; + + ngx_shmtx_lock(&shm->shpool->mutex); + + shm_kv_retrieve_keys(kv->rbtree.root, kv->rbtree.sentinel, &total, n, keys); + + ngx_shmtx_unlock(&shm->shpool->mutex); + + return total; +} + + +ngx_int_t +ngx_wa_ffi_shm_get_kv_value(ngx_wasm_shm_t *shm, ngx_str_t *k, ngx_str_t **v, + uint32_t *cas) +{ + if (!shm || !k || !v || !cas) { + return NGX_ERROR; + } + + return ngx_wasm_shm_kv_get_locked(shm, k, NULL, v, cas); +} + + +ngx_int_t +ngx_wa_ffi_shm_get_metric(ngx_str_t *name, + u_char *m_buf, size_t mbs, + u_char *h_buf, size_t hbs) +{ + uint32_t metric_id; + ngx_wa_metrics_t *metrics; + ngx_wa_metric_t *m; + + if (!name || !m_buf || !h_buf) { + return NGX_ERROR; + } + + metrics = ngx_wasmx_metrics((ngx_cycle_t *) ngx_cycle); + metric_id = ngx_crc32_long(name->data, name->len); + m = (ngx_wa_metric_t *) m_buf; + + ngx_wa_metrics_set_histogram_buffer(m, h_buf, hbs); + + return ngx_wa_metrics_get(metrics, metric_id, m); +} #endif diff --git a/src/common/lua/ngx_wasm_lua_ffi.h b/src/common/lua/ngx_wasm_lua_ffi.h index 0ed226f86..299f80a66 100644 --- a/src/common/lua/ngx_wasm_lua_ffi.h +++ b/src/common/lua/ngx_wasm_lua_ffi.h @@ -24,6 +24,9 @@ typedef struct { } ngx_wasm_ffi_filter_t; +typedef void (*ngx_wa_ffi_shm_get_zones_handler_pt)(ngx_wasm_shm_t *shm); + + ngx_int_t ngx_http_wasm_ffi_plan_new(ngx_wavm_t *vm, ngx_wasm_ffi_filter_t *filters, size_t n_filters, ngx_wasm_ops_plan_t **out, u_char *err, size_t *errlen); @@ -43,4 +46,25 @@ ngx_int_t ngx_http_wasm_ffi_set_host_properties_handlers(ngx_http_request_t *r, #endif +ngx_int_t ngx_wa_ffi_shm_get_zones(ngx_wa_ffi_shm_get_zones_handler_pt handler); +ngx_int_t ngx_wa_ffi_shm_get_keys(ngx_wasm_shm_t *shm, ngx_uint_t n, + ngx_str_t **keys); +ngx_int_t ngx_wa_ffi_shm_get_kv_value(ngx_wasm_shm_t *shm, ngx_str_t *k, + ngx_str_t **v, uint32_t *cas); +ngx_int_t ngx_wa_ffi_shm_get_metric(ngx_str_t *k, u_char *m_buf, size_t mbs, + u_char *h_buf, size_t hbs); + + +ngx_int_t +ngx_wa_ffi_shm_one_slot_metric_size() +{ + return NGX_WA_METRICS_ONE_SLOT_METRIC_SIZE; +} + +ngx_int_t +ngx_wa_ffi_shm_max_histogram_size() +{ + return NGX_WA_METRICS_MAX_HISTOGRAM_SIZE; +} + #endif /* _NGX_WASM_LUA_FFI_H_INCLUDED_ */ diff --git a/src/common/metrics/ngx_wa_histogram.c b/src/common/metrics/ngx_wa_histogram.c index 9dbafafde..ad3e394f1 100644 --- a/src/common/metrics/ngx_wa_histogram.c +++ b/src/common/metrics/ngx_wa_histogram.c @@ -142,11 +142,11 @@ histogram_log(ngx_wa_metrics_t *metrics, ngx_wa_metric_t *m, uint32_t mid) for (i = 0; i < h->n_bins; i++) { b = &h->bins[i]; - if (b->upper_bound == 0) { + p = ngx_sprintf(p, " %uD: %uD;", b->upper_bound, b->count); + + if (b->upper_bound == NGX_MAX_UINT32_VALUE) { break; } - - p = ngx_sprintf(p, " %uD: %uD;", b->upper_bound, b->count); } ngx_log_debug3(NGX_LOG_DEBUG_WASM, metrics->shm->log, 0, @@ -224,12 +224,12 @@ ngx_wa_metrics_histogram_get(ngx_wa_metrics_t *metrics, ngx_wa_metric_t *m, for (j = 0; j < h->n_bins; j++) { b = &h->bins[j]; - if (b->upper_bound == 0) { - break; - } - out_b = histogram_bin(metrics, out, b->upper_bound, NULL); out_b->count += b->count; + + if (b->upper_bound == NGX_MAX_UINT32_VALUE) { + break; + } } } } diff --git a/src/common/metrics/ngx_wa_metrics.h b/src/common/metrics/ngx_wa_metrics.h index f3f4b261b..81f92c677 100644 --- a/src/common/metrics/ngx_wa_metrics.h +++ b/src/common/metrics/ngx_wa_metrics.h @@ -5,12 +5,18 @@ #include -#define NGX_WA_METRICS_BINS_INIT 5 -#define NGX_WA_METRICS_BINS_MAX 18 -#define NGX_WA_METRICS_BINS_INCREMENT 4 -#define NGX_WA_METRICS_MAX_HISTOGRAM_SIZE sizeof(ngx_wa_metrics_histogram_t) \ - + sizeof(ngx_wa_metrics_bin_t) \ - * NGX_WA_METRICS_BINS_MAX +#define ngx_wa_metrics_counter(m) m->slots[0].counter +#define ngx_wa_metrics_gauge(m) m->slots[0].gauge.value + + +#define NGX_WA_METRICS_BINS_INIT 5 +#define NGX_WA_METRICS_BINS_MAX 18 +#define NGX_WA_METRICS_BINS_INCREMENT 4 +#define NGX_WA_METRICS_MAX_HISTOGRAM_SIZE sizeof(ngx_wa_metrics_histogram_t)\ + + sizeof(ngx_wa_metrics_bin_t) \ + * NGX_WA_METRICS_BINS_MAX +#define NGX_WA_METRICS_ONE_SLOT_METRIC_SIZE sizeof(ngx_wa_metric_t) \ + + sizeof(ngx_wa_metric_val_t) typedef struct ngx_wa_metrics_s ngx_wa_metrics_t; @@ -84,7 +90,6 @@ ngx_int_t ngx_wa_metrics_record(ngx_wa_metrics_t *metrics, uint32_t metric_id, ngx_int_t ngx_wa_metrics_get(ngx_wa_metrics_t *metrics, uint32_t metric_id, ngx_wa_metric_t *o); - ngx_int_t ngx_wa_metrics_histogram_add_locked(ngx_wa_metrics_t *metrics, ngx_wa_metric_t *m); ngx_int_t ngx_wa_metrics_histogram_record(ngx_wa_metrics_t *metrics, @@ -93,4 +98,15 @@ void ngx_wa_metrics_histogram_get(ngx_wa_metrics_t *metrics, ngx_wa_metric_t *m, ngx_uint_t slots, ngx_wa_metrics_histogram_t *out); +static ngx_inline ngx_wa_metrics_histogram_t * +ngx_wa_metrics_set_histogram_buffer(ngx_wa_metric_t *m, u_char *b, size_t s) +{ + m->slots[0].histogram = (ngx_wa_metrics_histogram_t *) b; + m->slots[0].histogram->n_bins = (s - sizeof(ngx_wa_metrics_histogram_t)) + / sizeof(ngx_wa_metrics_bin_t); + m->slots[0].histogram->bins[0].upper_bound = NGX_MAX_UINT32_VALUE; + + return m->slots[0].histogram; +} + #endif /* _NGX_WA_METRICS_H_INCLUDED_ */ diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_host.c b/src/common/proxy_wasm/ngx_proxy_wasm_host.c index a1922080f..40785bb07 100644 --- a/src/common/proxy_wasm/ngx_proxy_wasm_host.c +++ b/src/common/proxy_wasm/ngx_proxy_wasm_host.c @@ -1667,26 +1667,30 @@ ngx_proxy_wasm_hfuncs_get_metric(ngx_wavm_instance_t *instance, ngx_uint_t *ret_value; ngx_cycle_t *cycle = (ngx_cycle_t *) ngx_cycle; ngx_wa_metrics_t *metrics = ngx_wasmx_metrics(cycle); - ngx_wa_metric_t m; + ngx_wa_metric_t *m; ngx_proxy_wasm_exec_t *pwexec = ngx_proxy_wasm_instance2pwexec(instance); - u_char trapmsg[NGX_MAX_ERROR_STR]; + u_char m_buf[NGX_WA_METRICS_ONE_SLOT_METRIC_SIZE]; u_char h_buf[NGX_WA_METRICS_MAX_HISTOGRAM_SIZE]; + u_char trapmsg[NGX_MAX_ERROR_STR]; - ngx_memzero(h_buf, NGX_WA_METRICS_MAX_HISTOGRAM_SIZE); + ngx_memzero(m_buf, sizeof(m_buf)); + ngx_memzero(h_buf, sizeof(h_buf)); metric_id = args[0].of.i32; ret_value = NGX_WAVM_HOST_LIFT(instance, args[1].of.i32, ngx_uint_t); - m.slots[0].histogram = (ngx_wa_metrics_histogram_t *) h_buf; - rc = ngx_wa_metrics_get(metrics, metric_id, &m); - if (rc == NGX_OK && m.type != NGX_WA_METRIC_HISTOGRAM) { - switch (m.type) { + m = (ngx_wa_metric_t *) m_buf; + ngx_wa_metrics_set_histogram_buffer(m, h_buf, sizeof(h_buf)); + + rc = ngx_wa_metrics_get(metrics, metric_id, m); + if (rc == NGX_OK && m->type != NGX_WA_METRIC_HISTOGRAM) { + switch (m->type) { case NGX_WA_METRIC_COUNTER: - *ret_value = m.slots[0].counter; + *ret_value = ngx_wa_metrics_counter(m); break; case NGX_WA_METRIC_GAUGE: - *ret_value = m.slots[0].gauge.value; + *ret_value = ngx_wa_metrics_gauge(m); break; default: @@ -1705,7 +1709,7 @@ ngx_proxy_wasm_hfuncs_get_metric(ngx_wavm_instance_t *instance, if (rc != NGX_DECLINED) { ngx_sprintf(p, "metric not found"); - } else if (m.type == NGX_WA_METRIC_HISTOGRAM) { + } else if (m->type == NGX_WA_METRIC_HISTOGRAM) { ngx_sprintf(p, "metric is a histogram"); } diff --git a/src/wasm/ngx_wasm_core_module.c b/src/wasm/ngx_wasm_core_module.c index 59bac7def..1c6ca4522 100644 --- a/src/wasm/ngx_wasm_core_module.c +++ b/src/wasm/ngx_wasm_core_module.c @@ -260,10 +260,11 @@ ngx_wasm_main_vm(ngx_cycle_t *cycle) ngx_inline ngx_array_t * ngx_wasm_core_shms(ngx_cycle_t *cycle) { - ngx_wasm_core_conf_t *wcf; + ngx_wasm_core_conf_t *wcf = ngx_wasm_core_cycle_get_conf(cycle); - wcf = ngx_wasm_core_cycle_get_conf(cycle); - ngx_wa_assert(wcf); + if (!wcf) { + return NULL; + } return &wcf->shms; } diff --git a/t/04-openresty/ffi/shm/001-get_zones.t b/t/04-openresty/ffi/shm/001-get_zones.t new file mode 100644 index 000000000..048d3aa0b --- /dev/null +++ b/t/04-openresty/ffi/shm/001-get_zones.t @@ -0,0 +1,100 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; +use t::TestWasmX::Lua; + +plan_tests(7); +run_tests(); + +__DATA__ + +=== TEST 1: shm - get_zones() +get_zones() is silently called when resty.wasmx.shm module is loaded. + +--- valgrind +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: hostcalls +--- shm_kv: kv1 1m +--- shm_queue: q1 1m +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + assert(shm.kv1 ~= nil) + assert(shm.q1 ~= nil) + assert(shm.metrics ~= nil) + } + + echo ok; + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] +[stub] + + + +=== TEST 2: shm - get_zones(), no zones +--- valgrind +--- load_nginx_modules: ngx_http_echo_module +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + assert(shm.metrics == nil) + } + + echo ok; + } +--- response_body +ok +--- error_log eval +qr/\[info\] .*? no shm zones found/, +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 3: shm - get_zones(), bad pointer +A call with a bad pointer is simply ignored. + +--- valgrind +--- load_nginx_modules: ngx_http_echo_module +--- config + location /t { + access_by_lua_block { + local ffi = require "ffi" + local C = ffi.C + + ffi.cdef [[ + typedef struct ngx_wasm_shm_t ngx_wasm_shm_t; + + typedef void (*ngx_wa_ffi_shm_get_zones_handler_pt)(ngx_wasm_shm_t *shm); + + int ngx_wa_ffi_shm_get_zones(ngx_wa_ffi_shm_get_zones_handler_pt handler); + ]] + + C.ngx_wa_ffi_shm_get_zones(nil) + } + + echo ok; + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] +[stub] diff --git a/t/04-openresty/ffi/shm/002-get_keys.t b/t/04-openresty/ffi/shm/002-get_keys.t new file mode 100644 index 000000000..86f1c5cb3 --- /dev/null +++ b/t/04-openresty/ffi/shm/002-get_keys.t @@ -0,0 +1,168 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; +use t::TestWasmX::Lua; + +plan_tests(9); +run_tests(); + +__DATA__ + +=== TEST 1: shm - get_keys() +--- valgrind +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: hostcalls +--- shm_kv: kv1 1m +--- config + location /t { + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv1/k1 \ + value=hello1'; + + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv1/k2 \ + value=hello2'; + + access_by_lua_block { + local shm = require "resty.wasmx.shm" + local keys = shm.kv1:get_keys() + + ngx.log(ngx.INFO, #keys, " key(s) retrieved") + + for i, k in ipairs(keys) do + ngx.log(ngx.INFO,"kv1 key: ", k) + end + } + + echo ok; + } +--- response_body +ok +--- error_log eval +[ + qr/\[info\] .*? 2 key\(s\) retrieved/, + qr/\[info\] .*? kv1 key: kv1\/k1/, + qr/\[info\] .*? kv1 key: kv1\/k2/, +] +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 2: shm - get_keys(), with limit +--- valgrind +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: hostcalls +--- shm_kv: kv1 1m +--- config + location /t { + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv1/k1 \ + value=hello1'; + + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv1/k2 \ + value=hello2'; + + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local kv1 = shm.kv1 + local keys = kv1:get_keys(1) + + ngx.log(ngx.INFO, #keys, " key(s) retrieved") + + for i, k in ipairs(keys) do + ngx.log(ngx.INFO,"kv1 key: ", k) + end + } + + echo ok; + } +--- response_body +ok +--- error_log eval +[ + qr/\[info\] .*? 1 key\(s\) retrieved/, + qr/\[info\] .*? kv1 key: kv1\/k1/, +] +--- no_error_log +[error] +[crit] +[emerg] +[alert] +qr/\[info\] .*? kv1 key: kv1\/t2/, + + + +=== TEST 3: shm - get_keys(), queue +--- valgrind +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: hostcalls +--- shm_queue: q1 1m +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + shm.q1:get_keys() + } + + echo fail; + } +--- error_code: 500 +--- error_log eval +qr/\[error\] .*? attempt to call method 'get_keys' \(a nil value\)/ +--- no_error_log +[crit] +[emerg] +[alert] +[stub] +[stub] +[stub] +[stub] + + + +=== TEST 4: shm - get_keys(), bad pointer +A call with a bad pointer is simply ignored. + +--- valgrind +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: hostcalls +--- shm_kv: kv1 1m +--- config + location /t { + access_by_lua_block { + local ffi = require "ffi" + local C = ffi.C + + ffi.cdef [[ + typedef uintptr_t ngx_uint_t; + typedef struct ngx_wasm_shm_t ngx_wasm_shm_t; + + int ngx_wa_ffi_shm_get_keys(ngx_wasm_shm_t *shm, + ngx_uint_t n, + ngx_str_t **keys); + ]] + + C.ngx_wa_ffi_shm_get_keys(nil, 0, nil) + } + + echo ok; + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] +[stub] +[stub] +[stub] diff --git a/t/04-openresty/ffi/shm/003-kv_get.t b/t/04-openresty/ffi/shm/003-kv_get.t new file mode 100644 index 000000000..80c525830 --- /dev/null +++ b/t/04-openresty/ffi/shm/003-kv_get.t @@ -0,0 +1,52 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; +use t::TestWasmX::Lua; + +plan_tests(8); +run_tests(); + +__DATA__ + +=== TEST 1: shm_kv - get() +--- valgrind +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: hostcalls +--- shm_kv: kv1 1m +--- config + location /t { + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv1/k1 \ + value=hello1'; + + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv1/k2 \ + value=hello2'; + + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local kv1 = shm.kv1 + local keys = kv1:get_keys() + + for i, k in ipairs(keys) do + local v = kv1:get(k) + ngx.log(ngx.INFO,"kv1 " .. k .. ": ", v) + end + } + echo ok; + } +--- response_body +ok +--- error_log eval +[ + qr/\[info\] .*? kv1 kv1\/k1: hello1/, + qr/\[info\] .*? kv1 kv1\/k2: hello2/, +] +--- no_error_log +[error] +[crit] +[emerg] +[alert] diff --git a/t/04-openresty/ffi/shm/004-metrics_get.t b/t/04-openresty/ffi/shm/004-metrics_get.t new file mode 100644 index 000000000..778eef9e1 --- /dev/null +++ b/t/04-openresty/ffi/shm/004-metrics_get.t @@ -0,0 +1,195 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; +use t::TestWasmX::Lua; + +plan_tests(9); +run_tests(); + +__DATA__ + +=== TEST 1: shm_metrics - get(), sanity +--- valgrind +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm hostcalls 'on_configure=define_metrics \ + on=request_headers \ + n_increments=13 \ + test=/t/metrics/increment_counters \ + metrics=c1'; + + proxy_wasm hostcalls 'on_configure=define_metrics \ + on=request_headers \ + test=/t/metrics/toggle_gauges \ + metrics=g1'; + + proxy_wasm hostcalls 'on_configure=define_metrics \ + on=request_headers \ + test=/t/metrics/record_histograms \ + metrics=h1 \ + value=10'; + + proxy_wasm hostcalls 'on_configure=define_metrics \ + on=request_headers \ + test=/t/metrics/record_histograms \ + metrics=h1 \ + value=100'; + + access_by_lua_block { + local pretty = require "pl.pretty" + local shm = require "resty.wasmx.shm" + + local metrics = shm.metrics + + local keys = { + "pw.hostcalls.c1", + "pw.hostcalls.g1", + "pw.hostcalls.h1" + } + + for i, k in pairs(keys) do + local m = metrics:get(k) + ngx.log(ngx.INFO, k .. ": " .. pretty.write(m, "")) + end + } + + echo ok; + } +--- response_body +ok +--- error_log eval +[ + qr/\[info\] .*? pw\.hostcalls\.c1: \{type="counter",value=13}/, + qr/\[info\] .*? pw\.hostcalls\.g1: \{type="gauge",value=1}/, + qr/\[info\] .*? pw\.hostcalls\.h1: \{type="histogram",value=\{\[4294967295]=0,\[128]=1,\[16]=1}}/, +] +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 2: shm_metrics - get(), invalid name +--- valgrind +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: hostcalls +--- config + location /t { + access_by_lua_block { + local pretty = require "pl.pretty" + local shm = require "resty.wasmx.shm" + + local metrics = shm.metrics + + local k = "invalid_name" + local m = metrics:get(k) + + ngx.log(ngx.INFO, k .. ": " .. pretty.write(m, "")) + } + + echo ok; + } +--- response_body +ok +--- error_log eval +[ + qr/\[info\] .*? invalid_name: nil/, +] +--- no_error_log +[error] +[crit] +[emerg] +[alert] +[stub] +[stub] + + + +=== TEST 3: shm_metrics - get(), non-initialized +--- valgrind +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm hostcalls 'on_configure=define_metrics \ + on=request_headers \ + metrics=c1,g1,h1'; + + access_by_lua_block { + local pretty = require "pl.pretty" + local shm = require "resty.wasmx.shm" + + local metrics = shm.metrics + + local keys = { + "pw.hostcalls.c1", + "pw.hostcalls.g1", + "pw.hostcalls.h1" + } + + for i, k in pairs(keys) do + local m = metrics:get(k) + ngx.log(ngx.INFO, k .. ": " .. pretty.write(m, "")) + end + } + + echo ok; + } +--- response_body +ok +--- error_log eval +[ + qr/\[info\] .*? pw\.hostcalls\.c1: \{type="counter",value=0}/, + qr/\[info\] .*? pw\.hostcalls\.g1: \{type="gauge",value=0}/, + qr/\[info\] .*? pw\.hostcalls\.h1: \{type="histogram",value=\{\[4294967295]=0}}/, +] +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 4: shm_metrics - get(), bad pointer +A call with bad a pointer is simply ignored. + +--- valgrind +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: hostcalls +--- shm_kv: kv1 1m +--- config + location /t { + access_by_lua_block { + local ffi = require "ffi" + local C = ffi.C + + ffi.cdef [[ + typedef unsigned char u_char; + + int ngx_wa_ffi_shm_get_metric(ngx_str_t *k, + u_char *mbuf, size_t mbs, + u_char *hbuf, size_t hbs); + ]] + + C.ngx_wa_ffi_shm_get_metric(nil, nil, 0, nil, 0) + } + + echo ok; + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] +[stub] +[stub] +[stub]