xref: /6.6.0/kv_engine/tracing/tracer.cc (revision ee5212ee)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2017 Couchbase, Inc.
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17#include <memcached/tracer.h>
18
19#include <algorithm>
20#include <cmath>
21#include <iostream>
22#include <limits>
23#include <sstream>
24
25std::chrono::microseconds to_micros(
26        const std::chrono::steady_clock::time_point tp) {
27    return std::chrono::time_point_cast<std::chrono::microseconds>(tp)
28            .time_since_epoch();
29}
30
31namespace cb {
32namespace tracing {
33
34SpanId Tracer::begin(Code tracecode,
35                     std::chrono::steady_clock::time_point startTime) {
36    return vecSpans.withLock([tracecode, startTime](auto& spans) {
37        spans.emplace_back(tracecode, startTime);
38        return spans.size() - 1;
39    });
40}
41
42bool Tracer::end(SpanId spanId, std::chrono::steady_clock::time_point endTime) {
43    return vecSpans.withLock([spanId, endTime](auto& spans) {
44        if (spanId >= spans.size()) {
45            return false;
46        }
47
48        auto& span = spans.at(spanId);
49        span.duration = std::chrono::duration_cast<Span::Duration>(endTime -
50                                                                   span.start);
51        return true;
52    });
53}
54
55std::vector<Span> Tracer::extractDurations() {
56    std::vector<Span> ret;
57    vecSpans.swap(ret);
58    return ret;
59}
60
61Span::Duration Tracer::getTotalMicros() const {
62    return vecSpans.withLock([](auto& spans) -> Span::Duration {
63        if (spans.empty()) {
64            return {};
65        }
66        const auto& top = spans.at(0);
67        // If the Span has not yet been closed; return the duration up to now.
68        if (top.duration == Span::Duration::max()) {
69            return std::chrono::duration_cast<Span::Duration>(
70                    std::chrono::steady_clock::now() - top.start);
71        }
72        return top.duration;
73    });
74}
75
76/**
77 * Encode the total micros in 2 bytes. Gives a much better coverage
78 * and reasonable error rates on larger values.
79 * Idea by Brett Lawson [@brett19]
80 * Max Time: 02:00.125042 (120125042)
81 */
82uint16_t Tracer::getEncodedMicros() const {
83    return encodeMicros(getTotalMicros().count());
84}
85
86uint16_t Tracer::encodeMicros(uint64_t actual) {
87    static const uint64_t maxVal = 120125042;
88    actual = std::min(actual, maxVal);
89    return uint16_t(std::round(std::pow(actual * 2, 1.0 / 1.74)));
90}
91
92std::chrono::microseconds Tracer::decodeMicros(uint16_t encoded) {
93    auto usecs = uint64_t(std::pow(encoded, 1.74) / 2);
94    return std::chrono::microseconds(usecs);
95}
96
97void Tracer::clear() {
98    vecSpans.lock()->clear();
99}
100
101std::string Tracer::to_string() const {
102    return vecSpans.withLock([](auto& spans) {
103        std::ostringstream os;
104        auto size = spans.size();
105        for (const auto& span : spans) {
106            os << ::to_string(span.code) << "="
107               << span.start.time_since_epoch().count() << ":";
108            if (span.duration == std::chrono::microseconds::max()) {
109                os << "--";
110            } else {
111                os << span.duration.count();
112            }
113            size--;
114            if (size > 0) {
115                os << " ";
116            }
117        }
118        return os.str();
119    });
120}
121
122} // end namespace tracing
123} // end namespace cb
124
125MEMCACHED_PUBLIC_API std::ostream& operator<<(
126        std::ostream& os, const cb::tracing::Tracer& tracer) {
127    return os << tracer.to_string();
128}
129
130MEMCACHED_PUBLIC_API std::string to_string(const cb::tracing::Code tracecode) {
131    using cb::tracing::Code;
132    switch (tracecode) {
133    case Code::Request:
134        return "request";
135    case Code::BackgroundWait:
136        return "bg.wait";
137    case Code::BackgroundLoad:
138        return "bg.load";
139    case Code::Get:
140        return "get";
141    case Code::GetIf:
142        return "get.if";
143    case Code::GetStats:
144        return "get.stats";
145    case Code::SetWithMeta:
146        return "set.with.meta";
147    case Code::Store:
148        return "store";
149    case Code::SyncWritePrepare:
150        return "sync_write.prepare";
151    case Code::SyncWriteAckLocal:
152        return "sync_write.ack_local";
153    case Code::SyncWriteAckRemote:
154        return "sync_write.ack_remote";
155    }
156    return "unknown tracecode";
157}
158