#endif
}
+ScopedId
+AccessLogEntry::codeContextGist() const
+{
+ if (request) {
+ if (const auto &mx = request->masterXaction)
+ return mx->id.detach();
+ }
+ // TODO: Carefully merge ALE and MasterXaction.
+ return ScopedId("ALE w/o master");
+}
+
+std::ostream &
+AccessLogEntry::detailCodeContext(std::ostream &os) const
+{
+ // TODO: Consider printing all instead of the first most important detail.
+
+ if (request) {
+ if (const auto &mx = request->masterXaction)
+ return os << Debug::Extra << "current master transaction: " << mx->id;
+ }
+
+ // provide helpful details since we cannot identify the transaction exactly
+
+ if (tcpClient)
+ return os << Debug::Extra << "current from-client connection: " << tcpClient;
+ else if (!cache.caddr.isNoAddr())
+ return os << Debug::Extra << "current client: " << cache.caddr;
+
+ const auto optionalMethod = [this,&os]() {
+ if (hasLogMethod())
+ os << getLogMethod() << ' ';
+ return "";
+ };
+ if (const auto uri = effectiveVirginUrl())
+ return os << Debug::Extra << "current client request: " << optionalMethod() << *uri;
+ else if (!url.isEmpty())
+ return os << Debug::Extra << "current request: " << optionalMethod() << url;
+ else if (hasLogMethod())
+ return os << Debug::Extra << "current request method: " << getLogMethod();
+
+ return os;
+}
+
const SBuf *
AccessLogEntry::effectiveVirginUrl() const
{
#define SQUID_HTTPACCESSLOGENTRY_H
#include "anyp/PortCfg.h"
-#include "base/RefCount.h"
+#include "base/CodeContext.h"
#include "comm/Connection.h"
#include "HierarchyLogEntry.h"
#include "http/ProtocolVersion.h"
class HttpRequest;
class CustomLog;
-class AccessLogEntry: public RefCountable
+class AccessLogEntry: public CodeContext
{
public:
typedef RefCount<AccessLogEntry> Pointer;
AccessLogEntry();
- ~AccessLogEntry();
+ virtual ~AccessLogEntry();
+
+ /* CodeContext API */
+ virtual std::ostream &detailCodeContext(std::ostream &os) const override;
+ virtual ScopedId codeContextGist() const override;
/// Fetch the client IP log string into the given buffer.
/// Knows about several alternate locations of the IP
/// configures the active debugging context to write syslog ALERT
static void ForceAlert();
+
+ /// prefixes each grouped debugs() line after the first one in the group
+ static std::ostream& Extra(std::ostream &os) { return os << "\n "; }
+
private:
static Context *Current; ///< deepest active context; nil outside debugs()
};
/* DEBUG: section 47 Store Directory Routines */
#include "squid.h"
+#include "base/CodeContext.h"
#include "base/RunnersRegistry.h"
#include "base/TextException.h"
#include "DiskIO/IORequestor.h"
{
// prevent queue overflows: check for responses to earlier requests
// warning: this call may result in indirect push() recursion
- HandleResponses("before push");
+ CallService(nullptr, [] {
+ HandleResponses("before push");
+ });
debugs(47, 7, HERE);
Must(diskId >= 0);
IpcIoFile::handleResponse(IpcIoMsg &ipcIo)
{
const int requestId = ipcIo.requestId;
- debugs(47, 7, HERE << "popped disker response: " <<
- SipcIo(KidIdentifier, ipcIo, diskId));
Must(requestId);
if (IpcIoPendingRequest *const pending = dequeueRequest(requestId)) {
- pending->completeIo(&ipcIo);
- delete pending; // XXX: leaking if throwing
+ CallBack(pending->codeContext, [&] {
+ debugs(47, 7, "popped disker response to " << SipcIo(KidIdentifier, ipcIo, diskId));
+ pending->completeIo(&ipcIo);
+ delete pending; // XXX: leaking if throwing
+ });
} else {
- debugs(47, 4, HERE << "LATE disker response to " << ipcIo.command <<
- "; ipcIo" << KidIdentifier << '.' << requestId);
+ debugs(47, 4, "LATE disker response to " << SipcIo(KidIdentifier, ipcIo, diskId));
// nothing we can do about it; completeIo() has been called already
}
}
typedef RequestMap::const_iterator RMCI;
for (RMCI i = olderRequests->begin(); i != olderRequests->end(); ++i) {
IpcIoPendingRequest *const pending = i->second;
-
- const unsigned int requestId = i->first;
- debugs(47, 7, HERE << "disker timeout; ipcIo" <<
- KidIdentifier << '.' << requestId);
-
- pending->completeIo(NULL); // no response
- delete pending; // XXX: leaking if throwing
+ CallBack(pending->codeContext, [&] {
+ const auto requestId = i->first;
+ debugs(47, 7, "disker timeout; ipcIo" << KidIdentifier << '.' << requestId);
+ pending->completeIo(nullptr); // no response
+ delete pending; // XXX: leaking if throwing
+ });
}
olderRequests->clear();
void
IpcIoFile::scheduleTimeoutCheck()
{
- // we check all older requests at once so some may be wait for 2*Timeout
- eventAdd("IpcIoFile::CheckTimeouts", &IpcIoFile::CheckTimeouts,
- reinterpret_cast<void *>(diskId), Timeout, 0, false);
- timeoutCheckScheduled = true;
+ // We may be running in an I/O requestor CodeContext, but are scheduling
+ // one-for-all CheckTimeouts() that is not specific to any request.
+ CallService(nullptr, [&] {
+ // we check all older requests at once so some may be wait for 2*Timeout
+ eventAdd("IpcIoFile::CheckTimeouts", &IpcIoFile::CheckTimeouts,
+ reinterpret_cast<void *>(diskId), Timeout, 0, false);
+ timeoutCheckScheduled = true;
+ });
}
/// returns and forgets the right IpcIoFile pending request
/* IpcIoPendingRequest */
IpcIoPendingRequest::IpcIoPendingRequest(const IpcIoFile::Pointer &aFile):
- file(aFile), readRequest(NULL), writeRequest(NULL)
+ file(aFile),
+ readRequest(nullptr),
+ writeRequest(nullptr),
+ codeContext(CodeContext::Current())
{
}
ReadRequest *readRequest; ///< set if this is a read requests
WriteRequest *writeRequest; ///< set if this is a write request
+ CodeContext::Pointer codeContext; ///< requestor's context
+
private:
IpcIoPendingRequest(const IpcIoPendingRequest &d); // not implemented
IpcIoPendingRequest &operator =(const IpcIoPendingRequest &d); // ditto
return b;
}
+ScopedId
+AnyP::PortCfg::codeContextGist() const
+{
+ // Unfortunately, .name lifetime is too short in FTP use cases.
+ // TODO: Consider adding InstanceId<uint32_t> to all RefCountable classes.
+ return ScopedId("port");
+}
+
+std::ostream &
+AnyP::PortCfg::detailCodeContext(std::ostream &os) const
+{
+ // parsePortSpecification() defaults optional port name to the required
+ // listening address so we cannot easily distinguish one from the other.
+ if (name)
+ os << Debug::Extra << "listening port: " << name;
+ else if (s.port())
+ os << Debug::Extra << "listening port address: " << s;
+ return os;
+}
+
#include "anyp/forward.h"
#include "anyp/ProtocolVersion.h"
#include "anyp/TrafficMode.h"
+#include "base/CodeContext.h"
#include "comm/Connection.h"
#include "sbuf/SBuf.h"
#include "security/ServerOptions.h"
namespace AnyP
{
-class PortCfg : public RefCountable
+class PortCfg : public CodeContext
{
public:
PortCfg();
~PortCfg();
AnyP::PortCfgPointer clone() const;
+ /* CodeContext API */
+ virtual ScopedId codeContextGist() const override;
+ virtual std::ostream &detailCodeContext(std::ostream &os) const override;
+
PortCfgPointer next;
Ip::Address s;
*/
#include "squid.h"
-#include "AsyncCall.h"
#include "base/AsyncCall.h"
#include "base/AsyncCallQueue.h"
+#include "base/CodeContext.h"
#include "cbdata.h"
#include "Debug.h"
#include <ostream>
/* AsyncCall */
-AsyncCall::AsyncCall(int aDebugSection, int aDebugLevel,
- const char *aName): name(aName), debugSection(aDebugSection),
- debugLevel(aDebugLevel), theNext(0), isCanceled(NULL)
+AsyncCall::AsyncCall(int aDebugSection, int aDebugLevel, const char *aName):
+ name(aName),
+ codeContext(CodeContext::Current()),
+ debugSection(aDebugSection),
+ debugLevel(aDebugLevel),
+ theNext(nullptr),
+ isCanceled(nullptr)
{
debugs(debugSection, debugLevel, "The AsyncCall " << name << " constructed, this=" << this <<
" [" << id << ']');
{
debugs(call->debugSection, call->debugLevel, fileName << "(" << fileLine <<
") will call " << *call << " [" << call->id << ']' );
+
+ // Support callback creators that did not get their context from service A,
+ // but the current caller (service B) got that context from another source.
+ if (!call->codeContext)
+ call->codeContext = CodeContext::Current();
+
AsyncCallQueue::Instance().schedule(call);
return true;
}
#ifndef SQUID_ASYNCCALL_H
#define SQUID_ASYNCCALL_H
+#include "base/CodeContext.h"
#include "base/InstanceId.h"
#include "event.h"
#include "RefCount.h"
public:
const char *const name;
+
+ /// what the callee is expected to work on
+ CodeContext::Pointer codeContext;
+
const int debugSection;
const int debugLevel;
const InstanceId<AsyncCall> id;
AsyncCallQueue::fire()
{
const bool made = theHead != NULL;
- while (theHead != NULL)
+ while (theHead) {
+ CodeContext::Reset(theHead->codeContext);
fireNext();
+ }
+ if (made)
+ CodeContext::Reset();
return made;
}
--- /dev/null
+/*
+ * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS code_contexts for details.
+ */
+
+#include "squid.h"
+#include "base/CodeContext.h"
+#include "Debug.h"
+
+/// represents a being-forgotten CodeContext (while it may be being destroyed)
+class FadingCodeContext: public CodeContext
+{
+public:
+ /* CodeContext API */
+ virtual ScopedId codeContextGist() const override { return gist; }
+ virtual std::ostream &detailCodeContext(std::ostream &os) const override { return os << gist; }
+
+ ScopedId gist; ///< identifies the being-forgotten CodeContext
+};
+
+/// guarantees the forever existence of the pointer, starting from the first use
+static CodeContext::Pointer &
+Instance()
+{
+ static const auto Instance = new CodeContext::Pointer(nullptr);
+ return *Instance;
+}
+
+const CodeContext::Pointer &
+CodeContext::Current()
+{
+ return Instance();
+}
+
+/// Forgets the current known context, possibly triggering its destruction.
+/// Preserves the gist of the being-forgotten context during its destruction.
+/// Knows nothing about the next context -- the caller must set it.
+void
+CodeContext::ForgetCurrent()
+{
+ static const RefCount<FadingCodeContext> fadingCodeContext = new FadingCodeContext();
+ auto ¤t = Instance();
+ assert(current);
+ fadingCodeContext->gist = current->codeContextGist();
+ current = fadingCodeContext;
+}
+
+/// Switches the current context to the given known context. Improves debugging
+/// output by replacing omni-directional "Reset" with directional "Entering".
+void
+CodeContext::Entering(const Pointer &codeCtx)
+{
+ auto ¤t = Instance();
+ if (current)
+ ForgetCurrent(); // ensure orderly closure of the old context
+ current = codeCtx;
+ debugs(1, 5, codeCtx->codeContextGist());
+}
+
+/// Forgets the current known context. Improves debugging output by replacing
+/// omni-directional "Reset" with directional "Leaving".
+void
+CodeContext::Leaving()
+{
+ ForgetCurrent();
+ auto ¤t = Instance();
+ debugs(1, 7, *current);
+ current = nullptr;
+}
+
+void
+CodeContext::Reset()
+{
+ if (Instance())
+ Leaving();
+}
+
+void
+CodeContext::Reset(const Pointer codeCtx)
+{
+ if (codeCtx == Current())
+ return; // context has not actually changed
+
+ if (!codeCtx)
+ return Leaving();
+
+ Entering(codeCtx);
+}
+
+std::ostream &
+CurrentCodeContextDetail(std::ostream &os)
+{
+ if (const auto ctx = CodeContext::Current())
+ ctx->detailCodeContext(os);
+ return os;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS code_contexts for details.
+ */
+
+#ifndef SQUID_BASE_CODE_CONTEXT_H
+#define SQUID_BASE_CODE_CONTEXT_H
+
+#include "base/InstanceId.h"
+#include "base/RefCount.h"
+
+#include <iosfwd>
+
+/// Interface for reporting what Squid code is working on.
+/// Such reports are usually requested outside creator's call stack.
+/// They are especially useful for attributing low-level errors to transactions.
+class CodeContext: public RefCountable
+{
+public:
+ typedef RefCount<CodeContext> Pointer;
+
+ /// \returns the known global context or, to indicate unknown context, nil
+ static const Pointer &Current();
+
+ /// forgets the current context, setting it to nil/unknown
+ static void Reset();
+
+ /// changes the current context; nil argument sets it to nil/unknown
+ static void Reset(const Pointer);
+
+ virtual ~CodeContext() {}
+
+ /// \returns a small, permanent ID of the current context
+ /// gists persist forever and are suitable for passing to other SMP workers
+ virtual ScopedId codeContextGist() const = 0;
+
+ /// appends human-friendly context description line(s) to a cache.log record
+ virtual std::ostream &detailCodeContext(std::ostream &os) const = 0;
+
+private:
+ static void ForgetCurrent();
+ static void Entering(const Pointer &codeCtx);
+ static void Leaving();
+};
+
+/// by default, only small context gist is printed
+inline
+std::ostream &operator <<(std::ostream &os, const CodeContext &ctx)
+{
+ return os << ctx.codeContextGist();
+}
+
+/* convenience context-reporting wrappers that also reduce linking problems */
+std::ostream &CurrentCodeContextBrief(std::ostream &os);
+std::ostream &CurrentCodeContextDetail(std::ostream &os);
+
+/// Convenience class that automatically restores the current/outer CodeContext
+/// when leaving the scope of the new-context following/inner code. \see Run().
+class CodeContextGuard
+{
+public:
+ CodeContextGuard(const CodeContext::Pointer &newContext): savedCodeContext(CodeContext::Current()) { CodeContext::Reset(newContext); }
+ ~CodeContextGuard() { CodeContext::Reset(savedCodeContext); }
+
+ // no copying of any kind (for simplicity and to prevent accidental copies)
+ CodeContextGuard(CodeContextGuard &&) = delete;
+
+ CodeContext::Pointer savedCodeContext;
+};
+
+/// Executes service `callback` in `callbackContext`. If an exception occurs,
+/// the callback context is preserved, so that the exception is associated with
+/// the callback that triggered them (rather than with the service).
+///
+/// Service code running in its own service context should use this function.
+template <typename Fun>
+inline void
+CallBack(const CodeContext::Pointer &callbackContext, Fun &&callback)
+{
+ // TODO: Consider catching exceptions and letting CodeContext handle them.
+ const auto savedCodeContext(CodeContext::Current());
+ CodeContext::Reset(callbackContext);
+ callback();
+ CodeContext::Reset(savedCodeContext);
+}
+
+/// Executes `service` in `serviceContext` but due to automatic caller context
+/// restoration, service exceptions are associated with the caller that suffered
+/// from (and/or caused) them (rather than with the service itself).
+///
+/// Service code running in caller's context should use this function to escape
+/// into service context (e.g., for submitting caller-agnostic requests).
+template <typename Fun>
+inline void
+CallService(const CodeContext::Pointer &serviceContext, Fun &&service)
+{
+ // TODO: Consider catching exceptions and letting CodeContext handle them.
+ CodeContextGuard guard(serviceContext);
+ service();
+}
+
+/// Executes context `creator` in the service context. If an exception occurs,
+/// the creator context is preserved, so that the exception is associated with
+/// the creator that triggered them (rather than with the service).
+///
+/// Service code running in its own context should use this function to create
+/// new code contexts. TODO: Use or, if this pattern is not repeated, remove.
+template <typename Fun>
+inline void
+CallContextCreator(Fun &&creator)
+{
+ const auto savedCodeContext(CodeContext::Current());
+ creator();
+ CodeContext::Reset(savedCodeContext);
+}
+
+#endif
+
#include <iosfwd>
+/// Represents an InstanceId<C> value independent from its owner class C. These
+/// "detached" IDs can be stored by and exchanged among C-unaware users at the
+/// price of storing a short scope c-string (that InstanceIds hard-code instead)
+/// and, in some cases, using more bits/space than InstanceId<C>::value uses.
+class ScopedId
+{
+public:
+ ScopedId(): scope(nullptr), value(0) {}
+ explicit ScopedId(const char *s): scope(s), value(0) {}
+ // when the values is zero/unknown, use other constructors
+ ScopedId(const char *s, uint64_t v): scope(s), value(v) { /* assert(value) */ }
+
+ /// either the prefix() of the InstanceId object that we were detached from
+ /// or, for 0 values, some other description (with endless lifetime) or nil
+ const char *scope;
+
+ /// either the value of the InstanceId object that we were detached from
+ /// or, if our creator did not know the exact value, zero
+ uint64_t value;
+};
+
+inline std::ostream&
+operator <<(std::ostream &os, const ScopedId &id)
+{
+ if (id.value)
+ os << id.scope << id.value;
+ else if (id.scope)
+ os << id.scope;
+ else
+ os << "[unknown]";
+ return os;
+}
+
typedef unsigned int InstanceIdDefaultValueType;
/** Identifier for class instances
* - unique IDs for a large number of concurrent instances, but may wrap;
bool operator !=(const InstanceId &o) const { return !(*this == o); }
void change();
- /// prints class-pecific prefix followed by ID value; \todo: use HEX for value printing?
+ /// writes a compact text representation of the ID
std::ostream &print(std::ostream &) const;
- /// returns the class-pecific prefix
+ // TODO: Refactor into static Scope().
+ /// \returns Class-specific nickname (with endless lifetime)
const char * prefix() const;
+ /// \returns a copy of the ID usable outside our Class context
+ ScopedId detach() const { return ScopedId(prefix(), value); }
+
public:
Value value = Value(); ///< instance identifier
CbDataList.h \
CharacterSet.cc \
CharacterSet.h \
+ CodeContext.cc \
+ CodeContext.h \
EnumIterator.h \
File.cc \
File.h \
p.p_=NULL;
}
+ /// Base::Pointer = Derived::Pointer
+ template <class Other>
+ RefCount(const RefCount<Other> &p): p_(p.getRaw()) {
+ reference(*this);
+ }
+
RefCount& operator = (const RefCount& p) {
// DO NOT CHANGE THE ORDER HERE!!!
// This preserves semantics on self assignment
public:
/// Must be passed an object. nil pointers are not permitted.
explicit CallSubscription(const RefCount<Call_> &aCall) : call(aCall) { assert(aCall != NULL); }
- virtual AsyncCall::Pointer callback() const { return new Call_(*call); }
+ virtual AsyncCall::Pointer callback() const
+ {
+ const AsyncCall::Pointer cb = new Call_(*call);
+ if (!cb->codeContext || CodeContext::Current())
+ cb->codeContext = CodeContext::Current();
+ return cb;
+ }
private:
const RefCount<Call_> call; ///< gets copied to create callback calls
std::ostream &
TextException::print(std::ostream &os) const
{
- os << std::runtime_error::what() << "\n" <<
- " exception location: " << where << "\n";
- // TODO: error_detail: " << (ERR_DETAIL_EXCEPTION_START+id()) << "\n";
+ os << std::runtime_error::what() <<
+ Debug::Extra << "exception location: " << where;
+ // TODO: ...error_detail: " << (ERR_DETAIL_EXCEPTION_START+id());
return os;
}
try { \
code \
} catch (...) { \
- debugs(0, DBG_IMPORTANT, "BUG: ignoring exception;\n" << \
- " bug location: " << Here() << "\n" << \
- " ignored exception: " << CurrentException); \
+ debugs(0, DBG_IMPORTANT, "BUG: ignoring exception;" << \
+ Debug::Extra << "bug location: " << Here() << \
+ Debug::Extra << "ignored exception: " << CurrentException); \
}
#endif /* SQUID__TEXTEXCEPTION_H */
#ifndef SQUID_SRC_BASE_FORWARD_H
#define SQUID_SRC_BASE_FORWARD_H
+class AsyncCallQueue;
+class AsyncJob;
+class CallDialer;
+class CodeContext;
+class ScopedId;
+
template<class Cbc> class CbcPointer;
+template<class RefCountableKid> class RefCount;
-class AsyncJob;
typedef CbcPointer<AsyncJob> AsyncJobPointer;
+typedef RefCount<CodeContext> CodeContextPointer;
#endif /* SQUID_SRC_BASE_FORWARD_H */
MasterXaction::Pointer mx = new MasterXaction(XactionInitiator::initClient);
mx->tcpClient = clientConnection;
- // Create a fake HTTP request for ssl_bump ACL check,
+ // Create a fake HTTP request and ALE for the ssl_bump ACL check,
// using tproxy/intercept provided destination IP and port.
+ // XXX: Merge with subsequent fakeAConnectRequest(), buildFakeRequest().
+ // XXX: Do this earlier (e.g., in Http[s]::One::Server constructor).
HttpRequest *request = new HttpRequest(mx);
static char ip[MAX_IPSTRLEN];
assert(clientConnection->flags & (COMM_TRANSPARENT | COMM_INTERCEPTION));
request->url.host(clientConnection->local.toStr(ip, sizeof(ip)));
request->url.port(clientConnection->local.port());
request->myportname = port->name;
+ const AccessLogEntry::Pointer connectAle = new AccessLogEntry;
+ CodeContext::Reset(connectAle);
+ // TODO: Use these request/ALE when waiting for new bumped transactions.
ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, request, NULL);
acl_checklist->src_addr = clientConnection->remote;
acl_checklist->my_addr = port->s;
// Build a local AccessLogEntry to allow requiresAle() acls work
- acl_checklist->al = new AccessLogEntry;
+ acl_checklist->al = connectAle;
acl_checklist->al->cache.start_time = current_time;
acl_checklist->al->tcpClient = clientConnection;
acl_checklist->al->cache.port = port;
const SBuf &scheme = AnyP::UriScheme(s->transport.protocol).image();
if (MAXTCPLISTENPORTS == NHttpSockets) {
- debugs(1, DBG_IMPORTANT, "WARNING: You have too many '" << scheme << "_port' lines.");
- debugs(1, DBG_IMPORTANT, " The limit is " << MAXTCPLISTENPORTS << " HTTP ports.");
+ debugs(1, DBG_IMPORTANT, "WARNING: You have too many '" << scheme << "_port' lines." <<
+ Debug::Extra << "The limit is " << MAXTCPLISTENPORTS << " HTTP ports.");
continue;
}
{
setConn(aConn);
al = new AccessLogEntry;
+ CodeContext::Reset(al);
al->cache.start_time = current_time;
if (aConn) {
al->tcpClient = clientConnection = aConn->clientConnection;
}
}
+// XXX: This code has been broken, unused, and untested since 933dd09. Remove.
#if LINGERING_CLOSE
static void
commLingerClose(int fd, void *unused)
++ statCounter.syscalls.sock.closes;
/* When one connection closes, give accept() a chance, if need be */
+ CodeContext::Reset(); // exit FD-specific context
Comm::AcceptLimiter::Instance().kick();
}
F->flags.close_request = true;
+ // We have caller's context and fde::codeContext. In the unlikely event they
+ // differ, it is not clear which context is more applicable to this closure.
+ // For simplicity sake, we remain in the caller's context while still
+ // allowing individual advanced callbacks to overwrite it.
+
if (F->ssl) {
AsyncCall::Pointer startCall=commCbCall(5,4, "commStartTlsClose",
FdeCbPtrFun(commStartTlsClose, nullptr));
if (writeTimedOut(fd)) {
// We have an active write callback and we are timed out
+ CodeContext::Reset(F->codeContext);
debugs(5, 5, "checkTimeouts: FD " << fd << " auto write timeout");
Comm::SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
COMMIO_FD_WRITECB(fd)->finish(Comm::COMM_ERROR, ETIMEDOUT);
+ CodeContext::Reset();
#if USE_DELAY_POOLS
} else if (F->writeQuotaHandler != nullptr && COMMIO_FD_WRITECB(fd)->conn != nullptr) {
+ // TODO: Move and extract quota() call to place it inside F->codeContext.
if (!F->writeQuotaHandler->selectWaiting && F->writeQuotaHandler->quota() && !F->closing()) {
+ CodeContext::Reset(F->codeContext);
F->writeQuotaHandler->selectWaiting = true;
Comm::SetSelect(fd, COMM_SELECT_WRITE, Comm::HandleWrite, COMMIO_FD_WRITECB(fd), 0);
+ CodeContext::Reset();
}
continue;
#endif
else if (AlreadyTimedOut(F))
continue;
+ CodeContext::Reset(F->codeContext);
debugs(5, 5, "checkTimeouts: FD " << fd << " Expired");
if (F->timeoutHandler != NULL) {
debugs(5, 5, "checkTimeouts: FD " << fd << ": Forcing comm_close()");
comm_close(fd);
}
+
+ CodeContext::Reset();
}
}
#include "SquidTime.h"
#include <ostream>
+InstanceIdDefinitions(Comm::Connection, "conn");
+
class CachePeer;
bool
Comm::IsConnOpen(const Comm::ConnectionPointer &conn)
return min(ctimeout, ftimeout);
}
+ScopedId
+Comm::Connection::codeContextGist() const {
+ return id.detach();
+}
+
+std::ostream &
+Comm::Connection::detailCodeContext(std::ostream &os) const
+{
+ return os << Debug::Extra << "connection: " << *this;
+}
+
std::ostream &
operator << (std::ostream &os, const Comm::Connection &conn)
{
- os << "local=" << conn.local << " remote=" << conn.remote;
+ os << conn.id;
+ if (!conn.local.isNoAddr() || conn.local.port())
+ os << " local=" << conn.local;
+ if (!conn.remote.isNoAddr() || conn.remote.port())
+ os << " remote=" << conn.remote;
if (conn.peerType)
os << ' ' << hier_code_str[conn.peerType];
if (conn.fd >= 0)
#ifndef _SQUIDCONNECTIONDETAIL_H_
#define _SQUIDCONNECTIONDETAIL_H_
+#include "base/CodeContext.h"
+#include "base/InstanceId.h"
#include "comm/forward.h"
#include "defines.h"
#if USE_SQUID_EUI
* These objects should not be passed around directly,
* but a Comm::ConnectionPointer should be passed instead.
*/
-class Connection : public RefCountable
+class Connection: public CodeContext
{
MEMPROXY_CLASS(Comm::Connection);
Connection();
/** Clear the connection properties and close any open socket. */
- ~Connection();
+ virtual ~Connection();
/** Copy an existing connections IP and properties.
* This excludes the FD. The new copy will be a closed connection.
Security::NegotiationHistory *tlsNegotiations();
const Security::NegotiationHistory *hasTlsNegotiations() const {return tlsHistory;}
+ /* CodeContext API */
+ virtual ScopedId codeContextGist() const override;
+ virtual std::ostream &detailCodeContext(std::ostream &os) const override;
+
private:
/** These objects may not be exactly duplicated. Use copyDetails() instead. */
Connection(const Connection &c);
Eui::Eui64 remoteEui64;
#endif
+ InstanceId<Connection> id;
+
private:
/** cache_peer data object (if any) */
CachePeer *peer_;
#if USE_EPOLL
+#include "base/CodeContext.h"
#include "comm/Loops.h"
#include "fde.h"
#include "globals.h"
if (timeout)
F->timeout = squid_curtime + timeout;
+
+ if (timeout || handler) // all non-cleanup requests
+ F->codeContext = CodeContext::Current(); // TODO: Avoid clearing if set?
+ else if (!ev.events) // full cleanup: no more FD-associated work expected
+ F->codeContext = nullptr;
+ // else: direction-specific/timeout cleanup requests preserve F->codeContext
}
static void commIncomingStats(StoreEntry * sentry);
for (i = 0, cevents = pevents; i < num; ++i, ++cevents) {
fd = cevents->data.fd;
F = &fd_table[fd];
+ CodeContext::Reset(F->codeContext);
debugs(5, DEBUG_EPOLL ? 0 : 8, HERE << "got FD " << fd << " events=" <<
std::hex << cevents->events << " monitoring=" << F->epoll_state <<
" F->read_handler=" << F->read_handler << " F->write_handler=" << F->write_handler);
}
}
+ CodeContext::Reset();
+
PROF_stop(comm_handle_ready_fd);
return Comm::OK;
void
Comm::TcpAcceptor::start()
{
+ if (listenPort_)
+ CodeContext::Reset(listenPort_);
debugs(5, 5, HERE << status() << " AsyncCall Subscription: " << theCallSub);
Must(IsConnOpen(conn));
errcode = errno = 0;
if (listen(conn->fd, Squid_MaxFD >> 2) < 0) {
errcode = errno;
- debugs(50, DBG_CRITICAL, "ERROR: listen(" << status() << ", " << (Squid_MaxFD >> 2) << "): " << xstrerr(errcode));
+ debugs(50, DBG_CRITICAL, "ERROR: listen(..., " << (Squid_MaxFD >> 2) << ") system call failed: " << xstrerr(errcode));
return;
}
return false;
}
-static void
-logAcceptError(const Comm::ConnectionPointer &conn)
+void
+Comm::TcpAcceptor::logAcceptError(const ConnectionPointer &tcpClient) const
{
AccessLogEntry::Pointer al = new AccessLogEntry;
- al->tcpClient = conn;
+ CodeContext::Reset(al);
+ al->tcpClient = tcpClient;
al->url = "error:accept-client-connection";
al->setVirginUrlForMissingRequest(al->url);
ACLFilledChecklist ch(nullptr, nullptr, nullptr);
- ch.src_addr = conn->remote;
- ch.my_addr = conn->local;
+ ch.src_addr = tcpClient->remote;
+ ch.my_addr = tcpClient->local;
ch.al = al;
accessLogLog(al, &ch);
+
+ CodeContext::Reset(listenPort_);
}
void
/* register interest again */
debugs(5, 5, "try later: " << conn << " handler Subscription: " << theCallSub);
} else {
+ // TODO: When ALE, MasterXaction merge, use them or ClientConn instead.
+ CodeContext::Reset(newConnDetails);
debugs(5, 5, "Listener: " << conn <<
" accepted new connection " << newConnDetails <<
" handler Subscription: " << theCallSub);
notify(flag, newConnDetails);
+ CodeContext::Reset(listenPort_);
}
SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
debugs(50, 3, status() << ": " << xstrerr(errcode));
return Comm::COMM_ERROR;
} else {
- debugs(50, DBG_IMPORTANT, MYNAME << status() << ": " << xstrerr(errcode));
+ debugs(50, DBG_IMPORTANT, "ERROR: failed to accept an incoming connection: " << xstrerr(errcode));
return Comm::COMM_ERROR;
}
}
void handleClosure(const CommCloseCbParams &io);
/// whether we are listening on one of the squid.conf *ports
bool intendedForUserConnections() const { return bool(listenPort_); }
+ void logAcceptError(const ConnectionPointer &tcpClient) const;
};
} // namespace Comm
void
Debug::Finish()
{
+ // TODO: #include "base/CodeContext.h" instead if doing so works well.
+ extern std::ostream &CurrentCodeContextDetail(std::ostream &os);
+ if (Current->level <= DBG_IMPORTANT)
+ Current->buf << CurrentCodeContextDetail;
+
// TODO: Optimize to remove at least one extra copy.
_db_print(Current->forceAlert, "%s\n", Current->buf.str().c_str());
Current->forceAlert = false;
#ifndef SQUID_FDE_H
#define SQUID_FDE_H
+#include "base/CodeContext.h" /* XXX: Remove by de-inlining ctor and clear() */
+#include "base/forward.h"
#include "comm.h"
#include "defines.h"
#include "ip/Address.h"
connection, whereas nfmarkToServer is the value to set on packets
*leaving* Squid. */
+ // TODO: Remove: Auto-convert legacy SetSelect() callers to AsyncCalls like
+ // comm_add_close_handler(CLCB) does, making readMethod_/writeMethod_
+ // AsyncCalls and giving each read/write a dedicated context instead.
+ /// What the I/O handlers are supposed to work on.
+ CodeContextPointer codeContext;
+
private:
// I/O methods connect Squid to the device/stack/library fde represents
READ_HANDLER *readMethod_ = nullptr; ///< imports bytes into Squid
Countstr signature;
};
-class htcpSpecifier : public RefCountable, public StoreClient
+class htcpSpecifier: public CodeContext, public StoreClient
{
MEMPROXY_CLASS(htcpSpecifier);
dhdr = aDataHeader;
}
+ /* CodeContext API */
+ virtual ScopedId codeContextGist() const; // override
+ virtual std::ostream &detailCodeContext(std::ostream &os) const; // override
+
/* StoreClient API */
void created(StoreEntry *);
virtual LogTags *loggingTags();
htcpSend(pkt, (int) pktlen, from);
}
+ScopedId
+htcpSpecifier::codeContextGist() const
+{
+ if (al) {
+ const auto gist = al->codeContextGist();
+ if (gist.value)
+ return gist;
+ }
+
+ if (request) {
+ if (const auto &mx = request->masterXaction)
+ return mx->id.detach();
+ }
+
+ return ScopedId("HTCP w/o master");
+}
+
+std::ostream &
+htcpSpecifier::detailCodeContext(std::ostream &os) const
+{
+ if (al)
+ return al->detailCodeContext(os);
+
+ if (request) {
+ if (const auto &mx = request->masterXaction)
+ return os << Debug::Extra << "current master transaction: " << mx->id;
+ }
+
+ // TODO: Report method, uri, and version if they have been set
+ return os;
+}
+
void
htcpSpecifier::checkHit()
{
static void
htcpHandleMsg(char *buf, int sz, Ip::Address &from)
{
+ // TODO: function-scoped CodeContext::Reset(...("HTCP message from", from))
+
htcpHeader htcpHdr;
htcpDataHeader hdr;
char *hbuf;
case STREAM_COMPLETE: {
debugs(33, 5, clientConnection << " Stream complete, keepalive is " <<
http->request->flags.proxyKeepalive);
+ // XXX: This code assumes we are done with the transaction, but we may
+ // still be receiving request body. TODO: Extend stopSending() instead.
ConnStateData *c = getConn();
if (!http->request->flags.proxyKeepalive)
clientConnection->close();
void
Http::Stream::finished()
{
+ CodeContext::Reset(clientConnection);
ConnStateData *conn = getConn();
/* we can't handle any more stream data - detach */
if (thisPeer->http_port != s->s.port())
continue;
- debugs(15, DBG_IMPORTANT, "WARNING: Peer looks like this host");
-
- debugs(15, DBG_IMPORTANT, " Ignoring " <<
+ debugs(15, DBG_IMPORTANT, "WARNING: Peer looks like this host." <<
+ Debug::Extra << "Ignoring " <<
neighborTypeStr(thisPeer) << " " << thisPeer->host <<
"/" << thisPeer->http_port << "/" <<
thisPeer->icp.port);
}
debugs(15, 3, "neighborsHtcpReply: e = " << e);
+ // TODO: Refactor (ping_reply_callback,ircb_data) to add CodeContext.
mem->ping_reply_callback(p, ntype, AnyP::PROTO_HTCP, htcp, mem->ircb_data);
}
void Comm::Connection::close() STUB
CachePeer * Comm::Connection::getPeer() const STUB_RETVAL(NULL)
void Comm::Connection::setPeer(CachePeer * p) STUB
+ScopedId Comm::Connection::codeContextGist() const STUB_RETVAL(id.detach())
+std::ostream &Comm::Connection::detailCodeContext(std::ostream &os) const STUB_RETVAL(os)
+InstanceIdDefinitions(Comm::Connection, "conn");
#include "comm/ConnOpener.h"
CBDATA_NAMESPACED_CLASS_INIT(Comm, ConnOpener);