Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions include/proxy/PluginThreadContext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/** @file

Per-plugin identity carried on the continuations a plugin creates.

@section license License

Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#pragma once

#include <string>
#include <string_view>

#include "tscore/Ptr.h"
#include "tsutil/Metrics.h"

/** Carries a plugin's identity on the continuations it creates so that
* proxy.process.plugin.<name>.* workload counters can be attributed back to the originating
* plugin DSO.
*
* This lives in ts::proxy rather than ts::http_remap because it is shared by both remap plugins
* (PluginDso) and global plugins (GlobalPluginContext, in Plugin.cc). The library dependency only
* runs ts::http_remap -> ts::proxy, so putting it here lets both paths resolve these symbols. */
class PluginThreadContext : public RefCountObjInHeap
{
public:
virtual void acquire() = 0;
virtual void release() = 0;

/** Register this plugin's proxy.process.plugin.<name>.* metrics. @a plugin_name is the DSO path;
* only its basename stem (extension removed) is used as <name>. */
void registerPluginMetrics(std::string_view plugin_name);

void countInvocation();

ts::Metrics::Counter::AtomicType *_invocations = nullptr;
ts::Metrics::Counter::AtomicType *_bytes = nullptr;
ts::Metrics::Counter::AtomicType *_transfers = nullptr;

static constexpr const char *const _tag = "plugin_context"; /** @brief log tag used by this class */

private:
/** Derive a metric-safe token from a plugin path: the basename with the extension removed, then any
* character outside [A-Za-z0-9_-] replaced by '_' (e.g. "/.../header_rewrite.so" -> "header_rewrite"). */
static std::string _metric_token(std::string_view name);
};
6 changes: 6 additions & 0 deletions include/proxy/PluginVC.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "proxy/Plugin.h"
#include "iocore/net/NetVConnection.h"
#include "tscore/ink_atomic.h"
#include "tsutil/Metrics.h"

class PluginVCCore;

Expand Down Expand Up @@ -253,6 +254,11 @@ class PluginVCCore : public Continuation
Continuation *connect_to = nullptr;
bool connected = false;

// Transport counters of the plugin that created this intercept, captured at alloc(). Registry-owned
// (process-lifetime), so safe to hold raw. Null for core-internal PluginVCs.
ts::Metrics::Counter::AtomicType *_bytes = nullptr;
ts::Metrics::Counter::AtomicType *_transfers = nullptr;

IpEndpoint passive_addr_struct;
IpEndpoint active_addr_struct;

Expand Down
11 changes: 4 additions & 7 deletions include/proxy/http/remap/PluginDso.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,14 @@
namespace fs = swoc::file;

#include "tscore/Ptr.h"
#include "tsutil/Metrics.h"
#include "iocore/eventsystem/EventSystem.h"

#include "proxy/Plugin.h"
#include "proxy/PluginThreadContext.h"

class PluginThreadContext : public RefCountObjInHeap
{
public:
virtual void acquire() = 0;
virtual void release() = 0;
static constexpr const char *const _tag = "plugin_context"; /** @brief log tag used by this class */
};
#include <string>
#include <string_view>

