parent
d5c6eca64b
commit
27f59afecb
29 changed files with 648 additions and 137 deletions
@ -0,0 +1,346 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "src/core/lib/iomgr/error.h" |
||||||
|
|
||||||
|
#include <stdbool.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/avl.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
#include <grpc/support/string_util.h> |
||||||
|
#include <grpc/support/useful.h> |
||||||
|
|
||||||
|
static void destroy_integer(void *key) {} |
||||||
|
|
||||||
|
static void *copy_integer(void *key) { return key; } |
||||||
|
|
||||||
|
static long compare_integers(void *key1, void *key2) { |
||||||
|
return GPR_ICMP((uintptr_t)key1, (uintptr_t)key2); |
||||||
|
} |
||||||
|
|
||||||
|
static void destroy_string(void *str) { gpr_free(str); } |
||||||
|
|
||||||
|
static void *copy_string(void *str) { return gpr_strdup(str); } |
||||||
|
|
||||||
|
static void destroy_err(void *err) { grpc_error_unref(err); } |
||||||
|
|
||||||
|
static void *copy_err(void *err) { return grpc_error_ref(err); } |
||||||
|
|
||||||
|
static const gpr_avl_vtable avl_vtable_ints = {destroy_integer, copy_integer, |
||||||
|
compare_integers, |
||||||
|
destroy_integer, copy_integer}; |
||||||
|
|
||||||
|
static const gpr_avl_vtable avl_vtable_strs = {destroy_integer, copy_integer, |
||||||
|
compare_integers, destroy_string, |
||||||
|
copy_string}; |
||||||
|
|
||||||
|
static const gpr_avl_vtable avl_vtable_errs = { |
||||||
|
destroy_integer, copy_integer, compare_integers, destroy_err, copy_err}; |
||||||
|
|
||||||
|
static const char *error_int_name(grpc_error_ints key) { |
||||||
|
switch (key) { |
||||||
|
case GRPC_ERROR_INT_STATUS_CODE: |
||||||
|
return "status_code"; |
||||||
|
case GRPC_ERROR_INT_ERRNO: |
||||||
|
return "errno"; |
||||||
|
} |
||||||
|
GPR_UNREACHABLE_CODE(return "unknown"); |
||||||
|
} |
||||||
|
|
||||||
|
static const char *error_str_name(grpc_error_strs key) { |
||||||
|
switch (key) { |
||||||
|
case GRPC_ERROR_STR_DESCRIPTION: |
||||||
|
return "description"; |
||||||
|
case GRPC_ERROR_STR_OS_ERROR: |
||||||
|
return "os_error"; |
||||||
|
case GRPC_ERROR_STR_TARGET_ADDRESS: |
||||||
|
return "target_address"; |
||||||
|
case GRPC_ERROR_STR_SYSCALL: |
||||||
|
return "syscall"; |
||||||
|
} |
||||||
|
GPR_UNREACHABLE_CODE(return "unknown"); |
||||||
|
} |
||||||
|
|
||||||
|
struct grpc_error { |
||||||
|
gpr_refcount refs; |
||||||
|
gpr_avl ints; |
||||||
|
gpr_avl strs; |
||||||
|
gpr_avl errs; |
||||||
|
uintptr_t next_err; |
||||||
|
}; |
||||||
|
|
||||||
|
static bool is_special(grpc_error *err) { |
||||||
|
return err == GRPC_ERROR_NONE || err == GRPC_ERROR_OOM; |
||||||
|
} |
||||||
|
|
||||||
|
grpc_error *grpc_error_ref(grpc_error *err) { |
||||||
|
if (is_special(err)) return err; |
||||||
|
gpr_ref(&err->refs); |
||||||
|
return err; |
||||||
|
} |
||||||
|
|
||||||
|
static void error_destroy(grpc_error *err) { |
||||||
|
GPR_ASSERT(!is_special(err)); |
||||||
|
gpr_avl_unref(err->ints); |
||||||
|
gpr_avl_unref(err->strs); |
||||||
|
gpr_avl_unref(err->errs); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_error_unref(grpc_error *err) { |
||||||
|
if (!is_special(err) && gpr_unref(&err->refs)) { |
||||||
|
error_destroy(err); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
grpc_error *grpc_error_create(void) { |
||||||
|
grpc_error *err = gpr_malloc(sizeof(*err)); |
||||||
|
if (err == NULL) { // TODO(ctiller): make gpr_malloc return NULL
|
||||||
|
return GRPC_ERROR_OOM; |
||||||
|
} |
||||||
|
err->ints = gpr_avl_create(&avl_vtable_ints); |
||||||
|
err->strs = gpr_avl_create(&avl_vtable_strs); |
||||||
|
err->errs = gpr_avl_create(&avl_vtable_errs); |
||||||
|
err->next_err = 0; |
||||||
|
gpr_ref_init(&err->refs, 1); |
||||||
|
return err; |
||||||
|
} |
||||||
|
|
||||||
|
static grpc_error *copy_error_and_unref(grpc_error *in) { |
||||||
|
if (is_special(in)) { |
||||||
|
return grpc_error_create(); |
||||||
|
} |
||||||
|
grpc_error *out = gpr_malloc(sizeof(*out)); |
||||||
|
out->ints = gpr_avl_ref(in->ints); |
||||||
|
out->strs = gpr_avl_ref(in->strs); |
||||||
|
out->errs = gpr_avl_ref(in->errs); |
||||||
|
out->next_err = in->next_err; |
||||||
|
gpr_ref_init(&out->refs, 1); |
||||||
|
grpc_error_unref(in); |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
grpc_error *grpc_error_set_int(grpc_error *src, grpc_error_ints which, |
||||||
|
intptr_t value) { |
||||||
|
grpc_error *new = copy_error_and_unref(src); |
||||||
|
new->ints = gpr_avl_add(new->ints, (void *)(uintptr_t)which, (void *)value); |
||||||
|
return new; |
||||||
|
} |
||||||
|
|
||||||
|
grpc_error *grpc_error_set_str(grpc_error *src, grpc_error_strs which, |
||||||
|
const char *value) { |
||||||
|
grpc_error *new = copy_error_and_unref(src); |
||||||
|
new->strs = gpr_avl_add(new->strs, (void *)(uintptr_t)which, (void *)value); |
||||||
|
return new; |
||||||
|
} |
||||||
|
|
||||||
|
grpc_error *grpc_error_add_child(grpc_error *src, grpc_error *child) { |
||||||
|
grpc_error *new = copy_error_and_unref(src); |
||||||
|
new->errs = gpr_avl_add(new->errs, (void *)(new->next_err++), child); |
||||||
|
return new; |
||||||
|
} |
||||||
|
|
||||||
|
static const char *no_error_string = "null"; |
||||||
|
static const char *oom_error_string = "\"Out of memory\""; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
char *key; |
||||||
|
char *value; |
||||||
|
} kv_pair; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
kv_pair *kvs; |
||||||
|
size_t num_kvs; |
||||||
|
size_t cap_kvs; |
||||||
|
} kv_pairs; |
||||||
|
|
||||||
|
static void append_kv(kv_pairs *kvs, char *key, char *value) { |
||||||
|
if (kvs->num_kvs == kvs->cap_kvs) { |
||||||
|
kvs->cap_kvs = GPR_MAX(3 * kvs->cap_kvs / 2, 4); |
||||||
|
kvs->kvs = gpr_realloc(kvs->kvs, sizeof(*kvs->kvs) * kvs->cap_kvs); |
||||||
|
} |
||||||
|
kvs->kvs[kvs->num_kvs].key = key; |
||||||
|
kvs->kvs[kvs->num_kvs].value = value; |
||||||
|
kvs->num_kvs++; |
||||||
|
} |
||||||
|
|
||||||
|
static void collect_kvs(gpr_avl_node *node, char *key(void *k), |
||||||
|
char *fmt(void *v), kv_pairs *kvs) { |
||||||
|
if (node == NULL) return; |
||||||
|
append_kv(kvs, key(node->key), fmt(node->value)); |
||||||
|
collect_kvs(node->left, key, fmt, kvs); |
||||||
|
collect_kvs(node->right, key, fmt, kvs); |
||||||
|
} |
||||||
|
|
||||||
|
static char *key_int(void *p) { |
||||||
|
return gpr_strdup(error_int_name((grpc_error_ints)(uintptr_t)p)); |
||||||
|
} |
||||||
|
|
||||||
|
static char *key_str(void *p) { |
||||||
|
return gpr_strdup(error_str_name((grpc_error_strs)(uintptr_t)p)); |
||||||
|
} |
||||||
|
|
||||||
|
static char *fmt_int(void *p) { |
||||||
|
char *s; |
||||||
|
gpr_asprintf(&s, "%lld", (intptr_t)p); |
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
static void append_chr(char c, char **s, size_t *sz, size_t *cap) { |
||||||
|
if (*sz == *cap) { |
||||||
|
*cap = GPR_MAX(8, 3 * *cap / 2); |
||||||
|
*s = gpr_realloc(*s, *cap); |
||||||
|
} |
||||||
|
(*s)[(*sz)++] = c; |
||||||
|
} |
||||||
|
|
||||||
|
static void append_str(const char *str, char **s, size_t *sz, size_t *cap) { |
||||||
|
for (const char *c = str; *c; c++) { |
||||||
|
append_chr(*c, s, sz, cap); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void append_esc_str(const char *str, char **s, size_t *sz, size_t *cap) { |
||||||
|
static const char *hex = "0123456789abcdef"; |
||||||
|
append_chr('"', s, sz, cap); |
||||||
|
for (const uint8_t *c = (const uint8_t *)str; *c; c++) { |
||||||
|
if (*c < 32 || *c >= 127) { |
||||||
|
append_chr('\\', s, sz, cap); |
||||||
|
switch (*c) { |
||||||
|
case '\b': |
||||||
|
append_chr('b', s, sz, cap); |
||||||
|
break; |
||||||
|
case '\f': |
||||||
|
append_chr('f', s, sz, cap); |
||||||
|
break; |
||||||
|
case '\n': |
||||||
|
append_chr('n', s, sz, cap); |
||||||
|
break; |
||||||
|
case '\r': |
||||||
|
append_chr('r', s, sz, cap); |
||||||
|
break; |
||||||
|
case '\t': |
||||||
|
append_chr('t', s, sz, cap); |
||||||
|
break; |
||||||
|
default: |
||||||
|
append_chr('u', s, sz, cap); |
||||||
|
append_chr('0', s, sz, cap); |
||||||
|
append_chr('0', s, sz, cap); |
||||||
|
append_chr(hex[*c >> 4], s, sz, cap); |
||||||
|
append_chr(hex[*c & 0x0f], s, sz, cap); |
||||||
|
break; |
||||||
|
} |
||||||
|
} else { |
||||||
|
append_chr((char)*c, s, sz, cap); |
||||||
|
} |
||||||
|
} |
||||||
|
append_chr('"', s, sz, cap); |
||||||
|
append_chr(0, s, sz, cap); |
||||||
|
} |
||||||
|
|
||||||
|
static char *fmt_str(void *p) { |
||||||
|
char *s = NULL; |
||||||
|
size_t sz = 0; |
||||||
|
size_t cap = 0; |
||||||
|
append_esc_str(p, &s, &sz, &cap); |
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
static void add_errs(gpr_avl_node *n, char **s, size_t *sz, size_t *cap) { |
||||||
|
if (n == NULL) return; |
||||||
|
add_errs(n->left, s, sz, cap); |
||||||
|
const char *e = grpc_error_string(n->value); |
||||||
|
append_str(e, s, sz, cap); |
||||||
|
grpc_error_free_string(e); |
||||||
|
add_errs(n->right, s, sz, cap); |
||||||
|
} |
||||||
|
|
||||||
|
static char *errs_string(grpc_error *err) { |
||||||
|
char *s = NULL; |
||||||
|
size_t sz = 0; |
||||||
|
size_t cap = 0; |
||||||
|
append_chr('[', &s, &sz, &cap); |
||||||
|
add_errs(err->errs.root, &s, &sz, &cap); |
||||||
|
append_chr(']', &s, &sz, &cap); |
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
static int cmp_kvs(const void *a, const void *b) { |
||||||
|
const kv_pair *ka = a; |
||||||
|
const kv_pair *kb = b; |
||||||
|
return strcmp(ka->key, kb->key); |
||||||
|
} |
||||||
|
|
||||||
|
static const char *finish_kvs(kv_pairs *kvs) { |
||||||
|
char *s = NULL; |
||||||
|
size_t sz = 0; |
||||||
|
size_t cap = 0; |
||||||
|
|
||||||
|
append_chr('{', &s, &sz, &cap); |
||||||
|
for (size_t i = 0; i < kvs->num_kvs; i++) { |
||||||
|
append_esc_str(kvs->kvs[i].key, &s, &sz, &cap); |
||||||
|
gpr_free(kvs->kvs[i].key); |
||||||
|
append_chr(':', &s, &sz, &cap); |
||||||
|
append_str(kvs->kvs[i].value, &s, &sz, &cap); |
||||||
|
gpr_free(kvs->kvs[i].value); |
||||||
|
} |
||||||
|
append_chr('}', &s, &sz, &cap); |
||||||
|
|
||||||
|
gpr_free(kvs->kvs); |
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
const char *grpc_error_string(grpc_error *err) { |
||||||
|
if (err == GRPC_ERROR_NONE) return no_error_string; |
||||||
|
if (err == GRPC_ERROR_OOM) return oom_error_string; |
||||||
|
|
||||||
|
kv_pairs kvs; |
||||||
|
memset(&kvs, 0, sizeof(kvs)); |
||||||
|
|
||||||
|
collect_kvs(err->ints.root, key_int, fmt_int, &kvs); |
||||||
|
collect_kvs(err->strs.root, key_str, fmt_str, &kvs); |
||||||
|
append_kv(&kvs, gpr_strdup("referenced_errors"), errs_string(err)); |
||||||
|
|
||||||
|
qsort(kvs.kvs, kvs.num_kvs, sizeof(kv_pair), cmp_kvs); |
||||||
|
|
||||||
|
return finish_kvs(&kvs); |
||||||
|
} |
||||||
|
|
||||||
|
grpc_error *grpc_os_error(int err, const char *call_name) { |
||||||
|
return grpc_error_set_str( |
||||||
|
grpc_error_set_str( |
||||||
|
grpc_error_set_int(grpc_error_create(), GRPC_ERROR_INT_ERRNO, err), |
||||||
|
GRPC_ERROR_STR_OS_ERROR, strerror(err)), |
||||||
|
GRPC_ERROR_STR_SYSCALL, call_name); |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef GRPC_CORE_LIB_IOMGR_ERROR_H |
||||||
|
#define GRPC_CORE_LIB_IOMGR_ERROR_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
typedef struct grpc_error grpc_error; |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
GRPC_ERROR_INT_STATUS_CODE, |
||||||
|
GRPC_ERROR_INT_ERRNO |
||||||
|
} grpc_error_ints; |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
GRPC_ERROR_STR_DESCRIPTION, |
||||||
|
GRPC_ERROR_STR_TARGET_ADDRESS, |
||||||
|
GRPC_ERROR_STR_OS_ERROR, |
||||||
|
GRPC_ERROR_STR_SYSCALL |
||||||
|
} grpc_error_strs; |
||||||
|
|
||||||
|
#define GRPC_ERROR_NONE ((grpc_error *)NULL) |
||||||
|
#define GRPC_ERROR_OOM ((grpc_error *)1) |
||||||
|
|
||||||
|
const char *grpc_error_string(grpc_error *error); |
||||||
|
void grpc_error_free_string(const char *str); |
||||||
|
|
||||||
|
grpc_error *grpc_error_create(void); |
||||||
|
grpc_error *grpc_error_ref(grpc_error *err); |
||||||
|
void grpc_error_unref(grpc_error *err); |
||||||
|
grpc_error *grpc_error_set_int(grpc_error *src, grpc_error_ints which, |
||||||
|
intptr_t value); |
||||||
|
grpc_error *grpc_error_set_str(grpc_error *src, grpc_error_strs which, |
||||||
|
const char *value); |
||||||
|
grpc_error *grpc_error_add_child(grpc_error *src, grpc_error *child); |
||||||
|
grpc_error *grpc_os_error(int err, const char *call_name); |
||||||
|
|
||||||
|
#endif /* GRPC_CORE_LIB_IOMGR_ERROR_H */ |
Loading…
Reference in new issue