blob: 1c37326b98b609a0cc09404243ab67a4176eeef3 [file] [log] [blame]
/*******************************************************************************
* 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->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