class PluginDso : public PluginThreadContext
{
Expand Down
9 changes: 7 additions & 2 deletions src/api/InkContInternal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,13 @@ INKContInternal::handle_event(int event, void *edata)
/* set the plugin context */
auto *previousContext = pluginThreadContext;
pluginThreadContext = reinterpret_cast<PluginThreadContext *>(m_context);
int retval = m_event_func((TSCont)this, (TSEvent)event, edata);
pluginThreadContext = previousContext;
// Every TSCont (continuation) callback dispatch flows through here; count it against the owning
// plugin. (Remap doRemap dispatch does not, and is counted in RemapPlugins::run_plugin instead.)
if (pluginThreadContext != nullptr) {
pluginThreadContext->countInvocation();
}
int retval = m_event_func((TSCont)this, (TSEvent)event, edata);
pluginThreadContext = previousContext;
if (edata && event == EVENT_INTERVAL) {
Event *e = reinterpret_cast<Event *>(edata);
if (e->period != 0) {
Expand Down
1 change: 1 addition & 0 deletions src/proxy/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ add_library(
ParentSelectionStrategy.cc
ParentSelection.cc
Plugin.cc
PluginThreadContext.cc
PluginVC.cc
ProtocolProbeSessionAccept.cc
ProxySession.cc
Expand Down
38 changes: 38 additions & 0 deletions src/proxy/Plugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@
#include <algorithm>
#include <filesystem>
#include <optional>
#include <string_view>
#include <vector>
#include "tscore/ink_platform.h"
#include "tscore/ink_file.h"
#include "tscore/ParseRules.h"
#include "records/RecCore.h"
#include "tscore/Layout.h"
#include "proxy/Plugin.h"
#include "proxy/http/remap/RemapPluginInfo.h"
#include "tscore/ink_cap.h"
#include "tscore/Filenames.h"
#include <yaml-cpp/yaml.h>
Expand Down Expand Up @@ -94,6 +97,31 @@ plugin_dir_init()

using init_func_t = void (*)(int, char **);

namespace
{
/** Plugin context for global plugins, which load via raw dlopen() rather than the
* PluginFactory/PluginDso path and so would otherwise have no PluginThreadContext to carry their
* identity. Installed as the thread-local pluginThreadContext around TSPluginInit so the plugin's
* continuations are stamped with it. Global plugins are never unloaded, so acquire()/release() are
* no-ops and instances live for the process lifetime. */
class GlobalPluginContext : public PluginThreadContext
{
public:
explicit GlobalPluginContext(std::string_view name) { registerPluginMetrics(name); }
void
acquire() override
{
}
void
release() override
{
}
};

// Keeps global-plugin contexts reachable for the process lifetime; mutated single-threaded at startup.
std::vector<GlobalPluginContext *> g_global_plugin_contexts;
} // namespace

static PluginLoadSummary s_plugin_load_summary;

const PluginLoadSummary &
Expand Down Expand Up @@ -215,7 +243,17 @@ single_plugin_init(int argc, char *argv[], bool validateOnly)
#endif
opterr = 0;
optarg = nullptr;

// Install this plugin's context around TSPluginInit so the continuations it creates carry its
// identity (see GlobalPluginContext).
auto *global_context = new GlobalPluginContext(path);
g_global_plugin_contexts.push_back(global_context);
auto *prev_plugin_context = pluginThreadContext;
pluginThreadContext = global_context;

init(argc, argv);

pluginThreadContext = prev_plugin_context;
Comment thread
moonchen marked this conversation as resolved.
} // done elevating access

if (plugin_reg_current->plugin_registered) {
Expand Down
65 changes: 65 additions & 0 deletions src/proxy/PluginThreadContext.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/** @file

Per-plugin identity carried on the continuations a plugin creates.

@section license License

Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "proxy/PluginThreadContext.h"

#include <algorithm>
#include <cctype>
#include <string>
#include <string_view>

void
PluginThreadContext::registerPluginMetrics(std::string_view plugin_name)
{
std::string prefix = "proxy.process.plugin." + _metric_token(plugin_name) + ".";

_invocations = ts::Metrics::Counter::createPtr(prefix + "invocations");
_bytes = ts::Metrics::Counter::createPtr(prefix + "bytes");
_transfers = ts::Metrics::Counter::createPtr(prefix + "transfers");
}

void
PluginThreadContext::countInvocation()
{
if (_invocations != nullptr) {
_invocations->increment(1);
}
}

std::string
PluginThreadContext::_metric_token(std::string_view name)
{
if (auto slash = name.find_last_of('/'); slash != std::string_view::npos) {
name.remove_prefix(slash + 1);
}
if (auto dot = name.find_last_of('.'); dot != std::string_view::npos) {
name = name.substr(0, dot);
}

std::string token{name};
std::replace_if(token.begin(), token.end(), [](unsigned char c) { return !(std::isalnum(c) || c == '_' || c == '-'); }, '_');
if (token.empty()) {
token = "unknown";
}
return token;
}
16 changes: 16 additions & 0 deletions src/proxy/PluginVC.cc
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
****************************************************************************/

#include "proxy/PluginVC.h"
#include "proxy/http/remap/RemapPluginInfo.h"
#include "../iocore/net/P_Net.h"
#include "tscore/Regression.h"
#if TS_HAS_TESTS
Expand Down Expand Up @@ -466,6 +467,11 @@ PluginVC::transfer_bytes(MIOBuffer *transfer_to, IOBufferReader *transfer_from,
total_added += moved;
}

// Attribute the bytes moved across this intercept to the owning plugin.
if (core_obj->_bytes != nullptr && total_added > 0) {
core_obj->_bytes->increment(total_added);
}

return total_added;
}

Expand Down Expand Up @@ -564,6 +570,11 @@ PluginVC::process_write_side()
return;
}

// Count a write-side pass for the owning plugin only when data actually moved to the peer.
if (core_obj->_transfers != nullptr && added > 0) {
core_obj->_transfers->increment(1);
}

write_state.vio.ndone += added;
other_side->read_state.vio.ndone += added;

Expand Down Expand Up @@ -1002,6 +1013,11 @@ PluginVCCore::alloc(Continuation *acceptor, int64_t buffer_index, int64_t buffer
PluginVCCore *pvc = new PluginVCCore;
pvc->init(buffer_index, buffer_water_mark);
pvc->connect_to = acceptor;
// Capture the creating plugin's transport counters (registry-owned) for per-plugin accounting.
if (pluginThreadContext != nullptr) {
pvc->_bytes = pluginThreadContext->_bytes;
pvc->_transfers = pluginThreadContext->_transfers;
}
return pvc;
}

Expand Down
7 changes: 7 additions & 0 deletions src/proxy/http/remap/PluginDso.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include "proxy/http/remap/PluginDso.h"
#include "iocore/eventsystem/Freer.h"
#include "tsutil/Metrics.h"
#ifdef PLUGIN_DSO_TESTS
#include "unit-tests/plugin_testing_common.h"
#else
Expand All @@ -38,6 +39,8 @@
#endif

#include <cstdlib>
#include <string>
#include <string_view>
#include <utility>

namespace
Expand Down Expand Up @@ -139,6 +142,10 @@ PluginDso::load(std::string &error, const fs::path &compilerPath)
}
PluginDbg(_dbg_ctl(), "plugin '%s' finished loading DSO", _configPath.c_str());

if (result) {
registerPluginMetrics(_effectivePath.string());
}

return result;
}

