Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(343)

Side by Side Diff: third_party/grpc/src/core/census/context.c

Issue 1932353002: Initial checkin of gRPC to third_party/ Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 *
3 * Copyright 2015-2016, Google Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34 #include <grpc/census.h>
35 #include <grpc/support/alloc.h>
36 #include <grpc/support/log.h>
37 #include <grpc/support/port_platform.h>
38 #include <grpc/support/useful.h>
39 #include <stdbool.h>
40 #include <string.h>
41 #include "src/core/support/string.h"
42
43 // Functions in this file support the public context API, including
44 // encoding/decoding as part of context propagation across RPC's. The overall
45 // requirements (in approximate priority order) for the
46 // context representation:
47 // 1. Efficient conversion to/from wire format
48 // 2. Minimal bytes used on-wire
49 // 3. Efficient context creation
50 // 4. Efficient lookup of tag value for a key
51 // 5. Efficient iteration over tags
52 // 6. Minimal memory footprint
53 //
54 // Notes on tradeoffs/decisions:
55 // * tag includes 1 byte length of key, as well as nil-terminating byte. These
56 // are to aid in efficient parsing and the ability to directly return key
57 // strings. This is more important than saving a single byte/tag on the wire.
58 // * The wire encoding uses only single byte values. This eliminates the need
59 // to handle endian-ness conversions. It also means there is a hard upper
60 // limit of 255 for both CENSUS_MAX_TAG_KV_LEN and CENSUS_MAX_PROPAGATED_TAGS.
61 // * Keep all tag information (keys/values/flags) in a single memory buffer,
62 // that can be directly copied to the wire.
63
64 // min and max valid chars in tag keys and values. All printable ASCII is OK.
65 #define MIN_VALID_TAG_CHAR 32 // ' '
66 #define MAX_VALID_TAG_CHAR 126 // '~'
67
68 // Structure representing a set of tags. Essentially a count of number of tags
69 // present, and pointer to a chunk of memory that contains the per-tag details.
70 struct tag_set {
71 int ntags; // number of tags.
72 int ntags_alloc; // ntags + number of deleted tags (total number of tags
73 // in all of kvm). This will always be == ntags, except during the process
74 // of building a new tag set.
75 size_t kvm_size; // number of bytes allocated for key/value storage.
76 size_t kvm_used; // number of bytes of used key/value memory
77 char *kvm; // key/value memory. Consists of repeated entries of:
78 // Offset Size Description
79 // 0 1 Key length, including trailing 0. (K)
80 // 1 1 Value length, including trailing 0 (V)
81 // 2 1 Flags
82 // 3 K Key bytes
83 // 3 + K V Value bytes
84 //
85 // We refer to the first 3 entries as the 'tag header'. If extra values are
86 // introduced in the header, you will need to modify the TAG_HEADER_SIZE
87 // constant, the raw_tag structure (and everything that uses it) and the
88 // encode/decode functions appropriately.
89 };
90
91 // Number of bytes in tag header.
92 #define TAG_HEADER_SIZE 3 // key length (1) + value length (1) + flags (1)
93 // Offsets to tag header entries.
94 #define KEY_LEN_OFFSET 0
95 #define VALUE_LEN_OFFSET 1
96 #define FLAG_OFFSET 2
97
98 // raw_tag represents the raw-storage form of a tag in the kvm of a tag_set.
99 struct raw_tag {
100 uint8_t key_len;
101 uint8_t value_len;
102 uint8_t flags;
103 char *key;
104 char *value;
105 };
106
107 // Use a reserved flag bit for indication of deleted tag.
108 #define CENSUS_TAG_DELETED CENSUS_TAG_RESERVED
109 #define CENSUS_TAG_IS_DELETED(flags) (flags & CENSUS_TAG_DELETED)
110
111 // Primary representation of a context. Composed of 2 underlying tag_set
112 // structs, one each for propagated and local (non-propagated) tags. This is
113 // to efficiently support tag encoding/decoding.
114 // TODO(aveitch): need to add tracing id's/structure.
115 struct census_context {
116 struct tag_set tags[2];
117 census_context_status status;
118 };
119
120 // Indices into the tags member of census_context
121 #define PROPAGATED_TAGS 0
122 #define LOCAL_TAGS 1
123
124 // Validate (check all characters are in range and size is less than limit) a
125 // key or value string. Returns 0 if the string is invalid, or the length
126 // (including terminator) if valid.
127 static size_t validate_tag(const char *kv) {
128 size_t len = 1;
129 char ch;
130 while ((ch = *kv++) != 0) {
131 if (ch < MIN_VALID_TAG_CHAR || ch > MAX_VALID_TAG_CHAR) {
132 return 0;
133 }
134 len++;
135 }
136 if (len > CENSUS_MAX_TAG_KV_LEN) {
137 return 0;
138 }
139 return len;
140 }
141
142 // Extract a raw tag given a pointer (raw) to the tag header. Allow for some
143 // extra bytes in the tag header (see encode/decode functions for usage: this
144 // allows for future expansion of the tag header).
145 static char *decode_tag(struct raw_tag *tag, char *header, int offset) {
146 tag->key_len = (uint8_t)(*header++);
147 tag->value_len = (uint8_t)(*header++);
148 tag->flags = (uint8_t)(*header++);
149 header += offset;
150 tag->key = header;
151 header += tag->key_len;
152 tag->value = header;
153 return header + tag->value_len;
154 }
155
156 // Make a copy (in 'to') of an existing tag_set.
157 static void tag_set_copy(struct tag_set *to, const struct tag_set *from) {
158 memcpy(to, from, sizeof(struct tag_set));
159 to->kvm = gpr_malloc(to->kvm_size);
160 memcpy(to->kvm, from->kvm, from->kvm_used);
161 }
162
163 // Delete a tag from a tag_set, if it exists (returns true if it did).
164 static bool tag_set_delete_tag(struct tag_set *tags, const char *key,
165 size_t key_len) {
166 char *kvp = tags->kvm;
167 for (int i = 0; i < tags->ntags_alloc; i++) {
168 uint8_t *flags = (uint8_t *)(kvp + FLAG_OFFSET);
169 struct raw_tag tag;
170 kvp = decode_tag(&tag, kvp, 0);
171 if (CENSUS_TAG_IS_DELETED(tag.flags)) continue;
172 if ((key_len == tag.key_len) && (memcmp(key, tag.key, key_len) == 0)) {
173 *flags |= CENSUS_TAG_DELETED;
174 tags->ntags--;
175 return true;
176 }
177 }
178 return false;
179 }
180
181 // Delete a tag from a context, return true if it existed.
182 static bool context_delete_tag(census_context *context, const census_tag *tag,
183 size_t key_len) {
184 return (
185 tag_set_delete_tag(&context->tags[LOCAL_TAGS], tag->key, key_len) ||
186 tag_set_delete_tag(&context->tags[PROPAGATED_TAGS], tag->key, key_len));
187 }
188
189 // Add a tag to a tag_set. Return true on success, false if the tag could
190 // not be added because of constraints on tag set size. This function should
191 // not be called if the tag may already exist (in a non-deleted state) in
192 // the tag_set, as that would result in two tags with the same key.
193 static bool tag_set_add_tag(struct tag_set *tags, const census_tag *tag,
194 size_t key_len, size_t value_len) {
195 if (tags->ntags == CENSUS_MAX_PROPAGATED_TAGS) {
196 return false;
197 }
198 const size_t tag_size = key_len + value_len + TAG_HEADER_SIZE;
199 if (tags->kvm_used + tag_size > tags->kvm_size) {
200 // allocate new memory if needed
201 tags->kvm_size += 2 * CENSUS_MAX_TAG_KV_LEN + TAG_HEADER_SIZE;
202 char *new_kvm = gpr_malloc(tags->kvm_size);
203 memcpy(new_kvm, tags->kvm, tags->kvm_used);
204 gpr_free(tags->kvm);
205 tags->kvm = new_kvm;
206 }
207 char *kvp = tags->kvm + tags->kvm_used;
208 *kvp++ = (char)key_len;
209 *kvp++ = (char)value_len;
210 // ensure reserved flags are not used.
211 *kvp++ = (char)(tag->flags & (CENSUS_TAG_PROPAGATE | CENSUS_TAG_STATS));
212 memcpy(kvp, tag->key, key_len);
213 kvp += key_len;
214 memcpy(kvp, tag->value, value_len);
215 tags->kvm_used += tag_size;
216 tags->ntags++;
217 tags->ntags_alloc++;
218 return true;
219 }
220
221 // Add/modify/delete a tag to/in a context. Caller must validate that tag key
222 // etc. are valid.
223 static void context_modify_tag(census_context *context, const census_tag *tag,
224 size_t key_len, size_t value_len) {
225 // First delete the tag if it is already present.
226 bool deleted = context_delete_tag(context, tag, key_len);
227 bool added = false;
228 if (CENSUS_TAG_IS_PROPAGATED(tag->flags)) {
229 added = tag_set_add_tag(&context->tags[PROPAGATED_TAGS], tag, key_len,
230 value_len);
231 } else {
232 added =
233 tag_set_add_tag(&context->tags[LOCAL_TAGS], tag, key_len, value_len);
234 }
235
236 if (deleted) {
237 context->status.n_modified_tags++;
238 } else {
239 if (added) {
240 context->status.n_added_tags++;
241 } else {
242 context->status.n_ignored_tags++;
243 }
244 }
245 }
246
247 // Remove memory used for deleted tags from a tag set. Basic algorithm:
248 // 1) Walk through tag set to find first deleted tag. Record where it is.
249 // 2) Find the next not-deleted tag. Copy all of kvm from there to the end
250 // "over" the deleted tags
251 // 3) repeat #1 and #2 until we have seen all tags
252 // 4) if we are still looking for a not-deleted tag, then all the end portion
253 // of the kvm is deleted. Just reduce the used amount of memory by the
254 // appropriate amount.
255 static void tag_set_flatten(struct tag_set *tags) {
256 if (tags->ntags == tags->ntags_alloc) return;
257 bool found_deleted = false; // found a deleted tag.
258 char *kvp = tags->kvm;
259 char *dbase = NULL; // record location of deleted tag
260 for (int i = 0; i < tags->ntags_alloc; i++) {
261 struct raw_tag tag;
262 char *next_kvp = decode_tag(&tag, kvp, 0);
263 if (found_deleted) {
264 if (!CENSUS_TAG_IS_DELETED(tag.flags)) {
265 ptrdiff_t reduce = kvp - dbase; // #bytes in deleted tags
266 GPR_ASSERT(reduce > 0);
267 ptrdiff_t copy_size = tags->kvm + tags->kvm_used - kvp;
268 GPR_ASSERT(copy_size > 0);
269 memmove(dbase, kvp, (size_t)copy_size);
270 tags->kvm_used -= (size_t)reduce;
271 next_kvp -= reduce;
272 found_deleted = false;
273 }
274 } else {
275 if (CENSUS_TAG_IS_DELETED(tag.flags)) {
276 dbase = kvp;
277 found_deleted = true;
278 }
279 }
280 kvp = next_kvp;
281 }
282 if (found_deleted) {
283 GPR_ASSERT(dbase > tags->kvm);
284 tags->kvm_used = (size_t)(dbase - tags->kvm);
285 }
286 tags->ntags_alloc = tags->ntags;
287 }
288
289 census_context *census_context_create(const census_context *base,
290 const census_tag *tags, int ntags,
291 census_context_status const **status) {
292 census_context *context = gpr_malloc(sizeof(census_context));
293 // If we are given a base, copy it into our new tag set. Otherwise set it
294 // to zero/NULL everything.
295 if (base == NULL) {
296 memset(context, 0, sizeof(census_context));
297 } else {
298 tag_set_copy(&context->tags[PROPAGATED_TAGS], &base->tags[PROPAGATED_TAGS]);
299 tag_set_copy(&context->tags[LOCAL_TAGS], &base->tags[LOCAL_TAGS]);
300 memset(&context->status, 0, sizeof(context->status));
301 }
302 // Walk over the additional tags and, for those that aren't invalid, modify
303 // the context to add/replace/delete as required.
304 for (int i = 0; i < ntags; i++) {
305 const census_tag *tag = &tags[i];
306 size_t key_len = validate_tag(tag->key);
307 // ignore the tag if it is invalid or too short.
308 if (key_len <= 1) {
309 context->status.n_invalid_tags++;
310 } else {
311 if (tag->value != NULL) {
312 size_t value_len = validate_tag(tag->value);
313 if (value_len != 0) {
314 context_modify_tag(context, tag, key_len, value_len);
315 } else {
316 context->status.n_invalid_tags++;
317 }
318 } else {
319 if (context_delete_tag(context, tag, key_len)) {
320 context->status.n_deleted_tags++;
321 }
322 }
323 }
324 }
325 // Remove any deleted tags, update status if needed, and return.
326 tag_set_flatten(&context->tags[PROPAGATED_TAGS]);
327 tag_set_flatten(&context->tags[LOCAL_TAGS]);
328 context->status.n_propagated_tags = context->tags[PROPAGATED_TAGS].ntags;
329 context->status.n_local_tags = context->tags[LOCAL_TAGS].ntags;
330 if (status) {
331 *status = &context->status;
332 }
333 return context;
334 }
335
336 const census_context_status *census_context_get_status(
337 const census_context *context) {
338 return &context->status;
339 }
340
341 void census_context_destroy(census_context *context) {
342 gpr_free(context->tags[PROPAGATED_TAGS].kvm);
343 gpr_free(context->tags[LOCAL_TAGS].kvm);
344 gpr_free(context);
345 }
346
347 void census_context_initialize_iterator(const census_context *context,
348 census_context_iterator *iterator) {
349 iterator->context = context;
350 iterator->index = 0;
351 if (context->tags[PROPAGATED_TAGS].ntags != 0) {
352 iterator->base = PROPAGATED_TAGS;
353 iterator->kvm = context->tags[PROPAGATED_TAGS].kvm;
354 } else if (context->tags[LOCAL_TAGS].ntags != 0) {
355 iterator->base = LOCAL_TAGS;
356 iterator->kvm = context->tags[LOCAL_TAGS].kvm;
357 } else {
358 iterator->base = -1;
359 }
360 }
361
362 int census_context_next_tag(census_context_iterator *iterator,
363 census_tag *tag) {
364 if (iterator->base < 0) {
365 return 0;
366 }
367 struct raw_tag raw;
368 iterator->kvm = decode_tag(&raw, iterator->kvm, 0);
369 tag->key = raw.key;
370 tag->value = raw.value;
371 tag->flags = raw.flags;
372 if (++iterator->index == iterator->context->tags[iterator->base].ntags) {
373 do {
374 if (iterator->base == LOCAL_TAGS) {
375 iterator->base = -1;
376 return 1;
377 }
378 } while (iterator->context->tags[++iterator->base].ntags == 0);
379 iterator->index = 0;
380 iterator->kvm = iterator->context->tags[iterator->base].kvm;
381 }
382 return 1;
383 }
384
385 // Find a tag in a tag_set by key. Return true if found, false otherwise.
386 static bool tag_set_get_tag(const struct tag_set *tags, const char *key,
387 size_t key_len, census_tag *tag) {
388 char *kvp = tags->kvm;
389 for (int i = 0; i < tags->ntags; i++) {
390 struct raw_tag raw;
391 kvp = decode_tag(&raw, kvp, 0);
392 if (key_len == raw.key_len && memcmp(raw.key, key, key_len) == 0) {
393 tag->key = raw.key;
394 tag->value = raw.value;
395 tag->flags = raw.flags;
396 return true;
397 }
398 }
399 return false;
400 }
401
402 int census_context_get_tag(const census_context *context, const char *key,
403 census_tag *tag) {
404 size_t key_len = strlen(key) + 1;
405 if (key_len == 1) {
406 return 0;
407 }
408 if (tag_set_get_tag(&context->tags[PROPAGATED_TAGS], key, key_len, tag) ||
409 tag_set_get_tag(&context->tags[LOCAL_TAGS], key, key_len, tag)) {
410 return 1;
411 }
412 return 0;
413 }
414
415 // Context encoding and decoding functions.
416 //
417 // Wire format for tag_set's on the wire:
418 //
419 // First, a tag set header:
420 //
421 // offset bytes description
422 // 0 1 version number
423 // 1 1 number of bytes in this header. This allows for future
424 // expansion.
425 // 2 1 number of bytes in each tag header.
426 // 3 1 ntags value from tag set.
427 //
428 // This is followed by the key/value memory from struct tag_set.
429
430 #define ENCODED_VERSION 0 // Version number
431 #define ENCODED_HEADER_SIZE 4 // size of tag set header
432
433 // Encode a tag set. Returns 0 if buffer is too small.
434 static size_t tag_set_encode(const struct tag_set *tags, char *buffer,
435 size_t buf_size) {
436 if (buf_size < ENCODED_HEADER_SIZE + tags->kvm_used) {
437 return 0;
438 }
439 buf_size -= ENCODED_HEADER_SIZE;
440 *buffer++ = (char)ENCODED_VERSION;
441 *buffer++ = (char)ENCODED_HEADER_SIZE;
442 *buffer++ = (char)TAG_HEADER_SIZE;
443 *buffer++ = (char)tags->ntags;
444 if (tags->ntags == 0) {
445 return ENCODED_HEADER_SIZE;
446 }
447 memcpy(buffer, tags->kvm, tags->kvm_used);
448 return ENCODED_HEADER_SIZE + tags->kvm_used;
449 }
450
451 size_t census_context_encode(const census_context *context, char *buffer,
452 size_t buf_size) {
453 return tag_set_encode(&context->tags[PROPAGATED_TAGS], buffer, buf_size);
454 }
455
456 // Decode a tag set.
457 static void tag_set_decode(struct tag_set *tags, const char *buffer,
458 size_t size) {
459 uint8_t version = (uint8_t)(*buffer++);
460 uint8_t header_size = (uint8_t)(*buffer++);
461 uint8_t tag_header_size = (uint8_t)(*buffer++);
462 tags->ntags = tags->ntags_alloc = (int)(*buffer++);
463 if (tags->ntags == 0) {
464 tags->ntags_alloc = 0;
465 tags->kvm_size = 0;
466 tags->kvm_used = 0;
467 tags->kvm = NULL;
468 return;
469 }
470 if (header_size != ENCODED_HEADER_SIZE) {
471 GPR_ASSERT(version != ENCODED_VERSION);
472 GPR_ASSERT(ENCODED_HEADER_SIZE < header_size);
473 buffer += (header_size - ENCODED_HEADER_SIZE);
474 }
475 tags->kvm_used = size - header_size;
476 tags->kvm_size = tags->kvm_used + CENSUS_MAX_TAG_KV_LEN;
477 tags->kvm = gpr_malloc(tags->kvm_size);
478 if (tag_header_size != TAG_HEADER_SIZE) {
479 // something new in the tag information. I don't understand it, so
480 // don't copy it over.
481 GPR_ASSERT(version != ENCODED_VERSION);
482 GPR_ASSERT(tag_header_size > TAG_HEADER_SIZE);
483 char *kvp = tags->kvm;
484 for (int i = 0; i < tags->ntags; i++) {
485 memcpy(kvp, buffer, TAG_HEADER_SIZE);
486 kvp += header_size;
487 struct raw_tag raw;
488 buffer =
489 decode_tag(&raw, (char *)buffer, tag_header_size - TAG_HEADER_SIZE);
490 memcpy(kvp, raw.key, (size_t)raw.key_len + raw.value_len);
491 kvp += raw.key_len + raw.value_len;
492 }
493 } else {
494 memcpy(tags->kvm, buffer, tags->kvm_used);
495 }
496 }
497
498 census_context *census_context_decode(const char *buffer, size_t size) {
499 census_context *context = gpr_malloc(sizeof(census_context));
500 memset(&context->tags[LOCAL_TAGS], 0, sizeof(struct tag_set));
501 if (buffer == NULL) {
502 memset(&context->tags[PROPAGATED_TAGS], 0, sizeof(struct tag_set));
503 } else {
504 tag_set_decode(&context->tags[PROPAGATED_TAGS], buffer, size);
505 }
506 memset(&context->status, 0, sizeof(context->status));
507 context->status.n_propagated_tags = context->tags[PROPAGATED_TAGS].ntags;
508 return context;
509 }
OLDNEW
« no previous file with comments | « third_party/grpc/src/core/census/aggregation.h ('k') | third_party/grpc/src/core/census/grpc_context.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698