diff options
Diffstat (limited to 'agent/tcf/services/memorymap.c')
-rw-r--r-- | agent/tcf/services/memorymap.c | 543 |
1 files changed, 543 insertions, 0 deletions
diff --git a/agent/tcf/services/memorymap.c b/agent/tcf/services/memorymap.c new file mode 100644 index 00000000..f96a07a8 --- /dev/null +++ b/agent/tcf/services/memorymap.c @@ -0,0 +1,543 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 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 + *******************************************************************************/ + +/* + * This module holds execution context memory maps. + */ + +#include <config.h> + +#if SERVICE_MemoryMap + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <framework/myalloc.h> +#include <framework/trace.h> +#include <framework/json.h> +#include <framework/events.h> +#include <framework/exceptions.h> +#include <services/memorymap.h> + +typedef struct Listener { + MemoryMapEventListener * listener; + void * args; +} Listener; + +typedef struct ContextExtensionMM { + int valid; + ErrorReport * error; + MemoryMap target_map; + MemoryMap client_map; +} ContextExtensionMM; + +typedef struct ClientMap { + LINK link_list; + LINK link_hash; + LINK link_ctx; + char * id; + MemoryMap map; + Channel * channel; +} ClientMap; + +static size_t context_extension_offset = 0; + +#define EXT(ctx) ((ContextExtensionMM *)((char *)(ctx) + context_extension_offset)) + +static const char MEMORY_MAP[] = "MemoryMap"; + +static Listener * listeners = NULL; +static unsigned listener_cnt = 0; +static unsigned listener_max = 0; + +#define list2map(A) ((ClientMap *)((char *)(A) - offsetof(ClientMap, link_list))) +#define hash2map(A) ((ClientMap *)((char *)(A) - offsetof(ClientMap, link_hash))) +#define ctx2map(A) ((ClientMap *)((char *)(A) - offsetof(ClientMap, link_ctx))) + +#define MAP_HASH_SIZE (4 * MEM_USAGE_FACTOR - 1) +static LINK client_map_list; +static LINK client_map_hash[MAP_HASH_SIZE]; + +static TCFBroadcastGroup * broadcast_group = NULL; + +static unsigned map_id_hash(const char * id) { + int i; + unsigned h = 0; + for (i = 0; id[i]; i++) h += id[i]; + return h % MAP_HASH_SIZE; +} + +static MemoryRegion * add_region(MemoryMap * map) { + MemoryRegion * r = NULL; + if (map->region_cnt >= map->region_max) { + map->region_max += 8; + map->regions = (MemoryRegion *)loc_realloc(map->regions, sizeof(MemoryRegion) * map->region_max); + } + r = map->regions + map->region_cnt++; + memset(r, 0, sizeof(MemoryRegion)); + return r; +} + +static int str_equ(char * x, char * y) { + if (x == y) return 1; + if (x == NULL) return 0; + if (y == NULL) return 0; + return strcmp(x, y) == 0; +} + +static unsigned find_maps(LINK * maps, const char * id) { + LINK * l; + unsigned cnt = 0; + unsigned h = map_id_hash(id); + for (l = client_map_hash[h].next; l != &client_map_hash[h]; l = l->next) { + ClientMap * m = hash2map(l); + if (list_is_empty(&m->link_ctx) && strcmp(m->id, id) == 0) { + list_add_last(&m->link_ctx, maps); + cnt += m->map.region_cnt; + } + } + return cnt; +} + +static Context * get_mem_context(Context * ctx) { +#if ENABLE_DebugContext + ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS); +#endif + return ctx; +} + +static Context * get_sym_context(Context * ctx) { +#if ENABLE_DebugContext + ctx = context_get_group(ctx, CONTEXT_GROUP_SYMBOLS); +#endif + return ctx; +} + +static void update_context_client_map(Context * ctx) { + ContextExtensionMM * ext = EXT(ctx); + Context * syms = get_sym_context(ctx); + unsigned r_cnt = 0; + int equ = 0; + unsigned i; + LINK * l; + LINK maps; + + if (syms == NULL) return; + assert(ctx == get_mem_context(ctx)); + list_init(&maps); + r_cnt += find_maps(&maps, syms->id); + if (syms->name != NULL) { + r_cnt += find_maps(&maps, syms->name); + r_cnt += find_maps(&maps, context_full_name(syms)); + } + equ = ext->client_map.region_cnt == r_cnt; + if (equ) { + unsigned k = 0; + for (l = maps.next; equ && l != &maps; l = l->next) { + ClientMap * m = ctx2map(l); + for (i = 0; equ && i < m->map.region_cnt; i++) { + MemoryRegion * x = m->map.regions + i; + MemoryRegion * y = ext->client_map.regions + k++; + equ = + y->addr == x->addr && + y->size == x->size && + y->file_offs == x->file_offs && + y->bss == x->bss && + y->flags == x->flags && + str_equ(y->file_name, x->file_name) && + str_equ(y->sect_name, x->sect_name); + } + } + assert(!equ || k == r_cnt); + } + if (!equ) { + context_clear_memory_map(&ext->client_map); + for (l = maps.next; l != &maps; l = l->next) { + ClientMap * m = ctx2map(l); + for (i = 0; i < m->map.region_cnt; i++) { + MemoryRegion * x = m->map.regions + i; + MemoryRegion * y = add_region(&ext->client_map); + y->addr = x->addr; + y->size = x->size; + y->file_offs = x->file_offs; + y->bss = x->bss; + y->flags = x->flags; + if (x->file_name) y->file_name = loc_strdup(x->file_name); + if (x->sect_name) y->sect_name = loc_strdup(x->sect_name); + if (x->id) y->id = loc_strdup(x->id); + } + } + assert(ext->client_map.region_cnt == r_cnt); + } + while (!list_is_empty(&maps)) list_remove(maps.next); + if (!equ) memory_map_event_mapping_changed(ctx); +} + +static void update_all_context_client_maps(void) { + LINK * l; + for (l = context_root.next; l != &context_root; l = l->next) { + Context * ctx = ctxl2ctxp(l); + if (ctx->exited) continue; + if (ctx != get_mem_context(ctx)) continue; + update_context_client_map(ctx); + } +} + +static void event_memory_map_changed(Context * ctx, void * args) { + OutputStream * out; + ContextExtensionMM * ext = EXT(ctx); + + if (ctx->exited) return; + if (!ext->valid) return; + if (ctx != get_mem_context(ctx)) return; + + context_clear_memory_map(&ext->target_map); + ext->valid = 0; + + out = &broadcast_group->out; + + write_stringz(out, "E"); + write_stringz(out, MEMORY_MAP); + write_stringz(out, "changed"); + + json_write_string(out, ctx->id); + write_stream(out, 0); + write_stream(out, MARKER_EOM); +} + +static void event_context_changed(Context * ctx, void * args) { + if (ctx->exited) return; + if (ctx != get_mem_context(ctx)) return; + update_context_client_map(ctx); +} + +static void event_context_disposed(Context * ctx, void * args) { + MemoryMap * map; + ContextExtensionMM * ext = EXT(ctx); + + map = &ext->target_map; + context_clear_memory_map(map); + loc_free(map->regions); + memset(map, 0, sizeof(MemoryMap)); + + map = &ext->client_map; + context_clear_memory_map(map); + loc_free(map->regions); + memset(map, 0, sizeof(MemoryMap)); + + release_error_report(ext->error); +} + +int memory_map_get(Context * ctx, MemoryMap ** client_map, MemoryMap ** target_map) { + ContextExtensionMM * ext = EXT(ctx); + assert(ctx == get_mem_context(ctx)); +#if ENABLE_DebugContext + if (!ext->valid) { + context_clear_memory_map(&ext->target_map); + release_error_report(ext->error); + if (context_get_memory_map(ctx, &ext->target_map) < 0) { + ext->error = get_error_report(errno); + ext->valid = get_error_code(errno) != ERR_CACHE_MISS; + } + else { + ext->error = NULL; + ext->valid = 1; + } + } +#endif + if (ext->error != NULL) { + set_error_report_errno(ext->error); + return -1; + } + *client_map = &ext->client_map; + *target_map = &ext->target_map; + return 0; +} + +void memory_map_event_module_loaded(Context * ctx) { + unsigned i; + assert(ctx->ref_count > 0); + assert(ctx == get_mem_context(ctx)); + event_memory_map_changed(ctx, NULL); + for (i = 0; i < listener_cnt; i++) { + Listener * l = listeners + i; + if (l->listener->module_loaded == NULL) continue; + l->listener->module_loaded(ctx, l->args); + } +} + +void memory_map_event_code_section_ummapped(Context * ctx, ContextAddress addr, ContextAddress size) { + unsigned i; + assert(ctx->ref_count > 0); + assert(ctx == get_mem_context(ctx)); + event_memory_map_changed(ctx, NULL); + for (i = 0; i < listener_cnt; i++) { + Listener * l = listeners + i; + if (l->listener->code_section_ummapped == NULL) continue; + l->listener->code_section_ummapped(ctx, addr, size, l->args); + } +} + +void memory_map_event_module_unloaded(Context * ctx) { + unsigned i; + assert(ctx->ref_count > 0); + assert(ctx == get_mem_context(ctx)); + event_memory_map_changed(ctx, NULL); + for (i = 0; i < listener_cnt; i++) { + Listener * l = listeners + i; + if (l->listener->module_unloaded == NULL) continue; + l->listener->module_unloaded(ctx, l->args); + } +} + +void memory_map_event_mapping_changed(Context * ctx) { + unsigned i; + assert(ctx->ref_count > 0); + assert(ctx == get_mem_context(ctx)); + event_memory_map_changed(ctx, NULL); + for (i = 0; i < listener_cnt; i++) { + Listener * l = listeners + i; + if (l->listener->mapping_changed == NULL) continue; + l->listener->mapping_changed(ctx, l->args); + } +} + +void add_memory_map_event_listener(MemoryMapEventListener * listener, void * client_data) { + Listener * l = NULL; + if (listener_cnt >= listener_max) { + listener_max += 8; + listeners = (Listener *)loc_realloc(listeners, listener_max * sizeof(Listener)); + } + l = listeners + listener_cnt++; + l->listener = listener; + l->args = client_data; +} + +static void write_map_region(OutputStream * out, MemoryRegion * m) { + MemoryRegionAttribute * x = m->attrs; + + write_stream(out, '{'); + json_write_string(out, "Addr"); + write_stream(out, ':'); + json_write_uint64(out, m->addr); + write_stream(out, ','); + json_write_string(out, "Size"); + write_stream(out, ':'); + json_write_uint64(out, m->size); + write_stream(out, ','); + json_write_string(out, "Flags"); + write_stream(out, ':'); + json_write_ulong(out, m->flags); + if (m->file_name != NULL) { + write_stream(out, ','); + json_write_string(out, "FileName"); + write_stream(out, ':'); + json_write_string(out, m->file_name); + write_stream(out, ','); + if (m->sect_name != NULL) { + json_write_string(out, "SectionName"); + write_stream(out, ':'); + json_write_string(out, m->sect_name); + } + else { + json_write_string(out, "Offs"); + write_stream(out, ':'); + json_write_uint64(out, m->file_offs); + } + if (m->bss) { + write_stream(out, ','); + json_write_string(out, "BSS"); + write_stream(out, ':'); + json_write_boolean(out, m->bss); + } + } + if (m->id != NULL) { + write_stream(out, ','); + json_write_string(out, "ID"); + write_stream(out, ':'); + json_write_string(out, m->id); + } + while (x != NULL) { + write_stream(out, ','); + json_write_string(out, x->name); + write_stream(out, ':'); + write_string(out, x->value); + x = x->next; + } + write_stream(out, '}'); +} + +static void command_get(char * token, Channel * c) { + char id[256]; + int err = 0; + Context * ctx = NULL; + MemoryMap * client_map = NULL; + MemoryMap * target_map = NULL; + + json_read_string(&c->inp, id, sizeof(id)); + if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); + if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX); + + ctx = id2ctx(id); + if (ctx == NULL) err = ERR_INV_CONTEXT; + else ctx = get_mem_context(ctx); + + if (!err && memory_map_get(ctx, &client_map, &target_map) < 0) err = errno; + + write_stringz(&c->out, "R"); + write_stringz(&c->out, token); + write_errno(&c->out, err); + if (err) { + write_stringz(&c->out, "null"); + } + else { + unsigned n; + unsigned cnt = 0; + write_stream(&c->out, '['); + for (n = 0; n < client_map->region_cnt; n++) { + if (cnt > 0) write_stream(&c->out, ','); + write_map_region(&c->out, client_map->regions + n); + cnt++; + } + for (n = 0; n < target_map->region_cnt; n++) { + if (cnt > 0) write_stream(&c->out, ','); + write_map_region(&c->out, target_map->regions + n); + cnt++; + } + write_stream(&c->out, ']'); + write_stream(&c->out, 0); + } + + write_stream(&c->out, MARKER_EOM); +} + +static void read_map_attribute(InputStream * inp, const char * name, void * args) { + MemoryRegion * r = (MemoryRegion *)args; + if (strcmp(name, "Addr") == 0) r->addr = (ContextAddress)json_read_uint64(inp); + else if (strcmp(name, "Size") == 0) r->size = (ContextAddress)json_read_uint64(inp); + else if (strcmp(name, "Offs") == 0) r->file_offs = json_read_uint64(inp); + else if (strcmp(name, "BSS") == 0) r->bss = json_read_boolean(inp); + else if (strcmp(name, "Flags") == 0) r->flags = (unsigned)json_read_long(inp); + else if (strcmp(name, "FileName") == 0) r->file_name = json_read_alloc_string(inp); + else if (strcmp(name, "SectionName") == 0) r->sect_name = json_read_alloc_string(inp); + else if (strcmp(name, "ID") == 0) r->id = json_read_alloc_string(inp); + else { + MemoryRegionAttribute * x = (MemoryRegionAttribute *)loc_alloc(sizeof(MemoryRegionAttribute)); + x->name = loc_strdup(name); + x->value = json_read_object(inp); + x->next = r->attrs; + r->attrs = x; + } +} + +static void read_map_item(InputStream * inp, void * args) { + MemoryMap * map = (MemoryMap *)args; + MemoryRegion * r = add_region(map); + + json_read_struct(inp, read_map_attribute, r); +} + +static void command_set(char * token, Channel * c) { + char id[256]; + unsigned h; + ClientMap * cm = NULL; + LINK * l = NULL; + MemoryMap map; + + memset(&map, 0, sizeof(map)); + + json_read_string(&c->inp, id, sizeof(id)); + if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); + json_read_array(&c->inp, read_map_item, &map); + if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); + if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX); + + h = map_id_hash(id); + for (l = client_map_hash[h].next; l != &client_map_hash[h]; l = l->next) { + ClientMap * m = hash2map(l); + if (m->channel == c && strcmp(m->id, id) == 0) { + context_clear_memory_map(&m->map); + loc_free(m->map.regions); + cm = m; + break; + } + } + if (map.region_cnt > 0) { + if (cm == NULL) { + cm = (ClientMap *)loc_alloc_zero(sizeof(ClientMap)); + cm->id = loc_strdup(id); + cm->channel = c; + list_add_last(&cm->link_list, &client_map_list); + list_add_last(&cm->link_hash, &client_map_hash[h]); + } + cm->map = map; + update_all_context_client_maps(); + } + else if (cm != NULL) { + list_remove(&cm->link_list); + list_remove(&cm->link_hash); + loc_free(cm->id); + loc_free(cm); + update_all_context_client_maps(); + } + + write_stringz(&c->out, "R"); + write_stringz(&c->out, token); + write_errno(&c->out, 0); + write_stream(&c->out, MARKER_EOM); +} + +static void channel_close_listener(Channel * c) { + int notify = 0; + LINK * l = client_map_list.next; + while (l != &client_map_list) { + ClientMap * m = list2map(l); + l = l->next; + if (m->channel != c) continue; + list_remove(&m->link_list); + list_remove(&m->link_hash); + context_clear_memory_map(&m->map); + loc_free(m->map.regions); + loc_free(m->id); + loc_free(m); + notify = 1; + } + if (notify) update_all_context_client_maps(); +} + +void ini_memory_map_service(Protocol * proto, TCFBroadcastGroup * bcg) { + int i; + static ContextEventListener listener = { + event_context_changed, + NULL, + NULL, + NULL, + event_context_changed, + event_context_disposed + }; + broadcast_group = bcg; + add_channel_close_listener(channel_close_listener); + list_init(&client_map_list); + for (i = 0; i < MAP_HASH_SIZE; i++) { + list_init(&client_map_hash[i]); + } + add_context_event_listener(&listener, NULL); + add_command_handler(proto, MEMORY_MAP, "get", command_get); + add_command_handler(proto, MEMORY_MAP, "set", command_set); + context_extension_offset = context_extension(sizeof(ContextExtensionMM)); +} + + +#endif |