Expand Down
2 changes: 2 additions & 0 deletions src/proxy/http/remap/RemapPlugins.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ RemapPlugins::run_plugin(RemapPluginInst *plugin)
_s->os_response_plugin_inst = plugin;
}

plugin->_plugin.countInvocation();

HttpTransact::milestone_start_api_time(_s);
plugin_retcode = plugin->doRemap(reinterpret_cast<TSHttpTxn>(_s->state_machine), &rri);
HttpTransact::milestone_update_api_time(_s);
Expand Down
2 changes: 0 additions & 2 deletions src/proxy/unit_tests/stub.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,3 @@
*/

#include "proxy/IPAllow.h"

uint8_t IpAllow::subjects[IpAllow::Subject::MAX_SUBJECTS];
3 changes: 2 additions & 1 deletion tests/gold_tests/pluginTest/lua/metrics.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ N=60
while (( N > 0 ))
do
rm -f metrics.out metrics.txt
traffic_ctl metric match lua > metrics.out
# Anchor to this plugin's own metrics; exclude the proxy.process.plugin.* workload counters.
traffic_ctl metric match '^plugin\.lua\.' > metrics.out
sleep 1
sed 's/ [0-9][0-9]*//' metrics.out > metrics.txt
if diff metrics.txt ${AUTEST_TEST_DIR}/gold/metrics.gold
Expand Down
Loading