blob: d145de922af30f0ebc285902c1c373f8325ed36f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2011 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 <config.h>
#if ENABLE_SymbolsProxy
#include <assert.h>
#include <stdio.h>
#include <framework/context.h>
#include <framework/cache.h>
#include <framework/json.h>
#include <framework/events.h>
#include <framework/myalloc.h>
#include <framework/exceptions.h>
#include <services/stacktrace.h>
#include <services/symbols.h>
#if ENABLE_RCBP_TEST
# include <main/test.h>
#endif
#define HASH_SIZE (4 * MEM_USAGE_FACTOR - 1)
/* 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_in_scope[HASH_SIZE];
LINK link_list[HASH_SIZE];
LINK link_frame[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 * register_id;
char * name;
Context * update_owner;
int update_policy;
int sym_class;
int type_class;
int has_size;
int has_address;
int has_offset;
int has_length;
int has_lower_bound;
int has_upper_bound;
SYM_FLAGS flags;
ContextAddress address;
ContextAddress size;
ContextAddress offset;
ContextAddress length;
int64_t lower_bound;
int64_t upper_bound;
char * value;
size_t value_size;
int big_endian;
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() */
typedef struct FindSymCache {
LINK link_syms;
AbstractCache cache;
ReplyHandlerInfo * pending;
ErrorReport * error;
int update_policy;
Context * ctx;
uint64_t ip;
char * scope;
char * name;
char * id;
int disposed;
} FindSymCache;
/* Cached result of enumerate_symbols() */
typedef struct ListSymCache {
LINK link_syms;
AbstractCache cache;
ReplyHandlerInfo * pending;
ErrorReport * error;
int update_policy;
Context * ctx;
uint64_t ip;
char ** list;
unsigned list_size;
unsigned list_max;
int disposed;
} ListSymCache;
typedef struct StackFrameCache {
LINK link_syms;
AbstractCache cache;
ReplyHandlerInfo * pending;
ErrorReport * error;
Context * ctx;
uint64_t ip;
uint64_t address;
uint64_t size;
StackTracingCommandSequence * fp;
StackTracingCommandSequence ** regs;
int regs_cnt;
int disposed;
} StackFrameCache;
#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 syms2list(A) ((ListSymCache *)((char *)(A) - offsetof(ListSymCache, link_syms)))
#define sym2arr(A) ((ArraySymCache *)((char *)(A) - offsetof(ArraySymCache, link_sym)))
#define syms2frame(A)((StackFrameCache *)((char *)(A) - offsetof(StackFrameCache, link_syms)))
struct Symbol {
unsigned magic;
SymInfoCache * cache;
};
#include <services/symbols_alloc.h>
static LINK root;
static const char * SYMBOLS = "Symbols";
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;
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_in_scope + i);
list_init(syms->link_list + i);
list_init(syms->link_frame + 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->register_id);
loc_free(c->name);
loc_free(c->value);
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) {
cache_dispose(&c->cache);
release_error_report(c->error);
context_unlock(c->ctx);
loc_free(c->name);
loc_free(c->id);
loc_free(c);
}
}
static void free_list_sym_cache(ListSymCache * c) {
list_remove(&c->link_syms);
c->disposed = 1;
if (c->pending == NULL) {
unsigned j;
cache_dispose(&c->cache);
release_error_report(c->error);
context_unlock(c->ctx);
for (j = 0; j < c->list_size; j++) loc_free(c->list[j]);
loc_free(c->list);
loc_free(c);
}
}
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++) loc_free(c->regs[i]);
loc_free(c->regs);
loc_free(c->fp);
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_in_scope + i)) {
free_find_sym_cache(syms2find(syms->link_find_in_scope[i].next));
}
while (!list_is_empty(syms->link_list + i)) {
free_list_sym_cache(syms2list(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 (!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, "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, "UpperBound") == 0) { s->upper_bound = json_read_int64(inp); s->has_upper_bound = 1; }
else if (strcmp(name, "Offset") == 0) { s->offset = json_read_long(inp); s->has_offset = 1; }
else if (strcmp(name, "Address") == 0) { s->address = (ContextAddress)json_read_uint64(inp); s->has_address = 1; }
else if (strcmp(name, "Register") == 0) s->register_id = json_read_alloc_string(inp);
else if (strcmp(name, "Flags") == 0) s->flags = json_read_ulong(inp);
else if (strcmp(name, "Value") == 0) s->value = json_read_alloc_binary(inp, &s->value_size);
else if (strcmp(name, "BigEndian") == 0) s->big_endian = json_read_boolean(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);
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);
if (trap.error) exception(trap.error);
}
static SymInfoCache * get_sym_info_cache(const Symbol * sym) {
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);
}
else if (s->error_get_context != NULL) {
exception(set_error_report_errno(s->error_get_context));
}
else if (!s->done_context) {
Channel * c = cache_channel();
if (c == NULL) 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 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 = 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;
}
f->error = get_error_report(error);
assert(f->error != NULL || f->id != NULL);
cache_notify(&f->cache);
if (f->disposed) free_find_sym_cache(f);
if (trap.error) exception(trap.error);
}
int find_symbol_by_name(Context * ctx, int frame, ContextAddress addr, 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_PROCESS);
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->ip == ip && strcmp(c->name, name) == 0) {
f = c;
break;
}
}
#if ENABLE_RCBP_TEST
if ((f == NULL && !syms->service_available) || (f != NULL && f->pending == NULL && f->error != NULL)) {
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);
loc_free(f->id);
f->error = NULL;
f->id = NULL;
}
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_PROCESS)->id);
f->id = loc_strdup(bf);
}
}
#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->ip = ip;
f->name = loc_strdup(name);
f->update_policy = ip ? UPDATE_ON_EXE_STATE_CHANGES : UPDATE_ON_MEMORY_MAP_CHANGES;
f->pending = protocol_send_command(c, SYMBOLS, "find", 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, sym) < 0) {
exception(errno);
}
clear_trap(&trap);
return 0;
}
int find_symbol_in_scope(Context * ctx, int frame, ContextAddress addr, Symbol * scope, 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_PROCESS);
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->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->ip = ip;
if (scope != NULL) f->scope = loc_strdup(scope->cache->id);
f->name = loc_strdup(name);
f->update_policy = ip ? UPDATE_ON_EXE_STATE_CHANGES : 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, sym) < 0) {
exception(errno);
}
clear_trap(&trap);
return 0;
}
int find_symbol_by_addr(Context * ctx, int frame, ContextAddress addr, Symbol ** sym) {
errno = ERR_UNSUPPORTED;
return -1;
}
static void read_sym_list_item(InputStream * inp, void * args) {
ListSymCache * f = (ListSymCache *)args;
char * id = json_read_alloc_string(inp);
if (f->list_size >= f->list_max) {
f->list_max += 16;
f->list = (char **)loc_realloc(f->list, f->list_max * sizeof(char *));
}
f->list[f->list_size++] = id;
}
static void validate_list(Channel * c, void * args, int error) {
Trap trap;
ListSymCache * f = (ListSymCache *)args;
assert(f->pending != NULL);
assert(f->error == NULL);
if (set_trap(&trap)) {
f->pending = NULL;
if (!error) {
error = read_errno(&c->inp);
json_read_array(&c->inp, read_sym_list_item, f);
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_list_sym_cache(f);
if (trap.error) exception(trap.error);
}
int enumerate_symbols(Context * ctx, int frame, EnumerateSymbolsCallBack * func, void * args) {
uint64_t ip = 0;
unsigned h;
LINK * l;
Trap trap;
SymbolsCache * syms = NULL;
ListSymCache * f = NULL;
if (!set_trap(&trap)) return -1;
if (frame == STACK_NO_FRAME) {
ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
}
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) {
ListSymCache * c = syms2list(l);
if (c->ctx == ctx && c->ip == ip) {
f = c;
break;
}
}
if (f == NULL) {
Channel * c = get_channel(syms);
f = (ListSymCache *)loc_alloc_zero(sizeof(ListSymCache));
list_add_first(&f->link_syms, syms->link_list + h);
context_lock(f->ctx = ctx);
f->ip = ip;
f->update_policy = ip ? UPDATE_ON_EXE_STATE_CHANGES : UPDATE_ON_MEMORY_MAP_CHANGES;
f->pending = protocol_send_command(c, SYMBOLS, "list", validate_list, 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 {
unsigned i;
for (i = 0; i < f->list_size; i++) {
Symbol * sym = NULL;
if (id2symbol(f->list[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);
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->has_address = 1;
s->address = (ContextAddress)address;
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);
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);
if (c == NULL) return -1;
if (c->type_id) return id2symbol(c->type_id, type);
return 0;
}
int get_symbol_type_class(const Symbol * sym, int * type_class) {
SymInfoCache * c = get_sym_info_cache(sym);
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);
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);
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);
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);
if (c == NULL) return -1;
if (c->index_type_id) return id2symbol(c->index_type_id, type);
return 0;
}
int get_symbol_size(const Symbol * sym, ContextAddress * size) {
SymInfoCache * c = get_sym_info_cache(sym);
if (c == NULL) return -1;
if (!c->has_size) {
errno = 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);
if (c == NULL) return -1;
if (c->has_length) {
*length = c->length;
return 0;
}
if (c->has_lower_bound && c->has_upper_bound) {
*length = (ContextAddress)(c->has_upper_bound - c->has_lower_bound + 1);
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);
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_offset(const Symbol * sym, ContextAddress * offset) {
SymInfoCache * c = get_sym_info_cache(sym);
if (c == NULL) return -1;
if (!c->has_offset) {
errno = ERR_INV_CONTEXT;
return -1;
}
*offset = c->offset;
return 0;
}
int get_symbol_value(const Symbol * sym, void ** value, size_t * size, int * big_endian) {
SymInfoCache * c = get_sym_info_cache(sym);
if (c == NULL) return -1;
if (c->sym_class != SYM_CLASS_VALUE) {
errno = ERR_INV_CONTEXT;
return -1;
}
*value = c->value;
*size = c->value_size;
*big_endian = c->big_endian;
return 0;
}
int get_symbol_address(const Symbol * sym, ContextAddress * address) {
SymInfoCache * c = get_sym_info_cache(sym);
if (c == NULL) return -1;
if (!c->has_address) {
errno = ERR_INV_ADDRESS;
return -1;
}
*address = c->address;
return 0;
}
int get_symbol_register(const Symbol * sym, Context ** ctx, int * frame, RegisterDefinition ** reg) {
SymInfoCache * c = get_sym_info_cache(sym);
if (c == NULL) return -1;
if (c->register_id == NULL) {
errno = ERR_INV_CONTEXT;
return -1;
}
return id2register(c->register_id, ctx, frame, reg);
}
int get_symbol_flags(const Symbol * sym, SYM_FLAGS * flags) {
SymInfoCache * c = get_sym_info_cache(sym);
if (c == NULL) return -1;
*flags = c->flags;
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 = json_read_alloc_string_array(&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);
if (trap.error) exception(trap.error);
}
int get_symbol_children(const Symbol * sym, Symbol *** children, int * count) {
Trap trap;
SymInfoCache * s = get_sym_info_cache(sym);
*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) 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;
static Symbol ** buf = NULL;
static int buf_len = 0;
if (buf_len < cnt) {
buf_len = cnt;
buf = (Symbol **)loc_realloc(buf, 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);
if (trap.error) exception(trap.error);
}
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);
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) 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 int trace_cmds_cnt = 0;
static int trace_cmds_max = 0;
static StackTracingCommand * trace_cmds = NULL;
static int trace_regs_cnt = 0;
static int trace_regs_max = 0;
static StackTracingCommandSequence ** trace_regs = NULL;
static int trace_error = 0;
ContextAddress is_plt_section(Context * ctx, ContextAddress addr) {
/* TODO: is_plt_section() in symbols proxy */
return 0;
}
static void read_stack_trace_command(InputStream * inp, void * args) {
char id[256];
Context * ctx = NULL;
int frame = STACK_NO_FRAME;
StackTracingCommand * cmd = NULL;
if (trace_cmds_cnt >= trace_cmds_max) {
trace_cmds_max += 16;
trace_cmds = (StackTracingCommand *)loc_realloc(trace_cmds, trace_cmds_max * sizeof(StackTracingCommand));
}
cmd = trace_cmds + trace_cmds_cnt++;
memset(cmd, 0, sizeof(*cmd));
cmd->cmd = json_read_long(inp);
switch (cmd->cmd) {
case SFT_CMD_NUMBER:
if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX);
cmd->num = json_read_int64(inp);
break;
case SFT_CMD_REGISTER:
if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX);
json_read_string(inp, id, sizeof(id));
if (id2register(id, &ctx, &frame, &cmd->reg) < 0) trace_error = errno;
break;
case SFT_CMD_DEREF:
if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX);
cmd->size = json_read_ulong(inp);
if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX);
cmd->big_endian = json_read_boolean(inp);
break;
}
}
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 = (StackTracingCommandSequence **)loc_realloc(trace_regs, trace_regs_max * sizeof(StackTracingCommandSequence *));
}
trace_cmds_cnt = 0;
if (json_read_array(inp, read_stack_trace_command, NULL)) {
Context * ctx = NULL;
int frame = STACK_NO_FRAME;
StackTracingCommandSequence * reg = (StackTracingCommandSequence *)loc_alloc(
sizeof(StackTracingCommandSequence) + (trace_cmds_cnt - 1) * sizeof(StackTracingCommand));
if (id2register(id, &ctx, &frame, &reg->reg) < 0) {
trace_error = errno;
loc_free(reg);
}
else {
reg->cmds_cnt = trace_cmds_cnt;
reg->cmds_max = trace_cmds_cnt;
memcpy(reg->cmds, trace_cmds, trace_cmds_cnt * sizeof(StackTracingCommand));
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;
trace_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;
}
trace_cmds_cnt = 0;
if (json_read_array(&c->inp, read_stack_trace_command, NULL)) {
f->fp = (StackTracingCommandSequence *)loc_alloc(sizeof(StackTracingCommandSequence) + (trace_cmds_cnt - 1) * sizeof(StackTracingCommand));
f->fp->reg = NULL;
f->fp->cmds_cnt = trace_cmds_cnt;
f->fp->cmds_max = trace_cmds_cnt;
memcpy(f->fp->cmds, trace_cmds, trace_cmds_cnt * sizeof(StackTracingCommand));
}
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 = (StackTracingCommandSequence **)loc_alloc(trace_regs_cnt * sizeof(StackTracingCommandSequence *));
memcpy(f->regs, trace_regs, trace_regs_cnt * sizeof(StackTracingCommandSequence *));
}
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
if (!error && trace_error) error = trace_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);
if (trap.error) exception(trap.error);
}
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_PROCESS);
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;
frame->fp = (ContextAddress)evaluate_stack_trace_commands(ctx, frame, f->fp);
for (i = 0; i < f->regs_cnt; i++) {
uint64_t v = evaluate_stack_trace_commands(ctx, frame, f->regs[i]);
if (write_reg_value(down, f->regs[i]->reg, v) < 0) exception(errno);
}
clear_trap(&trap);
}
else {
frame->fp = 0;
}
}
clear_trap(&trap);
return 0;
}
/*************************************************************************************************/
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_policy == 0 || c->update_owner == NULL || c->update_owner->exited) {
free_sym_info_cache(c);
}
else if ((mode & (1 << c->update_policy)) && ctx == c->update_owner) {
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) {
ListSymCache * c = syms2list(l);
l = l->next;
if ((mode & (1 << c->update_policy)) && c->ctx == ctx) {
free_list_sym_cache(c);
}
}
if (mode & (1 << UPDATE_ON_MEMORY_MAP_CHANGES)) {
Context * prs = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
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);
}
}
}
}
}
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
};
list_init(&root);
add_context_event_listener(&listener, NULL);
add_channel_close_listener(channel_close_listener);
}
#endif