static const RefCount<FadingCodeContext> fadingCodeContext = new FadingCodeContext();
auto ¤t = Instance();
assert(current);
+ current->busyTime.pause();
fadingCodeContext->gist = current->codeContextGist();
current = fadingCodeContext;
}
if (current)
ForgetCurrent(); // ensure orderly closure of the old context
current = codeCtx;
+ codeCtx->busyTime.resume();
debugs(1, 5, codeCtx->codeContextGist());
}
#include "base/InstanceId.h"
#include "base/RefCount.h"
+#include "base/Stopwatch.h"
#include <iosfwd>
/// appends human-friendly context description line(s) to a cache.log record
virtual std::ostream &detailCodeContext(std::ostream &os) const = 0;
+ /// time spent in this context (see also: %busy_time)
+ Stopwatch busyTime;
+
private:
static void ForgetCurrent();
static void Entering(const Pointer &codeCtx);
RegexPattern.h \
RunnersRegistry.cc \
RunnersRegistry.h \
+ Stopwatch.cc \
+ Stopwatch.h \
Subscription.h \
SupportOrVeto.h \
TextException.cc \
--- /dev/null
+/*
+ * Copyright (C) 1996-2022 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 files for details.
+ */
+
+#include "squid.h"
+#include "base/Stopwatch.h"
+#include "debug/Stream.h"
+
+static_assert(Stopwatch::Clock::is_steady,
+ "Stopwatch::Clock is suitable for measuring real-time intervals");
+
+Stopwatch::Stopwatch():
+ subtotal_(Clock::duration::zero())
+{
+}
+
+Stopwatch::Clock::duration
+Stopwatch::total() const
+{
+ auto result = subtotal_;
+ if (running())
+ result += Clock::now() - runStart_;
+ return result;
+}
+
+void
+Stopwatch::resume()
+{
+ if (!running()) {
+ runStart_ = Clock::now();
+ debugs(1, 7, "period " << resumes_<< " started after " << subtotal_.count());
+ }
+ ++resumes_;
+}
+
+/// ends the current measurement period if needed; requires prior resume()
+void Stopwatch::pause()
+{
+ ++pauses_;
+ if (!running()) {
+ const auto runtime = Clock::now() - runStart_;
+ subtotal_ += runtime;
+ debugs(1, 7, "period " << resumes_ << " ran for " << runtime.count());
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (C) 1996-2022 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 files for details.
+ */
+
+#ifndef SQUID_SRC_BASE_STOPWATCH_H
+#define SQUID_SRC_BASE_STOPWATCH_H
+
+#include <chrono>
+#include <cstdint>
+
+/// Quickly accumulates related real-time (a.k.a. "physical time" or "wall
+/// clock") periods. Continues to run if the caller is blocked on a system call.
+/// Usually suitable for measuring CPU overheads of non-sleeping code sequences.
+class Stopwatch
+{
+public:
+ // std::clock() is not precise enough and raises (minor) overflow concerns;
+ // clock_gettime(CLOCK_PROCESS_CPUTIME_ID) may not be portable, and both may
+ // include CPU ticks accumulated by [AUFS] threads. When the code does
+ // not sleep (i.e. always does something), wall clock time is better.
+
+ // Do not be tempted by std::high_resolution_clock spelling! That clock has
+ // unpredictable properties. It may not be steady (i.e. !is_steady). Its use
+ // is discouraged[1,3]. In most implementations, the steady_clock resolution
+ // is just as high[1,2,3].
+ // [1]: https://en.cppreference.com/w/cpp/chrono/high_resolution_clock
+ // [2]: https://howardhinnant.github.io/clock_survey.html
+ // [3]: https://stackoverflow.com/questions/38252022/does-standard-c11-guarantee-that-high-resolution-clock-measure-real-time-non
+
+ /// the underlying time measuring mechanism
+ using Clock = std::chrono::steady_clock;
+
+ Stopwatch();
+
+ /// whether we are currently measuring time (i.e. between resume() and pause())
+ bool running() const { return resumes_ > pauses_; }
+
+ /// whether we ever measured time (i.e. resume() has been called)
+ bool ran() const { return resumes_ > 0; }
+
+ /// the sum of all measurement period durations (or zero)
+ /// includes the current measurement period, if any
+ Clock::duration total() const;
+
+ /// (re)starts or continues the current measurement period; each resume()
+ /// call must be paired with a dedicated future pause() call
+ void resume();
+
+ /// ends the current measurement period if needed; each pause() call
+ /// requires a prior dedicated resume() call
+ void pause();
+
+private:
+ Clock::time_point runStart_; ///< when the current period was initiated
+
+ Clock::duration subtotal_; ///< the sum of all _finished_ periods
+
+ uint64_t resumes_ = 0; ///< the total number of resume() calls
+ uint64_t pauses_ = 0; ///< the total number of pause() calls
+};
+
+#endif /* SQUID_SRC_BASE_STOPWATCH_H */
+
class CallDialer;
class CodeContext;
class DelayedAsyncCalls;
-class ScopedId;
class Raw;
class RegexPattern;
+class ScopedId;
+class Stopwatch;
template<class Cbc> class CbcPointer;
template<class RefCountableKid> class RefCount;
similar to the default access.log "current time" field
(%ts.%03tu).
+ busy_time Time spent in transaction-related code (nanoseconds)
+ This cumulative measurement excludes periods of time when the
+ transaction was waiting (e.g., for a server or helper response)
+ while Squid worked on other transactions or was engaged in
+ transaction-unrelated activities (e.g., generating a cache index).
+ In other words, this measurement represents the total amount of
+ physical time when Squid was busy working on this transaction.
+
+ WARNING: This measurement relies on Squid transaction context
+ tracking features that currently have known context leak bugs and
+ coverage gaps. Until those features are fully implemented, logged
+ values may significantly understate or exaggerate actual times.
+ Do not use this measurement unless you know it works in your case.
+
Access Control related format codes:
et Tag returned by external acl
LFT_PEER_RESPONSE_TIME,
LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME,
LFT_DNS_WAIT_TIME,
+ LFT_BUSY_TIME,
/* Squid internal processing details */
LFT_SQUID_STATUS,
doSec = 1;
break;
+ case LFT_BUSY_TIME: {
+ const auto &stopwatch = al->busyTime;
+ if (stopwatch.ran()) {
+ // make sure total() returns nanoseconds compatible with outoff
+ using nanos = std::chrono::duration<decltype(outoff), std::nano>;
+ const nanos n = stopwatch.total();
+ outoff = n.count();
+ dooff = true;
+ }
+ }
+ break;
+
case LFT_TIME_TO_HANDLE_REQUEST:
outtv = al->cache.trTime;
doMsec = 1;
TokenTableEntry("<pt", LFT_PEER_RESPONSE_TIME),
TokenTableEntry("<tt", LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME),
TokenTableEntry("dt", LFT_DNS_WAIT_TIME),
+ TokenTableEntry("busy_time", LFT_BUSY_TIME),
TokenTableEntry(">ha", LFT_ADAPTED_REQUEST_HEADER),
TokenTableEntry(">ha", LFT_ADAPTED_REQUEST_ALL_HEADERS),