| /******************************************************************************* |
| * Copyright (c) 2007, 2012 Wind River Systems, Inc. and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * and Eclipse Distribution License v1.0 which accompany this distribution. |
| * The Eclipse Public License is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * and the Eclipse Distribution License is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * You may elect to redistribute this code under either of these licenses. |
| * |
| * Contributors: |
| * Wind River Systems - initial API and implementation |
| *******************************************************************************/ |
| |
| /* |
| * Symbols service - proxy implementation, gets symbols information from host. |
| */ |
| |
| /* TODO: need to cleanup symbols cache from data that not used for long time */ |
| |
| #include <tcf/config.h> |
| |
| #if ENABLE_SymbolsProxy |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <tcf/framework/context.h> |
| #include <tcf/framework/cache.h> |
| #include <tcf/framework/json.h> |
| #include <tcf/framework/events.h> |
| #include <tcf/framework/myalloc.h> |
| #include <tcf/framework/exceptions.h> |
| #include <tcf/services/stacktrace.h> |
| #include <tcf/services/symbols.h> |
| #include <tcf/services/vm.h> |
| #if ENABLE_RCBP_TEST |
| # include <tcf/main/test.h> |
| #endif |
| |
| #define HASH_SIZE (4 * MEM_USAGE_FACTOR - 1) |
| |
| #define ACC_SIZE 1 |
| #define ACC_LENGTH 2 |
| #define ACC_OTHER 3 |
| |
| /* Symbols cahce, one per channel */ |
| typedef struct SymbolsCache { |
| Channel * channel; |
| LINK link_root; |
| LINK link_sym[HASH_SIZE]; |
| LINK link_find_by_name[HASH_SIZE]; |
| LINK link_find_by_addr[HASH_SIZE]; |
| LINK link_find_in_scope[HASH_SIZE]; |
| LINK link_list[HASH_SIZE]; |
| LINK link_frame[HASH_SIZE]; |
| LINK link_location[HASH_SIZE]; |
| int service_available; |
| } SymbolsCache; |
| |
| /* Symbol properties cache */ |
| typedef struct SymInfoCache { |
| unsigned magic; |
| LINK link_syms; |
| AbstractCache cache; |
| char * id; |
| char * type_id; |
| char * base_type_id; |
| char * index_type_id; |
| char * container_id; |
| char * name; |
| Context * update_owner; |
| int update_policy; |
| int degraded; |
| int sym_class; |
| int type_class; |
| int has_size; |
| int has_length; |
| int has_lower_bound; |
| int frame; |
| SYM_FLAGS flags; |
| ContextAddress size; |
| ContextAddress length; |
| int64_t lower_bound; |
| char ** children_ids; |
| int children_count; |
| ReplyHandlerInfo * pending_get_context; |
| ReplyHandlerInfo * pending_get_children; |
| ErrorReport * error_get_context; |
| ErrorReport * error_get_children; |
| int done_context; |
| int done_children; |
| LINK array_syms; |
| int disposed; |
| } SymInfoCache; |
| |
| /* Cached result of get_array_symbol() */ |
| typedef struct ArraySymCache { |
| LINK link_sym; |
| AbstractCache cache; |
| ContextAddress length; |
| ReplyHandlerInfo * pending; |
| ErrorReport * error; |
| char * id; |
| int disposed; |
| } ArraySymCache; |
| |
| /* Cached result of find_symbol_by_name(), find_symbol_in_scope(), find_symbol_by_addr(), enumerate_symbols() */ |
| typedef struct FindSymCache { |
| LINK link_syms; |
| AbstractCache cache; |
| ReplyHandlerInfo * pending; |
| ErrorReport * error; |
| int update_policy; |
| Context * ctx; |
| int frame; |
| uint64_t ip; |
| uint64_t addr; |
| char * scope; |
| char * name; |
| char ** id_buf; |
| int id_cnt; |
| int disposed; |
| } FindSymCache; |
| |
| typedef struct StackFrameCache { |
| LINK link_syms; |
| AbstractCache cache; |
| ReplyHandlerInfo * pending; |
| ErrorReport * error; |
| Context * ctx; |
| uint64_t ip; |
| uint64_t address; |
| uint64_t size; |
| |
| StackFrameRegisterLocation * fp; |
| StackFrameRegisterLocation ** regs; |
| int regs_cnt; |
| |
| int disposed; |
| } StackFrameCache; |
| |
| typedef struct LocationInfoCache { |
| LINK link_syms; |
| AbstractCache cache; |
| ReplyHandlerInfo * pending; |
| ErrorReport * error; |
| char * sym_id; |
| Context * ctx; |
| uint64_t ip; |
| |
| LocationInfo info; |
| |
| int disposed; |
| } LocationInfoCache; |
| |
| #define SYM_CACHE_MAGIC 0x38254865 |
| |
| #define root2syms(A) ((SymbolsCache *)((char *)(A) - offsetof(SymbolsCache, link_root))) |
| #define syms2sym(A) ((SymInfoCache *)((char *)(A) - offsetof(SymInfoCache, link_syms))) |
| #define syms2find(A) ((FindSymCache *)((char *)(A) - offsetof(FindSymCache, link_syms))) |
| #define sym2arr(A) ((ArraySymCache *)((char *)(A) - offsetof(ArraySymCache, link_sym))) |
| #define syms2frame(A)((StackFrameCache *)((char *)(A) - offsetof(StackFrameCache, link_syms))) |
| #define syms2location(A)((LocationInfoCache *)((char *)(A) - offsetof(LocationInfoCache, link_syms))) |
| |
| struct Symbol { |
| unsigned magic; |
| SymInfoCache * cache; |
| }; |
| |
| static LINK root = TCF_LIST_INIT(root); |
| |
| static char ** find_next_buf = NULL; |
| static int find_next_pos = 0; |
| static int find_next_cnt = 0; |
| |
| static const char * SYMBOLS = "Symbols"; |
| |
| #define SYMBOL_MAGIC 0x34875234 |
| |
| static Symbol * alloc_symbol(void) { |
| Symbol * s = (Symbol *)tmp_alloc_zero(sizeof(Symbol)); |
| s->magic = SYMBOL_MAGIC; |
| return s; |
| } |
| |
| static unsigned hash_sym_id(const char * id) { |
| int i; |
| unsigned h = 0; |
| for (i = 0; id[i]; i++) h += id[i]; |
| return h % HASH_SIZE; |
| } |
| |
| static unsigned hash_find(Context * ctx, const char * name, uint64_t ip) { |
| int i; |
| unsigned h = 0; |
| if (name != NULL) for (i = 0; name[i]; i++) h += name[i]; |
| return (h + ((uintptr_t)ctx >> 4) + (unsigned)ip) % HASH_SIZE; |
| } |
| |
| static unsigned hash_list(Context * ctx, uint64_t ip) { |
| return (((uintptr_t)ctx >> 4) + (unsigned)ip) % HASH_SIZE; |
| } |
| |
| static unsigned hash_frame(Context * ctx) { |
| return ((uintptr_t)ctx >> 4) % HASH_SIZE; |
| } |
| |
| static SymbolsCache * get_symbols_cache(void) { |
| LINK * l = NULL; |
| SymbolsCache * syms = NULL; |
| Channel * c = cache_channel(); |
| if (c == NULL) str_exception(ERR_OTHER, "get_symbols_cache(): illegal cache access"); |
| for (l = root.next; l != &root; l = l->next) { |
| SymbolsCache * x = root2syms(l); |
| if (x->channel == c) { |
| syms = x; |
| break; |
| } |
| } |
| if (syms == NULL) { |
| int i = 0; |
| syms = (SymbolsCache *)loc_alloc_zero(sizeof(SymbolsCache)); |
| syms->channel = c; |
| list_add_first(&syms->link_root, &root); |
| for (i = 0; i < HASH_SIZE; i++) { |
| list_init(syms->link_sym + i); |
| list_init(syms->link_find_by_name + i); |
| list_init(syms->link_find_by_addr + i); |
| list_init(syms->link_find_in_scope + i); |
| list_init(syms->link_list + i); |
| list_init(syms->link_frame + i); |
| list_init(syms->link_location + i); |
| } |
| channel_lock(c); |
| for (i = 0; i < c->peer_service_cnt; i++) { |
| if (strcmp(c->peer_service_list[i], SYMBOLS) == 0) syms->service_available = 1; |
| } |
| } |
| return syms; |
| } |
| |
| static void free_arr_sym_cache(ArraySymCache * a) { |
| list_remove(&a->link_sym); |
| a->disposed = 1; |
| if (a->pending == NULL) { |
| cache_dispose(&a->cache); |
| release_error_report(a->error); |
| loc_free(a->id); |
| loc_free(a); |
| } |
| } |
| |
| static void free_sym_info_cache(SymInfoCache * c) { |
| assert(c->magic == SYM_CACHE_MAGIC); |
| list_remove(&c->link_syms); |
| c->disposed = 1; |
| if (c->pending_get_context == NULL && c->pending_get_children == NULL) { |
| c->magic = 0; |
| cache_dispose(&c->cache); |
| loc_free(c->id); |
| loc_free(c->type_id); |
| loc_free(c->base_type_id); |
| loc_free(c->index_type_id); |
| loc_free(c->container_id); |
| loc_free(c->name); |
| loc_free(c->children_ids); |
| if (c->update_owner != NULL) context_unlock(c->update_owner); |
| release_error_report(c->error_get_context); |
| release_error_report(c->error_get_children); |
| while (!list_is_empty(&c->array_syms)) { |
| free_arr_sym_cache(sym2arr(c->array_syms.next)); |
| } |
| loc_free(c); |
| } |
| } |
| |
| static void free_find_sym_cache(FindSymCache * c) { |
| list_remove(&c->link_syms); |
| c->disposed = 1; |
| if (c->pending == NULL) { |
| if (find_next_buf == c->id_buf) { |
| find_next_buf = NULL; |
| find_next_pos = 0; |
| find_next_cnt = 0; |
| } |
| cache_dispose(&c->cache); |
| release_error_report(c->error); |
| context_unlock(c->ctx); |
| loc_free(c->scope); |
| loc_free(c->name); |
| loc_free(c->id_buf); |
| loc_free(c); |
| } |
| } |
| |
| static void free_location_command_args(LocationExpressionCommand * cmd) { |
| if (cmd->cmd == SFT_CMD_LOCATION) loc_free(cmd->args.loc.code_addr); |
| else if (cmd->cmd == SFT_CMD_PIECE) loc_free(cmd->args.piece.value); |
| } |
| |
| static void free_sft_sequence(StackFrameRegisterLocation * seq) { |
| if (seq != NULL) { |
| unsigned i = 0; |
| while (i < seq->cmds_cnt) free_location_command_args(seq->cmds + i++); |
| loc_free(seq); |
| } |
| } |
| |
| static void free_stack_frame_cache(StackFrameCache * c) { |
| list_remove(&c->link_syms); |
| c->disposed = 1; |
| if (c->pending == NULL) { |
| int i; |
| cache_dispose(&c->cache); |
| release_error_report(c->error); |
| context_unlock(c->ctx); |
| for (i = 0; i < c->regs_cnt; i++) free_sft_sequence(c->regs[i]); |
| free_sft_sequence(c->fp); |
| loc_free(c->regs); |
| loc_free(c); |
| } |
| } |
| |
| static void free_location_commands(LocationCommands * cmds) { |
| unsigned i = 0; |
| while (i < cmds->cnt) free_location_command_args(cmds->cmds + i++); |
| loc_free(cmds->cmds); |
| } |
| |
| static void free_location_info_cache(LocationInfoCache * c) { |
| list_remove(&c->link_syms); |
| c->disposed = 1; |
| if (c->pending == NULL) { |
| cache_dispose(&c->cache); |
| release_error_report(c->error); |
| context_unlock(c->ctx); |
| loc_free(c->sym_id); |
| free_location_commands(&c->info.value_cmds); |
| loc_free(c); |
| } |
| } |
| |
| static void free_symbols_cache(SymbolsCache * syms) { |
| int i; |
| for (i = 0; i < HASH_SIZE; i++) { |
| while (!list_is_empty(syms->link_sym + i)) { |
| free_sym_info_cache(syms2sym(syms->link_sym[i].next)); |
| } |
| while (!list_is_empty(syms->link_find_by_name + i)) { |
| free_find_sym_cache(syms2find(syms->link_find_by_name[i].next)); |
| } |
| while (!list_is_empty(syms->link_find_by_addr + i)) { |
| free_find_sym_cache(syms2find(syms->link_find_by_addr[i].next)); |
| } |
| while (!list_is_empty(syms->link_find_in_scope + i)) { |
| free_find_sym_cache(syms2find(syms->link_find_in_scope[i].next)); |
| } |
| while (!list_is_empty(syms->link_list + i)) { |
| free_find_sym_cache(syms2find(syms->link_list[i].next)); |
| } |
| while (!list_is_empty(syms->link_frame + i)) { |
| free_stack_frame_cache(syms2frame(syms->link_frame[i].next)); |
| } |
| } |
| channel_unlock(syms->channel); |
| list_remove(&syms->link_root); |
| loc_free(syms); |
| } |
| |
| static Channel * get_channel(SymbolsCache * syms) { |
| if (is_channel_closed(syms->channel)) exception(ERR_CHANNEL_CLOSED); |
| if (!syms->service_available) str_exception(ERR_SYM_NOT_FOUND, "Symbols service not available"); |
| return syms->channel; |
| } |
| |
| static void read_context_data(InputStream * inp, const char * name, void * args) { |
| char id[256]; |
| SymInfoCache * s = (SymInfoCache *)args; |
| if (strcmp(name, "ID") == 0) { json_read_string(inp, id, sizeof(id)); assert(strcmp(id, s->id) == 0); } |
| else if (strcmp(name, "OwnerID") == 0) { json_read_string(inp, id, sizeof(id)); s->update_owner = id2ctx(id); } |
| else if (strcmp(name, "Name") == 0) s->name = json_read_alloc_string(inp); |
| else if (strcmp(name, "UpdatePolicy") == 0) s->update_policy = json_read_long(inp); |
| else if (strcmp(name, "Class") == 0) s->sym_class = json_read_long(inp); |
| else if (strcmp(name, "TypeClass") == 0) s->type_class = json_read_long(inp); |
| else if (strcmp(name, "TypeID") == 0) s->type_id = json_read_alloc_string(inp); |
| else if (strcmp(name, "BaseTypeID") == 0) s->base_type_id = json_read_alloc_string(inp); |
| else if (strcmp(name, "IndexTypeID") == 0) s->index_type_id = json_read_alloc_string(inp); |
| else if (strcmp(name, "ContainerID") == 0) s->container_id = json_read_alloc_string(inp); |
| else if (strcmp(name, "Size") == 0) { s->size = json_read_long(inp); s->has_size = 1; } |
| else if (strcmp(name, "Length") == 0) { s->length = json_read_long(inp); s->has_length = 1; } |
| else if (strcmp(name, "LowerBound") == 0) { s->lower_bound = json_read_int64(inp); s->has_lower_bound = 1; } |
| else if (strcmp(name, "Flags") == 0) s->flags = json_read_ulong(inp); |
| else if (strcmp(name, "Frame") == 0) s->frame = (int)json_read_long(inp); |
| else json_skip_object(inp); |
| } |
| |
| static void validate_context(Channel * c, void * args, int error) { |
| Trap trap; |
| SymInfoCache * s = (SymInfoCache *)args; |
| assert(s->pending_get_context != NULL); |
| assert(s->error_get_context == NULL); |
| assert(s->update_owner == NULL); |
| assert(!s->done_context); |
| assert(!s->degraded); |
| if (set_trap(&trap)) { |
| s->pending_get_context = NULL; |
| s->done_context = 1; |
| if (!error) { |
| error = read_errno(&c->inp); |
| json_read_struct(&c->inp, read_context_data, s); |
| if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); |
| if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX); |
| if (!error && s->update_owner == NULL) error = ERR_INV_CONTEXT; |
| if (!error && s->update_owner->exited) error = ERR_ALREADY_EXITED; |
| } |
| clear_trap(&trap); |
| if (s->update_owner != NULL) context_lock(s->update_owner); |
| } |
| else { |
| error = trap.error; |
| s->update_owner = NULL; |
| } |
| s->error_get_context = get_error_report(error); |
| cache_notify(&s->cache); |
| if (s->disposed) free_sym_info_cache(s); |
| } |
| |
| static SymInfoCache * get_sym_info_cache(const Symbol * sym, int acc_mode) { |
| Trap trap; |
| SymInfoCache * s = sym->cache; |
| assert(sym->magic == SYMBOL_MAGIC); |
| assert(s->magic == SYM_CACHE_MAGIC); |
| assert(s->id != NULL); |
| if (!set_trap(&trap)) return NULL; |
| if (s->pending_get_context != NULL) { |
| cache_wait(&s->cache); |
| } |
| if (s->error_get_context != NULL) { |
| exception(set_error_report_errno(s->error_get_context)); |
| } |
| if (s->done_context && s->degraded) { |
| /* Symbol info is partially outdated */ |
| int update = 0; |
| assert(s->update_owner != NULL); |
| assert(context_has_state(s->update_owner)); |
| switch (acc_mode) { |
| case ACC_SIZE: |
| case ACC_LENGTH: |
| if (s->type_class != TYPE_CLASS_ARRAY) break; |
| update = 1; |
| break; |
| } |
| if (update) { |
| if (!s->update_owner->stopped) exception(ERR_IS_RUNNING); |
| s->degraded = 0; |
| s->done_context = 0; |
| s->has_size = 0; |
| s->has_length = 0; |
| s->has_lower_bound = 0; |
| context_unlock(s->update_owner); |
| loc_free(s->type_id); |
| loc_free(s->base_type_id); |
| loc_free(s->index_type_id); |
| loc_free(s->container_id); |
| loc_free(s->name); |
| s->update_owner = NULL; |
| s->type_id = NULL; |
| s->base_type_id = NULL; |
| s->index_type_id = NULL; |
| s->container_id = NULL; |
| s->name = NULL; |
| } |
| } |
| if (!s->done_context) { |
| Channel * c = cache_channel(); |
| if (c == NULL || is_channel_closed(c)) exception(ERR_SYM_NOT_FOUND); |
| s->pending_get_context = protocol_send_command(c, SYMBOLS, "getContext", validate_context, s); |
| json_write_string(&c->out, s->id); |
| write_stream(&c->out, 0); |
| write_stream(&c->out, MARKER_EOM); |
| cache_wait(&s->cache); |
| } |
| clear_trap(&trap); |
| return s; |
| } |
| |
| static char ** string_to_symbol_list(char * id, int * cnt) { |
| if (id[0]) { |
| char ** buf = (char **)loc_alloc_zero(sizeof(char *) * 2 + strlen(id) + 1); |
| buf[0] = (char *)(buf + 2); |
| strcpy(buf[0], id); |
| *cnt = 1; |
| return buf; |
| } |
| *cnt = 0; |
| return NULL; |
| } |
| |
| static char ** read_symbol_list(InputStream * inp, int * id_cnt) { |
| char id[256]; |
| if (peek_stream(inp) == '[') return json_read_alloc_string_array(inp, id_cnt); |
| json_read_string(inp, id, sizeof(id)); |
| return string_to_symbol_list(id, id_cnt); |
| } |
| |
| static void validate_find(Channel * c, void * args, int error) { |
| Trap trap; |
| FindSymCache * f = (FindSymCache *)args; |
| assert(f->pending != NULL); |
| assert(f->error == NULL); |
| if (set_trap(&trap)) { |
| f->pending = NULL; |
| if (!error) { |
| error = read_errno(&c->inp); |
| f->id_buf = read_symbol_list(&c->inp, &f->id_cnt); |
| if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); |
| if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX); |
| } |
| clear_trap(&trap); |
| } |
| else { |
| error = trap.error; |
| } |
| f->error = get_error_report(error); |
| cache_notify(&f->cache); |
| if (f->disposed) free_find_sym_cache(f); |
| } |
| |
| int find_symbol_by_name(Context * ctx, int frame, ContextAddress addr, const char * name, Symbol ** sym) { |
| uint64_t ip = 0; |
| LINK * l = NULL; |
| SymbolsCache * syms = NULL; |
| FindSymCache * f = NULL; |
| unsigned h; |
| Trap trap; |
| |
| if (!set_trap(&trap)) return -1; |
| |
| if (frame == STACK_NO_FRAME) { |
| ctx = context_get_group(ctx, CONTEXT_GROUP_SYMBOLS); |
| ip = addr; |
| } |
| else { |
| StackFrame * info = NULL; |
| if (frame == STACK_TOP_FRAME && (frame = get_top_frame(ctx)) < 0) exception(errno);; |
| if (get_frame_info(ctx, frame, &info) < 0) exception(errno); |
| if (read_reg_value(info, get_PC_definition(ctx), &ip) < 0) exception(errno); |
| } |
| |
| h = hash_find(ctx, name, ip); |
| syms = get_symbols_cache(); |
| for (l = syms->link_find_by_name[h].next; l != syms->link_find_by_name + h; l = l->next) { |
| FindSymCache * c = syms2find(l); |
| if (c->ctx == ctx && c->frame == frame && c->ip == ip && strcmp(c->name, name) == 0) { |
| f = c; |
| break; |
| } |
| } |
| |
| #if ENABLE_RCBP_TEST |
| if (f == NULL && !syms->service_available) { |
| void * address = NULL; |
| int sym_class = 0; |
| if (find_test_symbol(ctx, name, &address, &sym_class) >= 0) { |
| char bf[256]; |
| if (f == NULL) { |
| f = (FindSymCache *)loc_alloc_zero(sizeof(FindSymCache)); |
| list_add_first(&f->link_syms, syms->link_find_by_name + h); |
| context_lock(f->ctx = ctx); |
| f->name = loc_strdup(name); |
| f->ip = ip; |
| } |
| else { |
| release_error_report(f->error); |
| f->error = NULL; |
| loc_free(f->id_buf); |
| f->id_cnt = 0; |
| } |
| f->update_policy = UPDATE_ON_MEMORY_MAP_CHANGES; |
| snprintf(bf, sizeof(bf), "@T.%X.%"PRIX64".%s", sym_class, |
| (uint64_t)(uintptr_t)address, context_get_group(ctx, CONTEXT_GROUP_SYMBOLS)->id); |
| f->id_buf = string_to_symbol_list(bf, &f->id_cnt); |
| } |
| } |
| #endif |
| |
| if (f == NULL) { |
| Channel * c = get_channel(syms); |
| f = (FindSymCache *)loc_alloc_zero(sizeof(FindSymCache)); |
| list_add_first(&f->link_syms, syms->link_find_by_name + h); |
| context_lock(f->ctx = ctx); |
| f->frame = frame; |
| f->ip = ip; |
| f->name = loc_strdup(name); |
| f->update_policy = UPDATE_ON_MEMORY_MAP_CHANGES; |
| f->pending = protocol_send_command(c, SYMBOLS, "findByName", validate_find, f); |
| if (frame != STACK_NO_FRAME) { |
| json_write_string(&c->out, frame2id(ctx, frame)); |
| } |
| else { |
| json_write_string(&c->out, ctx->id); |
| } |
| write_stream(&c->out, 0); |
| json_write_uint64(&c->out, ip); |
| write_stream(&c->out, 0); |
| json_write_string(&c->out, name); |
| write_stream(&c->out, 0); |
| write_stream(&c->out, MARKER_EOM); |
| cache_wait(&f->cache); |
| } |
| else if (f->pending != NULL) { |
| cache_wait(&f->cache); |
| } |
| else if (f->error != NULL) { |
| char msg[256]; |
| snprintf(msg, sizeof(msg), "Symbol '%s' not found", name); |
| exception(set_errno(set_error_report_errno(f->error), msg)); |
| } |
| else if (id2symbol(f->id_buf[0], sym) < 0) { |
| exception(errno); |
| } |
| else { |
| find_next_buf = f->id_buf; |
| find_next_cnt = f->id_cnt; |
| find_next_pos = 1; |
| } |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| int find_symbol_by_addr(Context * ctx, int frame, ContextAddress addr, Symbol ** sym) { |
| uint64_t ip = 0; |
| LINK * l = NULL; |
| SymbolsCache * syms = NULL; |
| FindSymCache * f = NULL; |
| unsigned h; |
| Trap trap; |
| |
| if (!set_trap(&trap)) return -1; |
| |
| if (frame == STACK_NO_FRAME) { |
| ctx = context_get_group(ctx, CONTEXT_GROUP_SYMBOLS); |
| ip = addr; |
| } |
| else { |
| StackFrame * info = NULL; |
| if (frame == STACK_TOP_FRAME && (frame = get_top_frame(ctx)) < 0) exception(errno);; |
| if (get_frame_info(ctx, frame, &info) < 0) exception(errno); |
| if (read_reg_value(info, get_PC_definition(ctx), &ip) < 0) exception(errno); |
| } |
| |
| h = hash_find(ctx, NULL, ip); |
| syms = get_symbols_cache(); |
| for (l = syms->link_find_by_addr[h].next; l != syms->link_find_by_addr + h; l = l->next) { |
| FindSymCache * c = syms2find(l); |
| if (c->ctx == ctx && c->frame == frame && c->ip == ip && c->addr == addr) { |
| f = c; |
| break; |
| } |
| } |
| |
| if (f == NULL) { |
| Channel * c = get_channel(syms); |
| f = (FindSymCache *)loc_alloc_zero(sizeof(FindSymCache)); |
| list_add_first(&f->link_syms, syms->link_find_by_addr + h); |
| context_lock(f->ctx = ctx); |
| f->frame = frame; |
| f->ip = ip; |
| f->addr = addr; |
| f->update_policy = ip ? UPDATE_ON_EXE_STATE_CHANGES : UPDATE_ON_MEMORY_MAP_CHANGES; |
| f->pending = protocol_send_command(c, SYMBOLS, "findByAddr", validate_find, f); |
| if (frame != STACK_NO_FRAME) { |
| json_write_string(&c->out, frame2id(ctx, frame)); |
| } |
| else { |
| json_write_string(&c->out, ctx->id); |
| } |
| write_stream(&c->out, 0); |
| json_write_uint64(&c->out, addr); |
| write_stream(&c->out, 0); |
| write_stream(&c->out, MARKER_EOM); |
| cache_wait(&f->cache); |
| } |
| else if (f->pending != NULL) { |
| cache_wait(&f->cache); |
| } |
| else if (f->error != NULL) { |
| exception(set_error_report_errno(f->error)); |
| } |
| else if (id2symbol(f->id_buf[0], sym) < 0) { |
| exception(errno); |
| } |
| else { |
| find_next_buf = f->id_buf; |
| find_next_cnt = f->id_cnt; |
| find_next_pos = 1; |
| } |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| int find_symbol_in_scope(Context * ctx, int frame, ContextAddress addr, Symbol * scope, const char * name, Symbol ** sym) { |
| uint64_t ip = 0; |
| LINK * l = NULL; |
| SymbolsCache * syms = NULL; |
| FindSymCache * f = NULL; |
| unsigned h; |
| Trap trap; |
| |
| if (!set_trap(&trap)) return -1; |
| |
| if (frame == STACK_NO_FRAME) { |
| ctx = context_get_group(ctx, CONTEXT_GROUP_SYMBOLS); |
| ip = addr; |
| } |
| else { |
| StackFrame * info = NULL; |
| if (frame == STACK_TOP_FRAME && (frame = get_top_frame(ctx)) < 0) exception(errno);; |
| if (get_frame_info(ctx, frame, &info) < 0) exception(errno); |
| if (read_reg_value(info, get_PC_definition(ctx), &ip) < 0) exception(errno); |
| } |
| |
| h = hash_find(ctx, name, ip); |
| syms = get_symbols_cache(); |
| for (l = syms->link_find_in_scope[h].next; l != syms->link_find_in_scope + h; l = l->next) { |
| FindSymCache * c = syms2find(l); |
| if (c->ctx == ctx && c->frame == frame && c->ip == ip && strcmp(c->name, name) == 0) { |
| if (scope == NULL && c->scope == NULL) { |
| f = c; |
| break; |
| } |
| if (scope == NULL || c->scope == NULL) continue; |
| if (strcmp(scope->cache->id, c->scope) == 0) { |
| f = c; |
| break; |
| } |
| } |
| } |
| |
| if (f == NULL) { |
| Channel * c = get_channel(syms); |
| f = (FindSymCache *)loc_alloc_zero(sizeof(FindSymCache)); |
| list_add_first(&f->link_syms, syms->link_find_in_scope + h); |
| context_lock(f->ctx = ctx); |
| f->frame = frame; |
| f->ip = ip; |
| if (scope != NULL) f->scope = loc_strdup(scope->cache->id); |
| f->name = loc_strdup(name); |
| f->update_policy = UPDATE_ON_MEMORY_MAP_CHANGES; |
| f->pending = protocol_send_command(c, SYMBOLS, "findInScope", validate_find, f); |
| if (frame != STACK_NO_FRAME) { |
| json_write_string(&c->out, frame2id(ctx, frame)); |
| } |
| else { |
| json_write_string(&c->out, ctx->id); |
| } |
| write_stream(&c->out, 0); |
| json_write_uint64(&c->out, ip); |
| write_stream(&c->out, 0); |
| json_write_string(&c->out, scope ? scope->cache->id : NULL); |
| write_stream(&c->out, 0); |
| json_write_string(&c->out, name); |
| write_stream(&c->out, 0); |
| write_stream(&c->out, MARKER_EOM); |
| cache_wait(&f->cache); |
| } |
| else if (f->pending != NULL) { |
| cache_wait(&f->cache); |
| } |
| else if (f->error != NULL) { |
| char msg[256]; |
| snprintf(msg, sizeof(msg), "Symbol '%s' not found", name); |
| exception(set_errno(set_error_report_errno(f->error), msg)); |
| } |
| else if (id2symbol(f->id_buf[0], sym) < 0) { |
| exception(errno); |
| } |
| else { |
| find_next_buf = f->id_buf; |
| find_next_cnt = f->id_cnt; |
| find_next_pos = 1; |
| } |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| int find_next_symbol(Symbol ** sym) { |
| if (find_next_buf != NULL && find_next_pos < find_next_cnt) { |
| if (id2symbol(find_next_buf[find_next_pos], sym) < 0) return -1; |
| find_next_pos++; |
| return 0; |
| } |
| errno = ERR_SYM_NOT_FOUND; |
| return -1; |
| } |
| |
| int enumerate_symbols(Context * ctx, int frame, EnumerateSymbolsCallBack * func, void * args) { |
| uint64_t ip = 0; |
| unsigned h; |
| LINK * l; |
| Trap trap; |
| SymbolsCache * syms = NULL; |
| FindSymCache * f = NULL; |
| |
| if (!set_trap(&trap)) return -1; |
| |
| if (frame == STACK_NO_FRAME) { |
| ctx = context_get_group(ctx, CONTEXT_GROUP_SYMBOLS); |
| } |
| else { |
| StackFrame * info = NULL; |
| if (frame == STACK_TOP_FRAME && (frame = get_top_frame(ctx)) < 0) exception(errno);; |
| if (get_frame_info(ctx, frame, &info) < 0) exception(errno); |
| if (read_reg_value(info, get_PC_definition(ctx), &ip) < 0) exception(errno); |
| } |
| |
| h = hash_list(ctx, ip); |
| syms = get_symbols_cache(); |
| for (l = syms->link_list[h].next; l != syms->link_list + h; l = l->next) { |
| FindSymCache * c = syms2find(l); |
| if (c->ctx == ctx && c->frame == frame && c->ip == ip) { |
| f = c; |
| break; |
| } |
| } |
| |
| if (f == NULL) { |
| Channel * c = get_channel(syms); |
| f = (FindSymCache *)loc_alloc_zero(sizeof(FindSymCache)); |
| list_add_first(&f->link_syms, syms->link_list + h); |
| context_lock(f->ctx = ctx); |
| f->frame = frame; |
| f->ip = ip; |
| f->update_policy = UPDATE_ON_MEMORY_MAP_CHANGES; |
| f->pending = protocol_send_command(c, SYMBOLS, "list", validate_find, f); |
| if (frame != STACK_NO_FRAME) { |
| json_write_string(&c->out, frame2id(ctx, frame)); |
| } |
| else { |
| json_write_string(&c->out, ctx->id); |
| } |
| write_stream(&c->out, 0); |
| write_stream(&c->out, MARKER_EOM); |
| cache_wait(&f->cache); |
| } |
| else if (f->pending != NULL) { |
| cache_wait(&f->cache); |
| } |
| else if (f->error != NULL) { |
| exception(set_error_report_errno(f->error)); |
| } |
| else { |
| int i; |
| for (i = 0; i < f->id_cnt; i++) { |
| Symbol * sym = NULL; |
| if (id2symbol(f->id_buf[i], &sym) < 0) exception(errno); |
| func(args, sym); |
| } |
| } |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| const char * symbol2id(const Symbol * sym) { |
| SymInfoCache * s = sym->cache; |
| assert(s->magic == SYM_CACHE_MAGIC); |
| assert(s->id != NULL); |
| return s->id; |
| } |
| |
| int id2symbol(const char * id, Symbol ** sym) { |
| LINK * l; |
| SymInfoCache * s = NULL; |
| unsigned h = hash_sym_id(id); |
| SymbolsCache * syms = get_symbols_cache(); |
| |
| for (l = syms->link_sym[h].next; l != syms->link_sym + h; l = l->next) { |
| SymInfoCache * x = syms2sym(l); |
| if (strcmp(x->id, id) == 0) { |
| s = x; |
| break; |
| } |
| } |
| if (s == NULL) { |
| s = (SymInfoCache *)loc_alloc_zero(sizeof(SymInfoCache)); |
| s->magic = SYM_CACHE_MAGIC; |
| s->id = loc_strdup(id); |
| s->frame = STACK_NO_FRAME; |
| list_add_first(&s->link_syms, syms->link_sym + h); |
| list_init(&s->array_syms); |
| #if ENABLE_RCBP_TEST |
| if (strncmp(id, "@T.", 3) == 0) { |
| int sym_class = 0; |
| uint64_t address = 0; |
| char ctx_id[256]; |
| if (sscanf(id, "@T.%X.%"SCNx64".%255s", &sym_class, &address, ctx_id) == 3) { |
| s->done_context = 1; |
| s->sym_class = sym_class; |
| s->update_policy = UPDATE_ON_MEMORY_MAP_CHANGES; |
| s->update_owner = id2ctx(ctx_id); |
| if (s->update_owner != NULL) context_lock(s->update_owner); |
| } |
| } |
| #endif |
| } |
| *sym = alloc_symbol(); |
| (*sym)->cache = s; |
| return 0; |
| } |
| |
| /*************** Functions for retrieving symbol properties ***************************************/ |
| |
| int get_symbol_class(const Symbol * sym, int * symbol_class) { |
| SymInfoCache * c = get_sym_info_cache(sym, ACC_OTHER); |
| if (c == NULL) return -1; |
| *symbol_class = c->sym_class; |
| return 0; |
| } |
| |
| int get_symbol_type(const Symbol * sym, Symbol ** type) { |
| SymInfoCache * c = get_sym_info_cache(sym, ACC_OTHER); |
| if (c == NULL) return -1; |
| if (c->type_id && strcmp(c->type_id, c->id)) return id2symbol(c->type_id, type); |
| *type = (Symbol *)sym; |
| return 0; |
| } |
| |
| int get_symbol_type_class(const Symbol * sym, int * type_class) { |
| SymInfoCache * c = get_sym_info_cache(sym, ACC_OTHER); |
| if (c == NULL) return -1; |
| *type_class = c->type_class; |
| return 0; |
| } |
| |
| int get_symbol_update_policy(const Symbol * sym, char ** id, int * policy) { |
| SymInfoCache * c = get_sym_info_cache(sym, ACC_OTHER); |
| if (c == NULL) return -1; |
| if (c->update_owner == NULL) { |
| errno = ERR_INV_CONTEXT; |
| return -1; |
| } |
| *id = c->update_owner->id; |
| *policy = c->update_policy; |
| return 0; |
| } |
| |
| int get_symbol_name(const Symbol * sym, char ** name) { |
| SymInfoCache * c = get_sym_info_cache(sym, ACC_OTHER); |
| if (c == NULL) return -1; |
| *name = c->name; |
| return 0; |
| } |
| |
| int get_symbol_base_type(const Symbol * sym, Symbol ** type) { |
| SymInfoCache * c = get_sym_info_cache(sym, ACC_OTHER); |
| if (c == NULL) return -1; |
| if (c->base_type_id) return id2symbol(c->base_type_id, type); |
| return 0; |
| } |
| |
| int get_symbol_index_type(const Symbol * sym, Symbol ** type) { |
| SymInfoCache * c = get_sym_info_cache(sym, ACC_OTHER); |
| if (c == NULL) return -1; |
| if (c->index_type_id) return id2symbol(c->index_type_id, type); |
| return 0; |
| } |
| |
| int get_symbol_container(const Symbol * sym, Symbol ** container) { |
| SymInfoCache * c = get_sym_info_cache(sym, ACC_OTHER); |
| if (c == NULL) return -1; |
| if (c->container_id) return id2symbol(c->container_id, container); |
| return 0; |
| } |
| |
| int get_symbol_size(const Symbol * sym, ContextAddress * size) { |
| SymInfoCache * c = get_sym_info_cache(sym, ACC_SIZE); |
| if (c == NULL) return -1; |
| if (!c->has_size) { |
| set_errno(ERR_OTHER, "Debug info not available"); |
| return -1; |
| } |
| *size = c->size; |
| return 0; |
| } |
| |
| int get_symbol_length(const Symbol * sym, ContextAddress * length) { |
| SymInfoCache * c = get_sym_info_cache(sym, ACC_LENGTH); |
| if (c == NULL) return -1; |
| if (c->has_length) { |
| *length = c->length; |
| return 0; |
| } |
| errno = ERR_INV_CONTEXT; |
| return -1; |
| } |
| |
| int get_symbol_lower_bound(const Symbol * sym, int64_t * lower_bound) { |
| SymInfoCache * c = get_sym_info_cache(sym, ACC_OTHER); |
| if (c == NULL) return -1; |
| if (!c->has_lower_bound) { |
| errno = ERR_INV_CONTEXT; |
| return -1; |
| } |
| *lower_bound = c->lower_bound; |
| return 0; |
| } |
| |
| int get_symbol_flags(const Symbol * sym, SYM_FLAGS * flags) { |
| SymInfoCache * c = get_sym_info_cache(sym, ACC_OTHER); |
| if (c == NULL) return -1; |
| *flags = c->flags; |
| return 0; |
| } |
| |
| int get_symbol_frame(const Symbol * sym, Context ** ctx, int * frame) { |
| SymInfoCache * c = get_sym_info_cache(sym, ACC_OTHER); |
| if (c == NULL) return -1; |
| *ctx = c->update_owner; |
| *frame = c->frame; |
| return 0; |
| } |
| |
| static void validate_children(Channel * c, void * args, int error) { |
| Trap trap; |
| SymInfoCache * s = (SymInfoCache *)args; |
| assert(s->pending_get_children != NULL); |
| assert(s->error_get_children == NULL); |
| assert(!s->done_children); |
| if (set_trap(&trap)) { |
| s->pending_get_children = NULL; |
| s->done_children = 1; |
| if (!error) { |
| error = read_errno(&c->inp); |
| s->children_ids = read_symbol_list(&c->inp, &s->children_count); |
| if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); |
| if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX); |
| } |
| clear_trap(&trap); |
| } |
| else { |
| error = trap.error; |
| } |
| s->error_get_children = get_error_report(error); |
| cache_notify(&s->cache); |
| if (s->disposed) free_sym_info_cache(s); |
| } |
| |
| int get_symbol_children(const Symbol * sym, Symbol *** children, int * count) { |
| Trap trap; |
| SymInfoCache * s = get_sym_info_cache(sym, ACC_OTHER); |
| *children = NULL; |
| *count = 0; |
| if (s == NULL) return -1; |
| if (!set_trap(&trap)) return -1; |
| if (s->pending_get_children) { |
| cache_wait(&s->cache); |
| } |
| else if (s->error_get_children) { |
| exception(set_error_report_errno(s->error_get_children)); |
| } |
| else if (!s->done_children) { |
| Channel * c = cache_channel(); |
| if (c == NULL || is_channel_closed(c)) exception(ERR_SYM_NOT_FOUND); |
| s->pending_get_children = protocol_send_command(c, SYMBOLS, "getChildren", validate_children, s); |
| json_write_string(&c->out, s->id); |
| write_stream(&c->out, 0); |
| write_stream(&c->out, MARKER_EOM); |
| cache_wait(&s->cache); |
| } |
| else if (s->children_count > 0) { |
| int i, cnt = s->children_count; |
| Symbol ** buf = (Symbol **)tmp_alloc(cnt * sizeof(Symbol *)); |
| for (i = 0; i < cnt; i++) { |
| if (id2symbol(s->children_ids[i], buf + i) < 0) exception(errno); |
| } |
| *children = buf; |
| *count = cnt; |
| } |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| static void validate_type_id(Channel * c, void * args, int error) { |
| Trap trap; |
| ArraySymCache * s = (ArraySymCache *)args; |
| assert(s->pending != NULL); |
| assert(s->error == NULL); |
| assert(s->id == NULL); |
| if (set_trap(&trap)) { |
| s->pending = NULL; |
| if (!error) { |
| error = read_errno(&c->inp); |
| s->id = json_read_alloc_string(&c->inp); |
| if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); |
| if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX); |
| } |
| clear_trap(&trap); |
| } |
| else { |
| error = trap.error; |
| } |
| s->error = get_error_report(error); |
| cache_notify(&s->cache); |
| if (s->disposed) free_arr_sym_cache(s); |
| } |
| |
| int get_array_symbol(const Symbol * sym, ContextAddress length, Symbol ** ptr) { |
| LINK * l; |
| Trap trap; |
| ArraySymCache * a = NULL; |
| SymInfoCache * s = get_sym_info_cache(sym, ACC_OTHER); |
| if (s == NULL) return -1; |
| if (!set_trap(&trap)) return -1; |
| for (l = s->array_syms.next; l != &s->array_syms; l = l->next) { |
| ArraySymCache * x = sym2arr(l); |
| if (x->length == length) { |
| a = x; |
| break; |
| } |
| } |
| if (a == NULL) { |
| Channel * c = cache_channel(); |
| if (c == NULL || is_channel_closed(c)) exception(ERR_SYM_NOT_FOUND); |
| a = (ArraySymCache *)loc_alloc_zero(sizeof(*a)); |
| list_add_first(&a->link_sym, &s->array_syms); |
| a->length = length; |
| a->pending = protocol_send_command(c, SYMBOLS, "getArrayType", validate_type_id, a); |
| json_write_string(&c->out, s->id); |
| write_stream(&c->out, 0); |
| json_write_uint64(&c->out, length); |
| write_stream(&c->out, 0); |
| write_stream(&c->out, MARKER_EOM); |
| cache_wait(&a->cache); |
| } |
| else if (a->pending != NULL) { |
| cache_wait(&a->cache); |
| } |
| else if (a->error != NULL) { |
| exception(set_error_report_errno(a->error)); |
| } |
| else if (id2symbol(a->id, ptr) < 0) { |
| exception(errno); |
| } |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| /*************************************************************************************************/ |
| |
| static LocationCommands location_cmds = { NULL, 0, 0}; |
| |
| static int trace_regs_cnt = 0; |
| static int trace_regs_max = 0; |
| static StackFrameRegisterLocation ** trace_regs = NULL; |
| |
| static int id2register_error = 0; |
| |
| ContextAddress is_plt_section(Context * ctx, ContextAddress addr) { |
| /* TODO: is_plt_section() in symbols proxy */ |
| return 0; |
| } |
| |
| static LocationExpressionCommand * add_location_command(int op) { |
| LocationExpressionCommand * cmd = NULL; |
| if (location_cmds.cnt >= location_cmds.max) { |
| location_cmds.max += 16; |
| location_cmds.cmds = (LocationExpressionCommand *)loc_realloc(location_cmds.cmds, |
| sizeof(LocationExpressionCommand) * location_cmds.max); |
| } |
| cmd = location_cmds.cmds + location_cmds.cnt++; |
| memset(cmd, 0, sizeof(LocationExpressionCommand)); |
| cmd->cmd = op; |
| return cmd; |
| } |
| |
| static void read_dwarf_location_params(InputStream * inp, const char * nm, void * arg) { |
| LocationExpressionCommand * cmd = (LocationExpressionCommand *)arg; |
| if (strcmp(nm, "Machine") == 0) cmd->args.loc.reg_id_scope.machine = (uint16_t)json_read_long(inp); |
| else if (strcmp(nm, "ABI") == 0) cmd->args.loc.reg_id_scope.os_abi = (uint8_t)json_read_long(inp); |
| else if (strcmp(nm, "FPABI") == 0) cmd->args.loc.reg_id_scope.fp_abi = (uint8_t)json_read_long(inp); |
| else if (strcmp(nm, "ELF64") == 0) cmd->args.loc.reg_id_scope.elf64 = (uint8_t)json_read_boolean(inp); |
| else if (strcmp(nm, "RegIdType") == 0) cmd->args.loc.reg_id_scope.id_type = (uint8_t)json_read_long(inp); |
| else if (strcmp(nm, "AddrSize") == 0) cmd->args.loc.addr_size = (size_t)json_read_long(inp); |
| else if (strcmp(nm, "BigEndian") == 0) cmd->args.loc.reg_id_scope.big_endian = (uint8_t)json_read_boolean(inp); |
| } |
| |
| static void read_location_command(InputStream * inp, void * args) { |
| char id[256]; |
| size_t val_size = 0; |
| Context * ctx = NULL; |
| int frame = STACK_NO_FRAME; |
| LocationExpressionCommand * cmd = NULL; |
| cmd = add_location_command((int)json_read_long(inp)); |
| switch (cmd->cmd) { |
| case SFT_CMD_NUMBER: |
| if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX); |
| cmd->args.num = json_read_int64(inp); |
| break; |
| case SFT_CMD_ARG: |
| if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX); |
| cmd->args.num = (unsigned)json_read_ulong(inp); |
| break; |
| case SFT_CMD_RD_REG: |
| case SFT_CMD_WR_REG: |
| if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX); |
| json_read_string(inp, id, sizeof(id)); |
| if (id2register(id, &ctx, &frame, &cmd->args.reg) < 0) id2register_error = errno; |
| break; |
| case SFT_CMD_RD_MEM: |
| case SFT_CMD_WR_MEM: |
| if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX); |
| cmd->args.mem.size = json_read_ulong(inp); |
| if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX); |
| cmd->args.mem.big_endian = json_read_boolean(inp); |
| break; |
| case SFT_CMD_LOCATION: |
| if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX); |
| cmd->args.loc.code_addr = (uint8_t *)json_read_alloc_binary(inp, &cmd->args.loc.code_size); |
| if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX); |
| json_read_struct(inp, read_dwarf_location_params, cmd); |
| cmd->args.loc.func = evaluate_vm_expression; |
| break; |
| case SFT_CMD_PIECE: |
| if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX); |
| cmd->args.piece.bit_offs = (unsigned)json_read_ulong(inp); |
| if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX); |
| cmd->args.piece.bit_size = (unsigned)json_read_ulong(inp); |
| if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX); |
| if (json_read_string(inp, id, sizeof(id)) > 0) { |
| if (id2register(id, &ctx, &frame, &cmd->args.piece.reg) < 0) id2register_error = errno; |
| } |
| if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX); |
| cmd->args.piece.value = json_read_alloc_binary(inp, &val_size); |
| if (cmd->args.piece.value != NULL && val_size < (cmd->args.piece.bit_size + 7) / 8) { |
| exception(ERR_JSON_SYNTAX); |
| } |
| break; |
| } |
| } |
| |
| static void read_location_command_array(InputStream * inp, LocationCommands * cmds) { |
| location_cmds.cnt = 0; |
| if (json_read_array(inp, read_location_command, NULL)) { |
| cmds->cmds = (LocationExpressionCommand *)loc_alloc(location_cmds.cnt * sizeof(LocationExpressionCommand)); |
| memcpy(cmds->cmds, location_cmds.cmds, location_cmds.cnt * sizeof(LocationExpressionCommand)); |
| cmds->cnt = cmds->max = location_cmds.cnt; |
| } |
| } |
| |
| static void read_location_attrs(InputStream * inp, const char * name, void * x) { |
| LocationInfoCache * f = (LocationInfoCache *)x; |
| if (strcmp(name, "ArgCnt") == 0) f->info.args_cnt = (unsigned)json_read_ulong(inp); |
| else if (strcmp(name, "CodeAddr") == 0) f->info.code_addr = (ContextAddress)json_read_uint64(inp); |
| else if (strcmp(name, "CodeSize") == 0) f->info.code_size = (ContextAddress)json_read_uint64(inp); |
| else if (strcmp(name, "BigEndian") == 0) f->info.big_endian = json_read_boolean(inp); |
| else if (strcmp(name, "ValueCmds") == 0) read_location_command_array(inp, &f->info.value_cmds); |
| else json_skip_object(inp); |
| } |
| |
| static void validate_location_info(Channel * c, void * args, int error) { |
| Trap trap; |
| LocationInfoCache * f = (LocationInfoCache *)args; |
| assert(f->pending != NULL); |
| assert(f->error == NULL); |
| if (set_trap(&trap)) { |
| f->pending = NULL; |
| if (!error) { |
| id2register_error = 0; |
| error = read_errno(&c->inp); |
| json_read_struct(&c->inp, read_location_attrs, f); |
| if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); |
| if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX); |
| if (!error && id2register_error) error = id2register_error; |
| } |
| clear_trap(&trap); |
| } |
| else { |
| error = trap.error; |
| } |
| f->error = get_error_report(error); |
| cache_notify(&f->cache); |
| if (f->disposed) free_location_info_cache(f); |
| } |
| |
| int get_location_info(const Symbol * sym, LocationInfo ** loc) { |
| Trap trap; |
| unsigned h; |
| LINK * l; |
| SymbolsCache * syms = NULL; |
| LocationInfoCache * f = NULL; |
| SymInfoCache * sym_cache = NULL; |
| Context * ctx = NULL; |
| Context * prs = NULL; |
| uint64_t ip = 0; |
| |
| sym_cache = get_sym_info_cache(sym, ACC_OTHER); |
| if (sym_cache == NULL) return -1; |
| |
| /* Here we assume that symbol location info is valid for all threads in same memory space */ |
| ctx = sym_cache->update_owner; |
| prs = context_get_group(ctx, CONTEXT_GROUP_SYMBOLS); |
| |
| if (!set_trap(&trap)) return -1; |
| |
| if (sym_cache->frame != STACK_NO_FRAME) { |
| StackFrame * frame = NULL; |
| if (get_frame_info(ctx, sym_cache->frame, &frame) < 0) exception(errno); |
| if (read_reg_value(frame, get_PC_definition(ctx), &ip) < 0) exception(errno); |
| } |
| |
| h = hash_sym_id(sym_cache->id); |
| syms = get_symbols_cache(); |
| for (l = syms->link_location[h].next; l != syms->link_location + h; l = l->next) { |
| LocationInfoCache * c = syms2location(l); |
| if (c->ctx == prs && strcmp(sym_cache->id, c->sym_id) == 0) { |
| if (c->pending != NULL) { |
| cache_wait(&c->cache); |
| } |
| else if (c->info.code_size == 0 || |
| (c->info.code_addr <= ip && c->info.code_addr + c->info.code_size > ip)) { |
| f = c; |
| break; |
| } |
| } |
| } |
| |
| assert(f == NULL || f->pending == NULL); |
| |
| if (f == NULL) { |
| f = (LocationInfoCache *)loc_alloc_zero(sizeof(LocationInfoCache)); |
| list_add_first(&f->link_syms, syms->link_location + h); |
| context_lock(f->ctx = prs); |
| f->ip = ip; |
| #if ENABLE_RCBP_TEST |
| if (strncmp(sym_cache->id, "@T.", 3) == 0) { |
| int sym_class = 0; |
| uint64_t address = 0; |
| char ctx_id[256]; |
| if (sscanf(sym_cache->id, "@T.%X.%"SCNx64".%255s", &sym_class, &address, ctx_id) == 3) { |
| location_cmds.cnt = 0; |
| add_location_command(SFT_CMD_NUMBER)->args.num = address; |
| f->info.value_cmds.cmds = (LocationExpressionCommand *)loc_alloc(location_cmds.cnt * sizeof(LocationExpressionCommand)); |
| memcpy(f->info.value_cmds.cmds, location_cmds.cmds, location_cmds.cnt * sizeof(LocationExpressionCommand)); |
| f->info.value_cmds.cnt = f->info.value_cmds.max = location_cmds.cnt; |
| f->info.big_endian = big_endian_host(); |
| f->sym_id = loc_strdup(sym_cache->id); |
| } |
| } |
| #endif |
| } |
| if (f->sym_id == NULL) { |
| Channel * c = get_channel(syms); |
| f->sym_id = loc_strdup(sym_cache->id); |
| f->pending = protocol_send_command(c, SYMBOLS, "getLocationInfo", validate_location_info, f); |
| json_write_string(&c->out, f->sym_id); |
| write_stream(&c->out, 0); |
| write_stream(&c->out, MARKER_EOM); |
| cache_wait(&f->cache); |
| } |
| else if (f->error != NULL) { |
| exception(set_error_report_errno(f->error)); |
| } |
| else { |
| *loc = &f->info; |
| } |
| |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| static void read_stack_trace_register(InputStream * inp, const char * id, void * args) { |
| if (trace_regs_cnt >= trace_regs_max) { |
| trace_regs_max += 16; |
| trace_regs = (StackFrameRegisterLocation **)loc_realloc(trace_regs, trace_regs_max * sizeof(StackFrameRegisterLocation *)); |
| } |
| location_cmds.cnt = 0; |
| if (json_read_array(inp, read_location_command, NULL)) { |
| Context * ctx = NULL; |
| int frame = STACK_NO_FRAME; |
| StackFrameRegisterLocation * reg = (StackFrameRegisterLocation *)loc_alloc( |
| sizeof(StackFrameRegisterLocation) + (location_cmds.cnt - 1) * sizeof(LocationExpressionCommand)); |
| if (id2register(id, &ctx, &frame, ®->reg) < 0) { |
| id2register_error = errno; |
| loc_free(reg); |
| } |
| else { |
| reg->cmds_cnt = location_cmds.cnt; |
| reg->cmds_max = location_cmds.cnt; |
| memcpy(reg->cmds, location_cmds.cmds, location_cmds.cnt * sizeof(LocationExpressionCommand)); |
| trace_regs[trace_regs_cnt++] = reg; |
| } |
| } |
| } |
| |
| static void validate_frame(Channel * c, void * args, int error) { |
| Trap trap; |
| StackFrameCache * f = (StackFrameCache *)args; |
| assert(f->pending != NULL); |
| assert(f->error == NULL); |
| if (set_trap(&trap)) { |
| f->pending = NULL; |
| if (!error) { |
| uint64_t addr, size; |
| id2register_error = 0; |
| error = read_errno(&c->inp); |
| addr = json_read_uint64(&c->inp); |
| if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); |
| size = json_read_uint64(&c->inp); |
| if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); |
| if (error || size == 0) { |
| f->address = f->ip & ~(uint64_t)3; |
| f->size = 4; |
| } |
| else { |
| assert(addr <= f->ip); |
| assert(addr + size > f->ip); |
| f->address = addr; |
| f->size = size; |
| } |
| location_cmds.cnt = 0; |
| if (json_read_array(&c->inp, read_location_command, NULL)) { |
| f->fp = (StackFrameRegisterLocation *)loc_alloc(sizeof(StackFrameRegisterLocation) + |
| (location_cmds.cnt - 1) * sizeof(LocationExpressionCommand)); |
| f->fp->reg = NULL; |
| f->fp->cmds_cnt = location_cmds.cnt; |
| f->fp->cmds_max = location_cmds.cnt; |
| memcpy(f->fp->cmds, location_cmds.cmds, location_cmds.cnt * sizeof(LocationExpressionCommand)); |
| } |
| if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); |
| trace_regs_cnt = 0; |
| if (json_read_struct(&c->inp, read_stack_trace_register, NULL)) { |
| f->regs_cnt = trace_regs_cnt; |
| f->regs = (StackFrameRegisterLocation **)loc_alloc(trace_regs_cnt * sizeof(StackFrameRegisterLocation *)); |
| memcpy(f->regs, trace_regs, trace_regs_cnt * sizeof(StackFrameRegisterLocation *)); |
| } |
| if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); |
| if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX); |
| if (!error && id2register_error) error = id2register_error; |
| } |
| clear_trap(&trap); |
| } |
| else { |
| error = trap.error; |
| } |
| if (get_error_code(error) != ERR_INV_COMMAND) f->error = get_error_report(error); |
| cache_notify(&f->cache); |
| if (f->disposed) free_stack_frame_cache(f); |
| } |
| |
| int get_next_stack_frame(StackFrame * frame, StackFrame * down) { |
| Trap trap; |
| unsigned h; |
| LINK * l; |
| uint64_t ip = 0; |
| Context * ctx = frame->ctx; |
| /* Here we assume that stack tracing info is valid for all threads in same memory space */ |
| Context * prs = context_get_group(ctx, CONTEXT_GROUP_SYMBOLS); |
| SymbolsCache * syms = NULL; |
| StackFrameCache * f = NULL; |
| |
| if (!set_trap(&trap)) return -1; |
| |
| if (read_reg_value(frame, get_PC_definition(ctx), &ip) < 0) { |
| if (frame->is_top_frame) exception(errno); |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| h = hash_frame(prs); |
| syms = get_symbols_cache(); |
| for (l = syms->link_frame[h].next; l != syms->link_frame + h; l = l->next) { |
| StackFrameCache * c = syms2frame(l); |
| if (c->ctx == prs) { |
| if (c->pending != NULL) { |
| cache_wait(&c->cache); |
| } |
| else if (c->address <= ip && c->address + c->size > ip) { |
| f = c; |
| break; |
| } |
| } |
| } |
| |
| assert(f == NULL || f->pending == NULL); |
| |
| if (f == NULL && !syms->service_available) { |
| /* nothing */ |
| } |
| else if (f == NULL) { |
| Channel * c = get_channel(syms); |
| f = (StackFrameCache *)loc_alloc_zero(sizeof(StackFrameCache)); |
| list_add_first(&f->link_syms, syms->link_frame + h); |
| context_lock(f->ctx = prs); |
| f->ip = ip; |
| f->pending = protocol_send_command(c, SYMBOLS, "findFrameInfo", validate_frame, f); |
| json_write_string(&c->out, f->ctx->id); |
| write_stream(&c->out, 0); |
| json_write_uint64(&c->out, ip); |
| write_stream(&c->out, 0); |
| write_stream(&c->out, MARKER_EOM); |
| cache_wait(&f->cache); |
| } |
| else if (f->error != NULL) { |
| exception(set_error_report_errno(f->error)); |
| } |
| else if (f->fp != NULL) { |
| Trap trap; |
| if (set_trap(&trap)) { |
| int i; |
| LocationExpressionState * state; |
| state = evaluate_location_expression(ctx, frame, f->fp->cmds, f->fp->cmds_cnt, NULL, 0); |
| if (state->stk_pos != 1) str_exception(ERR_OTHER, "Invalid stack trace expression"); |
| frame->fp = (ContextAddress)state->stk[0]; |
| frame->is_walked = 1; |
| for (i = 0; i < f->regs_cnt; i++) { |
| int ok = 0; |
| uint64_t v = 0; |
| Trap trap_reg; |
| if (set_trap(&trap_reg)) { |
| /* If a saved register value cannot be evaluated - ignore it */ |
| state = evaluate_location_expression(ctx, frame, f->regs[i]->cmds, f->regs[i]->cmds_cnt, NULL, 0); |
| if (state->stk_pos == 1) { |
| v = state->stk[0]; |
| ok = 1; |
| } |
| clear_trap(&trap_reg); |
| } |
| if (ok && write_reg_value(down, f->regs[i]->reg, v) < 0) exception(errno); |
| } |
| clear_trap(&trap); |
| } |
| else { |
| frame->fp = 0; |
| } |
| } |
| |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| int get_funccall_info(const Symbol * func, |
| const Symbol ** args, unsigned args_cnt, FunctionCallInfo ** info) { |
| /* TODO: get_funccall_info() in symbols proxy */ |
| set_errno(ERR_OTHER, "get_funccall_info() is not supported yet by TCF server"); |
| return -1; |
| } |
| |
| const char * get_symbol_file_name(MemoryRegion * module) { |
| errno = 0; |
| return NULL; |
| } |
| |
| /*************************************************************************************************/ |
| |
| static void flush_syms(Context * ctx, int mode) { |
| LINK * l; |
| LINK * m; |
| int i; |
| |
| for (m = root.next; m != &root; m = m->next) { |
| SymbolsCache * syms = root2syms(m); |
| for (i = 0; i < HASH_SIZE; i++) { |
| l = syms->link_sym[i].next; |
| while (l != syms->link_sym + i) { |
| SymInfoCache * c = syms2sym(l); |
| l = l->next; |
| if (!c->done_context || c->error_get_context != NULL) { |
| free_sym_info_cache(c); |
| } |
| else if (c->update_owner == NULL || c->update_owner->exited) { |
| free_sym_info_cache(c); |
| } |
| else if ((mode & (1 << c->update_policy)) && ctx == c->update_owner) { |
| if (mode == (1 << UPDATE_ON_EXE_STATE_CHANGES)) { |
| c->degraded = 1; |
| } |
| else { |
| free_sym_info_cache(c); |
| } |
| } |
| } |
| l = syms->link_find_by_name[i].next; |
| while (l != syms->link_find_by_name + i) { |
| FindSymCache * c = syms2find(l); |
| l = l->next; |
| if ((mode & (1 << c->update_policy)) && c->ctx == ctx) { |
| free_find_sym_cache(c); |
| } |
| } |
| l = syms->link_find_in_scope[i].next; |
| while (l != syms->link_find_in_scope + i) { |
| FindSymCache * c = syms2find(l); |
| l = l->next; |
| if ((mode & (1 << c->update_policy)) && c->ctx == ctx) { |
| free_find_sym_cache(c); |
| } |
| } |
| l = syms->link_list[i].next; |
| while (l != syms->link_list + i) { |
| FindSymCache * c = syms2find(l); |
| l = l->next; |
| if ((mode & (1 << c->update_policy)) && c->ctx == ctx) { |
| free_find_sym_cache(c); |
| } |
| } |
| if (mode & (1 << UPDATE_ON_MEMORY_MAP_CHANGES)) { |
| Context * prs = context_get_group(ctx, CONTEXT_GROUP_SYMBOLS); |
| l = syms->link_frame[i].next; |
| while (l != syms->link_frame + i) { |
| StackFrameCache * c = syms2frame(l); |
| l = l->next; |
| if (c->ctx == prs) free_stack_frame_cache(c); |
| } |
| l = syms->link_location[i].next; |
| while (l != syms->link_location + i) { |
| LocationInfoCache * c = syms2location(l); |
| l = l->next; |
| if (c->ctx == prs) free_location_info_cache(c); |
| } |
| } |
| } |
| } |
| } |
| |
| static void event_context_created(Context * ctx, void * x) { |
| flush_syms(ctx, ~0); |
| } |
| |
| static void event_context_exited(Context * ctx, void * x) { |
| flush_syms(ctx, ~0); |
| } |
| |
| static void event_context_stopped(Context * ctx, void * x) { |
| flush_syms(ctx, (1 << UPDATE_ON_EXE_STATE_CHANGES)); |
| } |
| |
| static void event_context_started(Context * ctx, void * x) { |
| flush_syms(ctx, (1 << UPDATE_ON_EXE_STATE_CHANGES)); |
| } |
| |
| static void event_context_changed(Context * ctx, void * x) { |
| flush_syms(ctx, (1 << UPDATE_ON_MEMORY_MAP_CHANGES) | (1 << UPDATE_ON_EXE_STATE_CHANGES)); |
| } |
| |
| static void channel_close_listener(Channel * c) { |
| LINK * l = root.next; |
| while (l != &root) { |
| SymbolsCache * s = root2syms(l); |
| l = l->next; |
| if (s->channel == c) free_symbols_cache(s); |
| } |
| } |
| |
| void ini_symbols_lib(void) { |
| static ContextEventListener listener = { |
| event_context_created, |
| event_context_exited, |
| event_context_stopped, |
| event_context_started, |
| event_context_changed |
| }; |
| add_context_event_listener(&listener, NULL); |
| add_channel_close_listener(channel_close_listener); |
| } |
| |
| #endif |