Home | History | Annotate | Download | only in census
      1 /*
      2  *
      3  * Copyright 2018 gRPC authors.
      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  */
     18 
     19 #ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H
     20 #define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H
     21 
     22 #include <grpc/support/port_platform.h>
     23 
     24 #include <string.h>
     25 
     26 #include "absl/base/internal/endian.h"
     27 #include "absl/strings/string_view.h"
     28 #include "opencensus/trace/span_context.h"
     29 #include "opencensus/trace/span_id.h"
     30 #include "opencensus/trace/trace_id.h"
     31 
     32 namespace grpc {
     33 
     34 // TODO: Rename to GrpcTraceContextV0.
     35 struct GrpcTraceContext {
     36   GrpcTraceContext() {}
     37 
     38   explicit GrpcTraceContext(const ::opencensus::trace::SpanContext& ctx) {
     39     ctx.trace_id().CopyTo(trace_id);
     40     ctx.span_id().CopyTo(span_id);
     41     ctx.trace_options().CopyTo(trace_options);
     42   }
     43 
     44   ::opencensus::trace::SpanContext ToSpanContext() const {
     45     return ::opencensus::trace::SpanContext(
     46         ::opencensus::trace::TraceId(trace_id),
     47         ::opencensus::trace::SpanId(span_id),
     48         ::opencensus::trace::TraceOptions(trace_options));
     49   }
     50 
     51   // TODO: For performance:
     52   // uint8_t version;
     53   // uint8_t trace_id_field_id;
     54   uint8_t trace_id[::opencensus::trace::TraceId::kSize];
     55   // uint8_t span_id_field_id;
     56   uint8_t span_id[::opencensus::trace::SpanId::kSize];
     57   // uint8_t trace_options_field_id;
     58   uint8_t trace_options[::opencensus::trace::TraceOptions::kSize];
     59 };
     60 
     61 // TraceContextEncoding encapsulates the logic for encoding and decoding of
     62 // trace contexts.
     63 class TraceContextEncoding {
     64  public:
     65   // Size of encoded GrpcTraceContext. (16 + 8 + 1 + 4)
     66   static constexpr size_t kGrpcTraceContextSize = 29;
     67   // Error value.
     68   static constexpr size_t kEncodeDecodeFailure = 0;
     69 
     70   // Deserializes a GrpcTraceContext from the incoming buffer. Returns the
     71   // number of bytes deserialized from the buffer. If the incoming buffer is
     72   // empty or the encoding version is not supported it will return 0 bytes,
     73   // currently only version 0 is supported. If an unknown field ID is
     74   // encountered it will return immediately without parsing the rest of the
     75   // buffer. Inlined for performance reasons.
     76   static size_t Decode(absl::string_view buf, GrpcTraceContext* tc) {
     77     if (buf.empty()) {
     78       return kEncodeDecodeFailure;
     79     }
     80     uint8_t version = buf[kVersionIdOffset];
     81     // TODO: Support other versions later. Only support version 0 for
     82     // now.
     83     if (version != kVersionId) {
     84       return kEncodeDecodeFailure;
     85     }
     86 
     87     size_t pos = kVersionIdSize;
     88     while (pos < buf.size()) {
     89       size_t bytes_read =
     90           ParseField(absl::string_view(&buf[pos], buf.size() - pos), tc);
     91       if (bytes_read == 0) {
     92         break;
     93       } else {
     94         pos += bytes_read;
     95       }
     96     }
     97     return pos;
     98   }
     99 
    100   // Serializes a GrpcTraceContext into the provided buffer. Returns the number
    101   // of bytes serialized into the buffer. If the buffer is not of sufficient
    102   // size (it must be at least kGrpcTraceContextSize bytes) it will drop
    103   // everything and return 0 bytes serialized. Inlined for performance reasons.
    104   static size_t Encode(const GrpcTraceContext& tc, char* buf, size_t buf_size) {
    105     if (buf_size < kGrpcTraceContextSize) {
    106       return kEncodeDecodeFailure;
    107     }
    108     buf[kVersionIdOffset] = kVersionId;
    109     buf[kTraceIdOffset] = kTraceIdField;
    110     memcpy(&buf[kTraceIdOffset + 1], tc.trace_id,
    111            opencensus::trace::TraceId::kSize);
    112     buf[kSpanIdOffset] = kSpanIdField;
    113     memcpy(&buf[kSpanIdOffset + 1], tc.span_id,
    114            opencensus::trace::SpanId::kSize);
    115     buf[kTraceOptionsOffset] = kTraceOptionsField;
    116     memcpy(&buf[kTraceOptionsOffset + 1], tc.trace_options,
    117            opencensus::trace::TraceOptions::kSize);
    118     return kGrpcTraceContextSize;
    119   }
    120 
    121  private:
    122   // Parses the next field from the incoming buffer and stores the parsed value
    123   // in a GrpcTraceContext struct.  If it does not recognize the field ID it
    124   // will return 0, otherwise it returns the number of bytes read.
    125   static size_t ParseField(absl::string_view buf, GrpcTraceContext* tc) {
    126     // TODO: Add support for multi-byte field IDs.
    127     if (buf.empty()) {
    128       return 0;
    129     }
    130     // Field ID is always the first byte in a field.
    131     uint32_t field_id = buf[0];
    132     size_t bytes_read = kFieldIdSize;
    133     switch (field_id) {
    134       case kTraceIdField:
    135         bytes_read += kTraceIdSize;
    136         if (bytes_read > buf.size()) {
    137           return 0;
    138         }
    139         memcpy(tc->trace_id, &buf[kFieldIdSize],
    140                opencensus::trace::TraceId::kSize);
    141         break;
    142       case kSpanIdField:
    143         bytes_read += kSpanIdSize;
    144         if (bytes_read > buf.size()) {
    145           return 0;
    146         }
    147         memcpy(tc->span_id, &buf[kFieldIdSize],
    148                opencensus::trace::SpanId::kSize);
    149         break;
    150       case kTraceOptionsField:
    151         bytes_read += kTraceOptionsSize;
    152         if (bytes_read > buf.size()) {
    153           return 0;
    154         }
    155         memcpy(tc->trace_options, &buf[kFieldIdSize],
    156                opencensus::trace::TraceOptions::kSize);
    157         break;
    158       default:  // Invalid field ID
    159         return 0;
    160     }
    161 
    162     return bytes_read;
    163   }
    164 
    165   // Size of Version ID.
    166   static constexpr size_t kVersionIdSize = 1;
    167   // Size of Field ID.
    168   static constexpr size_t kFieldIdSize = 1;
    169 
    170   // Offset and value for currently supported version ID.
    171   static constexpr size_t kVersionIdOffset = 0;
    172   static constexpr size_t kVersionId = 0;
    173 
    174   // Fixed Field ID values:
    175   enum FieldIdValue {
    176     kTraceIdField = 0,
    177     kSpanIdField = 1,
    178     kTraceOptionsField = 2,
    179   };
    180 
    181   // Field data sizes in bytes
    182   enum FieldSize {
    183     kTraceIdSize = 16,
    184     kSpanIdSize = 8,
    185     kTraceOptionsSize = 1,
    186   };
    187 
    188   // Fixed size offsets for field ID start positions during encoding.  Field
    189   // data immediately follows.
    190   enum FieldIdOffset {
    191     kTraceIdOffset = kVersionIdSize,
    192     kSpanIdOffset = kTraceIdOffset + kFieldIdSize + kTraceIdSize,
    193     kTraceOptionsOffset = kSpanIdOffset + kFieldIdSize + kSpanIdSize,
    194   };
    195 
    196   TraceContextEncoding() = delete;
    197   TraceContextEncoding(const TraceContextEncoding&) = delete;
    198   TraceContextEncoding(TraceContextEncoding&&) = delete;
    199   TraceContextEncoding operator=(const TraceContextEncoding&) = delete;
    200   TraceContextEncoding operator=(TraceContextEncoding&&) = delete;
    201 };
    202 
    203 // TODO: This may not be needed. Check to see if opencensus requires
    204 // a trailing server response.
    205 // RpcServerStatsEncoding encapsulates the logic for encoding and decoding of
    206 // rpc server stats messages. Rpc server stats consists of a uint64_t time
    207 // value (server latency in nanoseconds).
    208 class RpcServerStatsEncoding {
    209  public:
    210   // Size of encoded RPC server stats.
    211   static constexpr size_t kRpcServerStatsSize = 10;
    212   // Error value.
    213   static constexpr size_t kEncodeDecodeFailure = 0;
    214 
    215   // Deserializes rpc server stats from the incoming 'buf' into *time.  Returns
    216   // number of bytes decoded. If the buffer is of insufficient size (it must be
    217   // at least kRpcServerStatsSize bytes) or the encoding version or field ID are
    218   // unrecognized, *time will be set to 0 and it will return
    219   // kEncodeDecodeFailure. Inlined for performance reasons.
    220   static size_t Decode(absl::string_view buf, uint64_t* time) {
    221     if (buf.size() < kRpcServerStatsSize) {
    222       *time = 0;
    223       return kEncodeDecodeFailure;
    224     }
    225 
    226     uint8_t version = buf[kVersionIdOffset];
    227     uint32_t fieldID = buf[kServerElapsedTimeOffset];
    228     if (version != kVersionId || fieldID != kServerElapsedTimeField) {
    229       *time = 0;
    230       return kEncodeDecodeFailure;
    231     }
    232     *time = absl::little_endian::Load64(
    233         &buf[kServerElapsedTimeOffset + kFieldIdSize]);
    234     return kRpcServerStatsSize;
    235   }
    236 
    237   // Serializes rpc server stats into the provided buffer.  It returns the
    238   // number of bytes written to the buffer. If the buffer is smaller than
    239   // kRpcServerStatsSize bytes it will return kEncodeDecodeFailure. Inlined for
    240   // performance reasons.
    241   static size_t Encode(uint64_t time, char* buf, size_t buf_size) {
    242     if (buf_size < kRpcServerStatsSize) {
    243       return kEncodeDecodeFailure;
    244     }
    245 
    246     buf[kVersionIdOffset] = kVersionId;
    247     buf[kServerElapsedTimeOffset] = kServerElapsedTimeField;
    248     absl::little_endian::Store64(&buf[kServerElapsedTimeOffset + kFieldIdSize],
    249                                  time);
    250     return kRpcServerStatsSize;
    251   }
    252 
    253  private:
    254   // Size of Version ID.
    255   static constexpr size_t kVersionIdSize = 1;
    256   // Size of Field ID.
    257   static constexpr size_t kFieldIdSize = 1;
    258 
    259   // Offset and value for currently supported version ID.
    260   static constexpr size_t kVersionIdOffset = 0;
    261   static constexpr size_t kVersionId = 0;
    262 
    263   enum FieldIdValue {
    264     kServerElapsedTimeField = 0,
    265   };
    266 
    267   enum FieldSize {
    268     kServerElapsedTimeSize = 8,
    269   };
    270 
    271   enum FieldIdOffset {
    272     kServerElapsedTimeOffset = kVersionIdSize,
    273   };
    274 
    275   RpcServerStatsEncoding() = delete;
    276   RpcServerStatsEncoding(const RpcServerStatsEncoding&) = delete;
    277   RpcServerStatsEncoding(RpcServerStatsEncoding&&) = delete;
    278   RpcServerStatsEncoding operator=(const RpcServerStatsEncoding&) = delete;
    279   RpcServerStatsEncoding operator=(RpcServerStatsEncoding&&) = delete;
    280 };
    281 
    282 }  // namespace grpc
    283 
    284 #endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H */
    285